Bundler 的作用是为了管理 Ruby 项目中所需要的 Gem 包,确保在开发环境,测试环境和产品环境中 Gem 包的正确安装。

Gemfile 中声明依赖的 Gem 包

source 'https://rubygems.org'

gem 'nokogiri'
gem 'rails', '3.0.0.beta3'
gem 'rack',  '>=1.0'
gem 'thin',  '~>1.1'

source 说明了 Gem 包的安装源。声明依赖的 Gem 包,需要注意版本说明符,>= 1.0 的意思很显而易见,通过示例来说明 ~> 的含义,~> 2.0.3 等价于 >= 2.0.3 和 < 2.1~> 2.1 等价于 >= 2.1 和 < 3.0~> 2.2.beta 会匹配像 2.2.beta.12 这样的预发版本。

安装 Gem 包

运行 bundle install 会看到如下输出:

$ bundle install
Fetching gem metadata from https://rubygems.org/.........
Fetching additional metadata from https://rubygems.org/..
Resolving dependencies...
Using rake 10.3.1
Using json 1.8.1
Installing minitest 5.3.3
Installing i18n 0.6.9
...
Installing rails 4.1.0.rc2
Installing nokogiri 1.6.1
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.

Gem 包安装完成后,会在 Gemfile 的同级目录生成一个 Gemfile.lock,其中记录了所有 Gem 包的名称和版本,内容大致如下:

GEM
  remote: http://rubygems.org/
  specs:
    actionmailer (4.1.8)
        actionpack (= 4.1.8)
        actionview (= 4.1.8)
        mail (~> 2.5, >= 2.5.4)
    actionpack (4.1.8)
      actionview (= 4.1.8)
      activesupport (= 4.1.8)
      rack (~> 1.5.2)
      rack-test (~> 0.6.2)
    ...

PLATFORMS
  ruby

DEPENDENCIES
  bootstrap-sass (~> 3.3.4)
  cancan
  ...

Gemfile.lock 文件的内容不需要手动修改,改动 Gemfile 文件,运行 Bundler 相关命令,Gemfile.lock 文件的内容自然会变化,我们需要将 Gemfile 和 Gemfile.lock 加入到版本库中,其他程序员在获取项目代码,运行 bundle install 就会根据 Gemfile.lock 来安装精确版本的 Gem 包,和你的开发环境一模一样。

$ git add Gemfile Gemfile.lock

项目中加载通过 Bundler 安装的 Gem 包

require 'rubygems'
require 'bundler/setup'

# require your gems as usual
require 'nokogiri'

运行通过 Bundler 安装的 Gem 包中的执行脚本

$ bundle exec rspec spec/models

生成执行脚本的快捷方式

$ bundle install --binstubs
$ bin/rspec spec/models

Rails 3 中使用 Bundler

Rails 3 已经集成了 Bundler,你只需要修改 Gemfile,再运行 bundle install 就行了,但是有一个小问题需要注意,Rails 项目区分了开发,测试和产品环境,不同的环境中需要的 Gem 包自然是有所不同的,所以需要知道 Bundler 中 Group 的概念,典型的 Rails 项目中,Gemfile 内容如下:

gem 'mysql2'

group :development do
  gem 'capistrano', '~> 3.2.0', :require => false
  gem 'capistrano-rails', '~> 1.1', :require => false
  gem 'capistrano-bundler', '~> 1.1', :require => false
end

group :test do
  gem 'cucumber-rails', :require => false
  gem 'rspec-rails', '~> 3.0.0.beta'
  gem 'factory_girl_rails'
  gem 'selenium-webdriver'
end

没有写 group 的 Gem 包都在 default group,在通过 bundle install 安装 Gem 包时,我们可以指明不需要那个 group 的 Gem 包,命令如下:

$ bundle install --without test development

项目中加载通过 Bundler 安装的 Gem 包可以指明需要那个 group 的 Gem 包,方法如下:

Bundler.require(:default, Rails.env)

在 Rails 项目中的 application.rb 文件中,你会看到如下内容,你应该明白其中关于 Bundler 一段代码的含义了吧:

require File.expand_path('../boot', __FILE__)

require 'rails/all'

if defined?(Bundler)
  # Require the gems listed in Gemfile, including any gems
  # you've limited to :test, :development, or :production.
  Bundler.require(*Rails.groups)
end

module Kiwi
  class Application < Rails::Application
  ...
  end
end