源代码排版

所有风格都又丑又难读,自己的除外。几乎人人都这样想。把“自己的除外”拿掉,他们或许是对的…

——Jerry Coffin(论缩排)


  • 使用 UTF-8 作为源文件的编码。
    [link]


  • 每个缩排层级使用两个空格。不要使用制表符。
    [link]

    1. # 差 - 四个空格
    2. def some_method
    3. do_something
    4. end
    5. # 好
    6. def some_method
    7. do_something
    8. end

  • 使用 Unix 风格的换行符。(*BSD/Solaris/Linux/OS X 系统的用户不需担心,Windows 用户则要格外小心。)
    [link]

    • 如果你使用 Git,可用下面这个配置来保护你的项目不被 Windows 的换行符干扰:

      1. $ git config --global core.autocrlf true

  • 不要使用 ; 隔开语句与表达式。推论:一行一条语句。
    [link]

    1. # 差
    2. puts 'foobar'; # 不必要的分号
    3. puts 'foo'; puts 'bar' # 一行里有两个表达式
    4. # 好
    5. puts 'foobar'
    6. puts 'foo'
    7. puts 'bar'
    8. puts 'foo', 'bar' # 仅对 puts 适用

  • 对于没有主体的类,倾向使用单行定义。
    [link]

    1. # 差
    2. class FooError < StandardError
    3. end
    4. # 勉强可以
    5. class FooError < StandardError; end
    6. # 好
    7. FooError = Class.new(StandardError)

  • 定义方法时,避免单行写法。尽管这种写法有时颇为普遍,但其略显古怪的定义语法容易使人犯错。无论如何,至少保证单行写法的方法不应该拥有一个以上的表达式。
    [link]

    1. # 差
    2. def too_much; something; something_else; end
    3. # 勉强可以 - 注意第一个 ; 是必选的
    4. def no_braces_method; body end
    5. # 勉强可以 - 注意第二个 ; 是可选的
    6. def no_braces_method; body; end
    7. # 勉强可以 - 语法正确,但没有 ; 使得可读性欠佳
    8. def some_method() body end
    9. # 好
    10. def some_method
    11. body
    12. end

    这个规则的一个例外是空方法。

    1. # 好
    2. def no_op; end

  • 操作符前后适当地添加空格,在逗号 ,、冒号 : 及分号 ; 之后。尽管 Ruby 解释器(大部分情况下)会忽略空格,但适量的空格可以增强代码的可读性。
    [link]

    1. sum = 1 + 2
    2. a, b = 1, 2
    3. class FooError < StandardError; end

    (对于操作符)唯一的例外是当使用指数操作符时:

    1. # 差
    2. e = M * c ** 2
    3. # 好
    4. e = M * c**2

  • ([ 之后,]) 之前,不要添加任何空格。在 { 前后,在 } 之前添加空格。
    [link]
  1. # 差
  2. some( arg ).other
  3. [ 1, 2, 3 ].each{|e| puts e}
  4. # 好
  5. some(arg).other
  6. [1, 2, 3].each { |e| puts e }

{} 需要额外说明,因为它们可以同时用在区块、哈希字面量及字符串插值中。

对于哈希字面量,有两种可被接受的风格。第一种风格更具可读性(在 Ruby 社区里似乎更为流行)。第二种风格的优点是,在视觉上使得区块与哈希字面量有所区分。无论你选择何种风格,务必在使用时保持连贯性。

  1. # 好 - { 之后 与 } 之前有空格
  2. { one: 1, two: 2 }
  3. # 好 - { 之后 与 } 之前无空格
  4. {one: 1, two: 2}

对于插值表达式,括号内两端不要添加空格。

  1. # 差
  2. "From: #{ user.first_name }, #{ user.last_name }"
  3. # 好
  4. "From: #{user.first_name}, #{user.last_name}"

  • ! 之后,不要添加任何空格。
    [link]

    1. # 差
    2. ! something
    3. # 好
    4. !something

  • 范围的字面量语法中,不要添加任何空格。
    [link]

    1. # 差
    2. 1 .. 3
    3. 'a' ... 'z'
    4. # 好
    5. 1..3
    6. 'a'...'z'

  • whencase 缩排在同一层级。这是《Programming Ruby》与《The Ruby Programming Language》中早已确立的风格。
    [link]

    1. # 差
    2. case
    3. when song.name == 'Misty'
    4. puts 'Not again!'
    5. when song.duration > 120
    6. puts 'Too long!'
    7. when Time.now.hour > 21
    8. puts "It's too late"
    9. else
    10. song.play
    11. end
    12. # 好
    13. case
    14. when song.name == 'Misty'
    15. puts 'Not again!'
    16. when song.duration > 120
    17. puts 'Too long!'
    18. when Time.now.hour > 21
    19. puts "It's too late"
    20. else
    21. song.play
    22. end

  • 当将一个条件表达式的结果赋值给一个变量时,保持分支缩排在同一层级。
    [link]

    1. # 差 - 非常费解
    2. kind = case year
    3. when 1850..1889 then 'Blues'
    4. when 1890..1909 then 'Ragtime'
    5. when 1910..1929 then 'New Orleans Jazz'
    6. when 1930..1939 then 'Swing'
    7. when 1940..1950 then 'Bebop'
    8. else 'Jazz'
    9. end
    10. result = if some_cond
    11. calc_something
    12. else
    13. calc_something_else
    14. end
    15. # 好 - 结构清晰
    16. kind = case year
    17. when 1850..1889 then 'Blues'
    18. when 1890..1909 then 'Ragtime'
    19. when 1910..1929 then 'New Orleans Jazz'
    20. when 1930..1939 then 'Swing'
    21. when 1940..1950 then 'Bebop'
    22. else 'Jazz'
    23. end
    24. result = if some_cond
    25. calc_something
    26. else
    27. calc_something_else
    28. end
    29. # 好 - 并且更好地利用行宽
    30. kind =
    31. case year
    32. when 1850..1889 then 'Blues'
    33. when 1890..1909 then 'Ragtime'
    34. when 1910..1929 then 'New Orleans Jazz'
    35. when 1930..1939 then 'Swing'
    36. when 1940..1950 then 'Bebop'
    37. else 'Jazz'
    38. end
    39. result =
    40. if some_cond
    41. calc_something
    42. else
    43. calc_something_else
    44. end

  • 在各个方法定义之间添加空行,并且将方法分成若干合乎逻辑的段落。
    [link]

    1. def some_method
    2. data = initialize(options)
    3. data.manipulate!
    4. data.result
    5. end
    6. def some_method
    7. result
    8. end

  • 在各个段落之间,使用一个空行分隔。
    [link]

    ```Ruby

    差 - 使用了两个空行

    some_method

some_method

some_method

some_method

  1. * <a name="empty-lines-around-access-modifier"></a>
  2. 在属性修饰器之后,使用一个空行分隔。
  3. <sup>[[link](#empty-lines-around-access-modifier)]</sup>
  4. ```Ruby
  5. # 差
  6. class Foo
  7. attr_reader :foo
  8. def foo
  9. # 做一些事情
  10. end
  11. end
  12. # 好
  13. class Foo
  14. attr_reader :foo
  15. def foo
  16. # 做一些事情
  17. end
  18. end

  • 在不同缩进的代码之间,不要使用空行分隔。
    [link]

    1. # 差
    2. class Foo
    3. def foo
    4. begin
    5. do_something do
    6. something
    7. end
    8. rescue
    9. something
    10. end
    11. end
    12. end
    13. # 好
    14. class Foo
    15. def foo
    16. begin
    17. do_something do
    18. something
    19. end
    20. rescue
    21. something
    22. end
    23. end
    24. end

  • 避免在方法调用的最后一个参数之后添加逗号,尤其当参数没有分布在同一行时。
    [link]

    1. # 差 - 尽管移动、新增、删除参数颇为方便,但仍不推荐这种写法
    2. some_method(
    3. size,
    4. count,
    5. color,
    6. )
    7. # 差
    8. some_method(size, count, color, )
    9. # 好
    10. some_method(size, count, color)

  • 当给方法的参数赋予默认值时,在 = 前后添加空格。
    [link]

    1. # 差
    2. def some_method(arg1=:default, arg2=nil, arg3=[])
    3. # 做一些事情
    4. end
    5. # 好
    6. def some_method(arg1 = :default, arg2 = nil, arg3 = [])
    7. # 做一些事情
    8. end

    尽管有几本 Ruby 书籍推荐使用第一种风格,但第二种在实践中更为常见(而且似乎更具可读性)。


  • 避免在非必要的情形下使用续行符 \。在实践中,除了字符串拼接,避免在其他任何地方使用续行。
    [link]

    1. # 差
    2. result = 1 - \
    3. 2
    4. # 好 - 但仍然丑到爆
    5. result = 1 \
    6. - 2
    7. long_string = 'First part of the long string' \
    8. ' and second part of the long string'

  • 使用统一的风格进行多行链式方法调用。在 Ruby 社区中存在两种流行的风格:前置 .(风格 A)与后置 .(风格 B)。
    [link]

    • (风格 A) 当多行链式方法调用需要另起一行继续时,将 . 放在第二行开头。

      1. # 差 - 需要查看第一行才能理解第二行在做什么
      2. one.two.three.
      3. four
      4. # 好 - 立刻能够明白第二行在做什么
      5. one.two.three
      6. .four
    • (风格 B). 放在第一行末尾,以表示当前表达式尚未结束。

      1. # 差 - 需要查看第二行才能知道链式方法调用是否结束
      2. one.two.three
      3. .four
      4. # 好 - 立刻能够明白第二行还有其他方法调用
      5. one.two.three.
      6. four

    两种风格各自优点查阅这里


  • 当方法调用参数过长时,将它们排列在多行并对齐。若对齐后长度超过行宽限制,将首个参数位置挪到下一行进行缩排也是可以接受的。
    [link]

    1. # 初始(行太长了)
    2. def send_mail(source)
    3. Mailer.deliver(to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: source.text)
    4. end
    5. # 差 - 双倍缩排
    6. def send_mail(source)
    7. Mailer.deliver(
    8. to: 'bob@example.com',
    9. from: 'us@example.com',
    10. subject: 'Important message',
    11. body: source.text)
    12. end
    13. # 好
    14. def send_mail(source)
    15. Mailer.deliver(to: 'bob@example.com',
    16. from: 'us@example.com',
    17. subject: 'Important message',
    18. body: source.text)
    19. end
    20. # 好 - 普通缩排
    21. def send_mail(source)
    22. Mailer.deliver(
    23. to: 'bob@example.com',
    24. from: 'us@example.com',
    25. subject: 'Important message',
    26. body: source.text
    27. )
    28. end

  • 当构建数组时,若元素跨行,应当保持对齐。
    [link]

    1. # 差 - 没有对齐
    2. menu_item = ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam',
    3. 'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam']
    4. # 好
    5. menu_item = [
    6. 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam',
    7. 'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam'
    8. ]
    9. # 好
    10. menu_item =
    11. ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam',
    12. 'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam']

  • 使用 _ 语法改善大数的数值字面量的可读性。
    [link]

    1. # 差 - 有几个零?
    2. num = 1000000
    3. # 好 - 方便人脑理解
    4. num = 1_000_000

  • 当数值需要前缀标识进制时,倾向使用小写字母。使用 0o 标识八进制,使用 0x 标识十六进制,使用 0b 标识二进制。十进制数值无需前缀(0d)标识。
    [link]

    1. # 差
    2. num = 01234
    3. num = 0O1234
    4. num = 0X12AB
    5. num = 0B10101
    6. num = 0D1234
    7. num = 0d1234
    8. # 好 - 方便区分数值前缀与具体数字
    9. num = 0o1234
    10. num = 0x12AB
    11. num = 0b10101
    12. num = 1234

  • 使用 [RDoc][rdoc] 及其惯例来编写 API 文档。注意,不要在注释与 def 之间添加空行。
    [link]


  • 将单行长度控制在 80 个字符内。
    [link]


  • 避免行尾空格。
    [link]


  • 文件以空白行结束。
    [link]


  • 不要使用区块注释。它们不能被空白字符引导,且不如常规注释容易辨认。
    [link]

    1. # 差
    2. =begin
    3. comment line
    4. another comment line
    5. =end
    6. # 好
    7. # comment line
    8. # another comment line