Ruby编程中的语法使用风格推荐
使用::引用常量(包括类和模块)和构造器(比如Array()或者Nokogiri::HTML())。
永远不要使用::来调用方法。
#bad SomeClass::some_method some_object::some_method #good SomeClass.some_method some_object.some_method SomeModule::SomeClass::SOME_CONST SomeModule::SomeClass()
使用括号将def的参数括起来。当方法不接收任何参数的时候忽略括号。
#bad defsome_method() #bodyomitted end #good defsome_method #bodyomitted end #bad defsome_method_with_argumentsarg1,arg2 #bodyomitted end #good defsome_method_with_arguments(arg1,arg2) #bodyomitted end
从来不要使用for,除非你知道使用它的准确原因。大多数时候迭代器都可以用来替for。for是由一组each实现的(因此你正间接添加了一级),但是有一个小道道-for并不包含一个新的scope(不像each)并且在它的块中定义的变量在外面也是可以访问的。
arr=[1,2,3] #bad foreleminarrdo putselem end #notethatelemisaccessibleoutsideoftheforloop elem#=>3 #good arr.each{|elem|putselem} #elemisnotaccessibleoutsideeach'sblock elem#=>NameError:undefinedlocalvariableormethod`elem'
在多行的if/unless中坚决不要使用then。
#bad ifsome_conditionthen #bodyomitted end #good ifsome_condition #bodyomitted end
在多行的if/unless总是把条件放在与if/unless的同一行。
#bad if some_condition do_something do_something_else end #good ifsome_condition do_something do_something_else end
喜欢三元操作运算(?:)超过if/then/else/end结构。
它更加普遍而且明显的更加简洁。
#bad result=ifsome_conditionthensomethingelsesomething_elseend #good result=some_condition?something:something_else
使用一个表达式在三元操作运算的每一个分支下面只使用一个表达式。也就是说三元操作符不要被嵌套。在这样的情形中宁可使用if/else。
#bad some_condition?(nested_condition?nested_something:nested_something_else):something_else #good ifsome_condition nested_condition?nested_something:nested_something_else else something_else end
不要使用ifx:...-它在Ruby1.9中已经移除。使用三元操作运算代替。
#bad result=ifsome_conditionthensomethingelsesomething_elseend #good result=some_condition?something:something_else
不要使用ifx;...。使用三元操作运算代替。
利用ifandcase是表达式这样的事实它们返回一个结果。
#bad ifcondition result=x else result=y end #good result= ifcondition x else y end
在one-linecases的时候使用whenxthen...。替代的语法whenx:xxx已经在Ruby1.9中移除。
不要使用whenx;...。查看上面的规则。
使用!替代not.
#差-因为操作符有优先级,需要用括号。 x=(notsomething) #good x=!something 避免使用!!. #bad x='test' #obscurenilcheck if!!x #bodyomitted end x=false #doublenegationisuselessonbooleans !!x#=>false #good x='test' unlessx.nil? #bodyomitted end
Theandandorkeywordsarebanned.It'sjustnotworth
it.Alwaysuse&&and||instead.
and和or这两个关键字被禁止使用了。它名不符实。总是使用&&和||来取代。
#bad #booleanexpression ifsome_conditionandsome_other_condition do_something end #controlflow document.saved?ordocument.save! #good #booleanexpression ifsome_condition&&some_other_condition do_something end #controlflow
document.saved?||document.save!
避免多行的?:(三元操作符);使用if/unless来取代。
单行主体喜欢使用if/unless修饰符。另一个好方法是使用&&/||控制流程。
#bad ifsome_condition do_something end #good do_somethingifsome_condition #anothergoodoption some_condition&&do_something
布尔表达式使用&&/||,and/or用于控制流程。(经验Rule:如果你必须使用额外的括号(表达逻辑),那么你正在使用错误的的操作符。)
#booleanexpression ifsome_condition&&some_other_condition do_something end #controlflow document.save?ordocument.save!
避免多行?:(三元操作运算),使用if/unless替代。
在单行语句的时候喜爱使用if/unless修饰符。另一个好的选择就是使and/or来做流程控制。
#bad ifsome_condition do_something end #good do_somethingifsome_condition #anothergoodoption some_conditionanddo_something
永远不要使用unless和else组合。将它们改写成肯定条件。
#bad unlesssuccess? puts'failure' else puts'success' end #good ifsuccess? puts'success' else puts'failure' end
不用使用括号包含if/unless/while的条件。
#bad if(x>10) #bodyomitted end #good ifx>10 #bodyomitted end
在多行while/until中不要使用while/untilconditiondo。
#bad whilex>5do #bodyomitted end untilx>5do #bodyomitted end #good whilex>5 #bodyomitted end untilx>5 #bodyomitted end
当你有单行主体时,尽量使用while/until修饰符。
#bad whilesome_condition do_something end #good do_somethingwhilesome_condition
否定条件判断尽量使用until而不是while。
#bad do_somethingwhile!some_condition #good do_somethinguntilsome_condition
循环后条件判断使用Kernel#loop和break,而不是begin/end/until或者begin/end/while。
#bad begin putsval val+=1 endwhileval<0 #good loopdo putsval val+=1 breakunlessval<0 end
忽略围绕内部DSL方法参数的括号(如:Rake,Rails,RSpec),Ruby中带有"关键字"状态的方法(如:attr_reader,puts)以及属性存取方法。所有其他的方法调用使用括号围绕参数。
classPerson attr_reader:name,:age #omitted end temperance=Person.new('Temperance',30) temperance.name putstemperance.age x=Math.sin(y) array.delete(e) bowling.score.should==0
忽略隐式选项hash外部的花括号。
#bad user.set({name:'John',age:45,permissions:{read:true}}) #good user.set(name:'John',age:45,permissions:{read:true})
内部DSL方法的外部括号和大括号。
classPerson<ActiveRecord::Base #bad validates(:name,{presence:true,length:{within:1..10}}) #good validates:name,presence:true,length:{within:1..10} end
方法调用不需要参数,那么忽略圆括号。
#bad Kernel.exit!() 2.even?() fork() 'test'.upcase() #good Kernel.exit! 2.even? fork 'test'.upcase
在单行代码块的时候宁愿使用{...}而不是do...end。避免在多行代码块使用{...}(多行链式通常变得非常丑陋)。通常使用do...end来做流程控制和方法定义(例如在Rakefiles和某些DSLs中)。避免在链式调用中使用do...end。
names=['Bozhidar','Steve','Sarah'] #bad names.eachdo|name| putsname end #good names.each{|name|putsname} #bad names.selectdo|name| name.start_with?('S') end.map{|name|name.upcase} #good names.select{|name|name.start_with?('S')}.map{|name|name.upcase}
有人会争论多行链式看起来和使用{...}一样工作,但是他们问问自己-这样的代码真的有可读性码并且为什么代码块中的内容不能被提取到美丽的方法中。
Considerusingexplicitblockargumenttoavoidwritingblock
literalthatjustpassesitsargumentstoanotherblock.Bewareof
theperformanceimpact,though,astheblockgetsconvertedtoa
Proc.
考虑使用明确的块参数来避免写入的块字面量仅仅传递参数的给另一个块。小心性能的影响,即使,
块被转换成了Proc。
require'tempfile' #bad defwith_tmp_dir Dir.mktmpdirdo|tmp_dir| Dir.chdir(tmp_dir){|dir|yielddir}#blockjustpassesarguments end end #good defwith_tmp_dir(&block) Dir.mktmpdirdo|tmp_dir| Dir.chdir(tmp_dir,&block) end end with_tmp_dirdo|dir| puts"dirisaccessibleasparameterandpwdisset:#{dir}" end
避免在不需要流的控制使用return。
#bad defsome_method(some_arr) returnsome_arr.size end #good defsome_method(some_arr) some_arr.size end
避免在不需要的地方使用self(它仅仅在调用一些self做写访问的时候需要)(Itisonlyrequiredwhencallingaselfwriteaccessor.)
#bad defready? ifself.last_reviewed_at>self.last_updated_at self.worker.update(self.content,self.options) self.status=:in_progress end self.status==:verified end #good defready? iflast_reviewed_at>last_updated_at worker.update(content,options) self.status=:in_progress end status==:verified end
作为一个必然的结果,避免将方法(参数)放于局部变量阴影之下除非它们是相等的。
classFoo attr_accessor:options #ok definitialize(options) self.options=options #bothoptionsandself.optionsareequivalenthere end #bad defdo_something(options={}) unlessoptions[:when]==:later output(self.options[:message]) end end #good defdo_something(params={}) unlessparams[:when]==:later output(options[:message]) end end end
不要在条件表达式里使用=(赋值)的返回值,除非条件表达式在圆括号内被赋值。
这是一个相当流行的ruby方言,有时被称为safeassignmentincondition。
#bad(+awarning) ifv=array.grep(/foo/) do_something(v) ... end #good(MRIwouldstillcomplain,butRuboCopwon't) if(v=array.grep(/foo/)) do_something(v) ... end #good v=array.grep(/foo/) ifv do_something(v) ... end
在任何可以的地方使用快捷的selfassignment操作符。
#bad x=x+y x=x*y x=x**y x=x/y x=x||y x=x&&y #good x+=y x*=y x**=y x/=y x||=y x&&=y
只有在变量没有被初始化的时候使用||=来初始化变量。
#setnametoVozhidar,onlyifit'snilorfalse name||='Bozhidar'
不要使用||=来初始化布尔变量。(想想如果当前值为false的时候会发生什么。)
#bad-wouldsetenabledtotrueevenifitwasfalse enable||=true #good enabled=trueifenabled.nil?
使用&&=来预处理变量不确定是否存在的变量。使用&&=仅仅在(变量)存在的时候
才会改变值,除去了使用if来检查它的存在性。
#bad ifsomething something=something.downcase end #bad something=something?nil:something.downcase #ok something=something.downcaseifsomething #good something=something&&something.downcase #better something&&=something.downcase
避免全等(caseequality)===操作符的使用。从名称可知,这是case表达式的隐式使用并且在case语句外的场合使用会产生难以理解的代码。
#bad Array===something (1..100)===7 /something/===some_string #good something.is_a?(Array) (1..100).include?(7) some_string=~/something/
避免使用Perl的指定变量风格(比如,$:,$;等等。)。它们相当神秘,不鼓励在单行代码之外使用它们。
使用English库提供的友好别名。
#bad $:.unshiftFile.dirname(__FILE__) #good require'English' $LOAD_PATH.unshiftFile.dirname(__FILE__) 从来不要在方法名和(参数)开括号之间使用空格。 #bad f(3+2)+1 #good f(3+2)+1
如果方法的第一个参数以开括号开始,通常使用括号把它们全部括起来。例如f((3+2)+1)。
通常使用-w选项运行Ruby解释器,在你忘记上面所诉规则,ruby将会提示你。
定义单行块使用新的lambda语法。定义多行块中使用lambda方法。
#bad l=lambda{|a,b|a+b} l.call(1,2) #correct,butlooksextremelyawkward l=->(a,b)do tmp=a*7 tmp*b/50 end #good l=->(a,b){a+b} l.call(1,2) l=lambdado|a,b| tmp=a*7 tmp*b/50 end
用proc而不是Proc.new。
#bad p=Proc.new{|n|putsn} #good p=proc{|n|putsn} 匿名方法和块用proc.call()而不是proc[]或proc.()。 #bad-lookssimilartoEnumerationaccess l=->(v){putsv} l[1] #alsobad-uncommonsyntax l=->(v){putsv} l.(1) #good l=->(v){putsv} l.call(1)
未使用的块参数和局部变量使用_。它也可以接受通过_来使用(即使它有少了些描述性)。
这个惯例由Ruby解释器以及RuboCop这样的工具组织其将会抑制它们的未使用参数警告。
#bad result=hash.map{|k,v|v+1} defsomething(x) unused_var,used_var=something_else(x) #... end #good result=hash.map{|_k,v|v+1} defsomething(x) _unused_var,used_var=something_else(x) #... end #good result=hash.map{|_,v|v+1} defsomething(x) _,used_var=something_else(x) #... end
使用$stdout/$stderr/$stdin而不是STDOUT/STDERR/STDIN。STDOUT/STDERR/STDIN是常量,虽然在Ruby中是可以给常量重新赋值的(可能是重定向到某个流),但解释器会警告如果你执意这样。
使用warn而不是$stderr.puts。除了更加清晰简洁,如果你需要的话,
warn还允许你抑制(suppress)警告(通过-W0将警告级别设为0)。
倾向使用sprintf和它的别名format而不是相当隐晦的String#%方法.
#bad '%d%d'%[20,10] #=>'2010' #good sprintf('%d%d',20,10) #=>'2010' #good sprintf('%{first}%{second}',first:20,second:10) #=>'2010' format('%d%d',20,10) #=>'2010' #good format('%{first}%{second}',first:20,second:10) #=>'2010'
倾向使用Array#join而不是相当隐晦的使用字符串作参数的Array#*。
#bad %w(onetwothree)*',' #=>'one,two,three' #good %w(onetwothree).join(',') #=>'one,two,three'
当处理你希望像Array那样对待的变量,但是你不确定它是一个数组时,
使用[*var]orArray()而不是显式的Array检查。
#bad paths=[paths]unlesspaths.is_a?Array paths.each{|path|do_something(path)} #good [*paths].each{|path|do_something(path)} #good(andabitmorereadable) Array(paths).each{|path|do_something(path)}
尽量使用范围或Comparable#between?来替换复杂的逻辑比较。
#bad do_somethingifx>=1000&&x<=2000 #good do_somethingif(1000..2000).include?(x) #good do_somethingifx.between?(1000,2000)
尽量用谓词方法而不是使用==。比较数字除外。
#bad ifx%2==0 end ifx%2==1 end ifx==nil end #good ifx.even? end ifx.odd? end ifx.nil? end ifx.zero? end ifx==0 end
避免使用BEGIN区块。
使用Kernel#at_exit。永远不要用END区块。
#bad END{puts'Goodbye!'} #good at_exit{puts'Goodbye!'}
避免使用flip-flops。
避免使用嵌套的条件来控制流程。
当你可能断言不合法的数据,使用一个防御语句。一个防御语句是一个在函数顶部的条件声明,这样如果数据不合法就能尽快的跳出函数。
#bad defcompute_thing(thing) ifthing[:foo] update_with_bar(thing) ifthing[:foo][:bar] partial_compute(thing) else re_compute(thing) end end end #good defcompute_thing(thing) returnunlessthing[:foo] update_with_bar(thing[:foo]) returnre_compute(thing)unlessthing[:foo][:bar] partial_compute(thing) end