讓您的寶石使用者和其他開發人員的生活更輕鬆的常見做法。
一致的命名
電腦科學中只有兩件事很難:快取無效化和命名事物。-Phil Karlton
檔案名稱
一致地命名您的寶石檔案在 lib
和 bin
中。 hola 寶石來自 製作您自己的寶石 指南是一個很好的範例
% tree
.
├── Rakefile
├── bin
│ └── hola
├── hola.gemspec
├── lib
│ ├── hola
│ │ └── translator.rb
│ └── hola.rb
└── test
└── test_hola.rb
可執行檔和 lib
中的主要檔案名稱相同。開發人員可以輕鬆地跳入並呼叫 require 'hola'
沒有問題。
為你的寶石命名
為你的寶石命名很重要。在為你的寶石挑選名稱之前,請在 RubyGems.org 和 GitHub 上快速搜尋,看看是否有人已經取用了。每個已發布的寶石都必須有獨特的名稱。在你找到你喜歡的名稱時,請務必閱讀我們的 命名建議。
語意版本控制
版本控制政策只不過是一組簡單的規則,用於管理版本號碼的分配方式。它可以非常簡單(例如版本號碼是一個從 1 開始並隨著每個後續版本遞增的單一數字),或者它可以非常奇怪(Knuth 的 TeX 專案有版本號碼:3、3.1、3.14、3.141、3.1415;每個後續版本都會在 PI 中新增一個數字)。
RubyGems 團隊敦促寶石開發人員為其寶石的版本遵循 語意化版本控制 標準。RubyGems 函式庫本身並未強制執行嚴格的版本控制政策,但使用「非理性」政策只會對社群中使用你的寶石的人造成不便。
假設你有一個「堆疊」寶石,其中包含一個 Stack
類別,同時具有 push
和 pop
功能。如果你使用語意化版本控制,你的 CHANGELOG
可能如下所示
- 版本 0.1.0:發布初始
Stack
類別。 - 版本 0.2.0:切換到連結清單實作,因為它比較酷。
- 版本 0.3.0:新增
depth
方法。 - 版本 1.0.0:新增
top
並讓pop
傳回nil
(pop
用於傳回舊的頂部項目)。 - 版本 1.1.0:
push
現在傳回推入的值(它用於傳回nil
)。 - 版本 1.1.1:修正連結清單實作中的錯誤。
- 版本 1.1.2:修正上次修正中引入的錯誤。
語意化版本控制歸結為
- PATCH
0.0.x
層級變更,用於實作層級詳細變更,例如小錯誤修正 - MINOR
0.x.0
層級變更,用於任何向後相容的 API 變更,例如新功能/特性 - MAJOR
x.0.0
等級變更會導致向後不兼容的 API 變更,例如會在使用者更新後損壞現有使用者程式碼的變更
宣告依賴關係
寶石與其他寶石搭配使用。以下是確保它們彼此友善的一些秘訣。
執行時期與開發時期
RubyGems 提供兩種主要的相依性「類型」:執行時期和開發時期。執行時期相依性是您的寶石運作所需的(例如 rails 需要 activesupport)。
當有人想要修改您的寶石時,開發時期相依性會很有用。當您指定開發時期相依性時,其他開發人員可以執行 gem install --dev your_gem
,而 RubyGems 會擷取兩組相依性(執行時期和開發時期)。典型的開發時期相依性包括測試架構和建置系統。
在 gemspec 中設定相依性很簡單。只要使用 add_runtime_dependency
和 add_development_dependency
Gem::Specification.new do |s|
s.name = "hola"
s.version = "2.0.0"
s.add_runtime_dependency "daemons",
["= 1.1.0"]
s.add_development_dependency "bourne",
[">= 0"]
不要在您的寶石中使用 gem
您可能看過類似這樣的程式碼,用於確保您使用的是寶石的特定版本
gem "extlib", ">= 1.0.8"
require "extlib"
使用寶石的應用程式使用這個是合理的(儘管它們也可以使用 Bundler 等工具)。寶石本身不應該這樣做。它們應該改用 gemspec 中的相依性,以便 RubyGems 可以處理載入相依性,而不是使用者。
悲觀版本約束
如果您的寶石正確遵循 語意化版本 的版本編號規則,那麼其他 Ruby 開發人員在選擇版本約束以在他們的應用程式中鎖定您的寶石時,就可以利用這一點。
假設存在寶石的以下版本
- 版本 2.1.0 — 基準
- 版本 2.2.0 — 引入一些新的(向後兼容的)功能。
- 版本 2.2.1 — 修復一些錯誤
- 版本 2.2.2 — 簡化您的程式碼
- 版本 2.3.0 — 更多新功能(但仍向後兼容)。
- 版本 3.0.0 — 重新設計介面。寫給版本 2.x 的程式碼可能無法運作。
您想要使用一個寶石,並且您已確定版本 2.2.0 可以與您的軟體搭配使用,但版本 2.1.0 沒有您需要的一個功能。在您的寶石(或 Bundler 的 Gemfile
)中新增一個相依性可能看起來像
# gemspec
spec.add_runtime_dependency 'library',
'>= 2.2.0'
# bundler
gem 'library', '>= 2.2.0'
這是「樂觀」版本限制。它表示大於或等於 2.2.0 的所有版本都適用於您的軟體。
不過,您可能知道 3.0 引入了重大變更,不再相容。指定此變更的方法是「悲觀」。這會明確排除可能會中斷您程式碼的版本
# gemspec
spec.add_runtime_dependency 'library',
['>= 2.2.0', '< 3.0']
# bundler
gem 'library', '>= 2.2.0', '< 3.0'
RubyGems 提供了一個捷徑,通常稱為 twiddle-wakka
# gemspec
spec.add_runtime_dependency 'library',
'~> 2.2'
# bundler
gem 'library', '~> 2.2'
請注意,我們已捨棄版本號的 PATCH
層級。如果我們說 ~> 2.2.0
,那將等於 ['>= 2.2.0', '< 2.3.0']
。
如果您要允許使用較新的向下相容版本,但需要特定的錯誤修正,可以使用複合需求
# gemspec
spec.add_runtime_dependency 'library', '~> 2.2', '>= 2.2.1'
# bundler
gem 'library', '~> 2.2', '>= 2.2.1'
此處要記住的重要事項是,其他人會使用您的寶石,因此請使用 ~>
代替 >=
(如果可能的話),以避免未來版本中潛在的錯誤/失敗。
如果您在應用程式中處理大量的寶石依賴項,我們建議您查看 Bundler 或 Isolate,它們可以很好地管理許多寶石的複雜版本清單。
另外,重要的是要知道,如果您只指定主要版本,如下所示
# gemspec
spec.add_runtime_dependency 'library', '~> 2'
它只會使用 2.x 系列中的最新版本,也就是 2.3.0,而不是 3.0.0。此行為可能會讓一些人感到驚訝,但自動允許任何大於版本 2 的主要版本會帶來更令人驚訝的行為。
您也可以使用 !=
排除特定版本。假設版本 2.2.1 有個會導致程式停止運作的錯誤,或意外中斷向下相容性的變更,進而中斷您的寶石。您可以如下排除它
# gemspec
spec.add_runtime_dependency 'library', '~> 2', '!= 2.2.1'
您可以透過將其他版本新增為 add_runtime_dependency
的其他引數來附加其他版本,畢竟其最後一個引數只是一個陣列。
預發行版本依賴項
在使用穩定需求時,Bundler 會「偏好」使用穩定的 gem,只有在必要時才會使用預發行版本 gem。
不過,如果您想要使用預發行版本 gem,您可以使用非數字字元宣告預發行版本需求
# gemspec
spec.add_runtime_dependency 'library', '>= 2.0.0.a', '< 2.0.0'
當提供預發行版本需求時,Bundler 會尊重該 gem 的實際語意化版本優先順序。
需要 RubyGems
摘要:不要。
這行…
require 'rubygems'
…不應該在您的 gem 程式碼中是必要的,因為當 gem 被需要時,RubyGems 已經載入。在您的程式碼中沒有 require 'rubygems'
表示 gem 可以輕鬆使用,而不需要執行 RubyGems 程式。
如需更多資訊,請查看 Ryan Tomayko 關於此主題的原始文章。
載入程式碼
RubyGems 的核心是協助您管理 Ruby 的 $LOAD_PATH
,這是 require
陳述式如何擷取新程式碼的方式。您可以採取多種措施來確保您正確載入程式碼。
尊重全域載入路徑
在封裝您的 gem 檔案時,您需要小心 lib
目錄中的內容。您安裝的每個 gem 都會將其 lib
目錄附加到您的 $LOAD_PATH
。這表示 lib
目錄頂層的任何檔案都可能被需要。
例如,假設我們有一個結構如下所示的 foo
gem
.
└── lib
├── foo
│ └── cgi.rb
├── erb.rb
├── foo.rb
└── set.rb
這看起來似乎無害,因為您的自訂 erb
和 set
檔案在您的 gem 中。不過,這並非無害,任何需要這個 gem 的人將無法引入 Ruby 標準程式庫提供的 ERB 或 Set 類別。
解決這個問題的最佳方法是將檔案保留在 lib
下的不同目錄中。慣例通常是一致的,並將它們放在與您的 gem 名稱相同的資料夾名稱中,例如 lib/foo/cgi.rb
。
互相需要檔案
寶石不應使用 __FILE__
來引入其他 Ruby 檔案到你的寶石中。這種程式碼在寶石中意外地常見
require File.join(
File.dirname(__FILE__),
"foo", "bar")
或
require File.expand_path(File.join(
File.dirname(__FILE__),
"foo", "bar"))
修正方法很簡單,只要根據載入路徑來載入檔案
require 'foo/bar'
或使用 require_relative
require_relative 'foo/bar'
製作你自己的寶石 指南有一個實務上此行為的範例,包含一個可用的測試套件。那個寶石的程式碼在 GitHub 上。
破壞載入路徑
寶石不應變更 $LOAD_PATH
變數。RubyGems 會為你管理這個。以下程式碼不應是必要的
lp = File.expand_path(File.dirname(__FILE__))
unless $LOAD_PATH.include?(lp)
$LOAD_PATH.unshift(lp)
end
或
__DIR__ = File.dirname(__FILE__)
$LOAD_PATH.unshift __DIR__ unless
$LOAD_PATH.include?(__DIR__) ||
$LOAD_PATH.include?(File.expand_path(__DIR__))
當 RubyGems 啟動一個寶石時,它會將你的套件的 lib
資料夾加入到 $LOAD_PATH
中,準備好讓另一個 lib 或應用程式正常載入。你可以安全地假設你可以在你的 lib
資料夾中載入任何檔案。
預發佈寶石
許多寶石開發人員在一個大型寶石發布之前,準備好他們的寶石版本進行測試或「邊緣」發布。RubyGems 支援「預發布」版本的觀念,這些版本可能是測試版、開發版,或任何其他尚未準備好作為正式發布的版本。
要利用這個功能很簡單。你只需要在寶石版本中使用一個或多個字母。例如,以下是一個預發布 gemspec 的 version
欄位可能的外觀
Gem::Specification.new do |s|
s.name = "hola"
s.version = "1.0.0.pre"
其他預發布版本號碼可能包括 2.0.0.rc1
或 1.5.0.beta.3
。它只需要包含一個字母,你就可以設定了。這些寶石可以使用 --pre
旗標安裝,如下所示
% gem list factory_bot -r --pre
*** REMOTE GEMS ***
factory_bot (2.0.0.beta2, 2.0.0.beta1)
factory_bot_rails (1.1.beta1)
% gem install factory_bot --pre
Successfully installed factory_bot-2.0.0.beta2
1 gem installed
鳴謝
本指南的內容來自多個來源