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).
If you want to cause the transaction to rollback, you must raise an error. You have a couple of choices:
ActiveRecord::Rollback
and the transaction will be rolled back and no error will be re-raised outside the transaction block. As you said, this will silently roll back the transaction. Probably not what you want.Returning an error object does nothing. It's just another object being passed around.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With