v7.1.3.2
更多資訊請至 rubyonrails.org: 更多 Ruby on Rails

API 文件指南

本指南記錄 Ruby on Rails API 文件編寫指南。

閱讀本指南後,您將了解

1 RDoc

使用 Rails API 文件 產生 RDoc。若要產生,請確定您在 rails 根目錄中,執行 bundle install 並執行

$ bundle exec rake rdoc

產生的 HTML 檔案可以在 ./doc/rdoc 目錄中找到。

請參閱 RDoc 標記參考 以取得語法說明。

Rails API 文件並非用於在 GitHub 上檢視,因此連結應使用 RDoc 連結標記 標記,相對於目前的 API。

這是因為 GitHub Markdown 和在 api.rubyonrails.orgedgeapi.rubyonrails.org 發布的已產生 RDoc 之間的差異。

例如,我們使用 [link:classes/ActiveRecord/Base.html] 來建立連結至 RDoc 所產生的 ActiveRecord::Base 類別。

這比使用絕對 URL(例如 [https://rails-api.dev.org.tw/classes/ActiveRecord/Base.html])更佳,因為後者會將讀者帶離他們目前的說明文件版本(例如 edgeapi.rubyonrails.org)。

3 文字

撰寫簡單、具說明性的句子。簡潔明瞭是優點。直奔主題。

# BAD
# Caching may interfere with being able to see the results
# of code changes.

# GOOD
# Caching interferes with seeing the results of code changes.

使用現在式

# BAD
# Returned a hash that...
# Will return a hash that...
# Return a hash that...

# GOOD
# Returns a hash that...

以大寫字母開始註解。遵循一般的標點符號規則

# BAD
# declares an attribute reader backed by an internally-named
# instance variable

# GOOD
# Declares an attribute reader backed by an internally-named
# instance variable.

明確且含蓄地向讀者傳達目前執行的作業方式。使用 edge 中建議的慣用語。視需要重新排列章節以強調偏好的方法等。說明文件應作為最佳實務範例和規範的、現代的 Rails 使用方式。

# BAD
# Book.where('name = ?', "Where the Wild Things Are")
# Book.where('year_published < ?', 50.years.ago)

# GOOD
# Book.where(name: "Where the Wild Things Are")
# Book.where(year_published: ...50.years.ago)

說明文件必須簡潔但全面。探索並記錄臨界狀況。如果模組是匿名的,會發生什麼事?如果集合是空的,會發生什麼事?如果參數為 nil,會發生什麼事?

3.1 命名

Rails 元件的專有名稱在字詞之間有空格,例如「Active Support」。ActiveRecord 是 Ruby 模組,而 Active Record 是 ORM。所有 Rails 說明文件都應一致地以專有名稱來稱呼 Rails 元件。

# GOOD
# Active Record classes can be created by inheriting from
# ActiveRecord::Base.

當參照「Rails 應用程式」時,與「引擎」或「外掛程式」相對應,請務必使用「應用程式」。Rails 應用程式並非「服務」,除非特別討論與服務導向架構相關的事項。

# BAD
# Production services can report their status upstream.
# Devise is a Rails authentication application.

# GOOD
# Production applications can report their status upstream.
# Devise is a Rails authentication enging.

正確拼寫軟體名稱。如有疑問,請參閱一些權威來源,例如其官方文件。

# GOOD
# Arel
# ERB
# Hotwire
# HTML
# JavaScript
# minitest
# MySQL
# npm
# PostgreSQL
# RSpec

為「SQL」使用冠詞「an」

# BAD
# Creates a SQL statement.
# Starts a SQLite database.

# GOOD
# Creates an SQL statement.
# Starts an SQLite database.

3.2 代名詞

優先使用避免「你」和「你的」的措辭。

# BAD
# If you need to use +return+ statements in your callbacks, it is
# recommended that you explicitly define them as methods.

# GOOD
# If +return+ is needed, it is recommended to explicitly define a
# method.

話雖如此,在使用代名詞指涉假設人物時,例如「具有會話 Cookie 的使用者」,應使用性別中立代名詞(他們/他們的/他們自己)。請勿使用

  • 他或她... 使用他們。
  • 他或她... 使用他們。
  • 他的或她的... 使用他們的。
  • 他的或她的... 使用他們的。
  • 他或她自己... 使用他們自己。

4 英文

請使用美式英語(colorcentermodularize 等)。請參閱此處的美式英語和英式英語拼寫差異清單

5 牛津逗號

請使用牛津逗號(「紅色、白色和藍色」,而非「紅色、白色和藍色」)。

6 範例程式碼

選擇有意義的範例,描述並涵蓋基礎知識以及有趣之處或陷阱。

為正確呈現,請從左邊距縮排程式碼兩個空格。範例本身應使用Rails 程式碼慣例

簡短的文件不需要明確的「範例」標籤來介紹程式碼片段;它們僅遵循段落

# Converts a collection of elements into a formatted string by
# calling +to_s+ on all elements and joining them.
#
#   Blog.all.to_fs # => "First PostSecond PostThird Post"

另一方面,大量結構化文件可能有一個單獨的「範例」區段

# ==== Examples
#
#   Person.exists?(5)
#   Person.exists?('5')
#   Person.exists?(name: "David")
#   Person.exists?(['name LIKE ?', "%#{query}%"])

表達式的結果會跟在它們後面,並由「# => 」引入,垂直對齊

# For checking if an integer is even or odd.
#
#   1.even? # => false
#   1.odd?  # => true
#   2.even? # => true
#   2.odd?  # => false

如果一行太長,則註解可以放在下一行

#   label(:article, :title)
#   # => <label for="article_title">Title</label>
#
#   label(:article, :title, "A short title")
#   # => <label for="article_title">A short title</label>
#
#   label(:article, :title, "A short title", class: "title_label")
#   # => <label for="article_title" class="title_label">A short title</label>

避免使用任何列印方法,例如 putsp

另一方面,一般註解不使用箭頭

#   polymorphic_url(record)  # same as comment_url(record)

6.1 SQL

在記錄 SQL 陳述式時,結果不應在輸出前有 =>

例如,

#   User.where(name: 'Oscar').to_sql
#   # SELECT "users".* FROM "users"  WHERE "users"."name" = 'Oscar'

6.2 IRB

在記錄 IRB(Ruby 的互動式 REPL)的行為時,命令前應始終加上 irb> 前綴。輸出應加上 => 前綴。

例如,

# Find the customer with primary key (id) 10.
#   irb> customer = Customer.find(10)
#   # => #<Customer id: 10, first_name: "Ryan">

6.3 Bash / 命令列

對於命令列範例,命令前應始終加上 $ 前綴。輸出不一定要加上任何前綴。

# Run the following command:
#   $ bin/rails new zomg
#   ...

7 布林值

對於謂詞和旗標,優先記錄布林語義,而非確切值。

當「true」或「false」用於 Ruby 中定義時,請使用一般字體。單例 truefalse 需要使用等寬字體。請避免使用「truthy」等術語。Ruby 定義了語言中的 true 和 false,因此這些字詞具有技術含義,不需要替代字詞。

根據經驗法則,除非絕對必要,否則不要記錄單例。這可以防止 !! 或三元運算式等人工建構,允許重構,而且程式碼不需要依賴實作中呼叫方法所傳回的確切值。

例如

# +config.action_mailer.perform_deliveries+ specifies whether mail
# will actually be delivered and is true by default

使用者不需要知道旗標的實際預設值,因此我們只記錄其布林語義。

帶有謂詞的範例

# Returns true if the collection is empty.
#
# If the collection has been loaded it is equivalent to
# +collection.size.zero?+. If the collection has not been loaded,
# it is equivalent to +!collection.exists?+. If the collection has
# not already been loaded and you are going to fetch the records
# anyway, it is better to check +collection.length.zero?+.
def empty?
  if loaded?
    size.zero?
  else
    @target.blank? && !scope.exists?
  end
end

API 謹慎不承諾任何特定值,該方法具有謂詞語義,這就足夠了。

8 檔案名稱

根據經驗法則,使用相對於應用程式根目錄的檔案名稱:config/routes.rb,而非 routes.rbRAILS_ROOT/config/routes.rb

9 字體

9.1 等寬字體

對以下項目使用等寬字體

  • 常數,特別是類別和模組名稱
  • 方法名稱
  • 字面值,例如 nilfalsetrueself
  • 符號
  • 方法參數
  • 檔案名稱
  • HTML 標籤和屬性
  • CSS 選擇器、屬性和值
class Array
  # Calls +to_param+ on all its elements and joins the result with
  # slashes. This is used by +url_for+ in Action Pack.
  def to_param
    collect { |e| e.to_param }.join '/'
  end
end

只對普通類別、模組、方法名稱、符號、路徑(使用正斜線)等簡單內容使用 +...+ 才能使用固定寬度的字型。對於其他所有內容,請使用 <tt>...</tt>

您可以使用下列指令快速測試 RDoc 輸出

$ echo "+:to_param+" | rdoc --pipe
# => <p><code>:to_param</code></p>

例如,包含空白或引號的程式碼應使用 <tt>...</tt> 形式。

9.2 一般字型

當「true」和「false」是英文單字而非 Ruby 關鍵字時,請使用一般字型

# Runs all the validations within the specified context.
# Returns true if no errors are found, false otherwise.
#
# If the argument is false (default is +nil+), the context is
# set to <tt>:create</tt> if <tt>new_record?</tt> is true,
# and to <tt>:update</tt> if it is not.
#
# Validations with no <tt>:on</tt> option will run no
# matter the context. Validations with # some <tt>:on</tt>
# option will only run in the specified context.
def valid?(context = nil)
  # ...
end

10 說明清單

在選項、參數等清單中,請在項目及其說明之間使用連字號(因為選項通常是符號,所以比冒號更好讀)

# * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+.

說明以大寫字母開頭,並以句點結尾,這是標準英文。

當您想要提供其他詳細資訊和範例時,另一種方法是使用選項區段樣式。

ActiveSupport::MessageEncryptor#encrypt_and_sign 就是一個很好的範例。

# ==== Options
#
# [+:expires_at+]
#   The datetime at which the message expires. After this datetime,
#   verification of the message will fail.
#
#     message = encryptor.encrypt_and_sign("hello", expires_at: Time.now.tomorrow)
#     encryptor.decrypt_and_verify(message) # => "hello"
#     # 24 hours later...
#     encryptor.decrypt_and_verify(message) # => nil

11 動態產生的方法

使用 (module|class)_eval(STRING) 建立的方法會在側邊附上一個註解,其中包含產生的程式碼實例。該註解與範本相隔 2 個空白

(module|class)_eval(STRING) code comments

如果產生的行太寬(例如 200 個欄位或更多),請將註解放在呼叫上方

# def self.find_by_login_and_activated(*args)
#   options = args.extract_options!
#   ...
# end
self.class_eval %{
  def self.#{method_id}(*args)
    options = args.extract_options!
    ...
  end
}, __FILE__, __LINE__

12 方法可見性

在撰寫 Rails 文件時,區分使用者導向 API 和內部 API 非常重要。

在 Ruby 私有範圍內的方法會從使用者介面的 API 中排除。然而,某些內部 API 方法必須在 Ruby 的公開範圍中,才能在架構中的其他地方呼叫。若要從使用者介面的 API 中排除此類方法,請使用 RDoc 的 :nodoc: 指令。

範例為 ActiveRecord::Core::ClassMethods#arel_table

module ActiveRecord::Core::ClassMethods
  def arel_table # :nodoc:
    # do some magic..
  end
end

即使這是一個公開方法,使用者也不應依賴它。此方法的名稱可能會變更,或傳回值可能會變更,或此方法可能會完全移除。透過標記 :nodoc:,它會從使用者介面的 API 文件中移除。

作為貢獻者,思考 API 應該是使用者介面還是內部 API 非常重要。Rails 團隊承諾不會在未先經過完整棄用週期的情況下,對使用者介面的 API 進行重大變更。因此,你應該將 :nodoc: 加入任何內部方法或模組,除非它們已經是私有的。(將 :nodoc: 加入模組或類別表示所有方法都是內部 API,且應該從使用者介面的 API 文件中移除。)

13 關於 Rails 堆疊

在記錄 Rails API 的部分時,請務必注意整個 Rails 堆疊。你正在記錄的方法或類別的行為可能會根據脈絡而有所不同。

其中一個範例為 ActionView::Helpers::AssetTagHelper#image_tag

# image_tag("icon.png")
#   # => <img src="/assets/icon.png" />

孤立來看,image_tag 會傳回 /images/icon.png。然而,當我們考量到完整的 Rails 堆疊,包括 Asset Pipeline 時,我們可能會看到上述結果。

我們想要記錄的是架構的行為,而不仅仅是孤立的方法。我們關注的是使用者在使用完整的預設 Rails 堆疊時所體驗到的行為。

如果你對 Rails 團隊如何處理特定 API 有任何疑問,請隨時開啟問題單或將程式碼補丁傳送至 問題追蹤器

回饋

歡迎您協助提升本指南的品質。

如果您發現任何錯字或事實錯誤,請協助我們修正。您可先閱讀我們的文件貢獻章節,了解如何開始。

您也可能會發現內容不完整或已過時。請務必為 main 新增任何遺漏的文件。請先查看Edge Guides,確認問題是否已在 main 分支中修正。請查看Ruby on Rails 指南指南,了解風格和慣例。

如果您發現需要修正的地方,但無法自行修補,請開啟問題

最後,歡迎在官方 Ruby on Rails 論壇上討論任何與 Ruby on Rails 文件相關的事項。