Rails 源代码 100 天(5)

我们今天继续 Rails 源代码 100 天这个项目。而且,未来这个项目也会做出一些调整,不能每周末更新(甚至周末也有时候不更新),那样的话,就成了源代码 100 周了,目前的计划是每天上午发布一篇文章,这个文章可能不长,但一定会有。而且每天在文章的开头都会给出今天文章的小目标:要讲清楚什么。这样,文章的长度会比较可控,也方便读者快速了解文章的内容,而不是在 Rails 源代码100天这个大标题下的笼罩下看不清今天要讲的是什么。

我们今天的目标是了解什么是 macro,以及在 ActiveRecord 中提到 macro 时,它是在讲什么。

封装好的操作

根据维基百科的解释):

A macro in computer science is a rule or pattern that specifies how a certain input sequence should be mapped to a replacement output sequence according to a defined procedure.

这句话的大概意思是说:macro 是一种对应关系,会把输入的特定指令对应到一个事先封装好操作上。很多时候,被映射的操作被找到后,我们还会执行他们。这其实就是宏的概念。

我猜,他之所以被叫做 macro ,可能是因为输入的指令可以对应到一个非常复杂的操作上,然后实际执行的程序相对于那被输入的简单的指令来说是巨大的。

其实这在编程中并不陌生。程序员会不断的把复杂的方法抽象出去,封装在一个起好名字的方法中。当那个被封装好的方法被执行时,实际执行的可能就是一大堆程序。其实这也就是 Rails 中的 macro 的实际用途。

最后,很多人第一次见到 macro 可能是在 Microsoft Office 的办公软件中。在这个系列的办公软件中,可以用 VBA 来编程,封装好一个程序,然后当有特定指令输入的时候,就可以执行很多预先定义好的操作。但 macro 的概念没有变,还是指那个从简单指令对应到复杂操作的对应关系。

has_many 中的 macro

我们上一次说到,在ActiveRecord::Associations::Builder这个 module 下面的self.create_reflection方法中,最重要的就是 rails 用 ActiveRecord::Reflection.create(macro, name, scope, options, model)来实际创造了一个HasManyReflection对象,并且,这个对象也将是Builder::HasMany.build方法的返回值。其中,create 方法接受的第一个参数就是 macro,这个 macro 是一个方法,对于HasMany这个 class 来说,macro 方法是一个简单的返回:has_many这个 symbol 的方法:

1
2
3
4
5
6
class HasMany < CollectionAssociation
def self.macro
:has_many
end
# ...
end

这个方法返回:has_many后会被 create 方法怎样使用呢,我们来看 create 方法的源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def self.create(macro, name, scope, options, ar)
klass = \
case macro
when :composed_of
AggregateReflection
when :has_many
HasManyReflection
when :has_one
HasOneReflection
when :belongs_to
BelongsToReflection
else
raise "Unsupported Macro: #{macro}"
end

reflection = klass.new(name, scope, options, ar)
options[:through] ? ThroughReflection.new(reflection) : reflection
end

可以看到,create 方法是用过 macro 来判断要构建那个类的对象,也就是说 macro 帮助 rails 映射到正确的构造函数中。