diff --git a/README.md b/README.md index a7d07691e..d620d93ce 100644 --- a/README.md +++ b/README.md @@ -1,4574 +1,4571 @@ -# คำนำ - -> Role models are important.
-> -- Officer Alex J. Murphy / RoboCop - -มีสิ่งหนึ่งที่ทำให้ผมรู้สึกรำคาญตลอดเวลาในฐานะ นักพัฒนาภาษา Ruby—Python -นั้นก็คือการมีเอกสารอ้างอิงสำหรับการเขียนโปรแกรมที่ดี ([PEP-8][]) และการที่เราไม่เคยมีคู่มือที่เป็นทางการ -หรือ เอกสารการเขียนโค้ด Ruby และ แนวทางที่ดีในการเขียนโค้ด -One thing has always bothered me as a Ruby developer—Python developers -have a great programming style reference ([PEP-8][]) and we never got an -official guide, documenting Ruby coding style and best practices. And I do -believe that style matters. I also believe that a great hacker community, such -as Ruby has, should be quite capable of producing this coveted document. - -This guide started its life as our internal company Ruby coding guidelines -(written by yours truly). At some point I decided that the work I was doing -might be interesting to members of the Ruby community in general and that the -world had little need for another internal company guideline. But the world -could certainly benefit from a community-driven and community-sanctioned set of -practices, idioms and style prescriptions for Ruby programming. - -Since the inception of the guide I've received a lot of feedback from members of -the exceptional Ruby community around the world. Thanks for all the suggestions -and the support! Together we can make a resource beneficial to each and every -Ruby developer out there. - -By the way, if you're into Rails you might want to check out the complementary -[Ruby on Rails Style Guide][rails-style-guide]. - -# The Ruby Style Guide - -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 rules. I've tried to add -the rationale behind the rules (if it's omitted I've assumed it's pretty -obvious). - -I didn't come up with all the rules out of nowhere—they are mostly -based on my extensive career as a professional software engineer, -feedback and suggestions from members of the Ruby community and -various highly regarded Ruby programming resources, such as -["Programming Ruby"][pickaxe] and -["The Ruby Programming Language"][trpl]. - -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. - -This style guide evolves over time as additional conventions are -identified and past conventions are rendered obsolete by changes in -Ruby itself. - -You can generate a PDF or an HTML copy of this guide using -[Pandoc][]. - -[RuboCop][] is a static code analyzer (linter) and formatter, based on this -style guide. - -Translations of the guide are available in the following languages: - - * [Chinese Simplified](https://github.com/JuanitoFatas/ruby-style-guide/blob/master/README-zhCN.md) - * [Chinese Traditional](https://github.com/JuanitoFatas/ruby-style-guide/blob/master/README-zhTW.md) - * [Egyptian Arabic](https://github.com/HassanTC/ruby-style-guide/blob/master/README-EgAr.md) - * [French](https://github.com/gauthier-delacroix/ruby-style-guide/blob/master/README-frFR.md) - * [Japanese](https://github.com/fortissimo1997/ruby-style-guide/blob/japanese/README.ja.md) - * [Korean](https://github.com/dalzony/ruby-style-guide/blob/master/README-koKR.md) - * [Portuguese (pt-BR)](https://github.com/rubensmabueno/ruby-style-guide/blob/master/README-PT-BR.md) - * [Russian](https://github.com/arbox/ruby-style-guide/blob/master/README-ruRU.md) - * [Spanish](https://github.com/alemohamad/ruby-style-guide/blob/master/README-esLA.md) - * [Vietnamese](https://github.com/CQBinh/ruby-style-guide/blob/master/README-viVN.md) - -## Table of Contents - - * [Source Code Layout](#source-code-layout) - * [Syntax](#syntax) - * [Naming](#naming) - * [Comments](#comments) - * [Comment Annotations](#comment-annotations) - * [Magic Comments](#magic-comments) - * [Classes & Modules](#classes--modules) - * [Exceptions](#exceptions) - * [Collections](#collections) - * [Numbers](#numbers) - * [Strings](#strings) - * [Date & Time](#date--time) - * [Regular Expressions](#regular-expressions) - * [Percent Literals](#percent-literals) - * [Metaprogramming](#metaprogramming) - * [Misc](#misc) - * [Tools](#tools) - -## Source Code Layout - -> 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...
-> -- Jerry Coffin (on indentation) - - * - Use `UTF-8` as the source file encoding. - [[link](#utf-8)] - - * - Use two **spaces** per indentation level (aka soft tabs). No hard tabs. - [[link](#spaces-indentation)] - - ```ruby - # bad - four spaces - def some_method - do_something - end - - # good - def some_method - do_something - end - ``` - - * - Use Unix-style line endings. (\*BSD/Solaris/Linux/macOS users are covered by - default, Windows users have to be extra careful.) - [[link](#crlf)] - - * If you're using Git you might want to add the following - configuration setting to protect your project from Windows line - endings creeping in: - - ```bash - $ git config --global core.autocrlf true - ``` - - * - Don't use `;` to separate statements and expressions. As a corollary—use - one expression per line. - [[link](#no-semicolon)] - - ```ruby - # bad - puts 'foobar'; # superfluous semicolon - - puts 'foo'; puts 'bar' # two expressions on the same line - - # good - puts 'foobar' - - puts 'foo' - puts 'bar' - - puts 'foo', 'bar' # this applies to puts in particular - ``` - - * - Prefer a single-line format for class definitions with no body. - [[link](#single-line-classes)] - - ```ruby - # bad - class FooError < StandardError - end - - # okish - class FooError < StandardError; end - - # good - FooError = Class.new(StandardError) - ``` - - * - 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. - [[link](#no-single-line-methods)] - - ```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. - - ```ruby - # good - def no_op; end - ``` - - * - 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. - [[link](#spaces-operators)] - - ```ruby - sum = 1 + 2 - a, b = 1, 2 - class FooError < StandardError; end - ``` - - There are a few exceptions. One is the exponent operator: - - ```ruby - # bad - e = M * c ** 2 - - # good - e = M * c**2 - ``` - - Another exception is the slash in rational literals: - - ```ruby - # bad - o_scale = 1 / 48r - - # good - o_scale = 1/48r - ``` - - Another exception is the safe navigation operator: - ```ruby - # bad - foo &. bar - foo &.bar - foo&. bar - - # good - foo&.bar - ``` - - * - No spaces after `(`, `[` or before `]`, `)`. - Use spaces around `{` and before `}`. - [[link](#spaces-braces)] - - ```ruby - # bad - some( arg ).other - [ 1, 2, 3 ].each{|e| puts e} - - # good - some(arg).other - [1, 2, 3].each { |e| puts e } - ``` - - `{` and `}` deserve a bit of clarification, since they are used - for block and hash literals, as well as string interpolation. - - For hash literals two styles are considered acceptable. - The first variant is slightly more readable (and arguably more - popular in the Ruby community in general). The second variant has - the advantage of adding visual difference between block and hash - literals. Whichever one you pick—apply it consistently. - - ```ruby - # good - space after { and before } - { one: 1, two: 2 } - - # good - no space after { and before } - {one: 1, two: 2} - ``` - - With interpolated expressions, there should be no padded-spacing inside the braces. - - ```ruby - # bad - "From: #{ user.first_name }, #{ user.last_name }" - - # good - "From: #{user.first_name}, #{user.last_name}" - ``` - - * - No space after `!`. - [[link](#no-space-bang)] - - ```ruby - # bad - ! something - - # good - !something - ``` - - * - No space inside range literals. - [[link](#no-space-inside-range-literals)] - - ```ruby - # bad - 1 .. 3 - 'a' ... 'z' - - # good - 1..3 - 'a'...'z' - ``` - - * - Indent `when` as deep as `case`. 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). - [[link](#indent-when-to-case)] - - ```ruby - # bad - 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 - - # 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 - ``` - - * - When assigning the result of a conditional expression to a variable, - preserve the usual alignment of its branches. - [[link](#indent-conditional-assignment)] - - ```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 - ``` - - * - Use empty lines between method definitions and also to break up methods - into logical paragraphs internally. - [[link](#empty-lines-between-methods)] - - ```ruby - def some_method - data = initialize(options) - - data.manipulate! - - data.result - end - - def some_method - result - end - ``` - - * - Don't use several empty lines in a row. - [[link](#two-or-more-empty-lines)] - - ```ruby - # bad - It has two empty lines. - some_method - - - some_method - - # good - some_method - - some_method - ``` - - * - Use empty lines around access modifiers. - [[link](#empty-lines-around-access-modifier)] - - ```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 - ``` - - * - Don't use empty lines around method, class, module, block bodies. - [[link](#empty-lines-around-bodies)] - - ```ruby - # bad - class Foo - - def foo - - begin - - do_something do - - something - - end - - rescue - - something - - end - - end - - end - - # good - class Foo - def foo - begin - do_something do - something - end - rescue - something - end - end - end - ``` - - * - Avoid comma after the last parameter in a method call, especially when the - parameters are not on separate lines. - [[link](#no-trailing-params-comma)] - - ```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) - ``` - - * - Use spaces around the `=` operator when assigning default values to method - parameters: - [[link](#spaces-around-equals)] - - ```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). - - * - Avoid line continuation `\` where not required. In practice, avoid using - line continuations for anything but string concatenation. - [[link](#no-trailing-backslash)] - - ```ruby - # bad - result = 1 - \ - 2 - - # good (but still ugly as hell) - result = 1 \ - - 2 - - long_string = 'First part of the long string' \ - ' and second part of the long string' - ``` - - * - Adopt a consistent multi-line method chaining style. There are two popular - styles in the Ruby community, both of which are considered - good—leading `.` (Option A) and trailing `.` (Option B). - [[link](#consistent-multi-line-chains)] - - * **(Option A)** When continuing a chained method invocation on - another line keep the `.` on the second line. - - ```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 - ``` - - * **(Option B)** When continuing a chained method invocation on another line, - include the `.` on the first line to indicate that the - expression continues. - - ```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 - [here](https://github.com/rubocop-hq/ruby-style-guide/pull/176). - - * - Align the parameters of a method call if they span more than one - line. When aligning parameters is not appropriate due to line-length - constraints, single indent for the lines after the first is also - acceptable. - [[link](#no-double-indent)] - - ```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 - ``` - - * - Align the elements of array literals spanning multiple lines. - [[link](#align-multiline-arrays)] - - ```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] - ``` - - * - Add underscores to large numeric literals to improve their readability. - [[link](#underscores-in-numerics)] - - ```ruby - # bad - how many 0s are there? - num = 1000000 - - # good - much easier to parse for the human brain - num = 1_000_000 - ``` - - * - Prefer lowercase letters for numeric literal prefixes. - `0o` for octal, `0x` for hexadecimal and `0b` for binary. - Do not use `0d` prefix for decimal literals. - [[link](#numeric-literal-prefixes)] - - ```ruby - # bad - num = 01234 - num = 0O1234 - num = 0X12AB - num = 0B10101 - num = 0D1234 - num = 0d1234 - - # good - easier to separate digits from the prefix - num = 0o1234 - num = 0x12AB - num = 0b10101 - num = 1234 - ``` - - * - Use [YARD][yard] and its conventions for API documentation. - [[link](#api-documentation)] - - * - Limit lines to 80 characters. - [[link](#80-character-limits)] - - * - Avoid trailing whitespace. - [[link](#no-trailing-whitespace)] - - * - End each file with a newline. - [[link](#newline-eof)] - - * - Don't use block comments. They cannot be preceded by whitespace and are not - as easy to spot as regular comments. - [[link](#no-block-comments)] - - ```ruby - # bad - =begin - comment line - another comment line - =end - - # good - # comment line - # another comment line - ``` - -## Syntax - - * - Use `::` only to reference constants (this includes classes and - modules) and constructors (like `Array()` or `Nokogiri::HTML()`). - * Do not use `::` for regular method invocation. - [[link](#double-colons)] - - ```ruby - # bad - SomeClass::some_method - some_object::some_method - - # good - SomeClass.some_method - some_object.some_method - SomeModule::SomeClass::SOME_CONST - SomeModule::SomeClass() - ``` - - * - Do not use `::` to define class methods. - [[link](#colon-method-definition)] - - ```ruby - # bad - class Foo - def self::some_method - end - end - - # good - class Foo - def self.some_method - end - end - ``` - - * - Use `def` with parentheses when there are parameters. Omit the - parentheses when the method doesn't accept any parameters. - [[link](#method-parens)] - - ```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 - ``` - - * - Use parentheses around the arguments of method invocations, - especially if the first argument begins with an open parenthesis `(`, - as in `f((3 + 2) + 1)`. - [[link](#method-invocation-parens)] - - ```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) - ``` - - Always omit parentheses for - - * Method calls with no arguments: - - ```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 (e.g., Rake, Rails, RSpec): - - ```ruby - # bad - validates(:name, presence: true) - # good - validates :name, presence: true - ``` - - * Methods that have "keyword" status in Ruby: - - ```ruby - class Person - # bad - attr_reader(:name, :age) - # good - attr_reader :name, :age - - # body omitted - end - ``` - - Can omit parentheses for - - * Methods that have "keyword" status in Ruby, but are not declarative: - - ```Ruby - # good - puts(temperance.age) - system('ls') - # also good - puts temperance.age - system 'ls' - ``` - - * - 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. - [[link](#optional-arguments)] - - ```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' - ``` - - * - Use keyword arguments when passing boolean argument to a method. - [[link](#boolean-keyword-arguments)] - - ```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 - ``` - * - Prefer keyword arguments over optional arguments. - [[link](#keyword-arguments-vs-optional-arguments)] - - ```Ruby - # bad - def some_method(a, b = 5, c = 1) - # body omitted - end - - # good - def some_method(a, b: 5, c: 1) - # body omitted - end - ``` - - * - Use keyword arguments instead of option hashes. - [[link](#keyword-arguments-vs-option-hashes)] - - ```Ruby - # bad - def some_method(options = {}) - bar = options.fetch(:bar, false) - puts bar - end - - # good - def some_method(bar: false) - puts bar - end - ``` - - * - 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. - [[link](#parallel-assignment)] - - ```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] - ``` - - * - 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. - [[link]](#trailing-underscore-variables) - - ```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(',') - ``` - - * - 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. - [[link](#no-for-loops)] - - ```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's block - elem # => NameError: undefined local variable or method `elem' - ``` - - * - Do not use `then` for multi-line `if`/`unless`. - [[link](#no-then)] - - ```ruby - # bad - if some_condition then - # body omitted - end - - # good - if some_condition - # body omitted - end - ``` - - * - Always put the condition on the same line as the `if`/`unless` in a - multi-line conditional. - [[link](#same-line-condition)] - - ```ruby - # bad - if - some_condition - do_something - do_something_else - end - - # good - if some_condition - do_something - do_something_else - end - ``` - - * - Favor the ternary operator(`?:`) over `if/then/else/end` constructs. - It's more common and obviously more concise. - [[link](#ternary-operator)] - - ```ruby - # bad - result = if some_condition then something else something_else end - - # good - result = some_condition ? something : something_else - ``` - - * - 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. - [[link](#no-nested-ternary)] - - ```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 - ``` - - * - Do not use `if x; ...`. Use the ternary - operator instead. - [[link](#no-semicolon-ifs)] - - ```ruby - # bad - result = if some_condition; something else something_else end - - # good - result = some_condition ? something : something_else - ``` - - * - Leverage the fact that `if` and `case` are expressions which return a - result. - [[link](#use-if-case-returns)] - - ```ruby - # bad - if condition - result = x - else - result = y - end - - # good - result = - if condition - x - else - y - end - ``` - - * - Use `when x then ...` for one-line cases. The alternative syntax `when x: - ...` has been removed as of Ruby 1.9. - [[link](#one-line-cases)] - - * - Do not use `when x; ...`. See the previous rule. - [[link](#no-when-semicolons)] - - * - Use `!` instead of `not`. - [[link](#bang-not-not)] - - ```ruby - # bad - parentheses are required because of op precedence - x = (not something) - - # good - x = !something - ``` - - * - Avoid the use of `!!`. - [[link](#no-bang-bang)] - - `!!` 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. If you want to do a `nil` check, use `nil?` - instead. - - ```ruby - # bad - x = 'test' - # obscure nil check - if !!x - # body omitted - end - - # good - x = 'test' - if x - # body omitted - end - ``` - - * - 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. - [[link](#no-and-or-or)] - - ```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!") - ``` - - * - Avoid multi-line `?:` (the ternary operator); use `if`/`unless` instead. - [[link](#no-multiline-ternary)] - - * - Favor modifier `if`/`unless` usage when you have a single-line body. Another - good alternative is the usage of control flow `&&`/`||`. - [[link](#if-as-a-modifier)] - - ```ruby - # bad - if some_condition - do_something - end - - # good - do_something if some_condition - - # another good option - some_condition && do_something - ``` - - * - Avoid modifier `if`/`unless` usage at the end of a non-trivial multi-line - block. - [[link](#no-multiline-if-modifiers)] - - ```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 - ``` - - * - Avoid nested modifier `if`/`unless`/`while`/`until` usage. Favor `&&`/`||` if - appropriate. - [[link](#no-nested-modifiers)] - - ```ruby - # bad - do_something if other_condition if some_condition - - # good - do_something if some_condition && other_condition - ``` - - * - Favor `unless` over `if` for negative conditions (or control flow `||`). - [[link](#unless-for-negatives)] - - ```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 - ``` - - * - Do not use `unless` with `else`. Rewrite these with the positive case first. - [[link](#no-else-with-unless)] - - ```ruby - # bad - unless success? - puts 'failure' - else - puts 'success' - end - - # good - if success? - puts 'success' - else - puts 'failure' - end - ``` - - * - Don't use parentheses around the condition of a control expression. - [[link](#no-parens-around-condition)] - - ```ruby - # bad - if (x > 10) - # body omitted - end - - # good - if x > 10 - # body omitted - end - ``` - -Note that there is an exception to this rule, namely [safe assignment in -condition](#safe-assignment-in-condition). - - * - Do not use `while/until condition do` for multi-line `while/until`. - [[link](#no-multiline-while-do)] - - ```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 - ``` - - * - Favor modifier `while/until` usage when you have a single-line body. - [[link](#while-as-a-modifier)] - - ```ruby - # bad - while some_condition - do_something - end - - # good - do_something while some_condition - ``` - - * - Favor `until` over `while` for negative conditions. - [[link](#until-for-negatives)] - - ```ruby - # bad - do_something while !some_condition - - # good - do_something until some_condition - ``` - - * - Use `Kernel#loop` instead of `while/until` when you need an infinite loop. - [[link](#infinite-loop)] - - ```ruby - # bad - while true - do_something - end - - until false - do_something - end - - # good - loop do - do_something - end - ``` - - * - Use `Kernel#loop` with `break` rather than `begin/end/until` or - `begin/end/while` for post-loop tests. - [[link](#loop-with-break)] - - ```ruby - # bad - begin - puts val - val += 1 - end while val < 0 - - # good - loop do - puts val - val += 1 - break unless val < 0 - end - ``` - - * - Omit the outer braces around an implicit options hash. - [[link](#no-braces-opts-hash)] - - ```ruby - # bad - user.set({ name: 'John', age: 45, permissions: { read: true } }) - - # good - user.set(name: 'John', age: 45, permissions: { read: true }) - ``` - - * - Omit both the outer braces and parentheses for methods that are part of an - internal DSL. - [[link](#no-dsl-decorating)] - - ```ruby - class Person < ActiveRecord::Base - # bad - validates(:name, { presence: true, length: { within: 1..10 } }) - - # good - validates :name, presence: true, length: { within: 1..10 } - end - ``` - - * - Use the proc invocation shorthand when the invoked method is the only operation of a block. - [[link](#single-action-blocks)] - - ```ruby - # bad - names.map { |name| name.upcase } - - # good - names.map(&:upcase) - ``` - - * - 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. - [[link](#single-line-blocks)] - - ```ruby - names = %w[Bozhidar Steve 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? - - * - Consider using explicit block argument to avoid writing block literal that - just passes its arguments to another block. Beware of the performance impact, - though, as the block gets converted to a Proc. - [[link](#block-argument)] - - ```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 - ``` - - * - Avoid `return` where not required for flow of control. - [[link](#no-explicit-return)] - - ```ruby - # bad - def some_method(some_arr) - return some_arr.size - end - - # good - def some_method(some_arr) - some_arr.size - end - ``` - - * - Avoid `self` where not required. (It is only required when calling a self - write accessor, methods named after reserved words, or overloadable operators.) - [[link](#no-self-unless-required)] - - ```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 - ``` - - * - As a corollary, avoid shadowing methods with local variables unless they are - both equivalent. - [[link](#no-shadowing)] - - ```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 - ``` - - * - 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*. - [[link](#safe-assignment-in-condition)] - - ```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 - ``` - - * - Use shorthand self assignment operators whenever applicable. - [[link](#self-assignment)] - - ```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 - ``` - - * - Use `||=` to initialize variables only if they're not already initialized. - [[link](#double-pipe-for-uninit)] - - ```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' - ``` - - * - Don't use `||=` to initialize boolean variables. (Consider what would happen - if the current value happened to be `false`.) - [[link](#no-double-pipes-for-bools)] - - ```ruby - # bad - would set enabled to true even if it was false - enabled ||= true - - # good - enabled = true if enabled.nil? - ``` - - * - 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`. - [[link](#double-amper-preprocess)] - - ```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 - ``` - - * - 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. - [[link](#no-case-equality)] - - ```ruby - # bad - Array === something - (1..100) === 7 - /something/ === some_string - - # good - something.is_a?(Array) - (1..100).include?(7) - some_string.match?(/something/) - ``` - - * - Do not use `eql?` when using `==` will do. The stricter comparison semantics - provided by `eql?` are rarely needed in practice. - [[link](#eql)] - - ```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 - ``` - - * - Avoid using Perl-style special variables (like `$:`, `$;`, etc. ). They are - quite cryptic and their use in anything but one-liner scripts is discouraged. - Use the human-friendly aliases provided by the `English` library. - [[link](#no-cryptic-perlisms)] - - ```ruby - # bad - $:.unshift File.dirname(__FILE__) - - # good - require 'English' - $LOAD_PATH.unshift File.dirname(__FILE__) - ``` - - * - Do not put a space between a method name and the opening parenthesis. - [[link](#parens-no-spaces)] - - ```ruby - # bad - f (3 + 2) + 1 - - # good - f(3 + 2) + 1 - ``` - - * - Always run the Ruby interpreter with the `-w` option so it will warn you if - you forget either of the rules above! - [[link](#always-warn-at-runtime)] - - * - 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 invoked. - [[link](#no-nested-methods)] - - ```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 - ``` - - * - Use the new lambda literal syntax for single line body blocks. Use the - `lambda` method for multi-line blocks. - [[link](#lambda-multi-line)] - - ```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 - ``` - - * - Don't omit the parameter parentheses when defining a stabby lambda with - parameters. - [[link](#stabby-lambda-with-args)] - - ```ruby - # bad - l = ->x, y { something(x, y) } - - # good - l = ->(x, y) { something(x, y) } - ``` - - * - Omit the parameter parentheses when defining a stabby lambda with - no parameters. - [[link](#stabby-lambda-no-args)] - - ```ruby - # bad - l = ->() { something } - - # good - l = -> { something } - ``` - - * - Prefer `proc` over `Proc.new`. - [[link](#proc)] - - ```ruby - # bad - p = Proc.new { |n| puts n } - - # good - p = proc { |n| puts n } - ``` - - * - Prefer `proc.call()` over `proc[]` or `proc.()` for both lambdas and procs. - [[link](#proc-call)] - - ```ruby - # bad - looks similar to Enumeration access - l = ->(v) { puts v } - l[1] - - # also bad - uncommon syntax - l = ->(v) { puts v } - l.(1) - - # good - l = ->(v) { puts v } - l.call(1) - ``` - - * - 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 and - will suppress their unused variable warnings. - [[link](#underscore-unused-vars)] - - ```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 - ``` - - * - Use `$stdout/$stderr/$stdin` instead of `STDOUT/STDERR/STDIN`. - `STDOUT/STDERR/STDIN` are constants, and while you can actually reassign - (possibly to redirect some stream) constants in Ruby, you'll get an - interpreter warning if you do so. - [[link](#global-stdout)] - - * - Use `warn` instead of `$stderr.puts`. Apart from being more concise and - clear, `warn` allows you to suppress warnings if you need to (by setting the - warn level to 0 via `-W0`). - [[link](#warn)] - - * - Favor the use of `sprintf` and its alias `format` over the fairly cryptic - `String#%` method. - [[link](#sprintf)] - - ```ruby - # bad - '%d %d' % [20, 10] - # => '20 10' - - # good - sprintf('%d %d', 20, 10) - # => '20 10' - - # good - sprintf('%d %d', first: 20, second: 10) - # => '20 10' - - format('%d %d', 20, 10) - # => '20 10' - - # good - format('%d %d', first: 20, second: 10) - # => '20 10' - ``` - - * - When using named format string tokens, favor `%s` over `%{name}` because it encodes information about the type of the value. - [[link]](#named-format-tokens) - - ```ruby - # bad - format('Hello, %{name}', name: 'John') - - # good - format('Hello, %s', name: 'John') - ``` - - * - Favor the use of `Array#join` over the fairly cryptic `Array#*` with - a string argument. - [[link](#array-join)] - - ```ruby - # bad - %w[one two three] * ', ' - # => 'one, two, three' - - # good - %w[one two three].join(', ') - # => 'one, two, three' - ``` - - * - Use `Array()` instead of explicit `Array` check or `[*var]`, when dealing - with a variable you want to treat as an Array, but you're not certain it's an - array. - [[link](#array-coercion)] - - ```ruby - # bad - paths = [paths] unless paths.is_a? Array - paths.each { |path| do_something(path) } - - # bad (always creates a new Array instance) - [*paths].each { |path| do_something(path) } - - # good (and a bit more readable) - Array(paths).each { |path| do_something(path) } - ``` - - * - Use ranges or `Comparable#between?` instead of complex comparison logic when - possible. - [[link](#ranges-or-between)] - - ```ruby - # bad - do_something if x >= 1000 && x <= 2000 - - # good - do_something if (1000..2000).include?(x) - - # good - do_something if x.between?(1000, 2000) - ``` - - * - Favor the use of predicate methods to explicit comparisons with `==`. - Numeric comparisons are OK. - [[link](#predicate-methods)] - - ```ruby - # bad - if x % 2 == 0 - end - - if x % 2 == 1 - end - - if x == nil - end - - # good - if x.even? - end - - if x.odd? - end - - if x.nil? - end - - if x.zero? - end - - if x == 0 - end - ``` - - * - Don't do explicit non-`nil` checks unless you're dealing with boolean - values. - [[link](#no-non-nil-checks)] - - ```ruby - # bad - do_something if !something.nil? - do_something if something != nil - - # good - do_something if something - - # good - dealing with a boolean - def value_set? - !@some_boolean.nil? - end - ``` - - * - Avoid the use of `BEGIN` blocks. - [[link](#no-BEGIN-blocks)] - - * - Do not use `END` blocks. Use `Kernel#at_exit` instead. - [[link](#no-END-blocks)] - - ```ruby - # bad - END { puts 'Goodbye!' } - - # good - at_exit { puts 'Goodbye!' } - ``` - - * - Avoid the use of flip-flops. - [[link](#no-flip-flops)] - - * - Avoid use of nested conditionals for flow of control. - [[link](#no-nested-conditionals)] - - 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. - - ```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. - - ```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 - ``` - - * - Prefer `map` over `collect`, `find` over `detect`, `select` over `find_all`, - `reduce` over `inject` and `size` over `length`. This is not a hard - requirement; if the use of the alias enhances readability, it's ok to use it. - The rhyming methods are inherited from Smalltalk and are not common in other - programming languages. The reason the use of `select` is encouraged over - `find_all` is that it goes together nicely with `reject` and its name is - pretty self-explanatory. - [[link](#map-find-select-reduce-size)] - - * - Don't use `count` as a substitute for `size`. For `Enumerable` objects other - than `Array` it will iterate the entire collection in order to determine its - size. - [[link](#count-vs-size)] - - ```ruby - # bad - some_hash.count - - # good - some_hash.size - ``` - - * - Use `flat_map` instead of `map` + `flatten`. This does not apply for arrays - with a depth greater than 2, i.e. if `users.first.songs == ['a', ['b','c']]`, - then use `map + flatten` rather than `flat_map`. `flat_map` flattens the - array by 1, whereas `flatten` flattens it all the way. - [[link](#flat-map)] - - ```ruby - # bad - all_songs = users.map(&:songs).flatten.uniq - - # good - all_songs = users.flat_map(&:songs).uniq - ``` - - * - Prefer `reverse_each` to `reverse.each` because some classes that `include - Enumerable` will provide an efficient implementation. Even in the worst case - where a class does not provide a specialized implementation, the general - implementation inherited from `Enumerable` will be at least as efficient as - using `reverse.each`. - [[link](#reverse-each)] - - ```ruby - # bad - array.reverse.each { ... } - - # good - array.reverse_each { ... } - ``` - -## Naming - -> The only real difficulties in programming are cache invalidation and -> naming things.
-> -- Phil Karlton - - * - Name identifiers in English. - [[link](#english-identifiers)] - - ```ruby - # bad - identifier using non-ascii characters - заплата = 1_000 - - # bad - identifier is a Bulgarian word, written with Latin letters (instead of Cyrillic) - zaplata = 1_000 - - # good - salary = 1_000 - ``` - - * - Use `snake_case` for symbols, methods and variables. - [[link](#snake-case-symbols-methods-vars)] - - ```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 - ``` - - * - Do not separate numbers from letters on symbols, methods and variables. - [[link](#snake-case-symbols-methods-vars-with-numbers)] - - ```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 - ``` - - - * - Use `CamelCase` for classes and modules. (Keep acronyms like HTTP, RFC, XML - uppercase.) - [[link](#camelcase-classes)] - - ```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 - ``` - - * - Use `snake_case` for naming files, e.g. `hello_world.rb`. - [[link](#snake-case-files)] - - * - Use `snake_case` for naming directories, e.g. - `lib/hello_world/hello_world.rb`. - [[link](#snake-case-dirs)] - - * - Aim to have just a single class/module per source file. Name the file name - as the class/module, but replacing `CamelCase` with `snake_case`. - [[link](#one-class-per-file)] - - * - Use `SCREAMING_SNAKE_CASE` for other constants. - [[link](#screaming-snake-case)] - - ```ruby - # bad - SomeConst = 5 - - # good - SOME_CONST = 5 - ``` - - * - 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. - [[link](#bool-methods-qmark)] - - * - 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?`. - [[link](#bool-methods-prefix)] - - ```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 - ``` - - * - 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. - [[link](#dangerous-method-bang)] - - ```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 - ``` - - * - Define the non-bang (safe) method in terms of the bang (dangerous) one if - possible. - [[link](#safe-because-unsafe)] - - ```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 - ``` - - * - 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 same or coercible - types. - [[link](#other-arg)] - - * 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)`. - - ```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 - ``` - -## Comments - -> Good code is its own best documentation. As you're about to add a -> comment, ask yourself, "How can I improve the code so that this -> comment isn't needed?" Improve the code and then document it to make -> it even clearer.
-> -- Steve McConnell - - * - Write self-documenting code and ignore the rest of this section. Seriously! - [[link](#no-comments)] - - * - Write comments in English. - [[link](#english-comments)] - - * - Use one space between the leading `#` character of the comment and the text - of the comment. - [[link](#hash-space)] - - * - Comments longer than a word are capitalized and use punctuation. Use [one - space](https://en.wikipedia.org/wiki/Sentence_spacing) after periods. - [[link](#english-syntax)] - - * - Avoid superfluous comments. - [[link](#no-superfluous-comments)] - - ```ruby - # bad - counter += 1 # Increments counter by one. - ``` - - * - Keep existing comments up-to-date. An outdated comment is worse than no - comment at all. - [[link](#comment-upkeep)] - -> Good code is like a good joke: it needs no explanation.
-> — old programmers maxim, through [Russ Olsen](http://eloquentruby.com/blog/2011/03/07/good-code-and-good-jokes/) - - * - Avoid writing comments to explain bad code. Refactor the code to make it - self-explanatory. ("Do or do not—there is no try." Yoda) - [[link](#refactor-dont-comment)] - -### Comment Annotations - - * - Annotations should usually be written on the line immediately above the - relevant code. - [[link](#annotate-above)] - - * - The annotation keyword is followed by a colon and a space, then a note - describing the problem. - [[link](#annotate-keywords)] - - * - If multiple lines are required to describe the problem, subsequent lines - should be indented three spaces after the `#` (one general plus two for - indentation purpose). - [[link](#indent-annotations)] - - ```ruby - def bar - # FIXME: This has crashed occasionally since v3.2.1. It may - # be related to the BarBazUtil upgrade. - baz(:quux) - end - ``` - - * - In cases where the problem is so obvious that any documentation would be - redundant, annotations may be left at the end of the offending line with no - note. This usage should be the exception and not the rule. - [[link](#rare-eol-annotations)] - - ```ruby - def bar - sleep 100 # OPTIMIZE - end - ``` - - * - Use `TODO` to note missing features or functionality that should be added at - a later date. - [[link](#todo)] - - * - Use `FIXME` to note broken code that needs to be fixed. - [[link](#fixme)] - - * - Use `OPTIMIZE` to note slow or inefficient code that may cause performance - problems. - [[link](#optimize)] - - * - Use `HACK` to note code smells where questionable coding practices were used - and should be refactored away. - [[link](#hack)] - - * - Use `REVIEW` to note anything that should be looked at to confirm it is - working as intended. For example: `REVIEW: Are we sure this is how the client - does X currently?` - [[link](#review)] - - * - Use other custom annotation keywords if it feels appropriate, but be sure to - document them in your project's `README` or similar. - [[link](#document-annotations)] - -### Magic Comments - - * - Place magic comments above all code and documentation in a file (except shebangs, which are discussed next). - - ```ruby - # bad - # Some documentation about Person - - # frozen_string_literal: true - class Person - end - - # good - # frozen_string_literal: true - - # Some documentation about Person - class Person - end - ``` - - * - Place magic comments below shebangs when they are present in a file. - [[link](#below-shebang)] - - ```ruby - # bad - # frozen_string_literal: true - #!/usr/bin/env ruby - - App.parse(ARGV) - - # good - #!/usr/bin/env ruby - # frozen_string_literal: true - - App.parse(ARGV) - ``` - - * - Use one magic comment per line if you need multiple. - [[link](#one-magic-comment-per-line)] - - ```ruby - # bad - # -*- frozen_string_literal: true; encoding: ascii-8bit -*- - - # good - # frozen_string_literal: true - # encoding: ascii-8bit - ``` - - * - Separate magic comments from code and documentation with a blank line. - [[link](#separate-magic-comments-from-code)] - - ```ruby - # bad - # frozen_string_literal: true - # Some documentation for Person - class Person - # Some code - end - - # good - # frozen_string_literal: true - - # Some documentation for Person - class Person - # Some code - end - ``` - -## Classes & Modules - - * - Use a consistent structure in your class definitions. - [[link](#consistent-classes)] - - ```ruby - class Person - # extend and include go first - extend SomeModule - include AnotherModule - - # inner classes - CustomError = Class.new(StandardError) - - # constants are next - SOME_CONSTANT = 20 - - # afterwards we have attribute macros - attr_reader :name - - # followed by other macros (if any) - validates :name - - # public class methods are next in line - def self.some_method - end - - # initialization goes between class methods and other instance methods - def initialize - end - - # followed by other public instance methods - def some_method - end - - # protected and private methods are grouped near the end - protected - - def some_protected_method - end - - private - - def some_private_method - end - end - ``` - - * - Split multiple mixins into separate statements. - [[link](#mixin-grouping)] - - ```ruby - # bad - class Person - include Foo, Bar - end - - # good - class Person - # multiple mixins go in separate statements - include Foo - include Bar - end - ``` - - * - Don't nest multi-line classes within classes. Try to have such nested - classes each in their own file in a folder named like the containing class. - [[link](#file-classes)] - - ```ruby - # bad - - # foo.rb - class Foo - class Bar - # 30 methods inside - end - - class Car - # 20 methods inside - end - - # 30 methods inside - end - - # good - - # foo.rb - class Foo - # 30 methods inside - end - - # foo/bar.rb - class Foo - class Bar - # 30 methods inside - end - end - - # foo/car.rb - class Foo - class Car - # 20 methods inside - end - end - ``` - - * - Define (and reopen) namespaced classes and modules using explicit nesting. - Using the scope resolution operator can lead to surprising constant lookups - due to Ruby's [lexical scoping](https://cirw.in/blog/constant-lookup.html), - which depends on the module nesting at the point of definition. - [[link](#namespace-definition)] - - ```Ruby - module Utilities - class Queue - end - end - - # bad - class Utilities::Store - Module.nesting # => [Utilities::Store] - - def initialize - # Refers to the top level ::Queue class because Utilities isn't in the - # current nesting chain. - @queue = Queue.new - end - end - - # good - module Utilities - class WaitingList - Module.nesting # => [Utilities::WaitingList, Utilities] - - def initialize - @queue = Queue.new # Refers to Utilities::Queue - end - end - end - ``` - - * - Prefer modules to classes with only class methods. Classes should be used - only when it makes sense to create instances out of them. - [[link](#modules-vs-classes)] - - ```ruby - # bad - class SomeClass - def self.some_method - # body omitted - end - - def self.some_other_method - # body omitted - end - end - - # good - module SomeModule - module_function - - def some_method - # body omitted - end - - def some_other_method - # body omitted - end - end - ``` - - * - Favor the use of `module_function` over `extend self` when you want to turn - a module's instance methods into class methods. - [[link](#module-function)] - - ```ruby - # bad - module Utilities - extend self - - def parse_something(string) - # do stuff here - end - - def other_utility_method(number, string) - # do some more stuff - end - end - - # good - module Utilities - module_function - - def parse_something(string) - # do stuff here - end - - def other_utility_method(number, string) - # do some more stuff - end - end - ``` - - * - When designing class hierarchies make sure that they conform to the [Liskov - Substitution - Principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle). - [[link](#liskov)] - - * - Try to make your classes as - [SOLID](https://en.wikipedia.org/wiki/SOLID_\(object-oriented_design\)) as - possible. - [[link](#solid-design)] - - * - Always supply a proper `to_s` method for classes that represent domain - objects. - [[link](#define-to-s)] - - ```ruby - class Person - attr_reader :first_name, :last_name - - def initialize(first_name, last_name) - @first_name = first_name - @last_name = last_name - end - - def to_s - "#{first_name} #{last_name}" - end - end - ``` - - * - Use the `attr` family of functions to define trivial accessors or mutators. - [[link](#attr_family)] - - ```ruby - # bad - class Person - def initialize(first_name, last_name) - @first_name = first_name - @last_name = last_name - end - - def first_name - @first_name - end - - def last_name - @last_name - end - end - - # good - class Person - attr_reader :first_name, :last_name - - def initialize(first_name, last_name) - @first_name = first_name - @last_name = last_name - end - end - ``` - - * - For accessors and mutators, avoid prefixing method names with - `get_` and `set_`. - It is a Ruby convention to use attribute names for accessors (readers) and - `attr_name=` for mutators (writers). - [[link](#accessor_mutator_method_names)] - - ```ruby - # bad - class Person - def get_name - "#{@first_name} #{@last_name}" - end - - def set_name(name) - @first_name, @last_name = name.split(' ') - end - end - - # good - class Person - def name - "#{@first_name} #{@last_name}" - end - - def name=(name) - @first_name, @last_name = name.split(' ') - end - end - ``` - - * - Avoid the use of `attr`. Use `attr_reader` and `attr_accessor` instead. - [[link](#attr)] - - ```ruby - # bad - creates a single attribute accessor (deprecated in Ruby 1.9) - attr :something, true - attr :one, :two, :three # behaves as attr_reader - - # good - attr_accessor :something - attr_reader :one, :two, :three - ``` - - * - Consider using `Struct.new`, which defines the trivial accessors, - constructor and comparison operators for you. - [[link](#struct-new)] - - ```ruby - # good - class Person - attr_accessor :first_name, :last_name - - def initialize(first_name, last_name) - @first_name = first_name - @last_name = last_name - end - end - - # better - Person = Struct.new(:first_name, :last_name) do - end - ``` - - * - Don't extend an instance initialized by `Struct.new`. Extending it introduces - a superfluous class level and may also introduce weird errors if the file is - required multiple times. - [[link](#no-extend-struct-new)] - - ```ruby - # bad - class Person < Struct.new(:first_name, :last_name) - end - - # good - Person = Struct.new(:first_name, :last_name) - ``` - - * - Consider adding factory methods to provide additional sensible ways to - create instances of a particular class. - [[link](#factory-methods)] - - ```ruby - class Person - def self.create(options_hash) - # body omitted - end - end - ``` - - * - Prefer [duck-typing](https://en.wikipedia.org/wiki/Duck_typing) over - inheritance. - [[link](#duck-typing)] - - ```ruby - # bad - class Animal - # abstract method - def speak - end - end - - # extend superclass - class Duck < Animal - def speak - puts 'Quack! Quack' - end - end - - # extend superclass - class Dog < Animal - def speak - puts 'Bau! Bau!' - end - end - - # good - class Duck - def speak - puts 'Quack! Quack' - end - end - - class Dog - def speak - puts 'Bau! Bau!' - end - end - ``` - - * - Avoid the usage of class (`@@`) variables due to their "nasty" behavior in - inheritance. - [[link](#no-class-vars)] - - ```ruby - class Parent - @@class_var = 'parent' - - def self.print_class_var - puts @@class_var - end - end - - class Child < Parent - @@class_var = 'child' - end - - Parent.print_class_var # => will print 'child' - ``` - - As you can see all the classes in a class hierarchy actually share one - class variable. Class instance variables should usually be preferred - over class variables. - - * - Assign proper visibility levels to methods (`private`, `protected`) in - accordance with their intended usage. Don't go off leaving everything `public` - (which is the default). After all we're coding in *Ruby* now, not in *Python*. - [[link](#visibility)] - - * - Indent the `public`, `protected`, and `private` methods as much as the method - definitions they apply to. Leave one blank line above the visibility modifier - and one blank line below in order to emphasize that it applies to all methods - below it. - [[link](#indent-public-private-protected)] - - ```ruby - class SomeClass - def public_method - # some code - end - - private - - def private_method - # some code - end - - def another_private_method - # some code - end - end - ``` - - * - Use `def self.method` to define class methods. This makes the code - easier to refactor since the class name is not repeated. - [[link](#def-self-class-methods)] - - ```ruby - class TestClass - # bad - def TestClass.some_method - # body omitted - end - - # good - def self.some_other_method - # body omitted - end - - # Also possible and convenient when you - # have to define many class methods. - class << self - def first_method - # body omitted - end - - def second_method_etc - # body omitted - end - end - end - ``` - - * - Prefer `alias` when aliasing methods in lexical class scope as the - resolution of `self` in this context is also lexical, and it communicates - clearly to the user that the indirection of your alias will not be altered - at runtime or by any subclass unless made explicit. - [[link](#alias-method-lexically)] - - ```ruby - class Westerner - def first_name - @names.first - end - - alias given_name first_name - end - ``` - - Since `alias`, like `def`, is a keyword, prefer bareword arguments over - symbols or strings. In other words, do `alias foo bar`, not - `alias :foo :bar`. - - Also be aware of how Ruby handles aliases and inheritance: an alias - references the method that was resolved at the time the alias was defined; - it is not dispatched dynamically. - - ```ruby - class Fugitive < Westerner - def first_name - 'Nobody' - end - end - ``` - - In this example, `Fugitive#given_name` would still call the original - `Westerner#first_name` method, not `Fugitive#first_name`. To override the - behavior of `Fugitive#given_name` as well, you'd have to redefine it in the - derived class. - - ```ruby - class Fugitive < Westerner - def first_name - 'Nobody' - end - - alias given_name first_name - end - ``` - - * - Always use `alias_method` when aliasing methods of modules, classes, or - singleton classes at runtime, as the lexical scope of `alias` leads to - unpredictability in these cases. - [[link](#alias-method)] - - ```ruby - module Mononymous - def self.included(other) - other.class_eval { alias_method :full_name, :given_name } - end - end - - class Sting < Westerner - include Mononymous - end - ``` - - * - When class (or module) methods call other such methods, omit the use of a - leading `self` or own name followed by a `.` when calling other such methods. - This is often seen in "service classes" or other similar concepts where a - class is treated as though it were a function. This convention tends to reduce - repetitive boilerplate in such classes. - [[link](#class-and-self)] - - ```ruby - class TestClass - # bad -- more work when class renamed/method moved - def self.call(param1, param2) - TestClass.new(param1).call(param2) - end - - # bad -- more verbose than necessary - def self.call(param1, param2) - self.new(param1).call(param2) - end - - # good - def self.call(param1, param2) - new(param1).call(param2) - end - - # ...other methods... - end - ``` - -## Exceptions - - * - Prefer `raise` over `fail` for exceptions. - [[link](#prefer-raise-over-fail)] - - ```ruby - # bad - fail SomeException, 'message' - - # good - raise SomeException, 'message' - ``` - - * - Don't specify `RuntimeError` explicitly in the two argument version of - `raise`. - [[link](#no-explicit-runtimeerror)] - - ```ruby - # bad - raise RuntimeError, 'message' - - # good - signals a RuntimeError by default - raise 'message' - ``` - - * - Prefer supplying an exception class and a message as two separate arguments - to `raise`, instead of an exception instance. - [[link](#exception-class-messages)] - - ```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`. - ``` - - * - 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. - [[link](#no-return-ensure)] - - ```ruby - # bad - def foo - raise - ensure - return 'very bad idea' - end - ``` - - * - Use *implicit begin blocks* where possible. - [[link](#begin-implicit)] - - ```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 - ``` - - * - Mitigate the proliferation of `begin` blocks by using *contingency methods* - (a term coined by Avdi Grimm). - [[link](#contingency-methods)] - - ```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 } - ``` - - * - Don't suppress exceptions. - [[link](#dont-hide-exceptions)] - - ```ruby - # bad - begin - # an exception occurs here - rescue SomeError - # the rescue clause does absolutely nothing - end - - # bad - do_something rescue nil - ``` - - * - Avoid using `rescue` in its modifier form. - [[link](#no-rescue-modifiers)] - - ```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 => ex - handle_error(ex) - end - ``` - - * - Don't use exceptions for flow of control. - [[link](#no-exceptional-flows)] - - ```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 - ``` - - * - Avoid rescuing the `Exception` class. This will trap signals and calls to - `exit`, requiring you to `kill -9` the process. - [[link](#no-blind-rescues)] - - ```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 - ``` - - * - Put more specific exceptions higher up the rescue chain, otherwise they'll - never be rescued from. - [[link](#exception-ordering)] - - ```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 - ``` - - * - Release external resources obtained by your program in an `ensure` block. - [[link](#release-resources)] - - ```ruby - f = File.open('testfile') - begin - # .. process - rescue - # .. handle error - ensure - f.close if f - end - ``` - - * - Use versions of resource obtaining methods that do automatic - resource cleanup when possible. - [[link](#auto-release-resources)] - - ```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 - ``` - - * - Favor the use of exceptions from the standard library over introducing new - exception classes. - [[link](#standard-exceptions)] - -## Collections - - * - Prefer literal array and hash creation notation (unless you need to pass - parameters to their constructors, that is). - [[link](#literal-array-hash)] - - ```ruby - # bad - arr = Array.new - hash = Hash.new - - # good - arr = [] - arr = Array.new(10) - hash = {} - hash = Hash.new(0) - ``` - - * - Prefer `%w` to the literal array syntax when you need an array of words - (non-empty strings without spaces and special characters in them). Apply this - rule only to arrays with two or more elements. - [[link](#percent-w)] - - ```ruby - # bad - STATES = ['draft', 'open', 'closed'] - - # good - STATES = %w[draft open closed] - ``` - - * - Prefer `%i` to the literal array syntax when you need an array of symbols - (and you don't need to maintain Ruby 1.9 compatibility). Apply this rule only - to arrays with two or more elements. - [[link](#percent-i)] - - ```ruby - # bad - STATES = [:draft, :open, :closed] - - # good - STATES = %i[draft open closed] - ``` - - * - Avoid comma after the last item of an `Array` or `Hash` literal, especially - when the items are not on separate lines. - [[link](#no-trailing-array-commas)] - - ```ruby - # bad - easier to move/add/remove items, but still not preferred - VALUES = [ - 1001, - 2020, - 3333, - ] - - # bad - VALUES = [1001, 2020, 3333, ] - - # good - VALUES = [1001, 2020, 3333] - ``` - - * - Avoid the creation of huge gaps in arrays. - [[link](#no-gappy-arrays)] - - ```ruby - arr = [] - arr[100] = 1 # now you have an array with lots of nils - ``` - - * - When accessing the first or last element from an array, prefer `first` or - `last` over `[0]` or `[-1]`. - [[link](#first-and-last)] - - * - Use `Set` instead of `Array` when dealing with unique elements. `Set` - implements a collection of unordered values with no duplicates. This is a - hybrid of `Array`'s intuitive inter-operation facilities and `Hash`'s fast - lookup. - [[link](#set-vs-array)] - - * - Prefer symbols instead of strings as hash keys. - [[link](#symbols-as-keys)] - - ```ruby - # bad - hash = { 'one' => 1, 'two' => 2, 'three' => 3 } - - # good - hash = { one: 1, two: 2, three: 3 } - ``` - - * - Avoid the use of mutable objects as hash keys. - [[link](#no-mutable-keys)] - - * - Use the Ruby 1.9 hash literal syntax when your hash keys are symbols. - [[link](#hash-literals)] - - ```ruby - # bad - hash = { :one => 1, :two => 2, :three => 3 } - - # good - hash = { one: 1, two: 2, three: 3 } - ``` - - * - Don't mix the Ruby 1.9 hash syntax with hash rockets in the same hash - literal. When you've got keys that are not symbols stick to the hash rockets - syntax. - [[link](#no-mixed-hash-syntaces)] - - ```ruby - # bad - { a: 1, 'b' => 2 } - - # good - { :a => 1, 'b' => 2 } - ``` - - * - Use `Hash#key?` instead of `Hash#has_key?` and `Hash#value?` instead of - `Hash#has_value?`. - [[link](#hash-key)] - - ```ruby - # bad - hash.has_key?(:test) - hash.has_value?(value) - - # good - hash.key?(:test) - hash.value?(value) - ``` - - * - Use `Hash#each_key` instead of `Hash#keys.each` and `Hash#each_value` - instead of `Hash#values.each`. - [[link](#hash-each)] - - ```ruby - # bad - hash.keys.each { |k| p k } - hash.values.each { |v| p v } - hash.each { |k, _v| p k } - hash.each { |_k, v| p v } - - # good - hash.each_key { |k| p k } - hash.each_value { |v| p v } - ``` - - * - Use `Hash#fetch` when dealing with hash keys that should be present. - [[link](#hash-fetch)] - - ```ruby - heroes = { batman: 'Bruce Wayne', superman: 'Clark Kent' } - # bad - if we make a mistake we might not spot it right away - heroes[:batman] # => 'Bruce Wayne' - heroes[:supermann] # => nil - - # good - fetch raises a KeyError making the problem obvious - heroes.fetch(:supermann) - ``` - - * - Introduce default values for hash keys via `Hash#fetch` as opposed to using - custom logic. - [[link](#hash-fetch-defaults)] - - ```ruby - batman = { name: 'Bruce Wayne', is_evil: false } - - # bad - if we just use || operator with falsy value we won't get the expected result - batman[:is_evil] || true # => true - - # good - fetch works correctly with falsy values - batman.fetch(:is_evil, true) # => false - ``` - - * - Prefer the use of the block instead of the default value in `Hash#fetch` - if the code that has to be evaluated may have side effects or be expensive. - [[link](#use-hash-blocks)] - - ```ruby - batman = { name: 'Bruce Wayne' } - - # bad - if we use the default value, we eager evaluate it - # so it can slow the program down if done multiple times - batman.fetch(:powers, obtain_batman_powers) # obtain_batman_powers is an expensive call - - # good - blocks are lazy evaluated, so only triggered in case of KeyError exception - batman.fetch(:powers) { obtain_batman_powers } - ``` - - * - Use `Hash#values_at` when you need to retrieve several values consecutively - from a hash. - [[link](#hash-values-at)] - - ```ruby - # bad - email = data['email'] - username = data['nickname'] - - # good - email, username = data.values_at('email', 'nickname') - ``` - - * - Rely on the fact that as of Ruby 1.9 hashes are ordered. - [[link](#ordered-hashes)] - - * - Do not modify a collection while traversing it. - [[link](#no-modifying-collections)] - - * - When accessing elements of a collection, avoid direct access - via `[n]` by using an alternate form of the reader method if it is - supplied. This guards you from calling `[]` on `nil`. - [[link](#accessing-elements-directly)] - - ```ruby - # bad - Regexp.last_match[1] - - # good - Regexp.last_match(1) - ``` - - * - When providing an accessor for a collection, provide an alternate form - to save users from checking for `nil` before accessing an element in - the collection. - [[link](#provide-alternate-accessor-to-collections)] - - ```ruby - # bad - def awesome_things - @awesome_things - end - - # good - def awesome_things(index = nil) - if index && @awesome_things - @awesome_things[index] - else - @awesome_things - end - end - ``` -## Numbers - - * - ควรใช้ `Integer` ในการตรวจสอบประเภทของเลขจำนวนเต็ม. เพราะหากตรวจสอบด้วย `Fixnum` ที่จะให้ผลลัพท์ขึ้นกับ Platform ที่ใช้อาจทำให้ได้ผลลัพท์ที่ต่างกันระหว่างเครื่องระบบ 32-bit และระบบ 64-bit. - [[link](#integer-type-checking)] - - ```ruby - timestamp = Time.now.to_i - - # bad - timestamp.is_a? Fixnum - timestamp.is_a? Bignum - - # good - timestamp.is_a? Integer - ``` - - * - แนะนำให้ใช้ลำดับตัวเลขเมื่อต้องการสร้างตัวเลขสุ่ม แทนวิธี่การใส่ตัวเลขแล้วเพิ่มค่าทดแทน, เพราะจะเป็นการทำงานที่ตรงต่อจุดมุ่งหมายของคุณมากกว่า. ลองจินตนาการหากเป็นการทอยลูกเต๋า: - [[link](#random-numbers)] - - ```ruby - # bad - rand(6) + 1 - - # good - rand(1..6) - ``` - -## Strings - - * - Prefer string interpolation and string formatting instead of string - concatenation: - [[link](#string-interpolation)] - - ```ruby - # bad - email_with_name = user.name + ' <' + user.email + '>' - - # good - email_with_name = "#{user.name} <#{user.email}>" - - # good - email_with_name = format('%s <%s>', user.name, user.email) - ``` - - * - Adopt a consistent string literal quoting style. There are two popular - styles in the Ruby community, both of which are considered good—single - quotes by default (Option A) and double quotes by default (Option B). - [[link](#consistent-string-literals)] - - * **(Option A)** Prefer single-quoted strings when you don't need - string interpolation or special symbols such as `\t`, `\n`, `'`, - etc. - - ```ruby - # bad - name = "Bozhidar" - - name = 'De\'Andre' - - # good - name = 'Bozhidar' - - name = "De'Andre" - ``` - - * **(Option B)** Prefer double-quotes unless your string literal - contains `"` or escape characters you want to suppress. - - ```ruby - # bad - name = 'Bozhidar' - - sarcasm = "I \"like\" it." - - # good - name = "Bozhidar" - - sarcasm = 'I "like" it.' - ``` - - The string literals in this guide are aligned with the first style. - - * - Don't use the character literal syntax `?x`. Since Ruby 1.9 it's basically - redundant—`?x` would be interpreted as `'x'` (a string with a single - character in it). - [[link](#no-character-literals)] - - ```ruby - # bad - char = ?c - - # good - char = 'c' - ``` - - * - Don't leave out `{}` around instance and global variables being interpolated - into a string. - [[link](#curlies-interpolate)] - - ```ruby - class Person - attr_reader :first_name, :last_name - - def initialize(first_name, last_name) - @first_name = first_name - @last_name = last_name - end - - # bad - valid, but awkward - def to_s - "#@first_name #@last_name" - end - - # good - def to_s - "#{@first_name} #{@last_name}" - end - end - - $global = 0 - # bad - puts "$global = #$global" - - # good - puts "$global = #{$global}" - ``` - - * - Don't use `Object#to_s` on interpolated objects. It's invoked on them - automatically. - [[link](#no-to-s)] - - ```ruby - # bad - message = "This is the #{result.to_s}." - - # good - message = "This is the #{result}." - ``` - - * - Avoid using `String#+` when you need to construct large data chunks. - Instead, use `String#<<`. Concatenation mutates the string instance in-place - and is always faster than `String#+`, which creates a bunch of new string - objects. - [[link](#concat-strings)] - - ```ruby - # bad - html = '' - html += '

Page title

' - - paragraphs.each do |paragraph| - html += "

#{paragraph}

" - end - - # good and also fast - html = '' - html << '

Page title

' - - paragraphs.each do |paragraph| - html << "

#{paragraph}

" - end - ``` - - * - Don't use `String#gsub` in scenarios in which you can use a faster and more specialized alternative. - [[link](#dont-abuse-gsub)] - - ```ruby - url = 'http://example.com' - str = 'lisp-case-rules' - - # bad - url.gsub('http://', 'https://') - str.gsub('-', '_') - - # good - url.sub('http://', 'https://') - str.tr('-', '_') - ``` - - * - When using heredocs for multi-line strings keep in mind the fact that they - preserve leading whitespace. It's a good practice to employ some margin based - on which to trim the excessive whitespace. - [[link](#heredocs)] - - ```ruby - code = <<-END.gsub(/^\s+\|/, '') - |def test - | some_method - | other_method - |end - END - # => "def test\n some_method\n other_method\nend\n" - ``` - - * - Use Ruby 2.3's squiggly heredocs for nicely indented multi-line strings. - [[link](#squiggly-heredocs)] - - ```ruby - # bad - using Powerpack String#strip_margin - code = <<-RUBY.strip_margin('|') - |def test - | some_method - | other_method - |end - RUBY - - # also bad - code = <<-RUBY - def test - some_method - other_method - end - RUBY - - # good - code = <<~RUBY - def test - some_method - other_method - end - RUBY - ``` - - * - Use descriptive delimiters for heredocs. Delimiters add valuable information - about the heredoc content, and as an added bonus some editors can highlight - code within heredocs if the correct delimiter is used. - [[link](#heredoc-delimiters)] - - ```ruby - # bad - code = <<~END - def foo - bar - end - END - - # good - code = <<~RUBY - def foo - bar - end - RUBY - - # good - code = <<~SUMMARY - An imposing black structure provides a connection between the past and - the future in this enigmatic adaptation of a short story by revered - sci-fi author Arthur C. Clarke. - SUMMARY - ``` - -## Date & Time - - * - Prefer `Time.now` over `Time.new` when retrieving the current system time. - [[link](#time-now)] - - * - Don't use `DateTime` unless you need to account for historical calendar - reform—and if you do, explicitly specify the `start` argument to - clearly state your intentions. - [[link](#no-datetime)] - - ```ruby - # bad - uses DateTime for current time - DateTime.now - - # good - uses Time for current time - Time.now - - # bad - uses DateTime for modern date - DateTime.iso8601('2016-06-29') - - # good - uses Date for modern date - Date.iso8601('2016-06-29') - - # good - uses DateTime with start argument for historical date - DateTime.iso8601('1751-04-23', Date::ENGLAND) - ``` - -## Regular Expressions - -> Some people, when confronted with a problem, think -> "I know, I'll use regular expressions." Now they have two problems.
-> -- Jamie Zawinski - - * - Don't use regular expressions if you just need plain text search in string: - `string['text']` - [[link](#no-regexp-for-plaintext)] - - * - For simple constructions you can use regexp directly through string index. - [[link](#regexp-string-index)] - - ```ruby - match = string[/regexp/] # get content of matched regexp - first_group = string[/text(grp)/, 1] # get content of captured group - string[/text (grp)/, 1] = 'replace' # string => 'text replace' - ``` - - * - Use non-capturing groups when you don't use the captured result. - [[link](#non-capturing-regexp)] - - ```ruby - # bad - /(first|second)/ - - # good - /(?:first|second)/ - ``` - - * - Don't use the cryptic Perl-legacy variables denoting last regexp group - matches (`$1`, `$2`, etc). Use `Regexp.last_match(n)` instead. - [[link](#no-perl-regexp-last-matchers)] - - ```ruby - /(regexp)/ =~ string - ... - - # bad - process $1 - - # good - process Regexp.last_match(1) - ``` - - * - Avoid using numbered groups as it can be hard to track what they contain. - Named groups can be used instead. - [[link](#no-numbered-regexes)] - - ```ruby - # bad - /(regexp)/ =~ string - # some code - process Regexp.last_match(1) - - # good - /(?regexp)/ =~ string - # some code - process meaningful_var - ``` - - * - Character classes have only a few special characters you should care about: - `^`, `-`, `\`, `]`, so don't escape `.` or brackets in `[]`. - [[link](#limit-escapes)] - - * - Be careful with `^` and `$` as they match start/end of line, not string - endings. If you want to match the whole string use: `\A` and `\z` (not to be - confused with `\Z` which is the equivalent of `/\n?\z/`). - [[link](#caret-and-dollar-regexp)] - - ```ruby - string = "some injection\nusername" - string[/^username$/] # matches - string[/\Ausername\z/] # doesn't match - ``` - - * - Use `x` modifier for complex regexps. This makes them more readable and you - can add some useful comments. Just be careful as spaces are ignored. - [[link](#comment-regexes)] - - ```ruby - regexp = / - start # some text - \s # white space char - (group) # first group - (?:alt1|alt2) # some alternation - end - /x - ``` - - * - For complex replacements `sub`/`gsub` can be used with a block or a hash. - [[link](#gsub-blocks)] - - ```ruby - words = 'foo bar' - words.sub(/f/, 'f' => 'F') # => 'Foo bar' - words.gsub(/\w+/) { |word| word.capitalize } # => 'Foo Bar' - ``` - -## Percent Literals - - * - Use `%()`(it's a shorthand for `%Q`) for single-line strings which require - both interpolation and embedded double-quotes. For multi-line strings, prefer - heredocs. - [[link](#percent-q-shorthand)] - - ```ruby - # bad (no interpolation needed) - %(
Some text
) - # should be '
Some text
' - - # bad (no double-quotes) - %(This is #{quality} style) - # should be "This is #{quality} style" - - # bad (multiple lines) - %(
\n#{exclamation}\n
) - # should be a heredoc. - - # good (requires interpolation, has quotes, single line) - %(#{name}) - ``` - - * - Avoid `%()` or the equivalent `%q()` unless you have a string with both `'` and - `"` in it. Regular string literals are more readable and should be preferred - unless a lot of characters would have to be escaped in them. - [[link](#percent-q)] - - ```ruby - # bad - name = %q(Bruce Wayne) - time = %q(8 o'clock) - question = %q("What did you say?") - - # good - name = 'Bruce Wayne' - time = "8 o'clock" - question = '"What did you say?"' - quote = %q(

"What did you say?"

) - ``` - - * - Use `%r` only for regular expressions matching *at least* one `'/'` - character. - [[link](#percent-r)] - - ```ruby - # bad - %r{\s+} - - # good - %r{^/(.*)$} - %r{^/blog/2011/(.*)$} - ``` - - * - Avoid the use of `%x` unless you're going to invoke a command with - backquotes in it (which is rather unlikely). - [[link](#percent-x)] - - ```ruby - # bad - date = %x(date) - - # good - date = `date` - echo = %x(echo `date`) - ``` - - * - Avoid the use of `%s`. It seems that the community has decided `:"some - string"` is the preferred way to create a symbol with spaces in it. - [[link](#percent-s)] - - * - Use the braces that are the most appropriate for the various kinds of percent - literals. - [[link](#percent-literal-braces)] - - `()` for string literals(`%q`, `%Q`). - - `[]` for array literals(`%w`, `%i`, `%W`, `%I`) as it is aligned with - the standard array literals. - - `{}` for regexp literals(`%r`) since parentheses often appear inside regular - expressions. That's why a less common character with `{` is usually the best - delimiter for `%r` literals. - - `()` for all other literals (e.g. `%s`, `%x`) - - ```ruby - # bad - %q{"Test's king!", John said.} - - # good - %q("Test's king!", John said.) - - # bad - %w(one two three) - %i(one two three) - - # good - %w[one two three] - %i[one two three] - - # bad - %r((\w+)-(\d+)) - %r{\w{1,2}\d{2,5}} - - # good - %r{(\w+)-(\d+)} - %r|\w{1,2}\d{2,5}| - ``` - -## Metaprogramming - - * - Avoid needless metaprogramming. - [[link](#no-needless-metaprogramming)] - - * - Do not mess around in core classes when writing libraries. (Do not - monkey-patch them.) - [[link](#no-monkey-patching)] - - * - The block form of `class_eval` is preferable to the string-interpolated - form. - [[link](#block-class-eval)] - - - when you use the string-interpolated form, always supply `__FILE__` - and `__LINE__`, so that your backtraces make sense: - - ```ruby - class_eval 'def use_relative_model_naming?; true; end', __FILE__, __LINE__ - ``` - - - `define_method` is preferable to `class_eval{ def ... }` - - * - When using `class_eval` (or other `eval`) with string interpolation, add a - comment block showing its appearance if interpolated (a practice used in Rails - code): - [[link](#eval-comment-docs)] - - ```ruby - # from activesupport/lib/active_support/core_ext/string/output_safety.rb - UNSAFE_STRING_METHODS.each do |unsafe_method| - if 'String'.respond_to?(unsafe_method) - class_eval <<-EOT, __FILE__, __LINE__ + 1 - def #{unsafe_method}(*params, &block) # def capitalize(*params, &block) - to_str.#{unsafe_method}(*params, &block) # to_str.capitalize(*params, &block) - end # end - - def #{unsafe_method}!(*params) # def capitalize!(*params) - @dirty = true # @dirty = true - super # super - end # end - EOT - end - end - ``` - - * - Avoid using `method_missing` for metaprogramming because backtraces become - messy, the behavior is not listed in `#methods`, and misspelled method calls - might silently work, e.g. `nukes.launch_state = false`. Consider using - delegation, proxy, or `define_method` instead. If you must use - `method_missing`: - [[link](#no-method-missing)] - - - Be sure to [also define `respond_to_missing?`](http://blog.marc-andre.ca/2010/11/methodmissing-politely.html) - - Only catch methods with a well-defined prefix, such as `find_by_*`—make your code as assertive as possible. - - Call `super` at the end of your statement - - Delegate to assertive, non-magical methods: - - ```ruby - # bad - def method_missing(meth, *params, &block) - if /^find_by_(?.*)/ =~ meth - # ... lots of code to do a find_by - else - super - end - end - - # good - def method_missing(meth, *params, &block) - if /^find_by_(?.*)/ =~ meth - find_by(prop, *params, &block) - else - super - end - end - - # best of all, though, would to define_method as each findable attribute is declared - ``` - - * - Prefer `public_send` over `send` so as not to circumvent `private`/`protected` visibility. - [[link](#prefer-public-send)] - - ```ruby - # We have an ActiveModel Organization that includes concern Activatable - module Activatable - extend ActiveSupport::Concern - - included do - before_create :create_token - end - - private - - def reset_token - # some code - end - - def create_token - # some code - end - - def activate! - # some code - end - end - - class Organization < ActiveRecord::Base - include Activatable - end - - linux_organization = Organization.find(...) - # BAD - violates privacy - linux_organization.send(:reset_token) - # GOOD - should throw an exception - linux_organization.public_send(:reset_token) - ``` - - * - Prefer `__send__` over `send`, as `send` may overlap with existing methods. - [[link](#prefer-__send__)] - - ```ruby - require 'socket' - - u1 = UDPSocket.new - u1.bind('127.0.0.1', 4913) - u2 = UDPSocket.new - u2.connect('127.0.0.1', 4913) - # Won't send a message to the receiver obj. - # Instead it will send a message via UDP socket. - u2.send :sleep, 0 - # Will actually send a message to the receiver obj. - u2.__send__ ... - ``` - -## Misc - - * - Write `ruby -w` safe code. - [[link](#always-warn)] - - * - Avoid hashes as optional parameters. Does the method do too much? (Object - initializers are exceptions for this rule). - [[link](#no-optional-hash-params)] - - * - 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. - [[link](#short-methods)] - - * - Avoid parameter lists longer than three or four parameters. - [[link](#too-many-params)] - - * - If you really need "global" methods, add them to Kernel and make them - private. - [[link](#private-global-methods)] - - * - Use module instance variables instead of global variables. - [[link](#instance-vars)] - - ```ruby - # bad - $foo_bar = 1 - - # good - module Foo - class << self - attr_accessor :bar - end - end - - Foo.bar = 1 - ``` - - * - Use `OptionParser` for parsing complex command line options and `ruby -s` - for trivial command line options. - [[link](#optionparser)] - - * - Code in a functional way, avoiding mutation when that makes sense. - [[link](#functional-code)] - - ```ruby - a = []; [1, 2, 3].each { |i| a << i * 2 } # bad - a = [1, 2, 3].map { |i| i * 2 } # good - - a = {}; [1, 2, 3].each { |i| a[i] = i * 17 } # bad - a = [1, 2, 3].reduce({}) { |h, i| h[i] = i * 17; h } # good - a = [1, 2, 3].each_with_object({}) { |i, h| h[i] = i * 17 } # good - ``` - - * - Do not mutate parameters unless that is the purpose of the method. - [[link](#no-param-mutations)] - - * - Avoid more than three levels of block nesting. - [[link](#three-is-the-number-thou-shalt-count)] - - * - Be consistent. In an ideal world, be consistent with these guidelines. - [[link](#be-consistent)] - - * - Use common sense. - [[link](#common-sense)] - -## Tools - -Here are some tools to help you automatically check Ruby code against -this guide. - -### RuboCop - -[RuboCop][] is a Ruby static code analyzer and formatter, based on this -style guide. RuboCop already covers a significant portion of the -Guide and plugins for most popular Ruby editors and IDEs. - -### RubyMine - -[RubyMine](http://www.jetbrains.com/ruby/)'s code inspections are -[partially based](http://confluence.jetbrains.com/display/RUBYDEV/RubyMine+Inspections) -on this guide. - -# Contributing - -The guide is still a work in progress—some rules are lacking examples, -some rules don't have examples that illustrate them clearly enough. Improving -such rules is a great (and simple way) to help the Ruby community! - -In due time these issues will (hopefully) be addressed—just keep them in -mind for now. - -Nothing written in this guide is set in stone. It's my desire to work -together with everyone interested in Ruby coding style, so that we could -ultimately create a resource that will be beneficial to the entire Ruby -community. - -Feel free to open tickets or send pull requests with improvements. Thanks in -advance for your help! - -You can also support the project (and RuboCop) with financial -contributions via [Patreon](https://www.patreon.com/bbatsov). - -## How to Contribute? - -It's easy, just follow the [contribution guidelines](https://github.com/rubocop-hq/ruby-style-guide/blob/master/CONTRIBUTING.md). - -# License - -![Creative Commons License](http://i.creativecommons.org/l/by/3.0/88x31.png) -This work is licensed under a [Creative Commons Attribution 3.0 Unported License](http://creativecommons.org/licenses/by/3.0/deed.en_US) - -# Spread the Word - -A community-driven style guide is of little use to a community that -doesn't know about its existence. Tweet about the guide, share it with -your friends and colleagues. Every comment, suggestion or opinion we -get makes the guide just a little bit better. And we want to have the -best possible guide, don't we? - -Cheers,
-[Bozhidar](https://twitter.com/bbatsov) - -[PEP-8]: https://www.python.org/dev/peps/pep-0008/ -[rails-style-guide]: https://github.com/rubocop-hq/rails-style-guide -[pickaxe]: https://pragprog.com/book/ruby4/programming-ruby-1-9-2-0 -[trpl]: http://www.amazon.com/Ruby-Programming-Language-David-Flanagan/dp/0596516177 -[Pandoc]: http://pandoc.org/ -[RuboCop]: https://github.com/rubocop-hq/rubocop -[yard]: https://yardoc.org/ +# คำนำ + +> Role models are important.
+> -- Officer Alex J. Murphy / RoboCop + +มีสิ่งหนึ่งที่ทำให้ผมรู้สึกรำคาญตลอดเวลาในฐานะ นักพัฒนาภาษา Ruby—Python +นั้นก็คือการมีเอกสารอ้างอิงสำหรับการเขียนโปรแกรมที่ดี ([PEP-8][]) และการที่เราไม่เคยมีคู่มือที่เป็นทางการ +หรือ เอกสารการเขียนโค้ด Ruby และ แนวทางที่ดีในการเขียนโค้ด +One thing has always bothered me as a Ruby developer—Python developers +have a great programming style reference ([PEP-8][]) and we never got an +official guide, documenting Ruby coding style and best practices. And I do +believe that style matters. I also believe that a great hacker community, such +as Ruby has, should be quite capable of producing this coveted document. + +This guide started its life as our internal company Ruby coding guidelines +(written by yours truly). At some point I decided that the work I was doing +might be interesting to members of the Ruby community in general and that the +world had little need for another internal company guideline. But the world +could certainly benefit from a community-driven and community-sanctioned set of +practices, idioms and style prescriptions for Ruby programming. + +Since the inception of the guide I've received a lot of feedback from members of +the exceptional Ruby community around the world. Thanks for all the suggestions +and the support! Together we can make a resource beneficial to each and every +Ruby developer out there. + +By the way, if you're into Rails you might want to check out the complementary +[Ruby on Rails Style Guide][rails-style-guide]. + +# The Ruby Style Guide + +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 rules. I've tried to add +the rationale behind the rules (if it's omitted I've assumed it's pretty +obvious). + +I didn't come up with all the rules out of nowhere—they are mostly +based on my extensive career as a professional software engineer, +feedback and suggestions from members of the Ruby community and +various highly regarded Ruby programming resources, such as +["Programming Ruby"][pickaxe] and +["The Ruby Programming Language"][trpl]. + +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. + +This style guide evolves over time as additional conventions are +identified and past conventions are rendered obsolete by changes in +Ruby itself. + +You can generate a PDF or an HTML copy of this guide using +[Pandoc][]. + +[RuboCop][] is a static code analyzer (linter) and formatter, based on this +style guide. + +Translations of the guide are available in the following languages: + + * [Chinese Simplified](https://github.com/JuanitoFatas/ruby-style-guide/blob/master/README-zhCN.md) + * [Chinese Traditional](https://github.com/JuanitoFatas/ruby-style-guide/blob/master/README-zhTW.md) + * [Egyptian Arabic](https://github.com/HassanTC/ruby-style-guide/blob/master/README-EgAr.md) + * [French](https://github.com/gauthier-delacroix/ruby-style-guide/blob/master/README-frFR.md) + * [Japanese](https://github.com/fortissimo1997/ruby-style-guide/blob/japanese/README.ja.md) + * [Korean](https://github.com/dalzony/ruby-style-guide/blob/master/README-koKR.md) + * [Portuguese (pt-BR)](https://github.com/rubensmabueno/ruby-style-guide/blob/master/README-PT-BR.md) + * [Russian](https://github.com/arbox/ruby-style-guide/blob/master/README-ruRU.md) + * [Spanish](https://github.com/alemohamad/ruby-style-guide/blob/master/README-esLA.md) + * [Vietnamese](https://github.com/CQBinh/ruby-style-guide/blob/master/README-viVN.md) + +## Table of Contents + + * [Source Code Layout](#source-code-layout) + * [Syntax](#syntax) + * [Naming](#naming) + * [Comments](#comments) + * [Comment Annotations](#comment-annotations) + * [Magic Comments](#magic-comments) + * [Classes & Modules](#classes--modules) + * [Exceptions](#exceptions) + * [Collections](#collections) + * [Numbers](#numbers) + * [Strings](#strings) + * [Date & Time](#date--time) + * [Regular Expressions](#regular-expressions) + * [Percent Literals](#percent-literals) + * [Metaprogramming](#metaprogramming) + * [Misc](#misc) + * [Tools](#tools) + +## Source Code Layout + +> 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...
+> -- Jerry Coffin (on indentation) + + * + Use `UTF-8` as the source file encoding. + [[link](#utf-8)] + + * + Use two **spaces** per indentation level (aka soft tabs). No hard tabs. + [[link](#spaces-indentation)] + + ```ruby + # bad - four spaces + def some_method + do_something + end + + # good + def some_method + do_something + end + ``` + + * + Use Unix-style line endings. (\*BSD/Solaris/Linux/macOS users are covered by + default, Windows users have to be extra careful.) + [[link](#crlf)] + + * If you're using Git you might want to add the following + configuration setting to protect your project from Windows line + endings creeping in: + + ```bash + $ git config --global core.autocrlf true + ``` + + * + Don't use `;` to separate statements and expressions. As a corollary—use + one expression per line. + [[link](#no-semicolon)] + + ```ruby + # bad + puts 'foobar'; # superfluous semicolon + + puts 'foo'; puts 'bar' # two expressions on the same line + + # good + puts 'foobar' + + puts 'foo' + puts 'bar' + + puts 'foo', 'bar' # this applies to puts in particular + ``` + + * + Prefer a single-line format for class definitions with no body. + [[link](#single-line-classes)] + + ```ruby + # bad + class FooError < StandardError + end + + # okish + class FooError < StandardError; end + + # good + FooError = Class.new(StandardError) + ``` + + * + 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. + [[link](#no-single-line-methods)] + + ```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. + + ```ruby + # good + def no_op; end + ``` + + * + 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. + [[link](#spaces-operators)] + + ```ruby + sum = 1 + 2 + a, b = 1, 2 + class FooError < StandardError; end + ``` + + There are a few exceptions. One is the exponent operator: + + ```ruby + # bad + e = M * c ** 2 + + # good + e = M * c**2 + ``` + + Another exception is the slash in rational literals: + + ```ruby + # bad + o_scale = 1 / 48r + + # good + o_scale = 1/48r + ``` + + Another exception is the safe navigation operator: + ```ruby + # bad + foo &. bar + foo &.bar + foo&. bar + + # good + foo&.bar + ``` + + * + No spaces after `(`, `[` or before `]`, `)`. + Use spaces around `{` and before `}`. + [[link](#spaces-braces)] + + ```ruby + # bad + some( arg ).other + [ 1, 2, 3 ].each{|e| puts e} + + # good + some(arg).other + [1, 2, 3].each { |e| puts e } + ``` + + `{` and `}` deserve a bit of clarification, since they are used + for block and hash literals, as well as string interpolation. + + For hash literals two styles are considered acceptable. + The first variant is slightly more readable (and arguably more + popular in the Ruby community in general). The second variant has + the advantage of adding visual difference between block and hash + literals. Whichever one you pick—apply it consistently. + + ```ruby + # good - space after { and before } + { one: 1, two: 2 } + + # good - no space after { and before } + {one: 1, two: 2} + ``` + + With interpolated expressions, there should be no padded-spacing inside the braces. + + ```ruby + # bad + "From: #{ user.first_name }, #{ user.last_name }" + + # good + "From: #{user.first_name}, #{user.last_name}" + ``` + + * + No space after `!`. + [[link](#no-space-bang)] + + ```ruby + # bad + ! something + + # good + !something + ``` + + * + No space inside range literals. + [[link](#no-space-inside-range-literals)] + + ```ruby + # bad + 1 .. 3 + 'a' ... 'z' + + # good + 1..3 + 'a'...'z' + ``` + + * + Indent `when` as deep as `case`. 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). + [[link](#indent-when-to-case)] + + ```ruby + # bad + 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 + + # 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 + ``` + + * + When assigning the result of a conditional expression to a variable, + preserve the usual alignment of its branches. + [[link](#indent-conditional-assignment)] + + ```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 + ``` + + * + Use empty lines between method definitions and also to break up methods + into logical paragraphs internally. + [[link](#empty-lines-between-methods)] + + ```ruby + def some_method + data = initialize(options) + + data.manipulate! + + data.result + end + + def some_method + result + end + ``` + + * + Don't use several empty lines in a row. + [[link](#two-or-more-empty-lines)] + + ```ruby + # bad - It has two empty lines. + some_method + + + some_method + + # good + some_method + + some_method + ``` + + * + Use empty lines around access modifiers. + [[link](#empty-lines-around-access-modifier)] + + ```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 + ``` + + * + Don't use empty lines around method, class, module, block bodies. + [[link](#empty-lines-around-bodies)] + + ```ruby + # bad + class Foo + + def foo + + begin + + do_something do + + something + + end + + rescue + + something + + end + + end + + end + + # good + class Foo + def foo + begin + do_something do + something + end + rescue + something + end + end + end + ``` + + * + Avoid comma after the last parameter in a method call, especially when the + parameters are not on separate lines. + [[link](#no-trailing-params-comma)] + + ```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) + ``` + + * + Use spaces around the `=` operator when assigning default values to method + parameters: + [[link](#spaces-around-equals)] + + ```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). + + * + Avoid line continuation `\` where not required. In practice, avoid using + line continuations for anything but string concatenation. + [[link](#no-trailing-backslash)] + + ```ruby + # bad + result = 1 - \ + 2 + + # good (but still ugly as hell) + result = 1 \ + - 2 + + long_string = 'First part of the long string' \ + ' and second part of the long string' + ``` + + * + Adopt a consistent multi-line method chaining style. There are two popular + styles in the Ruby community, both of which are considered + good—leading `.` (Option A) and trailing `.` (Option B). + [[link](#consistent-multi-line-chains)] + + * **(Option A)** When continuing a chained method invocation on + another line keep the `.` on the second line. + + ```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 + ``` + + * **(Option B)** When continuing a chained method invocation on another line, + include the `.` on the first line to indicate that the + expression continues. + + ```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 + [here](https://github.com/rubocop-hq/ruby-style-guide/pull/176). + + * + Align the parameters of a method call if they span more than one + line. When aligning parameters is not appropriate due to line-length + constraints, single indent for the lines after the first is also + acceptable. + [[link](#no-double-indent)] + + ```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 + ``` + + * + Align the elements of array literals spanning multiple lines. + [[link](#align-multiline-arrays)] + + ```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] + ``` + + * + Add underscores to large numeric literals to improve their readability. + [[link](#underscores-in-numerics)] + + ```ruby + # bad - how many 0s are there? + num = 1000000 + + # good - much easier to parse for the human brain + num = 1_000_000 + ``` + + * + Prefer lowercase letters for numeric literal prefixes. + `0o` for octal, `0x` for hexadecimal and `0b` for binary. + Do not use `0d` prefix for decimal literals. + [[link](#numeric-literal-prefixes)] + + ```ruby + # bad + num = 01234 + num = 0O1234 + num = 0X12AB + num = 0B10101 + num = 0D1234 + num = 0d1234 + + # good - easier to separate digits from the prefix + num = 0o1234 + num = 0x12AB + num = 0b10101 + num = 1234 + ``` + + * + Use [YARD][yard] and its conventions for API documentation. + [[link](#api-documentation)] + + * + Limit lines to 80 characters. + [[link](#80-character-limits)] + + * + Avoid trailing whitespace. + [[link](#no-trailing-whitespace)] + + * + End each file with a newline. + [[link](#newline-eof)] + + * + Don't use block comments. They cannot be preceded by whitespace and are not + as easy to spot as regular comments. + [[link](#no-block-comments)] + + ```ruby + # bad + =begin + comment line + another comment line + =end + + # good + # comment line + # another comment line + ``` + +## Syntax + + * + Use `::` only to reference constants (this includes classes and + modules) and constructors (like `Array()` or `Nokogiri::HTML()`). + * Do not use `::` for regular method invocation. + [[link](#double-colons)] + + ```ruby + # bad + SomeClass::some_method + some_object::some_method + + # good + SomeClass.some_method + some_object.some_method + SomeModule::SomeClass::SOME_CONST + SomeModule::SomeClass() + ``` + + * + Do not use `::` to define class methods. + [[link](#colon-method-definition)] + + ```ruby + # bad + class Foo + def self::some_method + end + end + + # good + class Foo + def self.some_method + end + end + ``` + + * + Use `def` with parentheses when there are parameters. Omit the + parentheses when the method doesn't accept any parameters. + [[link](#method-parens)] + + ```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 + ``` + + * + Use parentheses around the arguments of method invocations, + especially if the first argument begins with an open parenthesis `(`, + as in `f((3 + 2) + 1)`. + [[link](#method-invocation-parens)] + + ```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) + ``` + + Always omit parentheses for + + * Method calls with no arguments: + + ```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 (e.g., Rake, Rails, RSpec): + + ```ruby + # bad + validates(:name, presence: true) + # good + validates :name, presence: true + ``` + + * Methods that have "keyword" status in Ruby: + + ```ruby + class Person + # bad + attr_reader(:name, :age) + # good + attr_reader :name, :age + + # body omitted + end + ``` + + Can omit parentheses for + + * Methods that have "keyword" status in Ruby, but are not declarative: + + ```Ruby + # good + puts(temperance.age) + system('ls') + # also good + puts temperance.age + system 'ls' + ``` + + * + 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. + [[link](#optional-arguments)] + + ```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' + ``` + + * + Use keyword arguments when passing boolean argument to a method. + [[link](#boolean-keyword-arguments)] + + ```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 + ``` + * + Prefer keyword arguments over optional arguments. + [[link](#keyword-arguments-vs-optional-arguments)] + + ```Ruby + # bad + def some_method(a, b = 5, c = 1) + # body omitted + end + + # good + def some_method(a, b: 5, c: 1) + # body omitted + end + ``` + + * + Use keyword arguments instead of option hashes. + [[link](#keyword-arguments-vs-option-hashes)] + + ```Ruby + # bad + def some_method(options = {}) + bar = options.fetch(:bar, false) + puts bar + end + + # good + def some_method(bar: false) + puts bar + end + ``` + + * + 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. + [[link](#parallel-assignment)] + + ```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] + ``` + + * + 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. + [[link]](#trailing-underscore-variables) + + ```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(',') + ``` + + * + 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. + [[link](#no-for-loops)] + + ```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's block + elem # => NameError: undefined local variable or method `elem' + ``` + + * + Do not use `then` for multi-line `if`/`unless`. + [[link](#no-then)] + + ```ruby + # bad + if some_condition then + # body omitted + end + + # good + if some_condition + # body omitted + end + ``` + + * + Always put the condition on the same line as the `if`/`unless` in a + multi-line conditional. + [[link](#same-line-condition)] + + ```ruby + # bad + if + some_condition + do_something + do_something_else + end + + # good + if some_condition + do_something + do_something_else + end + ``` + + * + Favor the ternary operator(`?:`) over `if/then/else/end` constructs. + It's more common and obviously more concise. + [[link](#ternary-operator)] + + ```ruby + # bad + result = if some_condition then something else something_else end + + # good + result = some_condition ? something : something_else + ``` + + * + 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. + [[link](#no-nested-ternary)] + + ```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 + ``` + + * + Do not use `if x; ...`. Use the ternary + operator instead. + [[link](#no-semicolon-ifs)] + + ```ruby + # bad + result = if some_condition; something else something_else end + + # good + result = some_condition ? something : something_else + ``` + + * + Leverage the fact that `if` and `case` are expressions which return a + result. + [[link](#use-if-case-returns)] + + ```ruby + # bad + if condition + result = x + else + result = y + end + + # good + result = + if condition + x + else + y + end + ``` + + * + Use `when x then ...` for one-line cases. The alternative syntax `when x: + ...` has been removed as of Ruby 1.9. + [[link](#one-line-cases)] + + * + Do not use `when x; ...`. See the previous rule. + [[link](#no-when-semicolons)] + + * + Use `!` instead of `not`. + [[link](#bang-not-not)] + + ```ruby + # bad - parentheses are required because of op precedence + x = (not something) + + # good + x = !something + ``` + + * + Avoid the use of `!!`. + [[link](#no-bang-bang)] + + `!!` 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. If you want to do a `nil` check, use `nil?` + instead. + + ```ruby + # bad + x = 'test' + # obscure nil check + if !!x + # body omitted + end + + # good + x = 'test' + if x + # body omitted + end + ``` + + * + 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. + [[link](#no-and-or-or)] + + ```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!") + ``` + + * + Avoid multi-line `?:` (the ternary operator); use `if`/`unless` instead. + [[link](#no-multiline-ternary)] + + * + Favor modifier `if`/`unless` usage when you have a single-line body. Another + good alternative is the usage of control flow `&&`/`||`. + [[link](#if-as-a-modifier)] + + ```ruby + # bad + if some_condition + do_something + end + + # good + do_something if some_condition + + # another good option + some_condition && do_something + ``` + + * + Avoid modifier `if`/`unless` usage at the end of a non-trivial multi-line + block. + [[link](#no-multiline-if-modifiers)] + + ```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 + ``` + + * + Avoid nested modifier `if`/`unless`/`while`/`until` usage. Favor `&&`/`||` if + appropriate. + [[link](#no-nested-modifiers)] + + ```ruby + # bad + do_something if other_condition if some_condition + + # good + do_something if some_condition && other_condition + ``` + + * + Favor `unless` over `if` for negative conditions (or control flow `||`). + [[link](#unless-for-negatives)] + + ```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 + ``` + + * + Do not use `unless` with `else`. Rewrite these with the positive case first. + [[link](#no-else-with-unless)] + + ```ruby + # bad + unless success? + puts 'failure' + else + puts 'success' + end + + # good + if success? + puts 'success' + else + puts 'failure' + end + ``` + + * + Don't use parentheses around the condition of a control expression. + [[link](#no-parens-around-condition)] + + ```ruby + # bad + if (x > 10) + # body omitted + end + + # good + if x > 10 + # body omitted + end + ``` + +Note that there is an exception to this rule, namely [safe assignment in +condition](#safe-assignment-in-condition). + + * + Do not use `while/until condition do` for multi-line `while/until`. + [[link](#no-multiline-while-do)] + + ```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 + ``` + + * + Favor modifier `while/until` usage when you have a single-line body. + [[link](#while-as-a-modifier)] + + ```ruby + # bad + while some_condition + do_something + end + + # good + do_something while some_condition + ``` + + * + Favor `until` over `while` for negative conditions. + [[link](#until-for-negatives)] + + ```ruby + # bad + do_something while !some_condition + + # good + do_something until some_condition + ``` + + * + Use `Kernel#loop` instead of `while/until` when you need an infinite loop. + [[link](#infinite-loop)] + + ```ruby + # bad + while true + do_something + end + + until false + do_something + end + + # good + loop do + do_something + end + ``` + + * + Use `Kernel#loop` with `break` rather than `begin/end/until` or + `begin/end/while` for post-loop tests. + [[link](#loop-with-break)] + + ```ruby + # bad + begin + puts val + val += 1 + end while val < 0 + + # good + loop do + puts val + val += 1 + break unless val < 0 + end + ``` + + * + Omit the outer braces around an implicit options hash. + [[link](#no-braces-opts-hash)] + + ```ruby + # bad + user.set({ name: 'John', age: 45, permissions: { read: true } }) + + # good + user.set(name: 'John', age: 45, permissions: { read: true }) + ``` + + * + Omit both the outer braces and parentheses for methods that are part of an + internal DSL. + [[link](#no-dsl-decorating)] + + ```ruby + class Person < ActiveRecord::Base + # bad + validates(:name, { presence: true, length: { within: 1..10 } }) + + # good + validates :name, presence: true, length: { within: 1..10 } + end + ``` + + * + Use the proc invocation shorthand when the invoked method is the only operation of a block. + [[link](#single-action-blocks)] + + ```ruby + # bad + names.map { |name| name.upcase } + + # good + names.map(&:upcase) + ``` + + * + 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. + [[link](#single-line-blocks)] + + ```ruby + names = %w[Bozhidar Steve 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? + + * + Consider using explicit block argument to avoid writing block literal that + just passes its arguments to another block. Beware of the performance impact, + though, as the block gets converted to a Proc. + [[link](#block-argument)] + + ```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 + ``` + + * + Avoid `return` where not required for flow of control. + [[link](#no-explicit-return)] + + ```ruby + # bad + def some_method(some_arr) + return some_arr.size + end + + # good + def some_method(some_arr) + some_arr.size + end + ``` + + * + Avoid `self` where not required. (It is only required when calling a self + write accessor, methods named after reserved words, or overloadable operators.) + [[link](#no-self-unless-required)] + + ```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 + ``` + + * + As a corollary, avoid shadowing methods with local variables unless they are + both equivalent. + [[link](#no-shadowing)] + + ```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 + ``` + + * + 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*. + [[link](#safe-assignment-in-condition)] + + ```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 + ``` + + * + Use shorthand self assignment operators whenever applicable. + [[link](#self-assignment)] + + ```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 + ``` + + * + Use `||=` to initialize variables only if they're not already initialized. + [[link](#double-pipe-for-uninit)] + + ```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' + ``` + + * + Don't use `||=` to initialize boolean variables. (Consider what would happen + if the current value happened to be `false`.) + [[link](#no-double-pipes-for-bools)] + + ```ruby + # bad - would set enabled to true even if it was false + enabled ||= true + + # good + enabled = true if enabled.nil? + ``` + + * + 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`. + [[link](#double-amper-preprocess)] + + ```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 + ``` + + * + 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. + [[link](#no-case-equality)] + + ```ruby + # bad + Array === something + (1..100) === 7 + /something/ === some_string + + # good + something.is_a?(Array) + (1..100).include?(7) + some_string.match?(/something/) + ``` + + * + Do not use `eql?` when using `==` will do. The stricter comparison semantics + provided by `eql?` are rarely needed in practice. + [[link](#eql)] + + ```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 + ``` + + * + Avoid using Perl-style special variables (like `$:`, `$;`, etc. ). They are + quite cryptic and their use in anything but one-liner scripts is discouraged. + Use the human-friendly aliases provided by the `English` library. + [[link](#no-cryptic-perlisms)] + + ```ruby + # bad + $:.unshift File.dirname(__FILE__) + + # good + require 'English' + $LOAD_PATH.unshift File.dirname(__FILE__) + ``` + + * + Do not put a space between a method name and the opening parenthesis. + [[link](#parens-no-spaces)] + + ```ruby + # bad + f (3 + 2) + 1 + + # good + f(3 + 2) + 1 + ``` + + * + Always run the Ruby interpreter with the `-w` option so it will warn you if + you forget either of the rules above! + [[link](#always-warn-at-runtime)] + + * + 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 invoked. + [[link](#no-nested-methods)] + + ```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 + ``` + + * + Use the new lambda literal syntax for single line body blocks. Use the + `lambda` method for multi-line blocks. + [[link](#lambda-multi-line)] + + ```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 + ``` + + * + Don't omit the parameter parentheses when defining a stabby lambda with + parameters. + [[link](#stabby-lambda-with-args)] + + ```ruby + # bad + l = ->x, y { something(x, y) } + + # good + l = ->(x, y) { something(x, y) } + ``` + + * + Omit the parameter parentheses when defining a stabby lambda with + no parameters. + [[link](#stabby-lambda-no-args)] + + ```ruby + # bad + l = ->() { something } + + # good + l = -> { something } + ``` + + * + Prefer `proc` over `Proc.new`. + [[link](#proc)] + + ```ruby + # bad + p = Proc.new { |n| puts n } + + # good + p = proc { |n| puts n } + ``` + + * + Prefer `proc.call()` over `proc[]` or `proc.()` for both lambdas and procs. + [[link](#proc-call)] + + ```ruby + # bad - looks similar to Enumeration access + l = ->(v) { puts v } + l[1] + + # also bad - uncommon syntax + l = ->(v) { puts v } + l.(1) + + # good + l = ->(v) { puts v } + l.call(1) + ``` + + * + 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 and + will suppress their unused variable warnings. + [[link](#underscore-unused-vars)] + + ```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 + ``` + + * + Use `$stdout/$stderr/$stdin` instead of `STDOUT/STDERR/STDIN`. + `STDOUT/STDERR/STDIN` are constants, and while you can actually reassign + (possibly to redirect some stream) constants in Ruby, you'll get an + interpreter warning if you do so. + [[link](#global-stdout)] + + * + Use `warn` instead of `$stderr.puts`. Apart from being more concise and + clear, `warn` allows you to suppress warnings if you need to (by setting the + warn level to 0 via `-W0`). + [[link](#warn)] + + * + Favor the use of `sprintf` and its alias `format` over the fairly cryptic + `String#%` method. + [[link](#sprintf)] + + ```ruby + # bad + '%d %d' % [20, 10] + # => '20 10' + + # good + sprintf('%d %d', 20, 10) + # => '20 10' + + # good + sprintf('%d %d', first: 20, second: 10) + # => '20 10' + + format('%d %d', 20, 10) + # => '20 10' + + # good + format('%d %d', first: 20, second: 10) + # => '20 10' + ``` + + * + When using named format string tokens, favor `%s` over `%{name}` because it encodes information about the type of the value. + [[link]](#named-format-tokens) + + ```ruby + # bad + format('Hello, %{name}', name: 'John') + + # good + format('Hello, %s', name: 'John') + ``` + + * + Favor the use of `Array#join` over the fairly cryptic `Array#*` with + a string argument. + [[link](#array-join)] + + ```ruby + # bad + %w[one two three] * ', ' + # => 'one, two, three' + + # good + %w[one two three].join(', ') + # => 'one, two, three' + ``` + + * + Use `Array()` instead of explicit `Array` check or `[*var]`, when dealing + with a variable you want to treat as an Array, but you're not certain it's an + array. + [[link](#array-coercion)] + + ```ruby + # bad + paths = [paths] unless paths.is_a? Array + paths.each { |path| do_something(path) } + + # bad (always creates a new Array instance) + [*paths].each { |path| do_something(path) } + + # good (and a bit more readable) + Array(paths).each { |path| do_something(path) } + ``` + + * + Use ranges or `Comparable#between?` instead of complex comparison logic when + possible. + [[link](#ranges-or-between)] + + ```ruby + # bad + do_something if x >= 1000 && x <= 2000 + + # good + do_something if (1000..2000).include?(x) + + # good + do_something if x.between?(1000, 2000) + ``` + + * + Favor the use of predicate methods to explicit comparisons with `==`. + Numeric comparisons are OK. + [[link](#predicate-methods)] + + ```ruby + # bad + if x % 2 == 0 + end + + if x % 2 == 1 + end + + if x == nil + end + + # good + if x.even? + end + + if x.odd? + end + + if x.nil? + end + + if x.zero? + end + + if x == 0 + end + ``` + + * + Don't do explicit non-`nil` checks unless you're dealing with boolean + values. + [[link](#no-non-nil-checks)] + + ```ruby + # bad + do_something if !something.nil? + do_something if something != nil + + # good + do_something if something + + # good - dealing with a boolean + def value_set? + !@some_boolean.nil? + end + ``` + + * + Avoid the use of `BEGIN` blocks. + [[link](#no-BEGIN-blocks)] + + * + Do not use `END` blocks. Use `Kernel#at_exit` instead. + [[link](#no-END-blocks)] + + ```ruby + # bad + END { puts 'Goodbye!' } + + # good + at_exit { puts 'Goodbye!' } + ``` + + * + Avoid the use of flip-flops. + [[link](#no-flip-flops)] + + * + Avoid use of nested conditionals for flow of control. + [[link](#no-nested-conditionals)] + + 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. + + ```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. + + ```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 + ``` + + * + Prefer `map` over `collect`, `find` over `detect`, `select` over `find_all`, + `reduce` over `inject` and `size` over `length`. This is not a hard + requirement; if the use of the alias enhances readability, it's ok to use it. + The rhyming methods are inherited from Smalltalk and are not common in other + programming languages. The reason the use of `select` is encouraged over + `find_all` is that it goes together nicely with `reject` and its name is + pretty self-explanatory. + [[link](#map-find-select-reduce-size)] + + * + Don't use `count` as a substitute for `size`. For `Enumerable` objects other + than `Array` it will iterate the entire collection in order to determine its + size. + [[link](#count-vs-size)] + + ```ruby + # bad + some_hash.count + + # good + some_hash.size + ``` + + * + Use `flat_map` instead of `map` + `flatten`. This does not apply for arrays + with a depth greater than 2, i.e. if `users.first.songs == ['a', ['b','c']]`, + then use `map + flatten` rather than `flat_map`. `flat_map` flattens the + array by 1, whereas `flatten` flattens it all the way. + [[link](#flat-map)] + + ```ruby + # bad + all_songs = users.map(&:songs).flatten.uniq + + # good + all_songs = users.flat_map(&:songs).uniq + ``` + + * + Prefer `reverse_each` to `reverse.each` because some classes that `include + Enumerable` will provide an efficient implementation. Even in the worst case + where a class does not provide a specialized implementation, the general + implementation inherited from `Enumerable` will be at least as efficient as + using `reverse.each`. + [[link](#reverse-each)] + + ```ruby + # bad + array.reverse.each { ... } + + # good + array.reverse_each { ... } + ``` + +## Naming + +> The only real difficulties in programming are cache invalidation and +> naming things.
+> -- Phil Karlton + + * + Name identifiers in English. + [[link](#english-identifiers)] + + ```ruby + # bad - identifier using non-ascii characters + заплата = 1_000 + + # bad - identifier is a Bulgarian word, written with Latin letters (instead of Cyrillic) + zaplata = 1_000 + + # good + salary = 1_000 + ``` + + * + Use `snake_case` for symbols, methods and variables. + [[link](#snake-case-symbols-methods-vars)] + + ```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 + ``` + + * + Do not separate numbers from letters on symbols, methods and variables. + [[link](#snake-case-symbols-methods-vars-with-numbers)] + + ```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 + ``` + + + * + Use `CamelCase` for classes and modules. (Keep acronyms like HTTP, RFC, XML + uppercase.) + [[link](#camelcase-classes)] + + ```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 + ``` + + * + Use `snake_case` for naming files, e.g. `hello_world.rb`. + [[link](#snake-case-files)] + + * + Use `snake_case` for naming directories, e.g. + `lib/hello_world/hello_world.rb`. + [[link](#snake-case-dirs)] + + * + Aim to have just a single class/module per source file. Name the file name + as the class/module, but replacing `CamelCase` with `snake_case`. + [[link](#one-class-per-file)] + + * + Use `SCREAMING_SNAKE_CASE` for other constants. + [[link](#screaming-snake-case)] + + ```ruby + # bad + SomeConst = 5 + + # good + SOME_CONST = 5 + ``` + + * + 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. + [[link](#bool-methods-qmark)] + + * + 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?`. + [[link](#bool-methods-prefix)] + + ```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 + ``` + + * + 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. + [[link](#dangerous-method-bang)] + + ```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 + ``` + + * + Define the non-bang (safe) method in terms of the bang (dangerous) one if + possible. + [[link](#safe-because-unsafe)] + + ```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 + ``` + + * + 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 same or coercible + types. + [[link](#other-arg)] + + * 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)`. + + ```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 + ``` + +## Comments + +> Good code is its own best documentation. As you're about to add a +> comment, ask yourself, "How can I improve the code so that this +> comment isn't needed?" Improve the code and then document it to make +> it even clearer.
+> -- Steve McConnell + + * + Write self-documenting code and ignore the rest of this section. Seriously! + [[link](#no-comments)] + + * + Write comments in English. + [[link](#english-comments)] + + * + Use one space between the leading `#` character of the comment and the text + of the comment. + [[link](#hash-space)] + + * + Comments longer than a word are capitalized and use punctuation. Use [one + space](https://en.wikipedia.org/wiki/Sentence_spacing) after periods. + [[link](#english-syntax)] + + * + Avoid superfluous comments. + [[link](#no-superfluous-comments)] + + ```ruby + # bad + counter += 1 # Increments counter by one. + ``` + + * + Keep existing comments up-to-date. An outdated comment is worse than no + comment at all. + [[link](#comment-upkeep)] + +> Good code is like a good joke: it needs no explanation.
+> — old programmers maxim, through [Russ Olsen](http://eloquentruby.com/blog/2011/03/07/good-code-and-good-jokes/) + + * + Avoid writing comments to explain bad code. Refactor the code to make it + self-explanatory. ("Do or do not—there is no try." Yoda) + [[link](#refactor-dont-comment)] + +### Comment Annotations + + * + Annotations should usually be written on the line immediately above the + relevant code. + [[link](#annotate-above)] + + * + The annotation keyword is followed by a colon and a space, then a note + describing the problem. + [[link](#annotate-keywords)] + + * + If multiple lines are required to describe the problem, subsequent lines + should be indented three spaces after the `#` (one general plus two for + indentation purpose). + [[link](#indent-annotations)] + + ```ruby + def bar + # FIXME: This has crashed occasionally since v3.2.1. It may + # be related to the BarBazUtil upgrade. + baz(:quux) + end + ``` + + * + In cases where the problem is so obvious that any documentation would be + redundant, annotations may be left at the end of the offending line with no + note. This usage should be the exception and not the rule. + [[link](#rare-eol-annotations)] + + ```ruby + def bar + sleep 100 # OPTIMIZE + end + ``` + + * + Use `TODO` to note missing features or functionality that should be added at + a later date. + [[link](#todo)] + + * + Use `FIXME` to note broken code that needs to be fixed. + [[link](#fixme)] + + * + Use `OPTIMIZE` to note slow or inefficient code that may cause performance + problems. + [[link](#optimize)] + + * + Use `HACK` to note code smells where questionable coding practices were used + and should be refactored away. + [[link](#hack)] + + * + Use `REVIEW` to note anything that should be looked at to confirm it is + working as intended. For example: `REVIEW: Are we sure this is how the client + does X currently?` + [[link](#review)] + + * + Use other custom annotation keywords if it feels appropriate, but be sure to + document them in your project's `README` or similar. + [[link](#document-annotations)] + +### Magic Comments + + * + Place magic comments above all code and documentation in a file (except shebangs, which are discussed next). + + ```ruby + # bad + # Some documentation about Person + + # frozen_string_literal: true + class Person + end + + # good + # frozen_string_literal: true + + # Some documentation about Person + class Person + end + ``` + + * + Place magic comments below shebangs when they are present in a file. + [[link](#below-shebang)] + + ```ruby + # bad + # frozen_string_literal: true + #!/usr/bin/env ruby + + App.parse(ARGV) + + # good + #!/usr/bin/env ruby + # frozen_string_literal: true + + App.parse(ARGV) + ``` + + * + Use one magic comment per line if you need multiple. + [[link](#one-magic-comment-per-line)] + + ```ruby + # bad + # -*- frozen_string_literal: true; encoding: ascii-8bit -*- + + # good + # frozen_string_literal: true + # encoding: ascii-8bit + ``` + + * + Separate magic comments from code and documentation with a blank line. + [[link](#separate-magic-comments-from-code)] + + ```ruby + # bad + # frozen_string_literal: true + # Some documentation for Person + class Person + # Some code + end + + # good + # frozen_string_literal: true + + # Some documentation for Person + class Person + # Some code + end + ``` + +## Classes & Modules + + * + Use a consistent structure in your class definitions. + [[link](#consistent-classes)] + + ```ruby + class Person + # extend and include go first + extend SomeModule + include AnotherModule + + # inner classes + CustomError = Class.new(StandardError) + + # constants are next + SOME_CONSTANT = 20 + + # afterwards we have attribute macros + attr_reader :name + + # followed by other macros (if any) + validates :name + + # public class methods are next in line + def self.some_method + end + + # initialization goes between class methods and other instance methods + def initialize + end + + # followed by other public instance methods + def some_method + end + + # protected and private methods are grouped near the end + protected + + def some_protected_method + end + + private + + def some_private_method + end + end + ``` + + * + Split multiple mixins into separate statements. + [[link](#mixin-grouping)] + + ```ruby + # bad + class Person + include Foo, Bar + end + + # good + class Person + # multiple mixins go in separate statements + include Foo + include Bar + end + ``` + + * + Don't nest multi-line classes within classes. Try to have such nested + classes each in their own file in a folder named like the containing class. + [[link](#file-classes)] + + ```ruby + # bad + + # foo.rb + class Foo + class Bar + # 30 methods inside + end + + class Car + # 20 methods inside + end + + # 30 methods inside + end + + # good + + # foo.rb + class Foo + # 30 methods inside + end + + # foo/bar.rb + class Foo + class Bar + # 30 methods inside + end + end + + # foo/car.rb + class Foo + class Car + # 20 methods inside + end + end + ``` + + * + Define (and reopen) namespaced classes and modules using explicit nesting. + Using the scope resolution operator can lead to surprising constant lookups + due to Ruby's [lexical scoping](https://cirw.in/blog/constant-lookup.html), + which depends on the module nesting at the point of definition. + [[link](#namespace-definition)] + + ```Ruby + module Utilities + class Queue + end + end + + # bad + class Utilities::Store + Module.nesting # => [Utilities::Store] + + def initialize + # Refers to the top level ::Queue class because Utilities isn't in the + # current nesting chain. + @queue = Queue.new + end + end + + # good + module Utilities + class WaitingList + Module.nesting # => [Utilities::WaitingList, Utilities] + + def initialize + @queue = Queue.new # Refers to Utilities::Queue + end + end + end + ``` + + * + Prefer modules to classes with only class methods. Classes should be used + only when it makes sense to create instances out of them. + [[link](#modules-vs-classes)] + + ```ruby + # bad + class SomeClass + def self.some_method + # body omitted + end + + def self.some_other_method + # body omitted + end + end + + # good + module SomeModule + module_function + + def some_method + # body omitted + end + + def some_other_method + # body omitted + end + end + ``` + + * + Favor the use of `module_function` over `extend self` when you want to turn + a module's instance methods into class methods. + [[link](#module-function)] + + ```ruby + # bad + module Utilities + extend self + + def parse_something(string) + # do stuff here + end + + def other_utility_method(number, string) + # do some more stuff + end + end + + # good + module Utilities + module_function + + def parse_something(string) + # do stuff here + end + + def other_utility_method(number, string) + # do some more stuff + end + end + ``` + + * + When designing class hierarchies make sure that they conform to the [Liskov + Substitution + Principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle). + [[link](#liskov)] + + * + Try to make your classes as + [SOLID](https://en.wikipedia.org/wiki/SOLID_\(object-oriented_design\)) as + possible. + [[link](#solid-design)] + + * + Always supply a proper `to_s` method for classes that represent domain + objects. + [[link](#define-to-s)] + + ```ruby + class Person + attr_reader :first_name, :last_name + + def initialize(first_name, last_name) + @first_name = first_name + @last_name = last_name + end + + def to_s + "#{first_name} #{last_name}" + end + end + ``` + + * + Use the `attr` family of functions to define trivial accessors or mutators. + [[link](#attr_family)] + + ```ruby + # bad + class Person + def initialize(first_name, last_name) + @first_name = first_name + @last_name = last_name + end + + def first_name + @first_name + end + + def last_name + @last_name + end + end + + # good + class Person + attr_reader :first_name, :last_name + + def initialize(first_name, last_name) + @first_name = first_name + @last_name = last_name + end + end + ``` + + * + For accessors and mutators, avoid prefixing method names with + `get_` and `set_`. + It is a Ruby convention to use attribute names for accessors (readers) and + `attr_name=` for mutators (writers). + [[link](#accessor_mutator_method_names)] + + ```ruby + # bad + class Person + def get_name + "#{@first_name} #{@last_name}" + end + + def set_name(name) + @first_name, @last_name = name.split(' ') + end + end + + # good + class Person + def name + "#{@first_name} #{@last_name}" + end + + def name=(name) + @first_name, @last_name = name.split(' ') + end + end + ``` + + * + Avoid the use of `attr`. Use `attr_reader` and `attr_accessor` instead. + [[link](#attr)] + + ```ruby + # bad - creates a single attribute accessor (deprecated in Ruby 1.9) + attr :something, true + attr :one, :two, :three # behaves as attr_reader + + # good + attr_accessor :something + attr_reader :one, :two, :three + ``` + + * + Consider using `Struct.new`, which defines the trivial accessors, + constructor and comparison operators for you. + [[link](#struct-new)] + + ```ruby + # good + class Person + attr_accessor :first_name, :last_name + + def initialize(first_name, last_name) + @first_name = first_name + @last_name = last_name + end + end + + # better + Person = Struct.new(:first_name, :last_name) do + end + ``` + + * + Don't extend an instance initialized by `Struct.new`. Extending it introduces + a superfluous class level and may also introduce weird errors if the file is + required multiple times. + [[link](#no-extend-struct-new)] + + ```ruby + # bad + class Person < Struct.new(:first_name, :last_name) + end + + # good + Person = Struct.new(:first_name, :last_name) + ``` + + * + Consider adding factory methods to provide additional sensible ways to + create instances of a particular class. + [[link](#factory-methods)] + + ```ruby + class Person + def self.create(options_hash) + # body omitted + end + end + ``` + + * + Prefer [duck-typing](https://en.wikipedia.org/wiki/Duck_typing) over + inheritance. + [[link](#duck-typing)] + + ```ruby + # bad + class Animal + # abstract method + def speak + end + end + + # extend superclass + class Duck < Animal + def speak + puts 'Quack! Quack' + end + end + + # extend superclass + class Dog < Animal + def speak + puts 'Bau! Bau!' + end + end + + # good + class Duck + def speak + puts 'Quack! Quack' + end + end + + class Dog + def speak + puts 'Bau! Bau!' + end + end + ``` + + * + Avoid the usage of class (`@@`) variables due to their "nasty" behavior in + inheritance. + [[link](#no-class-vars)] + + ```ruby + class Parent + @@class_var = 'parent' + + def self.print_class_var + puts @@class_var + end + end + + class Child < Parent + @@class_var = 'child' + end + + Parent.print_class_var # => will print 'child' + ``` + + As you can see all the classes in a class hierarchy actually share one + class variable. Class instance variables should usually be preferred + over class variables. + + * + Assign proper visibility levels to methods (`private`, `protected`) in + accordance with their intended usage. Don't go off leaving everything `public` + (which is the default). After all we're coding in *Ruby* now, not in *Python*. + [[link](#visibility)] + + * + Indent the `public`, `protected`, and `private` methods as much as the method + definitions they apply to. Leave one blank line above the visibility modifier + and one blank line below in order to emphasize that it applies to all methods + below it. + [[link](#indent-public-private-protected)] + + ```ruby + class SomeClass + def public_method + # some code + end + + private + + def private_method + # some code + end + + def another_private_method + # some code + end + end + ``` + + * + Use `def self.method` to define class methods. This makes the code + easier to refactor since the class name is not repeated. + [[link](#def-self-class-methods)] + + ```ruby + class TestClass + # bad + def TestClass.some_method + # body omitted + end + + # good + def self.some_other_method + # body omitted + end + + # Also possible and convenient when you + # have to define many class methods. + class << self + def first_method + # body omitted + end + + def second_method_etc + # body omitted + end + end + end + ``` + + * + Prefer `alias` when aliasing methods in lexical class scope as the + resolution of `self` in this context is also lexical, and it communicates + clearly to the user that the indirection of your alias will not be altered + at runtime or by any subclass unless made explicit. + [[link](#alias-method-lexically)] + + ```ruby + class Westerner + def first_name + @names.first + end + + alias given_name first_name + end + ``` + + Since `alias`, like `def`, is a keyword, prefer bareword arguments over + symbols or strings. In other words, do `alias foo bar`, not + `alias :foo :bar`. + + Also be aware of how Ruby handles aliases and inheritance: an alias + references the method that was resolved at the time the alias was defined; + it is not dispatched dynamically. + + ```ruby + class Fugitive < Westerner + def first_name + 'Nobody' + end + end + ``` + + In this example, `Fugitive#given_name` would still call the original + `Westerner#first_name` method, not `Fugitive#first_name`. To override the + behavior of `Fugitive#given_name` as well, you'd have to redefine it in the + derived class. + + ```ruby + class Fugitive < Westerner + def first_name + 'Nobody' + end + + alias given_name first_name + end + ``` + + * + Always use `alias_method` when aliasing methods of modules, classes, or + singleton classes at runtime, as the lexical scope of `alias` leads to + unpredictability in these cases. + [[link](#alias-method)] + + ```ruby + module Mononymous + def self.included(other) + other.class_eval { alias_method :full_name, :given_name } + end + end + + class Sting < Westerner + include Mononymous + end + ``` + + * + When class (or module) methods call other such methods, omit the use of a + leading `self` or own name followed by a `.` when calling other such methods. + This is often seen in "service classes" or other similar concepts where a + class is treated as though it were a function. This convention tends to reduce + repetitive boilerplate in such classes. + [[link](#class-and-self)] + + ```ruby + class TestClass + # bad -- more work when class renamed/method moved + def self.call(param1, param2) + TestClass.new(param1).call(param2) + end + + # bad -- more verbose than necessary + def self.call(param1, param2) + self.new(param1).call(param2) + end + + # good + def self.call(param1, param2) + new(param1).call(param2) + end + + # ...other methods... + end + ``` + +## Exceptions + + * + Prefer `raise` over `fail` for exceptions. + [[link](#prefer-raise-over-fail)] + + ```ruby + # bad + fail SomeException, 'message' + + # good + raise SomeException, 'message' + ``` + + * + Don't specify `RuntimeError` explicitly in the two argument version of + `raise`. + [[link](#no-explicit-runtimeerror)] + + ```ruby + # bad + raise RuntimeError, 'message' + + # good - signals a RuntimeError by default + raise 'message' + ``` + + * + Prefer supplying an exception class and a message as two separate arguments + to `raise`, instead of an exception instance. + [[link](#exception-class-messages)] + + ```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`. + ``` + + * + 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. + [[link](#no-return-ensure)] + + ```ruby + # bad + def foo + raise + ensure + return 'very bad idea' + end + ``` + + * + Use *implicit begin blocks* where possible. + [[link](#begin-implicit)] + + ```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 + ``` + + * + Mitigate the proliferation of `begin` blocks by using *contingency methods* + (a term coined by Avdi Grimm). + [[link](#contingency-methods)] + + ```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 } + ``` + + * + Don't suppress exceptions. + [[link](#dont-hide-exceptions)] + + ```ruby + # bad + begin + # an exception occurs here + rescue SomeError + # the rescue clause does absolutely nothing + end + + # bad + do_something rescue nil + ``` + + * + Avoid using `rescue` in its modifier form. + [[link](#no-rescue-modifiers)] + + ```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 => ex + handle_error(ex) + end + ``` + + * + Don't use exceptions for flow of control. + [[link](#no-exceptional-flows)] + + ```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 + ``` + + * + Avoid rescuing the `Exception` class. This will trap signals and calls to + `exit`, requiring you to `kill -9` the process. + [[link](#no-blind-rescues)] + + ```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 + ``` + + * + Put more specific exceptions higher up the rescue chain, otherwise they'll + never be rescued from. + [[link](#exception-ordering)] + + ```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 + ``` + + * + Release external resources obtained by your program in an `ensure` block. + [[link](#release-resources)] + + ```ruby + f = File.open('testfile') + begin + # .. process + rescue + # .. handle error + ensure + f.close if f + end + ``` + + * + Use versions of resource obtaining methods that do automatic + resource cleanup when possible. + [[link](#auto-release-resources)] + + ```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 + ``` + + * + Favor the use of exceptions from the standard library over introducing new + exception classes. + [[link](#standard-exceptions)] + +## Collections + + * + Prefer literal array and hash creation notation (unless you need to pass + parameters to their constructors, that is). + [[link](#literal-array-hash)] + + ```ruby + # bad + arr = Array.new + hash = Hash.new + + # good + arr = [] + arr = Array.new(10) + hash = {} + hash = Hash.new(0) + ``` + + * + Prefer `%w` to the literal array syntax when you need an array of words + (non-empty strings without spaces and special characters in them). Apply this + rule only to arrays with two or more elements. + [[link](#percent-w)] + + ```ruby + # bad + STATES = ['draft', 'open', 'closed'] + + # good + STATES = %w[draft open closed] + ``` + + * + Prefer `%i` to the literal array syntax when you need an array of symbols + (and you don't need to maintain Ruby 1.9 compatibility). Apply this rule only + to arrays with two or more elements. + [[link](#percent-i)] + + ```ruby + # bad + STATES = [:draft, :open, :closed] + + # good + STATES = %i[draft open closed] + ``` + + * + Avoid comma after the last item of an `Array` or `Hash` literal, especially + when the items are not on separate lines. + [[link](#no-trailing-array-commas)] + + ```ruby + # bad - easier to move/add/remove items, but still not preferred + VALUES = [ + 1001, + 2020, + 3333, + ] + + # bad + VALUES = [1001, 2020, 3333, ] + + # good + VALUES = [1001, 2020, 3333] + ``` + + * + Avoid the creation of huge gaps in arrays. + [[link](#no-gappy-arrays)] + + ```ruby + arr = [] + arr[100] = 1 # now you have an array with lots of nils + ``` + + * + When accessing the first or last element from an array, prefer `first` or + `last` over `[0]` or `[-1]`. + [[link](#first-and-last)] + + * + Use `Set` instead of `Array` when dealing with unique elements. `Set` + implements a collection of unordered values with no duplicates. This is a + hybrid of `Array`'s intuitive inter-operation facilities and `Hash`'s fast + lookup. + [[link](#set-vs-array)] + + * + Prefer symbols instead of strings as hash keys. + [[link](#symbols-as-keys)] + + ```ruby + # bad + hash = { 'one' => 1, 'two' => 2, 'three' => 3 } + + # good + hash = { one: 1, two: 2, three: 3 } + ``` + + * + Avoid the use of mutable objects as hash keys. + [[link](#no-mutable-keys)] + + * + Use the Ruby 1.9 hash literal syntax when your hash keys are symbols. + [[link](#hash-literals)] + + ```ruby + # bad + hash = { :one => 1, :two => 2, :three => 3 } + + # good + hash = { one: 1, two: 2, three: 3 } + ``` + + * + Don't mix the Ruby 1.9 hash syntax with hash rockets in the same hash + literal. When you've got keys that are not symbols stick to the hash rockets + syntax. + [[link](#no-mixed-hash-syntaces)] + + ```ruby + # bad + { a: 1, 'b' => 2 } + + # good + { :a => 1, 'b' => 2 } + ``` + + * + Use `Hash#key?` instead of `Hash#has_key?` and `Hash#value?` instead of + `Hash#has_value?`. + [[link](#hash-key)] + + ```ruby + # bad + hash.has_key?(:test) + hash.has_value?(value) + + # good + hash.key?(:test) + hash.value?(value) + ``` + + * + Use `Hash#each_key` instead of `Hash#keys.each` and `Hash#each_value` + instead of `Hash#values.each`. + [[link](#hash-each)] + + ```ruby + # bad + hash.keys.each { |k| p k } + hash.values.each { |v| p v } + hash.each { |k, _v| p k } + hash.each { |_k, v| p v } + + # good + hash.each_key { |k| p k } + hash.each_value { |v| p v } + ``` + + * + Use `Hash#fetch` when dealing with hash keys that should be present. + [[link](#hash-fetch)] + + ```ruby + heroes = { batman: 'Bruce Wayne', superman: 'Clark Kent' } + # bad - if we make a mistake we might not spot it right away + heroes[:batman] # => 'Bruce Wayne' + heroes[:supermann] # => nil + + # good - fetch raises a KeyError making the problem obvious + heroes.fetch(:supermann) + ``` + + * + Introduce default values for hash keys via `Hash#fetch` as opposed to using + custom logic. + [[link](#hash-fetch-defaults)] + + ```ruby + batman = { name: 'Bruce Wayne', is_evil: false } + + # bad - if we just use || operator with falsy value we won't get the expected result + batman[:is_evil] || true # => true + + # good - fetch works correctly with falsy values + batman.fetch(:is_evil, true) # => false + ``` + + * + Prefer the use of the block instead of the default value in `Hash#fetch` + if the code that has to be evaluated may have side effects or be expensive. + [[link](#use-hash-blocks)] + + ```ruby + batman = { name: 'Bruce Wayne' } + + # bad - if we use the default value, we eager evaluate it + # so it can slow the program down if done multiple times + batman.fetch(:powers, obtain_batman_powers) # obtain_batman_powers is an expensive call + + # good - blocks are lazy evaluated, so only triggered in case of KeyError exception + batman.fetch(:powers) { obtain_batman_powers } + ``` + + * + Use `Hash#values_at` when you need to retrieve several values consecutively + from a hash. + [[link](#hash-values-at)] + + ```ruby + # bad + email = data['email'] + username = data['nickname'] + + # good + email, username = data.values_at('email', 'nickname') + ``` + + * + Rely on the fact that as of Ruby 1.9 hashes are ordered. + [[link](#ordered-hashes)] + + * + Do not modify a collection while traversing it. + [[link](#no-modifying-collections)] + + * + When accessing elements of a collection, avoid direct access + via `[n]` by using an alternate form of the reader method if it is + supplied. This guards you from calling `[]` on `nil`. + [[link](#accessing-elements-directly)] + + ```ruby + # bad + Regexp.last_match[1] + + # good + Regexp.last_match(1) + ``` + + * + When providing an accessor for a collection, provide an alternate form + to save users from checking for `nil` before accessing an element in + the collection. + [[link](#provide-alternate-accessor-to-collections)] + + ```ruby + # bad + def awesome_things + @awesome_things + end + + # good + def awesome_things(index = nil) + if index && @awesome_things + @awesome_things[index] + else + @awesome_things + end + end + ``` +## Numbers + + * + ควรใช้ `Integer` ในการตรวจสอบประเภทของเลขจำนวนเต็ม. เพราะหากตรวจสอบด้วย `Fixnum` ที่จะให้ผลลัพท์ขึ้นกับ Platform ที่ใช้อาจทำให้ได้ผลลัพท์ที่ต่างกันระหว่างเครื่องระบบ 32-bit และระบบ 64-bit. + [[link](#integer-type-checking)] + + ```ruby + timestamp = Time.now.to_i + + # bad + timestamp.is_a? Fixnum + timestamp.is_a? Bignum + + # good + timestamp.is_a? Integer + ``` + + * + แนะนำให้ใช้ลำดับตัวเลขเมื่อต้องการสร้างตัวเลขสุ่ม แทนวิธี่การใส่ตัวเลขแล้วเพิ่มค่าทดแทน, เพราะจะเป็นการทำงานที่ตรงต่อจุดมุ่งหมายของคุณมากกว่า. ลองจินตนาการหากเป็นการทอยลูกเต๋า: + [[link](#random-numbers)] + + ```ruby + # bad + rand(6) + 1 + + # good + rand(1..6) + ``` + +## Strings + + * + Prefer string interpolation and string formatting instead of string + concatenation: + [[link](#string-interpolation)] + + ```ruby + # bad + email_with_name = user.name + ' <' + user.email + '>' + + # good + email_with_name = "#{user.name} <#{user.email}>" + + # good + email_with_name = format('%s <%s>', user.name, user.email) + ``` + + * + Adopt a consistent string literal quoting style. There are two popular + styles in the Ruby community, both of which are considered good—single + quotes by default (Option A) and double quotes by default (Option B). + [[link](#consistent-string-literals)] + + * **(Option A)** Prefer single-quoted strings when you don't need + string interpolation or special symbols such as `\t`, `\n`, `'`, + etc. + + ```ruby + # bad + name = "Bozhidar" + + name = 'De\'Andre' + + # good + name = 'Bozhidar' + + name = "De'Andre" + ``` + + * **(Option B)** Prefer double-quotes unless your string literal + contains `"` or escape characters you want to suppress. + + ```ruby + # bad + name = 'Bozhidar' + + sarcasm = "I \"like\" it." + + # good + name = "Bozhidar" + + sarcasm = 'I "like" it.' + ``` + + The string literals in this guide are aligned with the first style. + + * + Don't use the character literal syntax `?x`. Since Ruby 1.9 it's basically + redundant—`?x` would be interpreted as `'x'` (a string with a single + character in it). + [[link](#no-character-literals)] + + ```ruby + # bad + char = ?c + + # good + char = 'c' + ``` + + * + Don't leave out `{}` around instance and global variables being interpolated + into a string. + [[link](#curlies-interpolate)] + + ```ruby + class Person + attr_reader :first_name, :last_name + + def initialize(first_name, last_name) + @first_name = first_name + @last_name = last_name + end + + # bad - valid, but awkward + def to_s + "#@first_name #@last_name" + end + + # good + def to_s + "#{@first_name} #{@last_name}" + end + end + + $global = 0 + # bad + puts "$global = #$global" + + # good + puts "$global = #{$global}" + ``` + + * + Don't use `Object#to_s` on interpolated objects. It's invoked on them + automatically. + [[link](#no-to-s)] + + ```ruby + # bad + message = "This is the #{result.to_s}." + + # good + message = "This is the #{result}." + ``` + + * + Avoid using `String#+` when you need to construct large data chunks. + Instead, use `String#<<`. Concatenation mutates the string instance in-place + and is always faster than `String#+`, which creates a bunch of new string + objects. + [[link](#concat-strings)] + + ```ruby + # bad + html = '' + html += '

Page title

' + + paragraphs.each do |paragraph| + html += "

#{paragraph}

" + end + + # good and also fast + html = '' + html << '

Page title

' + + paragraphs.each do |paragraph| + html << "

#{paragraph}

" + end + ``` + + * + Don't use `String#gsub` in scenarios in which you can use a faster and more specialized alternative. + [[link](#dont-abuse-gsub)] + + ```ruby + url = 'http://example.com' + str = 'lisp-case-rules' + + # bad + url.gsub('http://', 'https://') + str.gsub('-', '_') + + # good + url.sub('http://', 'https://') + str.tr('-', '_') + ``` + + * + When using heredocs for multi-line strings keep in mind the fact that they + preserve leading whitespace. It's a good practice to employ some margin based + on which to trim the excessive whitespace. + [[link](#heredocs)] + + ```ruby + code = <<-END.gsub(/^\s+\|/, '') + |def test + | some_method + | other_method + |end + END + # => "def test\n some_method\n other_method\nend\n" + ``` + + * + Use Ruby 2.3's squiggly heredocs for nicely indented multi-line strings. + [[link](#squiggly-heredocs)] + + ```ruby + # bad - using Powerpack String#strip_margin + code = <<-RUBY.strip_margin('|') + |def test + | some_method + | other_method + |end + RUBY + + # also bad + code = <<-RUBY + def test + some_method + other_method + end + RUBY + + # good + code = <<~RUBY + def test + some_method + other_method + end + RUBY + ``` + + * + Use descriptive delimiters for heredocs. Delimiters add valuable information + about the heredoc content, and as an added bonus some editors can highlight + code within heredocs if the correct delimiter is used. + [[link](#heredoc-delimiters)] + + ```ruby + # bad + code = <<~END + def foo + bar + end + END + + # good + code = <<~RUBY + def foo + bar + end + RUBY + + # good + code = <<~SUMMARY + An imposing black structure provides a connection between the past and + the future in this enigmatic adaptation of a short story by revered + sci-fi author Arthur C. Clarke. + SUMMARY + ``` + +## Date & Time + + * + ให้ความสำคัญกับ `Time.now` มากกว่า `Time.new` เมื่อเรียกใช้เวลาปัจจุบัน. + [[link](#time-now)] + * + ห้ามใช้ `DateTime` เว้นแต่คุณต้องการปรับปรุงบัญชีย้อนหลังในปฏิทิน และถ้าคุณทำการระบุอาร์กิวเมนต์เริ่มต้นสถานะที่ระบุอย่างชัดเจน. + [[link](#no-datetime)] + + ```ruby + # bad - uses DateTime for current time + DateTime.now + + # good - uses Time for current time + Time.now + + # bad - uses DateTime for modern date + DateTime.iso8601('2016-06-29') + + # good - uses Date for modern date + Date.iso8601('2016-06-29') + + # good - uses DateTime with start argument for historical date + DateTime.iso8601('1751-04-23', Date::ENGLAND) + ``` + +## Regular Expressions + +> Some people, when confronted with a problem, think +> "I know, I'll use regular expressions." Now they have two problems.
+> -- Jamie Zawinski + + * + Don't use regular expressions if you just need plain text search in string: + `string['text']` + [[link](#no-regexp-for-plaintext)] + + * + For simple constructions you can use regexp directly through string index. + [[link](#regexp-string-index)] + + ```ruby + match = string[/regexp/] # get content of matched regexp + first_group = string[/text(grp)/, 1] # get content of captured group + string[/text (grp)/, 1] = 'replace' # string => 'text replace' + ``` + + * + Use non-capturing groups when you don't use the captured result. + [[link](#non-capturing-regexp)] + + ```ruby + # bad + /(first|second)/ + + # good + /(?:first|second)/ + ``` + + * + Don't use the cryptic Perl-legacy variables denoting last regexp group + matches (`$1`, `$2`, etc). Use `Regexp.last_match(n)` instead. + [[link](#no-perl-regexp-last-matchers)] + + ```ruby + /(regexp)/ =~ string + ... + + # bad + process $1 + + # good + process Regexp.last_match(1) + ``` + + * + Avoid using numbered groups as it can be hard to track what they contain. + Named groups can be used instead. + [[link](#no-numbered-regexes)] + + ```ruby + # bad + /(regexp)/ =~ string + # some code + process Regexp.last_match(1) + + # good + /(?regexp)/ =~ string + # some code + process meaningful_var + ``` + + * + Character classes have only a few special characters you should care about: + `^`, `-`, `\`, `]`, so don't escape `.` or brackets in `[]`. + [[link](#limit-escapes)] + + * + Be careful with `^` and `$` as they match start/end of line, not string + endings. If you want to match the whole string use: `\A` and `\z` (not to be + confused with `\Z` which is the equivalent of `/\n?\z/`). + [[link](#caret-and-dollar-regexp)] + + ```ruby + string = "some injection\nusername" + string[/^username$/] # matches + string[/\Ausername\z/] # doesn't match + ``` + + * + Use `x` modifier for complex regexps. This makes them more readable and you + can add some useful comments. Just be careful as spaces are ignored. + [[link](#comment-regexes)] + + ```ruby + regexp = / + start # some text + \s # white space char + (group) # first group + (?:alt1|alt2) # some alternation + end + /x + ``` + + * + For complex replacements `sub`/`gsub` can be used with a block or a hash. + [[link](#gsub-blocks)] + + ```ruby + words = 'foo bar' + words.sub(/f/, 'f' => 'F') # => 'Foo bar' + words.gsub(/\w+/) { |word| word.capitalize } # => 'Foo Bar' + ``` + +## Percent Literals + + * + Use `%()`(it's a shorthand for `%Q`) for single-line strings which require + both interpolation and embedded double-quotes. For multi-line strings, prefer + heredocs. + [[link](#percent-q-shorthand)] + + ```ruby + # bad (no interpolation needed) + %(
Some text
) + # should be '
Some text
' + + # bad (no double-quotes) + %(This is #{quality} style) + # should be "This is #{quality} style" + + # bad (multiple lines) + %(
\n#{exclamation}\n
) + # should be a heredoc. + + # good (requires interpolation, has quotes, single line) + %(#{name}) + ``` + + * + Avoid `%()` or the equivalent `%q()` unless you have a string with both `'` and + `"` in it. Regular string literals are more readable and should be preferred + unless a lot of characters would have to be escaped in them. + [[link](#percent-q)] + + ```ruby + # bad + name = %q(Bruce Wayne) + time = %q(8 o'clock) + question = %q("What did you say?") + + # good + name = 'Bruce Wayne' + time = "8 o'clock" + question = '"What did you say?"' + quote = %q(

"What did you say?"

) + ``` + + * + Use `%r` only for regular expressions matching *at least* one `'/'` + character. + [[link](#percent-r)] + + ```ruby + # bad + %r{\s+} + + # good + %r{^/(.*)$} + %r{^/blog/2011/(.*)$} + ``` + + * + Avoid the use of `%x` unless you're going to invoke a command with + backquotes in it (which is rather unlikely). + [[link](#percent-x)] + + ```ruby + # bad + date = %x(date) + + # good + date = `date` + echo = %x(echo `date`) + ``` + + * + Avoid the use of `%s`. It seems that the community has decided `:"some + string"` is the preferred way to create a symbol with spaces in it. + [[link](#percent-s)] + + * + Use the braces that are the most appropriate for the various kinds of percent + literals. + [[link](#percent-literal-braces)] + - `()` for string literals(`%q`, `%Q`). + - `[]` for array literals(`%w`, `%i`, `%W`, `%I`) as it is aligned with + the standard array literals. + - `{}` for regexp literals(`%r`) since parentheses often appear inside regular + expressions. That's why a less common character with `{` is usually the best + delimiter for `%r` literals. + - `()` for all other literals (e.g. `%s`, `%x`) + + ```ruby + # bad + %q{"Test's king!", John said.} + + # good + %q("Test's king!", John said.) + + # bad + %w(one two three) + %i(one two three) + + # good + %w[one two three] + %i[one two three] + + # bad + %r((\w+)-(\d+)) + %r{\w{1,2}\d{2,5}} + + # good + %r{(\w+)-(\d+)} + %r|\w{1,2}\d{2,5}| + ``` + +## Metaprogramming + + * + Avoid needless metaprogramming. + [[link](#no-needless-metaprogramming)] + + * + Do not mess around in core classes when writing libraries. (Do not + monkey-patch them.) + [[link](#no-monkey-patching)] + + * + The block form of `class_eval` is preferable to the string-interpolated + form. + [[link](#block-class-eval)] + + - when you use the string-interpolated form, always supply `__FILE__` + and `__LINE__`, so that your backtraces make sense: + + ```ruby + class_eval 'def use_relative_model_naming?; true; end', __FILE__, __LINE__ + ``` + + - `define_method` is preferable to `class_eval{ def ... }` + + * + When using `class_eval` (or other `eval`) with string interpolation, add a + comment block showing its appearance if interpolated (a practice used in Rails + code): + [[link](#eval-comment-docs)] + + ```ruby + # from activesupport/lib/active_support/core_ext/string/output_safety.rb + UNSAFE_STRING_METHODS.each do |unsafe_method| + if 'String'.respond_to?(unsafe_method) + class_eval <<-EOT, __FILE__, __LINE__ + 1 + def #{unsafe_method}(*params, &block) # def capitalize(*params, &block) + to_str.#{unsafe_method}(*params, &block) # to_str.capitalize(*params, &block) + end # end + + def #{unsafe_method}!(*params) # def capitalize!(*params) + @dirty = true # @dirty = true + super # super + end # end + EOT + end + end + ``` + + * + Avoid using `method_missing` for metaprogramming because backtraces become + messy, the behavior is not listed in `#methods`, and misspelled method calls + might silently work, e.g. `nukes.launch_state = false`. Consider using + delegation, proxy, or `define_method` instead. If you must use + `method_missing`: + [[link](#no-method-missing)] + + - Be sure to [also define `respond_to_missing?`](http://blog.marc-andre.ca/2010/11/methodmissing-politely.html) + - Only catch methods with a well-defined prefix, such as `find_by_*`—make your code as assertive as possible. + - Call `super` at the end of your statement + - Delegate to assertive, non-magical methods: + + ```ruby + # bad + def method_missing(meth, *params, &block) + if /^find_by_(?.*)/ =~ meth + # ... lots of code to do a find_by + else + super + end + end + + # good + def method_missing(meth, *params, &block) + if /^find_by_(?.*)/ =~ meth + find_by(prop, *params, &block) + else + super + end + end + + # best of all, though, would to define_method as each findable attribute is declared + ``` + + * + Prefer `public_send` over `send` so as not to circumvent `private`/`protected` visibility. + [[link](#prefer-public-send)] + + ```ruby + # We have an ActiveModel Organization that includes concern Activatable + module Activatable + extend ActiveSupport::Concern + + included do + before_create :create_token + end + + private + + def reset_token + # some code + end + + def create_token + # some code + end + + def activate! + # some code + end + end + + class Organization < ActiveRecord::Base + include Activatable + end + + linux_organization = Organization.find(...) + # BAD - violates privacy + linux_organization.send(:reset_token) + # GOOD - should throw an exception + linux_organization.public_send(:reset_token) + ``` + + * + Prefer `__send__` over `send`, as `send` may overlap with existing methods. + [[link](#prefer-__send__)] + + ```ruby + require 'socket' + + u1 = UDPSocket.new + u1.bind('127.0.0.1', 4913) + u2 = UDPSocket.new + u2.connect('127.0.0.1', 4913) + # Won't send a message to the receiver obj. + # Instead it will send a message via UDP socket. + u2.send :sleep, 0 + # Will actually send a message to the receiver obj. + u2.__send__ ... + ``` + +## Misc + + * + Write `ruby -w` safe code. + [[link](#always-warn)] + + * + Avoid hashes as optional parameters. Does the method do too much? (Object + initializers are exceptions for this rule). + [[link](#no-optional-hash-params)] + + * + 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. + [[link](#short-methods)] + + * + Avoid parameter lists longer than three or four parameters. + [[link](#too-many-params)] + + * + If you really need "global" methods, add them to Kernel and make them + private. + [[link](#private-global-methods)] + + * + Use module instance variables instead of global variables. + [[link](#instance-vars)] + + ```ruby + # bad + $foo_bar = 1 + + # good + module Foo + class << self + attr_accessor :bar + end + end + + Foo.bar = 1 + ``` + + * + Use `OptionParser` for parsing complex command line options and `ruby -s` + for trivial command line options. + [[link](#optionparser)] + + * + Code in a functional way, avoiding mutation when that makes sense. + [[link](#functional-code)] + + ```ruby + a = []; [1, 2, 3].each { |i| a << i * 2 } # bad + a = [1, 2, 3].map { |i| i * 2 } # good + + a = {}; [1, 2, 3].each { |i| a[i] = i * 17 } # bad + a = [1, 2, 3].reduce({}) { |h, i| h[i] = i * 17; h } # good + a = [1, 2, 3].each_with_object({}) { |i, h| h[i] = i * 17 } # good + ``` + + * + Do not mutate parameters unless that is the purpose of the method. + [[link](#no-param-mutations)] + + * + Avoid more than three levels of block nesting. + [[link](#three-is-the-number-thou-shalt-count)] + + * + Be consistent. In an ideal world, be consistent with these guidelines. + [[link](#be-consistent)] + + * + Use common sense. + [[link](#common-sense)] + +## Tools + +Here are some tools to help you automatically check Ruby code against +this guide. + +### RuboCop + +[RuboCop][] is a Ruby static code analyzer and formatter, based on this +style guide. RuboCop already covers a significant portion of the +Guide and plugins for most popular Ruby editors and IDEs. + +### RubyMine + +[RubyMine](http://www.jetbrains.com/ruby/)'s code inspections are +[partially based](http://confluence.jetbrains.com/display/RUBYDEV/RubyMine+Inspections) +on this guide. + +# Contributing + +The guide is still a work in progress—some rules are lacking examples, +some rules don't have examples that illustrate them clearly enough. Improving +such rules is a great (and simple way) to help the Ruby community! + +In due time these issues will (hopefully) be addressed—just keep them in +mind for now. + +Nothing written in this guide is set in stone. It's my desire to work +together with everyone interested in Ruby coding style, so that we could +ultimately create a resource that will be beneficial to the entire Ruby +community. + +Feel free to open tickets or send pull requests with improvements. Thanks in +advance for your help! + +You can also support the project (and RuboCop) with financial +contributions via [Patreon](https://www.patreon.com/bbatsov). + +## How to Contribute? + +It's easy, just follow the [contribution guidelines](https://github.com/rubocop-hq/ruby-style-guide/blob/master/CONTRIBUTING.md). + +# License + +![Creative Commons License](http://i.creativecommons.org/l/by/3.0/88x31.png) +This work is licensed under a [Creative Commons Attribution 3.0 Unported License](http://creativecommons.org/licenses/by/3.0/deed.en_US) + +# Spread the Word + +A community-driven style guide is of little use to a community that +doesn't know about its existence. Tweet about the guide, share it with +your friends and colleagues. Every comment, suggestion or opinion we +get makes the guide just a little bit better. And we want to have the +best possible guide, don't we? + +Cheers,
+[Bozhidar](https://twitter.com/bbatsov) + +[PEP-8]: https://www.python.org/dev/peps/pep-0008/ +[rails-style-guide]: https://github.com/rubocop-hq/rails-style-guide +[pickaxe]: https://pragprog.com/book/ruby4/programming-ruby-1-9-2-0 +[trpl]: http://www.amazon.com/Ruby-Programming-Language-David-Flanagan/dp/0596516177 +[Pandoc]: http://pandoc.org/ +[RuboCop]: https://github.com/rubocop-hq/rubocop +[yard]: https://yardoc.org/