- Notifications
You must be signed in to change notification settings - Fork760
airbnb/ruby
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
This is Airbnb's Ruby Style Guide.
It was inspired byGitHub's guide andRuboCop's guide.
Airbnb also maintains aJavaScript Style Guide.
- Whitespace
- Line Length
- Commenting
- Methods
- Conditional Expressions
- Syntax
- Naming
- Classes
- Exceptions
- Collections
- Strings
- Regular Expressions
- Percent Literals
- Rails
- Be Consistent
- Translation
Use soft-tabs with atwo-space indent.[link]
Indent
whenas deep ascase.[link]casewhensong.name =='Misty'puts'Not again!'whensong.duration >120puts'Too long!'whenTime.now.hour >21puts"It's too late"elsesong.playendkind=caseyearwhen1850..1889then'Blues'when1890..1909then'Ragtime'when1910..1929then'New Orleans Jazz'when1930..1939then'Swing'when1940..1950then'Bebop'else'Jazz'end
Align function parameters either all onthe same line or one per line.[link]
# baddefself.create_translation(phrase_id,phrase_key,target_locale,value,user_id,do_xss_check,allow_verification) ...end# gooddefself.create_translation(phrase_id,phrase_key,target_locale,value,user_id,do_xss_check,allow_verification) ...end# gooddefself.create_translation(phrase_id,phrase_key,target_locale,value,user_id,do_xss_check,allow_verification) ...end
Indent succeeding lines in multi-lineboolean expressions.[link]
# baddefis_eligible?(user)Trebuchet.current.launch?(ProgramEligibilityHelper::PROGRAM_TREBUCHET_FLAG) &&is_in_program?(user) &&program_not_expiredend# gooddefis_eligible?(user)Trebuchet.current.launch?(ProgramEligibilityHelper::PROGRAM_TREBUCHET_FLAG) &&is_in_program?(user) &&program_not_expiredend
Never leave trailing whitespace.[link]
When making inline comments, include aspace between the end of the code and the start of your comment.[link]
# badresult=func(a,b)# we might want to change b to c# goodresult=func(a,b)# we might want to change b to c
Use spaces around operators; after commas,colons, and semicolons; and around
{and before}.[link]sum=1 +2a,b=1,21 >2 ?true :false;puts'Hi'[1,2,3].each{ |e|putse}
Never include a space before a comma.[link]
result=func(a,b)
Do not include space inside blockparameter pipes. Include one space between parameters in a block.Include one space outside block parameter pipes.[link]
# bad{}.each{ |x,y |putsx}# good{}.each{ |x,y|putsx}
Do not leave space between
!and itsargument.[link]!somethingNo spaces after
(,[or before],).[link]some(arg).other[1,2,3].length
Omit whitespace when doingstring interpolation.[link]
# badvar="This#{foobar} is interpolated."# goodvar="This#{foobar} is interpolated."
Don't use extra whitespace in rangeliterals.[link]
# bad(0 ...coll).eachdo |item|# good(0...coll).eachdo |item|
Add a new line after
ifconditions spanningmultiple lines to help differentiate between the conditions and the body.[link]if@reservation_alteration.checkin ==@reservation.start_date &&@reservation_alteration.checkout ==(@reservation.start_date +@reservation.nights)redirect_to_alteration@reservation_alterationend
Add a new line after conditionals,blocks, case statements, etc.[link]
ifrobot.is_awesome?send_robot_presentendrobot.add_trait(:human_like_intelligence)
Don’t include newlines between areasof different indentation (such as around class or module bodies).[link]
# badclassFoodefbar# body omittedendend# goodclassFoodefbar# body omittedendend
Include one, but no more than one, newline between methods.[link]
defaenddefbend
Use a single empty line to break betweenstatements to break up methods into logical paragraphs internally.[link]
deftransformorize_carcar=manufacture(options)t=transformer(robot,disguise)car.after_market_mod!t.transform(car)car.assign_cool_name!fleet.add(car)carend
End each file with a newline. Don't includemultiple newlines at the end of a file.[link]
- Keep each line of code to a readable length. Unlessyou have a reason not to, keep lines to fewer than 100 characters.(rationale)[link]
Though a pain to write, comments are absolutely vital to keeping our codereadable. The following rules describe what you should comment and where. Butremember: while comments are very important, the best code isself-documenting. Giving sensible names to types and variables is much betterthan using obscure names that you must then explain through comments.
When writing your comments, write for your audience: the next contributor whowill need to understand your code. Be generous — the next one may be you!
Portions of this section borrow heavily from the GoogleC++ andPython style guides.
Every class definition should have an accompanying comment that describes whatit is for and how it should be used.
A file that contains zero classes or more than one class should have a commentat the top describing its contents.
# Automatic conversion of one locale to another where it is possible, like# American to British English.moduleTranslation# Class for converting between text between similar locales.# Right now only conversion between American English -> British, Canadian,# Australian, New Zealand variations is provided.classPrimAndProperdefinitialize@converters={:en=>{:"en-AU"=>AmericanToAustralian.new,:"en-CA"=>AmericanToCanadian.new,:"en-GB"=>AmericanToBritish.new,:"en-NZ"=>AmericanToKiwi.new,}}end ...# Applies transforms to American English that are common to# variants of all other English colonies.classAmericanToColonial ...end# Converts American to British English.# In addition to general Colonial English variations, changes "apartment"# to "flat".classAmericanToBritish <AmericanToColonial ...end
All files, including data and config files, should have file-level comments.
# List of American-to-British spelling variants.## This list is made with# lib/tasks/list_american_to_british_spelling_variants.rake.## It contains words with general spelling variation patterns:# [trave]led/lled, [real]ize/ise, [flav]or/our, [cent]er/re, plus# and these extras:# learned/learnt, practices/practises, airplane/aeroplane, ...sectarianizes:sectarianisesneutralization:neutralisation...
Every function declaration should have comments immediately preceding it thatdescribe what the function does and how to use it. These comments should bedescriptive ("Opens the file") rather than imperative ("Open the file"); thecomment describes the function, it does not tell the function what to do. Ingeneral, these comments do not describe how the function performs its task.Instead, that should be left to comments interspersed in the function's code.
Every function should mention what the inputs and outputs are, unless it meetsall of the following criteria:
- not externally visible
- very short
- obvious
You may use whatever format you wish. In Ruby, two popular functiondocumentation schemes areTomDoc andYARD. You can alsojust write things out concisely:
# Returns the fallback locales for the_locale.# If opts[:exclude_default] is set, the default locale, which is otherwise# always the last one in the returned list, will be excluded.## For example:# fallbacks_for(:"pt-BR")# => [:"pt-BR", :pt, :en]# fallbacks_for(:"pt-BR", :exclude_default => true)# => [:"pt-BR", :pt]deffallbacks_for(the_locale,opts={}) ...end
The final place to have comments is in tricky parts of the code. If you'regoing to have to explain it at the next code review, you should comment it now.Complicated operations get a few lines of comments before the operationscommence. Non-obvious ones get comments at the end of the line.
deffallbacks_for(the_locale,opts={})# dup() to produce an array that we can mutate.ret=@fallbacks[the_locale].dup# We make two assumptions here:# 1) There is only one default locale (that is, it has no less-specific# children).# 2) The default locale is just a language. (Like :en, and not :"en-US".)ifopts[:exclude_default] &&ret.last ==default_locale &&ret.last !=language_from_locale(the_locale)ret.popendretend
On the other hand, never describe the code. Assume the person reading the codeknows the language (though not what you're trying to do) better than you do.
Related: do not use block comments. They cannotbe preceded by whitespace and are not as easy to spot as regular comments.[link]
# bad=begincomment lineanother comment line=end# good# comment line# another comment line
Pay attention to punctuation, spelling, and grammar; it is easier to readwell-written comments than badly written ones.
Comments should be as readable as narrative text, with proper capitalizationand punctuation. In many cases, complete sentences are more readable thansentence fragments. Shorter comments, such as comments at the end of a line ofcode, can sometimes be less formal, but you should be consistent with yourstyle.
Although it can be frustrating to have a code reviewer point out that you areusing a comma when you should be using a semicolon, it is very important thatsource code maintain a high level of clarity and readability. Properpunctuation, spelling, and grammar help with that goal.
Use TODO comments for code that is temporary, a short-term solution, orgood-enough but not perfect.
TODOs should include the string TODO in all caps, followed by the full nameof the person who can best provide context about the problem referenced by theTODO, in parentheses. A colon is optional. A comment explaining what there isto do is required. The main purpose is to have a consistent TODO format thatcan be searched to find the person who can provide more details upon request.A TODO is not a commitment that the person referenced will fix the problem.Thus when you create a TODO, it is almost always your name that is given.
# bad# TODO(RS): Use proper namespacing for this constant.# bad# TODO(drumm3rz4lyfe): Use proper namespacing for this constant.# good# TODO(Ringo Starr): Use proper namespacing for this constant.
- Never leave commented-out code in our codebase.[link]
Use
defwith parentheses when there areparameters. Omit the parentheses when the method doesn't accept anyparameters.[link]defsome_method# body omittedenddefsome_method_with_parameters(arg1,arg2)# body omittedend
Do not use default positional arguments.Use keyword arguments (if available - in Ruby 2.0 or later) or an optionshash instead.[link]
# baddefobliterate(things,gently=true,except=[],at=Time.now) ...end# gooddefobliterate(things,gently:true,except:[],at:Time.now) ...end# gooddefobliterate(things,options={})options={:gently=>true,# obliterate with soft-delete:except=>[],# skip obliterating these things:at=>Time.now,# don't obliterate them until later}.merge(options) ...end
Avoid single-line methods. Althoughthey are somewhat popular in the wild, there are a few peculiarities abouttheir definition syntax that make their use undesirable.[link]
# baddeftoo_much;something;something_else;end# gooddefsome_method# bodyend
Use parentheses for a method call:
If the method returns a value.[link]
# bad@current_user=User.find_by_id1964192# good@current_user=User.find_by_id(1964192)
If the first argument to the method usesparentheses.[link]
# badput!(x +y) %len,value# goodput!((x +y) %len,value)
Never put a space between a method name andthe opening parenthesis.[link]
# badf(3 +2) +1# goodf(3 +2) +1
Omit parentheses for a method call if themethod accepts no arguments.[link]
# badnil?()# goodnil?
If the method doesn't return a value (or wedon't care about the return), parentheses are optional. (Especially if thearguments overflow to multiple lines, parentheses may add readability.)[link]
# okayrender(:partial=>'foo')# okayrender:partial=>'foo'
In either case:
If a method accepts an options hash as thelast argument, do not use
{}during invocation.[link]# badget'/v1/reservations',{:id=>54875}# goodget'/v1/reservations',:id=>54875
Never use
thenfor multi-lineif/unless.[link]# badifsome_conditionthen ...end# goodifsome_condition ...end
Never use
dofor multi-linewhileoruntil.[link]# badwhilex >5do ...enduntilx >5do ...end# goodwhilex >5 ...enduntilx >5 ...end
The
and,or, andnotkeywords are banned. It'sjust not worth it. Always use&&,||, and!instead.[link]Modifier
if/unlessusage is okay whenthe body is simple, the condition is simple, and the whole thing fits onone line. Otherwise, avoid modifierif/unless.[link]# bad - this doesn't fit on one lineadd_trebuchet_experiments_on_page(request_opts[:trebuchet_experiments_on_page])ifrequest_opts[:trebuchet_experiments_on_page] && !request_opts[:trebuchet_experiments_on_page].empty?# okayifrequest_opts[:trebuchet_experiments_on_page] && !request_opts[:trebuchet_experiments_on_page].empty?add_trebuchet_experiments_on_page(request_opts[:trebuchet_experiments_on_page])end# bad - this is complex and deserves multiple lines and a commentparts[i]=part.to_i(INTEGER_BASE)if !part.nil? &&[0,2,3].include?(i)# okayreturnifreconciled?
Never use
unlesswithelse. Rewritethese with the positive case first.[link]# badunlesssuccess?puts'failure'elseputs'success'end# goodifsuccess?puts'success'elseputs'failure'end
Avoid
unlesswith multipleconditions.[link]# badunlessfoo? &&bar? ...end# okayif !(foo? &&bar?) ...end
Avoid
unlesswith comparison operators if you can useifwith an opposing comparison operator.[link]# badunlessx ==10 ...end# goodifx !=10 ...end# badunlessx <10 ...end# goodifx >=10 ...end# okunlessx ===10 ...end
Don't use parentheses around thecondition of an
if/unless/while.[link]# badif(x >10) ...end# goodifx >10 ...end
Avoid the ternary operator (
?:) exceptin cases where all expressions are extremely trivial. However, do use theternary operator(?:) overif/then/else/endconstructs for single lineconditionals.[link]# badresult=ifsome_conditionthensomethingelsesomething_elseend# goodresult=some_condition ?something :something_else
Use one expression per branch in a ternaryoperator. This also means that ternary operators must not be nested. Prefer
if/elseconstructs in these cases.[link]# badsome_condition ?(nested_condition ?nested_something :nested_something_else) :something_else# goodifsome_conditionnested_condition ?nested_something :nested_something_elseelsesomething_elseend
Avoid multiple conditions in ternaries.Ternaries are best used with single conditions.[link]
Avoid multi-line
?:(the ternaryoperator), useif/then/else/endinstead.[link]# badsome_really_long_condition_that_might_make_you_want_to_split_lines ?something :something_else# goodifsome_really_long_condition_that_might_make_you_want_to_split_linessomethingelsesomething_elseend
Avoid the use of nested conditionals for flow of control.(More on this.)[link]
Prefer a guard clause when you can assert invalid data. A guard clauseis a conditional statement at the top of a function that returns as soonas it can.
The general principles boil down to:
- Return immediately once you know your function cannot do anything more.
- Reduce nesting and indentation in the code by returning early. This makesthe code easier to read and requires less mental bookkeeping on the partof the reader to keep track of
elsebranches. - The core or most important flows should be the least indented.
# baddefcomputeserver=find_serverifserverclient=server.clientifclientrequest=client.make_requestifrequestprocess_request(request)endendendend# gooddefcomputeserver=find_serverreturnunlessserverclient=server.clientreturnunlessclientrequest=client.make_requestreturnunlessrequestprocess_request(request)end
Prefer
nextin loops instead of conditional blocks.# bad[0,1,2,3].eachdo |item|ifitem >1putsitemendend# good[0,1,2,3].eachdo |item|nextunlessitem >1putsitemend
See also the section "Guard Clause", p68-70 in Beck, Kent.Implementation Patterns. Upper Saddle River: Addison-Wesley, 2008, whichhas inspired some of the content above.
Never use
for, unless you know exactly why. Most of thetime iterators should be used instead.foris implemented in terms ofeach(so you're adding a level of indirection), but with a twist -fordoesn't introduce a new scope (unlikeeach) and variables defined in itsblock will be visible outside it.[link]arr=[1,2,3]# badforeleminarrdoputselemend# goodarr.each{ |elem|putselem}
Prefer
{...}overdo...endforsingle-line blocks. Avoid using{...}for multi-line blocks (multilinechaining is always ugly). Always usedo...endfor "control flow" and"method definitions" (e.g. in Rakefiles and certain DSLs). Avoiddo...endwhen chaining.[link]names=["Bozhidar","Steve","Sarah"]# goodnames.each{ |name|putsname}# badnames.eachdo |name|putsnameend# goodnames.eachdo |name|putsnameputs'yay!'end# badnames.each{ |name|putsnameputs'yay!'}# goodnames.select{ |name|name.start_with?("S")}.map{ |name|name.upcase}# badnames.selectdo |name|name.start_with?("S")end.map{ |name|name.upcase}
Some will argue that multiline chaining would look okay with the use of
{...}, but they should ask themselves if this code is really readable andwhether the block's content can be extracted into nifty methods.Use shorthand self assignment operatorswhenever applicable.[link]
# badx=x +yx=x *yx=x**yx=x /yx=x ||yx=x &&y# goodx +=yx *=yx **=yx /=yx ||=yx &&=y
Avoid semicolons except for in single line classdefinitions. When it is appropriate to use a semicolon, it should bedirectly adjacent to the statement it terminates: there should be nospace before the semicolon.[link]
# badputs'foobar';# superfluous semicolonputs'foo';puts'bar'# two expressions on the same line# goodputs'foobar'puts'foo'puts'bar'puts'foo','bar'# this applies to puts in particular
Use :: only to reference constants(this includesclasses and modules) and constructors (like Array() or Nokogiri::HTML()).Do not use :: for regular method invocation.[link]
# badSomeClass::some_methodsome_object::some_method# goodSomeClass.some_methodsome_object.some_methodSomeModule::SomeClass::SOME_CONSTSomeModule::SomeClass()
Avoid
returnwhere not required.[link]# baddefsome_method(some_arr)returnsome_arr.sizeend# gooddefsome_method(some_arr)some_arr.sizeend
Don't use the return value of
=inconditionals[link]# bad - shows intended use of assignmentif(v=array.grep(/foo/)) ...end# badifv=array.grep(/foo/) ...end# goodv=array.grep(/foo/)ifv ...end
Use
||=freely to initialize variables.[link]# set name to Bozhidar, only if it's nil or falsename ||='Bozhidar'
Don't use
||=to initialize booleanvariables. (Consider what would happen if the current value happened to befalse.)[link]# bad - would set enabled to true even if it was falseenabled ||=true# goodenabled=trueifenabled.nil?
Use
.callexplicitly when calling lambdas.[link]# badlambda.(x,y)# goodlambda.call(x,y)
Avoid using Perl-style special variables (like
$0-9,$, etc. ). They are quite cryptic and their use in anything butone-liner scripts is discouraged. Prefer long form versions such as$PROGRAM_NAME.[link]When a method block takes only oneargument, and the body consists solely of reading an attribute or callingone method with no arguments, use the
&:shorthand.[link]# badbluths.map{ |bluth|bluth.occupation}bluths.select{ |bluth|bluth.blue_self?}# goodbluths.map(&:occupation)bluths.select(&:blue_self?)
Prefer
some_methodoverself.some_methodwhencalling a method on the current instance.[link]# baddefend_dateself.start_date +self.nightsend# gooddefend_datestart_date +nightsend
In the following three common cases,
self.is required by the languageand is good to use:- When defining a class method:
def self.some_method. - Theleft hand side when calling an assignment method, including assigningan attribute when
selfis an ActiveRecord model:self.guest = user. - Referencing the current instance's class:
self.class.
- When defining a class method:
When defining an object of any mutabletype meant to be a constant, make sure to call
freezeon it. Commonexamples are strings, arrays, and hashes.(More on this.)[link]The reason is that Ruby constants are actually mutable. Calling
freezeensures they are not mutated and are therefore truly constant andattempting to modify them will raise an exception. For strings, this allowsolder versions of Ruby below 2.2 to intern them.# badclassColorRED='red'BLUE='blue'GREEN='green'ALL_COLORS=[RED,BLUE,GREEN,]COLOR_TO_RGB={RED=>0xFF0000,BLUE=>0x0000FF,GREEN=>0x00FF00,}end# goodclassColorRED='red'.freezeBLUE='blue'.freezeGREEN='green'.freezeALL_COLORS=[RED,BLUE,GREEN,].freezeCOLOR_TO_RGB={RED=>0xFF0000,BLUE=>0x0000FF,GREEN=>0x00FF00,}.freezeend
Use
snake_casefor methods and variables.[link]Use
CamelCasefor classes and modules. (Keepacronyms like HTTP, RFC, XML uppercase.)[link]Use
SCREAMING_SNAKE_CASEfor otherconstants.[link]The names of predicate methods (methodsthat return a boolean value) should end in a question mark.(i.e.
Array#empty?).[link]The names of potentially "dangerous" methods(i.e. methods that modify
selfor the arguments,exit!, etc.) shouldend with an exclamation mark. Bang methods should only exist if a non-bangmethod exists. (More on this.)[link]Name throwaway variables
_.[link]version='3.2.1'major_version,minor_version,_=version.split('.')
Avoid the usage of class (
@@) variablesdue to their "nasty" behavior in inheritance.[link]classParent@@class_var='parent'defself.print_class_varputs@@class_varendendclassChild <Parent@@class_var='child'endParent.print_class_var# => will print "child"
As you can see all the classes in a class hierarchy actually share oneclass variable. Class instance variables should usually be preferredover class variables.
Use
def self.methodto define singletonmethods. This makes the methods more resistant to refactoring changes.[link]classTestClass# baddefTestClass.some_method ...end# gooddefself.some_other_method ...end
Avoid
class << selfexcept when necessary,e.g. single accessors and aliased attributes.[link]classTestClass# badclass <<selfdeffirst_method ...enddefsecond_method_etc ...endend# goodclass <<selfattr_accessor:per_pagealias_method:nwo,:find_by_name_with_ownerenddefself.first_method ...enddefself.second_method_etc ...endend
Indent the
public,protected, andprivatemethods as much the method definitions they apply to. Leave oneblank line above and below them.[link]classSomeClassdefpublic_method# ...endprivatedefprivate_method# ...endend
Don't use exceptions for flow of control.[link]
# badbeginn /drescueZeroDivisionErrorputs"Cannot divide by 0!"end# goodifd.zero?puts"Cannot divide by 0!"elsen /dend
Avoid rescuing the
Exceptionclass.[link]# badbegin# an exception occurs hererescueException# exception handlingend# goodbegin# an exception occurs hererescueStandardError# exception handlingend# acceptablebegin# an exception occurs hererescue# exception handlingend
Don't specify
RuntimeErrorexplicitly inthe two argument version of raise. Prefer error sub-classes for clarity andexplicit error creation.[link]# badraiseRuntimeError,'message'# better - RuntimeError is implicit hereraise'message'# bestclassMyExplicitError <RuntimeError;endraiseMyExplicitError
Prefer supplying an exception class and a message as two separate argumentsto
raise, instead of an exception instance.[link]# badraiseSomeException.new('message')# Note that there is no way to do `raise SomeException.new('message'), backtrace`.# goodraiseSomeException,'message'# Consistent with `raise SomeException, 'message', backtrace`.
Avoid using rescue in its modifier form.[link]
# badread_filerescuehandle_error($!)# goodbeginread_filerescueErrno:ENOENT=>exhandle_error(ex)end
Prefer
mapovercollect.[link]Prefer
detectoverfind. The use offindis ambiguous with regard to ActiveRecord'sfindmethod -detectmakesclear that you're working with a Ruby collection, not an AR object.[link]Prefer
reduceoverinject.[link]Prefer
sizeover eitherlengthorcountfor performance reasons.[link]Prefer literal array and hash creationnotation unless you need to pass parameters to their constructors.[link]
# badarr=Array.newhash=Hash.new# goodarr=[]hash={}# good because constructor requires parametersx=Hash.new{ |h,k|h[k]={}}
Favor
Array#joinoverArray#*for clarity.[link]# bad%w(onetwothree) *', '# => 'one, two, three'# good%w(onetwothree).join(', ')# => 'one, two, three'
Use symbols instead of strings as hash keys.[link]
# badhash={'one'=>1,'two'=>2,'three'=>3}# goodhash={:one=>1,:two=>2,:three=>3}
Relatedly, use plain symbols instead of stringsymbols when possible.[link]
# bad:"symbol"# good:symbol
Use
Hash#key?instead ofHash#has_key?andHash#value?instead ofHash#has_value?. Accordingto Matz, the longer forms are considered deprecated.[link]# badhash.has_key?(:test)hash.has_value?(value)# goodhash.key?(:test)hash.value?(value)
Use multi-line hashes when it makes the codemore readable, and use trailing commas to ensure that parameter changesdon't cause extraneous diff lines when the logic has not otherwise changed.[link]
hash={:protocol=>'https',:only_path=>false,:controller=>:users,:action=>:set_password,:redirect=>@redirect_url,:secret=>@secret,}
Use a trailing comma in an
Arraythatspans more than 1 line[link]# goodarray=[1,2,3]# goodarray=["car","bear","plane","zoo",]
Prefer string interpolation instead ofstring concatenation:[link]
# bademail_with_name=user.name +' <' +user.email +'>'# goodemail_with_name="#{user.name} <#{user.email}>"
Furthermore, keep in mind Ruby 1.9-style interpolation. Let's say you arecomposing cache keys like this:
CACHE_KEY='_store'cache.write(@user.id +CACHE_KEY)
Prefer string interpolation instead of string concatenation:
CACHE_KEY='%d_store'cache.write(CACHE_KEY %@user.id)
Avoid using
String#+when you need toconstruct large data chunks. Instead, useString#<<. Concatenation mutatesthe string instance in-place and is always faster thanString#+, whichcreates a bunch of new string objects.[link]# good and also faststory=''story <<'The Ugly Duckling'paragraphs.eachdo |paragraph|story <<paragraphend
Use
\at the end of the line instead of+or<<to concatenate multi-line strings.[link]# bad"Some string is really long and " +"spans multiple lines.""Some string is really long and " <<"spans multiple lines."# good"Some string is really long and " \"spans multiple lines."
Avoid using
$1-9as it can be hard to trackwhat they contain. Named groups can be used instead.[link]# bad/(regexp)/ =~string...process $1# good/(?<meaningful_var>regexp)/ =~string...processmeaningful_var
Be careful with
^and$as theymatch start/end of line, not string endings. If you want to match the wholestring use:\Aand\z.[link]string="some injection\nusername"string[/^username$/]# matchesstring[/\Ausername\z/]# don't match
Use
xmodifier for complex regexps. This makesthem more readable and you can add some useful comments. Just be careful asspaces are ignored.[link]regexp=%r{ start # some text\s # white space char (group) # first group (?:alt1|alt2) # some alternation end}x
Prefer parentheses over curlybraces, brackets, or pipes when using
%-literal delimiters forconsistency, and because the behavior of%-literals is closer to methodcalls than the alternatives.[link]# bad%w[datelocale]%w{datelocale}%w|datelocale|# good%w(datelocale)
Use
%wfreely.[link]STATES=%w(draftopenclosed)
Use
%()for single-line strings which requireboth interpolation and embedded double-quotes. For multi-line strings,prefer heredocs.[link]# bad - no interpolation needed%(Welcome, Jane!)# should be 'Welcome, Jane!'# bad - no double-quotes%(This is#{quality} style)# should be "This is #{quality} style"# bad - multiple lines%(Welcome, Jane!\nPlease enjoy your stay at#{location}\nCheers!)# should be a heredoc.# good - requires interpolation, has quotes, single line%(Welcome,#{name}!)
Use
%ronly for regular expressions matchingmorethan one '/' character.[link]# bad%r(\s+)# still bad%r(^/(.*)$)# should be /^\/(.*)$/# good%r(^/blog/2011/(.*)$)
Avoid the use of %x unless you're going to invoke acommand with backquotes in it (which is rather unlikely).[link]
# baddate=%x(date)# gooddate=`date`echo=%x(echo `date`)
When immediately returning after calling
renderorredirect_to, putreturnon the next line, not the same line.[link]# badrender:text=>'Howdy'andreturn# goodrender:text=>'Howdy'return# still badrender:text=>'Howdy'andreturniffoo.present?# goodiffoo.present?render:text=>'Howdy'returnend
When defining ActiveRecord model scopes, wrap therelation in a
lambda. A naked relation forces a database connection to beestablished at class load time (instance startup).[link]# badscope:foo,where(:bar=>1)# goodscope:foo,->{where(:bar=>1)}
If you're editing code, take a few minutes to look at the code around you anddetermine its style. If they use spaces around all their arithmeticoperators, you should too. If their comments have little boxes of hash marksaround them, make your comments have little boxes of hash marks around themtoo.
The point of having style guidelines is to have a common vocabulary of codingso people can concentrate on what you're saying rather than on how you'resaying it. We present global style rules here so people know the vocabulary,but local style is also important. If code you add to a file looksdrastically different from the existing code around it, it throws readers outof their rhythm when they go to read it. Avoid this.
This style guide is also available in other languages:
Chinese (Simplified):1c7/ruby-airbnb
About
Ruby Style Guide
Resources
License
Code of conduct
Contributing
Security policy
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.