2011-06-13 16 views
2
class Bear < ActiveRecord::Base 
    def feed! 
     self.transaction do 
      raise Exception unless self.foods_eaten << Food.new(:name => "fish") 
      self.fed_at = Time.now 
      save! 
     end 
    end 
end 

class Hippo < ActiveRecord::Base 
    def wash! 
     self.transaction do 
      @soap.inventory -= 1 
      @soap.save! 
      self.washed_at = Time.now 
      save! 
     end 
    end 
end 

class ZookeeperController < ApplicationController 

    def chores 
     @zookeeper = Zookeeper.find(params[:id]) 
     Animal.transaction do 
      begin 
       @hippo.wash! 
       @bear.feed! # => FAIL AT THIS LINE 
       @zookeeper.finished_at = Time.now 
       @zookeeper.save! 
       redirect_to chores_completed_path 
      rescue Exception => e 
       render "new_chores" 
      end 
     end 
    end 
end 

如果Zookeeper#chores被调用且@bear.feed!失败并引发异常,那么将所有的回滚?Rails 3 Transactions,回滚一切

对于如何改进此代码的任何其他建议也是受欢迎的。

+0

我使用MySQL 46年1月5日,Rails的3.0.8和Ruby 1.9.2 – Dex 2011-06-15 02:43:16

回答

4

看来我必须做的是手动引发ActiveRecord :: Rollback,否则它将无法按预期工作。 ActiveRecord ::回滚是唯一一个不会导致你的屏幕转储。 http://api.rubyonrails.org/classes/ActiveRecord/Rollback.html

它是有道理的,它会这样工作,但不是真的我直觉上认为它会工作。如果我错了,请纠正我。

因此,新的代码将是这个样子:

class ZookeeperController < ApplicationController 

    def chores 
     @zookeeper = Zookeeper.find(params[:id]) 
     Animal.transaction do 
      begin 
       @hippo.wash! 
       @bear.feed! # => FAIL AT THIS LINE 
       @zookeeper.finished_at = Time.now 
       @zookeeper.save! 
       redirect_to chores_completed_path 
      rescue Exception => e 
       @_errors = true 
       render "new_chores" 
      end 
      raise ActiveRecord::Rollback if @_errors 
     end 
    end 
end 
+0

我只是好奇,这实际上可以作为你的预期。当你在救援块中调用渲染时,它不会结束'chores'动作,并且永远不会使它进入'ActiveRecord :: Rollback'? – Joey 2011-09-13 07:18:36

+1

我还建议将动物事务中的所有内容重构为模型实例方法,如'do_chores!'返回true或false,并在您的控制器中调用'if @ zookeeper.do_chores!'来决定下一步发送用户的位置。 – Joey 2011-09-13 07:25:35