Rails 源代码 100 天(4)

今天我们进入 ActiveRecord 的部分,这可能是 Rails 中最常用的部分之一(或者没有之一)。

ActiveRecord 中常用的方法有:建立关联关系用的belongs_tohas_many等方法;建立数据验证用的validates等方法;当然还有作为一个 ORM ,最基本的 CRUD 功能;当然还有连接数据库等的establish_connection等方法。

我们先从 has_many 这个方法说起。

has_many 的第一个特点是,生成了很多实例方法,比如:

1
2
3
class Product < ActiveRecord::Base
has_many :users
end

会生成usersusers.create 等方法。这是怎么实现的呢?让我们来看看源代码。

has_many 方法是这么定义的

1
2
3
4
def has_many(name, scope = nil, options = {}, &extension)
reflection = Builder::HasMany.build(self, name, scope, options, &extension)
Reflection.add_reflection self, name, reflection
end

众所周知,这个方法可以接受很多可选的参数,如果没有那些参数,Rails 会给出默认值。我们这一阶段的主要目标是看看 Rails 如何利用has_many生成那么多的实例方法,所以我们不会对 has_many 方法传很多options。因此,在has_many 方法的内部,传入Builder::HasMany.build方法的参数其实只有self,也就是 has_many方法被使用的那个 model,以及name

而在Builder::HasMany.build 方法中,我们可以很快的看到这些方法是如何被定义出来的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def self.build(model, name, scope, options, &block)
if model.dangerous_attribute_method?(name)
raise ArgumentError, "You tried to define an association named #{name} on the model #{model.name}, but " \
"this will conflict with a method #{name} already defined by Active Record. " \
"Please choose a different association name."
end

extension = define_extensions model, name, &block
reflection = create_reflection model, name, scope, options, extension
define_accessors model, reflection
define_callbacks model, reflection
define_validations model, reflection
reflection
end

这里插一句题外话,ruby 中的 class 也是对象,类方法其实是一个类的对象方法(或者叫单例方法,英文 singleton_method),这个 singleton_method 存放在这个对象的 singleton_class 中,singleton_class 会随着类的继承而出现在继承链中。所以尽管self.build这个类方法是定义在ActiveRecord::Associations::Builder::Association中的,继承了这个类的Builder::HasMany 也可以因此调用build方法。

回到正题,我们看到上文self.build方法中,extension 是define_extensions的返回值,这个值只有在&block存在的情况下,才会有非 nil 的返回值,否则返回值只能是 nil。具体原因可以看源代码

更重要的是 reflection 这个值是create_reflection方法的返回值,而在这个方法中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def self.create_reflection(model, name, scope, options, extension = nil)
raise ArgumentError, "association names must be a Symbol" unless name.kind_of?(Symbol)

if scope.is_a?(Hash)
options = scope
scope = nil
end

validate_options(options)

scope = build_scope(scope, extension)

ActiveRecord::Reflection.create(macro, name, scope, options, model)
end

Rails 通过最后一行代码ActiveRecord::Reflection.create(macro, name, scope, options, model)创建了很多东西。相比于这行代码,这个方法中的其他代码其实没那么重要,他们都是在判断参数是否合法,或者调整一些参数。

ActiveRecord::Reflection.create中到底创建了什么呢?

首先说,传进这个方法的 macro 是一个 symbol —— :has_many,有兴趣的可以自己看看源代码。name也是一个 symbol,在我们的文章中,这个参数是 :users,而model就是Product中的model。

这里再插入一个题外话,macro 在中文世界里通常被翻译为宏,或者宏指令,我以前在使用微软的 office 办公软件时,经常会被提醒是否禁用宏。这里的宏的英文应该也就是 macro 了。宏这个翻译或许有他抽象的意义,但真的不够直观,如果这个词忽然出现在一个人的 office 软件的提示框里,会让很多人有不明所以的感觉。那么宏到底是什么呢,我们能不能给他一个更直观的翻译呢?节省一下非 developer 的使用者的认知成本呢?

我们下次继续