RubyGems 導覽選單
指南

讓您的寶石使用者和其他開發人員的生活更輕鬆的常見做法。

一致的命名

電腦科學中只有兩件事很難:快取無效化和命名事物。-Phil Karlton

檔案名稱

一致地命名您的寶石檔案在 libbin 中。 hola 寶石來自 製作您自己的寶石 指南是一個很好的範例

% tree
.
├── Rakefile
├── bin
│   └── hola
├── hola.gemspec
├── lib
│   ├── hola
│   │   └── translator.rb
│   └── hola.rb
└── test
    └── test_hola.rb

可執行檔和 lib 中的主要檔案名稱相同。開發人員可以輕鬆地跳入並呼叫 require 'hola' 沒有問題。

為你的寶石命名

為你的寶石命名很重要。在為你的寶石挑選名稱之前,請在 RubyGems.orgGitHub 上快速搜尋,看看是否有人已經取用了。每個已發布的寶石都必須有獨特的名稱。在你找到你喜歡的名稱時,請務必閱讀我們的 命名建議

語意版本控制

版本控制政策只不過是一組簡單的規則,用於管理版本號碼的分配方式。它可以非常簡單(例如版本號碼是一個從 1 開始並隨著每個後續版本遞增的單一數字),或者它可以非常奇怪(Knuth 的 TeX 專案有版本號碼:3、3.1、3.14、3.141、3.1415;每個後續版本都會在 PI 中新增一個數字)。

RubyGems 團隊敦促寶石開發人員為其寶石的版本遵循 語意化版本控制 標準。RubyGems 函式庫本身並未強制執行嚴格的版本控制政策,但使用「非理性」政策只會對社群中使用你的寶石的人造成不便。

假設你有一個「堆疊」寶石,其中包含一個 Stack 類別,同時具有 pushpop 功能。如果你使用語意化版本控制,你的 CHANGELOG 可能如下所示

  • 版本 0.1.0:發布初始 Stack 類別。
  • 版本 0.2.0:切換到連結清單實作,因為它比較酷。
  • 版本 0.3.0:新增 depth 方法。
  • 版本 1.0.0:新增 top 並讓 pop 傳回 nilpop 用於傳回舊的頂部項目)。
  • 版本 1.1.0push 現在傳回推入的值(它用於傳回 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_dependencyadd_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'

此處要記住的重要事項是,其他人使用您的寶石,因此請使用 ~> 代替 >=(如果可能的話),以避免未來版本中潛在的錯誤/失敗。

如果您在應用程式中處理大量的寶石依賴項,我們建議您查看 BundlerIsolate,它們可以很好地管理許多寶石的複雜版本清單。

另外,重要的是要知道,如果您只指定主要版本,如下所示

# 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

這看起來似乎無害,因為您的自訂 erbset 檔案在您的 gem 中。不過,這並非無害,任何需要這個 gem 的人將無法引入 Ruby 標準程式庫提供的 ERBSet 類別。

解決這個問題的最佳方法是將檔案保留在 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.rc11.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

鳴謝

本指南的內容來自多個來源