ruby代码规范

Posted Ruby与Go研习社

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ruby代码规范相关的知识,希望对你有一定的参考价值。

The Ruby Style Guide

  • github Ruby style guide:https://github.com/github/rubocop-github/blob/master/STYLEGUIDE.md


  • 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

  • 代码大全:https://book.douban.com/subject/1477390/

  • 代码整洁之道:https://book.douban.com/subject/4199741/

  • 重构:https://book.douban.com/subject/1229923/

排版

  • 使用UTF-8编码

set encoding=utf-8
set fileencodings=utf-8,gb2312,gb18030,gbk,ucs-bom,cp936,latin1 " 如果你要打开的文件编码不在此列,那就添加进去
set termencoding=utf-8
  • 使用等宽字体

set guifont=Bitstream\ Vera\ Sans\ Mono\ 12
  • 使用两个空格作为缩进

# good
def some_method do_something
end

# bad - four spaces
def some_method do_something
end
  • 不要在缩进中留下制表符(Tab)。设置编辑器,自动将制表符展开为两个空格

" 示例1:全局设置
set autoindent
set expandtab
set tabstop=2 shiftwidth=2 softtabstop=2
" 示例2:每语言单独设置
autocmd FileType ruby,eruby,yaml set ai ts=2 sw=2 sts=2 et
  • 正确拼写英文单词

# 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个字符以下


# 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, 但更好的做法是考虑重命名方法或将相应逻辑抽取成子方法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
  • 不要在行尾留下空白字符


set list
set listchars=tab:,.,trail:.,extends:#,nbsp:. " Highlight problematic whitespace
  • 在以下位置加上空格:操作符前后, 逗号、冒号、分号后,'{'前后, '}'前

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

唯一的例外是指数操作符

# bad
e = M * c ** 2

# good
e = M * c**2
  • 以下位置没有空格:'(', '['后和']', ')'前

some(arg).other
[1, 2, 3].length
  • 保证whencase的缩进层次相同

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
  • 使用空行分隔不同方法及同一方法的不同逻辑段

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
  • 如果使用表达式赋值,表达式的缩进要保持一致


# 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
  • 多行赋值时,为保持美观,尽量将等号对齐

# ok

var1 = 1

var12 = 12

var12345 = 12345

var123 = 123


# good

var1     = 1

var12    = 12

var12345 = 12345

var123   = 123

  • 使用哈希赋值时,如果哈希长度过长,应该用多行表示,且尽量保持对齐

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


语法

  • 定义方法时,如果带参要加上括号,否则不要加括号


 def some_method
   # body omitted
 end

 def some_method_with_arguments(arg1, arg2)
# body omitted end
  • 使用迭代器,而不要使用for语句


# bad
for elem in arr do puts elem
end

# good
arr.each { |elem| puts elem }
  • 不要在多行的if/unless语句中使用then

# bad
if some_condition then # body omitted
end

# good
if some_condition
# body omitted
end

  • 不要使用冗长的三元表达式(?:)。但在逻辑较简单,能用一行表达的情况下,使用三元表达式而不是if/then/else/end语句

# bad
result = if some_condition then something else something_else end

# good
result = some_condition ? something : something_else
  • 三元表达式中不要出现逻辑分支。在这种情况下应该使用if/else语句


# 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. 总是使用&&和||

# bad. 注意操作符的优先级
# 等价于(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!')
  • 不要跨行使用三元表达式, 使用if/unless语句

  • 在语句非常简短的情况下使用单行后置if/unless语句

# bad
if some_condition do_something
end

# good
do_something if some_condition
  • 不要使用unless/else语句,将它改写为if/else语句

# bad
unless success? puts 'failure'
else puts 'success'
end

# good
if success? puts 'success'
else puts 'failure'
end
  • 不要对if/unless/while语句的中的单个条件使用括号

# 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进行链式调用

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, 但更佳的方式是把复杂逻辑包装成方法调用
names.select { |name| name.start_with?('S') && name.end_with?('e') }. map { |name| name.upcase }
  • 不要在不必要的情况下使用return

# bad
def some_method(some_arr)
return some_arr.size
end

# good
def some_method(some_arr) some_arr.size
end
  • 方法定义时,在定义有默认值的参数时,等号的前后要加上空格

# bad
def some_method(arg1=:default, arg2=nil, arg3=[])
# do something...
end

# good
def some_method(arg1 = :default, arg2 = nil, arg3 = [])
# do something...
end
  • 可以使用赋值语句(=)的返回值,但要注意优先级,需要时以括号环绕

# ok
if (v = array.grep(/foo/))
# do something...
end

# good
if v = array.grep(/foo/)
# do something...
end

# also good - 用括号保证操作符优先级正确
if (v = self.next_value) == "hello" # do something...
end
  • 可以使用||=给变量赋初值,但不要使用||=给布尔类型变量赋初值(因为原始值可能为false)

# 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?
  • 在用实例变量实现memoize模式的方法中,使用defined?而不是||=

# bad, 当do_something的返回值为nil或false时,没有起到memoize的效果
def some_method @result ||= do_something
end

# good
def some_method defined?(@result) ? @result : @result = do_something
end
  • 不要在方法名和括号中间加空格

# 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参数

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

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

命名规则

  • 方法和变量使用snake_case

# bad
def someMethod myName = 'luoxin'
end

# good
def some_method my_name = 'luoxin'
end
  • 类和模块的定义使用CamelCase. 注意保持首字母缩写词的原有形式,如HTTP, RFC, XML等

# bad
module HttpServer # ...
end

# good
module HTTPServer # ...
end
  • 常量的定义使用SCREAMING_SNAKE_CASE

# bad
class Week Days_In_A_Week = 7
end

# good
class Week DAYS_IN_A_WEEK = 7
end
  • 配置相关信息不要使用常量

# 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
  • 逻辑判断的方法(返回值为布尔型)应该使用形容词或动词完成时态,并以问号结尾。不要使用is_前缀

# bad
def empty something.count > 0
end

# bad
def is_empty? something.count > 0
end

# good
def empty? something.count > 0
end
  • 使用具有描述性的方法名和变量名

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

集合

  • 当声明全由字符串组成的数组时,使用%w()

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

# good
STATES = %w(draft open closed)
  • 如果集合中的元素皆为唯一,使用Set而不是Array

s = Set.new
s << 1 << 2 << 3 << 2 << 1 # <Set: {1, 2, 3}>
  • 尽量使用symbol而不是string作为哈希的键

# 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),不要使用字符串加法

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

# good
email_with_name = "#{user.name} <#{user.email}>"
  • 当不需要字符串插入或者处理\t, \n等特殊字符时,使用单引号

# bad
require "active_support"
name = "Bozhidar"

# good
require 'active_support'
name = 'Bozhidar'
  • 构造字符串时不要使用字符串加法, 要使用字符串连接操作符

# good and also fast
html = ''
html << '<h1>Page title</h1>'
paragraphs.each { |paragraph| html << "<p>#{paragraph}</p>" }

特殊构造符

  • 使用%w构造字符串数组

STATES = %w(draft open closed)
  • 使用%()构造单行的、包含双引号和插入的字符串

# bad (no interpolation needed)
%(<div class="text">Some text</div>)
# should be '<div class="text">Some text</div>'

# bad (no double-quotes)
%(This is #{quality} style)
# should be "This is #{quality} style"

# bad (multiple lines)
%(<div>\n<span class="big">#{exclamation}</span>\n</div>)
# should be a heredoc

# bad (no double-quotes)
%(<tr><td class='name'>#{name}</td>)
# should be "<tr><td class='name'>#{name}</td>"

# good (requires interpolation, has double quotes, single line)
%(<tr><td class="name">#{name}</td>)
  • 对多行的字符串使用heredoc

# bad (multiple lines)
%(<div>\n<span class="big">#{exclamation}</span>\n</div>)
# should be a heredoc

# good
<<-HTML
 <div>
   <span class="big">#{exclamation}</span>
 </div>
HTML
  • 在正则表达式中包含多个'/'时使用%r

# bad
%r(\s+)# still bad%r(^/(.*)$)
# should be /^\/(.*)$/

# good
%r(^/blog/2011/(.*)$)
  • 不要使用%q, %Q, %x, %s, and %W. 使用%构造符时,首选()作为分割符

# bad
name = %q(I'm luoxin)
# should be "I'm luoxin" or 'I\'m luoxin'

# bad
div = %Q(<div id="#@div_id" name="#@div_name"></div>)
# should be %(<div id="#@div_id" name="#@div_name"></div>)

# bad
div = %Q[div id="#@div_id" name="#@div_name"></div>]
# should be %(<div id="#@div_id" name="#@div_name"></div>)

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


其他

  • 代码中嵌入SQL语句时,SQL关键字大写,其他字符小写

# 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语句过长,使用换行来划分逻辑


# 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
  • 不要出现N+1查询

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

  • 方法参数的个数尽量不超过两个,最多不超过三个(*args除外)。如果有多选项,一般通过最末的哈希参数来传递

# 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中使用新的哈希语法

# 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语法

# 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)
  • 在不期望修改的对象上调用freeze方法

# 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




以上是关于ruby代码规范的主要内容,如果未能解决你的问题,请参考以下文章

php Yoast SEO规范输出的代码片段

php Yoast SEO规范输出的代码片段

java 代码片段

在 ruby​​ DSL 实现中捕获规范文件名?

ruby Ruby片段

递进迭代敏捷