ruby-style-guide
文件大小: unknow
源码售价: 5 个金币 积分规则     积分充值
资源说明:A community-driven Ruby coding style guide
= The Ruby Style Guide
:idprefix:
:idseparator: -
:sectanchors:
:sectlinks:
:toc: preamble
:toclevels: 1
ifndef::backend-pdf[]
:toc-title: pass:[

Table of Contents

] endif::[] :source-highlighter: rouge == Introduction [quote, Officer Alex J. Murphy / RoboCop] ____ Role models are important. ____ ifdef::env-github[] TIP: You can find a beautiful version of this guide with much improved navigation at https://rubystyle.guide. endif::[] This Ruby style guide recommends best practices so that real-world Ruby programmers can write code that can be maintained by other real-world Ruby programmers. A style guide that reflects real-world usage gets used, while a style guide that holds to an ideal that has been rejected by the people it is supposed to help risks not getting used at all - no matter how good it is. The guide is separated into several sections of related guidelines. We've tried to add the rationale behind the guidelines (if it's omitted we've assumed it's pretty obvious). We didn't come up with all the guidelines out of nowhere - they are mostly based on the professional experience of the editors, feedback and suggestions from members of the Ruby community and various highly regarded Ruby programming resources, such as https://pragprog.com/book/ruby4/programming-ruby-1-9-2-0["Programming Ruby"] and https://www.amazon.com/Ruby-Programming-Language-David-Flanagan/dp/0596516177["The Ruby Programming Language"]. This style guide evolves over time as additional conventions are identified and past conventions are rendered obsolete by changes in Ruby itself. ifdef::env-github[] You can generate a PDF copy of this guide using https://asciidoctor.org/docs/asciidoctor-pdf/[AsciiDoctor PDF], and an HTML copy https://asciidoctor.org/docs/convert-documents/#converting-a-document-to-html[with] https://asciidoctor.org/#installation[AsciiDoctor] using the following commands: [source,shell] ---- # Generates README.pdf asciidoctor-pdf -a allow-uri-read README.adoc # Generates README.html asciidoctor README.adoc ---- [TIP] ==== Install the `rouge` gem to get nice syntax highlighting in the generated document. [source,shell] ---- gem install rouge ---- ==== endif::[] [TIP] ==== If you're into Rails or RSpec you might want to check out the complementary https://github.com/rubocop/rails-style-guide[Ruby on Rails Style Guide] and https://github.com/rubocop/rspec-style-guide[RSpec Style Guide]. ==== TIP: https://github.com/rubocop/rubocop[RuboCop] is a static code analyzer (linter) and formatter, based on this style guide. === Guiding Principles [quote, Harold Abelson, Structure and Interpretation of Computer Programs] ____ Programs must be written for people to read, and only incidentally for machines to execute. ____ It's common knowledge that code is read much more often than it is written. The guidelines provided here are intended to improve the readability of code and make it consistent across the wide spectrum of Ruby code. They are also meant to reflect real-world usage of Ruby instead of a random ideal. When we had to choose between a very established practice and a subjectively better alternative we've opted to recommend the established practice.footnote:[Occasionally we might suggest to the reader to consider some alternatives, though.] There are some areas in which there is no clear consensus in the Ruby community regarding a particular style (like string literal quoting, spacing inside hash literals, dot position in multi-line method chaining, etc.). In such scenarios all popular styles are acknowledged and it's up to you to pick one and apply it consistently. Ruby had existed for over 15 years by the time the guide was created, and the language's flexibility and lack of common standards have contributed to the creation of numerous styles for just about everything. Rallying people around the cause of community standards took a lot of time and energy, and we still have a lot of ground to cover. Ruby is famously optimized for programmer happiness. We'd like to believe that this guide is going to help you optimize for maximum programmer happiness. === A Note about Consistency [quote, Ralph Waldo Emerson] ____ A foolish consistency is the hobgoblin of little minds, adored by little statesmen and philosophers and divines. ____ A style guide is about consistency. Consistency with this style guide is important. Consistency within a project is more important. Consistency within one class or method is the most important. However, know when to be inconsistent -- sometimes style guide recommendations just aren't applicable. When in doubt, use your best judgment. Look at other examples and decide what looks best. And don't hesitate to ask! In particular: do not break backwards compatibility just to comply with this guide! Some other good reasons to ignore a particular guideline: * When applying the guideline would make the code less readable, even for someone who is used to reading code that follows this style guide. * To be consistent with surrounding code that also breaks it (maybe for historic reasons) -- although this is also an opportunity to clean up someone else's mess (in true XP style). * Because the code in question predates the introduction of the guideline and there is no other reason to be modifying that code. * When the code needs to remain compatible with older versions of Ruby that don't support the feature recommended by the style guide. === Translations Translations of the guide are available in the following languages: * https://github.com/JuanitoFatas/ruby-style-guide/blob/master/README-zhCN.md[Chinese Simplified] * https://github.com/JuanitoFatas/ruby-style-guide/blob/master/README-zhTW.md[Chinese Traditional] * https://github.com/HassanTC/ruby-style-guide/blob/master/README-EgAr.md[Egyptian Arabic] * https://github.com/gauthier-delacroix/ruby-style-guide/blob/master/README-frFR.md[French] * https://github.com/fortissimo1997/ruby-style-guide/blob/japanese/README.ja.md[Japanese] * https://github.com/dalzony/ruby-style-guide/blob/master/README-koKR.md[Korean] * https://github.com/rubensmabueno/ruby-style-guide/blob/master/README-PT-BR.md[Portuguese (pt-BR)] * https://github.com/arbox/ruby-style-guide/blob/master/README-ruRU.md[Russian] * https://github.com/alemohamad/ruby-style-guide/blob/master/README-esLA.md[Spanish] * https://github.com/CQBinh/ruby-style-guide/blob/master/README-viVN.md[Vietnamese] NOTE: These translations are not maintained by our editor team, so their quality and level of completeness may vary. The translated versions of the guide often lag behind the upstream English version. == Source Code Layout [quote, Jerry Coffin (on indentation)] ____ Nearly everybody is convinced that every style but their own is ugly and unreadable. Leave out the "but their own" and they're probably right... ____ === Source Encoding [[utf-8]] Use `UTF-8` as the source file encoding. TIP: UTF-8 has been the default source file encoding since Ruby 2.0. === Tabs or Spaces? [[tabs-or-spaces]] Use only spaces for indentation. No hard tabs. === Indentation [[spaces-indentation]] Use two *spaces* per indentation level (aka soft tabs). [source,ruby] ---- # bad - four spaces def some_method do_something end # good def some_method do_something end ---- === Maximum Line Length [[max-line-length]] Limit lines to 80 characters. TIP: Most editors and IDEs have configuration options to help you with that. They would typically highlight lines that exceed the length limit. .Why Bother with 80 characters in a World of Modern Widescreen Displays? **** A lot of people these days feel that a maximum line length of 80 characters is just a remnant of the past and makes little sense today. After all - modern displays can easily fit 200+ characters on a single line. Still, there are some important benefits to be gained from sticking to shorter lines of code. First, and foremost - numerous studies have shown that humans read much faster vertically and very long lines of text impede the reading process. As noted earlier, one of the guiding principles of this style guide is to optimize the code we write for human consumption. Additionally, limiting the required editor window width makes it possible to have several files open side-by-side, and works well when using code review tools that present the two versions in adjacent columns. The default wrapping in most tools disrupts the visual structure of the code, making it more difficult to understand. The limits are chosen to avoid wrapping in editors with the window width set to 80, even if the tool places a marker glyph in the final column when wrapping lines. Some web based tools may not offer dynamic line wrapping at all. Some teams strongly prefer a longer line length. For code maintained exclusively or primarily by a team that can reach agreement on this issue, it is okay to increase the line length limit up to 100 characters, or all the way up to 120 characters. Please, restrain the urge to go beyond 120 characters. **** === No Trailing Whitespace [[no-trailing-whitespace]] Avoid trailing whitespace. TIP: Most editors and IDEs have configuration options to visualize trailing whitespace and to remove it automatically on save. === Line Endings [[crlf]] Use Unix-style line endings.footnote:[*BSD/Solaris/Linux/macOS users are covered by default, Windows users have to be extra careful.] [TIP] ==== If you're using Git you might want to add the following configuration setting to protect your project from Windows line endings creeping in: [source,bash] ---- $ git config --global core.autocrlf true ---- ==== === Should I Terminate Files with a Newline? [[newline-eof]] End each file with a newline. TIP: This should be done via editor configuration, not manually. === Should I Terminate Expressions with `;`? [[no-semicolon]] Don't use `;` to terminate statements and expressions. [source,ruby] ---- # bad puts 'foobar'; # superfluous semicolon # good puts 'foobar' ---- === One Expression Per Line [[one-expression-per-line]] Use one expression per line. [source,ruby] ---- # bad puts 'foo'; puts 'bar' # two expressions on the same line # good puts 'foo' puts 'bar' puts 'foo', 'bar' # this applies to puts in particular ---- === Spaces and Operators [[spaces-operators]] Use spaces around operators, after commas, colons and semicolons. Whitespace might be (mostly) irrelevant to the Ruby interpreter, but its proper use is the key to writing easily readable code. [source,ruby] ---- # bad sum=1+2 a,b=1,2 class FooError 120 puts 'Too long!' when Time.now.hour > 21 puts "It's too late" else song.play end # good 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 ---- .A Bit of History **** This is the style established in both "The Ruby Programming Language" and "Programming Ruby". Historically it is derived from the fact that `case` and `switch` statements are not blocks, hence should not be indented, and the `when` and `else` keywords are labels (compiled in the C language, they are literally labels for `JMP` calls). **** === Indent Conditional Assignment [[indent-conditional-assignment]] When assigning the result of a conditional expression to a variable, preserve the usual alignment of its branches. [source,ruby] ---- # bad - pretty convoluted 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 result = if some_cond calc_something else calc_something_else end # good - it's apparent what's going on 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 result = if some_cond calc_something else calc_something_else end # good (and a bit more width efficient) 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 result = if some_cond calc_something else calc_something_else end ---- === Empty Lines between Methods [[empty-lines-between-methods]] Use empty lines between method definitions and also to break up methods into logical paragraphs internally. [source,ruby] ---- # bad def some_method data = initialize(options) data.manipulate! data.result end def some_other_method result end # good def some_method data = initialize(options) data.manipulate! data.result end def some_other_method result end ---- === Two or More Empty Lines [[two-or-more-empty-lines]] Don't use several empty lines in a row. [source,ruby] ---- # bad - It has two empty lines. some_method some_method # good some_method some_method ---- === Empty Lines around Attribute Accessor [[empty-lines-around-attribute-accessor]] Use empty lines around attribute accessor. [source,ruby] ---- # bad class Foo attr_reader :foo def foo # do something... end end # good class Foo attr_reader :foo def foo # do something... end end ---- === Empty Lines around Access Modifier [[empty-lines-around-access-modifier]] Use empty lines around access modifier. [source,ruby] ---- # bad class Foo def bar; end private def baz; end end # good class Foo def bar; end private def baz; end end ---- === Empty Lines around Bodies [[empty-lines-around-bodies]] Don't use empty lines around method, class, module, block bodies. [source,ruby] ---- # bad class Foo def foo begin do_something do something end rescue something end true end end # good class Foo def foo begin do_something do something end rescue something end end end ---- === Trailing Comma in Method Arguments [[no-trailing-params-comma]] Avoid comma after the last parameter in a method call, especially when the parameters are not on separate lines. [source,ruby] ---- # bad - easier to move/add/remove parameters, but still not preferred some_method( size, count, color, ) # bad some_method(size, count, color, ) # good some_method(size, count, color) ---- === Spaces around Equals [[spaces-around-equals]] Use spaces around the `=` operator when assigning default values to method parameters: [source,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 ---- While several Ruby books suggest the first style, the second is much more prominent in practice (and arguably a bit more readable). === Line Continuation in Expressions [[no-trailing-backslash]] Avoid line continuation with `\` where not required. In practice, avoid using line continuations for anything but string concatenation. [source,ruby] ---- # bad (\ is not needed here) result = 1 - \ 2 # bad (\ is required, but still ugly as hell) result = 1 \ - 2 # good result = 1 - 2 long_string = 'First part of the long string' \ ' and second part of the long string' ---- === Multi-line Method Chains [[consistent-multi-line-chains]] Adopt a consistent multi-line method chaining style. There are two popular styles in the Ruby community, both of which are considered good - leading `.` and trailing `.`. ==== Leading `.` [[leading-dot-in-multi-line-chains]] When continuing a chained method call on another line, keep the `.` on the second line. [source,ruby] ---- # bad - need to consult first line to understand second line one.two.three. four # good - it's immediately clear what's going on the second line one.two.three .four ---- ==== Trailing `.` [[trailing-dot-in-multi-line-chains]] When continuing a chained method call on another line, include the `.` on the first line to indicate that the expression continues. [source,ruby] ---- # bad - need to read ahead to the second line to know that the chain continues one.two.three .four # good - it's immediately clear that the expression continues beyond the first line one.two.three. four ---- A discussion on the merits of both alternative styles can be found https://github.com/rubocop/ruby-style-guide/pull/176[here]. === Method Arguments Alignment [[no-double-indent]] Align the arguments of a method call if they span more than one line. When aligning arguments is not appropriate due to line-length constraints, single indent for the lines after the first is also acceptable. [source,ruby] ---- # starting point (line is too long) def send_mail(source) Mailer.deliver(to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: source.text) end # bad (double indent) def send_mail(source) Mailer.deliver( to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: source.text) end # good def send_mail(source) Mailer.deliver(to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: source.text) end # good (normal indent) def send_mail(source) Mailer.deliver( to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: source.text ) end ---- === Implicit Options Hash [[no-braces-opts-hash]] IMPORTANT: As of Ruby 2.7 braces around an options hash are no longer optional. Omit the outer braces around an implicit options hash. [source,ruby] ---- # bad user.set({ name: 'John', age: 45, permissions: { read: true } }) # good user.set(name: 'John', age: 45, permissions: { read: true }) ---- === DSL Method Calls [[no-dsl-decorating]] Omit both the outer braces and parentheses for methods that are part of an internal DSL. [source,ruby] ---- class Person < ActiveRecord::Base # bad validates(:name, { presence: true, length: { within: 1..10 } }) # good validates :name, presence: true, length: { within: 1..10 } end ---- === Space in Method Calls [[parens-no-spaces]] Do not put a space between a method name and the opening parenthesis. [source,ruby] ---- # bad puts (x + y) # good puts(x + y) ---- === Space in Brackets Access Do not put a space between a receiver name and the opening brackets. [source,ruby] ---- # bad collection [index_or_key] # good collection[index_or_key] ---- === Multi-line Arrays Alignment [[align-multiline-arrays]] Align the elements of array literals spanning multiple lines. [source,ruby] ---- # bad - single indent menu_item = %w[Spam Spam Spam Spam Spam Spam Spam Spam Baked beans Spam Spam Spam Spam Spam] # good menu_item = %w[ Spam Spam Spam Spam Spam Spam Spam Spam Baked beans Spam Spam Spam Spam Spam ] # good menu_item = %w[Spam Spam Spam Spam Spam Spam Spam Spam Baked beans Spam Spam Spam Spam Spam] ---- == Naming Conventions [quote, Phil Karlton] ____ The only real difficulties in programming are cache invalidation and naming things. ____ === English for Identifiers [[english-identifiers]] Name identifiers in English. [source,ruby] ---- # bad - identifier is a Bulgarian word, using non-ascii (Cyrillic) characters заплата = 1_000 # bad - identifier is a Bulgarian word, written with Latin letters (instead of Cyrillic) zaplata = 1_000 # good salary = 1_000 ---- === Snake Case for Symbols, Methods and Variables [[snake-case-symbols-methods-vars]] Use `snake_case` for symbols, methods and variables. [source,ruby] ---- # bad :'some symbol' :SomeSymbol :someSymbol someVar = 5 def someMethod # some code end def SomeMethod # some code end # good :some_symbol some_var = 5 def some_method # some code end ---- === Identifiers with a Numeric Suffix [[snake-case-symbols-methods-vars-with-numbers]] Do not separate numbers from letters on symbols, methods and variables. [source,ruby] ---- # bad :some_sym_1 some_var_1 = 1 var_10 = 10 def some_method_1 # some code end # good :some_sym1 some_var1 = 1 var10 = 10 def some_method1 # some code end ---- === CapitalCase for Classes and Modules [[camelcase-classes]] NOTE: `CapitalCase` is also known as `UpperCamelCase, `CapitalWords` and `PascalCase`. Use `CapitalCase` for classes and modules. (Keep acronyms like HTTP, RFC, XML uppercase). [source,ruby] ---- # bad class Someclass # some code end class Some_Class # some code end class SomeXml # some code end class XmlSomething # some code end # good class SomeClass # some code end class SomeXML # some code end class XMLSomething # some code end ---- === Snake Case for Files [[snake-case-files]] Use `snake_case` for naming files, e.g. `hello_world.rb`. === Snake Case for Directories [[snake-case-dirs]] Use `snake_case` for naming directories, e.g. `lib/hello_world/hello_world.rb`. === One Class per File [[one-class-per-file]] Aim to have just a single class/module per source file. Name the file name as the class/module, but replacing `CapitalCase` with `snake_case`. === Screaming Snake Case for Constants [[screaming-snake-case]] Use `SCREAMING_SNAKE_CASE` for other constants (those that don't refer to classes and modules). [source,ruby] ---- # bad SomeConst = 5 # good SOME_CONST = 5 ---- === Predicate Methods Suffix [[bool-methods-qmark]] The names of predicate methods (methods that return a boolean value) should end in a question mark (i.e. `Array#empty?`). Methods that don't return a boolean, shouldn't end in a question mark. [source,ruby] ---- # bad def even(value) end # good def even?(value) end ---- === Predicate Methods Prefix [[bool-methods-prefix]] Avoid prefixing predicate methods with the auxiliary verbs such as `is`, `does`, or `can`. These words are redundant and inconsistent with the style of boolean methods in the Ruby core library, such as `empty?` and `include?`. [source,ruby] ---- # bad class Person def is_tall? true end def can_play_basketball? false end def does_like_candy? true end end # good class Person def tall? true end def basketball_player? false end def likes_candy? true end end ---- === Dangerous Method Suffix [[dangerous-method-bang]] The names of potentially _dangerous_ methods (i.e. methods that modify `self` or the arguments, `exit!` (doesn't run the finalizers like `exit` does), etc) should end with an exclamation mark if there exists a safe version of that _dangerous_ method. [source,ruby] ---- # bad - there is no matching 'safe' method class Person def update! end end # good class Person def update end end # good class Person def update! end def update end end ---- === Relationship between Safe and Dangerous Methods [[safe-because-unsafe]] Define the non-bang (safe) method in terms of the bang (dangerous) one if possible. [source,ruby] ---- class Array def flatten_once! res = [] each do |e| [*e].each { |f| res << f } end replace(res) end def flatten_once dup.flatten_once! end end ---- === Unused Variables Prefix [[underscore-unused-vars]] Prefix with `+_+` unused block parameters and local variables. It's also acceptable to use just `+_+` (although it's a bit less descriptive). This convention is recognized by the Ruby interpreter and tools like RuboCop will suppress their unused variable warnings. [source,ruby] ---- # bad result = hash.map { |k, v| v + 1 } def something(x) unused_var, used_var = something_else(x) # some code end # good result = hash.map { |_k, v| v + 1 } def something(x) _unused_var, used_var = something_else(x) # some code end # good result = hash.map { |_, v| v + 1 } def something(x) _, used_var = something_else(x) # some code end ---- === `other` Parameter [[other-arg]] When defining binary operators and operator-alike methods, name the parameter `other` for operators with "symmetrical" semantics of operands. Symmetrical semantics means both sides of the operator are typically of the same or coercible types. Operators and operator-alike methods with symmetrical semantics (the parameter should be named `other`): `+`, `-`, `+*+`, `/`, `%`, `**`, `==`, `>`, `<`, `|`, `&`, `^`, `eql?`, `equal?`. Operators with non-symmetrical semantics (the parameter should *not* be named `other`): `<<`, `[]` (collection/item relations between operands), `===` (pattern/matchable relations). Note that the rule should be followed *only* if both sides of the operator have the same semantics. Prominent exception in Ruby core is, for example, `Array#*(int)`. [source,ruby] ---- # good def +(other) # body omitted end # bad def <<(other) @internal << other end # good def <<(item) @internal << item end # bad # Returns some string multiplied `other` times def *(other) # body omitted end # good # Returns some string multiplied `num` times def *(num) # body omitted end ---- == Flow of Control === `for` Loops [[no-for-loops]] Do not use `for`, unless you know exactly why. Most of the time iterators should be used instead. `for` is implemented in terms of `each` (so you're adding a level of indirection), but with a twist - `for` doesn't introduce a new scope (unlike `each`) and variables defined in its block will be visible outside it. [source,ruby] ---- arr = [1, 2, 3] # bad for elem in arr do puts elem end # note that elem is accessible outside of the for loop elem # => 3 # good arr.each { |elem| puts elem } # elem is not accessible outside each block elem # => NameError: undefined local variable or method `elem' ---- === `then` in Multi-line Expression [[no-then]] Do not use `then` for multi-line `if`/`unless`/`when`/`in`. [source,ruby] ---- # bad if some_condition then # body omitted end # bad case foo when bar then # body omitted end # bad case expression in pattern then # body omitted end # good if some_condition # body omitted end # good case foo when bar # body omitted end # good case expression in pattern # body omitted end ---- === Condition Placement [[same-line-condition]] Always put the condition on the same line as the `if`/`unless` in a multi-line conditional. [source,ruby] ---- # bad if some_condition do_something do_something_else end # good if some_condition do_something do_something_else end ---- === Ternary Operator vs `if` [[ternary-operator]] Prefer the ternary operator(`?:`) over `if/then/else/end` constructs. It's more common and obviously more concise. [source,ruby] ---- # bad result = if some_condition then something else something_else end # good result = some_condition ? something : something_else ---- === Nested Ternary Operators [[no-nested-ternary]] Use one expression per branch in a ternary operator. This also means that ternary operators must not be nested. Prefer `if/else` constructs in these cases. [source,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 ---- === Semicolon in `if` [[no-semicolon-ifs]] Do not use `if x; ...`. Use the ternary operator instead. [source,ruby] ---- # bad result = if some_condition; something else something_else end # good result = some_condition ? something : something_else ---- === `case` vs `if-else` [[case-vs-if-else]] Prefer `case` over `if-elsif` when compared value is the same in each clause. [source,ruby] ---- # bad if status == :active perform_action elsif status == :inactive || status == :hibernating check_timeout else final_action end # good case status when :active perform_action when :inactive, :hibernating check_timeout else final_action end ---- === Returning Result from `if`/`case` [[use-if-case-returns]] Leverage the fact that `if` and `case` are expressions which return a result. [source,ruby] ---- # bad if condition result = x else result = y end # good result = if condition x else y end ---- === One-line Cases [[one-line-cases]] Use `when x then ...` for one-line cases. NOTE: The alternative syntax `when x: ...` has been removed as of Ruby 1.9. === Semicolon in `when` [[no-when-semicolons]] Do not use `when x; ...`. See the previous rule. === Semicolon in `in` [[no-in-pattern-semicolons]] Do not use `in pattern; ...`. Use `in pattern then ...` for one-line `in` pattern branches. [source,ruby] ---- # bad case expression in pattern; do_something end # good case expression in pattern then do_something end ---- === `!` vs `not` [[bang-not-not]] Use `!` instead of `not`. [source,ruby] ---- # bad - parentheses are required because of op precedence x = (not something) # good x = !something ---- === Double Negation [[no-bang-bang]] Avoid unnecessary uses of `!!` `!!` converts a value to boolean, but you don't need this explicit conversion in the condition of a control expression; using it only obscures your intention. Consider using it only when there is a valid reason to restrict the result `true` or `false`. Examples include outputting to a particular format or API like JSON, or as the return value of a `predicate?` method. In these cases, also consider doing a nil check instead: `!something.nil?`. [source,ruby] ---- # bad x = 'test' # obscure nil check if !!x # body omitted end # good x = 'test' if x # body omitted end # good def named? !name.nil? end # good def banned? !!banned_until&.future? end ---- === `and`/`or` [[no-and-or-or]] The `and` and `or` keywords are banned. The minimal added readability is just not worth the high probability of introducing subtle bugs. For boolean expressions, always use `&&` and `||` instead. For flow control, use `if` and `unless`; `&&` and `||` are also acceptable but less clear. [source,ruby] ---- # bad # boolean expression ok = got_needed_arguments and arguments_are_valid # control flow document.save or raise("Failed to save document!") # good # boolean expression ok = got_needed_arguments && arguments_are_valid # control flow raise("Failed to save document!") unless document.save # ok # control flow document.save || raise("Failed to save document!") ---- .Why Ban `and` and `or`? **** The main reason is very simple - they add a lot of cognitive overhead, as they don't behave like similarly named operators in other languages. First of all, `and` and `or` operators have lower precedence than the `=` operator, whereas the `&&` and `||` operators have higher precedence than the `=` operator, based on order of operations. [source,ruby] ---- foo = true and false # results in foo being equal to true. Equivalent to ( foo = true ) and false bar = false or true # results in bar being equal to false. Equivalent to ( bar = false ) or true ---- Also `&&` has higher precedence than `||`, where as `and` and `or` have the same one. Funny enough, even though `and` and `or` were inspired by Perl, they don't have different precedence in Perl. [source,ruby] ---- foo = true or true and false # => false (it's effectively (true or true) and false) foz = true || true && false # => true (it's effectively true || (true && false) bar = false or true and false # => false (it's effectively (false or true) and false) baz = false || true && false # => false (it's effectively false || (true && false)) ---- **** === Multi-line Ternary Operator [[no-multiline-ternary]] Avoid multi-line `?:` (the ternary operator); use `if`/`unless` instead. === `if` as a Modifier [[if-as-a-modifier]] Prefer modifier `if`/`unless` usage when you have a single-line body. Another good alternative is the usage of control flow `&&`/`||`. [source,ruby] ---- # bad if some_condition do_something end # good do_something if some_condition # another good option some_condition && do_something ---- === Multi-line `if` Modifiers [[no-multiline-if-modifiers]] Avoid modifier `if`/`unless` usage at the end of a non-trivial multi-line block. [source,ruby] ---- # bad 10.times do # multi-line body omitted end if some_condition # good if some_condition 10.times do # multi-line body omitted end end ---- === Nested Modifiers [[no-nested-modifiers]] Avoid nested modifier `if`/`unless`/`while`/`until` usage. Prefer `&&`/`||` if appropriate. [source,ruby] ---- # bad do_something if other_condition if some_condition # good do_something if some_condition && other_condition ---- === `if` vs `unless` [[unless-for-negatives]] Prefer `unless` over `if` for negative conditions (or control flow `||`). [source,ruby] ---- # bad do_something if !some_condition # bad do_something if not some_condition # good do_something unless some_condition # another good option some_condition || do_something ---- === Using `else` with `unless` [[no-else-with-unless]] Do not use `unless` with `else`. Rewrite these with the positive case first. [source,ruby] ---- # bad unless success? puts 'failure' else puts 'success' end # good if success? puts 'success' else puts 'failure' end ---- === Parentheses around Condition [[no-parens-around-condition]] Don't use parentheses around the condition of a control expression. [source,ruby] ---- # bad if (x > 10) # body omitted end # good if x > 10 # body omitted end ---- NOTE: There is an exception to this rule, namely <>. === Multi-line `while do` [[no-multiline-while-do]] Do not use `while/until condition do` for multi-line `while/until`. [source,ruby] ---- # bad while x > 5 do # body omitted end until x > 5 do # body omitted end # good while x > 5 # body omitted end until x > 5 # body omitted end ---- === `while` as a Modifier [[while-as-a-modifier]] Prefer modifier `while/until` usage when you have a single-line body. [source,ruby] ---- # bad while some_condition do_something end # good do_something while some_condition ---- === `while` vs `until` [[until-for-negatives]] Prefer `until` over `while` for negative conditions. [source,ruby] ---- # bad do_something while !some_condition # good do_something until some_condition ---- === Infinite Loop [[infinite-loop]] Use `Kernel#loop` instead of `while`/`until` when you need an infinite loop. [source,ruby] ---- # bad while true do_something end until false do_something end # good loop do do_something end ---- === `loop` with `break` [[loop-with-break]] Use `Kernel#loop` with `break` rather than `begin/end/until` or `begin/end/while` for post-loop tests. [source,ruby] ---- # bad begin puts val val += 1 end while val < 0 # good loop do puts val val += 1 break unless val < 0 end ---- === Explicit `return` [[no-explicit-return]] Avoid `return` where not required for flow of control. [source,ruby] ---- # bad def some_method(some_arr) return some_arr.size end # good def some_method(some_arr) some_arr.size end ---- === Explicit `self` [[no-self-unless-required]] Avoid `self` where not required. (It is only required when calling a `self` write accessor, methods named after reserved words, or overloadable operators.) [source,ruby] ---- # bad def ready? if self.last_reviewed_at > self.last_updated_at self.worker.update(self.content, self.options) self.status = :in_progress end self.status == :verified end # good def ready? if last_reviewed_at > last_updated_at worker.update(content, options) self.status = :in_progress end status == :verified end ---- === Shadowing Methods [[no-shadowing]] As a corollary, avoid shadowing methods with local variables unless they are both equivalent. [source,ruby] ---- class Foo attr_accessor :options # ok def initialize(options) self.options = options # both options and self.options are equivalent here end # bad def do_something(options = {}) unless options[:when] == :later output(self.options[:message]) end end # good def do_something(params = {}) unless params[:when] == :later output(options[:message]) end end end ---- === Safe Assignment in Condition [[safe-assignment-in-condition]] Don't use the return value of `=` (an assignment) in conditional expressions unless the assignment is wrapped in parentheses. This is a fairly popular idiom among Rubyists that's sometimes referred to as _safe assignment in condition_. [source,ruby] ---- # bad (+ a warning) if v = array.grep(/foo/) do_something(v) # some code end # good (MRI would still complain, but RuboCop won't) if (v = array.grep(/foo/)) do_something(v) # some code end # good v = array.grep(/foo/) if v do_something(v) # some code end ---- === `BEGIN` Blocks [[no-BEGIN-blocks]] Avoid the use of `BEGIN` blocks. === `END` Blocks [[no-END-blocks]] Do not use `END` blocks. Use `Kernel#at_exit` instead. [source,ruby] ---- # bad END { puts 'Goodbye!' } # good at_exit { puts 'Goodbye!' } ---- === Nested Conditionals [[no-nested-conditionals]] Avoid use of nested conditionals for flow of control. Prefer a guard clause when you can assert invalid data. A guard clause is a conditional statement at the top of a function that bails out as soon as it can. [source,ruby] ---- # bad def compute_thing(thing) if thing[:foo] update_with_bar(thing[:foo]) if thing[:foo][:bar] partial_compute(thing) else re_compute(thing) end end end # good def compute_thing(thing) return unless thing[:foo] update_with_bar(thing[:foo]) return re_compute(thing) unless thing[:foo][:bar] partial_compute(thing) end ---- Prefer `next` in loops instead of conditional blocks. [source,ruby] ---- # bad [0, 1, 2, 3].each do |item| if item > 1 puts item end end # good [0, 1, 2, 3].each do |item| next unless item > 1 puts item end ---- == Exceptions === `raise` vs `fail` [[prefer-raise-over-fail]] Prefer `raise` over `fail` for exceptions. [source,ruby] ---- # bad fail SomeException, 'message' # good raise SomeException, 'message' ---- === Raising Explicit `RuntimeError` [[no-explicit-runtimeerror]] Don't specify `RuntimeError` explicitly in the two argument version of `raise`. [source,ruby] ---- # bad raise RuntimeError, 'message' # good - signals a RuntimeError by default raise 'message' ---- === Exception Class Messages [[exception-class-messages]] Prefer supplying an exception class and a message as two separate arguments to `raise`, instead of an exception instance. [source,ruby] ---- # bad raise SomeException.new('message') # Note that there is no way to do `raise SomeException.new('message'), backtrace`. # good raise SomeException, 'message' # Consistent with `raise SomeException, 'message', backtrace`. ---- === `return` from `ensure` [[no-return-ensure]] Do not return from an `ensure` block. If you explicitly return from a method inside an `ensure` block, the return will take precedence over any exception being raised, and the method will return as if no exception had been raised at all. In effect, the exception will be silently thrown away. [source,ruby] ---- # bad def foo raise ensure return 'very bad idea' end ---- === Implicit `begin` [[begin-implicit]] Use _implicit begin blocks_ where possible. [source,ruby] ---- # bad def foo begin # main logic goes here rescue # failure handling goes here end end # good def foo # main logic goes here rescue # failure handling goes here end ---- === Contingency Methods [[contingency-methods]] Mitigate the proliferation of `begin` blocks by using _contingency methods_ (a term coined by Avdi Grimm). [source,ruby] ---- # bad begin something_that_might_fail rescue IOError # handle IOError end begin something_else_that_might_fail rescue IOError # handle IOError end # good def with_io_error_handling yield rescue IOError # handle IOError end with_io_error_handling { something_that_might_fail } with_io_error_handling { something_else_that_might_fail } ---- === Suppressing Exceptions [[dont-hide-exceptions]] Don't suppress exceptions. [source,ruby] ---- # bad begin do_something # an exception occurs here rescue SomeError end # good begin do_something # an exception occurs here rescue SomeError handle_exception end # good begin do_something # an exception occurs here rescue SomeError # Notes on why exception handling is not performed end # good do_something rescue nil ---- === Using `rescue` as a Modifier [[no-rescue-modifiers]] Avoid using `rescue` in its modifier form. [source,ruby] ---- # bad - this catches exceptions of StandardError class and its descendant classes read_file rescue handle_error($!) # good - this catches only the exceptions of Errno::ENOENT class and its descendant classes def foo read_file rescue Errno::ENOENT => e handle_error(e) end ---- === Using Exceptions for Flow of Control [[no-exceptional-flows]] Don't use exceptions for flow of control. [source,ruby] ---- # bad begin n / d rescue ZeroDivisionError puts 'Cannot divide by 0!' end # good if d.zero? puts 'Cannot divide by 0!' else n / d end ---- === Blind Rescues [[no-blind-rescues]] Avoid rescuing the `Exception` class. This will trap signals and calls to `exit`, requiring you to `kill -9` the process. [source,ruby] ---- # bad begin # calls to exit and kill signals will be caught (except kill -9) exit rescue Exception puts "you didn't really want to exit, right?" # exception handling end # good begin # a blind rescue rescues from StandardError, not Exception as many # programmers assume. rescue => e # exception handling end # also good begin # an exception occurs here rescue StandardError => e # exception handling end ---- === Exception Rescuing Ordering [[exception-ordering]] Put more specific exceptions higher up the rescue chain, otherwise they'll never be rescued from. [source,ruby] ---- # bad begin # some code rescue StandardError => e # some handling rescue IOError => e # some handling that will never be executed end # good begin # some code rescue IOError => e # some handling rescue StandardError => e # some handling end ---- === Reading from a file [[file-read]] Use the convenience methods `File.read` or `File.binread` when only reading a file start to finish in a single operation. [source,ruby] ---- ## text mode # bad (only when reading from beginning to end - modes: 'r', 'rt', 'r+', 'r+t') File.open(filename).read File.open(filename, &:read) File.open(filename) { |f| f.read } File.open(filename) do |f| f.read end File.open(filename, 'r').read File.open(filename, 'r', &:read) File.open(filename, 'r') { |f| f.read } File.open(filename, 'r') do |f| f.read end # good File.read(filename) ## binary mode # bad (only when reading from beginning to end - modes: 'rb', 'r+b') File.open(filename, 'rb').read File.open(filename, 'rb', &:read) File.open(filename, 'rb') { |f| f.read } File.open(filename, 'rb') do |f| f.read end # good File.binread(filename) ---- === Writing to a file [[file-write]] Use the convenience methods `File.write` or `File.binwrite` when only opening a file to create / replace its content in a single operation. [source,ruby] ---- ## text mode # bad (only truncating modes: 'w', 'wt', 'w+', 'w+t') File.open(filename, 'w').write(content) File.open(filename, 'w') { |f| f.write(content) } File.open(filename, 'w') do |f| f.write(content) end # good File.write(filename, content) ## binary mode # bad (only truncating modes: 'wb', 'w+b') File.open(filename, 'wb').write(content) File.open(filename, 'wb') { |f| f.write(content) } File.open(filename, 'wb') do |f| f.write(content) end # good File.binwrite(filename, content) ---- === Release External Resources [[release-resources]] Release external resources obtained by your program in an `ensure` block. [source,ruby] ---- f = File.open('testfile') begin # .. process rescue # .. handle error ensure f.close if f end ---- === Auto-release External Resources [[auto-release-resources]] Use versions of resource obtaining methods that do automatic resource cleanup when possible. [source,ruby] ---- # bad - you need to close the file descriptor explicitly f = File.open('testfile') # some action on the file f.close # good - the file descriptor is closed automatically File.open('testfile') do |f| # some action on the file end ---- === Standard Exceptions [[standard-exceptions]] Prefer the use of exceptions from the standard library over introducing new exception classes. == Assignment & Comparison === Parallel Assignment [[parallel-assignment]] Avoid the use of parallel assignment for defining variables. Parallel assignment is allowed when it is the return of a method call, used with the splat operator, or when used to swap variable assignment. Parallel assignment is less readable than separate assignment. [source,ruby] ---- # bad a, b, c, d = 'foo', 'bar', 'baz', 'foobar' # good a = 'foo' b = 'bar' c = 'baz' d = 'foobar' # good - swapping variable assignment # Swapping variable assignment is a special case because it will allow you to # swap the values that are assigned to each variable. a = 'foo' b = 'bar' a, b = b, a puts a # => 'bar' puts b # => 'foo' # good - method return def multi_return [1, 2] end first, second = multi_return # good - use with splat first, *list = [1, 2, 3, 4] # first => 1, list => [2, 3, 4] hello_array = *'Hello' # => ["Hello"] a = *(1..3) # => [1, 2, 3] ---- === Values Swapping [[values-swapping]] Use parallel assignment when swapping 2 values. [source,ruby] ---- # bad tmp = x x = y y = tmp # good x, y = y, x ---- === Dealing with Trailing Underscore Variables in Destructuring Assignment [[trailing-underscore-variables]] Avoid the use of unnecessary trailing underscore variables during parallel assignment. Named underscore variables are to be preferred over underscore variables because of the context that they provide. Trailing underscore variables are necessary when there is a splat variable defined on the left side of the assignment, and the splat variable is not an underscore. [source,ruby] ---- # bad foo = 'one,two,three,four,five' # Unnecessary assignment that does not provide useful information first, second, _ = foo.split(',') first, _, _ = foo.split(',') first, *_ = foo.split(',') # good foo = 'one,two,three,four,five' # The underscores are needed to show that you want all elements # except for the last number of underscore elements *beginning, _ = foo.split(',') *beginning, something, _ = foo.split(',') a, = foo.split(',') a, b, = foo.split(',') # Unnecessary assignment to an unused variable, but the assignment # provides us with useful information. first, _second = foo.split(',') first, _second, = foo.split(',') first, *_ending = foo.split(',') ---- === Self-assignment [[self-assignment]] Use shorthand self assignment operators whenever applicable. [source,ruby] ---- # 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 ---- === Conditional Variable Initialization Shorthand [[double-pipe-for-uninit]] Use `||=` to initialize variables only if they're not already initialized. [source,ruby] ---- # bad name = name ? name : 'Bozhidar' # bad name = 'Bozhidar' unless name # good - set name to 'Bozhidar', only if it's nil or false name ||= 'Bozhidar' ---- [WARNING] ==== Don't use `||=` to initialize boolean variables. (Consider what would happen if the current value happened to be `false`.) [source,ruby] ---- # bad - would set enabled to true even if it was false enabled ||= true # good enabled = true if enabled.nil? ---- ==== === Existence Check Shorthand [[double-amper-preprocess]] Use `&&=` to preprocess variables that may or may not exist. Using `&&=` will change the value only if it exists, removing the need to check its existence with `if`. [source,ruby] ---- # bad if something something = something.downcase end # bad something = something ? something.downcase : nil # ok something = something.downcase if something # good something = something && something.downcase # better something &&= something.downcase ---- === Identity Comparison [[identity-comparison]] Prefer `equal?` over `==` when comparing `object_id`. `Object#equal?` is provided to compare objects for identity, and in contrast `Object#==` is provided for the purpose of doing value comparison. [source,ruby] ---- # bad foo.object_id == bar.object_id # good foo.equal?(bar) ---- Similarly, prefer using `Hash#compare_by_identity` than using `object_id` for keys: [source,ruby] ---- # bad hash = {} hash[foo.object_id] = :bar if hash.key?(baz.object_id) # ... # good hash = {}.compare_by_identity hash[foo] = :bar if hash.key?(baz) # ... ---- Note that `Set` also has `Set#compare_by_identity` available. === Explicit Use of the Case Equality Operator [[no-case-equality]] Avoid explicit use of the case equality operator `===`. As its name implies it is meant to be used implicitly by `case` expressions and outside of them it yields some pretty confusing code. [source,ruby] ---- # bad Array === something (1..100) === 7 /something/ === some_string # good something.is_a?(Array) (1..100).include?(7) some_string.match?(/something/) ---- NOTE: With direct subclasses of `BasicObject`, using `is_a?` is not an option since `BasicObject` doesn't provide that method (it's defined in `Object`). In those rare cases it's OK to use `===`. === `is_a?` vs `kind_of?` [[is-a-vs-kind-of]] Prefer `is_a?` over `kind_of?`. The two methods are synonyms, but `is_a?` is the more commonly used name in the wild. [source,ruby] ---- # bad something.kind_of?(Array) # good something.is_a?(Array) ---- === `is_a?` vs `instance_of?` [[is-a-vs-instance-of]] Prefer `is_a?` over `instance_of?`. While the two methods are similar, `is_a?` will consider the whole inheritance chain (superclasses and included modules), which is what you normally would want to do. `instance_of?`, on the other hand, only returns `true` if an object is an instance of that exact class you're checking for, not a subclass. [source,ruby] ---- # bad something.instance_of?(Array) # good something.is_a?(Array) ---- === `instance_of?` vs class comparison [[instance-of-vs-class-comparison]] Use `Object#instance_of?` instead of class comparison for equality. [source,ruby] ---- # bad var.class == Date var.class.equal?(Date) var.class.eql?(Date) var.class.name == 'Date' # good var.instance_of?(Date) ---- === `==` vs `eql?` [[eql]] Do not use `eql?` when using `==` will do. The stricter comparison semantics provided by `eql?` are rarely needed in practice. [source,ruby] ---- # bad - eql? is the same as == for strings 'ruby'.eql? some_str # good 'ruby' == some_str 1.0.eql? x # eql? makes sense here if want to differentiate between Integer and Float 1 ---- == Blocks, Procs & Lambdas === Proc Application Shorthand [[single-action-blocks]] Use the Proc call shorthand when the called method is the only operation of a block. [source,ruby] ---- # bad names.map { |name| name.upcase } # good names.map(&:upcase) ---- === Single-line Blocks Delimiters [[single-line-blocks]] Prefer `{...}` over `do...end` for single-line blocks. Avoid using `{...}` for multi-line blocks (multi-line chaining is always ugly). Always use `do...end` for "control flow" and "method definitions" (e.g. in Rakefiles and certain DSLs). Avoid `do...end` when chaining. [source,ruby] ---- names = %w[Bozhidar Filipp Sarah] # bad names.each do |name| puts name end # good names.each { |name| puts name } # bad names.select do |name| name.start_with?('S') end.map { |name| name.upcase } # good names.select { |name| name.start_with?('S') }.map(&:upcase) ---- Some will argue that multi-line chaining would look OK with the use of {...}, but they should ask themselves - is this code really readable and can the blocks' contents be extracted into nifty methods? === Explicit Block Argument [[block-argument]] Consider using explicit block argument to avoid writing block literal that just passes its arguments to another block. [source,ruby] ---- require 'tempfile' # bad def with_tmp_dir Dir.mktmpdir do |tmp_dir| Dir.chdir(tmp_dir) { |dir| yield dir } # block just passes arguments end end # good def with_tmp_dir(&block) Dir.mktmpdir do |tmp_dir| Dir.chdir(tmp_dir, &block) end end with_tmp_dir do |dir| puts "dir is accessible as a parameter and pwd is set: #{dir}" end ---- === Trailing Comma in Block Parameters [[no-trailing-parameters-comma]] Avoid comma after the last parameter in a block, except in cases where only a single argument is present and its removal would affect functionality (for instance, array destructuring). [source,ruby] ---- # bad - easier to move/add/remove parameters, but still not preferred [[1, 2, 3], [4, 5, 6]].each do |a, b, c,| a + b + c end # good [[1, 2, 3], [4, 5, 6]].each do |a, b, c| a + b + c end # bad [[1, 2, 3], [4, 5, 6]].each { |a, b, c,| a + b + c } # good [[1, 2, 3], [4, 5, 6]].each { |a, b, c| a + b + c } # good - this comma is meaningful for array destructuring [[1, 2, 3], [4, 5, 6]].map { |a,| a } ---- === Nested Method Definitions [[no-nested-methods]] Do not use nested method definitions, use lambda instead. Nested method definitions actually produce methods in the same scope (e.g. class) as the outer method. Furthermore, the "nested method" will be redefined every time the method containing its definition is called. [source,ruby] ---- # bad def foo(x) def bar(y) # body omitted end bar(x) end # good - the same as the previous, but no bar redefinition on every foo call def bar(y) # body omitted end def foo(x) bar(x) end # also good def foo(x) bar = ->(y) { ... } bar.call(x) end ---- === Multi-line Lambda Definition [[lambda-multi-line]] Use the new lambda literal syntax for single-line body blocks. Use the `lambda` method for multi-line blocks. [source,ruby] ---- # bad l = lambda { |a, b| a + b } l.call(1, 2) # correct, but looks extremely awkward l = ->(a, b) do tmp = a * 7 tmp * b / 50 end # good l = ->(a, b) { a + b } l.call(1, 2) l = lambda do |a, b| tmp = a * 7 tmp * b / 50 end ---- === Stabby Lambda Definition with Parameters [[stabby-lambda-with-args]] Don't omit the parameter parentheses when defining a stabby lambda with parameters. [source,ruby] ---- # bad l = ->x, y { something(x, y) } # good l = ->(x, y) { something(x, y) } ---- === Stabby Lambda Definition without Parameters [[stabby-lambda-no-args]] Omit the parameter parentheses when defining a stabby lambda with no parameters. [source,ruby] ---- # bad l = ->() { something } # good l = -> { something } ---- === `proc` vs `Proc.new` [[proc]] Prefer `proc` over `Proc.new`. [source,ruby] ---- # bad p = Proc.new { |n| puts n } # good p = proc { |n| puts n } ---- === Proc Call [[proc-call]] Prefer `proc.call()` over `proc[]` or `proc.()` for both lambdas and procs. [source,ruby] ---- # bad - looks similar to Enumeration access l = ->(v) { puts v } l[1] # good - most compact form, but might be confusing for newcomers to Ruby l = ->(v) { puts v } l.(1) # good - a bit verbose, but crystal clear l = ->(v) { puts v } l.call(1) ---- == Methods === Short Methods [[short-methods]] Avoid methods longer than 10 LOC (lines of code). Ideally, most methods will be shorter than 5 LOC. Empty lines do not contribute to the relevant LOC. === Top-Level Methods Avoid top-level method definitions. Organize them in modules, classes or structs instead. NOTE: It is fine to use top-level method definitions in scripts. [source,ruby] ---- # bad def some_method; end # good class SomeClass def some_method; end end ---- === No Single-line Methods [[no-single-line-methods]] Avoid single-line methods. Although they are somewhat popular in the wild, there are a few peculiarities about their definition syntax that make their use undesirable. At any rate - there should be no more than one expression in a single-line method. NOTE: Ruby 3 introduced an alternative syntax for single-line method definitions, that's discussed in the next section of the guide. [source,ruby] ---- # bad def too_much; something; something_else; end # okish - notice that the first ; is required def no_braces_method; body end # okish - notice that the second ; is optional def no_braces_method; body; end # okish - valid syntax, but no ; makes it kind of hard to read def some_method() body end # good def some_method body end ---- One exception to the rule are empty-body methods. [source,ruby] ---- # good def no_op; end ---- === Endless Methods Only use Ruby 3.0's endless method definitions with a single line body. Ideally, such method definitions should be both simple (a single expression) and free of side effects. NOTE: It's important to understand that this guideline doesn't contradict the previous one. We still caution against the use of single-line method definitions, but if such methods are to be used, prefer endless methods. [source,ruby] ---- # bad def fib(x) = if x < 2 x else fib(x - 1) + fib(x - 2) end # good def the_answer = 42 def get_x = @x def square(x) = x * x # Not (so) good: has side effect def set_x(x) = (@x = x) def print_foo = puts("foo") ---- === Double Colons [[double-colons]] Use `::` only to reference constants (this includes classes and modules) and constructors (like `Array()` or `Nokogiri::HTML()`). Do not use `::` for regular method calls. [source,ruby] ---- # bad SomeClass::some_method some_object::some_method # good SomeClass.some_method some_object.some_method SomeModule::SomeClass::SOME_CONST SomeModule::SomeClass() ---- === Colon Method Definition [[colon-method-definition]] Do not use `::` to define class methods. [source,ruby] ---- # bad class Foo def self::some_method end end # good class Foo def self.some_method end end ---- === Method Definition Parentheses [[method-parens]] Use `def` with parentheses when there are parameters. Omit the parentheses when the method doesn't accept any parameters. [source,ruby] ---- # bad def some_method() # body omitted end # good def some_method # body omitted end # bad def some_method_with_parameters param1, param2 # body omitted end # good def some_method_with_parameters(param1, param2) # body omitted end ---- === Method Call Parentheses [[method-invocation-parens]][[method-call-parens]] Use parentheses around the arguments of method calls, especially if the first argument begins with an open parenthesis `(`, as in `f((3 + 2) + 1)`. [source,ruby] ---- # bad x = Math.sin y # good x = Math.sin(y) # bad array.delete e # good array.delete(e) # bad temperance = Person.new 'Temperance', 30 # good temperance = Person.new('Temperance', 30) ---- ==== Method Call with No Arguments [[method-invocation-parens-no-args]][[method-call-parens-no-args]] Always omit parentheses for method calls with no arguments. [source,ruby] ---- # bad Kernel.exit!() 2.even?() fork() 'test'.upcase() # good Kernel.exit! 2.even? fork 'test'.upcase ---- ==== Methods That are Part of an Internal DSL [[method-invocation-parens-internal-dsl]][[method-call-parens-internal-dsl]] Always omit parentheses for methods that are part of an internal DSL (e.g., Rake, Rails, RSpec): [source,ruby] ---- # bad validates(:name, presence: true) # good validates :name, presence: true ---- ==== Methods That Have "keyword" Status in Ruby [[method-invocation-parens-keyword]][[method-call-parens-keyword]] Always omit parentheses for methods that have "keyword" status in Ruby. NOTE: Unfortunately, it's not exactly clear _which_ methods have "keyword" status. There is agreement that declarative methods have "keyword" status. However, there's less agreement on which non-declarative methods, if any, have "keyword" status. ===== Declarative Methods That Have "keyword" Status in Ruby [[method-invocation-parens-declarative-keyword]][[method-call-parens-declarative-keyword]] Always omit parentheses for declarative methods (a.k.a. DSL methods or macro methods) that have "keyword" status in Ruby (e.g., various `Module` instance methods): [source,ruby] ---- class Person # bad attr_reader(:name, :age) # good attr_reader :name, :age # body omitted end ---- ===== Non-Declarative Methods That Have "keyword" Status in Ruby [[method-invocation-parens-non-declarative-keyword]][[method-call-parens-non-declarative-keyword]] For non-declarative methods with "keyword" status (e.g., various `Kernel` instance methods), two styles are considered acceptable. By far the most popular style is to omit parentheses. Rationale: The code reads better, and method calls look more like keywords. A less-popular style, but still acceptable, is to include parentheses. Rationale: The methods have ordinary semantics, so why treat them differently, and it's easier to achieve a uniform style by not worrying about which methods have "keyword" status. Whichever one you pick, apply it consistently. [source,ruby] ---- # good (most popular) puts temperance.age system 'ls' exit 1 # also good (less popular) puts(temperance.age) system('ls') exit(1) ---- ==== Using `super` with Arguments [[super-with-args]] Always use parentheses when calling `super` with arguments: [source,ruby] ---- # bad super name, age # good super(name, age) ---- IMPORTANT: When calling `super` without arguments, `super` and `super()` mean different things. Decide what is appropriate for your usage. === Too Many Params [[too-many-params]] Avoid parameter lists longer than three or four parameters. === Optional Arguments [[optional-arguments]] Define optional arguments at the end of the list of arguments. Ruby has some unexpected results when calling methods that have optional arguments at the front of the list. [source,ruby] ---- # bad def some_method(a = 1, b = 2, c, d) puts "#{a}, #{b}, #{c}, #{d}" end some_method('w', 'x') # => '1, 2, w, x' some_method('w', 'x', 'y') # => 'w, 2, x, y' some_method('w', 'x', 'y', 'z') # => 'w, x, y, z' # good def some_method(c, d, a = 1, b = 2) puts "#{a}, #{b}, #{c}, #{d}" end some_method('w', 'x') # => '1, 2, w, x' some_method('w', 'x', 'y') # => 'y, 2, w, x' some_method('w', 'x', 'y', 'z') # => 'y, z, w, x' ---- === Keyword Arguments Order Put required keyword arguments before optional keyword arguments. Otherwise, it's much harder to spot optional arguments there, if they're hidden somewhere in the middle. [source,ruby] ---- # bad def some_method(foo: false, bar:, baz: 10) # body omitted end # good def some_method(foo:, bar: false, baz: 10) # body omitted end ---- === Boolean Keyword Arguments [[boolean-keyword-arguments]] Use keyword arguments when passing a boolean argument to a method. [source,ruby] ---- # bad def some_method(bar = false) puts bar end # bad - common hack before keyword args were introduced def some_method(options = {}) bar = options.fetch(:bar, false) puts bar end # good def some_method(bar: false) puts bar end some_method # => false some_method(bar: true) # => true ---- === Keyword Arguments vs Optional Arguments [[keyword-arguments-vs-optional-arguments]] Prefer keyword arguments over optional arguments

本源码包内暂不包含可直接显示的源代码文件,请下载源码包。