今天的小目标是看看在调用has_many
的时候ActiveRecord::Reflection.create(macro, name, scope, options, model)
会创造一个怎样的对象。
另外,今天还有一个小调整,就是在引用 rails 的代码是,标注方法定义所在的文件路径。
HasManyReflection
的构造函数
我们上次已经说过了在ActiveRecord::Reflection.create
方法中,rails 会根据 macro 的内容而 new 出不同的 reflection 对象。对于has_many
方法,macro 等于:has_many
,因此在 create 方法中会对应到构造一个HasManyReflection
这个类的对象:
1 | # activerecord-5.1.4/lib/active_record/reflection.rb |
从 create 方法的代码中我们可以看到,传入构造函数的参数有4个,但对于:
1 | # app/models/product.rb |
这个简单的调用来说,scope
和options
都是 nil,而 name
就是:users
,ar
是调用has_many
的 ActiveRecord 的类的名称,也就是 model 的名称,在这里就是Product
。
我们已经了解了传入HasManyReflection.new
的参数是什么了,那么接下来就让我们看看这个构造函数的内部是什么样的:
1 | # activerecord-5.1.4/lib/active_record/reflection.rb |
其实这个方法是被定义在AssociationReflection
这个类中的,而HasManyReflection
继承了这个类。而正如这个方法的第一行所示,AssociationReflection
也是继承自己他的父类。为了更好的理解这个方法,我们先来看一下其中涉及到的继承关系:
HasManyReflection < AssociationReflection < MacroReflection
MacroReflection
的父类也是一个 ActiveRecord 中重要的类,但到MacroReflection
为止,我们已经能够获得执行HasManyReflection.new
的全部代码。我们先来看看MacroReflection
中的#intialize
构造函数,毕竟,那是AssociationReflection#initialize
中的第一步(通过上一个代码段中的super
方法调用)。
1 | # activerecord-5.1.4/lib/active_record/reflection.rb |
根据之前的解释,传入这个方法的参数是确定的,分别是:users
, nil
, nil
, Product
。通过这个方法,我们将获得6个实例变量,通过对代码的阅读,我们不能知道这些实例变量的值是什么。不过,我们需要另外说明的是,这些实例变量也都是对象的(可以访问的)属性,因为 rails 在MacroReflection
这个类中,定义了对应的attr_reader
。
以上就是MacroReflection#initialize
做的事情,接下来我们回到AssociationReflection#initialize
中。我们再一次引用它的源代码:
1 | # activerecord-5.1.4/lib/active_record/reflection.rb |
可以看到,首先这个方法声明了更多的实例变量,这也确实是 ActiveRecord 有关的对象的特点,就是有很多的实例变量。不过,对于has_many :users
这种简单的调用来说,@automatic_inverse_of
, @type
, @foreign_type
都是 nil。 @constructable
在构建HasManyReflection
时,会从calculate_constructable
方法中得到一个简单的true
,但对于处理belongs_to
, has_one
等方法的调用时,情况会复杂一点,我们以后再说。
另外一个实例变量@association_scope_cache
会在以后谈到,而最后一个实例变量@scope_lock = Mutex.new
会被赋值一个Mutex
对象。什么是Mutex
,有什么用,我们将在下一次的文章中来交接。
到这里,我们对于ActiveRecord::Reflection.create(macro, name, scope, options, model)
这行代码的作用已经清楚了:在处理has_many
的调用时,他会返回一个HasManyReflection
对象,这个对象中包含@name
, @scope
, @options
, @klass
等实例变量。而且,可以提前说明的是,这些实例变量将会在接下来被用到。具体在哪里被用到呢?回到create_reflection
方法返回的地方:
1 | # activerecord-5.1.4/lib/active_record/associations/builder/association.rb |
我们可以看到,接下来,这个HasManyReflection
对象将会被赋值给reflection
这个局部变量,并被传入define_accessors
等方法做一些操作。
具体做了那些操作,我们在了解完ruby 中的Mutex
后会继续探索。