前幾天讀到的一篇博客,覺得內(nèi)容很詳實,就翻譯了下給大家分享下。絕大部分為直譯,極少數(shù)地方加了點自己的注釋,若有不周到地方,還望大家指出。如果有排版方面的問題,也請指出。
原文:http://tosbourn.com/what-is-the-gemfile/
作為Ruby開發(fā)者,我們一直在使用Gemfile,并且大部分人知道一些關(guān)于Gemfile的基礎(chǔ)知識。在這篇文章里,我想更加深入到Gemfile里面去看看通過Gemfile所能做的一切。
Gemfile是我們創(chuàng)建的一個用于描述gem之間依賴的文件。gem是一堆Ruby代碼的集合,它能夠為我們提供調(diào)用。你的Gemfile必須放在項目的根目錄下面, 這是Bundler的要求,對于任何的其他形式的包管理文件來說,這也是標準。這里值得注意的一點是Gemfile會被作為Ruby代碼來執(zhí)行。當在Bundler上下文環(huán)境中被執(zhí)行的時能使我們訪問一些方法,我們用這些方法來解釋gem之間的require關(guān)系。
首先我們要做的就是告訴Gemfile到那里去找到這些gems, 這就是gem的源。
我們使用#source
方法來做這件事情
source "https://rubygems.org"
這里并不推薦一個項目有多個源。對于99%的項目,你的Gemfile的源都會被要求設(shè)置為https://rubygems.org,對于一個源,唯一的要求是它必須是一個合法的Rubygems的repo。
現(xiàn)在我們來探討下關(guān)于gem源的優(yōu)先級。
我們在Gemfile的頂部位置定義一個源的同時,我們也可以針對每個gem定義一個源。我們也能夠為一個本地的gem定義一個路徑或者是為gem定義一個git路徑,比如說GitHub之類的(我們在后面點講到這點)。
當Bundler嘗試定位一個gem的時候,它會首先查看這個gem有沒有顯示的設(shè)置源,如果有,就先使用這個源。如果你在設(shè)置gem的時候有使用source, path或者git依賴的話,Bundler將會先在這些地方找,然后再去其他地方尋找。如果沒有被顯示設(shè)置的話, Bundler將會依照你Gemfile里面定義的源的順序來找。如果一個gem能夠在多個源里面被找到的話(雖然這是極為罕見的,因為你最好只定義一個源),你將會得到一個warning來提示你哪個源被使用了。
你能夠使#source作為一個block來調(diào)用
source "https://my_awesome_source.com" do gem "my_gem" gem “my_other_gem”end
有些源需要你使用驗證才能夠被設(shè)定。Bundler有一個設(shè)置選項使得你可以為每個源設(shè)置用戶名和密碼
bundle config my_gem_source.com my_username:my_password
這是任何希望通過Bundler來安裝gem都必須要的因為它不會被放入版本管理里面。你也可以直接在Gemfile中設(shè)置你的驗證信息,當然,這些驗證信息也會被commit進你的版本管理工具。如下所示
source "https://username:password@my_gem_source.com"
你在源里面的設(shè)置,都會被你以bundle config的方式設(shè)置的東西所覆蓋。
如果你的應(yīng)用程序需要使用一個特別的Ruby版本或是引擎,我們都能夠在Gemfile里面進行設(shè)置。
ruby "1.9.3", :patchlevel => "247", :engine => "jruby", :engine_version => "1.6.7"
當設(shè)定這個的時候,需要的唯一點信息就是ruby的版本(我們這里使用1.9.3)
* :pathlevel 聲明了Ruby的patch level
* :engine 聲明了使用的Ruby引擎
* :engine_version 聲明了引擎的版本 (如果這個被設(shè)置了,engine也需要被設(shè)置)
現(xiàn)在我們到了Gemfile的核心,設(shè)置你的gems。最基本的語法如下:
gem "my_gem"
這里my_gem是 gem的名字,gem的名字是唯一要求的參數(shù),此外還有幾個可以選擇的參數(shù)可以使用。
對于一個gem,你最常做的事情就是設(shè)置它的版本,如果你不設(shè)置版本的話,你也可以說任意的版本都可以。
gem "my_gem", ">= 0.0"
這里有7個操作符供你用來設(shè)置你的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"
~> 操作能夠讓你使用這個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"
如果你使用Rails的話,這點小技巧可能被隱藏了,但是在你的config/application.rb文件里面你能看到這么一行代碼。
Bundler.require(:default, Rails.env)
它的意思是require所有沒有被放入group(后面會講到這個概念)里面的gems和所有放入和當前rails環(huán)境(RAILS_ENV, development, test, production)同名的group里面的gems。
默認方式下,如果你在Gemfile里面包含一個gem,當Bundler.require被調(diào)用的時候會被包含進來。我們也能通過下面的設(shè)置讓gem不被包含進來(譯者注釋:這樣你就只能安裝這個gem,在使用的時候必須在你的代碼里手動的添加require ‘my_gem’
來調(diào)用my_gem里面的方法了。為什么需要這樣呢,因為并不是所有的地方都需要使用這個gem,比如你在rake task里面使用了my_gem, 而其他地方?jīng)]有使用,故你只需要在這個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可以屬于一個或多個group,當它不屬于任何group的時候,它被放入了:default group
。
有兩種方法你可以對一個gem分組。第一種是對group屬性進行賦值,如下所示:
gem "my_gem", group: :development
它的意思是,這個gem只在development環(huán)境下被require。這也意味著當你在安裝gems的時候,你可以指定某個group下面的gems不被安裝,這樣在一定程度上能加快gem的安裝。
bundle install --without development test
上面的意思是安裝除development和test group意外的所有g(shù)ems。
第二種gem分組的方法就是你可以將gems放入一個block里面,如下所示:
group :development do gem "my_gem" gem "my_other_gem"end
這看上去更美觀,并且你也可以設(shè)置多個group。
group :development, :test do gem "my_gem" gem "my_other_gem"end
如果你想讓某個group變成可選的形式,你也可以像下面這樣,設(shè)置optional: true
group :development, optional: true do gem "my_gem" gem "my_other_gem"end
當上面被設(shè)置時,為了安裝development group下面的gems,需要運行bundle install —with development
如果某個gem只能在某個平臺上使用,你也可以在gemfile里面設(shè)置。平臺的原理和group很類似,但不同的是你不需要去通過—without這樣的option去指定,它會自動根據(jù)平臺判斷執(zhí)行。
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)
我發(fā)現(xiàn)平臺真的非常有用,當一個開發(fā)團隊在不同平臺開發(fā)的時候。當你team的一個開發(fā)者使用的是Windows平臺的時候,你可能需要不同版本的gem來支持。我經(jīng)常使用下面的block語法來使用platform設(shè)定。
platforms :jruby do gem "my_gem" gem "my_other_gem"end
ok,現(xiàn)在我們來講設(shè)置gem的源,如下所示:
gem "my_gem", source: "https://my_awesome_gemsite.com"
如果這個my_gem 在source里面找不到的話,Bundler也不會去default的源里面找,所以找不到的情況下這個gem就不會被安裝。
你可以設(shè)置gem的安裝源為一個git repo,比如GitHub, 這只需要你將source屬性替換為git。你可以設(shè)置這個repo的鏈接為HTTP(S), SSH, GIT等協(xié)議,但最好使用HTTP(S)和SSH,因為其他的會使你可能成為man-in-the-middle攻擊的受害者。如果你把gem放入到repo里面,你必須要在repo根目錄文件夾下面有一個.gemspec 文件。這里面需要包含一個合法gem的聲明。如果你沒有提供這個文件,Bundler會嘗試創(chuàng)建一個,但是他不會被依賴。如果你嘗試去include一個沒有提供.gemspec文件的git repo里面的gem,你必須指定一個版本號。
你可以為gem設(shè)置branch,tag,ref,默認是使用master branch。你也可以強制Bundler擴展submodule,通過以下方式來設(shè)置:
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
你可以設(shè)置一個URL來作為一個更廣義的源,你可以通過調(diào)用#git_source方法并將name作為參數(shù)傳進去,以及一個接收一個參數(shù)的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都是比較流行的git repo host,所以有兩者的helper method。在兩者里面,Bundler都默認repo是public的。
gem "my_gem", github: "tosbourn/my_gem" gem "my_gem", bitbucket: "tosbourn/my_gem"
你也可以設(shè)置兩者的branch。當用戶名和repo名字一致的時候,可以省略一個。
gem "rails", github: "rails"gem "rails", bitbucket: "rails"
注意:在Bundler 2出來之前,你不能使用:github這個參數(shù),目前它是使用git://協(xié)議的,就是前面講過的可能會受到man-in-the-middle攻擊的。還有一個helper :gist, 如果你Github上是以gist的形式存放的話就能夠使用它。你可以只使用gist ID作為path,也可以像:github, :bitbucket那樣傳入:branch參數(shù)。
gem "my_gem", :gist => "5935162112", branch: "my_custom_branch"
你可以通過傳入:path參數(shù)來依賴你本地的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的。
有時候你想在某個前提條件被滿足的情況下安裝這個gem,比如你系統(tǒng)里面是否有某個程序。下面這個方法能夠接收一個proc或lambda,下面的例子中我們將在你的系統(tǒng)是mac的時候安裝這個gem
install_if -> { RUBY_PLATFORM =~ /darwin/ } do gem "my_osx_gem"end