1 RDoc
使用 Rails API 文档 是用 RDoc 生成的。若要生成它,请确保您在 rails 根目录中,运行 bundle install
并执行
$ bundle exec rake rdoc
生成的 HTML 文件可以在 ./doc/rdoc
目录中找到。
请参考 RDoc 标记参考 以获得语法方面的帮助。
2 链接
Rails API 文档不适合在 GitHub 上查看,因此链接应该使用 RDoc 链接标记 相对于当前 API 的标记。
这是由于 GitHub Markdown 和发布在 api.rubyonrails.org 和 edgeapi.rubyonrails.org 上的生成的 RDoc 之间的差异。
例如,我们使用 [link:classes/ActiveRecord/Base.html]
来创建指向由 RDoc 生成的 ActiveRecord::Base
类。
这优于绝对 URL,例如 [https://api.rubyonrails.net.cn/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 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 代词
避免使用“你”和“你的”。
# 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 的用户”,应该使用中性代词(他们/他们的/他们)。不要使用
- 他或她... 使用他们。
- 他或她... 使用他们。
- 他或她的... 使用他们的。
- 他或她的... 使用他们的。
- 他自己或她自己... 使用他们自己。
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>
避免使用任何打印方法,例如 puts
或 p
来实现此目的。
另一方面,普通注释不使用箭头。
# 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
当记录 IRB(Ruby 的交互式 REPL)的行为时,始终使用 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 中被定义为使用时,使用常规字体。单例 true
和 false
需要使用固定宽度字体。请避免使用诸如“truthy”之类的术语。Ruby 定义了语言中什么是真和假,因此这些词具有技术意义,不需要替代品。
一般来说,除非绝对必要,否则不要记录单例。这样可以防止诸如 !!
或三元运算符之类的构造,允许重构,并且代码不需要依赖于方法在实现中返回的精确值。
例如
# +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.rb
或 RAILS_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 动态生成的 methods
使用 (module|class)_eval(STRING)
创建的 methods 在其旁边有一个注释,其中包含生成的代码实例。该注释与模板相距 2 个空格。
如果结果行太宽,例如 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 method 可见性
在编写 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 应该是用户界面还是内部的。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 栈,包括 Asset Pipeline 时,我们可能会看到上述结果。
我们想要记录框架的行为,而不仅仅是孤立的方法。我们关心的是用户使用完整的默认 Rails 栈时所经历的行为。
如果您对 Rails 团队如何处理某些 API 有疑问,请随时打开一个问题或向 问题跟踪器 发送补丁。