环境设定与Bundler

    这一章我们走访Rails的一些设定、Bundler,以及Command Line指令的用法。

    这一节让我们走访一个 Rails 的目录结构:

    app 目录是你主要工作的地方,不同子目录存放了 Models、Controllers、Views、Helpers 和 Assets 等档案。

    app/controllers

    Controller 的类别档案存放在这里

    app/models

    Model 的类别档案存放在这里

    app/views

    View 的样本(template)档案,依照不同 Controllers 分子目录存放。

    app/helpers

    Helper 一些在 Views 中可以使用的小方法,用来产生较复杂的 HTML。默认的 Helper 档案命名是对应 Controller 的,不过并不强制,定义在任一个 Helper 档案中的方法,都可以在任何 Views 中使用。

    app/assets

    Assets 静态档案存放在这里,包括有JavaScriptStylesheets样式表和Images图档。详细的用法会在Assets一章中介绍。

    config/

    虽然 Rails 的原则是惯例优于设定,不过还是有一些需要设定的地方。这个目录下存放了例如数据库设定档 database.yml、路由设定 routes.rb、应用程式设定档 application.rb 和不同执行环境的设定档在 config/environments 目录下。

    db/

    数据库 Schema(纲要) 和定义档 migrations

    lib/

    如果你有一些共享的类别或模组档案,可以放在这里,然后用require加载。例如一个放在lib/foobar.rb的类别或模组档案,可以在要使用的.rb档案中这样加载:

    如果放在子目录lib/foo/bar.rb的话:

    lib/tasks

    Rake 任务档案存放在这里,我们会在 一章介绍 Rake。

    log/

    不同执行环境的 log 档案会分别记录在这里

    public/

    这个目录对 Web 服务器来说,就是文件根目录(document root),也就是唯一可以在网络上读取到的目录。

    bin/

    Rails 的脚本档案,例如执行bin/rake

    test/

    单元测试、功能测试及整合测试的档案。本书会改用RSpec做测试,所以这一个目录会被砍掉。RSpec的测试档案会放在spec/目录下。

    .用来存放暂时用途的档案

    vendor/assets

    可将第三方的CSS/JavaScript函式库(没有提供Gem安装版本的)复制一份放在这里。

    其他根目录下的档案

    • config.ru 用来启动应用程式的 Rack 设定档
    • Gemfile 设定你的 Rails 应用程式会使用哪些 Gems
    • README.md 你的应用程式使用手册。你可以用来告诉其他人你的应用程式是做什么用的,如何使用等等。这会是Github上的专案首页。
    • Rakefile 用来加载可以被命令列执行的 Rake 任务

    多重环境

    Rails 应用程式默认提供了三种不同的执行模式:

    • development environment 开发模式,用在你的开发的时候
    • test environment 测试模式,用在执行测试程式时
    • production environment 正式上线模式,用在实际的上线运作环境

    不同环境的差异在于有不同的设定,除了数据库设定 database.yml 里分开设定之外,个别的环境设定放在 config/environments/development.rb、config/environments/test.rb 和 config/environments/production.rb,它们可以有不同的 Log 层级、Session 设定、Email 设定等等。除了默认的这三种模式,我们也可以自定模式,只需要建立对应的档案即可,例如 config/environments/staging.rb。我们会在下一节详述这些档案里面的设定。

    staging 可以用来表示准上线模式,用来做正式上线前的 QA 测试用途。

    因为程式本身是不是写死是哪一种执行模式,那么要怎么区分呢?根据不同情况有不同方法,包括:

    根据环境变量 RAILSENV 或 RACK_ENV 来决定使用哪一种模式,例如使用_rake时:

    1. RAILS_ENV=production bin/rake db:migrate

    下一节会介绍的rails指令根据参数决定:

    1. bin/rails console production
    2. bin/rails server -e production

    最后,应用程式服务器则看服务器设定档,例如Passenger里会设定RackEnv参数,布署一章会详细介绍。

    Rails 指令

    我们已经陆续使用过一些指令了,让我们看看全部的指令吧:

    generate 可缩写为 g

    产生各种不同类型的档案,例如

    1. bin/rails generate model person
    2. bin/rails g controller people

    console 可缩写为 c

    开启一个 Rails 主控台

    1. bin/rails console

    默认的环境是 developement,如果需要指定环境,请多输入环境名称即可,例如:

    1. bin/rails c production

    Rails也有提供沙箱模式(Sandbox),任何数据库的修改都会在离开时回复(原理是数据库Transaction):

    1. bin/rails c --sandbox

    在主控台中输入exit就会离开。

    server 可缩写为 s

    开启一个 Rails 服务器

    1. bin/rails s

    默认是使用 Port 3000 和 development 环境,如果需要指定:

    1. bin/rails s -p 4000 -e production

    new

    建立一个新 Rails 专案

    1. bin/rails new my_app

    将会建立一个叫做 MyApp 的 Rails 专案在 ./my_app 目录下。加上—database参数可以改变设定档的默认值,例如:

    1. bin/rails new my_app --database=mysql

    其他说明可以输入 rails 看到全部的指令。

    其他指令

    • dbconsole 开起一个数据库主控台 (可简写为 rails db),让你直接输入 SQL 指令。
    • destroy 删除 “generate” 所产生的档案
    • runner 在 Rails 环境中执行一段程式,例如 rails runner “puts Person.count”

    启动整个 Rails 程序(包括 rails server, rails runner, rails console 等) 时,会执行 application.rb 的应用程式设定,让我们来看看这个档案一些比较重要的部分吧。如果你对这个档案有修改,无论在什么模式下,都必须重新启动 Rails 设定才会生效。

    1. # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
    2. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
    3. # config.time_zone = 'Central Time (US & Canada)'

    设定默认的应用程式时区,默认是 UTC。在 Rails 中,数据库里面储存的时间皆为 UTC 时间,而设定此时区会自动帮你处理转换动作。例如设定 Taipei 的话,从数据库读取出来时会自动加八小时,存进数据库时会自动减八小时。

    1. # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
    2. # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
    3. # config.i18n.default_locale = :de

    设定默认的应用程式语系,默认是:en。我们会在”I18n 多国语系及时区”一章介绍如何使用。

    其他初始设定档(initialzers)

    如果将所有的设定都放到 application.rb 就太混乱了,所以非 Rails 核心的设定,我们会放在config/initializers目录下。这个目录下的所有.rb档案会在Rails启动时都会自动加载执行。默认产生的档案有五个:

    filter_parameter_logging

    设定 filter_paramsters 可以避免任何叫做 password 的参数值记录到 log 中,有效防止使用者的原始密码外洩到 log 档案。

    backtrace silencers

    可以让你选择性地移除例外追踪(exception backtrace)讯息,例如有些套件可能会很吵,妨碍你除错。

    inflections

    Rails 的命名惯例十分倚赖英文的单复数,例如将单数的类别名称 Person 转成复数的表格名称 people。Inflector 就是负责将字串转换成单复数的类别,虽然它内建了一些基本的转换规格,但是英文常常有例外的时候,你可以在这个档案中加上新的规格来做修正。如果你不太确定 Rails 转换的对不对,请进入 console 主控台试试看:

    1. $ rails c
    2. $ Loading development environment (Rails 3.2.8)
    3. $ > "Business".singularize => "Busines" # 转单数
    4. $ > "moose".pluralize => "mooses" # 转复数

    很不幸地这两个例子 Rails 都没转对,这时候你就可以利用 inflections.rb 来修正。

    Rails 核心不接受有关单复数转换的单字错误回报,毕竟它不是想做字典。

    mime_types

    Rails 默认支援了如下常见的标准 MIME(Multipurpose Internet Mail Extensions) 格式,MIME 被用在 HTTP 通讯协定中的请求标头 Accept 和回应标头 Content-Type 中,来说明此文件的格式。例如 Accept:application/xml,application/xhtml+xml,text/html; 和 Content-Type:text/html; charset=UTF-8。而 Rails 会在 Controller 的 respond_to 方法中辨识并回应所请求的格式样板,例如浏览器请求 application/xml 就会回应 xml 格式

    如果你需要客制,可以在这里注册。

    sesssion_store

    Rails 默认使用了 Cookie 来储存 Session 讯息。它会用上述的 key 编码之后,直接存放在使用者浏览器 Cookie 上。除了 Cookie Session,我们也可以使用 ActiveRecord 储存在数据库中。我们会在 Controller 一章中详细介绍及比较。

    config/secrets.yml

    这个设定档包括了乱数产生的一组secretkey_base用来编码需要保护的_Cookie讯息。修改这组 key 会让已经存放在使用者浏览器上的 Cookie Session 和 Signed Cookie 失效。你可以用来强制使用者需要重新登入。

    1. development:
    2. secret_key_base: 8dd8d723d33d474710ab65b....
    3. some_api_key: SOMEKEY
    4. test:
    5. secret_key_base: 175fa99b200ba23cf82fec6c....
    6. # Do not keep production secrets in the repository,
    7. # instead read values from the environment.
    8. production:
    9. secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

    除了本来的secretkey_base之外,你也可以用这个档案放其他设定,例如第三方应用的_KeyToken。上述的例子透过Rails.application.secrets.some_api_key方法就会回传SOMEKEY

    环境设定档

    我们在上一节”多重环境设定”曾经介绍不同环境会有不同的设定档,让我们来更深入看看有哪些设定值,以及这些值是如果影响 Development、Production 和 Test 环境的不同:

    Development 模式

    1. # In the development environment your application's code is reloaded on
    2. # since you don't have to restart the web server when you make code changes.
    3. config.cache_classes = false

    使用 Rails 开发可以快速的原因之一,就是当你修改一个小东西,只要重新整理浏览器就可以马上看到修改后的结果。这个秘诀就在于 cache_classes = false 会让每一次的 HTTP 请求都重新加载类别档案。更仔细的说,当这个值是 false 的时候,Rails 会改用 Ruby 的 load 方法,每次执行都会重新加载一次。相反地,如果这个值是 true,则会用 Ruby 的 require 方法,只会在第一次碰到的时候加载,之后碰到 require 相同的档案,就会自动忽略,也就是说如果你启动 Rails 后,档案有修改想看到结果,必须重新启动 Rails 才行,否则无法立即看到结果。

    1. # Show full error reports and disable caching
    2. config.consider_all_requests_local = true

    Rails只有在连线是来自本地端的时候,才会将发生错误时的Call stack trace资讯给浏览器显示。这个设定将所有连线都当做本地端连线,好让开发模式时所有人连线都可以看到错误讯息。

    1. config.action_controller.perform_caching = false

    是否启用 Controller 层级的快取(我们会在 Controller 一章介绍到有哪些快取方法),一般来说在开发模式不会启用,除非你要测试它。

    1. # Don't care if the mailer can't send
    2. config.action_mailer.raise_delivery_errors = false

    如果寄信失败,是否要丢出例外。建议可以改成 true。

    建议可以在开发模式设定 config.action_mailer.perform_deliveries = false,这样就不会真的寄信出去。我们会再 ActionMailer 一章详细介绍如何实作寄信功能。

    1. # Print deprecation notices to the Rails logger
    2. config.active_support.deprecation = :log

    随着 Rails 版本的升级,如果有方法会在之后的版本中移除,deprecation 会提示你如何因应。这里的 :log 表示会记录到 log 档案中。

    Production 模式

    1. # The production environment is meant for finished, "live" apps.
    2. # Code is not reloaded between requests
    3. config.cache_classes = true

    cache_classes = true 表示在 production 中,类别档案加载进内存中就快取起来了,大大获得效能。不像在 development 环境中每一次 HTTP 请求就会重新加载一次。

    1. # Full error reports are disabled and caching is turned on
    2. config.consider_all_requests_local = false
    3. config.action_controller.perform_caching = true

    不同于 development,如果在 production 环境出现例外错误,不会显示程式 call stack 讯息,而是回传 public/500.html 页面。

    1. # Disable Rails's static asset server (Apache or nginx will already do this)
    2. config.serve_static_assets = false

    “X-Sendfile” 是网页服务器提供的功能,可以让下载档案的动作完全委派给网页服务器,Rails 送出 X-Sendfile 标头后就毋需再佔住资源。

    1. # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
    2. # config.force_ssl = true

    是否限制全站必须SSL才能使用。

    1. # See everything in the log (default is :info)
    2. # config.log_level = :debug

    我们在 RESTful 应用程式 一章最后介绍了 Logger。这里可以设定 Logger 的层级。默认 production 是 :info,其他则是 :debug

    1. # Use a different logger for distributed setups
    2. # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)

    可以更换掉 Rails 内建的 Logger,例如换成使用 syslog 的 SyslogLogger

    设定不同的快取储存库,默认是 :memory_store,也就是每个 Rails process 各自用内存存放。业界最常用的则是 内存快取服务器。

    1. # Enable serving of images, stylesheets, and javascripts from an asset server
    2. # config.action_controller.asset_host = "http://assets.example.com"

    默认的静态档案位置是目前主机的 public 目录,你可以透过修改 asset_host 变更位置。例如你的静态档案放在不同台机器或 CDN(Content delivery network) 上。

    这就是为什么 Rails 在 View 中会使用 Helper 方法的原因之一,我们不会平舖直叙的写 ,而是使用 <%= image_tag(“rails.png”) %> 目的就在于透过程式来获得修改位置的弹性。其他还包括 stylesheets、javascripts 等静态档案都有 Helper 可以使用。

    1. # Disable delivery errors, bad email addresses will be ignored
    2. # config.action_mailer.raise_delivery_errors = false
    3. # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
    4. # the I18n.default_locale when a translation can not be found)
    5. config.i18n.fallbacks = true

    如果 I18n 翻译档找不到,则找用默认语系的文字。我们会在I18n一章详细介绍多国语系功能。

    1. # Send deprecation notices to registered listeners
    2. config.active_support.deprecation = :notify

    将 deprecation 讯息传到 Notifications 频道,你可以用以下程式去订阅这个讯息:

    1. ActiveSupport::Notifications.subscribe("deprecation.rails") do |message, callstack|
    2. # deprecation message
    3. end

    如果没有订阅的话,就什么事都不会发生。

    Test 模式

    1. # Show full error reports and disable caching
    2. config.consider_all_requests_local = true
    3. config.action_controller.perform_caching = false
    4. # Raise exceptions instead of rendering exception templates
    5. config.action_dispatch.show_exceptions = false

    不同于 development 或 production 碰到例外会捕捉例外后,给浏览器显示出 call stack trace 或 public/500.html 画面,在 test 模式就不处理,让例外直接爆出。

    1. # Tell Action Mailer not to deliver emails to the real world.
    2. # The :test delivery method accumulates sent emails in the
    3. # ActionMailer::Base.deliveries array.
    4. config.action_mailer.delivery_method = :test

    测试模式下不会真的去寄送email

    1. config.active_support.deprecation = :stderr

    让 deprecation 讯息会直接显示到视窗之中。

    数据库设定档 database.yml

    一个 Mysql 的设定档范例如下:

    1. development:
    2. adapter: mysql
    3. encoding: utf8mb4
    4. database: blog_development
    5. username: root
    6. password:
    7. production:
    8. adapter: mysql
    9. encoding: utf8mb4
    10. database: blog_production
    11. username: root
    12. password:
    13. test:
    14. adapter: mysql
    15. encoding: utf8mb4
    16. database: blog_test
    17. username: root
    18. password:

    Bundler 与 Gemfile 设定档

    Bundler 是管理应用程式 Gem 依存性(dependencies)管理工具,它会根据 Gemfile 的设定自动下载及安装 Gem 套件,并且帮你解决不同套件之间的依存关系,更重要的是,它可以让不同开发者之间和布署时,所有依存套件的版本都能够一致。

    在 Rails3 之后要使用的 Gems,都必须宣告在 Gemfile 设定档中,没写在里面的话,就算手动 require 也找不到。这跟 Rails2 以前可以直接 require 任意 rubygems 不同,在使用 Bundler 的环境中,要 require 什么 rubygems 必须透过 Gemfile 管理。

    Bundler 不只用在 Rails,其他例如 Sinatra 或是旧版 Rails2 也都可以使用

    Gemfile 的写法说明如下:

    1. # 第二个参数可以指定版本
    2. gem "rails", "5.1.0"
    3. # 也可以不指定版本,这样会安装最新的稳定版本 (不包括 .pre 或 .rc 结尾的版本)
    4. gem 'mysql2'
    5. # 如果 require 的档名不同,可以加上 :require
    6. gem 'yajl-ruby', :require => 'yajl'
    7. # 可以用 Git 当做来源(根目录要有 .gemspec 档案),甚至可以指定 branch, tag 或 ref。
    8. gem 'authlogic', :git => 'git://github.com/odorcicd/authlogic.git',
    9. :branch => 'rails3'
    10. # 也可以直接用电脑里的其他目录
    11. # gem "rails", :path => '/Users/ihower/github/rails'
    12. # Group 功能可以让特定环境才会加载
    13. group :development, :test do
    14. gem "rspec", "~> 2.0"
    15. gem "rspec-rails", "~> 2.0"
    16. end

    安装及更新 Gems

    如果你修改了这个档案,请执行 bundle install,这样 Bundler 就会检查并安装这些函式库,并产生一个 Gemfile.lock 档案。Gemfile.lock 档案会详细列出所有使用到的套件版本,你应该把这个档案也 commit 送进版本控制系统,这样其他开发者及上线的版本就都会安装完全一样的版本了。

    执行 bundle update gem_name 则会更新此 gem 的版本。bundle update 则会检查所有的 gem 更新到最新版本。一般来说你只需要在每次 Gemfile 修改后,执行 bundle install 即可。如果有套件关连性 bundle install 无法解决,它会提示你执行 bundle update。

    什么时候该执行 bundle install 或 bundle update 呢?一般来说,总是执行 bundle install 即可。这个指令只会做必要的更新到 Gemfile.lock,执行速度较快,它不会帮你升级现有的 Gem。而 bundle update 会重新产生整个 Gemfile.lock 档案,更新所有 Gem 到最新版本。但是,一次升级太多套件,可能会造成除错上的困难。因此会建议如果要升级,请执行 bundle update gem_name 一次升级一个套件。

    怎么知道可以升级哪些Gem呢?

    1. bundle outdated

    这个指令就会列出有新版本可以升级的gems

    如果你想知道打开套件的原始码,可以输入:

    1. bundle open GEM_NAME

    这样就会用默认的编辑器打开了。

    如何设定默认编辑器呢? 如果你使用MacBash的话,编辑~/.bash_profile加上export EDITOR="vim"这样就会用VIM当作默认编辑器。

    执行以下指令,会将所有用到的 Gems 打包进 vendor/cache 目录。如此执行 bundle install 时就不会连线到 http://rubygems.org 下载套件。

    1. bundle package

    什么时候需要用到这个功能呢?例如你希望布署的时候避免外部连线,或是你有非公开的 gems 不会上传到 网站上。

    如果你有非 Rails 的 script 需要执行(也就是放在 Gemfile 档案中的 Gem 所自行提供的执行档),使用 bundle exec 可以正确的加载 Bundler 的环境。例如 bundle exec rspec spec/

    在 Rails 中有一些命名上的惯例:

    类别命名与自动加载

    档名使用小写、单数,用底线区隔。例如当 Rails 看到一个 OrderItem 的类别或模组(Module),它会在Rails设定的自动加载目录(包括app/modelsapp/models/concerns等等)中去加载叫做 order_item.rb 的档案,也就是自动 require “order_item”。

    如果是有嵌套的类别或模组,例如 Admin::OrderItem,则会多一层目录,例如加载app/models/admin/order_item.rb 的档案,也就是自动 require “admin/order_item”。

    如果你想要新增目录到Rails设定的自动加载目录,可以编辑config/application.rb加上

    1. config.eager_load_paths += %W( #{config.root}/lib )

    这样就会自动加载lib目录了。如果你没有设定 eager_load_paths,或是你的档案没有依照惯例命名,那么你会需要在程式中手动 require 它。基本上,只要依照命名惯例,你不太需要在程式中写 require。

    eager_load_paths 目录是指 Rails 会自动根据命名惯例加载,而 Ruby 的 $LOAD_PATH 常数则是 require 时会寻找的目录。像 lib 这个目录 Rails 默认就只有加到 $LOAD_PATH 之中,所以你放在 lib 的档案是可以 require 到,但是因为默认没有加到 eager_load_paths 之中,所以没有自动加载的机制。

    Model 命名

    类别名称使用大写、单数,没有底线。而档名使用小写、单数,用底线。数据库表格名称用小写且为复数。例如:

    • 数据库表格 line_items
    • 档名 app/models/line_item.rb
    • 类别名称 LineItem

    Controller 命名

    假设有一个stores controller的话:

    • 档名 app/controllers/stores_controller.rb
    • 类别名称 StoresController

    如果需要将controllers档案做分类,这时候可以使用Module,将档案放在子目录下,例如后台专用的controllers

    • 档名 app/controllers/admin/stores_controller.rb
    • 类别名称 Admin::StoresController

    View 命名

    例如一个叫做 People 的 controller,其中的 index action:

    • 档名 app/views/people/index.html.erb
    • Helper 名称 module PeopleHelper
    • 档名 app/helpers/people_helper.rb

    Rails 元件导览

    Rails 包含许多个别的函式库元件:

    • Action Pack
    • Action Controller
    • Action Dispatch
    • Action View
    • Action Mailer
    • Active Model
    • Active Record
    • Active Support
    • Railties

    Action Pack

    Action Pack 是个包含 Action Controller、Action View 和 Action Dispatch 的 gem。也就是 “MVC” 中的 “VC” 部分。

    Action Controller

    Action Controller 是 Rails 应用程式中,管理 Controllers 的元件。Action Controller 框架处理传给 Rails 的 HTTP 请求,萃取出参数,然后分派给所属的 Action。Action Controller 还提供了 session 管理、样板演算显示(template rendering) 和 redirect 功能。

    Action View

    Action View 负责 Rails 应用程式中的 Views。它默认可以产生 HTML 或 XML 输出。Action View 负责样板的演算显示(template rendering),包括嵌套(nesting)或局部(partial)样板,甚至也内建支援一些 Ajax。

    Action Dispatch

    Action Dispatch 处理 HTTP 请求的路由(routing),它把 HTTP 请求发派(dispatch)到它该去的地方,也许是你的应用程式或其他 Rack 程式。

    Action Mailer

    Action Mailer 是个建构 E-mail 功能的框架。你可以使用 Action Mailer 来接收来信,或是使用样板来寄出纯文字或复杂的 multipart 信件。

    Active Model

    Active Model 在 Action Pack gem 和 ORM gem (例如 Active Record) 之间定义了一组接口。Active Model 允许 Rails 可以依你的需求把 Active Record 换成其他 ORM 框架。

    Active Record

    Active Record 是 Rails 应用程式中的 Models 基础。它不依存特定的数据库系统,提供了 CRUD 功能、先进的查询能力以及可以跟其他 Models 关联的本事。

    Active Support

    Active Support 是 Rails 里的工具函式库,它也扩充了一些 Ruby 标准函式库。除了被用在 Rails 核心程式中,你也可以在你的程式中使用。

    Railties

    Railties 是 Rails 的核心程式码,用来把以上各种的框架函式库以及 Plugin 全部组合在一起。

    更多线上资源