新建 Rails 应用
(发布于 2013 年 5 月 12 日)
本文针对 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.rb(source)。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.rb(souece)。这个文件第 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::Group。Thor::Group 的用法可以参考 Thor 的 Wiki,简单来说,Group 中定义的实例方法,会按照顺序执行,特别适合用来生成文件。
调用 Rails::Generators::AppGenerator.start 方法后,会按照顺序调用如下方法: create_root,create_root_files,create_app_files,create_bin_files,create_config_files,create_boot_file,create_active_record_files,create_db_files,create_lib_files,create_log_files,create_public_files,create_test_files,create_tmp_files,create_vendor_files,finish_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 应用骨架。
