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