Skip to main content
The Kai Way

Rails Internal Hierarchy

Rails 内部有清晰的层级结构,以实现Rails应用程序和Rails插件的配置以及初始化。

本文是Inspect Rails的一部分,Inspect Rails是由我正在编写的讲解Rails内部的实现与设计的一本书,欢迎阅读

digraph rails_hierarchy {
    label = "Rails Hierarchy";

    edge[dir=back, arrowtail=empty]

    node [
      style = box
      style = filled
      fontname = "Ubuntu Mono"
      fontcolor = "#F2F2F2"
      arrowhead = "empty"
    ];

    railtie [label = "Railtie", fillcolor = "#8FBCDB"];
    rengine [label = "Rails Engine", fillcolor = "#447294"];
    rapp    [label = "Rails Application", fillcolor = "#294052"];

    railtie -> rengine;
    rengine -> rapp;
}

如上图所示,所有Rails Application继承自Rails Engine,而Rails Engine继承自Railtie,这套继承体系的实现全部都封装在railties这个Rubygem里。值得一提的是,Railtie和Rails Engine的子类都是Singleton,Rails Application本身就是Singleton,所以在一个程序里Rails Application只有一个实例。

Railtie #

我们先从Railtie说起,如果你翻查过一些Rails插件的源码,会发现它都继承了Railtie。Railtie位于层级里最低最底层的部分,它实现了配置和初始化这两大功能,其中的逻辑都组织在以下两个Modules中

Initializable 模块顾名思义就是负责初始化,常用的方法只有一个叫initializer的方法,它的方法签名如下,接受一个名字,一个可选的参数,一个代码块

initializer(name, opts = {}, &blk)

定义好的 Initializer 代码块会在Rails应用程序启动时执行,并且可以在参数里指定before或者after选项, 让其在某个已定义的Initializer执行之前或之后执行,这个功能是通过Ruby内置的TSort实现的。以下是ActiveRecord设置Logger的Initializer

initializer "active_record.logger" do
  ActiveSupport.on_load(:active_record) { self.logger ||= ::Rails.logger }
end

Configuration 是实现常见的config.xxx = yyy这一常见写法的源头,它使用了Ruby的method_missing实现了配置参数的属性访问和设置,全部的配置都放在一个名为@@options的类变量里。

def method_missing(name, *args, &blk)
  if name.to_s =~ /=$/
    @@options[$`.to_sym] = args.first
  elsif @@options.key?(name)
    @@options[name]
  else
    super
  end
end

Action Mailer, Action Controller, Action View和Active Record 它们集成到Rails框架的都是通过Railtie实现。

如果你去看现实中的各种插件写的Railtie中,基本上就是调用initializer方法配置初始化逻辑和通过config变量在添加自身相关的各种配置选项。而且Rails也使用这两个模块去设置各种框架本身的初始化和参数配置。

Railtie除此之外,它还负责rake tasks和generator等部分与Rails应用程序的集成,暂不讲。

Rails Engine #

Rails Engine主要的设想就是把一些通用的Rails应用程序抽象出来并得到重用,也就是说每个Rails Engine几乎就是一个Rails应用程序,它拥有MVC结构,具有自己的路由,独立的Middleware Stack。社区里最广为人知的一个Rails Engine应该是devise

Rails Engine中实现Rails广为人知的"Convention over Configuration"特性,整套目录结构的加载就是在这里定义的。

Rails Engine是一个Rack Middleware,它实现了call方法,所以能Mount到其他Rails Engine或者Rails Application的路由上。

关于Rails的代码加载方式会在后续的章节详细讲解。

Rails Application #

组织起Rails应用程序的启动流程,是Rails Application这个类最主要的事情。而Rails Application区别于Rails Engine在于需要管理很多外部的资源,比如以下的内容

关于Rails的启动流程和Middleware Stack等话题会在后续的章节中展开并详细讲解。

本节暂时到此。