Giter Club home page Giter Club logo

ruby's Introduction

Ruby Style Guide

This is Airbnb's Ruby Style Guide.

It was inspired by GitHub's guide and RuboCop's guide.

Airbnb also maintains a JavaScript Style Guide.

Table of Contents

  1. Whitespace
    1. Indentation
    2. Inline
    3. Newlines
  2. Line Length
  3. Commenting
    1. File/class-level comments
    2. Function comments
    3. Block and inline comments
    4. Punctuation, spelling, and grammar
    5. TODO comments
    6. Commented-out code
  4. Methods
    1. Method definitions
    2. Method calls
  5. Conditional Expressions
    1. Conditional keywords
    2. Ternary operator
  6. Syntax
  7. Naming
  8. Classes
  9. Exceptions
  10. Collections
  11. Strings
  12. Regular Expressions
  13. Percent Literals
  14. Rails
    1. Scopes
  15. Be Consistent
  16. Translation

Whitespace

Indentation

  • Use soft-tabs with a two-space indent.[link]

  • Indent when as deep as case. [link]

    case
    when song.name == 'Misty'
      puts 'Not again!'
    when song.duration > 120
      puts 'Too long!'
    when Time.now.hour > 21
      puts "It's too late"
    else
      song.play
    end
    
    kind = case year
           when 1850..1889 then 'Blues'
           when 1890..1909 then 'Ragtime'
           when 1910..1929 then 'New Orleans Jazz'
           when 1930..1939 then 'Swing'
           when 1940..1950 then 'Bebop'
           else 'Jazz'
           end
  • Align function parameters either all on the same line or one per line.[link]

    # bad
    def self.create_translation(phrase_id, phrase_key, target_locale,
                                value, user_id, do_xss_check, allow_verification)
      ...
    end
    
    # good
    def self.create_translation(phrase_id,
                                phrase_key,
                                target_locale,
                                value,
                                user_id,
                                do_xss_check,
                                allow_verification)
      ...
    end
    
    # good
    def self.create_translation(
      phrase_id,
      phrase_key,
      target_locale,
      value,
      user_id,
      do_xss_check,
      allow_verification
    )
      ...
    end
  • Indent succeeding lines in multi-line boolean expressions.[link]

    # bad
    def is_eligible?(user)
      Trebuchet.current.launch?(ProgramEligibilityHelper::PROGRAM_TREBUCHET_FLAG) &&
      is_in_program?(user) &&
      program_not_expired
    end
    
    # good
    def is_eligible?(user)
      Trebuchet.current.launch?(ProgramEligibilityHelper::PROGRAM_TREBUCHET_FLAG) &&
        is_in_program?(user) &&
        program_not_expired
    end

Inline

  • Never leave trailing whitespace. [link]

  • When making inline comments, include a space between the end of the code and the start of your comment. [link]

    # bad
    result = func(a, b)# we might want to change b to c
    
    # good
    result = func(a, b) # we might want to change b to c
  • Use spaces around operators; after commas, colons, and semicolons; and around { and before }. [link]

    sum = 1 + 2
    a, b = 1, 2
    1 > 2 ? true : false; puts 'Hi'
    [1, 2, 3].each { |e| puts e }
  • Never include a space before a comma. [link]

    result = func(a, b)
  • Do not include space inside block parameter pipes. Include one space between parameters in a block. Include one space outside block parameter pipes. [link]

    # bad
    {}.each { | x,  y |puts x }
    
    # good
    {}.each { |x, y| puts x }
  • Do not leave space between ! and its argument.[link]

    !something
  • No spaces after (, [ or before ], ). [link]

    some(arg).other
    [1, 2, 3].length
  • Omit whitespace when doing string interpolation.[link]

    # bad
    var = "This #{ foobar } is interpolated."
    
    # good
    var = "This #{foobar} is interpolated."
  • Don't use extra whitespace in range literals.[link]

    # bad
    (0 ... coll).each do |item|
    
    # good
    (0...coll).each do |item|

Newlines

  • Add a new line after if conditions spanning multiple lines to help differentiate between the conditions and the body. [link]

    if @reservation_alteration.checkin == @reservation.start_date &&
       @reservation_alteration.checkout == (@reservation.start_date + @reservation.nights)
    
      redirect_to_alteration @reservation_alteration
    end
  • Add a new line after conditionals, blocks, case statements, etc.[link]

    if robot.is_awesome?
      send_robot_present
    end
    
    robot.add_trait(:human_like_intelligence)
  • Don’t include newlines between areas of different indentation (such as around class or module bodies). [link]

    # bad
    class Foo
    
      def bar
        # body omitted
      end
    
    end
    
    # good
    class Foo
      def bar
        # body omitted
      end
    end
  • Include one, but no more than one, new line between methods.[link]

    def a
    end
    
    def b
    end
  • Use a single empty line to break between statements to break up methods into logical paragraphs internally. [link]

    def transformorize_car
      car = manufacture(options)
      t = transformer(robot, disguise)
    
      car.after_market_mod!
      t.transform(car)
      car.assign_cool_name!
    
      fleet.add(car)
      car
    end
  • End each file with a newline. Don't include multiple newlines at the end of a file. [link]

Line Length

  • Keep each line of code to a readable length. Unless you have a reason not to, keep lines to fewer than 100 characters. (rationale) [link]

Commenting

Though a pain to write, comments are absolutely vital to keeping our code readable. The following rules describe what you should comment and where. But remember: while comments are very important, the best code is self-documenting. Giving sensible names to types and variables is much better than using obscure names that you must then explain through comments.

When writing your comments, write for your audience: the next contributor who will need to understand your code. Be generous — the next one may be you!

Google C++ Style Guide

Portions of this section borrow heavily from the Google C++ and Python style guides.

File/class-level comments

Every class definition should have an accompanying comment that describes what it is for and how it should be used.

A file that contains zero classes or more than one class should have a comment at the top describing its contents.

# Automatic conversion of one locale to another where it is possible, like
# American to British English.
module Translation
  # Class for converting between text between similar locales.
  # Right now only conversion between American English -> British, Canadian,
  # Australian, New Zealand variations is provided.
  class PrimAndProper
    def initialize
      @converters = { :en => { :"en-AU" => AmericanToAustralian.new,
                               :"en-CA" => AmericanToCanadian.new,
                               :"en-GB" => AmericanToBritish.new,
                               :"en-NZ" => AmericanToKiwi.new,
                             } }
    end

  ...

  # Applies transforms to American English that are common to
  # variants of all other English colonies.
  class AmericanToColonial
    ...
  end

  # Converts American to British English.
  # In addition to general Colonial English variations, changes "apartment"
  # to "flat".
  class AmericanToBritish < AmericanToColonial
    ...
  end

All files, including data and config files, should have file-level comments.

# List of American-to-British spelling variants.
#
# This list is made with
# lib/tasks/list_american_to_british_spelling_variants.rake.
#
# It contains words with general spelling variation patterns:
#   [trave]led/lled, [real]ize/ise, [flav]or/our, [cent]er/re, plus
# and these extras:
#   learned/learnt, practices/practises, airplane/aeroplane, ...

sectarianizes: sectarianises
neutralization: neutralisation
...

Function comments

Every function declaration should have comments immediately preceding it that describe what the function does and how to use it. These comments should be descriptive ("Opens the file") rather than imperative ("Open the file"); the comment describes the function, it does not tell the function what to do. In general, these comments do not describe how the function performs its task. Instead, that should be left to comments interspersed in the function's code.

Every function should mention what the inputs and outputs are, unless it meets all of the following criteria:

  • not externally visible
  • very short
  • obvious

You may use whatever format you wish. In Ruby, two popular function documentation schemes are TomDoc and YARD. You can also just write things out concisely:

# Returns the fallback locales for the_locale.
# If opts[:exclude_default] is set, the default locale, which is otherwise
# always the last one in the returned list, will be excluded.
#
# For example:
#   fallbacks_for(:"pt-BR")
#     => [:"pt-BR", :pt, :en]
#   fallbacks_for(:"pt-BR", :exclude_default => true)
#     => [:"pt-BR", :pt]
def fallbacks_for(the_locale, opts = {})
  ...
end

Block and inline comments

The final place to have comments is in tricky parts of the code. If you're going to have to explain it at the next code review, you should comment it now. Complicated operations get a few lines of comments before the operations commence. Non-obvious ones get comments at the end of the line.

def fallbacks_for(the_locale, opts = {})
  # dup() to produce an array that we can mutate.
  ret = @fallbacks[the_locale].dup

  # We make two assumptions here:
  # 1) There is only one default locale (that is, it has no less-specific
  #    children).
  # 2) The default locale is just a language. (Like :en, and not :"en-US".)
  if opts[:exclude_default] &&
      ret.last == default_locale &&
      ret.last != language_from_locale(the_locale)
    ret.pop
  end

  ret
end

On the other hand, never describe the code. Assume the person reading the code knows the language (though not what you're trying to do) better than you do.

Related: do not use block comments. They cannot be preceded by whitespace and are not as easy to spot as regular comments. [link]

# bad
=begin
comment line
another comment line
=end

# good
# comment line
# another comment line

Punctuation, spelling and grammar

Pay attention to punctuation, spelling, and grammar; it is easier to read well-written comments than badly written ones.

Comments should be as readable as narrative text, with proper capitalization and punctuation. In many cases, complete sentences are more readable than sentence fragments. Shorter comments, such as comments at the end of a line of code, can sometimes be less formal, but you should be consistent with your style.

Although it can be frustrating to have a code reviewer point out that you are using a comma when you should be using a semicolon, it is very important that source code maintain a high level of clarity and readability. Proper punctuation, spelling, and grammar help with that goal.

TODO comments

Use TODO comments for code that is temporary, a short-term solution, or good-enough but not perfect.

TODOs should include the string TODO in all caps, followed by the full name of the person who can best provide context about the problem referenced by the TODO, in parentheses. A colon is optional. A comment explaining what there is to do is required. The main purpose is to have a consistent TODO format that can be searched to find the person who can provide more details upon request. A TODO is not a commitment that the person referenced will fix the problem. Thus when you create a TODO, it is almost always your name that is given.

  # bad
  # TODO(RS): Use proper namespacing for this constant.

  # bad
  # TODO(drumm3rz4lyfe): Use proper namespacing for this constant.

  # good
  # TODO(Ringo Starr): Use proper namespacing for this constant.

Commented-out code

  • Never leave commented-out code in our codebase. [link]

Methods

Method definitions

  • Use def with parentheses when there are parameters. Omit the parentheses when the method doesn't accept any parameters.[link]

    def some_method
      # body omitted
    end
    
    def some_method_with_parameters(arg1, arg2)
      # body omitted
    end
  • Do not use default positional arguments. Use keyword arguments (if available - in Ruby 2.0 or later) or an options hash instead.[link]

    # bad
    def obliterate(things, gently = true, except = [], at = Time.now)
      ...
    end
    
    # good
    def obliterate(things, gently: true, except: [], at: Time.now)
      ...
    end
    
    # good
    def obliterate(things, options = {})
      options = {
        :gently => true, # obliterate with soft-delete
        :except => [], # skip obliterating these things
        :at => Time.now, # don't obliterate them until later
      }.merge(options)
    
      ...
    end
  • Avoid single-line methods. Although they are somewhat popular in the wild, there are a few peculiarities about their definition syntax that make their use undesirable. [link]

    # bad
    def too_much; something; something_else; end
    
    # good
    def some_method
      # body
    end

Method calls

Use parentheses for a method call:

  • If the method returns a value. [link]

    # bad
    @current_user = User.find_by_id 1964192
    
    # good
    @current_user = User.find_by_id(1964192)
  • If the first argument to the method uses parentheses.[link]

    # bad
    put! (x + y) % len, value
    
    # good
    put!((x + y) % len, value)
  • Never put a space between a method name and the opening parenthesis.[link]

    # bad
    f (3 + 2) + 1
    
    # good
    f(3 + 2) + 1
  • Omit parentheses for a method call if the method accepts no arguments.[link]

    # bad
    nil?()
    
    # good
    nil?
  • If the method doesn't return a value (or we don't care about the return), parentheses are optional. (Especially if the arguments overflow to multiple lines, parentheses may add readability.) [link]

    # okay
    render(:partial => 'foo')
    
    # okay
    render :partial => 'foo'

In either case:

  • If a method accepts an options hash as the last argument, do not use { } during invocation. [link]

    # bad
    get '/v1/reservations', { :id => 54875 }
    
    # good
    get '/v1/reservations', :id => 54875

Conditional Expressions

Conditional keywords

  • Never use then for multi-line if/unless. [link]

    # bad
    if some_condition then
      ...
    end
    
    # good
    if some_condition
      ...
    end
  • Never use do for multi-line while or until.[link]

    # bad
    while x > 5 do
      ...
    end
    
    until x > 5 do
      ...
    end
    
    # good
    while x > 5
      ...
    end
    
    until x > 5
      ...
    end
  • The and, or, and not keywords are banned. It's just not worth it. Always use &&, ||, and ! instead. [link]

  • Modifier if/unless usage is okay when the body is simple, the condition is simple, and the whole thing fits on one line. Otherwise, avoid modifier if/unless. [link]

    # bad - this doesn't fit on one line
    add_trebuchet_experiments_on_page(request_opts[:trebuchet_experiments_on_page]) if request_opts[:trebuchet_experiments_on_page] && !request_opts[:trebuchet_experiments_on_page].empty?
    
    # okay
    if request_opts[:trebuchet_experiments_on_page] &&
         !request_opts[:trebuchet_experiments_on_page].empty?
    
      add_trebuchet_experiments_on_page(request_opts[:trebuchet_experiments_on_page])
    end
    
    # bad - this is complex and deserves multiple lines and a comment
    parts[i] = part.to_i(INTEGER_BASE) if !part.nil? && [0, 2, 3].include?(i)
    
    # okay
    return if reconciled?
  • Never use unless with else. Rewrite these with the positive case first.[link]

    # bad
    unless success?
      puts 'failure'
    else
      puts 'success'
    end
    
    # good
    if success?
      puts 'success'
    else
      puts 'failure'
    end
  • Avoid unless with multiple conditions.[link]

      # bad
      unless foo? && bar?
        ...
      end
    
      # okay
      if !(foo? && bar?)
        ...
      end
  • Avoid unless with comparison operators if you can use if with an opposing comparison operator.[link]

      # bad
      unless x == 10
        ...
      end
    
      # good
      if x != 10
        ...
      end
    
      # bad
      unless x < 10
        ...
      end
    
      # good
      if x >= 10
        ...
      end
    
      # ok
      unless x === 10
        ...
      end
  • Don't use parentheses around the condition of an if/unless/while. [link]

    # bad
    if (x > 10)
      ...
    end
    
    # good
    if x > 10
      ...
    end

Ternary operator

  • Avoid the ternary operator (?:) except in cases where all expressions are extremely trivial. However, do use the ternary operator(?:) over if/then/else/end constructs for single line conditionals.[link]

    # 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]

    # 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
  • Avoid multiple conditions in ternaries. Ternaries are best used with single conditions. [link]

  • Avoid multi-line ?: (the ternary operator), use if/then/else/end instead. [link]

    # bad
    some_really_long_condition_that_might_make_you_want_to_split_lines ?
      something : something_else
    
    # good
    if some_really_long_condition_that_might_make_you_want_to_split_lines
      something
    else
      something_else
    end

Nested conditionals

  • Avoid the use of nested conditionals for flow of control. (More on this.) [link]

    Prefer a guard clause when you can assert invalid data. A guard clause is a conditional statement at the top of a function that returns as soon as it can.

    The general principles boil down to:

    • Return immediately once you know your function cannot do anything more.
    • Reduce nesting and indentation in the code by returning early. This makes the code easier to read and requires less mental bookkeeping on the part of the reader to keep track of else branches.
    • The core or most important flows should be the least indented.
    # bad
    def compute
      server = find_server
      if server
        client = server.client
        if client
          request = client.make_request
          if request
             process_request(request)
          end
        end
      end
    end
    
    # good
    def compute
      server = find_server
      return unless server
      client = server.client
      return unless client
      request = client.make_request
      return unless request
      process_request(request)
    end

    Prefer next in loops instead of conditional blocks.

    # 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

    See also the section "Guard Clause", p68-70 in Beck, Kent. Implementation Patterns. Upper Saddle River: Addison-Wesley, 2008, which has inspired some of the content above.

Syntax

  • Never 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]

    arr = [1, 2, 3]
    
    # bad
    for elem in arr do
      puts elem
    end
    
    # good
    arr.each { |elem| puts elem }
  • Prefer {...} over do...end for single-line blocks. Avoid using {...} for multi-line blocks (multiline 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]

    names = ["Bozhidar", "Steve", "Sarah"]
    
    # good
    names.each { |name| puts name }
    
    # bad
    names.each do |name| puts name end
    
    # good
    names.each do |name|
      puts name
      puts 'yay!'
    end
    
    # bad
    names.each { |name|
      puts name
      puts 'yay!'
    }
    
    # good
    names.select { |name| name.start_with?("S") }.map { |name| name.upcase }
    
    # bad
    names.select do |name|
      name.start_with?("S")
    end.map { |name| name.upcase }

    Some will argue that multiline chaining would look okay with the use of {...}, but they should ask themselves if this code is really readable and whether the block's content can be extracted into nifty methods.

  • Use shorthand self assignment operators whenever applicable.[link]

    # 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
  • Avoid semicolons except for in single line class definitions. When it is appropriate to use a semicolon, it should be directly adjacent to the statement it terminates: there should be no space before the semicolon.[link]

    # 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
  • 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]

    # bad
    SomeClass::some_method
    some_object::some_method
    
    # good
    SomeClass.some_method
    some_object.some_method
    SomeModule::SomeClass::SOME_CONST
    SomeModule::SomeClass()
  • Avoid return where not required. [link]

    # bad
    def some_method(some_arr)
      return some_arr.size
    end
    
    # good
    def some_method(some_arr)
      some_arr.size
    end
  • Don't use the return value of = in conditionals[link]

    # bad - shows intended use of assignment
    if (v = array.grep(/foo/))
      ...
    end
    
    # bad
    if v = array.grep(/foo/)
      ...
    end
    
    # good
    v = array.grep(/foo/)
    if v
      ...
    end
  • Use ||= freely to initialize variables. [link]

    # 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]

    # bad - would set enabled to true even if it was false
    enabled ||= true
    
    # good
    enabled = true if enabled.nil?
  • Use .call explicitly when calling lambdas. [link]

    # bad
    lambda.(x, y)
    
    # good
    lambda.call(x, y)
  • Avoid using Perl-style special variables (like $0-9, $, etc. ). They are quite cryptic and their use in anything but one-liner scripts is discouraged. Prefer long form versions such as $PROGRAM_NAME.[link]

  • When a method block takes only one argument, and the body consists solely of reading an attribute or calling one method with no arguments, use the &: shorthand. [link]

    # bad
    bluths.map { |bluth| bluth.occupation }
    bluths.select { |bluth| bluth.blue_self? }
    
    # good
    bluths.map(&:occupation)
    bluths.select(&:blue_self?)
  • Prefer some_method over self.some_method when calling a method on the current instance.[link]

    # bad
    def end_date
      self.start_date + self.nights
    end
    
    # good
    def end_date
      start_date + nights
    end

    In the following three common cases, self. is required by the language and is good to use:

    1. When defining a class method: def self.some_method.
    2. The left hand side when calling an assignment method, including assigning an attribute when self is an ActiveRecord model: self.guest = user.
    3. Referencing the current instance's class: self.class.
  • When defining an object of any mutable type meant to be a constant, make sure to call freeze on it. Common examples are strings, arrays, and hashes. (More on this.)[link]

    The reason is that Ruby constants are actually mutable. Calling freeze ensures they are not mutated and are therefore truly constant and attempting to modify them will raise an exception. For strings, this allows older versions of Ruby below 2.2 to intern them.

    # bad
    class Color
      RED = 'red'
      BLUE = 'blue'
      GREEN = 'green'
    
      ALL_COLORS = [
        RED,
        BLUE,
        GREEN,
      ]
    
      COLOR_TO_RGB = {
        RED => 0xFF0000,
        BLUE => 0x0000FF,
        GREEN => 0x00FF00,
      }
    end
    
    # good
    class Color
      RED = 'red'.freeze
      BLUE = 'blue'.freeze
      GREEN = 'green'.freeze
    
      ALL_COLORS = [
        RED,
        BLUE,
        GREEN,
      ].freeze
    
      COLOR_TO_RGB = {
        RED => 0xFF0000,
        BLUE => 0x0000FF,
        GREEN => 0x00FF00,
      }.freeze
    end

Naming

  • Use snake_case for methods and variables. [link]

  • Use CamelCase for classes and modules. (Keep acronyms like HTTP, RFC, XML uppercase.) [link]

  • Use SCREAMING_SNAKE_CASE for other constants.[link]

  • The names of predicate methods (methods that return a boolean value) should end in a question mark. (i.e. Array#empty?).[link]

  • The names of potentially "dangerous" methods (i.e. methods that modify self or the arguments, exit!, etc.) should end with an exclamation mark. Bang methods should only exist if a non-bang method exists. (More on this.) [link]

  • Name throwaway variables _. [link]

    version = '3.2.1'
    major_version, minor_version, _ = version.split('.')

Classes

  • Avoid the usage of class (@@) variables due to their "nasty" behavior in inheritance. [link]

    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.

  • Use def self.method to define singleton methods. This makes the methods more resistant to refactoring changes. [link]

    class TestClass
      # bad
      def TestClass.some_method
        ...
      end
    
      # good
      def self.some_other_method
        ...
      end
  • Avoid class << self except when necessary, e.g. single accessors and aliased attributes. [link]

    class TestClass
      # bad
      class << self
        def first_method
          ...
        end
    
        def second_method_etc
          ...
        end
      end
    
      # good
      class << self
        attr_accessor :per_page
        alias_method :nwo, :find_by_name_with_owner
      end
    
      def self.first_method
        ...
      end
    
      def self.second_method_etc
        ...
      end
    end
  • Indent the public, protected, and private methods as much the method definitions they apply to. Leave one blank line above and below them.[link]

    class SomeClass
      def public_method
        # ...
      end
    
      private
    
      def private_method
        # ...
      end
    end

Exceptions

  • Don't use exceptions for flow of control. [link]

    # 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. [link]

    # bad
    begin
      # an exception occurs here
    rescue Exception
      # exception handling
    end
    
    # good
    begin
      # an exception occurs here
    rescue StandardError
      # exception handling
    end
    
    # acceptable
    begin
      # an exception occurs here
    rescue
      # exception handling
    end
  • Don't specify RuntimeError explicitly in the two argument version of raise. Prefer error sub-classes for clarity and explicit error creation.[link]

    # bad
    raise RuntimeError, 'message'
    
    # better - RuntimeError is implicit here
    raise 'message'
    
    # best
    class MyExplicitError < RuntimeError; end
    raise MyExplicitError
  • Prefer supplying an exception class and a message as two separate arguments to raise, instead of an exception instance. [link]

    # 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`.
  • Avoid using rescue in its modifier form. [link]

    # bad
    read_file rescue handle_error($!)
    
    # good
    begin
      read_file
    rescue Errno:ENOENT => ex
      handle_error(ex)
    end

Collections

  • Prefer map over collect.[link]

  • Prefer detect over find. The use of find is ambiguous with regard to ActiveRecord's find method - detect makes clear that you're working with a Ruby collection, not an AR object. [link]

  • Prefer reduce over inject. [link]

  • Prefer size over either length or count for performance reasons.[link]

  • Prefer literal array and hash creation notation unless you need to pass parameters to their constructors. [link]

    # bad
    arr = Array.new
    hash = Hash.new
    
    # good
    arr = []
    hash = {}
    
    # good because constructor requires parameters
    x = Hash.new { |h, k| h[k] = {} }
  • Favor Array#join over Array#* for clarity. [link]

    # bad
    %w(one two three) * ', '
    # => 'one, two, three'
    
    # good
    %w(one two three).join(', ')
    # => 'one, two, three'
  • Use symbols instead of strings as hash keys. [link]

    # bad
    hash = { 'one' => 1, 'two' => 2, 'three' => 3 }
    
    # good
    hash = { :one => 1, :two => 2, :three => 3 }
  • Relatedly, use plain symbols instead of string symbols when possible.[link]

    # bad
    :"symbol"
    
    # good
    :symbol
  • Use Hash#key? instead of Hash#has_key? and Hash#value? instead of Hash#has_value?. According to Matz, the longer forms are considered deprecated. [link]

    # bad
    hash.has_key?(:test)
    hash.has_value?(value)
    
    # good
    hash.key?(:test)
    hash.value?(value)
  • Use multi-line hashes when it makes the code more readable, and use trailing commas to ensure that parameter changes don't cause extraneous diff lines when the logic has not otherwise changed. [link]

    hash = {
      :protocol => 'https',
      :only_path => false,
      :controller => :users,
      :action => :set_password,
      :redirect => @redirect_url,
      :secret => @secret,
    }
  • Use a trailing comma in an Array that spans more than 1 line[link]

    # good
    array = [1, 2, 3]
    
    # good
    array = [
      "car",
      "bear",
      "plane",
      "zoo",
    ]

Strings

  • Prefer string interpolation instead of string concatenation:[link]

    # bad
    email_with_name = user.name + ' <' + user.email + '>'
    
    # good
    email_with_name = "#{user.name} <#{user.email}>"

    Furthermore, keep in mind Ruby 1.9-style interpolation. Let's say you are composing cache keys like this:

    CACHE_KEY = '_store'
    
    cache.write(@user.id + CACHE_KEY)

    Prefer string interpolation instead of string concatenation:

    CACHE_KEY = '%d_store'
    
    cache.write(CACHE_KEY % @user.id)
  • Avoid using String#+ when you need 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]

    # good and also fast
    story = ''
    story << 'The Ugly Duckling'
    
    paragraphs.each do |paragraph|
      story << paragraph
    end
  • Use \ at the end of the line instead of + or << to concatenate multi-line strings. [link]

    # bad
    "Some string is really long and " +
      "spans multiple lines."
    
    "Some string is really long and " <<
      "spans multiple lines."
    
    # good
    "Some string is really long and " \
      "spans multiple lines."

Regular Expressions

  • Avoid using $1-9 as it can be hard to track what they contain. Named groups can be used instead. [link]

    # bad
    /(regexp)/ =~ string
    ...
    process $1
    
    # good
    /(?<meaningful_var>regexp)/ =~ string
    ...
    process meaningful_var
  • 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.[link]

    string = "some injection\nusername"
    string[/^username$/]   # matches
    string[/\Ausername\z/] # don'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]

    regexp = %r{
      start         # some text
      \s            # white space char
      (group)       # first group
      (?:alt1|alt2) # some alternation
      end
    }x

Percent Literals

  • Prefer parentheses over curly braces, brackets, or pipes when using %-literal delimiters for consistency, and because the behavior of %-literals is closer to method calls than the alternatives.[link]

    # bad
    %w[date locale]
    %w{date locale}
    %w|date locale|
    
    # good
    %w(date locale)
  • Use %w freely.[link]

    STATES = %w(draft open closed)
  • Use %() for single-line strings which require both interpolation and embedded double-quotes. For multi-line strings, prefer heredocs.[link]

    # bad - no interpolation needed
    %(Welcome, Jane!)
    # should be 'Welcome, Jane!'
    
    # bad - no double-quotes
    %(This is #{quality} style)
    # should be "This is #{quality} style"
    
    # bad - multiple lines
    %(Welcome, Jane!\nPlease enjoy your stay at #{location}\nCheers!)
    # should be a heredoc.
    
    # good - requires interpolation, has quotes, single line
    %(Welcome, #{name}!)
  • Use %r only for regular expressions matching more than one '/' character.[link]

    # bad
    %r(\s+)
    
    # still bad
    %r(^/(.*)$)
    # should be /^\/(.*)$/
    
    # good
    %r(^/blog/2011/(.*)$)
  • Avoid the use of %x unless you're going to invoke a command with backquotes in it (which is rather unlikely). [link]

    # bad
    date = %x(date)
    
    # good
    date = `date`
    echo = %x(echo `date`)

Rails

  • When immediately returning after calling render or redirect_to, put return on the next line, not the same line. [link]

    # bad
    render :text => 'Howdy' and return
    
    # good
    render :text => 'Howdy'
    return
    
    # still bad
    render :text => 'Howdy' and return if foo.present?
    
    # good
    if foo.present?
      render :text => 'Howdy'
      return
    end

Scopes

  • When defining ActiveRecord model scopes, wrap the relation in a lambda. A naked relation forces a database connection to be established at class load time (instance startup). [link]

    # bad
    scope :foo, where(:bar => 1)
    
    # good
    scope :foo, -> { where(:bar => 1) }

Be Consistent

If you're editing code, take a few minutes to look at the code around you and determine its style. If they use spaces around all their arithmetic operators, you should too. If their comments have little boxes of hash marks around them, make your comments have little boxes of hash marks around them too.

The point of having style guidelines is to have a common vocabulary of coding so people can concentrate on what you're saying rather than on how you're saying it. We present global style rules here so people know the vocabulary, but local style is also important. If code you add to a file looks drastically different from the existing code around it, it throws readers out of their rhythm when they go to read it. Avoid this.

Google C++ Style Guide

Translation

This style guide is also available in other languages:

ruby's People

Contributors

aagrawal2001 avatar akuhn avatar artofhuman avatar bmorearty avatar carmi avatar clizzin avatar conzjiang avatar devpuppy avatar jamesreggio avatar josemiguelmelo avatar ljharb avatar lkosak avatar maxjacobson avatar miloprice avatar nicolasleger avatar pariser avatar patbl avatar rahul342 avatar renzominelli avatar robotpistol avatar roysc avatar sethkrasnianski avatar sjdemartini avatar stjj89 avatar tay avatar tommydangerous avatar umofomia avatar venantius avatar vinibrsl avatar zachsabin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ruby's Issues

Typo in Syntax Section

After reading through these Ruby style guidelines, I'm extremely impressed, but there is a minor typo. In the syntax section, it currently says...

Some will argue that multiline chaining would look OK with the use of {...}, but they should ask themselves - it this code really readable and can't the blocks contents be extracted into nifty methods.

It should say...

... but they should ask themselves (a) if this code is readable and (b) if the contents of the block could be extracted into nifty methods.

Or something similar. Hope this change can get merged in soon!

Upgrade to rubocop 0.59.1

Since the version 0.59.1 of rubocop has been released, is there any plan to upgrade to it?

It's not a big issue but I'm still getting some conflicts because of the version 0.57.2 used by rubocop-airbnb.

I'm still a newbie to code but I can help if you tell me what to do to upgrade the dependency version. I guess we should do something like this. As a junior ruby developer, I would be very happy to help.

Rubocop dependency update to make VSC extension play well with it

The current gemspec rubocop dependency is set to ~> 1.32.0 locking rubocop to a version which has been released on July 21, 2022.
Any plans to relax the dependency specification to perhaps '>= 1.56.0'?

The real problem for us is that the official VSC extension for Rubocop requires at least version 1.53.0 of rubocop:

[client]   Version reported by `bundle exec rubocop -v`: 1.32.0 (>= 1.53.0 required)

otherwise it doesn't work and we miss the chance to have Rubocop running directly in the IDE.

Attributes

There is no style advice on attr_accessor, attr_reader, or attr_writer. Can this be added?

License?

Hello,

I'd like to use this style guide as a basis to start our own for my team, but there's no license information and I don't want to step on toes. Can someone please specify how this is made available?

Thanks!

Spanish version

Hi! :D

I've decided to translate this guide into Spanish on behalf of @ruby-ve, greatly inspired by the work of @1c7 on #102

Just one question: Should I translate the fork or is it better to create a new repo for the translation?

Broken link on http://airbnb.io/projects/ruby/

Hullo.

Firstly, thanks for the hard work and sharing your style guides.
http://airbnb.io/projects/ruby/ link to the Google C++ style guide is broken (http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml).

The C++ guide now lives here: https://google.github.io/styleguide/cppguide.html

I know http://airbnb.io/projects/ruby/ isn't governed by this repo but that page links to this repo under the "start a convo" button. I did a bit of digging to find the repo containing the source for airbnb.io but couldn't find it.

Let me know if there's a more appropriate repo for this ticket and again THANKS!

`RuboCop::Cop::Airbnb::ModuleMethodInWrongFile` returns incorrect error message when defining a method inside singleton class

When developing PR #189, tests were changed to use new rspec-rubocop syntax which also checks that the message generated is correct. After doing this, the spec that checks that an error is returned when using "<<" static methods and a non-matching name failed with an incorrect message:

Method baz should be defined in foo/foo.rb

When the expected message was:

Method baz should be defined in foo.rb

After digging a little about it to understand what was happening, we realized that this incorrect message is also present on master – but there are no assertions respecting the offense's text, thus the test succeeds.
The issue seems to be caused by the method normalize_module_name(node.parent_module_name) (on module_method_in_wrong_file.rb#L88) which returns Foo::Foo instead of just Foo.

Allowing symbols when specifying class for Airbnb/ClassName and Airbnb/FactoryClassUseString

Airbnb/ClassName and Airbnb/FactoryClassUseString both work on the idea of not loading the classes. Using a symbol is also a valid way to specify the classes.

# Bad
class: MyClass

# Good
class: "MyClass"

# Also Good
class: :MyClass

Looking into it, it'd be a pretty simple to add the symbol check. I'd be interested in making the PR if this is a use-case you'll allow

Shorten example code

A lot of our sample code blocks are longer than they need to be to get the point across. I think we could do with cleaning these up a bit. The 100-char line limit addressed in #77 is but one example of this.

Discuss.

Rubocop ruleset?

Is there a rubocop ruleset for this coding style similar to your eslint for js?

Link to rationales?

Perhaps a nice way to mitigate the concerns of those who want the guide to be approachable by non-rubyists, without compromising the terseness that some value, would be to add an explicit "Rationale" link to each rule/guideline that links to the pull request where it was discussed?

That would avoid readers having to hunt through git history to figure out why a rule exists.

Another alternative would be to make a separate rationales.md document that was a longer version of the guide, and we could deep link into that from the main readme.

Thoughts?

AutoCorrect Bug

Hi there!

I'm not sure if autocorrect functionality is even supposed to be maintained by this gem as I don't see it in the readme.

However I wanted to open this issue to demonstrate an problem I have been getting when trying to use autocorrect with this gem loaded into my project.

Thanks for your time
-Dan

110 files inspected, 237 offenses detected, 126 offenses corrected
undefined method `range_by_whole_lines' for #<RuboCop::Cop::RSpec::LeadingSubject:0x007fc0d0ae2098>
/Library/Ruby/Gems/2.3.0/gems/rubocop-rspec-1.22.1/lib/rubocop/cop/rspec/leading_subject.rb:65:in `node_range'
/Library/Ruby/Gems/2.3.0/gems/rubocop-rspec-1.22.1/lib/rubocop/cop/rspec/leading_subject.rb:50:in `block in autocorrect'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/cop/corrector.rb:61:in `block (2 levels) in rewrite'
/Library/Ruby/Gems/2.3.0/gems/parser-2.5.1.0/lib/parser/source/tree_rewriter.rb:220:in `transaction'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/cop/corrector.rb:60:in `block in rewrite'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/cop/corrector.rb:58:in `each'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/cop/corrector.rb:58:in `rewrite'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/cop/team.rb:126:in `autocorrect_all_cops'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/cop/team.rb:70:in `autocorrect'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/cop/team.rb:98:in `block in offenses'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/cop/team.rb:115:in `investigate'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/cop/team.rb:94:in `offenses'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/cop/team.rb:44:in `inspect_file'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/runner.rb:265:in `inspect_file'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/runner.rb:212:in `block in do_inspection_loop'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/runner.rb:244:in `block in iterate_until_no_changes'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/runner.rb:237:in `loop'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/runner.rb:237:in `iterate_until_no_changes'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/runner.rb:208:in `do_inspection_loop'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/runner.rb:111:in `block in file_offenses'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/runner.rb:129:in `file_offense_cache'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/runner.rb:109:in `file_offenses'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/runner.rb:100:in `process_file'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/runner.rb:78:in `block in each_inspected_file'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/runner.rb:75:in `each'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/runner.rb:75:in `reduce'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/runner.rb:75:in `each_inspected_file'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/runner.rb:67:in `inspect_files'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/runner.rb:39:in `run'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/cli.rb:160:in `execute_runner'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/cli.rb:88:in `execute_runners'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/lib/rubocop/cli.rb:41:in `run'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/exe/rubocop:13:in `block in <top (required)>'
/System/Library/Frameworks/Ruby.framework/Versions/2.3/usr/lib/ruby/2.3.0/benchmark.rb:308:in `realtime'
/Library/Ruby/Gems/2.3.0/gems/rubocop-0.57.2/exe/rubocop:12:in `<top (required)>'
/usr/local/bin/rubocop:22:in `load'
/usr/local/bin/rubocop:22:in `<main>'

Add guideline about method calls

I'm not sure if this is an intentional omission, so I figured I'd throw it out there.

Ruby has a lot of syntactic sugar around method invocation. These are my rules around the use of that syntax:

  • Use parenthesis...

    • If the method returns a value

      # bad
      @current_user = User.find_by_id 1964192
      
      # good
      @current_user = User.find_by_id(1964192)
    • If the first argument to the method uses a parenthesis

      # bad
      put! (x + y) % len, value
      
      # good
      put!((x + y) % len, value)
  • Omit the parenthesis...

    • If the method accepts no arguments

      # bad
      nil?()
      
      # good
      nil?
    • If the method doesn't return a value (or we don't care about the return)

      # bad
      render(:partial => 'foo')
      
      # good
      render :partial => 'foo'
  • If a method accepts an options hash as the last argument, do not use { } during invocation

    # bad
    get '/v1/reservations', { :id => 54875 }
    
    # good
    get '/v1/reservations', :id => 54875

return from block

The style guide ought to include a note about using return in a block.

From StackOverflow answer

Ruby has three constructs:

  1. A block is not an object and is created by { ... } or do ... end.
  2. A proc is a Proc object created by Proc.new or proc.
  3. A lambda is a Proc created by lambda (or proc in Ruby 1.8).

Ruby has three keywords that return from something:

  1. return terminates the method or lambda it is in.
  2. next terminates the block, proc, or lambda it is in.
  3. break terminates the method that yielded to the block or invoked the proc or lambda it is in.

In lambdas, return behaves like next, for whatever reason. next and break are named the way they are because they are most commonly used with methods like each, where terminating the block will cause the iteration to resume with the next element of the collection, and terminating each will cause you to break out of the loop.

Perhaps the following recommendation:

Avoid return in procs/blocks because return terminates the method calling a proc/block:

def all_even?
  self.numbers.all? do |num| 
    if num % 2 == 0
      # This will cause `all_even?` to return `true` when the first even number is reached
      return true
    else 
      return false
    end
  end
end
def all_even?
  self.numbers.all? do |num| 
    if num % 2 == 0
      # Proceed to the next iteration
      next true
    else 
     # Stop iterating, there is at least one odd number
      break false
    end
  end
end

trailing commas in multi-line method arguments

Which is the preferred style?

# 1
return I18n.t('Guest Profile Page: A note for the Host saying the potential Guest has '\
              'not cancelled a reservation since joining Airbnb.',
              :default => '%{guest_name} hasn’t canceled a reservation since joining '\
                          'Airbnb',
              :guest_name => @user.smart_name)

or

# 2
return I18n.t('Guest Profile Page: A note for the Host saying the potential Guest has '\
              'not cancelled a reservation since joining Airbnb.',
              :default => '%{guest_name} hasn’t canceled a reservation since joining '\
                          'Airbnb',
              :guest_name => @user.smart_name,
)

or

# 3
return I18n.t(
  'Guest Profile Page: A note for the Host saying the potential Guest has '\
  'not cancelled a reservation since joining Airbnb.',
  :default => '%{guest_name} hasn’t canceled a reservation since joining '\
              'Airbnb',
  :guest_name => @user.smart_name,
)

or

# 4
return I18n.t(
  'Guest Profile Page: A note for the Host saying the potential Guest has '\
  'not cancelled a reservation since joining Airbnb.',
  :default => '%{guest_name} hasn’t canceled a reservation since joining '\
              'Airbnb',
  :guest_name => @user.smart_name
)

Could you clarify why there is many disabled cops?

Hello,

first of all thank you to share this project.

I'd like to use the rubocop-airbnb gem in my projects but I'm wondering why there is many Rubocop cops disabled by default.

When I'm looking in the rubocop-airbnb/config folder, I can find 288 disabled cops.

For some of these cops, the disabling reason is documented. But for some others (a majority I guess), this is not the case.

Here are some examples of disabled cops for which I can't find the disabling reason:

  • Metrics/MethodLength
  • Metrics/PerceivedComplexity
  • Naming/ConstantName
  • Naming/MethodName
  • Bundler/OrderedGems

Will all these disabled cops, I'm feeling like loosing some checks that are done by Rubocop by default.

Kind regards,
Stéphane

Link to rule

One of the awesome things about bbatsov's guide is that each rule is accompanied by a link to that particular rule, so that you can easily reference it in a discussion. We should do the same thing here!

Hash symbol key style is unclear

In the Collections section, we have this example:

# bad
hash = { 'one' => 1, 'two' => 2, 'three' => 3 }

# good
hash = { one: 1, two: 2, three: 3 }

The point of this example is to show using symbols as keys instead of strings. This would be a more clear example as:

# bad
hash = { 'one' => 1, 'two' => 2, 'three' => 3 }

# good
hash = { :one => 1, :two => 2, :three => 3 }

Also, the style guide doesn't seem to comment on the preferred style of symbol keys; the Ruby <= 1.8 style vs the Ruby >= 1.9 style:

# bad/good???
hash = { :one => 1, :two => 2, :three => 3 }

# bad/good???
hash = { one: 1, two: 2, three: 3 }

Is it good to use `reverse_merge!` on the optional hash argument of a method?

I found an example in this guide

def obliterate(things, options = {})
  default_options = {
    :gently => true, # obliterate with soft-delete
    :except => [], # skip obliterating these things
    :at => Time.now, # don't obliterate them until later
  }
  options.reverse_merge!(default_options)

  ...
end

I'm wondering whether this is a good practice. When you use this method, the input options hash might be modified by reverse_merge! method, which, in my opinion, is very error-prone process in a big project.

Do not set DisableByDefault

You introduced this setting here but it breaks a lot of things, now even a malformed Ruby file (e.g. missing an end) passes as though it had no issue.

(Sorry for the terse issue, if you can't reproduce I will try to add more details later.)

Constants

Ruby style guide...

Use snake_case for methods and variables.
Use CamelCase for classes and modules. (Keep acronyms like HTTP, RFC, XML uppercase.)
Use SCREAMING_SNAKE_CASE for other constants.

^ This is bad style because it promotes unnecessary use of mutable state, which is a source of future bugs. SCREAMING_SNAKE_CASE is really ugly, so people tend to only use it when they feel obligated to, i.e. global constants. But GoF said that coders should favor immutability over mutability (everywhere), not just for global constants.

SCREAMING_SNAKE_CASE is okay for global constants, but what about all the local constants? I would argue that using SCREAMING_SNAKE_CASE for all the immutable local state would uglify the code and would obfuscate the difference between global immutable state and local immutable state. I would argue for a trailing underscore for local constants (so as to differentiate them from class names).

Use snake_case for methods and variables ( def spectacular_flight, bengal_tiger = :tigger ).
Use Camel_Case
(with trailing underscore) for local constants (Bengal_tiger_ = :tiger).
Use SCREAMING_SNAKE_CASE for global constants._

I would also argue that snake case doesn't work well with the red and blue underlines in the IDE because the underlines make it hard to see the non-trailing underscores, but the important thing is that compared to other programming languages, Ruby has too much mutable state.

The negation operator

When I try to use the negation operator without parens, it doesn't work!
With which ruby version did you try the examples?

Method chaining

Hello, is there a rule regarding method chaining? For example:

person = Person.new

# bad
person.name("Peter").age(21).introduce

# good
person.name("Peter")
      .age(21)
      .introduce

If not, can I make a pull request to add one?

Kali Linux ruby upgrade error

My problem is that

I use Kali Linux and I am trying to use the tool of "beef", however, for this ,I need to upgrade ruby because I see this error

'Ruby version 2.1.5 is no longer supported. Please upgrade to Ruby version 2.2 or later.'
When I check my version of the ruby it is 'ruby 2.1.5p273 (2014-11-13) [i386-linux-gnu]'.

And then I tried respectively

sudo apt-get install ruby2.0

sudo apt-get install ruby-full

apt-get upgrade

apt-get update
But still I could not upgrade it, how can I upgrade, could you help me, please?

Thank you.

config/default.yml does not match Rubocop of the same version

It looks like this file: https://github.com/airbnb/ruby/blob/master/rubocop-airbnb/config/default.yml must be updated. It has not been updated for 3 years even though rubocop dependency has been updated for many times since then.

This gem is dependent on 'rubocop', '~> 0.80.0', but config/default.yml in this repo is not the same as default.yml in rubocop 0.80.0. This is an issue because this file lists all cops and supported styles for them. So if one wants to use a style that was added in 0.80.0, they cannot because of the outdated default.yml. Therefore, the possibilities of configuration are greatly restricted.

Is there a reason why this file is so old?

Support for 2.1 Syntax

Should the style guide mention:

  1. Named parameters
  2. Using the { key: value } rather than the { :key => value } syntax

Ruby 3.1.3

This gem depends on such an outdated version of Rubocop, which is now at 1.39.0, that we're unable to use it with Ruby 3.1.3

Any chance this gem will be updated to support Ruby >= 3.1.3 (and a more recent rubocop version)?

Support ruby 2.6

The gem currently forces rubocop 0.58.0, therefore failing to run with Ruby 2.6, even though rubocop itself supports it:

Error: Unknown Ruby version 2.6 found in `.ruby-version`.
Supported versions: 2.2, 2.3, 2.4, 2.5

Tighten up line length section

I think, in line with the discussion going on in #74, that the current section on line length is probably quite a bit longer than needs to be in the main page. I think it's sufficient to say that lines shouldn't be over 100 characters, and link to a larger section in rationales.md explaining why and providing mitigation strategies. Thoughts?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.