Bundler 是由 Carl Lerche、Yehuda Katz、André Arko 和許多優秀的貢獻者建立的工具,用於管理 Ruby 函式庫中的 Rubygems 相依性。Bundler 1.0 在與 Rails 3 大約相同的時間發布,而 Rails 專案可能是 Bundler 最廣為人知的用途。但請記住,Bundler 不僅限於 Rails!
您知道 Bundler 不僅可用於管理寶石相依性,還能用來撰寫我們自己的寶石嗎?這真的很容易,而且 Bundler 提供了一些東西來協助您完成這項工作。
相關閱讀
我們為什麼要建立寶石?我們不能只將一些程式碼丟到我們的其他函式庫中,然後改用它嗎?當然,我們可以這麼做。但如果我們想在其他地方使用該程式碼,或者我們想分享它呢?這就是寶石完美的理由。我們可以將我們的函式庫和寶石分開編碼,然後只要讓函式庫需要寶石即可。如果我們想在另一個函式庫中使用寶石,那麼這只是一個小修改,而不是一大堆複製。
此外:分享就是關懷。
本指南使用 Bundler 1.9.0 版本。我們可以使用其他版本,但我們可能無法獲得完全相同的輸出。若要檢查我們目前擁有的 Bundler 版本,讓我們執行以下命令
$ bundle -v
我們應該會看到接近於Bundler 版本 1.9.0
的內容。如有必要,我們可以透過執行gem update bundler
來更新至 Bundler 的最新版本。
若要開始使用 Bundler 建立寶石,請像這樣使用bundle gem
命令
$ bundle gem foodie
我們將我們的寶石稱為foodie
,因為這個寶石將圍繞食物進行一些操作,例如將它們描繪成「美味!」或「噁心!」。敬請期待。
有關寶石命名慣例的資訊,您可以在 RubyGems 網站上閱讀「為您的寶石命名」指南。
此命令為我們的寶石建立一個範本目錄,如果我們已安裝 Git,則會在此目錄中初始化一個 Git 儲存庫,以便我們可以立即開始提交。如果您是第一次執行bundle gem
命令,系統會詢問您是否要將CODE_OF_CONDUCT.md
和LICENSE.txt
檔案包含在您的專案中。產生的檔案為
Gemfile:用於管理我們函式庫開發的寶石相依性。此檔案包含一個gemspec
行,表示 Bundler 也會包含在foodie.gemspec中指定的相依性。最佳做法是在gemspec中指定我們的函式庫所依賴的所有寶石。
Rakefile:需要 Bundler,並透過呼叫 Bundler::GemHelper.install_tasks
來新增 build
、install
和 release
Rake 任務。build
任務會建置目前版本的 gem 並將其儲存在 pkg 資料夾中,install
任務會建置 並將 gem 安裝到我們的系統中(就像我們執行 gem install
一樣),而 release
則會將 gem 推送到 Rubygems 以供公眾使用。
CODE_OF_CONDUCT.md:提供行為準則,你預期所有貢獻者都必須遵守此準則。僅在你選擇納入時才會包含。
LICENSE.txt:包含 MIT 授權。僅在你選擇納入時才會包含。
.gitignore:僅在我們有 Git 時。這會忽略 pkg 目錄中的任何內容(通常是 rake build
放置在那裡的檔案)、任何具有 .gem 副檔名的內容和 .bundle 目錄。
foodie.gemspec:Gem 規格檔案。我們在此提供 Rubygems 使用的資訊,例如我們 gem 的名稱、說明和首頁。我們也在此指定我們的 gem 執行所需的相依性。
lib/foodie.rb:定義我們 gem 程式碼的主要檔案。這是當我們的 gem 載入時,Bundler(或任何類似智慧系統)會需要的檔案。此檔案會定義一個 module
,我們可以使用它作為我們所有 gem 程式碼的命名空間。最好將我們的程式碼放在…
lib/foodie:這裡。這個資料夾應該包含我們 gem 的所有程式碼(類別等)。lib/foodie.rb 檔案用於設定我們 gem 的環境,而所有部分都放在這個資料夾中。如果我們的 gem 有多種用途,將其分開,以便人們一次需要一個類別/檔案,這會很有幫助。
lib/foodie/version.rb:定義一個 Foodie
模組,並在其中定義一個 VERSION
常數。此檔案由 foodie.gemspec 載入,以指定 gem 規格的版本。當我們發布新版本的 gem 時,我們會增加此版本號碼的一部分,以告知 Rubygems 我們正在發布新版本。
這就是我們的基礎和佈局,現在開始開發吧!
對於本指南,我們將使用 RSpec 來測試我們的 gem。我們撰寫測試以確保一切都按計畫進行,並防止未來的我們建置時光機,回來踢我們的屁股。
要開始撰寫我們的測試,我們將在 gem 的根目錄建立一個 spec 目錄,使用指令 mkdir spec
。接下來,我們將在 foodie.gemspec 檔案中指定 rspec
是開發依賴項,方法是在 Gem::Specification
區塊中加入這行
spec.add_development_dependency "rspec", "~> 3.2"
由於我們的 Gemfile 中有 gemspec
方法呼叫,Bundler 會自動將此 gem 新增到稱為「開發」的群組,然後我們可以在任何時候參考它,以載入這些 gem,指令如下
Bundler.require(:default, :development)
將此依賴項規格放在 foodie.gemspec 中,而非 Gemfile 的好處是,任何執行 gem install foodie --dev
的人都會安裝這些開發依賴項。這個指令用於人們想要測試 gem,而不必從 GitHub 分岔或複製它時。
當我們執行 bundle install
時,rspec 將會安裝到這個程式庫,以及我們使用 Bundler 的任何其他程式庫,但不會安裝到系統。這是個重要的區別:任何由 Bundler 安裝的 gem 都不會與由 gem install
安裝的 gem 發生衝突。它實際上是一個沙盒環境。最佳實務做法是使用 Bundler 管理我們的 gem,這樣我們就不會有 gem 版本衝突。
透過執行 bundle install
,Bundler 將會產生極為重要的 Gemfile.lock 檔案。這個檔案負責確保開發此程式庫的每個系統都有完全相同的 gem,因此它應該總是檢查版本控制。有關此檔案的更多資訊,請閱讀 bundle install
手冊頁的「THE GEMFILE.LOCK」區段。
此外,在 bundle install
輸出中,我們將看到這行
Using foodie (0.1.0) from source at /path/to/foodie
Bundler 會偵測我們的 gem,載入 gemspec 並將我們的 gem 與其他每個 gem 一樣打包。
現在有了這個架構,我們可以撰寫我們的第一次測試。對於測試,我們首先建立一個稱為 spec 的資料夾,用來放置我們的測試 (mkdir spec
)。然後,我們在 spec 目錄的根目錄建立一個新的 RSpec 檔案,用於我們要測試的每個類別。如果我們的 gem 有多個面向,我們會將它們分組在一個目錄下,例如 spec/facet;但這是一個簡單的 gem,所以我們不會這麼做。讓我們將這個新檔案命名為 spec/foodie_spec.rb
,並填入以下內容
describe Foodie::Food do
it "broccoli is gross" do
expect(Foodie::Food.portray("Broccoli")).to eql("Gross!")
end
it "anything else is delicious" do
expect(Foodie::Food.portray("Not Broccoli")).to eql("Delicious!")
end
end
當我們再次執行 bundle exec rspec spec
時,我們會被告知 Foodie::Food
常數不存在。這是真的,我們應該在 lib/foodie/food.rb
中定義它,如下所示
module Foodie
class Food
def self.portray(food)
if food.downcase == "broccoli"
"Gross!"
else
"Delicious!"
end
end
end
end
要載入這個檔案,我們需要為它新增一個 require 行到 lib/foodie.rb
require 'foodie/food'
我們還需要在 spec/foodie_spec.rb
的頂端載入 lib/foodie.rb
require 'foodie'
當我們使用 bundle exec rspec spec
執行我們的規格時,這個測試將會通過
2 example, 0 failures
太棒了!如果我們正在使用 Git(或任何其他原始碼控制系統),這是一個提交我們程式碼的絕佳檢查點。永遠記得要經常提交!
我們可以撰寫自己的程式碼,這很好,但如果我們想要依賴另一個 gem 呢?這也很容易。
我們現在將使用 Active Support 的 pluralize
方法,方法是使用我們的 gem 中的方法呼叫它。
若要使用另一個 gem,我們必須先在我們的 foodie.gemspec 中將它指定為相依性。我們可以在 foodie.gemspec 中透過在 Gem::Specification
物件內新增這行來指定對 activesupport
gem 的相依性
spec.add_dependency "activesupport"
如果我們想指定特定版本,我們可以使用這行
spec.add_dependency "activesupport", "4.2.0"
或指定版本約束
spec.add_dependency "activesupport", ">= 4.2.0"
然而,依賴於一個版本大於當時最新版本的做法,是一個在以後必定會遇到問題的保證方式。嘗試永遠使用 ~>
來指定相依性
spec.add_dependency "activesupport", "~> 4.2.0"
當我們再次執行 bundle install
時,activesupport
gem 將會被安裝,供我們使用。當然,就像我們這些勤奮的 TDD/BDD 狂熱份子一樣,我們將在編寫 pluralize
方法之前測試它。讓我們現在在 describe Foodie::Food
區塊內將這個測試新增到 spec/food_spec.rb
it "pluralizes a word" do
expect(Foodie::Food.pluralize("Tomato")).to eql("Tomatoes")
end
當然,當我們使用 bundle exec rspec spec
執行這個規格時,它將會失敗
expect(Failure/Error: Foodie::Food.pluralize("Tomato")).to eql("Tomatoes")
undefined method `pluralize' for Foodie::Food:Class
我們現在可以在 lib/foodie/food.rb 中定義這個 pluralize
方法,首先載入包含 pluralize
方法的 Active Support 部分。這行應該放在檔案的頂端,就像所有好的 require
一樣。
require 'active_support/inflector'
接下來,我們可以像這樣定義 pluralize
方法
def self.pluralize(word)
word.pluralize
end
當我們執行 bundle exec rspec spec
時,我們的規格將會通過
3 examples, 0 failures
這帶來另一個檢查點,在這一點上提交我們到目前為止的努力將是一個好主意。
我們現在可以呼叫我們的 gem 的方法(總共兩個!)並讓它們傳回字串,這很棒,但大家都知道最好的 gem 都附帶命令列介面(以下簡稱「CLI」)。你可以馬上看出這個 gem 有多麼遜,因為它沒有 CLI,對吧?它需要一個。它渴望一個。
它應得一個。
在我們急著為我們的寶石提供一個只有兩個方法(且兩個方法都傳回無用的字串)的最佳 CLI 之前,讓我們考慮一下我們將如何先測試這個。我們是狂熱者,還記得嗎?現在,如果只有一個我們可以使用工具。當然,它必須有一個很酷的名字。
像是「Aruba」。BAM
David Chelimsky 和 Aslak Hellesøy 合作建立了 Aruba,一個 CLI 測試工具,他們兩個都用於 RSpec 和 Cucumber,現在我們也可以用它來測試我們的寶石。喔,嘿,說到 Cucumber,那也是我們將用來定義 Aruba 測試的方法。人類程式碼客戶端可讀的測試是未來的道路,老兄。
我們現在將在 foodie.gemspec 中定義新的開發相依性,以用於 Cucumber。
spec.add_development_dependency "cucumber"
spec.add_development_dependency "aruba"
酷。讓我們執行 bundle install
來設定這些很棒的工具。
我們的 CLI 將有兩個方法,對應於我們在 Foodie::Food
中定義的兩個方法。我們現在將建立一個 features 目錄,我們將在其中使用 Aruba 為我們的 CLI 編寫測試。在這個目錄中,我們將建立一個名為 features/food.feature 的新檔案,並填入這個多汁的程式碼
Feature: Food
In order to portray or pluralize food
As a CLI
I want to be as objective as possible
Scenario: Broccoli is gross
When I run `foodie portray broccoli`
Then the output should contain "Gross!"
Scenario: Tomato, or Tomato?
When I run `foodie pluralize --word Tomato`
Then the output should contain "Tomatoes"
這些場景測試了我們的寶石將提供的 CLI。在 When I run
步驟中,引號內的字詞是我們可執行檔的名稱,第二個是任務名稱,任何後續文字都是引數或選項。是的,它正在測試看起來與我們的規格相同的事物。你真是觀察入微。金星獎!但它透過 CLI 進行測試,這讓它極度棒。虛構的範例在今年很流行。
第一個場景確保我們可以呼叫特定的任務,並傳遞一個單一引數給它,然後它會變成輸出文字的一部分。第二個場景有效地確保了相同的事物,但我們將該值作為選項而非引數傳遞。
若要執行這個功能,我們使用 cucumber
指令,但當然,因為它在我們的套件的內容中可用,所以我們這樣使用 bundle exec cucumber
$ bundle exec cucumber features/
看到那些黃色的東西了嗎?它們是未定義的步驟
When /^I run "([^"]*)"$/ do |arg1|
pending # express the regexp above with the code you wish you had
end
Then /^the output should contain "([^"]*)"$/ do |arg1|
pending # express the regexp above with the code you wish you had
end
我們可以透過需要 Aruba 來定義它們。在 Cucumber 中,features/support 目錄中的所有 .rb 檔案都會自動需要。為了向自己證明這一點,我們可以新增一個 features/support/setup.rb 檔案(先建立 support 目錄),並放入這一行
require 'aruba/cucumber'
這會載入 Aruba 提供的 Cucumber 步驟,這些步驟與我們的 Cucumber 功能需要的一樣,才能夠很棒。
我們必須重新執行 bundle exec cucumber features
,只是看看接下來會發生什麼事。我們看到紅色。紅色就像從牆壁不斷滲出的血液。它包含這個神秘的訊息
sh: foodie: command not found
好吧,所以它不是那麼神秘。它只表示它找不到我們寶石的可執行檔。不用擔心,我們可以在我們寶石的根目錄建立一個 exe 目錄,並在其中放入一個名為 foodie 的檔案。這個檔案沒有副檔名,因為它是一個可執行檔,而不是一個指令碼。我們不想到处呼叫 foodie.rb
,對吧?不,不,我們不想。我們將用這個內容填滿這個檔案
#!/usr/bin/env ruby
print "nothing."
如果這個檔案完全是空的,我們會遇到一個不友善的 Errno::ENOEXEC
錯誤。嘿,說到執行,我們應該從我們的終端機將這個檔案 chmod
為一個可執行檔
$ chmod +x exe/foodie
好吧,我們已經取得可執行檔,現在要怎麼做?如果我們重新執行我們的功能,我們會得到什麼都沒有的輸出。什麼都沒有!真的!
got: "nothing."
我們的exe/foodie 檔案是空的,這導致了這個什麼都沒有的鬧劇。移除 print "nothing."
這一行,並用執行我們的 CLI 所需的所有程式碼取代它,這包括兩行
require 'foodie/cli'
Foodie::CLI.start
轟!當我們再次執行 bundle exec cucumber features
時,它會抱怨沒有 foodie/cli 檔案可以載入。在我們深入探討這個檔案的作用之前,我們應該說明 exe/foodie 檔案另一行的程式碼。start
方法會啟動我們的 CLI
類別,並會尋找與我們要求的任務相符的任務。
好,因此顯然下一步是建立這個檔案,但它做什麼呢?
這個新的 lib/foodie/cli.rb 檔案將使用另一個稱為 Thor
的 gem 來定義命令列介面。Thor 是由 Yehuda Katz(及合作者)建立的,作為 Rake 建置工具的替代方案。Thor 為我們提供了一個方便的 API 來定義我們的 CLI,包括使用說明標語和說明輸出。語法與 Rake 非常類似。此外,Rails 和 Bundler 都使用 Thor 作為他們的 CLI 介面以及他們的產生器基礎。是的,Thor 甚至可以執行產生器!
現在我們只會看看我們如何使用 Thor 建立 CLI,然後,如果你表現良好,我們將看看如何使用它撰寫產生器。
為了讓這個 CLI 運作,我們需要建立一個 Foodie::CLI
類別,並在上面定義一個 start
方法。或者你知道,可能有一個 gem 可以讓我們使用。就像 Thor。這個 gem 以北歐神話中的強悍雷神命名,它絕對是成為同樣強悍的快速途徑。這個 gem 就是我們將用來建立我們的 CLI 介面,然後稍後建立產生器(如果你表現良好,還記得嗎?)。
現在讓我們這樣定義 lib/foodie/cli.rb
檔案
require 'thor'
module Foodie
class CLI < Thor
end
end
Thor
類別有一系列的方法,例如我們在 exe/foodie
中引用的 start
方法,我們可以使用這些方法來建立這個 CLI。喔,順便一提,我們的類別不必稱為 CLI
,這樣做只是最佳實務。我們不會神奇地取得這個 Thor
類別;我們需要透過在我們先前的 add_dependency
下方新增這一行來告訴我們的 gemspec 我們依賴這個 gem
spec.add_dependency "thor"
要安裝這個新的相依性,我們使用 bundle install
。當我們再次執行 bundle exec cucumber features
時,我們會看到它現在抱怨找不到我們呼叫的任務
Could not find task "portray"
...
Could not find task "pluralize"
Thor 任務定義為純粹的舊方法,但稍有不同。要在我們的 Foodie::CLI
類別中定義 portray
任務,我們會在 Foodie::CLI
類別中撰寫這段程式碼
desc "portray ITEM", "Determines if a piece of food is gross or delicious"
def portray(name)
puts Foodie::Food.portray(name)
end
desc
方法是這裡的「稍有不同」。它之後定義的方法會成為具有給定描述的任務。desc
的第一個引數是任務的使用說明,而第二個引數是該任務完成工作的簡短描述。portray
方法定義為單一引數,它會是傳遞給此任務的命令列第一個引數。在 portray
方法中,我們呼叫 Foodie::Food.portray
並傳遞這個引數給它。
在 Foodie::CLI
類別中,我們參照 Foodie::Food
類別,而不需要定義它的檔案。在這個檔案最上方的 require 'thor'
下,放入這行程式碼來需要定義 Foodie::Food
的檔案
require 'foodie'
當我們使用 bundle exec cucumber features
重新執行我們的功能時,我們的第一個情境會通過
2 scenarios (1 failed, 1 passed)
4 steps (1 failed, 3 passed)
第二個仍然失敗,因為我們尚未定義 pluralize
任務。這次,我們不會定義一個接受引數的任務,而是定義一個從傳遞給任務的選項中讀取值的任務。要在 Foodie::CLI
中定義 pluralize
任務,我們使用這段程式碼
desc "pluralize", "Pluralizes a word"
method_option :word, aliases: "-w"
def pluralize
puts Foodie::Food.pluralize(options[:word])
end
這裡有我們使用的新的 method_option
方法,它定義了方法選項。它接受一個雜湊,用來指出選項的詳細資料,以及它們應該如何傳回給我們的任務。查看 Thor README 以取得有效類型的完整清單。我們也可以使用傳遞給 method_option
的 :aliases
選項來定義這個方法的別名。在任務中,我們透過 options
雜湊參照選項的值,並使用 Foodie::Food.pluralize
來將一個字轉換成複數型。
當我們再次使用 bundle exec cucumber features
執行我們的腳本時,兩個情境都會通過
2 scenarios (2 passed)
4 steps (4 passed)
我們可以透過執行 bundle exec exe/foodie portray broccoli
來嘗試執行 CLI 應用程式。
如果我們想要在之後加入更多選項,我們可以使用 method_options
輔助程式來定義它們,如下所示
method_options word: :string, uppercase: :boolean
def pluralize
# accessed as options[:word], options[:uppercase]
end
在此範例中,options[:word]
會傳回一個 String
物件,而 options[:uppercase]
會傳回 true
或 false
,視其接收到的值而定。
這個引言應該已經激起你進一步了解 Thor 的胃口,建議你現在就這麼做。查看 Bundler::CLI
,了解如何將 Thor 用作 CLI 工具的絕佳範例。
由於我們的所有功能和規格現在都通過了,我們可以提交我們的程式碼了。
前面提到,我們可以使用 Thor 來做更多的事情,而不仅仅是 CLI。我們可以用它來建立一個產生器。這是真的。我們甚至可以建立產生器s,但現在不要太過得意忘形,先專注於建立一個。
你看到那個雙關語了吧?對吧,很明顯。
我們將稍作調整,為我們的 gem 新增一個新功能:一個用於建立食譜目錄的產生器。我們的想法是,我們可以這樣執行我們的產生器
foodie recipe dinner steak
這將在目前的位置產生一個食譜目錄,在該目錄內產生一個晚餐目錄,然後在該目錄內產生一個牛排.txt 檔案。這個牛排.txt 檔案將包含食譜的架構,例如食材和說明。
對我們來說,幸運的是,Aruba 有方法可以測試產生器是否產生檔案和目錄。讓我們建立一個名為 features/generator.feature
的新檔案,並填入以下內容
Feature: Generating things
In order to generate many a thing
As a CLI newbie
I want foodie to hold my hand, tightly
Scenario: Recipes
When I run `foodie recipe dinner steak`
Then the following files should exist:
| dinner/steak.txt |
Then the file "dinner/steak.txt" should contain:
"""
##### Ingredients #####
Ingredients for delicious steak go here.
##### Instructions #####
Tips on how to make delicious steak go here.
"""
請注意,「delicious」後面兩次出現的字詞都是「牛排」,這非常美味。它也是我們傳遞給我們執行的命令的最後一個引數,因此應該是我們範本中的動態變數。我們很快就會看到如何執行此操作。
當我們執行此功能時,系統會告訴我們找不到我們要求產生器建立的晚餐/牛排.txt 檔案。為什麼?
嗯,因為目前我們沒有在 Foodie::CLI
中定義執行此動作的 recipe
任務。我們可以定義一個產生器類別,就像定義一個 CLI 類別一樣
desc "recipe", "Generates a recipe scaffold"
def recipe(group, name)
Foodie::Generators::Recipe.start([group, name])
end
此方法的第一個引數是傳遞給產生器的引數。我們也需要為這個新類別需要檔案,我們可以在 lib/foodie/cli.rb 的頂端放置此程式碼列來執行此動作
require 'foodie/generators/recipe'
要定義此類別,我們繼承自 Thor::Group
而不是 Thor
。我們也需要包含 Thor::Actions
模組來定義產生器的輔助方法,其中包括能夠建立檔案和目錄的那些方法。因為這是一個產生器類別,我們會將它放入名為「generators」的新命名空間中,使此檔案的位置為 lib/foodie/generators/recipe.rb
require 'thor/group'
module Foodie
module Generators
class Recipe < Thor::Group
include Thor::Actions
argument :group, type: :string
argument :name, type: :string
end
end
end
透過繼承自 Thor::Group
,我們定義產生器而不是 CLI。當我們呼叫 argument
時,我們正在定義產生器的引數。這些引數與它們從 Foodie::CLI
中的 recipe
工作傳入的順序相同
要建立這個產生器,你知道的,產生一些東西,我們只要在類別中定義方法。在 Thor::Group
子項中定義的所有方法都會在對其呼叫 start
時執行。讓我們在這個類別中定義一個 create_group
方法,它會使用我們傳入的名稱建立目錄。
def create_group
empty_directory(group)
end
要將檔案放入這個目錄並為我們的美食家朋友省去一些輸入,我們會使用 template
方法。這會從預先定義的來源位置複製一個檔案,並將它評估為 ERB 範本。我們會定義一個 copy_recipe
方法來立即執行此動作
def copy_recipe
template("recipe.txt", "#{group}/#{name}.txt")
end
如果我們在這個檔案中有任何 ERB 呼叫,它們會被評估,而結果會在新的範本檔案中輸出。
我們已經很久沒有執行任何東西了。嘿,這裡有一個點子!讓我們執行我們的產生器!我們可以在不使用 Cucumber 的情況下執行此動作,方法是執行 bundle exec exe/foodie recipe dinner steak
,但僅限於這次。一般來說,我們會僅透過 Cucumber 來測試它。當我們執行此命令時,我們會被告知所有這些
create dinner
Could not find "recipe.txt" in any of your source paths. Please invoke Foodie::Generators::Recipe.source_root(PATH) with the PATH containing your templates. Currently you have no source paths.
第一行告訴我們 dinner 目錄已經建立。沒有什麼太花俏的。
不過,第二行比較令人興奮!它要求我們為我們的產生器定義 source_root
方法。這很容易!我們可以像這樣在 Foodie::Generators::Recipe
中將它定義為類別方法
def self.source_root
File.dirname(__FILE__) + "/recipe"
end
這會告訴我們的產生器在哪裡可以找到範本。現在,我們只需要建立範本,我們可以將它放在 lib/foodie/generators/recipe/recipe.txt
##### Ingredients #####
Ingredients for delicious <%= name %> go here.
##### Instructions #####
Tips on how to make delicious <%= name %> go here.
當我們使用 template
方法時,範本檔案會被視為 ERB 範本,它會在目前的 binding
中評估,這表示它可以存取與呼叫它的方法相同的變數和方法。
這樣就完成了!當我們執行 bundle exec cucumber features
時,我們所有的功能都會通過!
3 scenarios (3 passed)
7 steps (7 passed)
很驚人吧,嘿?
如果我們尚未這樣做,我們應該提交儲存庫的所有檔案
$ git add .
$ git commit -m "The beginnings of the foodie gem"
這是因為 foodie.gemspec
檔案使用 git ls-files
來偵測在我們釋出時應該新增到 gem 中的檔案。
在釋出我們的 gem 之前的最後一個步驟是在 foodie.gemspec 檔案中給予它摘要和說明。
現在我們將確保我們的 gem 已準備好發布。為執行此操作,我們可以執行 rake build
,它將建立 gem 的本機副本,然後執行 gem install pkg/foodie-0.1.0.gem
來安裝它。然後,我們可以透過執行它提供的指令,在本地端嘗試它。一旦我們知道一切都運作正常,我們就可以釋出第一個版本。
若要釋出我們 gem 的第一個版本,我們可以使用 rake release
指令,前提是我們已提交所有內容。此指令會執行一些事情。首先,它會將 gem 建立到 pkg 目錄,以準備推送到 Rubygems.org。
其次,它會為目前的提交建立一個標籤,反映目前的版本,並將它推送到 git 遠端。我們建議在 GitHub 上主機程式碼,以便其他人可以輕鬆地找到它。
如果此推送成功,最後一個步驟將是推送到 Rubygems.org,這將允許其他人下載和安裝 gem。
如果我們想要釋出我們 gem 的第二個版本,我們應該進行變更,然後將它們提交到 GitHub。之後,我們將 lib/foodie/version.rb 中的版本號調整為我們認為適當的版本,在 GitHub 中提交另一個有用的訊息,例如「調整到 0.0.2」,然後再次執行 rake release
。
如果我們想要讓此流程更容易,我們可以使用「gem-release」gem 安裝
$ gem install gem-release
此 gem 提供了幾種方法來協助一般的 gem 開發,但最有幫助的是 gem bump
指令,它會將 gem 版本調整到下一個修補程式層級。此方法也採用選項來執行這些事情
$ gem bump --version minor # bumps to the next minor version
$ gem bump --version major # bumps to the next major version
$ gem bump --version 1.1.1 # bumps to the specified version
如需更多資訊,請查看 “gem-release” GitHub 儲存庫首頁。
儘管這不是開發寶石的完整指南,但它涵蓋了寶石開發所需的基本知識。強烈建議您查看 Bundler、Rails 和 RSpec 的原始碼,以獲取寶石開發的絕佳範例。
如果您正在尋找此範例的完整原始碼,可以在這裡找到