源代码排版

源代码排版

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

——Jerry Coffin(论缩排)

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

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

## 差 - 四个空格
def some_method
    do_something
end

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

    • 如果你使用 Git,可用下面这个配置来保护你的项目不被 Windows 的换行符干扰:
$ git config --global core.autocrlf true
  • 不要使用 ; 隔开语句与表达式。推论:一行一条语句。
    [link]
## 差
puts 'foobar'; # 不必要的分号

puts 'foo'; puts 'bar' # 一行里有两个表达式

## 好
puts 'foobar'

puts 'foo'
puts 'bar'

puts 'foo', 'bar' # 仅对 puts 适用
  • 对于没有主体的类,倾向使用单行定义。
    [link]
## 差
class FooError < StandardError
end

## 勉强可以
class FooError < StandardError; end

## 好
FooError = Class.new(StandardError)
  • 定义方法时,避免单行写法。尽管这种写法有时颇为普遍,但其略显古怪的定义语法容易使人犯错。无论如何,至少保证单行写法的方法不应该拥有一个以上的表达式。
    [link]
## 差
def too_much; something; something_else; end

## 勉强可以 - 注意第一个 ; 是必选的
def no_braces_method; body end

## 勉强可以 - 注意第二个 ; 是可选的
def no_braces_method; body; end

## 勉强可以 - 语法正确,但没有 ; 使得可读性欠佳
def some_method() body end

## 好
def some_method
  body
end

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

## 好
def no_op; end
  • 操作符前后适当地添加空格,在逗号 ,、冒号 : 及分号 ; 之后。尽管 Ruby 解释器(大部分情况下)会忽略空格,但适量的空格可以增强代码的可读性。
    [link]
sum = 1 + 2
a, b = 1, 2
class FooError < StandardError; end

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

## 差
e = M * c ** 2

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

  # 好
  some(arg).other
  [1, 2, 3].each { |e| puts e }

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

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

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

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

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

  # 差
  "From: #{ user.first_name }, #{ user.last_name }"

  # 好
  "From: #{user.first_name}, #{user.last_name}"
  • ! 之后,不要添加任何空格。
    [link]
## 差
! something

## 好
!something
  • 范围的字面量语法中,不要添加任何空格。
    [link]
  # 差
  1 .. 3
  'a' ... 'z'

  # 好
  1..3
  'a'...'z'
  • 把 when 与 case 缩排在同一层级。这是《Programming Ruby》与《The Ruby Programming Language》中早已确立的风格。
    [link]
## 差
case
  when song.name == 'Misty'
    puts 'Not again!'
  when song.duration > 120
    puts 'Too long!'
  when Time.now.hour > 21
    puts "It's too late"
  else
    song.play
end

## 好
case
when song.name == 'Misty'
  puts 'Not again!'
when song.duration > 120
  puts 'Too long!'
when Time.now.hour > 21
  puts "It's too late"
else
  song.play
end
  • 当将一个条件表达式的结果赋值给一个变量时,保持分支缩排在同一层级。
    [link]
## 差 - 非常费解
kind = case year
when 1850..1889 then 'Blues'
when 1890..1909 then 'Ragtime'
when 1910..1929 then 'New Orleans Jazz'
when 1930..1939 then 'Swing'
when 1940..1950 then 'Bebop'
else 'Jazz'
end

result = if some_cond
  calc_something
else
  calc_something_else
end

## 好 - 结构清晰
kind = case year
       when 1850..1889 then 'Blues'
       when 1890..1909 then 'Ragtime'
       when 1910..1929 then 'New Orleans Jazz'
       when 1930..1939 then 'Swing'
       when 1940..1950 then 'Bebop'
       else 'Jazz'
       end

result = if some_cond
           calc_something
         else
           calc_something_else
         end

## 好 - 并且更好地利用行宽
kind =
  case year
  when 1850..1889 then 'Blues'
  when 1890..1909 then 'Ragtime'
  when 1910..1929 then 'New Orleans Jazz'
  when 1930..1939 then 'Swing'
  when 1940..1950 then 'Bebop'
  else 'Jazz'
  end

result =
  if some_cond
    calc_something
  else
    calc_something_else
  end
  • 在各个方法定义之间添加空行,并且将方法分成若干合乎逻辑的段落。
    [link]
def some_method
  data = initialize(options)

  data.manipulate!

  data.result
end

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

## 差 - 使用了两个空行

some_method

some_method

## 好

some_method

some_method

  • <a name="empty-lines-around-access-modifier"></a> 在属性修饰器之后,使用一个空行分隔。 <sup>[link]</sup>

```Ruby # 差 class Foo attr_reader :foo def foo # 做一些事情 end end

# 好 class Foo attr_reader :foo

def foo
  # 做一些事情
end

end


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

差

class Foo

def foo

begin

  do_something do

    something

  end

rescue

  something

end

end

end

好

class Foo def foo begin do_something do something end rescue something end end end

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

差 - 尽管移动、新增、删除参数颇为方便,但仍不推荐这种写法

some_method( size, count, color, )

差

some_method(size, count, color, )

好

some_method(size, count, color)

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

差

def some_method(arg1=:default, arg2=nil, arg3=[]) # 做一些事情 end

好

def some_method(arg1 = :default, arg2 = nil, arg3 = []) # 做一些事情 end


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

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

差

result = 1 - \ 2

好 - 但仍然丑到爆

result = 1 \ - 2

long_string = 'First part of the long string' \ ' and second part of the long string'

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

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

差 - 需要查看第一行才能理解第二行在做什么

one.two.three. four

好 - 立刻能够明白第二行在做什么

one.two.three .four

    * **(风格 B)** 将 `.` 放在第一行末尾,以表示当前表达式尚未结束。

差 - 需要查看第二行才能知道链式方法调用是否结束

one.two.three .four

好 - 立刻能够明白第二行还有其他方法调用

one.two.three. four


两种风格各自优点查阅[这里](&lt;https://github.com/bbatsov/ruby-style-guide/pull/176>)。

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

初始(行太长了)

def send_mail(source) Mailer.deliver(to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: source.text) end

差 - 双倍缩排

def send_mail(source) Mailer.deliver( to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: source.text) end

好

def send_mail(source) Mailer.deliver(to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: source.text) end

好 - 普通缩排

def send_mail(source) Mailer.deliver( to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: source.text ) end

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

差 - 没有对齐

menu_item = ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam']

好

menu_item = [ 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam' ]

好

menu_item = ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam']

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

差 - 有几个零?

num = 1000000

好 - 方便人脑理解

num = 1_000_000

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

差

num = 01234 num = 0O1234 num = 0X12AB num = 0B10101 num = 0D1234 num = 0d1234

好 - 方便区分数值前缀与具体数字

num = 0o1234 num = 0x12AB num = 0b10101 num = 1234

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

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

  *   
避免行尾空格。  
[link]

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

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

差

=begin comment line another comment line =end

好

comment line

another comment line

```