我有一段代码,只有在前一段代码没有引发错误的情况下才能运行。我已经实现了一个看似hacky的解决方案,我确信在Ruby中有更好的方法。
这是我到目前为止所拥有的:
existing_comments = Comment.all
catch(:creation_failure) do
begin
ActiveRecord::Base.transaction do
results.each do |row|
Comment.create!(row)
end
end
rescue
throw(:creation_failure)
end
existing_comments.destroy_all
end
要做到这一点还有更好的方法。
答案 0 :(得分:2)
很难弄清楚你要做的究竟是什么。正如@ehabkost已经指出的那样,如果引发异常,无论如何执行都会中止,所以你无需做任何事情。在引发异常的代码之后出现的任何内容都不会被执行,毕竟那是异常的整点。
这样做你想要的吗?
existing_comments = Comment.all
begin
ActiveRecord::Base.transaction do
results.each do |row|
Comment.create!(row)
end
end
rescue # You should *never* do this!
else
existing_comments.destroy_all
end
顺便说一句:你应该永远,在任何情况下,只是盲目地拯救所有例外。你应该只拯救完全你想要的那些。你真的认为盲目地吞下一个ThreadError
而没有注意到它是一个好主意吗?仅有ActiveRecordError
的39个直接子类,或许其中一个比仅仅拯救所有异常(或至少所有StandardError
例外)更合适。
答案 1 :(得分:1)
我建议使用这个重构器:
Comment.transaction do
Comment.destroy_all
results.each do |row|
comment = Comment.new(row)
raise ActiveRecord::Rollback unless comment.save
end
end
我已将评论销毁移至顶部。虽然它不完全相同(现在新评论不会与现有评论发生冲突),但我认为这更有意义。
请注意throw/catch
- 在某些情况下使用它们 - 不应该用于正常编码,否则你最终会得到不可分割的意大利面条代码。
答案 2 :(得分:0)
尝试(我没有机会测试它):
existing_comments = Comment.all
rescue Exception => e do
ActiveRecord::Base.transaction do
results.each do |row|
Comment.create!(row)
end
#you can log the failure messages, etc… by doing something with e
existing_comments.destroy_all
end
end
答案 3 :(得分:0)
事务 - http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html - 确保块作为原子操作执行。如果引发异常,则回滚整个事务。
答案 4 :(得分:0)
我会做这样的事情:
existing_comments = Comment.all
begin
ActiveRecord::Base.transaction do
results.each do |row|
Comment.create!(row)
end
end
existing_comments.destroy_all
rescue Exception => e
# do something with the exception??
end
在你的代码中,通过混合引发异常(由rescue
处理)和抛出某些东西(由catch
处理),你很难(对我而言)。大多数时候只使用raise
。
理论:raise / rescue用于处理异常,throw / catch用于控制流,你可以抛出任何东西(不只是异常)。除非确实需要,否则我会避免捕获/扔掉。
因为当引发异常时,流程被中断,我们可以很容易地从这个事实中获利,并且只有在没有异常发生的情况下才会这样做。
希望这会有所帮助:)
答案 5 :(得分:-1)
当我读到你的问题时,我想“等等,这是微不足道的:只需像往常一样编写你的代码”,例如:
def my_method
do_something
do_something_else
end
这样,do_something_else
仅在do_something
未引发异常时才会运行。你不需要做任何特别的事情。
但是,看起来你真正想要做的就是不运行do_something_else
和忽略do_something引发的异常,对吗?
我必须注意,如果你打算忽略异常,你应该非常小心,但如果你真的想这样做,一个简单的方法来忽略代码块中的异常而不运行任何其他东西将是只需从方法返回。像这样:
def my_method
existing_comments = Comment.all
begin
ActiveRecord::Base.transaction do
results.each do |row|
Comment.create!(row)
end
end
rescue
# just return. ignore any exceptions from the block above
return
end
# this will run only if the above doesn't raise any exception:
existing_comments.destroy_all
end