Ruby Style

# The Ruby Style Guide

> Hey jude, don't make it bad.

> Take a sad song and make it better.

> Remember to let her into your heart,

> Then you can start to make it better.

* [github Ruby style guide](https://github.com/styleguide/ruby)

* [community-driven Ruby style guide](https://github.com/bbatsov/ruby-style-guide)

* [Ruby on Rails 3 Style Guide](https://github.com/bbatsov/rails-style-guide)

* [代碼大全](http://book.douban.com/subject/1477390/)

* [代碼整潔之道](http://book.douban.com/subject/4199741/)

* [重構(gòu)](http://book.douban.com/subject/1229923/)

## 排版

* 使用`UTF-8`編碼

```VimL

set encoding=utf-8

set fileencodings=utf-8,gb2312,gb18030,gbk,ucs-bom,cp936,latin1 " 如果你要打開的文件編碼不在此列吏砂,那就添加進(jìn)去

set termencoding=utf-8

```

* 使用等寬字體

```VimL

set guifont=Bitstream\ Vera\ Sans\ Mono\ 12

```

* 使用兩個空格作為縮進(jìn)

```Ruby

# good

def some_method

do_something

end

# bad - four spaces

def some_method

do_something

end

```

* 不要在縮進(jìn)中留下制表符(Tab)芯砸。設(shè)置編輯器芬位,自動將制表符展開為兩個空格

```VimL

" 示例1:全局設(shè)置

set autoindent

set expandtab

set tabstop=2 shiftwidth=2 softtabstop=2

```

```VimL

" 示例2:每語言單獨設(shè)置

autocmd FileType ruby,eruby,yaml set ai ts=2 sw=2 sts=2 et

```

* 正確拼寫英文單詞

```Ruby

# bad

def has_joind?(talbe_name)

return false unless scoped.respond_to?(:joins_values)

scoped.joins_values.any?{|joins| joins =~ /inner\s+join\s+#{talbe_name}/i }

end

# good

def has_joined?(table_name)

return false unless scoped.respond_to?(:joins_values)

scoped.joins_values.any? { |joins| joins =~ /inner\s+join\s+#{table_name}/i }

end

```

* 盡量將行的長度控制在80個字符以下

```Ruby

# bad

def some_method

a_long_long_long_long_long_long_long_method_call if condition1 && condition2 && condition3

a_long_long_long_long_long_long_long_operation1 && a_long_long_long_long_long_long_long_operation2 && a_long_long_long_long_long_long_long_operation3

end

# good, 但更好的做法是考慮重命名方法或?qū)⑾鄳?yīng)邏輯抽取成子方法

def some_method

if condition1 && condition2 && condition3

a_long_long_long_long_long_long_long_method_call

end

a_long_long_long_long_long_long_long_operation1 &&

a_long_long_long_long_long_long_long_operation2 &&

a_long_long_long_long_long_long_long_operation3

end

```

* 不要在行尾留下空白字符

```VimL

set list

set listchars=tab:,.,trail:.,extends:#,nbsp:. " Highlight problematic whitespace

```

* 在以下位置加上空格:操作符前后, 逗號摸袁、冒號撼短、分號后祈秕,'{'前后牺堰, '}'前

```Ruby

sum = 1 + 2

result = 10 / (1 + 2)

a, b = 1, 2

1 > 2 ? some_operation : other_operation

[1, 2, 3].each { |e| puts e }

hash = { key1: 'luo', key2: 'xin' }

def find(name, options = {})

result

end

```

唯一的例外是指數(shù)操作符

```Ruby

# bad

e = M * c ** 2

# good

e = M * c**2

```

* 以下位置沒有空格:'(', '['后和']', ')'前

```Ruby

some(arg).other

[1, 2, 3].length

```

* 保證`when`和`case`的縮進(jìn)層次相同

```Ruby

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

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

```

* 使用空行分隔不同方法及同一方法的不同邏輯段

```Ruby

def some_method

data = initialize(options)

first_method_call_with(data)

second_method_call_with(data)

third_method_call_with(data)

data.result

end

def other_method

result

end

```

* 如果使用表達(dá)式賦值梁呈,表達(dá)式的縮進(jìn)要保持一致

```Ruby

# bad

result = if index > 10

do_something

elsif index > 5

do_other_thing

else

puts 'I can not bear it.'

end

# good

result = if index > 10

do_something

elsif index > 5

do_other_thing

else

puts 'I can not bear it.'

end

```

* 多行賦值時缨伊,為保持美觀摘刑,盡量將等號對齊

```Ruby

# ok

var1 = 1

var12 = 12

var12345 = 12345

var123 = 123

# good

var1 = 1

var12 = 12

var12345 = 12345

var123 = 123

```

* 使用哈希賦值時,如果哈希長度過長刻坊,應(yīng)該用多行表示枷恕,且盡量保持對齊

```Ruby

# very bad

result = {:key1=>:value1,:key2=>:value2}

# bad

result = { :this_is_the_first_key => :this_is_the_first_value, :this_is_another_key => :this_is_another_value, :this_is_the_last_key => :this_is_the_last_value }

# good for ruby 1.8

result = { :key1 => :value1, :key2 => :value2 }

result = { :this_is_the_first_key => :this_is_the_first_value,

:this_is_another_key => :this_is_another_value,

:this_is_the_last_key => :this_is_the_last_value }

# good for ruby 1.9

result = { key1: :value1, key2: :value2 }

result = { this_is_the_first_key: :this_is_the_first_value,

this_is_another_key: :this_is_another_value,

this_is_the_last_key: :this_is_the_last_value }

```

## 語法

* 定義方法時,如果帶參要加上括號谭胚,否則不要加括號

```Ruby

def some_method

# body omitted

end

def some_method_with_arguments(arg1, arg2)

# body omitted

end

```

* 使用迭代器徐块,而不要使用for語句

```Ruby

# bad

for elem in arr do

puts elem

end

# good

arr.each { |elem| puts elem }

```

* 不要在多行的if/unless語句中使用then

```Ruby

# bad

if some_condition then

# body omitted

end

# good

if some_condition

# body omitted

end

```

* 不要使用冗長的三元表達(dá)式(?:)。但在邏輯較簡單灾而,能用一行表達(dá)的情況下胡控,使用三元表達(dá)式而不是if/then/else/end語句

```Ruby

# bad

result = if some_condition then something else something_else end

# good

result = some_condition ? something : something_else

```

* 三元表達(dá)式中不要出現(xiàn)邏輯分支。在這種情況下應(yīng)該使用if/else語句

```Ruby

# bad

some_condition ? (nested_condition ? nested_something : nested_something_else) : something_else

# good

if some_condition

nested_condition ? nested_something : nested_something_else

else

something_else

end

```

* 不要使用and和or. 總是使用&&和||

```Ruby

# bad. 注意操作符的優(yōu)先級

# 等價于(result = find_by_name(name)) or find_by_code(code) or fail 'wow!'

result = find_by_name(name) or find_by_code(code) or fail 'wow!'

# good

result = find_by_name(name) || find_by_code(code) || fail('wow!')

```

* 不要跨行使用三元表達(dá)式, 使用if/unless語句

* 在語句非常簡短的情況下使用單行后置if/unless語句

```Ruby

# bad

if some_condition

do_something

end

# good

do_something if some_condition

```

* 不要使用unless/else語句旁趟,將它改寫為if/else語句

```Ruby

# bad

unless success?

puts 'failure'

else

puts 'success'

end

# good

if success?

puts 'success'

else

puts 'failure'

end

```

* 不要對if/unless/while語句的中的單個條件使用括號

```Ruby

# bad

if (x > 10)

# body omitted

end

# good

if x > 10

# body omitted

end

# good, 不用括號語義會改變昼激。即使用括號也不要用or

if (x = something) && y

# body omitted

end

```

* 單行block使用花括號{...}. 多行block使用do...end. 不要使用do...end進(jìn)行鏈?zhǔn)秸{(diào)用

```Ruby

names = %w(Bozhidar Steve Sarah)

# good

names.each { |name| puts name }

# bad

names.each do |name|

puts name

end

# good

names.select { |name| name.start_with?('S') }.map { |name| name.upcase }

# bad

names.select do |name|

name.start_with?("S")

end.map { |name| name.upcase }

# ok, 但更佳的方式是把復(fù)雜邏輯包裝成方法調(diào)用

names.select { |name| name.start_with?('S') && name.end_with?('e') }.

map { |name| name.upcase }

```

* 不要在不必要的情況下使用return

```Ruby

# bad

def some_method(some_arr)

return some_arr.size

end

# good

def some_method(some_arr)

some_arr.size

end

```

* 方法定義時,在定義有默認(rèn)值的參數(shù)時,等號的前后要加上空格

```Ruby

# bad

def some_method(arg1=:default, arg2=nil, arg3=[])

# do something...

end

# good

def some_method(arg1 = :default, arg2 = nil, arg3 = [])

# do something...

end

```

* 可以使用賦值語句(=)的返回值橙困,但要注意優(yōu)先級瞧掺,需要時以括號環(huán)繞

```Ruby

# ok

if (v = array.grep(/foo/))

# do something...

end

# good

if v = array.grep(/foo/)

# do something...

end

# also good - 用括號保證操作符優(yōu)先級正確

if (v = self.next_value) == "hello"

# do something...

end

```

* 可以使用||=給變量賦初值,但不要使用||=給布爾類型變量賦初值(因為原始值可能為false)

```Ruby

# set name to Bozhidar, only if it's nil or false

name ||= 'Bozhidar'

# bad - would set enabled to true even if it was false

enabled ||= true

# good

enabled = true if enabled.nil?

```

* 在用實例變量實現(xiàn)memoize模式的方法中凡傅,使用defined?而不是||=

```Ruby

# bad, 當(dāng)do_something的返回值為nil或false時辟狈,沒有起到memoize的效果

def some_method

@result ||= do_something

end

# good

def some_method

defined?(@result) ? @result : @result = do_something

end

```

* 不要在方法名和括號中間加空格

```Ruby

# bad

f (3 + 2) + 1

# bad

def fun (a)

a * 2

end

# good

f(3 + 2) + 1

# good

def fun(a)

a * 2

end

```

* 用下劃線取代未使用的block參數(shù)

```Ruby

# bad

result = hash.map { |k, v| v + 1 }

# good

result = hash.map { |_, v| v + 1 }

```

## 命名規(guī)則

* 方法和變量使用snake_case

```Ruby

# bad

def someMethod

myName = 'luoxin'

end

# good

def some_method

my_name = 'luoxin'

end

```

* 類和模塊的定義使用CamelCase. 注意保持首字母縮寫詞的原有形式,如HTTP, RFC, XML等

```Ruby

# bad

module HttpServer

# ...

end

# good

module HTTPServer

# ...

end

```

* 常量的定義使用SCREAMING_SNAKE_CASE

```Ruby

# bad

class Week

Days_In_A_Week = 7

end

# good

class Week

DAYS_IN_A_WEEK = 7

end

```

* 配置相關(guān)信息不要使用常量

```Ruby

# bad

class JavaService

JAVA_SERVICE_URL = '192.168.8.81'

end

# good

class JavaService

cattr_accessor :url, instance_write: false

self.url = '192.168.8.81'

end

```

* 邏輯判斷的方法(返回值為布爾型)應(yīng)該使用形容詞或動詞完成時態(tài)像捶,并以問號結(jié)尾上陕。不要使用is_前綴

```Ruby

# bad

def empty

something.count > 0

end

# bad

def is_empty?

something.count > 0

end

# good

def empty?

something.count > 0

end

```

* 使用具有描述性的方法名和變量名

```Ruby

# bad

def gen_ymdhms

# ...

end

# good

def generate_timestamp

# ...

end

# bad

all = deals.map { |x| x.id }

# good

deal_ids = deals.map { |deal| deal.id }

```

## 集合

* 當(dāng)聲明全由字符串組成的數(shù)組時桩砰,使用%w()

```Ruby

# bad

STATES = ['draft', 'open', 'closed']

# good

STATES = %w(draft open closed)

```

* 如果集合中的元素皆為唯一拓春,使用Set而不是Array

```Ruby

s = Set.new

s << 1 << 2 << 3 << 2 << 1 #

```

* 盡量使用symbol而不是string作為哈希的鍵

```Ruby

# bad

hash = { 'one' => 1, 'two' => 2, 'three' => 3 }

# good for ruby 1.8

hash = { :one => 1, :two => 2, :three => 3 }

# good for ruby 1.9

hash = { one: 1, two: 2, three: 3 }

```

## 字符串

* 使用字符串插入(interpolation),不要使用字符串加法

```Ruby

# bad

email_with_name = user.name + ' <' + user.email + '>'

# good

email_with_name = "#{user.name} <#{user.email}>"

```

* 當(dāng)不需要字符串插入或者處理\t, \n等特殊字符時亚隅,使用單引號

```Ruby

# bad

require "active_support"

name = "Bozhidar"

# good

require 'active_support'

name = 'Bozhidar'

```

* 構(gòu)造字符串時不要使用字符串加法, 要使用字符串連接操作符

```Ruby

# good and also fast

html = ''

html << 'Page title'

paragraphs.each { |paragraph| html << "#{paragraph}" }

```

## 特殊構(gòu)造符

* 使用%w構(gòu)造字符串?dāng)?shù)組

```Ruby

STATES = %w(draft open closed)

```

* 使用%()構(gòu)造單行的硼莽、包含雙引號和插入的字符串

```Ruby

# bad (no interpolation needed)

%(Some text)

# should be 'Some text'

# bad (no double-quotes)

%(This is #{quality} style)

# should be "This is #{quality} style"

# bad (multiple lines)

%(\n#{exclamation}\n)

# should be a heredoc

# bad (no double-quotes)

%(#{name})

# should be "#{name}"

# good (requires interpolation, has double quotes, single line)

%(#{name})

```

* 對多行的字符串使用heredoc

```Ruby

# bad (multiple lines)

%(\n#{exclamation}\n)

# should be a heredoc

# good

<<-HTML

#{exclamation}

HTML

```

* 在正則表達(dá)式中包含多個'/'時使用%r

```Ruby

# bad

%r(\s+)

# still bad

%r(^/(.*)$)

# should be /^\/(.*)$/

# good

%r(^/blog/2011/(.*)$)

```

* 不要使用%q, %Q, %x, %s, and %W. 使用%構(gòu)造符時,首選()作為分割符

```Ruby

# bad

name = %q(I'm luoxin)

# should be "I'm luoxin" or 'I\'m luoxin'

# bad

div = %Q()

# should be %()

# bad

div = %Q[div id="#@div_id" name="#@div_name">]

# should be %()

# good

js = %[alert("Hi, #@user_name");]

```

## 其他

* 代碼中嵌入SQL語句時煮纵,SQL關(guān)鍵字大寫懂鸵,其他字符小寫

```Ruby

# bad

User.find_by_sql('select id, name from users where id < 5')

# good

User.find_by_sql('SELECT id, name FROM users WHERE id < 5')

```

* 如果代碼中嵌入的SQL語句過長,使用換行來劃分邏輯

```Ruby

# bad, too long

ActiveRecord::Base.connection.execute('INSERT INTO hui800.deals VALUES (id, code, title, start_time, end_time) SELECT id, code, name, start_date, end_date FROM coupons.deal')

# good

ActiveRecord::Base.connection.execute <<-SQL

INSERT INTO hui800.deals

VALUES (id, code, title, start_time, end_time)

SELECT id, code, name, start_date, end_date

FROM coupons.deal

SQL

```

* 不要出現(xiàn)N+1查詢

```Ruby

# bad, N+1

users = User.where('id < 100')

users.each { |u| puts u.school }

# good

users = User.where('id < 100').includes(:school)

users.each { |u| puts u.school }

```

* 方法參數(shù)的個數(shù)盡量不超過兩個行疏,最多不超過三個(*args除外)匆光。如果有多選項,一般通過最末的哈希參數(shù)來傳遞

```Ruby

# bad

def my_cache(key, city_id = nil, page = nil, per_page = nil, order_by = nil)

end

# good

def my_cache(key, options = {})

end

```

* 在Ruby 1.9中使用新的哈希語法

```Ruby

# bad for Ruby 1.9

hash = { :a => 1, :b => 2, :c => 3 }

# good for Ruby 1.9

hash = { a: 1, b: 2, c: 3 }

```

* 在Ruby 1.9中使用新的lambda語法

```Ruby

# bad for Ruby 1.9

my_lambda = lambda { |x| puts x }

my_lambda.call(3)

# good for Ruby 1.9

my_lambda = ->(x) { puts x }

my_lambda.(3)

```

* 在不期望修改的對象上調(diào)用freeze方法

```Ruby

# bad

configuration = { database: 'tuan800', adapter: 'mysql2' }

# good

configuration = { database: 'tuan800', adapter: 'mysql2' }.freeze

```

* 熟練使用一種編輯器酿联。推薦使用Vim或Emacs

## 原則

1. Consistency(一致性)

2. KISS(簡潔性)

3. Common sense

4. Follow your heart

> 思想终息、語言、文字贞让,是一體的周崭,假如念起來亂糟糟,意思也不

> 會好——這是最簡單的真理喳张,但假如沒有前輩來告訴我续镇,我怎么

> 會知道啊。有時我也寫點不負(fù)責(zé)任的粗糙文字销部,以后重讀時摸航,

> 慚愧得無地自容,真想自己脫了褲子請道乾先生打我兩棍舅桩。孟

> 子曾說酱虎,無恥之恥,無恥矣〗龋現(xiàn)在我在文學(xué)上是個有廉恥的人逢净,

> 都是多虧了這些先生的教誨。對我來說,他們的作品是比鞭子

> 還有力量的鞭策爹土。

> 我一直想承認(rèn)我的文學(xué)師承是這樣一條鮮為人知的線索甥雕。這是

> 給我臉上貼金。我最終寫出了這些胀茵,不是因為我的書已經(jīng)寫得

> 好了社露,而是因為,不把這個秘密說出來琼娘,對現(xiàn)在的年輕人是不

> 公道的峭弟。沒有人告訴他們這些,只按名聲來理解文學(xué)脱拼,就會不

> 知道什么是壞瞒瘸,什么是好。

> --王小波《我的師承》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末熄浓,一起剝皮案震驚了整個濱河市情臭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌赌蔑,老刑警劉巖俯在,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異娃惯,居然都是意外死亡跷乐,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進(jìn)店門趾浅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來愕提,“玉大人,你說我怎么就攤上這事潮孽【救伲” “怎么了?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵往史,是天一觀的道長仗颈。 經(jīng)常有香客問我,道長椎例,這世上最難降的妖魔是什么挨决? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮订歪,結(jié)果婚禮上脖祈,老公的妹妹穿的比我還像新娘。我一直安慰自己刷晋,他們只是感情好盖高,可當(dāng)我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布慎陵。 她就那樣靜靜地躺著,像睡著了一般喻奥。 火紅的嫁衣襯著肌膚如雪席纽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天撞蚕,我揣著相機與錄音润梯,去河邊找鬼。 笑死甥厦,一個胖子當(dāng)著我的面吹牛纺铭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播刀疙,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼舶赔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了庙洼?” 一聲冷哼從身側(cè)響起顿痪,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤镊辕,失蹤者是張志新(化名)和其女友劉穎油够,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體征懈,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了捕传。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片录淡。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖亏娜,靈堂內(nèi)的尸體忽然破棺而出焕窝,到底是詐尸還是另有隱情,我是刑警寧澤维贺,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布它掂,位于F島的核電站,受9級特大地震影響溯泣,放射性物質(zhì)發(fā)生泄漏虐秋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一垃沦、第九天 我趴在偏房一處隱蔽的房頂上張望客给。 院中可真熱鬧,春花似錦肢簿、人聲如沸靶剑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽桩引。三九已至官觅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間阐污,已是汗流浹背休涤。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留笛辟,地道東北人功氨。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像手幢,于是被迫代替她去往敵國和親捷凄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,647評論 2 354

推薦閱讀更多精彩內(nèi)容