【问题标题】:How do Rails ActiveRecord relationship helpers allow methods such as `user.groups.new`?Rails ActiveRecord 关系助手如何允许诸如 `user.groups.new` 之类的方法?
【发布时间】:2012-07-26 15:59:27
【问题描述】:

在 Rails 中,我创建了一个从 LDAP 数据库而不是 ActiveRecord 检索用户的模型。现在我正在尝试将我的 ActiveRecord 模型与基于 LDAP 的模型集成,因此我正在我的模型中编写方法来模拟一些常见的 ActiveRecord 方法。

我试图模拟的方法之一是通常由 has_many 通过 ActiveRecord 上的关系创建的方法。在 ActiveRecord 中,这种关系将允许:

user = User.first
groups = user.groups # == Array of Groups
groups << Group.create(name: "Test") # How does Rails allow this?

Rails 究竟是如何允许这样做的?我尝试将方法动态分配给 user.groups 返回的数组实例,但似乎没有任何方法可以让这些方法知道数组是从哪个用户记录创建的。 (所以他们可以在新的关系记录上分配user_id。)我错过了什么?

【问题讨论】:

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


    【解决方案1】:

    虽然user.groups 看起来是一个组数组,但它实际上是一个完全独立的类——一个您通常不太了解的Rails 内部类,称为关联代理。代理通过将请求代理到目标类,然后适当地设置关联来响应&lt;&lt;createnew 等方法。

    如果您想要类似的功能,则必须实现自己的代理关联。这样做会很复杂,但这可能会让你开始。

    module LDAP
      class Association
        attr_accessor :source, :target
    
        def initialize(source, target)
          @source = source
          @target = target
        end
    
        def <<(obj)
          @source.group_ids = [group_ids + obj].flatten.uniq
          @source.save
        end
    
      end
    end
    
    class User
      def groups
        LDAP::Association.new(self, Group)
      end
    end
    

    这与 ActiveRecord 实现关联代理的方式并不特别接近。但是,这比 ActiveRecord 的解决方案要简单得多,应该足以复制一些基本的 ActiveRecord 功能。

    【讨论】:

    • 太棒了!那正是我一直在寻找的。奇怪的是,在 ActiveRecord 中调用 user.groups.class 会返回 Array(而不是其他类)。知道这是为什么吗?
    • Rails 在这里使用了一些诡计来实现这一点。它在Association Proxy documentation 中进行了总结,但本质上它称自己为一个数组,即使实际返回的是一个 AssociationProxy 实例(如ActiveRecord::Associations::HasManyAssociation)。同样,实现这一点的机制很复杂,而且似乎主要是因为is_a?(Array) 会以true 响应。
    【解决方案2】:

    我会通过查看 Rails 源代码来做到这一点,例如的代码 上面的 Group.create 示例可以在

    中找到

    http://api.rubyonrails.org/classes/ActiveRecord/Persistence/ClassMethods.html

      def create(attributes = nil, options = {}, &block)
        if attributes.is_a?(Array)
          attributes.collect { |attr| create(attr, options, &block) }
        else
          object = new(attributes, options, &block)
          object.save
          object
        end
      end
    end
    

    【讨论】:

      猜你喜欢
      • 2011-07-28
      • 1970-01-01
      • 1970-01-01
      • 2019-10-01
      • 1970-01-01
      • 2011-11-24
      • 1970-01-01
      • 2022-08-08
      • 2011-05-26
      相关资源
      最近更新 更多