基于我之前的一些思考,我现在觉得了解 Rails 源代码是一个应该上马的项目了。如果你也有看 Rails 源代码的需求,可以 Follow 这个项目。
当我有了这个需求之后,我就在想自己要如何开始呢?有这样几点思考和大家分享:
- 越早开始越好,如果我从100天(不过是3个月的时间)开始,现在这个项目已经完成了。
- 从相对独立、短小的 helper methods 开始。如果我从 ActiveRecord 中的某个方法开始,那么这个方法通过一层一层的调用别的方法,可以整个 Active Record gem 都被调用了一遍1。
- 了解源代码也是学习 Ruby 代码(如何组织代码,如何使用 Ruby 语法)的好方式。
- 解说完代码之后提炼一下我们能从中学到什么。
话不多说,那我们就开始吧。我们先从一个非常常用的 helper method —— link_to
开始,来找找感觉。
先写几个link_to
的例子:
1 | link_to '1st link', 'https://example.com' |
如果看不过来,可以不用全看,但有个问题值得想一想:一个方法的参数结构为什么可以这么多样?
我们可以通过link_to 的源代码来解释这一点。为了方便解说,我们把代码复制过来:
1 | # rails/actionview/lib/action_view/helpers/url_helper.rb |
这个方法接受4个参数,每种参数都不是必须的。
从方法内部的第一行可以看出,如果有block,参数的次序就要轮转一下。所以,在上面举例过程中的2nd link
中用括号传入的第一个参数的参数名其实叫name
,只不过在link_to
方法的内部,通过这行代码被赋值给了options
:
html_options, options, name = options, name, block if block_given?
这里有一个题外的补充,你可以用 block
,也可以用 yield
来代表传入的代码块,但两者有不同的效果,简单来说,block
代表的是个 Proc 对象,而yield
的使用会直接对 block
进行 call
。如果你不在定义方法时明确地给出block
这个参数(加一个 &
是为了更明确的表明那是个 code block),那么你就只能使用 yield。另外,在某些 ruby style guide 中,使用 block 会建议改为 yield。当然,block_given?
对于两者都是通用的。
在接下来的这这行代码:
1 | html_options = convert_options_to_data_attributes(options, html_options) |
Rails 的工作是整理出相关的字段,把那些原本应该是 options
的,但是为了传参方便被归为了 html_options
的字段拿掉,比如method
字段。
再接下来是生成 url 字符串的过程,并且用将所有整理好的参数传入content_tag
方法来生成<a>
便签:
1 | url = url_for(options) |
这具体是怎么生成的,以及上一段中 Rails 封装的方法convert_options_to_data_attributes
到底在做什么,我们在下一节继续探索。与此同时还有一个问题值得我们关注,为什么 Rails 对一些特定的字段要freeze
,比如"a".freeze
和 "href.freeze"
1. 这是我从一个 stack overflow 回答中得到的提示 ↩