Manual:Coding conventions/Ruby

From MediaWiki.org
Jump to: navigation, search
shortcut: CC/Ruby


This article describes the coding conventions for Ruby files of MediaWiki related codebases.

Unlike Python with its PEP 8, Ruby has no truly canonical style guide to which to reference. There are, however, a few well respected guides that can serve as reference points for Rubyists, most prominently those from GitHub, Heroku, and the community-driven guide from bbatsov.

Starting Point[edit | edit source]

For reasons both public-spirited and practical, we have adopted the bbatsov guide as a starting point; it's the most participatory of the three and comes with the rubocop code analyzer that can be easily customized and integrated into our continuous integration.

Exceptions[edit | edit source]

There are a handful of areas where the bbatsov guide attempts to draw the line with seemingly arbitrary limits or where there's quite a bit of disagreement among commenters. In most of these cases, we've opting to simply ignore the rules and defer in good faith to the developer.

Exceptions and additions to the bbatsov guide are enumerated herein.

Class Length[edit | edit source]

RuboCop ID 
Metrics/ClassLength
Enforced 
no

In general, developers should follow the single responsibility principle, but the bbatsov guide gives no hard-and-fast rule on class length and any attempt at a numeric limit seems arbitrary.[1]

Method Length[edit | edit source]

RuboCop ID 
Metrics/MethodLength
Enforced 
no

In general, developers should follow the single responsibility principle, but the bbatsov guide's 10-line limit seems rather arbitrary.[2]

Method Parameter Limit[edit | edit source]

RuboCop ID 
Metrics/ParameterLists
Enforced 
no

In general, developers should follow the single responsibility principle, but the bbatsov guide's 4-parameter limit seems rather arbitrary.[3]

Line Length[edit | edit source]

RuboCop ID 
Metrics/LineLength
Enforced 
yes, but at a 100 character limit

It's suggested in the bbatsov guide that lines should never exceed 80 characters in length, but there's an ongoing debate over it.[4][5] In fact, statistical analysis of the ten most popular Ruby projects on GitHub shows that a limit of 80 would invalidate more than 8% of existing lines while a more liberal limit of 100 or 120 would only invalid around 3% and 1% respectively.[6] Falling back on our own general coding conventions, 100 lines will be our current limit.

Cyclomatic Complexity[edit | edit source]

RuboCop ID 
Metrics/CyclomaticComplexity
Enforced 
no

In general, developers should follow the single responsibility principle, but the bbatsov guide gives no hard-and-fast rule on complexity and any attempt at a numeric limit seems arbitrary.[7]

Perceived Complexity[edit | edit source]

RuboCop ID 
Metrics/PerceivedComplexity
Enforced 
no

There's nothing in the actual bbatsov guide on this.

Multi-line Method Chaining[edit | edit source]

RuboCop ID 
Style/DotPosition
Enforced 
per project

Two options are presented in the bbatsov guide.[8] We allow either form as long as you stay consistent within each project.

Ok[edit | edit source]

things.map { |thing| thing.age }.
  reduce { |sum, age| sum + age }

Also Ok[edit | edit source]

things.map { |thing| thing.age }
  .reduce { |sum, age| sum + age }

Signaling Exceptions[edit | edit source]

RuboCop ID 
Style/SignalException
Enforced 
no

The bbatsov guide suggests using fail to signal new exceptions and raise to re-raise rescued ones.[9] However, there's some objection to the rule based on the lack of the former in existing Ruby projects or prominent Ruby literature, and the contrived nature of useful semantic differences given as a justification.[10] For now, we don't enforce this rule.

Ok[edit | edit source]

def foo
  # ...
 
  raise "warning!" if error_condition_one?
  raise "oops. something terrible has happened" if error_condition_two?
 
  # ...
end
 
begin
  foo
rescue => exception
  raise unless exception.message.start_with?("warning!")
end

Aliasing Methods[edit | edit source]

RuboCop ID 
Style/Alias
Enforced 
no

The bbatsov guide suggests that alias_method should always be used over alias.[11] However, there are cases where alias can be just as clear or even more appropriate when written alongside methods defined with def.[12]

In short, it's ok to use alias within the body of a class or module definition to alias a method also defined therein, but it should not be used to alias methods defined by a macro; alias_method is more appropriate in that case since it's a macro itself.

Good[edit | edit source]

class List
  attr_reader :length
  alias_method :size, :length
 
  def fold_left(&accumulator)
    # yield left to right (head to tail)
  end
 
  def fold_right(&accumulator)
    # yield right to left (tail to head)
  end
 
  alias fold fold_left
end

Bad[edit | edit source]

class List
  attr_reader :length
  alias size length
 
  def fold_left(&accumulator)
    # yield left to right (head to tail)
  end
 
  def fold_right(&accumulator)
    # yield right to left (tail to head)
  end
 
  alias_method :fold, :fold_left
end

RuboCop[edit | edit source]

A non-voting RuboCop check is enabled for MediaWiki and related repos that contain Ruby code, enforcing the conventions outlined here. In most cases, a configuration file has been initialized using the --auto-gen-config option; the generated config will ignore all violations by default, allowing maintainers to address them one by one.

Ignoring or Customizing RuboCop Rules[edit | edit source]

When you want to ignore a rule or customize its behavior, whether you're going by the recommendations in this guide or not, you'll need to add some configuration to the .rubocop.yml file in the project's root directory (after the inherit_from: entry).

For example, to ignore the Metrics/LineLength rule altogether, you'd add the following.

Metrics/LineLength:
  Enable: false

To customize it to using a more liberal line length limit, say 120, you'd add this.

Metrics/LineLength:
  Max: 120

Please consult the RuboCop documentation for possible rule names and options.

Base Configuration[edit | edit source]

Following is a RuboCop configuration that reflects the exceptions above. Please use it as a starting point for your Ruby projects.

Metrics/AbcSize:
  Enabled: false

Metrics/ClassLength:
  Enabled: false

Metrics/MethodLength:
  Enabled: false

Metrics/ParameterLists:
  Enabled: false

Metrics/LineLength:
  Max: 100

Metrics/CyclomaticComplexity:
  Enabled: false

Metrics/PerceivedComplexity:
  Enabled: false

Metrics/MethodLength:
  Enabled: false

Style/Alias:
  Enabled: false

Style/SignalException:
  Enabled: false

References[edit | edit source]

  1. Single Responsibility Principle, Wikipedia [1]
  2. Single Responsibility Principle, Wikipedia [2]
  3. Single Responsibility Principle, Wikipedia [3]
  4. The Ruby Style Guide, Source Code Layout, bbatsov [4]
  5. The Ruby Style Guide, Issue #207 [5]
  6. The Ruby Style Guide, Issue #207, lee-dohm [6]
  7. Single Responsibility Principle, Wikipedia [7]
  8. The Ruby Style Guide, Multi-line Method Chains, bbatsov [8]
  9. The Ruby Style Guide, Fail Method, bbatsov [9]
  10. The Ruby Style Guide, Issue #233 [10]
  11. The Ruby Style Guide, bbatsov [11]
  12. The Ruby Style Guide, Issue #377 [12]
Conventions
General All languages · Security for developers · Pre-commit checklist · Performance guidelines (draft) · Style guide · Accessibility guide for developers (draft)
PHP Code conventions · PHPUnit test conventions · Security checklist for developers
JavaScript Code conventions · Learning JavaScript
CSS Code conventions
Database Code conventions
Python Code conventions
Ruby Code conventions
Selenium/Cucumber Code conventions