elephant

赫本之后 再无女神

The Rails Way 翻译 第一章

1 Rails 环境与配置

Rails之所以获得很多关注和参与,是因为我不去取悦那些不愿意分享问题的人。生产环境和开发环境的区别对我来说是个大问题,所以我用我所知的最佳方式解决这个问题

–dhh

Rails预制了3种标准运行模式:developments,test以及production。这些基本的模式拥有一些标准的配置比如:连接那个数据库、是否对每个请求重新加载类。如果有必要,创建你自己的运行模式也很简单。

当前的环境由环境变量RAILS_ENV指定,RAILS_ENV对应定义的运行方式并决定加载config/enviroments下定义的配置文件,你可以设置环境变量决定运行方式,或者使用默认的production模式。因为环境配置项决定Rails最重要的表现例如类文件的加载,为了真正了解Rails的运行方式,你应该理解这些环境配置。

在本章,我们以Bundler开始,Bundler是一个管理ruby程序gem依赖的工具。Bundle有一个gem清单,他可以安装清单中的gems,以及这些gems所依赖的gems。之后我们通过研究boot.rb和application.rb以及三个标准模式的配置,理解Rails的启动和请求处理过程。我们也会讲到自定义运行环境的基础知识以及为什么自定义运行环境。

本书不是以最新的Rails进行书写,为了理解本书你最少必须知道如何启动Rails程序以及理解MVC的意义。如果不符合,我建议你先看看Michael Hartl书写的Ruby on Rails Tutorial website一书。

1.1 Bundler

Bundler不是专为Rails设计的技术,但他是管理应用程序gem依赖的优先方式。Rails4依赖并默认会使用Bundler,你不必单独安装bundler这个gem

因为我们觉得你应该使用Bundler,如何不使用Bundler只是作为练习以及为不循常规的人准备。

Bundler最重要的是一次解决配置中所有的gem依赖。这不同于Rubygems和之前Rails使用的一次解决一个(one-at-a-time)的依赖解决方案,我们通过下面的难以修正的问题来说明。

假设你的系统安装了下列版本的rubygem

  • activesupport 4.0.2
  • activesupport 3.2.11
  • activemerchant 1.29.3
  • rails 3.2.11

因为activemerchant 1.29.3依赖 activesupport >= 2.3.14,所以当你用下面的gem 命令加载时

1
gem 'activemerchant', '1.29.3'

这会加载activemerchant,同时加载了最新版本的依赖gem,activesupport 4.0.2 gem,因为他比2.3.14版本高,之后当加载rails时:

1
gem 'rails', '3.2.11'

运行时出现了如下错误

can’t activate activementsupport(=3.2.11,runtime) for[“rails-3.2.11”],already activated activesupport-4.0.2 for [“activemerchant-1.29.3”]

因为activemerchant对activesupport有广泛(broader)依赖,而旧版本的Rails却依赖较窄[narrow]的依赖。Bundler依靠一次评价并获取正确的gem版本来解决这个问题

想了解有关Bundler构思出来的有趣设想请阅读 Yehuda’s blog post on the subject

1.1.1 Gemfile

定位到Rails项目的根目录下,有一个gem的清单文件-Gemfile,该文件基于ruby。Gemfile列出了Rails项目的所有依赖,包括正在使用的Rails版本。Gemfile的语法超级简单:

1
2
gem 'kaminari'
gem 'nokogiri'

把依赖写到指定一个或多个环境名作为符号的块中,就只在指定的运行环境下才加载这些依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
group :development do
    gem 'pry-rails'
end

group :test do
    gem 'capybara'
    gem 'database_cleaner'
end

group  :development, :test do
    gem 'rspec-rails'
    gem 'factory_girl_rails'
end

如果你从Rails3升级,记住Rails4不再为asset pipeline依赖的包使用assets分组,你需要把移除assets group分组

gem指令第二个参数为可选的,他指定了需要gem的特定版本,忽略该参数会自动获取最新稳定版本的gem,可能不是可用的最新版本,你需要指定确切的版本才能加载特定版本或者预览版。

版本参数的格式你可能已经熟悉

1
2
3
4
gem 'nokogiri', '1.5.6'
gem 'pry-rails', '> 0.2.2'
gem 'decent_exposure', '~> 2.0.1'
gem 'draper', '1.0.0.beta6'

你可以在RubyGems documentation 找到版本字符参数的完整说明

有时候gem有特定的需求才能使用,不同于gem repo中的声明,这此种情况下,require选项很容易地解决

1
gem 'webmock',require:'webmock/rspec'

1.1.1.1 从git库加载gems

到现在为止我们已经学会了从http://rubygems.org 加载gem。我们也可以从源代码库加载特定的gem,只要他的根目录下有.gemspec文件。只需在调用gem时添加:git选项。

1
gem 'carrierwave', git: 'git@github.com:carriverwaveuploader/carrierwave.git'

如果gem的源代码库是在Github上托管并未public,你可以使用:github这个简便选项

1
gem  'carriverwave', github: 'carriverwaveuploader/carrierwave'

也支持二进制或者c扩展的gemspec

1
gem 'nokogiri', git: 'git://github.com/tenderlove/nokugiri.git'

如果gem的源代码库根目录下没有.gemspec文件,你必须指定确定的版本

1
gem 'deep_merge' ,'1.0', git: 'git://github.com/peritor/deep_merge.git'

如果gem源代码库有多个.gemrspec文件,你可以指定使用特定的ref,branch,或者tag:

1
2
3
gem 'rails', git 'git://github.com/rails/rails.git', ref: '4aded'
gem 'rails', git 'git://github.com/rails/rails.git', branch: '3-2-stable'
gem 'rails', git 'git://github.com/rails/rails.git', tag: 'v3.2.11'

1.1.1.2 从文件系统中加载gems

使用 :path选项,你可以使用本地开发中的gem

1
gem 'nokogiri', path: '~/code/nokogiri'

1.1.2 安装gems

每次修改Gemfile,或者更具体地说,如果引入了未安装的依赖,运行bundle来确保Gemfile的所有依赖都可用。

1
2
$ bundle install
#some information

安装指令会在不导致冲突的情况下安装Gemfile下的所有gems至最新版本 你可以使用 –without 选项来不安装指定运行模式下的gems

1
2
$ bundle install --without development test
$ bundle install --without test

1.1.3 gem锁

每次运行 bundle install 或者 bundle update,Bundler计算所有的依赖关系并将结果存在一个名为Gemfile.lock的文件中。从这点看来,Bundler只会加载Gemfile锁定时所指定版本,你知道可以正确运行的gems。

Note Gemfile.lcok文件应该加入版本控制中,以确保每个电脑运行该程序时使用指定的相同版本的gems

为了说明这点的重要性,想象一下在发布时Gemfile.lock丢失。因为依赖关系不存在了,Bundler只能在发布的机器上重新指定Gemfile中的gem版本,这会导致安装你未测试的新版本的gem,导致无法预料的问题。

1.1.4 打包gems

你可以在你的rails应用程序中把vendor/cache下所有的gem打包

1
$ bundle package

在拥有打包的gems的应用中运行 bundle install –local会安装包内的gems,而不会连接rubygems.org或其他gems源。你可以在发布时使用这点来避免外部的依赖、或者你依赖在public代码库不可用的私有gems。

确保gem依赖在非rails脚本中可用

非Rails脚本必须运行过Bundle exec来获得正确的初始运行环境。 $ bundle exec guard Rails 4在生成新项目时会为rails执行环境在bin目录中生成binstubs,binstub是包含了bundle运行上下文执行环境的脚本。这意味着你不用每次执行都要加上bundle exec。binstubs是rails 4的一等公民,应该加入版本控制代码中。 默认情况下,下边的stub在每个rails新项目中都可用: * bin/bundle * bin/rails * bin/rake * bin/spring

从rails 3升级 如果你从rails3升级且在以前使用Bundler生成过binstubs,你必须运行下列命令以升级binstubs

1
2
3
bundle config --delete bin#关闭Bundlers的stub生成器
rake rails:update:bin #使用rails4的新的指令
git add bin #把bin文件加到版本控制中

使用bundle binstubs some-gem-name可以为bundler加入可执行的binstub,示例如下:

1
$ bundle binstubs guard

可在bin目录下创建guard bistub:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/env ruby
#
# This file was generated by Bundle.
#
# The application 'guard' is installed as part of a gem,and this file is here to facilitate(正式) running it
#
require 'pathname'
ENV['BUNDLE_GEMFILE']  ||=
File.expand_path("../../Gemfile", Pathname.new(_FILE_).realpath)

require 'rubygems'
require 'bundler/setup'

load Gem.bin_path('guard','guard')

使用binstubs,bin目录下的脚本可以直接运行

1
$ bin/guard

1.2 rails启动以及应用配置

当你使用rails启动进程(比如运行rails server)以处理请求时,首先执行的是 config/boot.rb的加载,在rails栈的启动中,将会涉及三个文件: * boot.rb Bunlder和加载路径的建立 * application.rb 加载rails的gems,特定运行环境下的gems,配置应用程序 * enviroment.rb 执行所有的安装

1.2.1 application.rb

config/application.rb是rails程序配置的所在,他是在config/enviroment.rb之前唯一被加载的。 让我们一步一步看看创建rails程序的默认配置。顺便说下,改变这些配置文件需要重启程序才能起作用。