Imagine that you have a context that handles money transfers between user's accounts.
class Account < ActiveRecord::Base
belongs_to :user
end
class MoneySender < SimpleDelegator
class NotEnoughBalanceError < StandardError ; ; end
def initialize(account)
super(account)
end
def send_money(destination_account, amount)
raise NotEnoughBalanceError unless can_send?(amount)
self.transaction do
self.balance -= amount
destination_account.balance += amount
end
self.balance
end
def can_send?(amount)
self.balance >= amount
end
end
class HandleMoneyTransferContext
def initialize(source, destination, amount)
@source = source
@destination = destination
@amount = amount
end
def transfer
sender = MoneySender.new(@source
sender.send_money(@destination, @amount)
end
end
And money transfers are triggered by a web application and the rails controller that handle that operations does something like this
class AccountsController < AplicationController
def transfer
source = Account.find(params[:id])
destination = Account.find(params[:destination_account])
HandleMoneyTransferContext.new(source, destination, params[:amount]).transfer
render 'success_page'
rescue MoneySender::NotEnoughBalanceError => e
flash[:error] = t(accounts.transfer.not_enough_money)
render 'error_page', status: 400
end
end
So my question is, Is it OK for a context to raise exceptions? Should I catch the Role exception in the context and raise a context exception? (Context users should not know which roles are being used), Is there a better solution?
Thanks
A context is just an object (yes there are constraints so not all objects are contexts) and within an operation of an object some exceptions are in order. Eg an ArgumentNil could be valid if what should have been a RolePlayer is nil instead. So to answer your question. So yes a context can throw exceptions if they are related to the system operation(s) the context encapsulates.
That said I don't see much DCI in your example code. Eg there no role methods in the context and it would seem the actual behavior belongs to a class and neither a role or an object. There's a money transfer example on fulloo.info as well as one of the examples for the maroon gem. You can read more on using maroon to do DCI in ruby (Though the syntax has been simplified since the writing of that article together with the examples it should still be a good starting point)