msn email google-talk twitter tumblr flickr

asciicasts翻译:201-Bundler

在本篇中我们将继续关注Rails 3的新特性。这次我们将了解bundler——rails应用中全新的管理gem依赖的方式。

Bundler最近的更新相当的频繁,所以当你准备用它做些什么之前请确保你安装了最新版本。

gem install bundler

可能你注意到了,当我们安装gem的时候我们并没有使用sudo,那是因为我们使用安装在RVM中的一个RUBY版本。

使用Bundler安装Gems

在过去的教程中,当我们试着去启动服务器但是它报出缺少sqlite3-ruby gem的提示,我们一般是手工的使用gem install安装gem。

gem install sqlite3-ruby

我们可以在应用的Gemfile里加上sqlite3-ruby,让它变成我们应用的bundler依赖(绑定依赖 bundler也有捆绑 打包的意思)然后就可以用Bundler来替代完成这项工作了。运行bundle install语句来遍历应用的gem依赖并安装它们。

$ bundle install
    Fetching source index from http://gemcutter.org
    Resolving dependencies
    Installing abstract (1.0.0) from system gems
    Installing actionmailer (3.0.0.beta) from system gems
    Installing actionpack (3.0.0.beta) from system gems
    Installing activemodel (3.0.0.beta) from system gems
    Installing activerecord (3.0.0.beta) from system gems
    Installing activeresource (3.0.0.beta) from system gems
    Installing activesupport (3.0.0.beta) from system gems
    Installing arel (0.2.1) from rubygems repository at http://gemcutter.org
    Installing builder (2.1.2) from system gems
    Installing bundler (0.9.5) from system gems
    Installing erubis (2.6.5) from system gems
    Installing i18n (0.3.3) from system gems
    Installing mail (2.1.2) from system gems
    Installing memcache-client (1.7.8) from system gems
    Installing mime-types (1.16) from system gems
    Installing rack (1.1.0) from system gems
    Installing rack-mount (0.4.7) from rubygems repository at http://gemcutter.org
    Installing rack-test (0.5.3) from system gems
    Installing rails (3.0.0.beta) from system gems
    Installing railties (3.0.0.beta) from system gems
    Installing rake (0.8.7) from system gems
    Installing sqlite3-ruby (1.2.5) from system gems
    Installing text-format (1.0.0) from system gems
    Installing text-hyphen (1.0.0) from system gems
    Installing thor (0.13.1) from rubygems repository at http://gemcutter.org
    Installing tzinfo (0.3.16) from system gems
    Your bundle is complete!

我们可以从上面的输出中看到bundler已经安装了所有本应用需要的gem,包括sqlite3-ruby。如果还没有安装Bundler的话可以从Rubygems.com下载并安装。

所以,当你拿不准时,运行bundle install吧。例如当你新建一个Rails应用或clone别人的应用时你应该这么做,这样你就可以保证你拥有的都是正确的gem。当你部署一个应用的时候你该这么做,这样服务器上装的就都是没问题的gem了。你甚至可以把这句加到你的部署文档(deployment recipe 译注:应该是类似于capistrano的capfile的东东)中去,这样每当你部署的时候它就会运行。

需要特别注意的是不要在运行bundle install的时候加上sudo,即使你平时都是用sudo来安装gem的也不要这么做。

添加Gem依赖

现在我们知道了如何去管理gem,那么我们该如何安装及添加新的gem依赖呢?在之前版本的rails中我们在/config/environment.rb文件中管理这些依赖,但是到了Rails 3中gem 配置被单独安置到项目根目录下的一个名叫Gemfile的文件中。初始的Gemfile看起来是这样的:

   1      # Edit this Gemfile to bundle your application's dependencies.     
   2      source 'http://gemcutter.org'    
   3  
   4  
   5      gem "rails", "3.0.0.beta"    
   6  
   7      ## Bundle edge rails:     
   8      # gem "rails", :git => "git://github.com/rails/rails.git"     
   9  
  10      # ActiveRecord requires a database adapter. By default,     
  11      # Rails has selected sqlite3.     
  12      gem "sqlite3-ruby", :require => "sqlite3"    
  13  
  14      ## Bundle the gems you use:     
  15      # gem "bj"     
  16      # gem "hpricot", "0.6"     
  17      # gem "sqlite3-ruby", :require => "sqlite3"     
  18      # gem "aws-s3", :require => "aws/s3"     
  19  
  20      ## Bundle gems used only in certain environments:     
  21      # gem "rspec", :group => :test     
  22      # group :test do     
  23      #   gem "webrat"     
  24      # end

这个文件中已经包含了几个gem,其中包括rails和sqlite3-ruby。可能你已经注意到关于引用到rails gem的那一行包含了版本号。其实在这个文件中引用任何gem都可以指定一个版本号,这样我们就可以引入该gem的某一个特定的版本。如果在某个阶段我们想要升级rails的版本,在这里修改版本号是比在environment.rb中修改更好的选择。

如果我们希望在项目中使用其他的gem只需要将它们加到这个文件中。举个例子,我们希望使用will_paginate可以像这样引用它:

gem "will_paginate", ">=2.3.12"  

第二个参数是版本号,在这里的意思是让bundler安装2.3.12版或者是能找到的更新的版本。只要我们添加了gem引用我们就能通过bundle install命令来安装gem,但在我们做那之前先来示范另一个bundle命令:bundle check

我们可以运行bundle check来列出我们的应用需要的但并没有安装的gem。现在如果为我们的项目运行它就会显示出will_paginate gem是项目已经引用但还没有安装的。

$ bundle check
    The following dependencies are missing
      * will_paginate (>= 2.3.12, runtime)We can install the missing gems by running bundle install again.

查看其他bundle的可用命令我们运行bundle help

$ bundle help
    Tasks:
      bundle check        # Checks if the dependencies listed in Gemfile are satisfied by currently installed gems
      bundle exec         # Run the command in context of the bundle
      bundle help [TASK]  # Describe available tasks or one specific task
      bundle init         # Generates a Gemfile into the current working directory
      bundle install      # Install the current environment to the system
      bundle lock         # Locks the bundle to the current set of dependencies, including all child dependencies.
      bundle pack         # Packs all the gems to vendor/cache
      bundle show         # Shows all gems that are part of the bundle.
      bundle unlock       # Unlock the bundle. This allows gem versions to be changed
其他的Gemfile选项

让我们回到Gemfile看看还能在里面写些什么。一个很酷的新特性是能够从一个git仓库中取得gem。举个例子,如果我们希望在我们的项目中使用最新的rails版本我们可以指定rails gem到它的git仓库。

   1  # Bundle edge rails:     
   2  gem "rails", :git => "git://github.com/rails/rails.git"

这是个非常强大的功能。如果一个gem没有像我们的预期那样工作我们就可以在Github上建立分支、修改代码来满足需求,并且指定gem依赖到我们自己的版本。

我们同样可以为某一环境指定gem。例如我们我们希望使用RSpec测试,那么我们就可以传入:group选项指定它只在测试环境中安装。

   1      gem "rspec", :group => :test

当有一定量的gem要被指定给某一个环境时可以使用group并传给他一个block(代码块)。

   1      group :test do    
   2        gem "webrat"    
   3        get "rspec"    
   4      end

如果我们现在运行bundle install它将会为所有环境装上其所指定的gem。

$ bundle install
    Fetching source index from http://gemcutter.org
    Resolving dependencies
    ...
    Installing rspec (1.3.0) from rubygems repository at http://gemcutter.org
    ...
    Installing webrat (0.7.0) from rubygems repository at http://gemcutter.org
    Installing will_paginate (2.3.12) from system gems
    Your bundle is complete!

正如你所看到的,从以上的输出(删节过)中我们可以看到我们指定的两个gem已经安装上了。

如果我们我们希望安装除了某一环境之外指定的gem之外的所有gem,可以使用--without选项。例如,除了test环境特定的gem都安装我们可以这么做:

bundle install --without=test

当你在安装应用到产品服务器(production server)时并不想装上所有只在测试环境中使用的gem,此时这个功能就派上大用场了。

锁定gem

另一个有用的命令是bundle lock。这将锁定(你的应用中)用到的gem的特定版本。当我们运行它之后在项目的中会生成一个名叫Gemfile.lock的新文件。这个文件将列出所有安装的gem及(当前使用的)版本号。当运行过bundle lock命令后,我们再运行bundle install命令时安装的gem将是Gemfile.lock中锁定的版本,即使已有更新的版本可用(也只安装锁定时候的版本)。

你可能在想,什么时候才需要用到bundle lock。其实,在项目需要被移动的时候这么做都是值得的。如果我们正与其他的Rails开发人员协作开发一个项目,使用bundle lock将保证开发这个应用时每个人使用的都是相同版本的gem(译注:避免了很多因各自开发环境不同而导致的莫名奇妙的问题)。同样适用的情景还有,当应用即将被部署到服务器时我们希望生产服务器安装的gem版本与开发机器上的完全一样的版本。

如果我们需要在运行了bundle lock之后更改应用的gem,不要直接更改Gemfile.lock文件,我们只需要像以前一样的更新Gemfile文件就行了。因为Gemfile被锁住了,所以当修改好了Gemfile文件,再运行bundle install命令时却并不能更新应用的gem。要想更新gem我们需要加上--relock选项。

bundle install --relock

这一操作将解锁gemfile,升级gem后再重新上锁。

将Gem与应用打包到一起

以上就是bundler的基本工作流程了,足以应付你工作中的绝大部分需要。但我们最后还要了解一个bundler特性,那就是bundle pack。

Bundler会把gem都安装到我们的home目录下一个叫.bundle的文件夹中。

$ ls ~/.bundle
    cache doc gems ruby specifications

那就意味着gem跟我们的应用并不绑定,而且也不被包含在我们项目的版本控制体系里。如果我们希望将gem保存到我们的项目里的话我们就需要运行bundle pack了。

bundle pack
    Copying .gem files into vendor/cache
      * memcache-client-1.7.8.gem
      * rspec-1.3.0.gem
      * thor-0.13.3.gem
      * tzinfo-0.3.16.gem
      * builder-2.1.2.gem
      * nokogiri-1.4.1.gem
      * mime-types-1.16.gem
      * sqlite3-ruby-1.2.5.gem
      * i18n-0.3.3.gem
      * abstract-1.0.0.gem
      * erubis-2.6.5.gem
      * text-hyphen-1.0.0.gem
      * bundler-0.9.6.gem
      * rake-0.8.7.gem
      * will_paginate-2.3.12.gem
      * text-format-1.0.0.gem
      * rack-1.1.0.gem
      * rack-test-0.5.3.gem
      * webrat-0.7.0.gem
      * rack-mount-0.4.7.gem
      * activesupport-3.0.0.beta.gem
      * mail-2.1.2.gem
      * arel-0.2.1.gem
      * activemodel-3.0.0.beta.gem
      * actionpack-3.0.0.beta.gem
      * actionmailer-3.0.0.beta.gem
      * activerecord-3.0.0.beta.gem
      * activeresource-3.0.0.beta.gem
      * railties-3.0.0.beta.gem
      * rails-3.0.0.beta.gem

这将生成所有我们应用程序需要的.gem文件,并将它们拷贝到/vendor/cache(如果没有这个目录将会创建)下。(以后)这些(被打包的)gem将会直接从这些.gem文件安装而不再从远端获取,举个例子,Gemcutter。

这将不是一个常用的操作,但是设身处地的想象下如果你不希望生产环境直接连接到Gemcutter去下载gem这就是一个非常好的解决方案了(译注:例如一些重要的内部系统会不允许连接外网,这种打包就非常实用了)。

以上就是我这一集所介绍的了。简单的介绍了如何在你的应用中使用bundler管理gem。Bundler可能是需要费点神来习惯的,但是最终它将保证你远离过去的那些混乱的gem依赖关系的。

最后我鼓励大家用空去http://railsplugins.org逛逛。可能你正好奇着想知道哪些plugin和gem已经支持了Rails 3,在这里就可以找到答案。在这个网站你就可以查到plugin是否已经支持了Rails 3和Ruby 1.9。

如果在这个网站上发现你即将用在Rails3上的gem是被列为“不支持”或“未经过测试”的,那么这就是个非常好的机会去提问题,或者你也可以帮忙修复它。总之,通过这种方式大家都可以参与其中,好让更多的gem和plugin能与Rails 3协作。

E201I01.png

仅提供给英语困难的同学参考用,英文好的同学还是直接看原版教程。如有错误请发邮件到joey.d.darko@gmail.com

本篇是我参加蜗牛同学组织的asciicasts中文翻译计划中的一篇.目前已经发布在asciicast.com.自己也留一份.