Ruby Gemfile 详解

jackxu · 发布于 2015年07月27日 · 最后由 Holy 回复于 2016年09月23日 · 9093 次阅读
19891
本帖已被设为精华帖!

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

原文: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方法来做这件事情

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来调用

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里面进行设置。

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

* = 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之间的版本。下面的例子能够让你更加理解~> 操作符

* 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里面,如下所示:

group :development do
   gem "my_gem"
   gem "my_other_gem"
end

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

group :development, :test do
  gem "my_gem"
  gem "my_other_gem"
end

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

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。

* 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设定。

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形式组织起来。

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。

path "../my_path/gems" do
  gem "my_gem"
  gem "my_other_gem"
end

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

选择性的安装gems

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

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

结束语

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

共收到 31 条回复
1680

你的Gemfile的源都会背要求设置为

"背" -> "被"

19891

#1楼 @justin 谢谢,已经修改过来了

835

感谢楼主翻译!

我发先平台真的非常有用

"发先" -> "发现"

19891

#3楼 @zhaowenchina 谢谢,已修改,以后打完了还要多检查两遍错别字,哈哈

68

]]> Greater Than ">1.0" 笔误。写得很好。

19891

#5楼 @gazeldx 谢谢,已修~

1638

这篇文章又系统,又通俗,真是篇好文章。

835

还有一个小问题,有很多代码块中应该换行的地方写成了一行 类似下面这种 😄

group :development do
   gem "my_gem"  gem "my_other_gem"
end
19891

#8楼 @zhaowenchina 确实,是我从自己笔记本拷贝到这里来的时候没有注意,哥们你真细致。已修_^

10楼 已删除
15420

翻译的很清楚 :plus1:

17467

good job

19891

#11楼 @pathbox 谢谢肯定,若有不足,还望指正~

9618

:plus1:

3679

好文章

10351

所以放入和当前rails环境

“所以”=>“所有”

感谢楼主的翻译!

19891

#18楼 @liukun_lk 感谢,已修~

16207

入门好贴!

5557

 好贴!

9484

虽然用了ruby开发这么久,还是第一次了解到Gemfile的一些用法,已收藏~~

11870

已收藏。

9765

:plus1: 专门登录赞一个!

A908ae

翻译得好,收藏了。

15615

顶一下

96

翻译的很棒,赞一个

Ab72dc

赞~顶一个。

20103

翻译的很好,非常详细~适合给 新手做教程~

23576

赞,好文

96

楼主,请教个问题: 1)新建的rails项目如果在Gemfile中只指定了需要的gem,而不指定各gem版本,在执行bundle install时是怎么处理的?是直接使用本机已安装的gem版本(若gem版本不止一个,会用哪个) 还是重新搜索安装网上最新的gem版本? 2)若添加新的gem,再次执行bundle install, 之前已经安装的gem是按当时的版本被锁在了Gemefile.lock文件中, 还是又重新安装最新的? 3)如果想安装指定版本的gem,在Gemfile中指定了版本, Gemefile.lock中版本会自动变更吗?

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册