安道

博客文章

新建 Rails 程序

本文针对 Rails 4。

新建 Rails 程序的第一步是,在命令行中执行 rails new 命令。执行这个命令后到底发生了什么?

当我们在命令行中输入 rails new [PATH] 后,命令行先要找到 rails 这个可执行文件,可以使用 which 命令查看,具体的路径视安装方式而不同。我使用 rbenv 管理 Ruby,所以在我的电脑中,rails 可执行文件的位置在 ~/username/.rbenv/shims/rails

rails 这个可执行文件就是 railties/bin/rails,内容很简单:

#!/usr/bin/env ruby

git_path = File.join(File.expand_path('../../..', __FILE__), '.git')

if File.exists?(git_path)
  railties_path = File.expand_path('../../lib', __FILE__)
  $:.unshift(railties_path)
end
require "rails/cli"

第一行是 shebang 声明,其他几行暂且不提,最后一行才是关键,把命令的实现交由 railties/lib/rails/cli.rbsource)。railties/lib/rails/cli.rb 第 6 行,调用 Rails::AppRailsLoader.exec_app_rails 方法,在 railties/lib/rails/app_rails_loader.rb 中定义(source)。

exec_app_rails 这个类方法执行一个 loop 无限循环,直到在当前工作目录或上层目录中找到 bin/rails 这个可执行文件为止。这个方法的作用是判断 pwd 或上层目录是否为 Rails 程序目录,如果是一个现有的 Rails 程序,则终止执行 railties/lib/rails/cli.rb 的后续内容。

现在,假设我们的 pwd 不是既有 Rails 程序目录,执行 rails new [PATH] 命令,则会跳到 railties/lib/rails/cli.rb 的第 8 行,,判断 Ruby 的版本,如果小于 1.9.3,便终止命令。Rails 4 推荐使用 Ruby 2.0。

railties/lib/rails/cli.rb 第 11-16 行,是 if-else 分支语句,跳过 if 分支,直接看 else 分支,加载 railties/lib/rails/commands/application.rbsouece)。这个文件第 3-6 行不会在执行 rails new 命令是运行,暂且不提。接下来又是一个 if-else 分支,直接看 else 分支。

如果在执行 rails new 命令时没有指定 --no-rc 选项的话,会执行第 12-25 行,读取 ~/.railsrc 文件。

然后,加载所需的生成器文件:

require 'rails/generators'
require 'rails/generators/rails/app/app_generator'

第 43 行,才是关键,Rails::Generators::AppGenerator.start

Rails 的生成器是通过 Thor 实现的。Rails::Generators::AppGenerator 的父类如下:Rails::Generators::AppBase < Rails::Generators::Base < ::Thor::GroupThor::Group 的用法可以参考 Thor 的 Wiki,简单来说,Group 中定义的实例方法,会按照顺序执行,特别适合用来生成文件。

调用 Rails::Generators::AppGenerator.start 方法后,会按照顺序调用如下方法: create_rootcreate_root_filescreate_app_filescreate_bin_filescreate_config_filescreate_boot_filecreate_active_record_filescreate_db_filescreate_lib_filescreate_log_filescreate_public_filescreate_test_filescreate_tmp_filescreate_vendor_filesfinish_template。文件的模板位于 railties/lib/rails/generators/rails/app/templates/ 目录下。

然后,我们在命令行中就看到了一串说明:

$ rails new demo -B
      create
      create  README.rdoc
      create  Rakefile
      create  config.ru
      create  .gitignore
      create  Gemfile
      create  app
      create  app/assets/javascripts/application.js
      create  app/assets/stylesheets/application.css
      create  app/controllers/application_controller.rb
      create  app/helpers/application_helper.rb
      create  app/views/layouts/application.html.erb
      create  app/mailers/.keep
      create  app/models/.keep
      create  app/controllers/concerns/.keep
      create  app/models/concerns/.keep
      create  bin
      create  bin/bundle
      create  bin/rails
      create  bin/rake
      create  config
      create  config/routes.rb
      create  config/application.rb
      create  config/environment.rb
      create  config/environments
      create  config/environments/development.rb
      create  config/environments/production.rb
      create  config/environments/test.rb
      create  config/initializers
      create  config/initializers/backtrace_silencers.rb
      create  config/initializers/filter_parameter_logging.rb
      create  config/initializers/inflections.rb
      create  config/initializers/mime_types.rb
      create  config/initializers/secret_token.rb
      create  config/initializers/session_store.rb
      create  config/initializers/wrap_parameters.rb
      create  config/locales
      create  config/locales/en.yml
      create  config/boot.rb
      create  config/database.yml
      create  db
      create  db/seeds.rb
      create  lib
      create  lib/tasks
      create  lib/tasks/.keep
      create  lib/assets
      create  lib/assets/.keep
      create  log
      create  log/.keep
      create  public
      create  public/404.html
      create  public/422.html
      create  public/500.html
      create  public/favicon.ico
      create  public/robots.txt
      create  test/fixtures
      create  test/fixtures/.keep
      create  test/controllers
      create  test/controllers/.keep
      create  test/mailers
      create  test/mailers/.keep
      create  test/models
      create  test/models/.keep
      create  test/helpers
      create  test/helpers/.keep
      create  test/integration
      create  test/integration/.keep
      create  test/test_helper.rb
      create  tmp/cache
      create  tmp/cache/assets
      create  vendor/assets/javascripts
      create  vendor/assets/javascripts/.keep
      create  vendor/assets/stylesheets
      create  vendor/assets/stylesheets/.keep

最后,我们得到了一个 Rails 程序骨架。