公司推荐https://github.com/bbatsov/ruby-style-guide这个Ruby风格向导不错。我这边学习一下与大家共勉之。
蓝色字部分是我的个人理解。有不对之处,还请大家指出。
请记住下面的名言。
风格可以使一个好代码转变为伟大的代码。
-- Bozhidar Batsov
这个Ruby风格指南是推荐编写最佳的ruby代码风格。这样的话每个程序员写得代码都可以也其他程序员维护。
代码风格可以帮助世界上的人理解你的代码。但是不管什么样的代码风格都会被一些人认为有风险而拒绝使用。
该指南分为几个部分的相关规则。我尝试过添加规则背后的原因(如果它被省略,我假设是很明显的的)。
我没有把所有的代码风格都列出来。这些都是我作为一个软件工程师,在工作中从ruby交流社区得到的建议,反馈。
以及一些备受推荐的ruby资源。就像"Programming Ruby 1.9"和"The Ruby Programming Language".
这些规则有些正在写,有些缺少些例子。有些显而易见的就没有添加例子。在适当的时候,这些问题将得到解决 -
他们现在只需记住。
========源代码布局================
注:每个人都相信别人的代码风格是正确的,而自己是丑陋的,不可读。但是有时候他们也有可能是对的。--Jerry Coffin
这句话的意思大家不能过分相信代码风格向导,有时候还是需要一些自信,你的代码风格也有可能是正确的。
1. 使用UTF-8作为代码编码
一般ruby程序是在头上设定"# encoding: UTF-8"的方式来指定编码。
个人理解在rails里面就是在数据库设定里面指定编码为UTF-8就可以了。
2.使用2个空白作为代码缩进
# 好的缩进 2个空白
def some_method
do_something
end
# 不好的缩进 - 4个空白
def some_method
do_something
end
这个不用我解释了。比较简单。
3.结束符使用unix风格的 (*BSD/Solaris/Linux/OSX 用户不要担心,因为系统是默认的,Windows用户需要格外小心.)
你如果使用git管理代码的话,可以加入下面的设置保证你代码中不会有windows换行符侵入。
$ git config --global core.autocrlf true
个人认为是ruby是以LF认为换行。所以在代码中的换行要注意。
windows下以CR/LF表换行,而linux的换行符LF
4.在操作符左右,逗号,冒号,分号后面以及"{"前后,"}"前面使用空白.
空白可能和ruby解释器无关,但它可以使代码读起来更加轻松。
sum = 1 + 2
a, b = 1, 2
1 > 2 ? true : false; puts 'Hi'
[1, 2, 3].each { |e| puts e }
唯一的例外是你如果使用指数运算符时,不需要空白。
# 不好的
e = M * c ** 2
# 好的
e = M * c**2
5.不要在“(”,“ [“之后或者”]",")"之前使用空白
some(arg).other
[1, 2, 3].length
6.case 和when的缩进一样
可能很多人不同意这样的观点,但是"The RubyProgramming Language" 和 "Programming 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
7。使用空行在def之间和代码逻辑块之间
def some_method
data = initialize(options)
data.manipulate!
data.result
end
def some_method
result
end
8.使用RDoc并且他能转化为API文档。
不要在注释块和def之间使用空行。
9.保持每行代码不超过80字节长度
10.避免尾随空白
就是代码结束的地方不要加空白。
========语法================
1. def函数有参数的时候请用括号。没有参数的时候可以省略括号。
def some_method
# body omitted
end
def some_method_with_arguments(arg1, arg2)
# body omitted
end
2. 请不要使用for.除非你有充分的理由。一般情况下是迭代器可以代替for的。
For是each的一种实现。所以你使用for只是间接调用each。但是for没有block块。这点和each不一样。并且for循环里面定义的变量对于外部是可以访问的。
arr = [1, 2, 3]
# bad
for elem in arr do
puts elem
end
# good
arr.each { |elem| puts elem }
3. 多行使用if/unless的时候请不要使用then
# bad
if some_condition then
# body omitted
end
# good
if some_condition
# body omitted
end
4. 使用
if/then/else/end结构的地方请尽量用
三元操作符(
?:
)代替。
因为它能使你的代码更加简洁
# bad
result = if some_condition then something else something_else end
# good
result = some_condition ? something : something_else
5. 三元操作符每一个分支只有一个表达式。也就是说三元操作符的分支表达式里面不应该有嵌套。在这种情况下,请尽量使用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
6.
请不要使用ifx: ...
表达式。因为在ruby 1.9里面已经去除了。请用三元操作符代替。同样 if x;…也一样不推荐使用。
# bad
result = if some_condition: something else something_else end
# good
result = some_condition ? something : something_else
7.
一行的case语句请用when x then ...语句。请不要使用when x: ...语句。因为ruby 1.9里面已经废弃掉了。同样的when x;…也不要使用。
8.
布尔变量判断请用”&&/||”。控制流程判断请用“and/or”
# boolean expression
if some_condition && some_other_condition
do_something
end
# control flow
document.saved? or document.save!
9.
多行代码的情况下请尽量避免使用三元操作符。而是使用if/unless代替。
10.
如果你的if/unless后的操作代码只有一行。请不要写多行,一行就可以了。另外一种好的写法是用and 代替。
# bad
if some_condition
do_something
end
# good
do_something if some_condition
# another good option
some_condition and do_something
11. 相反条件的判断不要使用if 而是使用unless代替。或者用or也可以代替。
# bad
do_something if !some_condition
# good
do_something unless some_condition
# another good option
some_condition or do_something
12.
不要把unless和else在一起使用。可以用正常逻辑重写代码。
# bad
unless success?
puts 'failure'
else
puts 'success'
end
# good
if success?
puts 'success'
else
puts 'failure'
end
13. 请不要在if/unless/while的条件外面加上括号。
# bad
if (x > 10)
# body omitted
end
# good
if x > 10
# body omitted
end
14. 调用内部DSL(例如Rake, Rails, RSpec),还有一些关键字的方法是可以省略括号的。其他方法调用的时候都不能省略括号。
class Person
attr_reader name, age
# omitted
end
temperance = Person.new('Temperance', 30)
temperance.name
puts temperance.age
x = Math.sin(y)
array.delete(e)
15. 单行的block使用“{}”。多行的时候避免使用”{}”。一般是用do…end作为控制流程和一些方法(Rakefiles and certain DSLs)定义。避免使用 do…end 有链接方法的时候。
names = ["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 }
16. 尽量避免使用return。
# bad
def some_method(some_arr)
return some_arr.size
end
# good
def some_method(some_arr)
some_arr.size
end
17. 在方法的参数设置默认值的=操作符两边请加入空白
# bad
def some_method(arg1=:default, arg2=nil, arg3=[])
# do something...
end
# good
def some_method(arg1 = :default, arg2 = nil, arg3 = [])
# do something...
end
18.尽量避免使用换行符(\)。
# bad
result = 1 - \
2
# good (but still ugly as hell)
result = 1 \
- 2
19.使用=取得返回值是可以的。
if v = array.grep(/foo/) ...
20. 可以自由使用
||=来初始化变量。
# set name to Bozhidar, only if it's nil or false
name ||= 'Bozhidar'
21.
不要使用||=来初始化布尔型变量。
# bad - would set enabled to true even if it was false
enabled ||= true
# good
enabled = true if enabled.nil?
22.
避免使用Perl-style特殊的变量。例如($0-9
,
$
等
)
23.
在调用方法的时候,请不要在方法名和括号之间加空格
# bad
f (3 + 2) + 1
# good
f(3 + 2) + 1
24.
如果方法的第一个参数有括号,请使用括号在方法调用的时候。例如
f((3 + 2) + 1)
25.
在ruby代码运行时候,请加上–w 运行选项。他可以提醒你,如果你忘记上面的规则的话。
==============命名==================
1. 方法名和变量命名使用snake case。
snake case = 单词之间用下划线连接。单词要么全部大写,要么全部小写。
2.
定义类和模块名字使用CamelCase
。
CamelCase =
每个单词的首字母为大写,空格和标点符号删除。
3.
常量命名使用SCREAMING_SNAKE_CASE
SCREAMING_SNAKE_CASE=
全部大写的snakecase
4.
一个返回布尔型的方法命名的结尾请加上问号?.
5.
有潜在危险的方法的命名请在结尾加上感叹号!
有潜在危险的方法 = 改变自己或者参数的方法
6. 使用inject方法时,命名参数为|a,e|
(accumulator, element).
7. 如果定义二元运算符方法时,参数命名为other
def +(other)
# body omitted
end
8. 使用map优于collect,find优于detect,select优于find_all,size优于length.
这不是硬性规定。如果使用别名能增强可读性,那就可以使用。
===========注释==================
良好的代码就是最好的注释。当你想要增加注释的时候,问问你自己,怎么样提高代码的质量那样就没有必要写注释呢?
1.如果可以写出高质量的代码,你可以忽略这节的其他部分。
2.注释比较长的话,请注意首字母大写,或者加标点符号。单词之间有空格。
3.避免冗余的注释
# bad
counter += 1 # increments counter by one
4. 保持注释是最新的。没有注释都比过期的注释好。
5. 避免写注释来解释烂代码。应该重构代码让他能够更加易读。
==========注解===================
1. 注解一般写在相关代码的上面。并且紧挨在一起。
2. 注解一般写在#和一个空白之后,然后描述问题
3. 如果注解要写多行,第二行必须在#之后缩进2个空白
def bar
# FIXME: This has crashed occasionally since v3.2.1. It may
# be related to the BarBazUtil upgrade.
baz(:quux)
end
4. 如果问题很明显,写任何的注解有点冗余。这时候可以写在代码后面,不需要写任何注解。这个是特殊情况不是规则。
def bar
sleep 100 # OPTIMIZE
end
5. TODO 一般是表示缺少某些功能,以后将会完成。
6. FIXME 一般表示代码这块会引起中断,需要被解决。
7.
OPTIMIZE
一般表示这段代码效率有问题,有性能问题。
8. HACK 表示感觉这段代码很可疑有问题,需要重构。
9.
REVIEW
表示这段代码需要被别人确认是否达到预期。
10. 如果合适的话,也可以用其他关键字表示注解。但是你一定要确保他们在你的readme文件或者其他类似文件里面进行详细说明。
==================类========================
1. 始终提供一个适当的方法to_s.
class Person
attr_reader :first_name, :last_name
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
def to_s
"#@first_name #@last_name"
end
end
2. 利用attr的设定来定义琐碎的访问器或者存取器
3. 考虑增加工厂方法提供额外的方法来创建一个特定的类的实例。
4. 尽量使用鸭子类型而不是继承。(多态)
5. 避免使用@@类变量因为他们在继承里面会引起Bug。
6. 定义方法的时候请根据各个方法的使用范围确定他们的scope。到底是public,private,protect的请注明。而不是全部是public。
7. public,private,protect缩进是和方法缩进一样的。在上面要空一行。
8. 请使用self定义类方法。这样的话这些方法更有抵抗性。
class TestClass
# bad
def TestClass.some_method
# body omitted
end
# good
def self.some_other_method
# body omitted
end
# Also possible and convenient when you
# have to define many singleton methods.
class << self
def first_method
# body omitted
end
def second_method_etc
# body omitted
end
end
end
=============================异常===============================
1. 不要压制异常
2. 流程控制里面不要使用异常
3. 避免使用rescue Exception 类。
============================= 集合==============================1. 少的元素的集合可以使用数组Array
2. 请用%w定义一个字符串数组
%w(foo bar baz) => [“foo”,”bar”,”baz”]
3. 避免创建的数组的元素有巨大差异
4. 如果有很多元素的话请使用Set而不是Array
5. hash的key请用symbols而不是string
6. 避免使用动态的对象作为hash的key
7. 如果你使用ruby 1.9,请使用新的语法定义hash。而不是用旧的方式。 # old way that still works in Ruby 1.9
my_hash = { :a => 'apple', :b => 'banana' }
# new way
my_hash = { a: 'apple', b: 'banana' }
9. 在ruby 1.9里面hash的key不会自动排序了。按照插入的顺序的。
10. 遍历集合的时候,请不要修改他。
==========================字符串=======================================
1. 字符串连接尽量使用嵌入,而不是+号。
# bad
email_with_name = user.name + ' <' + user.email + '>'
# good
email_with_name = "#{user.name} <#{user.email}>"
2. 如果定义一个字符串,这个字符串不需要嵌入或者特殊字符的话,请用单引号字符串
# bad
name = "Bozhidar"
# good
name = 'Bozhidar'
3. 实例变量进行字符串嵌入的时候,{}花括号可以省略。
class Person
attr_reader :first_name, :last_name
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
# bad
def to_s
"#{@first_name} #{@last_name}"
end
# good
def to_s
"#@first_name #@last_name"
end
end
4. 字符串连接请用String#<<
而不是用String#+.连接字符串实例总比+要快。
因为+创建了一堆新字符串。
class Person
attr_reader :first_name, :last_name
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
# bad
def to_s
"#{@first_name} #{@last_name}"
end
# good
def to_s
"#@first_name #@last_name"
end
end
==================关于百分号=========================
1. 请自由使用%w
STATES = %w(draft open closed)
2. 需要字符串替换和双引号嵌入的字符串定义的话,请使用%().多行的字符串定义请使用定界符 <<.
# 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.
print <<EOS
the string
next line
EOS
# good (requires interpolation, has quotes, single line)
%(<tr><td class="name">#{name}</td>)
3. 当你的正则表达式里面含有2个以上的/的字符的时候才使用 %r
# bad
%r(\s+)
# still bad
%r(^/(.*)$)
# should be /^\/(.*)$/
# good
%r(^/blog/2011/(.*)$)
4. 避免使用%q
, %Q
, %x
,%s
和 %W
.
%Q 替代双引号
%q替代单引号
%s 定义symbols
%x 运行系统命令
%W 定义双引号的字符串数组
5. 对于所有的%请使用()作为分割符
==================杂项========================
1. 运行时候用 ruby –w来确保你的代码安全
2. 避免用hash作为方法的可选参数。是不是方法做的事情太多了。
3. 避免方法的长度超过10行。理想情况下,大多数方法是少于5行的。
空行不包含在内。
4. 避免方法的参数太长。比如,3,4个参数。
5. 你如果想增加全局方法到内核里面。请保持他们是private的。
6. 使用实例变量代替全局变量
#bad
$foo_bar = 1
#good
class Foo
class << self
attr_accessor :bar
end
end
Foo.bar = 1
7. 避免使用alias给方法起别名。而是使用alias_method.
8.使用OptionParser来解析复杂的命令行参数。以及使用ruby –s 来进行设定参数。
set the variable $opt to "electric".
% ruby -s prog -opt= electric ./mydata
9. 使用ruby 1.9的时候,请不要使用1.8的一些传统不好的语法
。使用javascript类似的hash语法
。使用新的lamda语法
。inject可以接受方法名作为参数
[1, 2, 3].inject(:+)
10. 避免不必要的metaprogramming
所谓的metaprogramming就是动态新建类的方法,增加类的属性等。
matz = Object.new
def matz.speak
"Place your burden to machine's shoulders"
end
matz.class #=> Object