前几天读到的一篇博客,觉得内容很详实,就翻译了下给大家分享下。绝大部分为直译,极少数地方加了点自己的注释,若有不周到地方,还望大家指出。如果有排版方面的问题,也请指出。

原文:http://tosbourn.com/what-is-the-gemfile/

作为Ruby开发者,我们一直在使用Gemfile,并且大部分人知道一些关于Gemfile的基础知识。在这篇文章里,我想更加深入到Gemfile里面去看看通过Gemfile所能做的一切。

什么是Gemfile

Gemfile是我们创建的一个用于描述gem之间依赖的文件。gem是一堆Ruby代码的集合,它能够为我们提供调用。你的Gemfile必须放在项目的根目录下面, 这是Bundler的要求,对于任何的其他形式的包管理文件来说,这也是标准。这里值得注意的一点是Gemfile会被作为Ruby代码来执行。当在Bundler上下文环境中被执行的时能使我们访问一些方法,我们用这些方法来解释gem之间的require关系。

创建Gemfile

首先我们要做的就是告诉Gemfile到那里去找到这些gems, 这就是gem的源。

我们使用#source方法来做这件事情

1
source "https://rubygems.org"

这里并不推荐一个项目有多个源。对于99%的项目,你的Gemfile的源都会被要求设置为https://rubygems.org,对于一个源,唯一的要求是它必须是一个合法的Rubygems的repo。

源的优先级

现在我们来探讨下关于gem源的优先级。 我们在Gemfile的顶部位置定义一个源的同时,我们也可以针对每个gem定义一个源。我们也能够为一个本地的gem定义一个路径或者是为gem定义一个git路径,比如说GitHub之类的(我们在后面点讲到这点)。

当Bundler尝试定位一个gem的时候,它会首先查看这个gem有没有显示的设置源,如果有,就先使用这个源。如果你在设置gem的时候有使用source, path或者git依赖的话,Bundler将会先在这些地方找,然后再去其他地方寻找。如果没有被显示设置的话, Bundler将会依照你Gemfile里面定义的源的顺序来找。如果一个gem能够在多个源里面被找到的话(虽然这是极为罕见的,因为你最好只定义一个源),你将会得到一个warning来提示你哪个源被使用了。

你能够使#source作为一个block来调用

1
2
3
4
source "https://my_awesome_source.com" do
   gem "my_gem"
   gem “my_other_gem”
end

带验证的源

有些源需要你使用验证才能够被设定。Bundler有一个设置选项使得你可以为每个源设置用户名和密码

bundle config my_gem_source.com my_username:my_password

这是任何希望通过Bundler来安装gem都必须要的因为它不会被放入版本管理里面。你也可以直接在Gemfile中设置你的验证信息,当然,这些验证信息也会被commit进你的版本管理工具。如下所示

source "https://username:password@my_gem_source.com" 你在源里面的设置,都会被你以bundle config的方式设置的东西所覆盖。

设置Ruby信息

如果你的应用程序需要使用一个特别的Ruby版本或是引擎,我们都能够在Gemfile里面进行设置。

1
ruby "1.9.3", :patchlevel => "247", :engine => "jruby", :engine_version => "1.6.7"

当设定这个的时候,需要的唯一点信息就是ruby的版本(我们这里使用1.9.3) * :pathlevel 声明了Ruby的patch level * :engine 声明了使用的Ruby引擎 * :engine_version 声明了引擎的版本 (如果这个被设置了,engine也需要被设置)

设置Gems

现在我们到了Gemfile的核心,设置你的gems。最基本的语法如下:

gem "my_gem"

这里my_gem是 gem的名字,gem的名字是唯一要求的参数,此外还有几个可以选择的参数可以使用。

设置Gem的版本

对于一个gem,你最常做的事情就是设置它的版本,如果你不设置版本的话,你也可以说任意的版本都可以。

gem "my_gem", ">= 0.0"

这里有7个操作符供你用来设置你的gem

1
2
3
4
5
6
7
8
* = Equal To "=1.0"
 * != Not Equal To "!=1.0"
 * > Greater Than ">1.0"
 * < Less Than "<1.0"
 * >= Greater Than or Equal To ">=1.0"
 * <= Less Than or Equal To "<=1.0"
 * ~> Pessimistically Greater Than or Equal To "~>1.0"
 * ~> Pessimistically Greater Than or Equal To

~> 操作能够让你使用这个gem的未来的某个安全的版本。如果你觉得使用一个大的版本更安全,你能够像下面这样声明.

gem "my_gem", "~> 2.0"

这能够允许你安装任意的2.x版本的gem,但是3.x版本是不被允许的。或许你对这么宽泛的版本感到不爽,你也可以声明一个更具体的版本,如下

gem "my_gem", "~> 2.5.0"

这能够让你使用2.5.0到2.6.0之间的版本。下面的例子能够让你更加理解~> 操作符

1
2
3
* gem "my_gem", "~> 1.0" –> gem "my_gem", ">= 1.0", "< 2.0"
   * gem "my_gem", "~> 1.5.0" –> gem "my_gem", ">= 1.5.0", "< 1.6.0"
   * gem "my_gem", "~> 1.5.5" –> gem "my_gem", ">= 1.5.5", "< 1.6.0"

设置gem被required

如果你使用Rails的话,这点小技巧可能被隐藏了,但是在你的config/application.rb文件里面你能看到这么一行代码。

Bundler.require(:default, Rails.env)

它的意思是require所有没有被放入group(后面会讲到这个概念)里面的gems和所有放入和当前rails环境(RAILS_ENV, development, test, production)同名的group里面的gems。

默认方式下,如果你在Gemfile里面包含一个gem,当Bundler.require被调用的时候会被包含进来。我们也能通过下面的设置让gem不被包含进来(译者注释:这样你就只能安装这个gem,在使用的时候必须在你的代码里手动的添加require ‘my_gem’来调用my_gem里面的方法了。为什么需要这样呢,因为并不是所有的地方都需要使用这个gem,比如你在rake task里面使用了my_gem, 而其他地方没有使用,故你只需要在这个gem require到task里面,避免了所有的进程都把这个gem加载进去)

gem "my_gem", require: false

当然你也可以指定哪些文件夹被required的,如下:

gem "my_gem", require: ["my_gem/specific_module/my_class", "my_gem"]

这点在当你的gem有很多功能的,你必须每次手动require的时候非常有用。

gem分组

正如我上面提到的一样,一个gem可以属于一个或多个group,当它不属于任何group的时候,它被放入了:default group。 有两种方法你可以对一个gem分组。第一种是对group属性进行赋值,如下所示:

gem "my_gem", group: :development

它的意思是,这个gem只在development环境下被require。这也意味着当你在安装gems的时候,你可以指定某个group下面的gems不被安装,这样在一定程度上能加快gem的安装。

bundle install --without development test

上面的意思是安装除development和test group意外的所有gems。 第二种gem分组的方法就是你可以将gems放入一个block里面,如下所示:

1
2
3
4
group :development do
   gem "my_gem"
   gem "my_other_gem"
end

这看上去更美观,并且你也可以设置多个group。

1
2
3
4
group :development, :test do
  gem "my_gem"
  gem "my_other_gem"
end

如果你想让某个group变成可选的形式,你也可以像下面这样,设置optional: true

1
2
3
4
group :development, optional: true do
   gem "my_gem"
   gem "my_other_gem"
end

当上面被设置时,为了安装development group下面的gems,需要运行bundle install —with development

设置gem的平台

如果某个gem只能在某个平台上使用,你也可以在gemfile里面设置。平台的原理和group很类似,但不同的是你不需要去通过—without这样的option去指定,它会自动根据平台判断执行。

gem "my_gem", platform: :jrubygem "my_other_gem", platform: [:ruby, :mri_18]

下面是一个不同平台的list。

1
2
3
4
5
6
7
8
9
10
11
* ruby – C Ruby (MRI) or Rubinius, but not Windows
   * ruby_18 to ruby_22 – ruby & (version 1.8 .. version 2.2)
   * mri – Same as ruby, but not Rubinius
   * mri_18 to mri_22 – mri & (version 1.8 .. version 2.2)
   * rbx – Same as ruby, but only Rubinius (not MRI)
   * jruby – JRuby
   * mswin – Windows
   * mingw – Windows 32 bit mingw32 platform (aka RubyInstaller)
   * mingw_18 to mingw_22 – mingw & (version 1.8 .. version 2.2)
   * x64_mingw – Windows 64 bit mingw32 platform
   * x64_mingw_20 to x64_mingw_22 – x64_mingw & (version 2.0 .. version 2.2)

我发现平台真的非常有用,当一个开发团队在不同平台开发的时候。当你team的一个开发者使用的是Windows平台的时候,你可能需要不同版本的gem来支持。我经常使用下面的block语法来使用platform设定。

1
2
3
4
platforms :jruby do
  gem "my_gem"
  gem "my_other_gem"
end

设置gem的源

ok,现在我们来讲设置gem的源,如下所示:

gem "my_gem", source: "https://my_awesome_gemsite.com"

如果这个my_gem 在source里面找不到的话,Bundler也不会去default的源里面找,所以找不到的情况下这个gem就不会被安装。

从git安装gem

你可以设置gem的安装源为一个git repo,比如GitHub, 这只需要你将source属性替换为git。你可以设置这个repo的链接为HTTP(S), SSH, GIT等协议,但最好使用HTTP(S)和SSH,因为其他的会使你可能成为man-in-the-middle攻击的受害者。如果你把gem放入到repo里面,你必须要在repo根目录文件夹下面有一个.gemspec 文件。这里面需要包含一个合法gem的声明。如果你没有提供这个文件,Bundler会尝试创建一个,但是他不会被依赖。如果你尝试去include一个没有提供.gemspec文件的git repo里面的gem,你必须指定一个版本号。

你可以为gem设置branch,tag,ref,默认是使用master branch。你也可以强制Bundler扩展submodule,通过以下方式来设置:

gem "my_gem", git: "ssh@githib.com/tosbourn/my_gem", branch: test_branch, submodules: true

如果你有多个gem来自同一个git repo,你也可以通过下面block形式组织起来。

1
2
3
4
git "git@github.com:tosbourn/my_gems.git" do
  gem "my_gem"
  gem "my_other_gem"
end

设置Git作为source

你可以设置一个URL来作为一个更广义的源,你可以通过调用#git_source方法并将name作为参数传进去,以及一个接收一个参数的block,并返回一个string作为repo的URL。如下所示:

git_source(:custom_git){ |repo| "https://my_secret_git_repos.com/#{repo}.git" }
gem "my_gem", custom_git: "tosbourn/test_repo"

BitBucket和Github的helper method

因为BitBucket和Github都是比较流行的git repo host,所以有两者的helper method。在两者里面,Bundler都默认repo是public的。

gem "my_gem", github: "tosbourn/my_gem" 
gem "my_gem", bitbucket: "tosbourn/my_gem"

你也可以设置两者的branch。当用户名和repo名字一致的时候,可以省略一个。

gem "rails", github: "rails"
gem "rails", bitbucket: "rails"

注意:在Bundler 2出来之前,你不能使用:github这个参数,目前它是使用git://协议的,就是前面讲过的可能会受到man-in-the-middle攻击的。还有一个helper :gist, 如果你Github上是以gist的形式存放的话就能够使用它。你可以只使用gist ID作为path,也可以像:github, :bitbucket那样传入:branch参数。

gem "my_gem", :gist => "5935162112", branch: "my_custom_branch"

用path包含本地Gem

你可以通过传入:path参数来依赖你本地的gems。

gem "my_gem", :path => "../my_path/my_gem"

如果你传入一个相对路径的话(如上),这个路径是相对于你Gemfile的路径的。如果你想把某个文件夹下所有的gems都包含进去的话,你可以使用如下的block。

1
2
3
4
path "../my_path/gems" do
  gem "my_gem"
  gem "my_other_gem"
end

有一点值得注意的是,如果你使用的是path的话,Bundler是不会编译c extension的。

选择性的安装gems

有时候你想在某个前提条件被满足的情况下安装这个gem,比如你系统里面是否有某个程序。下面这个方法能够接收一个proc或lambda,下面的例子中我们将在你的系统是mac的时候安装这个gem

1
2
3
install_if -> { RUBY_PLATFORM =~ /darwin/ } do
   gem "my_osx_gem"
end

结束语

谢谢你的阅读并希望它能对你有所帮助,如果我有什么遗漏或你有什么问题的话请联系我~


转载自 ruby-china