【问题标题】:How to hook into save! with rails before_save! callback如何钩入保存!使用导轨 before_save!打回来
【发布时间】:2015-04-28 13:43:18
【问题描述】:

有没有办法通过回调挂钩save!?

我正在寻找类似的东西:

class CompositeService < Service
  attr_accessible :subservices

  before_save :save_subservices
  before_save :save_subservices! if bang_save?

  private

  def save_subservices
    @subservices.each(&:save)
  end

  def save_subservices!
    @subservices.each(&:save!)
  end
end

save! 级联并在(假)关联 subservices 上调用 save!。

【问题讨论】:

    标签: ruby-on-rails callback


    【解决方案1】:

    从技术上讲,您可以这样做,但我建议不要在生产中使用这种方法,因为它可以在较新的 Rails 中发生变化。这是错误的。

    您可以检查之前回调的调用堆栈并检查是否有保存!方法。

    class CompositeService < Service
      before_save :some_callback
    
      def some_callback
        lines = caller.select { |line| line =~ /persistence.rb/ && line =~ /save!/ }
    
        if lines.any?
          @subservices.each(&:save!)
        else
          @subservices.each(&:save)
        end
      end
    end
    

    【讨论】:

    • 有趣的破解!当然。但我不会认为像这样的 hack 值得生产。仍然:一个创造性和意想不到的角度。谢谢。
    • 您是在自己的代码中调用save 还是save! 方法?也许你可以稍微改变你的设计而不使用回调。您可以创建其他方法,例如save_with_bang,它将调用save! 以及@subservices.each(&amp;:save!)
    【解决方案2】:

    我想知道:这个额外的逻辑是否必要?

    如果每个@subservices 上的save 方法遵循ActiveRecord save 语义,那么您可能会免费获得正确的行为。

    换句话说,确保您的 save 方法返回 true 或 false 以指示成功或失败。那么,复合代码就变得像这样简单了:

    class CompositeService < Service
      attr_accessible :subservices
    
      before_save :save_subservices
    
      private
    
      def save_subservices
        @subservices.all?(&:save)
      end
    end
    

    如果您的任何子服务保存失败,则save_subservices 回调将返回false,这将中止回调链。这将导致包装 save 返回 false。而对于save!,它会引发异常。

    composite.save
    # => false
    
    composite.save!
    # => ActiveRecord::RecordNotSaved
    

    【讨论】:

    • 有道理。使用额外的after_validate 来验证所有@subservices,您将获得完整的AR 行为:valid?、errors,并在无法保存“关联”的情况下停止保存。不错。
    【解决方案3】:

    【讨论】:

    • 正如我在示例中明确指出的,该关联不是Arel 关联。你是不是建议我应该把这种“虚假”的联想变成一个真实的联想?如果是这样,如果没有 a) 引入关联方法的大型公共接口和 b) 没有外键的数据库列,我该怎么做?
    • 对不起,我没有注意到它不是一个真正的模型。我不会放入 bang 变体,因为 ActiveRecord 在这两种情况下都会自动调用您的 before_save 回调。 IE。如果是 save 或 save!,将调用 before_save 回调。您所要做的就是在失败的情况下填充 self.errors
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多