rails内的class关于添加了has_many或者belongs_to后如何动态添加一些额外的方法 ?

Posted 慕斯bevan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了rails内的class关于添加了has_many或者belongs_to后如何动态添加一些额外的方法 ?相关的知识,希望对你有一定的参考价值。

rails版本5.0.0.1

比如有如下的两个类

class User < ActiveRecord::Base
  has_many :posts

end

class Post < ActiveRecord::Base
  belongs_to :user

end

这个时候有一个user

user = User.take

然后可以使用user.posts来找到属于user的所有的posts,同理如果有一个post也可以通过post.user来找到属于它自己的user

这个时候不禁想问,这个user.post方法是怎么来的呢?

ActiveRecord::Core里有个generated_association_methods方法

def generated_association_methods
  @generated_association_methods ||= begin
    mod = const_set(:GeneratedAssociationMethods, Module.new)
    include mod
    mod
  end
end

当你的rails加载你的class就会执行这个方法(比如加载了User class),并新建一个xxx::GeneratedAssociationMethods的module(加载User class就创建了属于user自己的module:User::GeneratedAssociationMethods),然后通过include方式将xxx::GeneratedAssociationMethods module加载进入到你的class里。而这个module就会用来存放user.posts类似这样的方法。

User class中添加的has_many :posts就在加载class时执行一系列方法到2个方法:self.define_readers(mixin, name)self.define_writers(mixin, name) 

这里的两个参数对应的分别为mixin: User, name: posts

具体代码如下:

 

    def self.define_readers(mixin, name)
      mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
        def #{name}(*args)
          association(:#{name}).reader(*args)
        end
      CODE
    end

    def self.define_writers(mixin, name)
      mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
        def #{name}=(value)
          association(:#{name}).writer(value)
        end
      CODE
    end

这两个方法就是动态地添加.posts(*args)和.posts=(value) 2个user的实例方法。

class_eval 这个方法会在一个已存在的类的上下文中执行一个块,并且会修改这个类的内容(《ruby元编程》的解释)。

如下代码会添加.posts(*args)到User::GeneratedAssociationMethods模块里

mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
        def #{name}(*args)
          association(:#{name}).reader(*args)
        end
      CODE

 

如果按照《ruby元编程》的知识的话,可以这样改写

mixin.class_eval do
  define_method(name) do |*args|
    association(name.to_sym).reader(*args)
  end
end

 又或者可以一行改写

mixin.class_eval("def #{name}(*args); association(:#{name}).reader(*args); end")

 

以上是关于rails内的class关于添加了has_many或者belongs_to后如何动态添加一些额外的方法 ?的主要内容,如果未能解决你的问题,请参考以下文章

Rails 4中has_many中的静态范围

如何将记录添加到has_many:通过rails中的关联

rails has_many setter 应该设置条件(如果指定)

Rails ActiveRecord 关联“has_many,每个都有 has_one”

has_many 中 :unique => true/Distinct 选项的问题,通过关联/命名范围(Rails)

Rails_admin gem 过滤 has_many 通过关联