Bundler 的作用及原理
Bundler 的作用及原理
翻譯?·?yesmeck?· Created at?one year ago?· Last by?teacafe2000?Replied at?one year ago?· 514 hits原文:http://bundler.io/rationale.html
首先,你要在你應(yīng)用根目錄下一個(gè)叫Gemfile文件里聲明這些依賴,它看起來(lái)是這個(gè)樣子的:
source 'https://rubygems.org'gem 'rails', '4.1.0.rc2' gem 'rack-cache' gem 'nokogiri', '~> 1.6.1'這個(gè)Gemfile說(shuō)明了這些事情:
首先,他告訴 bundler 默認(rèn)是在Gemfile里指定的https://rubygems.org上來(lái)找 gem。如果你的一些 gem 需要從一個(gè)私有的 gem 服務(wù)器上獲取,那么你可以為這些 gem 覆蓋掉這個(gè)默認(rèn)的源設(shè)置。
接著,你聲明了一些依賴:
- 版本是4.1.0.rc2的rails
- 任意版本的rack-cache
- 版本是>= 1.6.1但是< 1.7.0的nokogiri
在你第一次聲明完依賴后,你要告訴 bundler 去獲取它們:
$ bundle install # 也可以直接運(yùn)行 'bundle',相當(dāng)于 'bundle install'Bundler 會(huì)連接rubygems.org(或者其他你聲明的源),然后列出所有你指定的符合你需要的 gem。因?yàn)樗心阍贕emfile里的依賴有它們自己的依賴,所以基于上面的Gemfile運(yùn)行bundle install會(huì)安裝相當(dāng)多的的 gem。
$ 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 thread_safe 0.3.3 Installing builder 3.2.2 Installing rack 1.5.2 Installing erubis 2.7.0 Installing mime-types 1.25.1 Using bundler 1.6.2 Installing polyglot 0.3.4 Installing arel 5.0.1.20140414130214 Installing hike 1.2.3 Installing mini_portile 0.5.3 Installing multi_json 1.9.3 Installing thor 0.19.1 Installing tilt 1.4.1 Installing tzinfo 1.1.0 Installing rack-test 0.6.2 Installing rack-cache 1.2 Installing treetop 1.4.15 Installing sprockets 2.12.1 Installing activesupport 4.1.0.rc2 Installing mail 2.5.4 Installing actionview 4.1.0.rc2 Installing activemodel 4.1.0.rc2 Installing actionpack 4.1.0.rc2 Installing activerecord 4.1.0.rc2 Installing actionmailer 4.1.0.rc2 Installing sprockets-rails 2.0.1 Installing railties 4.1.0.rc2 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 已經(jīng)被安裝了,bundler 會(huì)直接使用它們。在你的系統(tǒng)上安裝完所有的 gem 后,bundler 會(huì)寫一個(gè)所有這些 gem 和它們的版本號(hào)的快照到 Gemfile.lock 里。
配置你的應(yīng)用使用 Bundler
Bundler 保證 Ruby 能找到Gemfile里的所有 gem 和這些 gem 自己的依賴。如果你的應(yīng)用是個(gè) Rails 3 以上的應(yīng)用的話,你的應(yīng)用默認(rèn)已經(jīng)有運(yùn)行 bundler 的代碼了。如果是 Rails 2.3 的應(yīng)用,可以看在 Rails 2.3 中設(shè)置 Bundler。
對(duì)于另外的應(yīng)用來(lái)說(shuō)(比如說(shuō)基于 Sinatra 的應(yīng)用),你需要在你引用任何 gem 之前配置一下 bundler。在你應(yīng)用加載的第一個(gè)文件的第一行(對(duì)于 Sinatra, 就是寫著require 'sinatra'的那個(gè)文件)加入以下下代碼:
require 'rubygems' # Ruby 1.8 以后的版本不再需要這句(樓主注) require 'bundle/setup'這樣 bundler 就能自動(dòng)找到你的Gemfile,并且讓你Gemfile里的所有 gem 是可用的(從技術(shù)上講,就是把這些 gem 放到$LOAD_PATH里)。
現(xiàn)在你的代碼就可以運(yùn)行了,你可以引用你需要的 gem。比如說(shuō)你可以require 'sinatra'。如果你有很多依賴,你可能希望“引用所有我Gemfile的 gem”。如果要這樣做的話,可以把下面這行代碼放到require 'bundler/setup'的下一行:
Bundler.require(:default)對(duì)于我們剛才的Gemfile來(lái)說(shuō),這行代碼相當(dāng)于:
require 'rails' require 'rack-cache' require 'nokogiri'精明的讀者會(huì)發(fā)現(xiàn)正確引用rack-cache的方式是rake/cache,而不是require 'rack-cache'。為了告訴 bundler 使用require 'rack/cache',只要更新你的Gemfie:
source 'https://rubygems.org'gem 'rails', '4.1.0.rc2' gem 'rack-cache', require: 'rack/cache' gem 'nokogiri', '~> 1.6.1'對(duì)于這么小的一個(gè)Gemfile來(lái)說(shuō),我們建議你跳過(guò)Bundler.require而是手動(dòng)引用這些 gem(特別是你還需要在Gemfile里寫一個(gè):require配置)。對(duì)于很大的Gemfile來(lái)說(shuō),使用Bundler.require讓你省略了大量重復(fù)的依賴引用。
把你的代碼放進(jìn)版本庫(kù)
在你開(kāi)發(fā)你的應(yīng)用一段時(shí)間后,把應(yīng)用跟Gemfile和Gemfile.lock一起放到版本庫(kù)里。這樣,你的版本庫(kù)里就有了你的應(yīng)用最后一次你確定能正常工作時(shí)所有的 gem 以及版本號(hào)的記錄。要記住,盡管你的Gemfile里只有三個(gè) gem,但是當(dāng)你去考慮你依賴的 gem 也依賴其他 gem 時(shí),你的應(yīng)用實(shí)際上依賴了大量的 gem,
這個(gè)非常重要:Gemfile.lock把你的應(yīng)用變成一個(gè)你的代碼跟第三方代碼最后一次你確定能正常工作的包。?在Gemfile里確切指定你依賴的第三方代碼的版本并不能提供同樣的保證,因?yàn)?gem 通常給它們自己的依賴聲明一個(gè)版本號(hào)的范圍。
你在同一臺(tái)機(jī)器上再次運(yùn)行bundle install的時(shí)候,bundler 會(huì)發(fā)現(xiàn)系統(tǒng)上已經(jīng)有了你需要的依賴,然后就會(huì)跳過(guò)安裝的過(guò)程。
不要把.bundle目錄放入版本庫(kù),以及所有它里面的文件。這些文件在不同的機(jī)器上是不同的,主要是用來(lái)保存運(yùn)行bundle install時(shí)的參數(shù)。
如果你運(yùn)行了bundle pack,你需要的 gem (除了來(lái)源是 git 倉(cāng)庫(kù)的 gem 以外)都會(huì)被下載到vendor/cache目錄。如果所有你需要的 gem 都在那個(gè)目錄里而且你把它放進(jìn)了版本庫(kù)里,bundler 運(yùn)行的時(shí)候就不需要聯(lián)網(wǎng)了。這是一個(gè)可選的步驟,因?yàn)檫@樣做你的版本庫(kù)就會(huì)變得很大。
與其他開(kāi)發(fā)者共享你的應(yīng)用
當(dāng)你的同事(或者你在另外一臺(tái)機(jī)器上)獲取你的代碼的時(shí)候,它會(huì)包含你最近開(kāi)發(fā)時(shí)使用的所有第三方代碼的確切版本。當(dāng)他們運(yùn)行bundle install,bundler 會(huì)找到Gemfile.lock并跳過(guò)解決依賴的步驟,改為安裝所有你原來(lái)機(jī)器上一樣的 gem。
換句話說(shuō),你不需要去猜你需要安裝什么版本的依賴。在我們剛才用過(guò)的栗子里,盡管rake-cache聲明了依賴rack >= 0.4,但是我們確定他能正常工作在rack 1.5.2下。即使 Rack 的團(tuán)隊(duì)發(fā)布了rack 1.5.3,bundler 還是會(huì)安裝1.5.2這個(gè)我們已經(jīng)知道的確切的版本。這為開(kāi)發(fā)者減輕了大量的維護(hù)負(fù)擔(dān),因?yàn)樗袡C(jī)器上都運(yùn)行著同樣的第三方代碼。
更新依賴
當(dāng)然,有時(shí)候你可能要更新你的應(yīng)用依賴的部分 gem。比如說(shuō),你想要把rails升級(jí)到4.1.0。重點(diǎn)是,你只想要升級(jí)一個(gè)依賴,而不是要重新解決你的所有依賴并且使用所有 gem 最新的版本。在我們栗子里,你只有3個(gè)依賴,但是即使在這個(gè)栗子里,更新任何一個(gè)東西都會(huì)變得復(fù)雜。
比如說(shuō),rails 4.1.0.rc2依賴actionpack 4.1.0.rc2,而actionpack又依賴rack ~> 1.5.2(意思是>= 1.5.2且< 1.6.0)。rack-cache又依賴rack >= 0.4。我們假設(shè)rails 4.1.0也依賴rack ~> 1.5.2,并且在rails 4.1.0發(fā)布后 Rack 團(tuán)隊(duì)發(fā)布了rack 1.5.3。
如果我們?yōu)榱烁?Rails,天真地更新了所有它依賴的 gem,我們得到了rack 1.5.3,這剛好滿足rails 4.1.0和rack-cache的要求。然而我們并沒(méi)有特別說(shuō)要更新rack-cache,它就可能跟rack 1.5.3不兼容(不管什么原因)。雖然把rack 1.5.2升級(jí)到rack 1.5.3不會(huì)搞壞什么東西,但是類似這種導(dǎo)致更大版本跨度的更新場(chǎng)景也會(huì)發(fā)生(見(jiàn)下面[1]更多討論)。
為了避免這個(gè)問(wèn)題,當(dāng)你更新一個(gè) gem 時(shí),如果有其他 gem 有與它相同的依賴,bundler 就不會(huì)更新那個(gè)相同的依賴。在上面的栗子里,由于rack-cache依然依賴rack,bundler 不會(huì)更新rack。這樣保證了更新rails不會(huì)不小心搞壞rack-cache。由于rails 4.1.0的依賴actionpack 4.1.0保留了rack 1.5.2的兼容,bundler 就不會(huì)管它,rack-cache就會(huì)繼續(xù)工作,盡管它可能面臨跟rack 1.5.3的不兼容。
由于你一開(kāi)始聲明了依賴rails 4.1.0.rc2,如果你想要更新到rails 4.1.0,只要簡(jiǎn)單地在Gemfile里更新成gem 'rails', '4.1.0'并且運(yùn)行:
$ bundle install根據(jù)上面地描述,bundle install總是執(zhí)行保守地升級(jí),不會(huì)更新你沒(méi)有在Gemfile里顯式更改的 gem(或者它們的依賴)。也就是說(shuō)你不修改Gemfile里的rack-cache,bundler 就會(huì)把它?和它的依賴(rack)?當(dāng)成一個(gè)不可修改的整體。如果rails 3.0.0跟rack-cache不兼容,bundler 就會(huì)顯示你的依賴快照(Gemfile.lock)跟你更新后的Gemfile之間的沖突。
如果你更新了你的Gemfile,并且你的系統(tǒng)上已經(jīng)有你所有需要的依賴了,當(dāng)你啟動(dòng)應(yīng)用的時(shí)候 bundler 會(huì)透明地更新Gemfile.lock。舉個(gè)栗子,如果你把mysql加到你的Gemfile里,并且已經(jīng)在你的系統(tǒng)上安裝了,你可以不需要運(yùn)行bundle install就能啟動(dòng)你的應(yīng)用,并且 bundler 會(huì)把最近一次正確的配置寫到Gemfile.lock里
這個(gè)功能在你添加或更新依賴很少的 gem 時(shí)就會(huì)比較方便。在你更新一些比較重要的 gem(比如?rails)或者有被很多 gem 依賴的 gem(比如rack)它就可能失敗。如果透明更新失敗了,你的應(yīng)用就會(huì)啟動(dòng)失敗,bundler 會(huì)顯示錯(cuò)誤引導(dǎo)你運(yùn)行bundle install。
不修改 Gemfile 來(lái)更新 Gem
有時(shí)候,你想要不修改 Gemfile 來(lái)更新一個(gè)依賴。比如說(shuō),你想要更新到最新版本的rack-cache。而你又沒(méi)有在Gemfie里指定rack-cache的版本,你可能想要周期性地獲取rakc-cache地最新版。那么你可以使用bundle update命令:
$ bundle update rack-cache這個(gè)命令會(huì)更新rack-cache和它地依賴更新到Gemfile里允許地最新版本(在這個(gè)栗子里就是更新到最新版本)。它不會(huì)修改其地依賴。
但是它會(huì)在需要地時(shí)候更新其他 gem 的依賴。舉個(gè)栗子,如果最新版的rack-cache指定了依賴rack >= 1.5.2,bundler 會(huì)更新rack到1.5.2盡管你沒(méi)有要求 bundler 更新?rack。如果 bundler 需要更新一個(gè)其他的 gem 依賴的 gem,那么它會(huì)在更新完成后告訴你這件事。
如果你要更新所有 Gemfile 里的 gem 到最新的能用的版本,運(yùn)行:
$ bundle update這個(gè)命令會(huì)從頭開(kāi)始解決依賴并忽略掉Gemfile.lock。如果你這么做了,你要準(zhǔn)備好git reset --hard和測(cè)試用例。從頭解決依賴會(huì)有意想不到的結(jié)果,特別是一部分你依賴的第三方庫(kù)在你上一次更新的時(shí)候發(fā)布了新的版本。
總結(jié)
一個(gè)簡(jiǎn)單的 Bundler 流程
- 當(dāng)你第一次創(chuàng)建 Rails 應(yīng)用的時(shí)候,它已經(jīng)包含了Gemfile。其他的應(yīng)用可以運(yùn)行:
bundle init命令會(huì)創(chuàng)建一個(gè)簡(jiǎn)單的Gemfile讓你編輯。
- 下面,添加你的應(yīng)用需要的 gem。如果你關(guān)心部分你需要的 gem 的版本,可以加一個(gè)合適的版本約束:
- 如果你有 gem 沒(méi)在你的系統(tǒng)上安裝,運(yùn)行:
- 更新一個(gè) gem 的版本,首先修改 Gemfile:
然后運(yùn)行:
$ bundle install- 如果bundle install說(shuō)你的Gemfile跟Gemfie.lock之間有沖突,運(yùn)行:
這個(gè)會(huì)升級(jí) Sinatra 這個(gè) gem,以及它所有的依賴。
- 更新所有你Gemfile里的 gem 到最新可用的版本,運(yùn)行:
-
每當(dāng)你的Gemfile.lock變化的時(shí)候,把它放入你的版本庫(kù)。它保存了你的應(yīng)用能成功運(yùn)行所依賴的所有第三方代碼的確切版本的歷史。
-
當(dāng)部署你的代碼到測(cè)試或者生產(chǎn)服務(wù)器的時(shí)候,首先運(yùn)行你的測(cè)試(或啟動(dòng)你的本地開(kāi)發(fā)服務(wù)器),確定你把Gemfile.lock放到了版本庫(kù)里。在遠(yuǎn)程服務(wù)器上,運(yùn)行:
備注
[1] 舉個(gè)栗子,如果rails 4.1.0依賴rack 2.0,這個(gè)rack 2.0滿足rack-cache的依賴,因?yàn)樗暶髁?gt;= 0.4的依賴。當(dāng)然你能指責(zé)說(shuō)rack-cache不指定依賴的最高版本很愚蠢,但是這種情況確實(shí)是普遍存在的,而且很多項(xiàng)目聲明依賴的時(shí)候會(huì)發(fā)現(xiàn)它們處在一個(gè)很尷尬的場(chǎng)面。依賴限制太嚴(yán)(rack = 1.5.1)就會(huì)讓你的項(xiàng)目很難兼容其他項(xiàng)目。依賴限制太寬(rack >= 1.0)會(huì)在 Rack 發(fā)布新版本的時(shí)候可能搞壞你的代碼。使用這樣的依賴聲明rack ~> 1.5.2和 SemVer 兼容的版本號(hào)基本上能解決這個(gè)問(wèn)題,但是這也只是一個(gè)普遍能接受的方案。由于 RubyGems 有超過(guò)十萬(wàn)個(gè)庫(kù),這個(gè)假設(shè)在實(shí)際應(yīng)用中可能并不成立。
?本帖已被設(shè)為精華帖! 共收到?19?條回復(fù) yesmeck?·?#1?·?one year ago半夜翻的,有幾處自己看的也不是很明白,大家多多指正。
est?·?#2?·?one year ago直接 bundle 和 bundle install 有啥不同?
imlcl?·?#3?·?one year ago yakczh?·?#4?·?one year agoruby 應(yīng)用服務(wù)器啟動(dòng)以后,是把所有的類都加載到內(nèi)存,還是運(yùn)行時(shí)用到的時(shí)候才去加載?
yesmeck?·?#5?·?one year ago#2樓?@est?是一樣的,bundle 不加子命令默認(rèn)執(zhí)行 install。
yuhaidonghd?·?#6?·?one year ago#4樓?@yakczh?如果是 Rails 應(yīng)用的話,這和啟動(dòng)的環(huán)境有關(guān)。development?環(huán)境肯定不是,production?的話應(yīng)該是幾乎全加載了,極少數(shù)不在啟動(dòng)時(shí)加載。不過(guò)這個(gè)問(wèn)題和 Bundler 無(wú)關(guān),不同的應(yīng)用不同的場(chǎng)景會(huì)使用不同的加載策略。
ericguo?·?#7?·?one year ago#6樓?@yuhaidonghd?
#4樓?@yakczh?config/intializers里面都會(huì)在啟動(dòng)時(shí)加載,但是只是在model/controller/helper里面require的話,還是在第一次用的時(shí)候加載
補(bǔ)充幾點(diǎn):
1. bundle install 可以將gem安裝在另外的目錄,參數(shù)是--path=
2. 如果讓bundle找到gem,可以設(shè)置ENV['GEM_PATH'] = "the_path_bundle_install_gem".
3.如果自己設(shè)置ruby代碼載入Gemfile定義的環(huán)境,需要下列代碼
贊!!!
yakczh?·?#10?·?one year agoruby也是動(dòng)態(tài)腳本,也有require 'xxxx' 為什么不能象php那樣修改了立即生效, 是因?yàn)閣eb容器啟動(dòng)的時(shí)候,已經(jīng)把所有類加載的內(nèi)存的原因嗎?
crazyjin?·?#11?·?one year ago這樣的帖子每天來(lái)一個(gè), 生活該多美好..:)
gofreesky?·?#12?·?one year ago?1 個(gè)贊#10樓?@yakczh?對(duì)于你說(shuō)的這種情況,應(yīng)該把require改為load
rubyu2?·?#13?·?one year ago官方文檔講的更清楚些。
jun1st?·?#14?·?one year ago#10樓?@yakczh?是rails在prod環(huán)境這么干的,啟動(dòng)時(shí)全部加載,
yakczh?·?#15?·?one year ago?1 個(gè)贊#14樓?@jun1st?developer環(huán)境下,是不是跟php一樣,新改了代碼會(huì)重新解析一遍?
jun1st?·?#16?·?one year ago#15樓?@yakczh?是的
hbin?·?#17?·?one year ago說(shuō)好的原理呢?標(biāo)題黨?
grd0n9?·?#18?·?one year ago?雖然這篇原文和 the rails 4 way 第一章第一節(jié)的內(nèi)容差不多,但支持下,翻譯得很好哦~
總結(jié)
以上是生活随笔為你收集整理的Bundler 的作用及原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 安装gitlab
- 下一篇: gitlab 安装报错:Could no