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

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

rails版本5.0.0.1

比如有如下的两个类

class User < ActiveRecord::Base  has_many :postsendclass Post < ActiveRecord::Base  belongs_to :userend

这个时候有一个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  endend

当你的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)  endend

 又或者可以一行改写

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

 

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