更多内容请访问 rubyonrails.org:

API 文档指南

本指南介绍了 Ruby on Rails API 文档指南。

阅读本指南后,您将了解

  • 如何为文档目的编写有效的散文。
  • 文档化不同种类 Ruby 代码的风格指南。

1. RDoc

Rails API 文档是使用 RDoc 生成的。要生成它,请确保您位于 rails 根目录中,运行 bundle install 并执行

$ bundle exec rake rdoc

生成的 HTML 文件可以在 ./doc/rdoc 目录中找到。

请查阅 RDoc 标记参考 以获取语法帮助。

Rails API 文档不应在 GitHub 上查看,因此链接应使用相对于当前 API 的 RDoc 链接标记

这是由于 GitHub Markdown 和发布在 api.rubyonrails.orgedgeapi.rubyonrails.org 上的 RDoc 生成之间存在差异。

例如,我们使用 [link:classes/ActiveRecord/Base.html] 来创建指向 RDoc 生成的 ActiveRecord::Base 类的链接。

这比绝对 URL(例如 [https://api.rubyonrails.cn/classes/ActiveRecord/Base.html])更可取,因为绝对 URL 会将读者带离其当前的文档版本(例如 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 engine.

正确拼写软件名称。如有疑问,请查阅其官方文档等权威来源。

# 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. 代词

避免使用“you”和“your”的措辞。

# 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.

话虽如此,当使用代词指代一个假设的人,例如“a user with a session cookie”时,应使用中性代词(they/their/them)。而不是

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

3.3. 美式英语

请使用美式英语(color, center, modularize 等)。请参阅此处列出的美式和英式英语拼写差异

3.4. 牛津逗号

请使用牛津逗号("red, white, and blue",而不是 "red, white and blue")。

4. 示例代码

选择有意义的示例,描绘并涵盖基础知识以及有趣的要点或陷阱。

为了正确渲染,代码从左边距缩进两个空格。示例本身应使用 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)

4.1. SQL

在文档化 SQL 语句时,结果不应在输出前有 =>

例如,

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

4.2. IRB

在文档化 Ruby 交互式 REPL IRB 的行为时,始终以 irb> 为命令前缀。输出应以 => 为前缀。

例如,

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

4.3. Bash / 命令行

对于命令行示例,始终以 $ 为命令前缀。输出不需要任何前缀。

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

5. 布尔值

对于谓词和标志,优先文档化布尔语义而不是精确值。

当“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 小心翼翼地不承诺任何特定值,该方法具有谓词语义,这已足够。

6. 文件名

根据经验,使用相对于应用程序根目录的文件名:config/routes.rb 而不是 routes.rbRAILS_ROOT/config/routes.rb

7. 字体

7.1. 等宽字体

使用等宽字体用于

  • 常量,特别是类名和模块名
  • 方法名
  • 字面量,如 nil, false, true, self
  • 符号
  • 方法参数
  • 文件名
  • 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> 形式。

7.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

8. 描述列表

在选项、参数等列表中,在项和其描述之间使用连字符(比冒号读起来更好,因为选项通常是符号)

# * <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

9. 动态生成的方法

使用 (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__

10. 方法可见性

在为 Rails 编写文档时,区分面向用户的 API 和内部 API 非常重要。

处于 Ruby private 作用域中的方法被排除在面向用户的 API 之外。然而,某些内部 API 方法必须处于 Ruby public 作用域中,以便它们可以在框架的其他地方调用。要将此类方法排除在面向用户的 API 之外,请使用 RDoc 的 :nodoc: 指令。

一个例子是 ActiveRecord::Core::ClassMethods#arel_table

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

尽管它是一个公共方法,但用户不应依赖它。此方法的名称可能会更改,或者返回值可能会更改,或者此方法可能会完全删除。通过用 :nodoc: 标记它,它将从面向用户的 API 文档中删除。

作为贡献者,考虑一个 API 应该是面向用户的还是内部的很重要。Rails 团队承诺在不经过完整的弃用周期的情况下,不对面向用户的 API 进行破坏性更改。因此,您应该将 :nodoc: 添加到任何内部方法或模块,除非它们已经是私有的。(将 :nodoc: 添加到模块或类表示所有方法都是内部 API,并且应将其从面向用户的 API 文档中删除。)

11. 关于 Rails 堆栈

在文档化 Rails API 的部分时,重要的是要留意整个 Rails 堆栈。您正在文档化的方法或类的行为可能会因上下文而异。

一个这样的例子是 ActionView::Helpers::AssetTagHelper#image_tag

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

单独来看,image_tag 将返回 /images/icon.png。但是,当我们考虑整个 Rails 堆栈,包括资产管道时,我们可能会看到上述结果。

我们想要文档化 框架 的行为,而不仅仅是孤立的方法。我们关心的是用户在使用完整的默认 Rails 堆栈时所体验到的行为。

如果您对 Rails 团队如何处理某些 API 有疑问,请随时在问题跟踪器上开一个工单或发送补丁。



回到顶部