create
是一个方法,只有在一切按预期进行时才应返回true,否则应返回false。我将采用错误代码样式的控制流。
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
这里的想法是为呼叫者提供这样的流程:
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
事务内部的错误返回会发生什么?
如果返回true,它是否保证交易成功?
发件人http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html
一个异常是ActiveRecord::Rollback异常,它将引发时触发ROLLBACK,但不会被事务块。
Rails是否有可能自己提出ActiveRecord::Rollback
?如果真的发生了,那么事务将默默地失败并返回true(这不是我们想要的)。
如果要使事务回滚,必须引发一个错误。你有几个选择:
- 引发
ActiveRecord::Rollback
,事务将回滚,并且不会在事务块外重新引发错误。正如您所说,这将悄悄地回滚交易。可能不是你想要的 - 引发任何其他类型的错误。这将导致事务回滚,并引发您的错误。您可以挽救该错误以适当地重定向用户
返回错误对象没有任何作用。这只是另一个被传递的物体。
我相信这在Rails 7中已经改变了。在编写时,最新版本的Rails(7.0.2)将在遇到return
时静默地回滚事务。
请参阅https://github.com/rails/rails/issues/45017以及早期版本中的弃用警告https://github.com/rails/rails/pull/29333
TLDR;如果需要从当前上下文返回,请不要使用return
使用next
。
如果您嵌套在几个块中,并且不能使用next
,请将事务的内容提取到它自己的方法中,并在那里使用return。