diff --git a/README.md b/README.md index b8af92a1..ab466b18 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,10 @@ If you are using a rubocop version < 1.0.0, you can use rubocop-github version ## Testing -`bundle install` -`bundle exec rake test` +``` bash +bundle install +bundle exec rake test +``` ## The Cops diff --git a/STYLEGUIDE.md b/STYLEGUIDE.md index 829f4b2c..b10d59c3 100644 --- a/STYLEGUIDE.md +++ b/STYLEGUIDE.md @@ -1,37 +1,42 @@ # Ruby Style Guide -* Use soft-tabs with a two space indent. - -* Keep each line of code to a readable length. Unless you have a reason to, keep lines to a maximum of 118 characters. Why 118? That's the width at which the pull request diff UI needs horizontal scrolling (making pull requests harder to review). - -* Never leave trailing whitespace. - -* End each file with a [newline](https://github.com/bbatsov/ruby-style-guide#newline-eof). - -* Use spaces around operators, after commas, colons and semicolons, around `{` - and before `}`. - -``` ruby -sum = 1 + 2 -a, b = 1, 2 -1 > 2 ? true : false; puts "Hi" -[1, 2, 3].each { |e| puts e } -``` - -* No spaces after `(`, `[` or before `]`, `)`. - -``` ruby -some(arg).other -[1, 2, 3].length -``` - -* No spaces after `!`. +This is GitHub's Ruby Style Guide, inspired by [RuboCop's guide][rubocop-guide]. + +## Table of Contents +1. [Layout](#layout) + 1. [Indentation](#indentation) + 2. [Inline](#inline) + 3. [Newlines](#newlines) +2. [Maximum Line Length](#line-length) +3. [Classes](#classes) +4. [Collections](#collections) +5. [Documentation](#documentation) +6. [Dynamic Dispatch](#dynamic-dispatch) +7. [Exceptions](#exceptions) +8. [Hashes](#hashes) +9. [Keyword Arguments](#keyword-arguments) +10. [Naming](#naming) +11. [Percent Literals](#percent-literals) +12. [Regular Expressions](#regular-expressions) +13. [Requires](#requires) +14. [Strings](#strings) +15. [Methods](#methods) + 1. [Method definitions](#method-definitions) + 2. [Method calls](#method-calls) +16. [Conditional Expressions](#conditional-expressions) + 1. [Conditional keywords](#conditional-keywords) + 2. [Ternary operator](#ternary-operator) +17. [Syntax](#syntax) + +## Layout + +### Indentation -``` ruby -!array.include?(element) -``` +* Use soft-tabs with a two space indent. + [[link](#default-indentation)] * Indent `when` with the start of the `case` expression. + [[link](#indent-when-as-start-of-case)] ``` ruby # bad @@ -71,8 +76,45 @@ else end ``` +### Inline + +* Never leave trailing whitespace. + [[link](#trailing-whitespace)] + +* Use spaces around operators, after commas, colons and semicolons, around `{` + and before `}`. + [[link](#spaces-operators)] + +``` ruby +sum = 1 + 2 +a, b = 1, 2 +1 > 2 ? true : false; puts "Hi" +[1, 2, 3].each { |e| puts e } +``` + +* No spaces after `(`, `[` or before `]`, `)`. + [[link](#no-spaces-braces)] + +``` ruby +some(arg).other +[1, 2, 3].length +``` + +* No spaces after `!`. + [[link](#no-spaces-bang)] + +``` ruby +!array.include?(element) +``` + +### Newlines + +* End each file with a [newline](https://github.com/bbatsov/ruby-style-guide#newline-eof). + [[link](#newline-eof)] + * Use empty lines between `def`s and to break up a method into logical paragraphs. + [[link](#empty-lines-def)] ``` ruby def some_method @@ -88,10 +130,16 @@ def some_method end ``` +## Maximum Line Length + +* Keep each line of code to a readable length. Unless you have a reason to, keep lines to a maximum of 118 characters. Why 118? That's the width at which the pull request diff UI needs horizontal scrolling (making pull requests harder to review). + [[link](#line-length)] + ## Classes * Avoid the usage of class (`@@`) variables due to their unusual behavior in inheritance. + [[link](#class-variables)] ``` ruby class Parent @@ -115,6 +163,7 @@ Parent.print_class_var # => will print "child" * Use `def self.method` to define singleton methods. This makes the methods more resistant to refactoring changes. + [[link](#singleton-methods)] ``` ruby class TestClass @@ -131,6 +180,7 @@ class TestClass * Avoid `class << self` except when necessary, e.g. single accessors and aliased attributes. + [[link](#class-method-definitions)] ``` ruby class TestClass @@ -163,6 +213,7 @@ end * Indent the `public`, `protected`, and `private` methods as much the method definitions they apply to. Leave one blank line above them. + [[link](#access-modifier-identation)] ``` ruby class SomeClass @@ -179,6 +230,7 @@ end * Avoid explicit use of `self` as the recipient of internal class or instance messages unless to specify a method shadowed by a variable. + [[link](#self-messages)] ``` ruby class SomeClass @@ -195,6 +247,7 @@ end * Prefer `%w` to the literal array syntax when you need an array of strings. + [[link](#percent-w)] ``` ruby # bad @@ -208,8 +261,10 @@ STATES = %w(draft open closed) 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](#prefer-set)] * Use symbols instead of strings as hash keys. + [[link](#symbols-as-keys)] ``` ruby # bad @@ -222,6 +277,7 @@ hash = { one: 1, two: 2, three: 3 } ## Documentation Use [TomDoc](http://tomdoc.org) to the best of your ability. It's pretty sweet: +[[link](#tomdoc)] ``` ruby # Public: Duplicate some text an arbitrary number of times. @@ -243,6 +299,7 @@ end ## Dynamic Dispatch Avoid calling `send` and its cousins unless you really need it. Metaprogramming can be extremely powerful, but in most cases you can write code that captures your meaning by being explicit: +[[link](#avoid-send)] ``` ruby # avoid @@ -268,6 +325,7 @@ end ## Exceptions * Don't use exceptions for flow of control. + [[link](#exceptions-flow-control)] ``` ruby # bad @@ -286,6 +344,7 @@ end ``` * Rescue specific exceptions, not `StandardError` or its superclasses. + [[link](#specific-exceptions)] ``` ruby # bad @@ -306,6 +365,7 @@ end ## Hashes Use the Ruby 1.9 syntax for hash literals when all the keys are symbols: +[[link](#symbols-as-hash-keys)] ``` ruby # bad @@ -322,6 +382,7 @@ user = { ``` Use the 1.9 syntax when calling a method with Hash options arguments or named arguments: +[[link](#symbols-as-hash-method-arguments)] ``` ruby # bad @@ -334,6 +395,9 @@ link_to("Account", controller: "users", action: "show", id: user) ``` If you have a hash with mixed key types, use the legacy hashrocket style to avoid mixing styles within the same hash: +[[link](#consistent-hash-syntax)] + +``` ruby ``` ruby # bad @@ -352,6 +416,9 @@ hsh = { ## Keyword Arguments [Keyword arguments](http://magazine.rubyist.net/?Ruby200SpecialEn-kwarg) are recommended but not required when a method's arguments may otherwise be opaque or non-obvious when called. Additionally, prefer them over the old "Hash as pseudo-named args" style from pre-2.0 ruby. +[[link](#keyword-arguments)] + +``` ruby So instead of this: ``` ruby @@ -376,23 +443,29 @@ remove_member(user, skip_membership_check: true) ## Naming * Use `snake_case` for methods and variables. + [[link](#snake-case-methods-vars)] * Use `CamelCase` for classes and modules. (Keep acronyms like HTTP, RFC, XML uppercase.) + [[link](#camelcase-classes-modules)] * Use `SCREAMING_SNAKE_CASE` for other constants. + [[link](#screaming-snake-case-constants)] * The names of predicate methods (methods that return a boolean value) should end in a question mark. (i.e. `Array#empty?`). + [[link](#bool-methods-qmark)] * 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 counterpart (method name which does NOT end with !) also exists. + [[link](#dangerous-method-bang)] ## Percent Literals * Use `%w` freely. + [[link](#use-percent-w-freely)] ``` ruby STATES = %w(draft open closed) @@ -400,6 +473,7 @@ 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](#percent-parens-single-line)] ``` ruby # bad (no interpolation needed) @@ -419,6 +493,7 @@ STATES = %w(draft open closed) ``` * Use `%r` only for regular expressions matching *more than* one '/' character. + [[link](#percent-r-regular-expressions)] ``` ruby # bad @@ -436,6 +511,7 @@ STATES = %w(draft open closed) * Avoid using $1-9 as it can be hard to track what they contain. Named groups can be used instead. + [[link](#capture-with-named-groups)] ``` ruby # bad @@ -451,6 +527,7 @@ 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](#regex-begin-end-markers)] ``` ruby string = "some injection\nusername" @@ -460,6 +537,7 @@ 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](#x-modifier-complex-regex)] ``` ruby regexp = %r{ @@ -476,6 +554,7 @@ regexp = %r{ Always `require` dependencies used directly in a script at the start of the same file. Resources that will get autoloaded on first use—such as Rails models, controllers, or helpers—don't need to be required. +[[link](#require-dependencies-directly)] ``` ruby require "set" @@ -491,6 +570,7 @@ documentation about the libraries that the current file uses. ## Strings * Prefer string interpolation instead of string concatenation: + [[link](#string-interpolation)] ``` ruby # bad @@ -503,6 +583,7 @@ email_with_name = "#{user.name} <#{user.email}>" * Use double-quoted strings. Interpolation and escaped characters will always work without a delimiter change, and `'` is a lot more common than `"` in string literals. + [[link](#double-quotes)] ``` ruby # bad @@ -515,6 +596,7 @@ name = "Bozhidar" * 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](#string-concatenation)] ``` ruby # good and also fast @@ -526,10 +608,13 @@ paragraphs.each do |paragraph| end ``` -## Syntax +## Methods + +### Method definitions * Use `def` with parentheses when there are arguments. Omit the parentheses when the method doesn't accept any arguments. + [[link](#method-parens-when-arguments)] ``` ruby def some_method @@ -541,25 +626,30 @@ end end ``` -* 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. +### Method calls -``` ruby -arr = [1, 2, 3] +* If the first argument to a method begins with an open parenthesis, + always use parentheses in the method invocation. For example, write + `f((3 + 2) + 1)`. + [[link](#parens-no-spaces)] + +* Never put a space between a method name and the opening parenthesis. + [[link](#no-spaces-method-parens)] +``` ruby # bad -for elem in arr do - puts elem -end +f (3 + 2) + 1 # good -arr.each { |elem| puts elem } +f(3 + 2) + 1 ``` +## Conditional Expressions + +### Conditional keywords + * Never use `then` for multi-line `if/unless`. + [[link](#no-then-for-multi-line-if-unless)] ``` ruby # bad @@ -573,40 +663,12 @@ if some_condition end ``` -* 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. - -``` 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. - -``` 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 -``` - * The `and` and `or` keywords are banned. It's just not worth it. Always use `&&` and `||` instead. - -* Avoid multi-line `?:` (the ternary operator), use `if/unless` instead. + [[link](#no-and-or-or)] * Favor modifier `if/unless` usage when you have a single-line body. + [[link](#favor-modifier-if-unless)] ``` ruby # bad @@ -619,6 +681,7 @@ do_something if some_condition ``` * Never use `unless` with `else`. Rewrite these with the positive case first. + [[link](#no-else-with-unless)] ``` ruby # bad @@ -637,6 +700,7 @@ end ``` * Don't use parentheses around the condition of an `if/unless/while`. + [[link](#no-parens-if-unless-while)] ``` ruby # bad @@ -650,11 +714,68 @@ 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](#trivial-ternary)] + +``` ruby +# bad +result = if some_condition then something else something_else end + +# good +result = some_condition ? something : something_else +``` + +* Avoid multi-line `?:` (the ternary operator), use `if/unless` instead. + [[link](#no-multiline-ternary)] + +* Use one expression per branch in a ternary operator. This + also means that ternary operators must not be nested. Prefer + `if/else` constructs in these cases. + [[link](#one-expression-per-branch)] + +``` 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 +``` + +## 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](#avoid-for)] + +``` ruby +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](#squiggly-braces)] ``` ruby names = ["Bozhidar", "Steve", "Sarah"] @@ -681,6 +802,7 @@ end.map { |name| name.upcase } nifty methods? * Avoid `return` where not required. + [[link](#avoid-return)] ``` ruby # bad @@ -695,6 +817,7 @@ end ``` * Use spaces around the `=` operator when assigning default values to method parameters: + [[link](#spaces-around-equals)] ``` ruby # bad @@ -712,6 +835,7 @@ While several Ruby books suggest the first style, the second is much more promin in practice (and arguably a bit more readable). * Using the return value of `=` (an assignment) is ok. + [[link](#use-return-value-of-assignment)] ``` ruby # bad @@ -725,6 +849,7 @@ if (v = next_value) == "hello" ... ``` * Use `||=` freely to initialize variables. + [[link](#memoize-away)] ``` ruby # set name to Bozhidar, only if it's nil or false @@ -733,6 +858,7 @@ name ||= "Bozhidar" * Don't use `||=` to initialize boolean variables. (Consider what would happen if the current value happened to be `false`.) + [[link](#no-memoization-for-boolean)] ``` ruby # bad - would set enabled to true even if it was false @@ -746,22 +872,10 @@ enabled = true if enabled.nil? etc. ). They are quite cryptic and their use in anything but one-liner scripts is discouraged. Prefer long form versions such as `$PROGRAM_NAME`. - -* Never put a space between a method name and the opening parenthesis. - -``` ruby -# bad -f (3 + 2) + 1 - -# good -f(3 + 2) + 1 -``` - -* If the first argument to a method begins with an open parenthesis, - always use parentheses in the method invocation. For example, write -`f((3 + 2) + 1)`. + [[link](#no-cryptic-vars)] * Use `_` for unused block parameters. + [[link](#underscore-unused-vars)] ``` ruby # bad @@ -775,5 +889,8 @@ result = hash.map { |_, v| v + 1 } implementation detail to support Ruby features like `case`, and it's not commutative. For example, `String === "hi"` is true and `"hi" === String` is false. Instead, use `is_a?` or `kind_of?` if you must. + [[link](#type-checking-is-a-kind-of)] Refactoring is even better. It's worth looking hard at any code that explicitly checks types. + +[rubocop-guide]: https://github.com/rubocop-hq/ruby-style-guide \ No newline at end of file