【问题标题】:Delegate all method calls on a model to an association将模型上的所有方法调用委托给关联
【发布时间】:2012-03-28 19:31:46
【问题描述】:

我的 ActiveRecord 模型具有这样的多态关联:

class Reach < ActiveRecord::Base
  belongs_to :reachable, :polymorphic => true
end

这个模型就像一个代理。我需要做的是将对该对象的所有方法调用转发到关联对象:reachable。我认为delegate 在这里无济于事,因为我必须明确命名我需要委托的所有方法。我需要像 delegate :all 这样的东西来委托所有方法(而不是 all 方法)。

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-3 activerecord


    【解决方案1】:

    自 Rails 5.1+ 起,您可以委托所有未使用 delegate_missing_to :reachable 实现的内容

    基本上,做你所期望的。 你可以阅读更多关于Api Doc

    如果您卡在以前的版本中,那么只建议使用来自@Veraticus answermethod_missing,虽然性能方面不如前面提到的那样,但我认为是更灵活的方法。

    【讨论】:

      【解决方案2】:

      您可以在这里做两件事:

      1. 较慢(性能方面)但更简单的方法是使用 method_missing:

        class Reach < ActiveRecord::Base
        
          def method_missing(method, *args)
            return reachable.send(method, *args) if reachable.respond_to?(method)
            super
          end
        end
        
      2. 执行速度更快的方法是动态定义您要委托的每个方法:

        class Reach < ActiveRecord::Base
        
          [:all, :my, :methods, :here].each do |m|
            define_method(m) do |*args|
              reachable.send(m, *args)
            end 
          end
        end
        

      如果您愿意,您甚至可以以更动态的方式使用该方法,方法是获取 Reach 类,查找在它上定义的方法,并且只定义在 Reachable 上的方法。不过我会手工完成,因为有些你可能不想包含在内。

      【讨论】:

      • 关于你的第一个方法。如果Reach 对象存在,这不会调用该方法吗?
      • 是的,对不起,我想我输入了reach,而我的意思是reachable
      • 如果您有想要委托的方法列表,您可以...像往常一样委托它们。
      【解决方案3】:

      对于 Rails,我做了以下工作:

      class User < ApplicationRecord
        has_one :member
        delegate (Member.new.attributes.keys - User.new.attributes.keys), to: :member
      end
      

      - User.new... 不会覆盖User 上的现有属性(例如,created_at

      不过,我不确定这种方法如何处理多态性。

      【讨论】:

        【解决方案4】:

        我找到了一种使用改进来解决问题的巧妙方法。标准库中已经有一个类允许将每个方法调用委托给目标对象。 Delegator and by extension SimpleDelegator 现在有一种方法可以将SimpleDelegator 插入到您的继承链中,而无需使用改进直接从它继承:

        def self.include_delegator
          mod = Module.new do
            include refine(SimpleDelegator) { yield if block_given? }
          end
          self.send :include, mod
        end
        include_delegator
        

        现在为了利用 SimpleDelegator 在初始化后回调中设置委托目标,如下所示:

        after_initialize do |instance|
          __setobj__(instance.reachable)
        end
        

        这相当于直接从SimpleDelegator继承并在构造中设置委托,无需手动整理要委托的方法,可以避免使用方法缺失。

        【讨论】:

          猜你喜欢
          • 2012-09-09
          • 1970-01-01
          • 1970-01-01
          • 2020-11-14
          • 2019-10-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多