create
is a method that should return true only when everything went as expected and false otherwise. I'm going for error codes style control flow.
class TransferOperator
class TransferError < Struct.new(:type, :message); ; end
attr_reader :transfer, :error
def initialize(transfer)
@transfer = transfer
end
# Creates the transfer and locks money in the bank
def create
return error(:validation_error) if transfer.invalid?
to_bank = transfer.main_to_bank
to_bank.with_lock do
# How does return here behave? Should a raise be issued instead and caught outside?
return error(:insufficient_buffer) if to_bank.available_balance < transfer.amount
to_bank.available_balance -= transfer.amount
to_bank.locked_balance += transfer.amount
to_bank.save!
transfer.save!
end
# Is it guaranteed here that the above transaction has always been succesful?
true
end
private
def error(type, message='')
@error = TransferError.new(type, message)
false
end
end
The idea here is to have a such flow for the caller:
def move_money
@transfer = Transfer.new(params)
operator = TransferOperator.new(@transfer)
if operator.create
redirect_to :root, notice: 'success!'
else
if operator.error.type == :validation_error
render action: 'new'
elsif operator.error.type == :insufficient_buffer
redirect_to :root, notice: 'not enough money'
else
# Handle other errors here...
end
end
end
What happens with the error return inside the transaction?
It it guaranteed that the transaction was successful if true is returned?
From http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html
One exception is the ActiveRecord::Rollback exception, which will trigger a ROLLBACK when raised, but not be re-raised by the transaction block.
Is it possible that Rails would raise ActiveRecord::Rollback
by itself? If it does, then the transaction would silently fail and true returned (which is not what we want).
ActiveRecord::Rollback
itself inside the transaction? – Femmine