代码之家  ›  专栏  ›  技术社区  ›  Daniel Vandersluis

如何引发ActiveRecord::Rollback异常并一起返回值?

  •  24
  • Daniel Vandersluis  · 技术社区  · 15 年前

    我有一个模型 acts_as_nested_set fork,我已经向模型添加了一个方法来保存模型并在一个事务中将节点移动到集合中。此方法调用验证方法以确保移动有效,该方法返回true或false。如果验证失败,我希望我的save方法 ActiveRecord::Rollback 回滚事务,但也向调用方返回false。

    我的模型如下:

    class Category < ActiveRecord::Base
      acts_as_nested_set :dependent => :destroy, :scope => :journal
    
      def save_with_place_in_set(parent_id)
        Category.transaction do
          return false if !save_without_place_in_set
    
          if !validate_move parent_id
            raise ActiveRecord::Rollback and return false
          else
            place_in_nested_set parent_id
            return true
          end
        end
      end
    
      alias_method_chain :save, :place_in_set
    
      def validate_move(parent_id)
        # return true or false if the move is valid
        # ...
      end
    
      def place_in_nested_set(parent_id)
        # place the node in the correct place in the set
        # ...
      end
    end
    

    但是,当我在失败的情况下调用save时,事务将回滚,但函数返回 nil :

    >> c = Category.new(:name => "test") 
    => #<Category id: nil, name: "test" parent_id: nil, lft: nil, rgt: nil>
    >> c.save_with_place_in_set 47
    => nil
    >> c.errors.full_messages
    => ["The specified parent is invalid"]
    
    3 回复  |  直到 8 年前
        1
  •  27
  •   Shadwell    15 年前

    您可以将函数返回的值存储在变量中,并在事务块之外返回该值。例如。

      def save_with_place_in_set(parent_id)
        return_value = false
        Category.transaction do
          if !save_without_place_in_set
            return_value = false
          elsif !validate_move parent_id
            return_value = false
            raise ActiveRecord::Rollback
          else
            place_in_nested_set parent_id
            return_value = true
          end
        end
        return return_value
      end
    

    我最初将返回值设置为false,因为从该事务块中获得的唯一其他方法是如果其他方法中的一个 ActiveRecord::Rollback 我相信。

        2
  •  10
  •   Daniel Vandersluis    15 年前

    因为 ActiveRecord::Rollback 异常已处理,但不会由重新引发 ActiveRecord::Transaction ,我可以将返回移出事务块,从而在事务回滚后返回值。

    稍微重构一下:

    def save_with_place_in_set(parent_id = nil)
      Category.transaction do
        return false if !save_without_place_in_set
        raise ActiveRecord::Rollback if !validate_move parent_id
    
        place_in_nested_set parent_id
        return true
      end
    
      return false
    end
    
        3
  •  0
  •   Matteo    8 年前

    我知道可能有点晚了,但是我遇到了同样的问题,我发现,在事务块中,您可以简单地引发一个异常并挽救一个……rails隐式回滚整个事务。所以不需要ActiveRecord::Rollback。

    例如:

    def create
      begin
        Model.transaction do
          # using create! will cause Exception on validation errors
          record = Model.create!({name: nil})
          check_something_afterwards(record)
          return true
        end
      rescue Exception => e
        puts e.message
        return false
      end
    end
    
    def check_something_afterwards(record)
      # just for demonstration purpose
      raise Exception, "name is missing" if record.name.nil?
    end
    

    我正在使用Rails3.2.15和Ruby1.9.3。