diff --git a/CONTRIBUTING-ruRU.md b/CONTRIBUTING-ruRU.md new file mode 100644 index 000000000..bc32eb8c6 --- /dev/null +++ b/CONTRIBUTING-ruRU.md @@ -0,0 +1,36 @@ +# Как сотрудничать в проекте + +Мы очень рады, что вы решили помочь нам в этом проекте! Пожалуйста, +следуйте простым правилам, описанным ниже. + +* Сделайте [личную копию][fork] (fork) этого проекта GitHub'е. + +* Если вы еще не решили, чем вы можете помочь этому проекту, то просмотрите + пометки с тегом `@FIXME` в исходном коде. Возможно, что-то из этих пунктов + покажется вам интересным. + +* Если задуманные изменения велики и вызывают сомнения, инициируйте + [обсуждение][issues] с описанием ваших намерений. + +* Создайте тематическую ветку с говорящим именем: `fb_some_important_fix`. + Не стесняйтесь создать несколько веток для связанных по смыслу изменений: + принять изменение проще, если оно атомарно, и сложно, если оно велико и + противоречиво, когда одна часть приветствуется, а другая не совсем. + +* Ссылки на разделы руководства оформляйте на отдельных строках: +``` + +Пишите комментарии по-английски. +[[ссылка](#english-comments)] +``` + +* Проделайте запланированную работу по исправлению ошибок или переводу в + созданной тематической ветке. Прокомментируйте вашу работу при добавлении. + +* Загрузите вашу ветку на GitHub. + +* Отправье нам [запрос на слияние][pull request], добавьте комментарий к нему. + +[fork]: https://help.github.com/articles/fork-a-repo +[pull request]: https://help.github.com/articles/using-pull-requests +[issues]: https://help.github.com/articles/about-issues diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 000000000..e45808056 --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,4 @@ +# Many thanks to those guys: +* [@piton4eg](https://github.com/piton4eg) +* [@constXife](https://github.com/constXife) +* [@Zloy](https://github.com/Zloy) diff --git a/README-deDE.md b/README-deDE.md new file mode 100644 index 000000000..0d136522d --- /dev/null +++ b/README-deDE.md @@ -0,0 +1,4 @@ +# The Ruby Style Guide: German Edition + +The resource moved to a separate repository: +https://github.com/arbox/de-ruby-style-guide/blob/master/README-deDE.md diff --git a/README-enUS.md b/README-enUS.md new file mode 100644 index 000000000..b3f7b9157 --- /dev/null +++ b/README-enUS.md @@ -0,0 +1,3653 @@ +# Prelude + +> Role models are important.
+> -- Officer Alex J. Murphy / RoboCop + +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, and 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 1.9"][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. + +Many projects have their own coding style guidelines (often derived +from this guide). In the event of any conflicts, such +project-specific guides take precedence for that project. + +You can generate a PDF or an HTML copy of this guide using +[Transmuter][]. + +[RuboCop][] is a code analyzer, 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) +* [French](https://github.com/porecreat/ruby-style-guide/blob/master/README-frFR.md) +* [German](https://github.com/arbox/ruby-style-guide/blob/master/README-deDE.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](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/scrum2b/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) +* [Classes](#classes--modules) +* [Exceptions](#exceptions) +* [Collections](#collections) +* [Strings](#strings) +* [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/OS X 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, around `{` + and before `}`. 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 + [1, 2, 3].each { |e| puts e } + class FooError < StandardError; end + ``` + + The only exception, regarding operators, is the exponent operator: + + ```Ruby + # bad + e = M * c ** 2 + + # good + e = M * c**2 + ``` + + `{` 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. + + ```Ruby + # good - space after { and before } + { one: 1, two: 2 } + + # good - no space after { and before } + {one: 1, two: 2} + ``` + + 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. + +* + No spaces after `(`, `[` or before `]`, `)`. +[[link](#no-spaces-braces)] + + ```Ruby + some(arg).other + [1, 2, 3].size + ``` + +* + 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`. I know that many would disagree + with this one, but it's the style established in both "The Ruby + Programming Language" and "Programming Ruby". +[[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 a method + 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 + ``` + +* + 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/bbatsov/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 = ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', + 'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam'] + + # good + menu_item = [ + 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', + 'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam' + ] + + # good + menu_item = + ['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 + ``` + +* + Use RDoc and its conventions for API documentation. Don't put an + empty line between the comment block and the `def`. +[[link](#rdoc-conventions)] + +* + 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() + ``` + +* + 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 + ``` + +* + 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 - braces are required because of op precedence + x = (not something) + + # good + x = !something + ``` + +* + Avoid the use of `!!`. +[[link](#no-bang-bang)] + + ```Ruby + # bad + x = 'test' + # obscure nil check + if !!x + # body omitted + end + + x = false + # double negation is useless on booleans + !!x # => false + + # good + x = 'test' + unless x.nil? + # body omitted + end + ``` + +* + The `and` and `or` keywords are banned. It's just not worth it. Always use + `&&` and `||` instead. +[[link](#no-and-or-or)] + + ```Ruby + # bad + # boolean expression + if some_condition and some_other_condition + do_something + end + + # control flow + document.saved? or document.save! + + # good + # boolean expression + if some_condition && some_other_condition + do_something + end + + # control flow + document.saved? || document.save! + ``` + +* + 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 + ``` + +* + 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 an `if/unless/while/until`. +[[link](#no-parens-if)] + + ```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 parentheses around parameters for methods that are part of an internal + DSL (e.g. Rake, Rails, RSpec), methods that have "keyword" status in Ruby + (e.g. `attr_reader`, `puts`) and attribute access methods. Use parentheses + around the arguments of all other method invocations. +[[link](#no-dsl-parens)] + + ```Ruby + class Person + attr_reader :name, :age + + # omitted + end + + temperance = Person.new('Temperance', 30) + temperance.name + + puts temperance.age + + x = Math.sin(y) + array.delete(e) + + bowling.score.should == 0 + ``` + +* + 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 + ``` + +* + Omit parentheses for method calls with no arguments. +[[link](#no-args-no-parens)] + + ```Ruby + # bad + Kernel.exit!() + 2.even?() + fork() + 'test'.upcase() + + # good + Kernel.exit! + 2.even? + fork + 'test'.upcase + ``` + +* + 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 (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](#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 multiline 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.) +[[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) + ... + end + + # good (MRI would still complain, but RuboCop won't) + if (v = array.grep(/foo/)) + do_something(v) + ... + end + + # good + v = array.grep(/foo/) + if v + do_something(v) + ... + 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 =~ /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 Fixnum 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 + ``` + +* + 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-as-args)] + +* + 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 + ``` + +* + 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) + # ... + end + + # good + result = hash.map { |_k, v| v + 1 } + + def something(x) + _unused_var, used_var = something_else(x) + # ... + end + + # good + result = hash.map { |_, v| v + 1 } + + def something(x) + _, used_var = something_else(x) + # ... + 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('%{first} %{second}', first: 20, second: 10) + # => '20 10' + + format('%d %d', 20, 10) + # => '20 10' + + # good + format('%{first} %{second}', first: 20, second: 10) + # => '20 10' + ``` + +* + Favor the use of `Array#join` over the fairly cryptic `Array#*` with +[[link](#array-join)] + a string argument. + + ```Ruby + # bad + %w(one two three) * ', ' + # => 'one, two, three' + + # good + %w(one two three).join(', ') + # => 'one, two, three' + ``` + +* + Use `[*var]` or `Array()` instead of explicit `Array` check, when dealing + with a variable you want to treat as an Array, but you're not certain it's an + array. +[[link](#splat-arrays)] + + ```Ruby + # bad + paths = [paths] unless paths.is_a? Array + paths.each { |path| do_something(path) } + + # good + [*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) + 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 + ... + end + + def SomeMethod + ... + end + + # good + :some_symbol + + def some_method + ... + end + ``` + +* + Use `CamelCase` for classes and modules. (Keep acronyms like HTTP, RFC, XML + uppercase.) +[[link](#camelcase-classes)] + + ```Ruby + # bad + class Someclass + ... + end + + class Some_Class + ... + end + + class SomeXml + ... + end + + # good + class SomeClass + ... + end + + class SomeXML + ... + 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)] + +* + 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 using `reduce` with short blocks, name the arguments `|a, e|` + (accumulator, element). +[[link](#reduce-blocks)] + +* + When defining binary operators, name the parameter `other`(`<<` and `[]` are + exceptions to the rule, since their semantics are different). +[[link](#other-arg)] + + ```Ruby + def +(other) + # 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](http://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.
+> -- Russ Olsen + +* + 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)] + +## 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 + CustomErrorKlass = 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 + ``` + +* + 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 + ``` + +* + 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 + end + end + + # good + module SomeModule + module_function + + def some_method + # body omitted + end + + def some_other_method + 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](http://en.wikipedia.org/wiki/Liskov_substitution_principle). +[[link](#liskov)] + +* + Try to make your classes as + [SOLID](http://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 + ``` + +* + Avoid the use of `attr`. Use `attr_reader` and `attr_accessor` instead. +[[link](#attr)] + + ```Ruby + # bad - creates a single attribute accessor (deprecated in 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](http://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 + # ... + end + + private + + def private_method + # ... + end + + def another_private_method + # ... + end + end + ``` + +* + Use `def self.method` to define singleton methods. This makes the code + easier to refactor since the class name is not repeated. +[[link](#def-self-singletons)] + + ```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 singleton 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 + ``` + +## Exceptions + +* + Signal exceptions using the `fail` method. Use `raise` only when catching an + exception and re-raising it (because here you're not failing, but explicitly + and purposefully raising an exception). +[[link](#fail-method)] + + ```Ruby + begin + fail 'Oops' + rescue => error + raise if error.message != 'Oops' + end + ``` + +* + Don't specify `RuntimeError` explicitly in the two argument version of + `fail/raise`. +[[link](#no-explicit-runtimeerror)] + + ```Ruby + # bad + fail RuntimeError, 'message' + + # good - signals a RuntimeError by default + fail 'message' + ``` + +* + Prefer supplying an exception class and a message as two separate arguments + to `fail/raise`, instead of an exception instance. +[[link](#exception-class-messages)] + + ```Ruby + # bad + fail SomeException.new('message') + # Note that there is no way to do `fail SomeException.new('message'), backtrace`. + + # good + fail SomeException, 'message' + # Consistent with `fail 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 + def foo + fail + 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 Exception => e + # some handling + rescue StandardError => e + # some handling that will never be executed + end + + # good + begin + # some code + rescue StandardError => e + # some handling + rescue Exception => 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') + # ... + f.close + + # good - the file descriptor is closed automatically + File.open('testfile') do |f| + # ... + end + ``` + +* + Favor the use of exceptions for 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 = [] + hash = {} + ``` + +* + 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?`. As noted + [here](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/43765) by + Matz, the longer forms are considered deprecated. +[[link](#hash-key)] + + ```Ruby + # bad + hash.has_key?(:test) + hash.has_value?(value) + + # good + hash.key?(:test) + hash.value?(value) + ``` + +* + 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 work 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, get_batman_powers) # get_batman_powers is an expensive call + + # good - blocks are lazy evaluated, so only triggered in case of KeyError exception + batman.fetch(:powers) { get_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 + ``` + +## 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) + ``` + +* + With interpolated expressions, there should be no padded-spacing inside the braces. +[[link](#pad-string-interpolation)] + + ```Ruby + # bad + "From: #{ user.first_name }, #{ user.last_name }" + + # good + "From: #{user.first_name}, #{user.last_name}" + ``` + +* + 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" + + # good + name = 'Bozhidar' + ``` + + * **(Option B)** Prefer double-quotes unless your string literal + contains `"` or escape characters you want to suppress. + + ```Ruby + # bad + name = 'Bozhidar' + + # good + name = "Bozhidar" + ``` + + 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 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 + # 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 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" + ``` + +## 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 captured result of parentheses. +[[link](#non-capturing-regexp)] + + ```Ruby + /(first|second)/ # bad + /(?:first|second)/ # good + ``` + +* + 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 + ... + process Regexp.last_match(1) + + # good + /(?regexp)/ =~ string + ... + 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 block or hash. +[[link](#gsub-blocks)] + +## 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 `%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?"' + ``` + +* + 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)] + +* + Prefer `()` as delimiters for all `%` literals, except `%r`. Since parentheses + often appear inside regular expressions in many scenarios a less common + character like `{` might be a better choice for a delimiter, depending on the + regexp's content. +[[link](#percent-literal-braces)] + + ```Ruby + # bad + %w[one two three] + %q{"Test's king!", John said.} + + # good + %w(one two three) + %q("Test's king!", John said.) + ``` + +## 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. - when you use the string-interpolated form, always supply `__FILE__` + and `__LINE__`, so that your backtraces make sense: +[[link](#block-class-eval)] + + ```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)] + +## 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)] + +* + Prefer `Time.now` over `Time.new` when retrieving the current system time. +[[link](#time-now)] + +* + Code in a functional way, avoiding mutation when that makes sense. +[[link](#functional-code)] + +* + 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's some tools to help you automatically check Ruby code against +this guide. + +### RuboCop + +[RuboCop][] is a Ruby code style +checker based on this style guide. RuboCop already covers a +significant portion of the Guide, supports both MRI 1.9 and MRI 2.0 +and has good Emacs integration. + +### 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 [gittip](https://www.gittip.com/bbatsov). + +[![Support via Gittip](https://rawgithub.com/twolfson/gittip-badge/0.2.0/dist/gittip.png)](https://www.gittip.com/bbatsov) + +## How to Contribute? + +It's easy, just follow the [contribution guidelines](https://github.com/bbatsov/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]: http://www.python.org/dev/peps/pep-0008/ +[rails-style-guide]: https://github.com/bbatsov/rails-style-guide +[pickaxe]: http://pragprog.com/book/ruby4/programming-ruby-1-9-2-0 +[trpl]: http://www.amazon.com/Ruby-Programming-Language-David-Flanagan/dp/0596516177 +[transmuter]: https://github.com/TechnoGate/transmuter +[RuboCop]: https://github.com/bbatsov/rubocop diff --git a/README-ruRU.md b/README-ruRU.md new file mode 100644 index 000000000..851b1bf5a --- /dev/null +++ b/README-ruRU.md @@ -0,0 +1,3557 @@ +# Вступление + +> Ролевые модели важны.
+> -- Офицер Алекс Мёрфи / Робот-полицейский + +Один из вопросов, который меня всегда беспокоил как разработчика на Руби, +— это то, что у разработчиков на Питоне есть великолепное руководствo +по стилю оформления ([PEP-8][]), а у нас +никогда не было официального руководства, описывавшего бы стиль оформления кода +на Руби и дающего примеры его успешного применения. Я же уверен, что стиль +оформления крайне важен. Также я верю, что такое замечательное сообщество +разработчиков, которое есть у Руби, вполне имеет силы создать этот давно +назревший документ. + +Это наставление появилось на свет в нашей фирме в виде внутреннего руководства +по оформлению кода на Руби (составленного вашим покорным слугой). И в какой-то +момент я решил, что данная работа, которой я тогда занимался, может быть +интересной и другим членам сообщества программистов на Руби и что миру вовсе +не нужно еще одно руководство для внутреннего пользования: окружающий мир может +получить пользу от совместно создаваемого и одобренного сообществом набора +практик, идиом и стилистических предписаний для программирования на Руби. + +Со времени опубликования этого руководства я получил многочисленные отклики +от членов сообщества программистов на Руби из разных уголков со всего мира. Я +очень благодарен им за полезные предложения и поддержку! Нашими общими усилиями +мы сможем сделать этот ресурс полезным для всех и каждого разработчика на Руби. + +И кстати, если вы работаете с Rails, вы можете взглянуть на дополняющее это +руководство [Ruby on Rails 3 & 4: Руководство по стилю оформления][rails-style-guide]. + +# Руби: руководство по стилю оформления + +Это руководство по оформлению кода на Руби дает передовые рекомендации, так что +обычный программист на Руби сможет создавать код, который с легкостью смогут +поддерживать другие обычные программисты на Руби. Руководство по оформлению, +которое отражает повседневную практику, будет применяться постоянно, а руководство, +стремящееся к идеалу, который не принимается обычными людьми, подвергается риску +вообще быть забытым — не важно, насколько хорошим оно является. + +Данное руководство разделено на несколько частей, состоящий из связанных по смыслу +правил. В каждом случае я попытался обосновать появление этих правил (объяснение +опущено в ситуациях, когда я посчитал его очевидным). + +Все эти правила не появились из пустоты, они по большей части основываются на моем +собственном обширном профессиональном опыте в качестве разработчика ПО, отзывах +и предложениях других членов сообщества программистов на Руби и различных +общепризнанных источниках по программированию на Руби, например, +["Programming Ruby 1.9"][pickaxe] и ["Язык программирования Ruby"][trpl] +(в оригинале ["The Ruby Programming Language"][entrpl]). + +Во многих областях до сих пор нет единого мнения в среде разработчиков на Руби +относительно конкретных аспектов стиля оформления (например, оформление строк +в кавычках, пробелы при оформлении хешей, месторасположение точки при +многострочном последовательном вызове методов и т.д.). В таких ситуациях мы +рассматривали все распространенные стили, вам же решать, какой из этих стилей +вы будете применять последовательно в вашем коде. + +Это руководство все еще находится в процессе создания: у многих правил нет +примеров, у других нет примеров, достаточно ясно объясняющих эти правила. В свое +время каждое правило найдет свое объяснение, а пока просто примите их к сведению. + +Вы можете создать копию этого руководства в форматах PDF или HTML при помощи +[Transmuter][]. + +[RuboCop][] — это анализатор кода, основывающийся на правилах этого +руководства по оформлению. + +Переводы данного руководства доступны на следующих языках: + +* [английский (исходная версия)](https://github.com/bbatsov/ruby-style-guide/blob/master/README.md) +* [вьетнамский](https://github.com/scrum2b/ruby-style-guide/blob/master/README-viVN.md) +* [испанский](https://github.com/alemohamad/ruby-style-guide/blob/master/README-esLA.md) +* [китайский традиционный](https://github.com/JuanitoFatas/ruby-style-guide/blob/master/README-zhTW.md) +* [китайский упрощенный](https://github.com/JuanitoFatas/ruby-style-guide/blob/master/README-zhCN.md) +* [корейский](https://github.com/dalzony/ruby-style-guide/blob/master/README-koKR.md) +* [немецкий](https://github.com/arbox/ruby-style-guide/blob/master/README-deDE.md) +* [французский](https://github.com/porecreat/ruby-style-guide/blob/master/README-frFR.md) +* [португальский](https://github.com/rubensmabueno/ruby-style-guide/blob/master/README-PT-BR.md) +* [русский (данный документ)](https://github.com/arbox/ruby-style-guide/blob/master/README-ruRU.md) +* [японский](https://github.com/fortissimo1997/ruby-style-guide/blob/japanese/README.ja.md) + +## Оглавление + +* [Организация исходного кода](#Организация-исходного-кода) +* [Синтаксис](#Синтаксис) +* [Наименование](#Hаименование) +* [Комментарии](#Комментарии) + * [Пометки в комментариях](#Пометки-в-комментариях) +* [Классы и модули](#Классы-и-модули) +* [Исключения](#Исключения) +* [Коллекции](#Коллекции) +* [Строки](#Строки) +* [Регулярные выражения](#Регулярные-выражения) +* [Процентные литералы](#Процентные-литералы) +* [Метапрограммирование](#Метапрограммирование) +* [Разное](#Разное) +* [Инструментарий](#Инструментарий) + +## Организация исходного кода + +> Почти все убеждены, что любой стиль кроме их собственного ужасен и нечитаем. +> Уберите отсюда "кроме их собственного" — и они будут, наверное, правы...
+> -- Джерри Коффин (Jerry Coffin) об отступах + +* Используйте `UTF-8` в качестве кодировки для исходного + кода.[[ссылка](#utf-8)] + +* Используйте два **пробела** на уровень + отступа (т.е. мягкую табуляцию). Никаких знаков табуляции. + [[ссылка](#spaces-indentation)] + + ```Ruby + # плохо (четыре пробела) + def some_method + do_something + end + + # хорошо + def some_method + do_something + end + ``` + +* Используйте стиль Unix для строк (пользователи + *BSD/Solaris/Linux/OS X используют их по умолчанию, пользователям Windows + нужно обратить особое внимание).[[ссылка](#crlf)] + + * Если вы используете Git, вы можете добавить следующие настройки + в вашу конфигурацию, чтобы предотвратить ненамеренное проникновение в ваш + код строк, оканчивающихся в стиле Windows: + + ```bash + $ git config --global core.autocrlf true + ``` + +* Не используйте `;` для разделения директив и + выражений. Отсюда непосредсвенно следует, что каждая директива должна занимать + свою отдельную строку.[[ссылка](#no-semicolon)] + + ```Ruby + # плохо (точка с запятой избыточна) + puts 'foobar'; + + puts 'foo'; puts 'bar' # две директивы на одной строке + + # хорошо + puts 'foobar' + + puts 'foo' + puts 'bar' + + puts 'foo', 'bar' # это частное правило для `puts` + ``` + +* Используйте преимущественно однострочный + формат для определений классов с пустым телом. + [[ссылка](#single-line-classes)] + + ```Ruby + # плохо + class FooError < StandardError + end + + # сносно + class FooError < StandardError; end + + # хорошо + FooError = Class.new(StandardError) + ``` + +* Избегайте однострочных методов. И хотя + они достаточно популярны в среде программистов, существует множество + неприятных мелочей, связанных с синтаксисом их определения, которые делают + применение таких методов нежелательным. В любом случае однострочные методы не + должны содержать больше одного выражения. + [[ссылка](#no-single-line-methods)] + + + ```Ruby + # плохо + def too_much; something; something_else; end + + # сносно (обратите внимание, что первая `;` обязательна) + def no_braces_method; body end + + # сносно (обратите внимание, что вторая `;` опциональна) + def no_braces_method; body; end + + # сносно (корректный синтаксис, но отсутствие `;` создает трудности при прочтении) + def some_method() body end + + # хорошо + def some_method + body + end + ``` + + Одним исключением в этом правиле являются методы с пустым телом. + + ```Ruby + # хорошо + def no_op; end + ``` + +* Вставляйте пробелы вокруг операторов, после + запятых, двоеточий и точек с запятыми, вокруг `{` и перед `}`. + Пробелы (по большей части) игнорируются интерпретатором Руби, но + их правильное использование является ключом к написанию легко читаемого кода. + [[ссылка](#spaces-operators)] + + ```Ruby + sum = 1 + 2 + a, b = 1, 2 + [1, 2, 3].each { |e| puts e } + class FooError < StandardError; end + ``` + + Единственным исключением для операторов является оператор степени: + + ```Ruby + # плохо + e = M * c ** 2 + + # хорошо + e = M * c**2 + ``` + + `{` и `}` заслуживают некоторого пояснения, так как они используются + для блоков и для литералов хешей, а также для интерполяции строк. + + Для литералов хешей два стиля являются общепринятыми: + + ```Ruby + # хорошо (пробел после { и до }) + { one: 1, two: 2 } + + # хорошо (пробелы отсутствуют после { и перед }) + {one: 1, two: 2} + ``` + + Первый вариант несколько проще для чтения и, по всей вероятности, + более распространен среди членов сообщества программистов на Руби. + Второй вариант имеет преимущество в том, что создается видимое различие + между блоками и литералами хешей. Какой бы стиль вы ни выбрали, применяйте + его единообразно. + +* Не используйте пробел после `(`, `[` или перед + `]`, `)`.[[ссылка](#no-spaces-braces)] + + ```Ruby + some(arg).other + [1, 2, 3].size + ``` + +* Не используйте пробел после `!`. + [[ссылка](#no-space-bang)] + + ```Ruby + # плохо + ! something + + # хорошо + !something + ``` +* Записывайте литералы диапазонов + без пробелов.[[link](#no-space-inside-range-literals)] + + ```Ruby + # плохо + 1 .. 3 + 'a' ... 'z' + + # хорошо + 1..3 + 'a'...'z' + ``` + +* Делайте отступ для `when` таким же, как и + для `case`. Я знаю, что многие не согласятся с этим, но этот стиль + предписывается как "Языком программирования Ruby", так и "Programming Ruby". + [[ссылка](#indent-when-to-case)] + + ```Ruby + # плохо + 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 + + # хорошо + 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 + ``` + +* Присваивая результат условного + выражения переменной, сохраняйте соответствие уровней отступа. + [[ссылка](#indent-conditional-assignment)] + + ```Ruby + # плохо (слишком запутано) + 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 + + # хорошо (намерения очевидны) + 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 + + # хорошо (и не так расточительно) + kind = + case year + when 1850..1889 then 'Blues' + when 1890..1909 then 'Ragtime' + when 1910..1929 then 'New Orleans Jazz' + when 1930..1939 then 'Swing' + when 1940..1950 then 'Bebop' + else 'Jazz' + end + + result = + if some_cond + calc_something + else + calc_something_else + end + ``` + +* Используйте пустые строки для + разделения определений методов и выделения логических частей определений + внутри них.[[ссылка](#empty-lines-between-methods)] + + ```Ruby + def some_method + data = initialize(options) + + data.manipulate! + + data.result + end + + def some_method + result + end + ``` + + +* Избегайте запятых после последнего + параметра в вызове метода, особенно когда параметры расположены в отдельных + строках.[[ссылка](#no-trailing-params-comma)] + + + ```Ruby + # плохо, хотя проще перемещать/добавлять/удалять строки + some_method( + size, + count, + color, + ) + + # плохо + some_method(size, count, color, ) + + # хорошо + some_method(size, count, color) + ``` + +* Вставляйте пробелы вокруг оператора + присваивания `=`, когда назначаете параметрам метода значения по умолчанию: + [[ссылка](#spaces-around-equals)] + + + ```Ruby + # плохо + def some_method(arg1=:default, arg2=nil, arg3=[]) + # do something... + end + + # хорошо + def some_method(arg1 = :default, arg2 = nil, arg3 = []) + # do something... + end + ``` + + Хотя в некоторых книгах по Ruby рекомендуют первый стиль, второй + гораздо более нагляден. + +* Не используйте символ продления строк `\` + везде, где можно обойтись без него. Практически не используйте его нигде, + кроме как при конкатенации строк.[[ссылка](#no-trailing-backslash)] + + + ```Ruby + # плохо + result = 1 - \ + 2 + + # возможно (но ужасно) + result = 1 \ + - 2 + + long_string = 'First part of the long string' \ + ' and second part of the long string' + ``` + +* Используйте единый стиль + многострочных последовательных цепочек вызовов методов. В сообществе Руби + популярны два взаимоисключающих стиля их оформления: с точкой в начале + (вариант A) и с точкой в конце (вариант B). + [[ссылка](#consistent-multi-line-chains)] + + * **A** Когда продолжаете цепочку вызовов методов на + следующую строку, начинайте её с точки. + + ```Ruby + # плохо - нужно посмотреть на предыдущую строку, чтобы понять + # смысл последующей + one.two.three. + four + + # хорошо - сразу ясно, что происходит во второй строке + one.two.three + .four + ``` + + * **B** Соответственно, наоборот, когда продолжаете цепочку + вызовов на следующей строке, завершайте строку точкой `.`, давая + понять, что продолжение выражения следует + + ```Ruby + # плохо - чтобы понять, что выражение не окончено, необходимо + # посмотреть на следующую строку. + one.two.three + .four + + # хорошо - сразу видно, что выражение будет продолжено на + # следующей строке + one.two.three. + four + ``` + + C аргументами за и против обоих стилей можно ознакомиться в дискуссии + [здесь](https://github.com/bbatsov/ruby-style-guide/pull/176). + +* Выравнивайте параметры вызова метода, если + вызов занимает более одной строки. Если выравнивание невозможно из-за + ограничений на длину строки, то используйте одинарный отступ. + [[ссылка](#no-double-indent)] + + ```Ruby + # первоначальный вариант (строка слишком длинная) + def send_mail(source) + Mailer.deliver(to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: source.text) + end + + # плохо (двойной отступ) + def send_mail(source) + Mailer.deliver( + to: 'bob@example.com', + from: 'us@example.com', + subject: 'Important message', + body: source.text) + end + + # хорошо + def send_mail(source) + Mailer.deliver(to: 'bob@example.com', + from: 'us@example.com', + subject: 'Important message', + body: source.text) + end + + # хорошо (одинарный отступ) + def send_mail(source) + Mailer.deliver( + to: 'bob@example.com', + from: 'us@example.com', + subject: 'Important message', + body: source.text + ) + end + ``` + +* Выравнивайте элементы литералов массива, + если они занимают несколько строк.[[ссылка](#align-multiline-arrays)] + + ```Ruby + # плохо + menu_item = ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', + 'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam'] + + # хорошо + menu_item = [ + 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', + 'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam' + ] + + # хорошо + menu_item = + ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', + 'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam'] + ``` + +* Добавляйте символ подчеркивания + в большие числовые константы для улучшения их восприятия. + [[ссылка](#underscores-in-numerics)] + + ```Ruby + # плохо (Сколько тут нолей?) + num = 1000000 + + # хорошо (число воспринимается гораздо легче) + num = 1_000_000 + ``` + +* Используйте устоявшиеся правила RDoc + для описания интерфейсов. Не отделяйте блок комментария от начала определения + метода `def` пустой строкой.[[ссылка](#rdoc-conventions)] + +* Ограничивайте длину строк 80-ю + символами.[[ссылка](#80-character-limits)] + +* Не оставляйте пробелы в конце строки. + [[ссылка](#no-trailing-whitespace)] + +* Завершайте каждый файл переводом строки. + [[ссылка](#newline-eof)] + +* Не пользуйтесь блочными комментариями. Их + нельзя разместить на необходимом уровне отступа. К тому же их сложнее + воспринимать, чем обычные комментарии.[[ссылка](#no-block-comments)] + + ```Ruby + # плохо + =begin + строка комментария + еще одна строка комментария + =end + + # хорошо + # строка комментария + # другая строка комментария + ``` + +## Синтаксис + +* + Используйте `::` только для обращения к константам (в том числе к классам и + модулям) и конструкторам класса (например, `Array()` или `Nokogiri::HTML()`). + Никогда не используйте `::` для обычного вызова методов. + [[ссылка](#double-colons)] + + ```Ruby + # плохо + SomeClass::some_method + some_object::some_method + + # хорошо + SomeClass.some_method + some_object.some_method + SomeModule::SomeClass::SOME_CONST + SomeModule::SomeClass() + ``` + +* + Используйте `def` со скобками, когда у метода есть параметры. Опускайте + скобки, когда метод не принимает параметров. + [[ссылка](#method-parens)] + + ```Ruby + # плохо + def some_method() + # некоторый код + end + + # хорошо + def some_method + # некоторый код + end + + # плохо + def some_method_with_parameters param1, param2 + # некоторый код + end + + # хорошо + def some_method_with_parameters(param1, param2) + # некоторый код + end + ``` + +* Используйте оператор `for` только в случаях, когда + вы точно знаете, зачем вы это делаете. В подавляющем большинстве остальных случаев + стоит применять итераторы. Оператор `for` реализуется при помощи `each` (таким + образом вы добавляете еще один уровень абстракции), но с некоторыми отличиями: + не создается отдельная область видимости (в отличии от `each`) и переменные, + объявленные в теле `for`, будут видны за пределами блока. + [[ссылка](#no-for-loops)] + + ```Ruby + arr = [1, 2, 3] + + # плохо + for elem in arr do + puts elem + end + + # Учтите, elem доступен за пределами цикла + elem #=> 3 + + # хорошо + arr.each { |elem| puts elem } + + # elem недоступен за пределами блока each + elem #=> NameError: undefined local variable or method `elem' + ``` + +* Не используйте `then` для условий `if/unless`, + объявленных на нескольких строках.[[ссылка](#no-then)] + + ```Ruby + # плохо + if some_condition then + # некоторое действие + end + + # хорошо + if some_condition + # некоторое действие + end + ``` + + +* Всегда записывайте условие для `if/unless` + на той же строке, что содержит `if/then` в многострочном условии. + [[ссылка](#same-line-condition)] + + ```Ruby + # плохо + if + some_condition + do_something + do_something_else + end + + # хорошо + if some_condition + do_something + do_something_else + end + ``` + +* Предпочитайте тернарный оператор (`?:`) + конструкциям с `if/then/else/end`. Он используется чаще и по определению + более краток.[[ссылка](#ternary-operator)] + + ```Ruby + # плохо + result = if some_condition then something else something_else end + + # хорошо + result = some_condition ? something : something_else + ``` + +* Используйте только одно выражение в каждой + ветви тернарного оператора. Отсюда следует, что лучше избегать вложенных + тернарных операторов. При возникновении такой необходимости применяйте + конструкции с `if/else`.[[ссылка](#no-nested-ternary)] + + ```Ruby + # плохо + some_condition ? (nested_condition ? nested_something : nested_something_else) : something_else + + # хорошо + if some_condition + nested_condition ? nested_something : nested_something_else + else + something_else + end + ``` + +* + Не используйте `if x: ...`, в Руби 1.9 эту синтаксическую конструкцию удалили, + используйте вместо нее тернарные операторы. + [[ссылка](#no-1.8-if-syntax)] + + ```Ruby + # плохо + result = if some_condition: something else something_else end + + # хорошо + result = some_condition ? something : something_else + ``` + +* + Не используйте точку с запятой в `if x; ...`. Применяйте тернарные операторы. + [[ссылка](#no-semicolon-ifs)] + +* + Извлекайте пользу из такого факта, что `if` и `case` являются выражениями, + возвращающими результирующие значения. + [[ссылка](#use-if-case-returns)] + + ```Ruby + # плохо + if condition + result = x + else + result = y + end + + # хорошо + result = + if condition + x + else + y + end + ``` + +* Применяйте `when x then ...` для однострочных + выражений. Вариант записи `when x: ...` был удален, начиная с Руби 1.9. + [[ссылка](#one-line-cases)] + +* Не используйте `when x; ...` по аналогии + с предыдущим правилом.[[ссылка](#no-when-semicolons)] + +* Используйте `!` вместо `not`. + [[ссылка](#bang-not-not)] + + ```Ruby + # плохо (необходимы скобки из-за неоднозначности приоритетов операторов) + x = (not something) + + # хорошо + x = !something + ``` + +* Не используйте `!!`. + [[ссылка](#no-bang-bang)] + + ```Ruby + # плохо + x = 'test' + # неявная проверка на nil + if !!x + # некоторое выражение + end + + x = false + # двойное отрицание бессмысленно для булевых значений + !!x # => false + + # хорошо + x = 'test' + unless x.nil? + # некоторое выражение + end + ``` + +* Ключевые слова `and` и `or` следует забыть. Они + не несут дополнительной пользы. Всегда используйте `&&` и `||` вместо них. + [[ссылка](#no-and-or-or)] + + ```Ruby + # плохо + # булево выражение + if some_condition and some_other_condition + do_something + end + + # управление потоком исполнения + document.saved? or document.save! + + # хорошо + # булево выражение + if some_condition && some_other_condition + do_something + end + + # управление потоком исполнения + document.saved? || document.save! + ``` + +* Избегайте многострочных тернарных + операторов `? :`. Используйте вместо них `if/unless`. + [[ссылка](#no-multiline-ternary)] + +* Для однострочных выражений по возможности + модификатор `if/unless`. Другим хорошим вариантом являются операторы + управления потоком исполнения `&&/||`. + [[ссылка](#if-as-a-modifier)] + + ```Ruby + # плохо + if some_condition + do_something + end + + # хорошо + do_something if some_condition + + # еще хороший вариант + some_condition && do_something + ``` + +* Избегайте `if/unless` в конце + нетривиального многострочного блока. + [[ссылка](#no-multiline-if-modifiers)] + + ```Ruby + # плохо + 10.times do + # multi-line body omitted + end if some_condition + + # хорошо + if some_condition + 10.times do + # multi-line body omitted + end + end + ``` + +* Используйте `unless` вместо `if` + для отрицательных условий (или `||` для управления потоком исполнения). + [[ссылка](#unless-for-negatives)] + + ```Ruby + # плохо + do_something if !some_condition + + # плохо + do_something if not some_condition + + # хорошо + do_something unless some_condition + + # тоже хорошо + some_condition || do_something + ``` + +* Не используйте `unless` вместе с `else`. + Перепишите такие выражение с положительной проверкой. + [[ссылка](#no-else-with-unless)] + + ```Ruby + # плохо + unless success? + puts 'failure' + else + puts 'success' + end + + # хорошо + if success? + puts 'success' + else + puts 'failure' + end + ``` + +* Не используйте скобки для ограничения условных + выражений в `if/unless/while/until`.[[ссылка](#no-parens-if)] + + ```Ruby + # плохо + if (x > 10) + # код опущен для краткости + end + + # хорошо + if x > 10 + # код опущен для краткости + end + ``` + Однако в этом правиле есть некоторые исключения, например, + [надежные присвоения в условных выражениях](#safe-assignment-in-condition). + +* Не используйте `while/until УСЛОВИЕ do` + для многострочных циклов с `while/until`. + [[ссылка](#no-multiline-while-do)] + + ```Ruby + # плохо + while x > 5 do + # код опущен для краткости + end + + until x > 5 do + # код опущен для краткости + end + + # хорошо + while x > 5 + # код опущен для краткости + end + + until x > 5 + # код опущен для краткости + end + ``` + +* Используйте `while/until` для однострочный + выражений.[[ссылка](#while-as-a-modifier)] + + ```Ruby + # плохо + while some_condition + do_something + end + + # хорошо + do_something while some_condition + ``` +* Используйте `until` вместо `while` + для условий на отрицания.[[ссылка](#until-for-negatives)] + + ```Ruby + # плохо + do_something while !some_condition + + # хорошо + do_something until some_condition + ``` +* Используйте `Kernel#loop` вместо `while/until` + для бесконечного цикла.[[ссылка](#infinite-loop)] + + ```Ruby + # плохо + while true + do_something + end + + until false + do_something + end + + # хорошо + loop do + do_something + end + ``` + +* Используйте `Kernel#loop` с `break` вместо + `begin/end/until` или `begin/end/while` для циклов с постусловием. + [[ссылка](#loop-with-break)] + + ```Ruby + # плохо + begin + puts val + val += 1 + end while val < 0 + + # хорошо + loop do + puts val + val += 1 + break unless val < 0 + end + ``` + +* Не используйте скобки при вызове методов, + являющихся частью таких DSL, как Rake, Rails, RSpec, методов, имеющих + статус ключевого слова, например, `attr_reader`, `puts` и при вызове + аксессоров. Используйте скобки при вызове прочих методов. + [[ссылка](#no-dsl-parens)] + + ```Ruby + class Person + attr_reader :name, :age + + # omitted + end + + temperance = Person.new('Temperance', 30) + temperance.name + + puts temperance.age + + x = Math.sin(y) + array.delete(e) + + bowling.score.should == 0 + ``` + +* Не используйте фигурные скобки для + ограничения хешей, передаваемых методу. + [[ссылка](#no-braces-opts-hash)] + + ```Ruby + # плохо + user.set({ name: 'John', age: 45, permissions: { read: true } }) + + # хорошо + user.set(name: 'John', age: 45, permissions: { read: true }) + ``` + +* Не используйте фигурные скобки для + ограничения хешей, передаваемых методу, и скобки вокруг параметров для + методов, являющихся частью DSL. + [[ссылка](#no-dsl-decorating)] + + ```Ruby + class Person < ActiveRecord::Base + # плохо + validates(:name, { presence: true, length: { within: 1..10 } }) + + # хорошо + validates :name, presence: true, length: { within: 1..10 } + end + ``` + +* Опускайте скобки при вызове метода без + параметров. + [[ссылка](#no-args-no-parens)] + + + ```Ruby + # плохо + Kernel.exit!() + 2.even?() + fork() + 'test'.upcase() + + # хорошо + Kernel.exit! + 2.even? + fork + 'test'.upcase + ``` + +* + Используйте краткую форму для вызова `proc`, если вызываемый метод является + единственным в блоке. + [[link](#single-action-blocks)] + + ```Ruby + # плохо + names.map { |name| name.upcase } + + # хорошо + names.map(&:upcase) + ``` + +* Используйте преимущественно `{...}` в случае + однострочных блоков, а `do...end` в случае многострочных блоков (многострочные + последовательности вызовов методов всегда выглядят ужасно). Старайтесь + применять `do...end` для логических операций и определений методов (например, + для Rakefile и некоторых DSL). Не используйте `do...end` в цепочках вызовов. + [[ссылка](#single-line-blocks)] + + ```Ruby + names = %w(Bozhidar Steve Sarah) + + # плохо + names.each do |name| + puts name + end + + # хорошо + names.each { |name| puts name } + + # плохо + names.select do |name| + name.start_with?('S') + end.map { |name| name.upcase } + + # хорошо + names.select { |name| name.start_with?('S') }.map(&:upcase) + ``` + + Некоторые из нас поспорят, что многострочные последовательные вызовы с блоками + при использовании {...} выглядят неплохо, но тогда стоит себя спросить, а + читается ли такой код и не стоит ли выделить эти блоки в отдельные специальные + методы. + +* Попробуйте использовать блоки напрямую в виде + аргумента в случае, когда блок просто передает свои аргументы в другой блок. + В этом случае обратите внимание на падение производительности, так как + аргументы будут преобразованы в объект класс `Proc`. + [[ссылка](#block-argument)] + + ```Ruby + require 'tempfile' + + # плохо + def with_tmp_dir + Dir.mktmpdir do |tmp_dir| + # блок просто передает аргументы дальше + Dir.chdir(tmp_dir) { |dir| yield dir } + end + end + + # хорошо + def with_tmp_dir(&block) + Dir.mktmpdir do |tmp_dir| + Dir.chdir(tmp_dir, &block) + end + end + + with_tmp_dir do |dir| + puts "dir доступен в виде параметра, и pwd имеет значение: #{dir}" + end + ``` + +* Избегайте ключевого слова `return` везде, + где это не нужно для управления ветвлением. + [[ссылка](#no-explicit-return)] + + ```Ruby + # плохо + def some_method(some_arr) + return some_arr.size + end + + # хорошо + def some_method(some_arr) + some_arr.size + end + ``` + +* Избегайте ключевого слова `self` везде, + где оно не требуется. Оно необходимо только при вызове методов доступа + (`attr_reader`, `attr_writer`, `attr_accessor`). + [[ссылка](#no-self-unless-required)] + + ```Ruby + # плохо + 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 + + # хорошо + def ready? + if last_reviewed_at > last_updated_at + worker.update(content, options) + self.status = :in_progress + end + status == :verified + end + ``` + +* В качестве бездоказательного утверждения: + избегайте маскирования методов локальными переменными, если они не + эквивалентны. + [[ссылка](#no-shadowing)] + + ```Ruby + class Foo + attr_accessor :options + + # cносно + # как options, так и self.options здесь эквивалентны + def initialize(options) + self.options = options + end + + # плохо + def do_something(options = {}) + unless options[:when] == :later + output(self.options[:message]) + end + end + + # хорошо + def do_something(params = {}) + unless params[:when] == :later + output(options[:message]) + end + end + end + ``` + +* Используйте возвращаемое + оператором присваивания (`=`) значение только в случаях, когда все + выражение стоит в скобках. Эта идиома достаточно распространена среди + программистов на Руби и часто называется *надежное присваивание в логических + выражениях*. + [[ссылка](#safe-assignment-in-condition)] + + + ```Ruby + # плохо (к тому же вызывает предупреждение) + if v = array.grep(/foo/) + do_something(v) + ... + end + + # хорошо (MRI будет вызывает предупреждение, но не Рубокоп) + if (v = array.grep(/foo/)) + do_something(v) + ... + end + + # хорошо + v = array.grep(/foo/) + if v + do_something(v) + ... + end + ``` + +* + По возможности используйте сокращенные операторы присваивания. + [[ссылка](#self-assignment)] + + ```Ruby + # плохо + x = x + y + x = x * y + x = x**y + x = x / y + x = x || y + x = x && y + + # хорошо + x += y + x *= y + x **= y + x /= y + x ||= y + x &&= y + ``` + +* + Используйте оператор `||=` для инициализации переменных, только если + переменная еще не инициализирована. + [[ссылка](#double-pipe-for-uninit)] + + ```Ruby + # плохо + name = name ? name : 'Bozhidar' + + # плохо + name = 'Bozhidar' unless name + + # хорошо (присвоить переменной name значение Bozhidar, только если ее значение + # nil или false + name ||= 'Bozhidar' + ``` + +* + Не используйте оператор `||=` для инициализации логических переменных. + Это вызовет проблемы, если текущим значением переменной будет `false`. + [[ссылка](#no-double-pipes-for-bools)] + + ```Ruby + # плохо (назначит переменной enabled значение true, даже если оно было false) + enabled ||= true + + # хорошо + enabled = true if enabled.nil? + ``` + +* + Используйте оператор `&&=` для предварительной работы с переменными, которые + уже или еще не инициализированы. Использование оператора `&&=` изменит + значение переменной, только если она инициализирована. При этом отпадает + необходимость в проверке с `if`. + [[ссылка](#double-amper-preprocess)] + + ```Ruby + # плохо + if something + something = something.downcase + end + + # плохо + something = something ? something.downcase : nil + + # сносно + something = something.downcase if something + + # хорошо + something = something && something.downcase + + # еще лучше + something &&= something.downcase + ``` + +* + Избегайте явного использования оператора равенства в case `===`. Как + подсказывает его имя, этот оператор предназначен для имплицитного + применения в выражениях `case`, в отрыве от них он приводит только к + разночтениям в коде. + [[ссылка](#no-case-equality)] + + ```Ruby + # плохо + Array === something + (1..100) === 7 + /something/ === some_string + + # хорошо + something.is_a?(Array) + (1..100).include?(7) + some_string =~ /something/ + ``` +* + Не используйте `eql?`, если будет достаточно `==`. Более строгая семантика + сравнения, реализованная в `eql?`, достаточно редко нужна на практике. + [[link](#eql)] + + ```Ruby + # плохо (`eql?` работает для строк, как и `==`) + "ruby".eql? some_str + + # хорошо + "ruby" == some_str + 1.0.eql? x # здесь `eql?` имеет смысл, если вы хотите различать классы числа: `Fixnum` vs. `Float` + ``` + +* + Избегайте специальных переменных, заимствованых из языка Перл, например, `$:`, + `$;` и т.д. Они сложно воспринимаются, и их использование приветствуется + только в однострочных скриптах. В остальных случаях применяйте легкие для + восприятия варианты этих переменных из библиотеки `English`. + [[ссылка](#no-cryptic-perlisms)] + + ```Ruby + # плохо + $:.unshift File.dirname(__FILE__) + + # хорошо + require 'English' + $LOAD_PATH.unshift File.dirname(__FILE__) + ``` + +* + Не оставляйте пробел между именем метода и открывающей скобкой. + [[ссылка](#parens-no-spaces)] + + ```Ruby + # плохо + f (3 + 2) + 1 + + # хорошо + f(3 + 2) + 1 + ``` + +* + Если первый аргумент при вызове метода начинается скобкой, то всегда + используйте скобки при вызове метода. Например, пишем так: `f((3 + 2) + 1)`. + [[ссылка](#parens-as-args)] + +* + Всегда вызывайте интерпретатор Руби с ключом `-w`, чтобы получать напоминия о + правилах, описанных выше, даже если вы о них забываете. + [[ссылка](#always-warn-at-runtime)] + +* + Используйте новый синтаксис лямбда-выражений для однострочных блоков. Используйте + метод `lambda` для многострочных блоков. + [[ссылка](#lambda-multi-line)] + + ```Ruby + # плохо + l = lambda { |a, b| a + b } + l.call(1, 2) + + # верно, но выглядит очень странно + l = ->(a, b) do + tmp = a * 7 + tmp * b / 50 + end + + # хорошо + l = ->(a, b) { a + b } + l.call(1, 2) + + l = lambda do |a, b| + tmp = a * 7 + tmp * b / 50 + end + ``` + +* + Используйте `proc` вместо `Proc.new`. + [[ссылка](#proc)] + + ```Ruby + # плохо + p = Proc.new { |n| puts n } + + # хорошо + p = proc { |n| puts n } + ``` + +* + Используйте `proc.call()` вместо `proc[]` или `proc.()` для + лямбда-выражений и блоков. + [[ссылка](#proc-call)] + + ```Ruby + # плохо (выглядит как доступ к энумератору) + l = ->(v) { puts v } + l[1] + + # тоже плохо (редкая формулировка) + l = ->(v) { puts v } + l.(1) + + # хорошо + l = ->(v) { puts v } + l.call(1) + ``` + +* + Начинайте неиспользуемые параметры блока с подчеркивания `_`. Также допустимо + использовать только подчеркивание `_`, хотя это и менее информативно. Эта + договоренность распознается интерпретатором Руби и Рубокопом и уберет + предупреждения о неиспользуемых переменных. + [[ссылка](#underscore-unused-vars)] + + ```Ruby + # плохо + result = hash.map { |k, v| v + 1 } + + def something(x) + unused_var, used_var = something_else(x) + # ... + end + + # хорошо + result = hash.map { |_k, v| v + 1 } + + def something(x) + _unused_var, used_var = something_else(x) + # ... + end + + # хорошо + result = hash.map { |_, v| v + 1 } + + def something(x) + _, used_var = something_else(x) + # ... + end + ``` + +* + Используйте переменные `$stdout/$stderr/$stdin` вместо констант + `STDOUT/STDERR/STDIN`. `STDOUT/STDERR/STDIN` являются константами, поэтому при + их переопределении (вы это можете сделать, например, для перенаправления + ввода-вывода) интерпретатор будет выдавать предупреждения. + [[ссылка](#global-stdout)] + +* + Используйте `warn` вместо `$stderr.puts`. Это не только короче, но и позволит + вам скрыть все предупреждения, если вам это понадобится (для этого задайте + уроверь предупреждений равный `0` при помощи опции `-W0`). + [[ссылка](#warn)] + +* + Используйте `sprintf` и его алиас `format` вместо довольно запутанного метода + `String#%`. + [[ссылка](#sprintf)] + + ```Ruby + # плохо + '%d %d' % [20, 10] + # => '20 10' + + # хорошо + sprintf('%d %d', 20, 10) + # => '20 10' + + # хорошо + sprintf('%{first} %{second}', first: 20, second: 10) + # => '20 10' + + format('%d %d', 20, 10) + # => '20 10' + + # хорошо + format('%{first} %{second}', first: 20, second: 10) + # => '20 10' + ``` + +* + Используйте `Array#join` вместо достаточно неочевидного `Array#*` со строковым + аргументом. + [[ссылка](#array-join)] + + ```Ruby + # плохо + %w(one two three) * ', ' + # => 'one, two, three' + + # хорошо + %w(one two three).join(', ') + # => 'one, two, three' + ``` + +* + Используйте `[*var]` или `Array()` вместо явной проверки с помощью `Array`, + когда вам приходится работать с переменной, которая по вашим ожиданиям должна + быть массивом, но вы в этом не полностью уверены. + [[ссылка](#splat-arrays)] + + ```Ruby + # плохо + paths = [paths] unless paths.is_a? Array + paths.each { |path| do_something(path) } + + # хорошо + [*paths].each { |path| do_something(path) } + + # хорошо (and a bit more readable) + Array(paths).each { |path| do_something(path) } + ``` + +* + Используйте интервалы или метод `Comparable#between?` вместо сложной логики + для сравнения, когда это возможно. + [[ссылка](#ranges-or-between)] + + ```Ruby + # плохо + do_something if x >= 1000 && x <= 2000 + + # хорошо + do_something if (1000..2000).include?(x) + + # хорошо + do_something if x.between?(1000, 2000) + ``` + +* + Используйте предикативные методы вместо явного сравнения с использованием + `==`. Сравнение чисел можно проводить явно. + [[ссылка](#predicate-methods)] + + ```Ruby + # плохо + if x % 2 == 0 + end + + if x % 2 == 1 + end + + if x == nil + end + + # хорошо + if x.even? + end + + if x.odd? + end + + if x.nil? + end + + if x.zero? + end + + if x == 0 + end + ``` + +* + Проводите явную проверку на значение `nil`, только если вы работаете + с логическими значениями. + [[ссылка](#no-non-nil-checks)] + + ```Ruby + # плохо + do_something if !something.nil? + do_something if something != nil + + # хорошо + do_something if something + + # хорошо (логическое значение) + def value_set? + !@some_boolean.nil? + end + ``` + +* + Старайтесь не использовать блоки `BEGIN`. + [[ссылка](#no-BEGIN-blocks)] + +* + Никогда не используйте блоки `END`. Используйте метод `Kernel#at_exit`. + [[ссылка](#no-END-blocks)] + + ```Ruby + # плохо + END { puts 'Goodbye!' } + + # хорошо + at_exit { puts 'Goodbye!' } + ``` + +* + Избегайте переменных-перевертышей (flip-flops). + [[ссылка](#no-flip-flops)] + +* + Избегайте вложенных условий для управления ветвлением. Используйте проверочные + выражения (guard clauses). Проверочные выражения - это условные выражения + в самом начале функции, которые срабатывают при первой же возможности. + [[ссылка](#no-nested-conditionals)] + + ```Ruby + # плохо + def compute_thing(thing) + if thing[:foo] + update_with_bar(thing) + if thing[:foo][:bar] + partial_compute(thing) + else + re_compute(thing) + end + end + end + + # хорошо + 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 + ``` + + Используйте в циклах `next` в место блоков с условием. + + ```Ruby + # плохо + [0, 1, 2, 3].each do |item| + if item > 1 + puts item + end + end + + # хорошо + [0, 1, 2, 3].each do |item| + next unless item > 1 + puts item + end + ``` + +## Наименование + +> Единственными настоящими сложностями в программировании являются очистка кэша +> и выбор наименований.
+> -- Фил Карлтон (Phil Karlton) + +* Используйте английский язык, называя + идентификаторы.[[ссылка](#english-identifiers)] + + ```Ruby + # плохо (идентификатор использует символы вне ASCII) + зарплата = 1_000 + + # плохо (идентификатор - это русское слово, набранное латиницей вместо + # кириллицы) + zarplata = 1_000 + + # хорошо + salary = 1_000 + ``` + +* Используйте `snake_case` для + имен символов, методов и переменных. + [[ссылка](#snake-case-symbols-methods-vars)] + + ```Ruby + # плохо + :'some symbol' + :SomeSymbol + :someSymbol + + someVar = 5 + + def someMethod + ... + end + + def SomeMethod + ... + end + + # хорошо + :some_symbol + + def some_method + ... + end + ``` + +* Используйте `CamelCase` для имен классов и + модулей. Сокращения вроде `HTTP`, `RFC`, `XML` набирайте заглавными буквами. + [[ссылка](#camelcase-classes)] + + ```Ruby + # плохо + class Someclass + ... + end + + class Some_Class + ... + end + + class SomeXml + ... + end + + # хорошо + class SomeClass + ... + end + + class SomeXML + ... + end + ``` + +* Используйте `snake_case`, называя файлы, + например, `hello_world.rb`.[[ссылка](#snake-case-files)] + +* Используйте `snake_case`, называя каталоги, + например, `lib/hello_world/hello_world.rb`. + [[ссылка](#snake-case-dirs)] + +* Старайтесь создавать только один класс или + модуль в каждом файле исходного кода. Называйте эти файлы по имени класса или + модуля, изменив запись в форме `CamelCase` на `snake_case`. + [[ссылка](#one-class-per-file)] + +* Используйте `SCREAMING_SNAKE_CASE` для + всех других констант кроме имен классов и модулей. + [[ссылка](#screaming-snake-case)] + + ```Ruby + # плохо + SomeConst = 5 + + # хорошо + SOME_CONST = 5 + ``` + +* Идентификаторы предикативных методов, т.е. + методов, возвращающих логическое значение, должны оканчиваться вопросительным + знаком. Например, `Array#empty?`. Методы, не возвращающие логическое значение, + не должны оканчиваться вопросительным знаком. + [[ссылка](#bool-methods-qmark)] + +* Идентификаторы потенциально *опасных* + методов, т.е. таких методов, которые могут именить `self` или его аргументы, + должны оканчиваться восклицательным знаком, если есть соответствующий + *безопасный* вариант такого метода. Например, `exit!`, который не вызывает + завершающий скрипт в отличии от `exit`, выполняющего финализацию. + [[ссылка](#dangerous-method-bang)] + + ```Ruby + # плохо (нет соответсвующего безопасного аналога) + class Person + def update! + end + end + + # хорошо + class Person + def update + end + end + + # хорошо + class Person + def update! + end + + def update + end + end + ``` + +* Определяйте безопасный метод (вариант + без восклицательного знака) при помощи вызова опасного метода (с + восклицательным знаком), если это возможно. + [[ссылка](#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 + ``` + +* При использовании `#reduce` с коротким блоком, + называйте аргументы `|a, e|` (accumulator, element). + [[ссылка](#reduce-blocks)] + +* + При определении бинарных операторов называйте параметр `other`. Исключение + составляют методы `#<<` и `#[]`, так как их семантика сильно отличается. + [[ссылка](#other-arg)] + + ```Ruby + def +(other) + # некоторый код + end + ``` + +* Используйте `#map` вместо + `#collect`, `#find` вместо `#detect`, `#select` вместо `#find_all`, + `#reduce` вместо `#inject` и `#size` вместо `#length`. Это требование + не сложно реализовать. Если использование альтернатив улучшит восприятие кода, + то можно использовать и их. Все описанные варианты были взяты из языка + Smalltalk и не распространены в других языках программирования. Причиной, + почему не следует использовать `#find_all` вместо `#select`, является хорошая + сочетаемость с методом `#reject`, и эти наименования очевидны. + [[ссылка](#map-find-select-reduce-size)] + +* Не используйте `#count` в качестве заметы для + `#size`. Для объектов классов с включенным `Enumerable` (кроме класса`Array`) + это приведет к затратному полному обходу всех элементов для определения + размера.[[ссылка](#count-vs-size)] + + ```Ruby + # плохо + some_hash.count + + # хорошо + some_hash.size + ``` + +* Используйте `#flat_map` вместо `#map` + `#flatten`. + Это правило не относится к массивам с глубиной больше 2, например, если + `users.first.songs == ['a', ['b', 'c']]`, то используйте `#map` + `#flatten`, + а не `#flat_map`. Метод `#flat_map` уменьшает глубину на один уровень. Метод + `#flatten` сглаживает вложенность любого уровня. + [[ссылка](#flat-map)] + + ```Ruby + # плохо + all_songs = users.map(&:songs).flatten.uniq + + # хорошо + all_songs = users.flat_map(&:songs).uniq + ``` + +* + Используйте метод `#reverse_each` вместо `#reverse.each`, так как некоторые + классы, включающие в себя модуль `Enumerable`, дадут вам очень эффективную + реализацию. Даже в худшем случае, когда класс не реализует этот метод + отдельно, наследуемая реализация из модуля `Enumerable` будет по меньшей мере + такой же эффективной, как и для `#reverse.each`. + [[ссылка](#reverse-each)] + + ```Ruby + # плохо + array.reverse.each { ... } + + # хорошо + array.reverse_each { ... } + ``` + +## Комментарии + +> Хороший код является лучшей документацией для себя. Каждый раз, когда вы +> готовитесь добавить комментарий, спросите себя: "Как я могу улучшить код, +> чтобы это комментарий стал ненужным?" Улучшите код и добавьте комментарий, +> чтобы сделать его еще понятнее.
+> -- Стив Макконнел (Steve McConnell) + +* + Пишите говорящий за себя код и смело пропускайте все остальное в этом разделе. + Серьезно! + [[ссылка](#no-comments)] + +* + Пишите комментарии по-английски. + [[ссылка](#english-comments)] + +* + Используйте один пробел между символом `#` в начале и текстом самого + комментария. + [[ссылка](#hash-space)] + +* Комментарии длиной больше одного слова должны + оформляться в виде законченных предложений (с большой буквы и со знаками + препинания). + Разделяйте предложения [одним пробелом](http://en.wikipedia.org/wiki/Sentence_spacing). + [[ссылка](#english-syntax)] + +* Избегайте избыточного комментирования. + [[ссылка](#no-superfluous-comments)] + + ```Ruby + # плохо + counter += 1 # Увеличивает счетчик на единицу. + ``` + +* Актуализируйте существующие комментарии. + Устаревший комментарий гораздо хуже отсутствующего комментария. + [[ссылка](#comment-upkeep)] + +> Хороший код подобен хорошей шутке: он не нуждается в пояснениях.
+> -- Рус Ольсен (Russ Olsen) + +* Не пишите комментарии для объяснения + плохого кода. Перепишите код, чтобы он говорил сам за себя. + [[ссылка](#refactor-dont-comment)] + +> Делай или не делай, тут нет места попыткам.
+> -- Мастер Йода + +### Пометки в комментариях + +* Обычно пометки следует записывать + на предшествующей описываемому коду строке.[[ссылка](#annotate-above)] + +* Пометка отделяется двоеточием и пробелом, потом + следует примечание, описывающее проблему.[[ссылка](#annotate-keywords)] + +* + Если для описания проблемы потребуются несколько строк, то на каждой + последующей строке следует сделать отступ в три пробела после символа `#`. + [[ссылка](#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 + ``` + +* В тех случаях, когда проблема настолько + очевидна, что любые описания покажутся избыточными, пометки можно поставить + в конце вызывающей проблему строки. Однако такое применение должно быть + исключением, а не правилом.[[ссылка](#rare-eol-annotations)] + + ```Ruby + def bar + sleep 100 # OPTIMIZE + end + ``` + +* + Используйте `TODO`, чтобы пометить отсутствующие возможности или функционал, + которые должны быть добавлены позже. + [[ссылка](#todo)] + +* + Используйте `FIXME`, чтобы пометить код с ошибками, который должен быть + исправлен. + [[ссылка](#fixme)] + +* + Используйте `OPTIMIZE`, чтобы пометить медленный или неэффективный код, + который может вызвать проблемы с производительностью. + [[ссылка](#optimize)] + +* + Используйте `HACK`, чтобы пометить код "с душком", который должен быть + переработан и использует сомнительные практики разработки. + [[ссылка](#hack)] + +* + Используйте `REVIEW`, чтобы пометить все, что должно быть проверено на + работоспособность. Например, `REVIEW: Are we sure this is how the client does + X currently?`. + [[ссылка](#review)] + +* + Используйте персональные пометки, если это подходит по месту, но обязательно + опишите их смысл в файле `README` (или похожем) для вашего проекта. + [[ссылка](#document-annotations)] + +## Классы и модули + +* Придерживайтесь единообразной структуры + классов.[[ссылка](#consistent-classes)] + + ```Ruby + class Person + # extend и include в начале + extend SomeModule + include AnotherModule + + # вложенные классы + CustomErrorKlass = Class.new(StandardError) + + # после этого константы + SOME_CONSTANT = 20 + + # после этого макросы методов доступа к атрибутам + attr_reader :name + + # и все прочие макросы (если имеются) + validates :name + + # следующими по списку будут публичные методы класса + def self.some_method + end + + # инициализация объекта стоит между методами класса и экземпляров + def initialize + end + + # и следующие за ними публичные методы экземпляров этого класса + def some_method + end + + # защищенные и частные методы нужно собрать ближе к концу + protected + + def some_protected_method + end + + private + + def some_private_method + end + end + ``` + +* + Если определение класса занимает несколько строк, постарайтесь вынести такой + класс в отдельный файл. Файл с определением стоит поместить в директорию, + названную по имени родительского класса, внутри которого определяется + вложенный класс. + [[ссылка](#file-classes)] + + ```Ruby + # плохо + + # foo.rb + class Foo + class Bar + # 30 методов внутри + end + + class Car + # 20 методов внутри + end + + # 30 методов внутри + end + + # хорошо + + # foo.rb + class Foo + # 30 методов внутри + end + + # foo/bar.rb + class Foo + class Bar + # 30 методов внутри + end + end + + # foo/car.rb + class Foo + class Car + # 20 методов внутри + end + end + ``` + +* Если класс определяет только методы класса, + то трансформируйте такой класс в модуль. Использовать классы логично в тех + ситуациях, когда нужно создавать экземпляры класса. + [[ссылка](#modules-vs-classes)] + + ```Ruby + # плохо + class SomeClass + def self.some_method + # некоторый код + end + + def self.some_other_method + end + end + + # хорошо + module SomeModule + module_function + + def some_method + # некоторый код + end + + def some_other_method + end + end + ``` + +* Используйте `module_function` вместо + `extend self`, когда вам нужно преобразовать включаемые методы модуля в + методы модуля. + [[ссылка](#module-function)] + + ```Ruby + # плохо + module Utilities + extend self + + def parse_something(string) + # здесь реализуется логика + end + + def other_utility_method(number, string) + # здесь реализуется дополнительная логика + end + end + + # хорошо + module Utilities + module_function + + def parse_something(string) + # здесь реализуется логика + end + + def other_utility_method(number, string) + # здесь реализуется дополнительная логика + end + end + ``` + +* Создавая иерархии классов, проверяйте их на + соответствие [принципу подстановки Барбары Лисков][Liskov]. + [[ссылка](#liskov)] + +* Проверяйте дизайн ваших классов на + соответствие принципу [SOLID](http://en.wikipedia.org/wiki/SOLID_\(object-oriented_design\)), + если такая возможность есть. + [[ссылка](#solid-design)] + +* Для описывающих предметные области объектов всегда + определяйте метод `#to_s`. + [[ссылка](#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 + ``` + +* Применяйте макросы из семества `attr_` для + тривиальных методов доступа к объекту. + [[ссылка](#attr_family)] + + ```Ruby + # плохо + 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 + + # хорошо + class Person + attr_reader :first_name, :last_name + + def initialize(first_name, last_name) + @first_name = first_name + @last_name = last_name + end + end + ``` + +* Не используйте обобщенную форму `attr`. Используйте + `attr_reader` и `attr_accessor` вместо нее. + [[ссылка](#attr)] + + ```Ruby + # плохо (создает единый метод доступа атрибуту, объявлено нежелательным 1.9) + attr :something, true + attr :one, :two, :three # ведет себя как attr_reader + + # хорошо + attr_accessor :something + attr_reader :one, :two, :three + ``` + +* Подумайте об использовании `Struct.new`, эта + конструкция даст вам сразу простейшие методы доступа к состоянию, + метод инициализации и методы сравнения. + [[ссылка](#struct-new)] + + ```Ruby + # хорошо + class Person + attr_accessor :first_name, :last_name + + def initialize(first_name, last_name) + @first_name = first_name + @last_name = last_name + end + end + + # лучше + Person = Struct.new(:first_name, :last_name) do + end + ```` + +* + Не дополняйте `Struct.new` при помощи `#extend`. В этом случае уже создается + новый класс. При дополнении вы создадите избыточный уровень абстракции, это + может привезти к странным ошибкам при многократной загрузке кода из файла. + [[ссылка](#no-extend-struct-new)] + + ```Ruby + # плохо + class Person < Struct.new(:first_name, :last_name) + end + + # хорошо + Person = Struct.new(:first_name, :last_name) + ``` + +* + Продумывайте варианты добавления фабричных методов как дополнительной + возможности создавать экземпляры конкретного класса. + [[ссылка](#factory-methods)] + + ```Ruby + class Person + def self.create(options_hash) + # некоторый код + end + end + ``` + +* + Используйте технику [утиной типизации][duck-typing] (duck typing) вместо + наследования. + [[ссылка](#duck-typing)] + + ```Ruby + # плохо + 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 + + # хорошо + class Duck + def speak + puts 'Quack! Quack' + end + end + + class Dog + def speak + puts 'Bau! Bau!' + end + end + ``` + +* Избегайте переменных класса (`@@`) из-за их + "непристойного" поведения при наследовании. + [[ссылка](#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 # => вернет "child" + ``` + + Как вы видите, все классы в иерархии фактически делять одну и ту же + переменную класса. Как правило, вам следует использовать переменные + экземпляра класса вместо переменной класса. + +* + Ограничивайте область видимости методов (`private`, `protected`) в зависимости + от их планируемого применения. Не оставляйте все в области `public` (это + стандартное значение). В конце концов мы пишем на *Руби*, а не на *Питоне*. + [[ссылка](#visibility)] + +* + Делайте отступы для указателей `public`, `protected` и `private` такими же, + как и у самих определений методов, к которым они относятся. Оставляйте пустую + строку выше, а также после указателя, чтобы подчеркнуть, что он относится ко + всем определяемым ниже методам. + [[ссылка](#indent-public-private-protected)] + + ```Ruby + class SomeClass + def public_method + # некоторый код + end + + private + + def private_method + # некоторый код + end + + def another_private_method + # некоторый код + end + end + ``` + +* + Для определения синглетных методов используйте `def self.method`. Это упростит + рефакторинг, так как имя класса будет использоваться только один раз. + [[ссылка](#def-self-singletons)] + + ```Ruby + class TestClass + # плохо + def TestClass.some_method + # некоторый код + end + + # хорошо + def self.some_other_method + # body omitted + end + + # Также допускается и будет удобным, когда + # нужно определить много синглетных методов. + class << self + def first_method + # некоторый код + end + + def second_method_etc + # некоторый код + end + end + end + ``` + +* + Используйте `alias` при определении алиасов методов в лексической области + видимости класса. `self` в данном случае также имеет лексическую область + видимости, и это подчеркивает тот факт, что алиас будет указывать на метод + того класса, в котором определен. Вызов не будет перенаправлен неявно. + [[link](#alias-method-lexically)] + + ```Ruby + class Westerner + def first_name + @names.first + end + + alias given_name first_name + end + ``` + + Так как `alias`, как и `def`, является ключевым словом, используйте простые + имена методов, а не символы или строки в качестве аргументов. Другими словами, + пишите `alias foo bar`, а не `alias :foo :bar`. + + Также обратите внимание, как Ruby обрабатывает алиасы при наследовании: алиас + будет привязан к тому методу, который находится в области видимости в момент + объявления. Динамическое перенаправление вызова не производится. + + ```Ruby + class Fugitive < Westerner + def first_name + 'Nobody' + end + end + ``` + + В этом примере `Fugitive#given_name` будет вызывать метод базовго класса + `Westerner#first_name`, а не `Fugitive#first_name`. Чтобы переопределить + поведение `Fugitive#given_name`, нужно объявить алиас в классе-наследнике. + + ```Ruby + class Fugitive < Westerner + def first_name + 'Nobody' + end + + alias given_name first_name + end + ``` + +* + Всегда применяйте `alias_method` для определения алиасов методов модулей, + классов или синглетных классов во время выполнения, так как `alias` + использует лексическую область видимости, что приводит к неопределенному + поведению в данном случае. + [[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 + ``` + + +## Исключения + +* Вызывайте исключения при помощи ключевого слова `fail`. + Используйте `raise` только при перехвате исключения и вызове его же заново. + В этом случае вы не вызываете исключение, а лишь намеренно передаете его дальше + по стеку.[[ссылка](#fail-method)] + + ```Ruby + begin + fail 'Oops' + rescue => error + raise if error.message != 'Oops' + end + ``` + +* Нет нужды задавать `RuntimeError` явно + в качестве аргумента при вызове `fail/raise` с двумя аргументами. + [[ссылка](#no-explicit-runtimeerror)] + + ```Ruby + # плохо + fail RuntimeError, 'message' + + # хорошо - вызывает `RuntimeError` по умолчанию + fail 'message' + ``` + +* Передавайте класс исключения и сообщение + в форме двух аргументов для `fail/raise` вместо экземпляра класса исключения. + [[ссылка](#exception-class-messages)] + + ```Ruby + # плохо + fail SomeException.new('message') + # Обратите внимение, что нет возможности вызвать + # `fail SomeException.new('message'), backtrace`. + + # хорошо + fail SomeException, 'message' + # Работает с `fail SomeException, 'message', backtrace`. + ``` + +* Не возвращайте значений в блоке `ensure`. + Если вы явным образом возвращаете значение из блока `ensure`, то возвращение + будет обрабатываться сначала и метод вернет значение, как если бы исключения + не было вовсе. По итогу исключение будет просто тихо проигнорированно. + [[ссылка](#no-return-ensure)] + + ```Ruby + def foo + fail + ensure + return 'very bad idea' + end + ``` + +* Используйте *имплицитную форму* блока `begin` + по возможности.[[ссылка](#begin-implicit)] + + ```Ruby + # плохо + def foo + begin + # основной код находится здесь + rescue + # здесь происходит обработка ошибок + end + end + + # хорошо + def foo + # здесь реализуется основная логика + rescue + # здесь происходит обработка ошибок + end + ``` + +* Смягчайте неудобства, связанные с + использование блоков `begin` при помощи *contingency methods* (термин введен + Авди Гриммом).[[ссылка](#contingency-methods)] + + ```Ruby + # плохо + begin + something_that_might_fail + rescue IOError + # handle IOError + end + + begin + something_else_that_might_fail + rescue IOError + # handle IOError + end + + # хорошо + 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 } + ``` + +* Не подавляйте исключения без обработки. + [[ссылка](#dont-hide-exceptions)] + + ```Ruby + # плохо + begin + # здесь образовалось исключение + rescue SomeError + # rescue не содержит никакой обработки + end + + # плохо + do_something rescue nil + ``` + +* Откажитесь от использывания `rescue` в виде + постмодификатора.[[ссылка](#no-rescue-modifiers)] + + ```Ruby + # плохо - это перехватывает исключения класса `StandardError` и его наследников + read_file rescue handle_error($!) + + # хорошо - это перехватывает только исключения класса `Errno::ENOENT` и его наследников + def foo + read_file + rescue Errno::ENOENT => ex + handle_error(ex) + end + ``` + +* Управляйте ветвлением в программе + без помощи исключений.[[ссылка](#no-exceptional-flows)] + + ```Ruby + # плохо + begin + n / d + rescue ZeroDivisionError + puts 'Cannot divide by 0!' + end + + # хорошо + if d.zero? + puts 'Cannot divide by 0!' + else + n / d + end + ``` + +* Не перехватывайте напрямую класс исключений + `Exception`. Это будет перехватывать сигналы и вызовы `exit`, что + потребует в крайнем случае завершения процесса при помощи `kill -9`. + [[ссылка](#no-blind-rescues)] + + ```Ruby + # плохо + begin + # сигналы выхода будет перехвачены (кроме kill -9) + exit + rescue Exception + puts "you didn't really want to exit, right?" + # обработка исключений + end + + # хорошо + begin + # `rescue` без параметров перехватывает `StandardError`, а не `Exception`, + # как предполагают многие разработчики. + rescue => e + # обработка исключений + end + + # тоже хорошо + begin + # здесь вызывается исключение + + rescue StandardError => e + # обработка ошибок + end + ``` + +* Размещайте более специфичные исключения + в иерархии проверки, иначе они никогда не будут отфильтрованы. + [[ссылка](#exception-ordering)] + + ```Ruby + # плохо + begin + # код с ошибкой + rescue Exception => e + # некоторое действие + rescue StandardError => e + # некоторое действие + end + + # хорошо + begin + # код с ошибкой + rescue StandardError => e + # некоторое действие + rescue Exception => e + # некоторое действие + end + ``` + +* + Освобождайте используемые вашей программой ресурсы в блоке `ensure`. + [[ссылка](#release-resources)] + + ```Ruby + f = File.open('testfile') + begin + # .. process + rescue + # .. handle error + ensure + f.close unless f.nil? + end + ``` + +* + Применяйте варианты доступа к ресурсам, которые гарантируют автоматический + возврат выделенных ресурсов, если есть такая возможность. + [[link](#auto-release-resources)] + + ```Ruby + # плохо (нужно специально закрывать ранее открытый файл) + f = File.open('testfile') + # ... + f.close + + # хорошо (открытый файл закрывается автоматически) + File.open('testfile') do |f| + # ... + end + ``` +* + Преимущественно используйте исключения, определенные в стандартной библиотеке, + не создавайте без нужды новые классы исключений. + [[ссылка](#standard-exceptions)] + +## Коллекции + +* При создании массивов и хешей применяйте + нотацию с литералами. Используйте конструкторы класса, только если вам нужно + передать дополнительные параметры при создании коллекций. + [[ссылка](#literal-array-hash)] + + ```Ruby + # плохо + arr = Array.new + hash = Hash.new + + # хорошо + arr = [] + hash = {} + ``` + +* Используйте нотацию `%w` для литералов массивов, + когда вам необходимо создать массив слов (непустых строк без пробелов и + метасимволов). Это правило касается лишь массивов с двумя и более + элементами.[[ссылка](#percent-w)] + + ```Ruby + # плохо + STATES = ['draft', 'open', 'closed'] + + # хорошо + STATES = %w(draft open closed) + ``` + +* Используйте нотацию `%i` для литералов массивов, + когда вам необходимо создать массив символов. Помните, что эта нотация + несовместима с синтаксисом Ruby 1.9 и старше. Это правило касается лишь + массивов с двумя и более элементами.[[ссылка](#percent-i)] + + ```Ruby + # плохо + STATES = [:draft, :open, :closed] + + # хорошо + STATES = %i(draft open closed) + ``` + +* Не ставьте запятую после последнего + элемента в литералах массивов и хешей, особенно если элементы находятся не на + разных строках.[[ссылка](#no-trailing-array-commas)] + + ```Ruby + # плохо (проще перемещать, добавлять и удалять элементы, но не идеально) + VALUES = [ + 1001, + 2020, + 3333, + ] + + # плохо + VALUES = [1001, 2020, 3333, ] + + # хорошо + VALUES = [1001, 2020, 3333] + ``` + +* Не создавайте массивы с большими незанятыми + промежутками адресов.[[ссылка](#no-gappy-arrays)] + + ```Ruby + arr = [] + arr[100] = 1 # Теперь у вас есть массив с кучей значений `nil`. + ``` + +* При доступе к первому и последнему элементам + массива используйте методы `#first` или `#last`, а не индексы `[0]` и `[-1]`. + [[ссылка](#first-and-last)] + +* Используйте класс `Set` вместо `Array`, если вы + работаете с уникальными элементами. Класс `Set` реализует несортированную + коллекцию элементов без повторений и является гибридом интуитивных операций + класса `Array` и легкого и быстрого доступа класса `Hash`. + [[ссылка](#set-vs-array)] + +* Используйте символы вместо строк в качестве + ключей хешей.[[ссылка](#symbols-as-keys)] + + ```Ruby + # плохо + hash = { 'one' => 1, 'two' => 2, 'three' => 3 } + + # хорошо + hash = { one: 1, two: 2, three: 3 } + ``` + +* Не используйте мутируемые объекты в качестве + ключей для хешей.[[ссылка](#no-mutable-keys)] + +* Применяйте введенный в Ruby 1.9 синтаксис для + литералов хешей, когда ключами являются символы. + [[ссылка](#hash-literals)] + + ```Ruby + # плохо + hash = { :one => 1, :two => 2, :three => 3 } + + # хорошо + hash = { one: 1, two: 2, three: 3 } + ``` + +* Не используйте разные способы записи + хешей одновременно (нотации до и после Ruby 1.9). Если вы используете не только + символы в качестве ключей, то применяйте только старую нотацию со стрелками. + [[ссылка](#no-mixed-hash-syntaces)] + + ```Ruby + # плохо + { a: 1, 'b' => 2 } + + # хорошо + { :a => 1, 'b' => 2 } + ``` + +* Применяйте `Hash#key?` вместо `Hash#has_key?` и + `Hash#value?` вместо `Hash#has_value?`. Матц описывает + [здесь](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/43765) + свои планы исключить эти методы в будущем. + [[ссылка](#hash-key)] + + ```Ruby + # плохо + hash.has_key?(:test) + hash.has_value?(value) + + # хорошо + hash.key?(:test) + hash.value?(value) + ``` + +* Для надежной работы с заданными ключами, о + существовании которых доподлинно известно, используйте `Hash#fetch`. + [[ссылка](#hash-fetch)] + + ```Ruby + heroes = { batman: 'Bruce Wayne', superman: 'Clark Kent' } + # плохо (закравшуюся ошибку можно и не заметить сразу) + heroes[:batman] # => "Bruce Wayne" + heroes[:supermann] # => nil + + # хорошо (`Hash#fetch` вызывает `KeyError` и явно указывает на проблему) + heroes.fetch(:supermann) + ``` + +* Задавайте стандартные значения для хешей + при помощи `Hash#fetch`, не реализуйте эту логику самостоятельно. + [[ссылка](#hash-fetch-defaults)] + + ```Ruby + batman = { name: 'Bruce Wayne', is_evil: false } + + # плохо (например, при использование оператора `||` мы получим неожиданный + # результат при ложном значении первого операнда) + batman[:is_evil] || true # => true + + # хорошо (`Hash#fetch` отрабатывает корректно) + batman.fetch(:is_evil, true) # => false + ``` + +* + Используйте блоки вместо значений `Hash#fetch` по умолчанию, если вызываемый + код имеет сторонние эффекты или сложен для выполнения. + [[ссылка](#use-hash-blocks)] + + ```Ruby + batman = { name: 'Bruce Wayne' } + + # плохо (при использовании значения по умолчанию метод его расчета будет + # вызываться каждый раз, сильно замедляя выполнение программы при + # многократных вызовах) + batman.fetch(:powers, get_batman_powers) # get_batman_powers - нагруженный метод + + # хорошо (блоки оцениваются лишь по необходимости, когда вызывается KeyError) + batman.fetch(:powers) { get_batman_powers } + ``` + +* + Используйте `Hash#values_at`, когда вам нужно получить несколько значений хеша + за один раз. + [[ссылка](#hash-values-at)] + + ```Ruby + # плохо + email = data['email'] + username = data['nickname'] + + # хорошо + email, username = data.values_at('email', 'nickname') + ``` + +* Вы можете положиться на то, что хеши в Ruby 1.9 + и младше отсортированны.[[ссылка](#ordered-hashes)] + +* Никогда не модифицируйте коллекцию в + процессе ее обхода.[[ссылка](#no-modifying-collections)] + +* + Получая доступ к элементам коллекций, старайтесь избегать доступа при помощи + `[n]`, а используйте альтернативные методы доступа, если таковые определены. + Это обезопасит вас от вызова `[]` на `nil`. + [[link](#accessing-elements-directly)] + + ```Ruby + # плохо + Regexp.last_match[1] + + # хорошо + Regexp.last_match(1) + ``` + +* + При определении методов доступа к коллекции, добавьте альтернативную форму, + чтобы оградить пользователей от необходимости проверки на `nil` перед доступом + к элементу коллекции. + [[link](#provide-alternate-accessor-to-collections)] + + ```Ruby + # плохо + def awesome_things + @awesome_things + end + + # хорошо + def awesome_things(index = nil) + if index && @awesome_things + @awesome_things[index] + else + @awesome_things + end + end + ``` + +## Строки + +* Используйте интерполяцию строк и форматные + шаблоны, а не конкатенацию строк. + [[ссылка](#string-interpolation)] + + ```Ruby + # плохо + email_with_name = user.name + ' <' + user.email + '>' + + # хорошо + email_with_name = "#{user.name} <#{user.email}>" + + # хорошо + email_with_name = format('%s <%s>', user.name, user.email) + ``` + +* + Избегайте пробелов внутри скобок вокруг интерполируемых выражений в строках. + [[ссылка](#string-interpolation)] + + ```Ruby + # плохо + "From: #{ user.first_name }, #{ user.last_name }" + + # хорошо + "From: #{user.first_name}, #{user.last_name}" + "#{ user.last_name }, #{ user.first_name }" + ``` + +* Постарайтесь внедрить единообразных + стиль кавычек для строчных литералов. В среде программистов на Руби есть два + популярных стиля, оба из них считаются приемлемыми. Стиль **А** подразумевает + одинарные кавычки по умолчанию, а стиль **B** двойные кавычки. + [[ссылка](#consistent-string-literals)] + + * **Стиль A:** Используйте одинарные кавычки, если вам не нужна интерполяция строк + или специальные символы вроде `\t`, `\n`, `'` и т.д. + + ```Ruby + # плохо + name = "Bozhidar" + + # хорошо + name = 'Bozhidar' + ``` + + * **Стиль B:** Используйте двойные кавычки в ваших строчных литералах, если они не + содержат `"` или экранируйте символы, которые не должны интерполироваться. + + ```Ruby + # плохо + name = 'Bozhidar' + + # хорошо + name = "Bozhidar" + ``` + + Второй стиль, по некоторым мнениям, более распространен среди разработчиков на + Руби. Однако в этом руководстве оформление строк следует первому правилу. + +* Не используйте запись для литералов + алфавитных символов `?x`. Начиная с версии Руби 1.9, этот вариант записи + избыточен: `?x` будет интерпретироваться в виде `'x'` (строка + с единственным символом в ней).[[ссылка](#no-character-literals)] + + ```Ruby + # плохо + char = ?c + + # хорошо + char = 'c' + ``` + +* Всегда применяйте фигурные скобки `{}` + вокруг глобальных переменных и переменных экземпляров класса при интерполяции + строк.[[ссылка](#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 + + # плохо (допустимо, но вычурно) + def to_s + "#@first_name #@last_name" + end + + # хорошо + def to_s + "#{@first_name} #{@last_name}" + end + end + + $global = 0 + # плохо + puts "$global = #$global" + + # хорошо + puts "$global = #{$global}" + ``` + +* Не используйте метод `Object#to_s` для интерполируемых + объектов, он вызывается автоматически при интерполяции. + [[ссылка](#no-to-s)] + + ```Ruby + # плохо + message = "This is the #{result.to_s}." + + # хорошо + message = "This is the #{result}." + ``` + +* Не применяйте метод `String#+`, когда вам нужно + собрать вместе большие отрезки строк. Вместо этого используйте `String#<<`. + Конкатенация изменяет экземпляр строки и всегда работает быстрее, чем `String#+`, + который создает целую кучу новых строковых объектов. + [[ссылка](#concat-strings)] + + ```Ruby + # хорошо и быстро + html = '' + html << '

Page title

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

#{paragraph}

" + end + ``` + +* + Избегайте метода `String#gsub` в случаях, когда можно использовать более + быстрый и специализированный альтернативный метод. + [[link](#dont-abuse-gsub)] + + ```Ruby + url = 'http://example.com' + str = 'lisp-case-rules' + + # плохо + url.gsub("http://", "https://") + str.gsub("-", "_") + + # хорошо + url.sub("http://", "https://") + str.tr("-", "_") + ``` + + +* + При использовании многострочных HEREDOC не забывайте, что пробелы в начале + строк тоже являются частью создаваемой строки. Примером хорошего стиля + является применение техник, основывающихся на ограничителях, для удаления + ненужных пробелов. + [[ссылка](#heredocs)] + + ```Ruby + code = <<-END.gsub(/^\s+\|/, '') + |def test + | some_method + | other_method + |end + END + #=> "def test\n some_method\n other_method\nend\n" + ``` + +## Регулярные выражения + +> Многие люди, встречаясь с проблемой, думают: +> "Я знаю решение, я применю регулярные выражения!" +> Теперь у них две проблемы.
+> -- Джейми Цавински / Jamie Zawinski + +* + Не используйте регулярные выражения, когда вам нужно просто найти в строке + подстроку: `string['text']`. + [[ссылка](#no-regexp-for-plaintext)] + +* + В простейших случаях вы просто можете использовать индексирование строк. + [[ссылка](#regexp-string-index)] + + ```Ruby + match = string[/regexp/] # Возвращает найденные совпадения. + first_group = string[/text(grp)/, 1] # Возвращает совпадения выделенной группы. + string[/text (grp)/, 1] = 'replace' # string => 'text replace' + ``` + +* Используйте группировку без сохранения, + если вы не планируете использовать содержание выделенной скобками группы. + [[ссылка](#non-capturing-regexp)] + + ```Ruby + /(first|second)/ # плохо + /(?:first|second)/ # хорошо + ``` + +* Откажитесь от использования наследия + Перла вроде мистических переменных, обозначающих группы совпадений (`$1`, `$2` + и т.д.). Вместо этого используйте `Regexp.last_match(n)`. + [[ссылка](#no-perl-regexp-last-matchers)] + + ```Ruby + /(regexp)/ =~ string + ... + + # плохо + process $1 + + # хорошо + process Regexp.last_match(1) + ``` + +* Применение пронумерованных групп + совпадений может быть сложной задачей. Вместо этого используйте поименованные + группы с говорящими именами.[[ссылка](#no-numbered-regexes)] + + ```Ruby + # плохо + /(regexp)/ =~ string + ... + process Regexp.last_match[1] + + # хорошо + /(?regexp)/ =~ string + ... + process meaningful_var + ``` + +* Классы символов используют лишь небольшой + набор метасимволов, которые вам придется обрабатывать: `^`, `-`, `\`, `]`, + поэтому нет нужды экранировать `.` или скобки внутри `[]`. + [[ссылка](#limit-escapes)] + +* Будьте осторожны с символами `^` и `$`, + так как они обозначают начало/конец строки в тексте, а не строчного литерала. + Если вам надо обозначить начало и конец литерала, то используйте `\A` и `\z`. + Не путайте `\Z` и `\z`: `\Z` является эквивалентом `/\n?\z/`. + [[ссылка](#caret-and-dollar-regexp)] + + ```Ruby + string = "some injection\nusername" + string[/^username$/] # есть совпадение + string[/\Ausername\z/] # нет совпадения + ``` + +* Используйте модификатор `x` для сложных регулярных + выражений. Он поможет вам сделать выражения удобочитаемыми и позволит добавлять + комментарии. Не забывайте при этом, что пробелы в данном случае игнорируются. + [[ссылка](#comment-regexes)] + + ```Ruby + regexp = / + start # какой-то текст + \s # знак пробела + (group) # первая группа + (?:alt1|alt2) # некоторая дизъюнкция + end + /x + ``` + +* В случае сложных замен либо подстановок `sub`/`gsub` + можно использовать с блоком или хешем параметров. + [[ссылка](#gsub-blocks)] + +## Процентные литералы + +* Используйте `%()` (это сокращение от `%Q`) + для строк без переносов, в которых реализуется интерполяция и присутствуют + двойные кавычки. Для строк с переносами лучше используйте формат HERE Doc. + [[ссылка](#percent-q-shorthand)] + + ```Ruby + # плохо (интерполяция не нужна) + %(
Some text
) + # должно быть '
Some text
' + + # плохо (нет двойных кавычек) + %(This is #{quality} style) + # должно быть "This is #{quality} style" + + # плохо (строка с переносами) + %(
\n#{exclamation}\n
) + # лучше применить HERE Doc + + # хорошо (необходима интерполяция, присутствуют кавычки, нет переносов) + %(#{name}) + ``` + +* + Избегайте `%q`, если это не случай строки с символами кавычек `'` и `"` + одновременно Обычные строки читаются проще, и их следует использовать, если + нет излишне большого количества символов, которые нужно будет экранировать. + [[ссылка](#percent-q)] + + ```Ruby + # плохо + name = %q(Bruce Wayne) + time = %q(8 o'clock) + question = %q("What did you say?") + + # хорошо + name = 'Bruce Wayne' + time = "8 o'clock" + question = '"What did you say?"' + ``` + +* + Используйте `%r` для регулярных выражений, которые обрабатывают *хотя бы один* + символ `/`, в остальных случаях используйте стандартный синтаксис. + [[ссылка](#percent-r)] + + ```Ruby + # плохо + %r{\s+} + + # хорошо + %r{^/(.*)$} + %r{^/blog/2011/(.*)$} + ``` + +* + Откажитесь от использования `%x` кроме случаев, когда вы хотите вызвать + внешнюю команду с обратными кавычками в теле (что само по себе маловероятно). + [[ссылка](#percent-x)] + + ```Ruby + # плохо + date = %x(date) + + # хорошо + date = `date` + echo = %x(echo `date`) + ``` + +* + Старайтесь избегать `%s`. По общепринятому мнению, предпочтительным способом + определения символа с пробелами в имени является `:"some string"`. + [[ссылка](#percent-s)] + +* Используйте `()` в качестве ограничителей + для всех литералов со знаком `%` кроме `%r`. Так как круглые скобки очень + часто используются в самих регулярных выражениях, во многих случаях менее + частый символ `{` может быть лучшим выбором для ограничителя (разумеется, + с учетом смысла регулярного выражения). + [[ссылка](#percent-literal-braces)] + + ```Ruby + # плохо + %w[one two three] + %q{"Test's king!", John said.} + + # хорошо + %w(one two three) + %q("Test's king!", John said.) + ``` + +## Метапрограммирование + +* Откажитесь от метапрограммирования + ради метапрограммирования как такового. + [[ссылка](#no-needless-metaprogramming)] + +* Не разводите беспорядок в базовых классах + при написании библиотек (не используйте "monkey patching"). + [[ссылка](#no-monkey-patching)] + +* Используйте `#class_eval` с блоком вместно + интерполяции значений в строке. Если вы используете интерполяцию, то всегда + указывайте дополнительно `__FILE__` и `__LINE__`, чтобы информация о стеке + вызова была осмысленной:[[ссылка](#block-class-eval)] + + ```Ruby + class_eval 'def use_relative_model_naming?; true; end', __FILE__, __LINE__ + ``` + + - `#define_method` предпочтительнее, чем `#class_eval { def ... }` + +* При использовании `#class_eval` (или других + `#eval`) с интерполяцией строк обязательно добавляйте комментарий, который + будет наглядно показывать, как интерполированные значения будут выглядеть + (примеры, используемые в исходном коде Rails):[[ссылка](#eval-comment-docs)] + + ```Ruby + # из 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 + ``` + +* Избегайте `#method_missing` для целей + метапрограммирования, так как стек вызова становится нечитаемым, метод не виден + в `#methods`, опечатки в вызовах методов пройдут незамеченными, например, + `nukes.launch_state = false`. Используйте делегирование, проксирование или же + `#define_method`. Если вы используете `#method_missing`: + [[ссылка](#no-method-missing)] + + - обязательно [задайте `#respond_to_missing?`](http://blog.marc-andre.ca/2010/11/methodmissing-politely.html); + - перехватывайте вызовы только с четко определенными префиксами, например, + `#find_by_*` -- задайте в своем коде наиболее узкие рамки для + неопределенностей; + - вызывайте `#super` в конце ваших выражений; + - делегируйте вызовы понятным, "немагическим" методам: + + ```Ruby + # плохо + def method_missing?(meth, *params, &block) + if /^find_by_(?.*)/ =~ meth + # ... lots of code to do a find_by + else + super + end + end + + # хорошо + def method_missing?(meth, *params, &block) + if /^find_by_(?.*)/ =~ meth + find_by(prop, *params, &block) + else + super + end + end + + # Самым лучшим будет все же использование `#define_method`, + # так как каждый видимый аргумент будет определен. + ``` + +## Разное + +* Пишите код, не дающий предупреждений при вызове + `ruby -w`.[[ссылка](#always-warn)] + +* Не используйте хеши в качестве + необязательных параметров. Возможно, ваш метод просто делает слишком много. + Это не касается, однако, методов инициализации объектов. + [[ссылка](#no-optional-hash-params)] + +* Старайтесь не писать методы длиннее 10 строк. В + идеальном случае большинство методов должны быть короче 5 строк. Пустные строки + не подсчитываются.[[ссылка](#short-methods)] + +* Не создаваете методы с более чем тремя-четырьмя + параметрами.[[ссылка](#too-many-params)] + +* Если вам действительно нужны глобальные + функции, включайте их в модуль Kernel и сделайте их приватными. + [[ссылка](#private-global-methods)] + +* Используйте переменные модулей вместо глобальных + переменных.[[ссылка](#instance-vars)] + + ```Ruby + # плохо + $foo_bar = 1 + + # хорошо + module Foo + class << self + attr_accessor :bar + end + end + + Foo.bar = 1 + ``` + +* Используйте `OptionParser` для анализа сложных + аргуметов командрой строки и `ruby -s` для элеметарных случаев. + [[ссылка](#optionparser)] + +* Используйте вариант `Time.now`, а не `Time.new`, + когда хотите получить текущее значение системного времени. + [[ссылка](#time-now)] + +* Пишите код в функциональном стиле без изменения + значений, когда это подходит по смыслу.[[ссылка](#functional-code)] + +* + Не изменяйте значения параметров, если только это не есть цель метода. + [[ссылка](#no-param-mutations)] + +* Старайтесь не создавать + вложенные структуры с уровнем вложения больше третьего. + [[ссылка](#three-is-the-number-thou-shalt-count)] + +* Будьте последовательны. В идеальном мире + последовательно придерживайтесь данного руководства. + [[ссылка](#be-consistent)] + +* Руководствуйтесь здравым смыслом. + [[ссылка](#common-sense)] + +## Инструментарий + +В этом разделе собраны инструменты, которые могут помочь вам автоматически +сверить ваш код на Руби с предписаниями этого руководства. + +### РубоКоп + +[RuboCop][] — это утилита проверки стиля +программного кода на Руби, который основывается на этом руководстве. +РубоКоп уже реализует большую часть этого руководства, поддерживает MRI 1.9 +и MRI 2.0 и хорошо интегрируется с редактором Емакс. + +### RubyMine + +Модуль проверки кода [RubyMine](http://www.jetbrains.com/ruby/) +[частично основывается](http://confluence.jetbrains.com/display/RUBYDEV/RubyMine+Inspections) +на этом руководстве. + +# Сотрудничество + +Ничто, описанное в этом руководстве, не высечено в камне. И я очень хотел бы +сотрудничать со всеми, кто интересуется стилистикой оформления кода на Руби, +чтобы мы смогли вместе создать ресурс, который был бы полезен для всего +сообщества программистов на Руби. + +Не стесняйтесь создавать отчеты об ошибках и присылать мне запросы на интеграцию +вашего кода. И заранее большое спасибо за вашу помощь! + +Вы можете поддержать проект (и РубоКоп) денежным взносом при помощи +[gittip](https://www.gittip.com/bbatsov). + +[![Дай Gittip](https://rawgithub.com/twolfson/gittip-badge/0.2.0/dist/gittip.png)](https://www.gittip.com/bbatsov) + +## Как сотрудничать в проекте? + +Это просто! Следуйте [руководству по +сотрудничеству](https://github.com/bbatsov/ruby-style-guide/blob/master/CONTRIBUTING.md). + +# Лицензирование + +![Creative Commons License](http://i.creativecommons.org/l/by/3.0/88x31.png) +Данная работа опубликована на условиях лицензии [Creative Commons Attribution +3.0 Unported License](http://creativecommons.org/licenses/by/3.0/deed.en_US) + +# Расскажи другому + +Создаваемое сообществом руководство по стилю оформления будет малопригодным для +сообщества, которое об этом руководстве ничего не знает. Делитесь ссылками на +это руководство с вашими друзьями и коллегами доступными вам средствами. Каждый +получаемый нами комментарий, предложение или мнение сделает это руководство еще +чуточку лучше. А ведь мы хотим самое лучшее руководство из возможных, не так ли? + +Всего,
+[Божидар](https://twitter.com/bbatsov) + + +[PEP-8]: http://www.python.org/dev/peps/pep-0008/ +[rails-style-guide]: https://github.com/arbox/rails-style-guide/blob/master/README-ruRU.md +[pickaxe]: http://pragprog.com/book/ruby4/programming-ruby-1-9-2-0 +[trpl]: http://www.ozon.ru/context/detail/id/5704300/ +[entrpl]: http://www.amazon.com/Ruby-Programming-Language-David-Flanagan/dp/0596516177 +[transmuter]: https://github.com/TechnoGate/transmuter +[RuboCop]: https://github.com/bbatsov/rubocop +[Liskov]: https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B8%D0%BD%D1%86%D0%B8%D0%BF_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B8_%D0%91%D0%B0%D1%80%D0%B1%D0%B0%D1%80%D1%8B_%D0%9B%D0%B8%D1%81%D0%BA%D0%BE%D0%B2 +[duck-typing]: https://ru.wikipedia.org/wiki/%D0%A3%D1%82%D0%B8%D0%BD%D0%B0%D1%8F_%D1%82%D0%B8%D0%BF%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F diff --git a/README.md b/README.md index dea7f9e15..2a53ea781 100644 --- a/README.md +++ b/README.md @@ -1,3478 +1,50 @@ -# Prelude +# Руби: руководство по стилю оформления -> Role models are important.
-> -- Officer Alex J. Murphy / RoboCop +Это руководство по оформлению кода на Руби дает передовые рекомендации. С его +помощью обычный программист на Руби будет создавать код, который с легкостью +смогут поддерживать и развивать другие обычные разработчики. -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. +Читайте [руководство][russian] на русском языке. -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. +[Оригинал][english] этого руководства был составлен [Божидаром Бацовым +][bbatsov]. Переводы данного руководства доступны на следующих языках: -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. +* [английский (исходная версия)][english] +* [вьетнамский](https://github.com/scrum2b/ruby-style-guide/blob/master/README-viVN.md) +* [испанский](https://github.com/alemohamad/ruby-style-guide/blob/master/README-esLA.md) +* [китайский традиционный](https://github.com/JuanitoFatas/ruby-style-guide/blob/master/README-zhTW.md) +* [китайский упрощенный](https://github.com/JuanitoFatas/ruby-style-guide/blob/master/README-zhCN.md) +* [корейский](https://github.com/dalzony/ruby-style-guide/blob/master/README-koKR.md) +* [немецкий](https://github.com/arbox/ruby-style-guide/blob/master/README-deDE.md) +* [французский](https://github.com/porecreat/ruby-style-guide/blob/master/README-frFR.md) +* [португальский](https://github.com/rubensmabueno/ruby-style-guide/blob/master/README-PT-BR.md) +* [русский (данный документ)](https://github.com/arbox/ruby-style-guide/blob/master/README-ruRU.md) +* [японский](https://github.com/fortissimo1997/ruby-style-guide/blob/japanese/README.ja.md) -By the way, if you're into Rails you might want to check out the complementary -[Ruby on Rails Style Guide][rails-style-guide]. +В дополнение к настоящему сборнику вас может заинтересовать +[Rails: руководство по стилю оформления](https://github.com/arbox/rails-style-guide/blob/master/README-ruRU.md). -# 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, and 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). +* https://github.com/bestie/objective-ruby-style-guide +* https://github.com/chneukirchen/styleguide/blob/master/RUBY-STYLE +* https://github.com/styleguide/ruby +* https://github.com/airbnb/ruby +* https://github.com/thoughtbot/guides/tree/master/style/ruby -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 1.9"][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. +* https://houndci.com/ -This style guide evolves over time as additional conventions are -identified and past conventions are rendered obsolete by changes in -Ruby itself. -Many projects have their own coding style guidelines (often derived -from this guide). In the event of any conflicts, such -project-specific guides take precedence for that project. +## Buttons -You can generate a PDF or an HTML copy of this guide using -[Transmuter][]. + +Follow @arbox -[RuboCop][] is a code analyzer, 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) -* [French](https://github.com/porecreat/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-koKO.md) -* [Portuguese](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/scrum2b/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) -* [Classes](#classes--modules) -* [Exceptions](#exceptions) -* [Collections](#collections) -* [Strings](#strings) -* [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/OS X 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, around `{` - and before `}`. 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 - [1, 2, 3].each { |e| puts e } - class FooError < StandardError; end - ``` - - The only exception, regarding operators, is the exponent operator: - - ```Ruby - # bad - e = M * c ** 2 - - # good - e = M * c**2 - ``` - - `{` and `}` deserve a bit of clarification, since they are used - for block and hash literals, as well as embedded expressions in - strings. For hash literals two styles are considered acceptable. - - ```Ruby - # good - space after { and before } - { one: 1, two: 2 } - - # good - no space after { and before } - {one: 1, two: 2} - ``` - - 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. - - As far as embedded expressions go, there are also two acceptable - options: - - ```Ruby - # good - no spaces - "string#{expr}" - - # ok - arguably more readable - "string#{ expr }" - ``` - - The first style is extremely more popular and you're generally - advised to stick with it. The second, on the other hand, is - (arguably) a bit more readable. As with hashes - pick one style - and apply it consistently. - -* - No spaces after `(`, `[` or before `]`, `)`. -[[link](#no-spaces-braces)] - - ```Ruby - some(arg).other - [1, 2, 3].size - ``` - -* - 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`. I know that many would disagree - with this one, but it's the style established in both "The Ruby - Programming Language" and "Programming Ruby". -[[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 a method - 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 - ``` - -* - 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/bbatsov/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 = ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', - 'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam'] - - # good - menu_item = [ - 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', - 'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam' - ] - - # good - menu_item = - ['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 - ``` - -* - Use RDoc and its conventions for API documentation. Don't put an - empty line between the comment block and the `def`. -[[link](#rdoc-conventions)] - -* - 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() - ``` - -* - Use `def` with parentheses when there are arguments. Omit the - parentheses when the method doesn't accept any arguments. -[[link](#method-parens)] - - ```Ruby - # bad - def some_method() - # body omitted - end - - # good - def some_method - # body omitted - end - - # bad - def some_method_with_arguments arg1, arg2 - # body omitted - end - - # good - def some_method_with_arguments(arg1, arg2) - # body omitted - end - ``` - -* - 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 - braces are required because of op precedence - x = (not something) - - # good - x = !something - ``` - -* - Avoid the use of `!!`. -[[link](#no-bang-bang)] - - ```Ruby - # bad - x = 'test' - # obscure nil check - if !!x - # body omitted - end - - x = false - # double negation is useless on booleans - !!x # => false - - # good - x = 'test' - unless x.nil? - # body omitted - end - ``` - -* - The `and` and `or` keywords are banned. It's just not worth it. Always use - `&&` and `||` instead. -[[link](#no-and-or-or)] - - ```Ruby - # bad - # boolean expression - if some_condition and some_other_condition - do_something - end - - # control flow - document.saved? or document.save! - - # good - # boolean expression - if some_condition && some_other_condition - do_something - end - - # control flow - document.saved? || document.save! - ``` - -* - 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 - ``` - -* - 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 an `if/unless/while/until`. -[[link](#no-parens-if)] - - ```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 parentheses around parameters for methods that are part of an internal - DSL (e.g. Rake, Rails, RSpec), methods that have "keyword" status in Ruby - (e.g. `attr_reader`, `puts`) and attribute access methods. Use parentheses - around the arguments of all other method invocations. -[[link](#no-dsl-parens)] - - ```Ruby - class Person - attr_reader :name, :age - - # omitted - end - - temperance = Person.new('Temperance', 30) - temperance.name - - puts temperance.age - - x = Math.sin(y) - array.delete(e) - - bowling.score.should == 0 - ``` - -* - 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 - ``` - -* - Omit parentheses for method calls with no arguments. -[[link](#no-args-no-parens)] - - ```Ruby - # bad - Kernel.exit!() - 2.even?() - fork() - 'test'.upcase() - - # good - Kernel.exit! - 2.even? - fork - 'test'.upcase - ``` - -* - 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](#single-line-blocks)] - - ```Ruby - names = ['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 { |name| name.upcase } - ``` - - Some will argue that multiline 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.) -[[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) - ... - end - - # good (MRI would still complain, but RuboCop won't) - if (v = array.grep(/foo/)) - do_something(v) - ... - end - - # good - v = array.grep(/foo/) - if v - do_something(v) - ... - 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 =~ /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 Fixnum 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 - ``` - -* - 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-as-args)] - -* - 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)] - -* - 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 - ``` - -* - 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) - # ... - end - - # good - result = hash.map { |_k, v| v + 1 } - - def something(x) - _unused_var, used_var = something_else(x) - # ... - end - - # good - result = hash.map { |_, v| v + 1 } - - def something(x) - _, used_var = something_else(x) - # ... - 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('%{first} %{second}', first: 20, second: 10) - # => '20 10' - - format('%d %d', 20, 10) - # => '20 10' - - # good - format('%{first} %{second}', first: 20, second: 10) - # => '20 10' - ``` - -* - Favor the use of `Array#join` over the fairly cryptic `Array#*` with -[[link](#array-join)] - a string argument. - - ```Ruby - # bad - %w(one two three) * ', ' - # => 'one, two, three' - - # good - %w(one two three).join(', ') - # => 'one, two, three' - ``` - -* - Use `[*var]` or `Array()` instead of explicit `Array` check, when dealing - with a variable you want to treat as an Array, but you're not certain it's an - array. -[[link](#splat-arrays)] - - ```Ruby - # bad - paths = [paths] unless paths.is_a? Array - paths.each { |path| do_something(path) } - - # good - [*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) - 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-fine-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 - ``` - -* - Use `reverse_each` instead of `reverse.each`. `reverse_each` doesn't do a - new array allocation and that's a good thing. -[[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 - ... - end - - def SomeMethod - ... - end - - # good - :some_symbol - - def some_method - ... - end - ``` - -* - Use `CamelCase` for classes and modules. (Keep acronyms like HTTP, RFC, XML - uppercase.) -[[link](#camelcase-classes)] - - ```Ruby - # bad - class Someclass - ... - end - - class Some_Class - ... - end - - class SomeXml - ... - end - - # good - class SomeClass - ... - end - - class SomeXML - ... - 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)] - -* - 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 using `reduce` with short blocks, name the arguments `|a, e|` - (accumulator, element). -[[link](#reduce-blocks)] - -* - When defining binary operators, name the argument `other`(`<<` and `[]` are - exceptions to the rule, since their semantics are different). -[[link](#other-arg)] - - ```Ruby - def +(other) - # 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](http://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.
-> -- Russ Olsen - -* - 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 two spaces after the `#`. -[[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)] - -## 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 - CustomErrorKlass = 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 - - # followed by 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 - ``` - -* - 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 - ``` - -* - 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 - end - end - - # good - module SomeModule - module_function - - def some_method - # body omitted - end - - def some_other_method - 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](http://en.wikipedia.org/wiki/Liskov_substitution_principle). -[[link](#liskov)] - -* - Try to make your classes as - [SOLID](http://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 - ``` - -* - Avoid the use of `attr`. Use `attr_reader` and `attr_accessor` instead. -[[link](#attr)] - - ```Ruby - # bad - creates a single attribute accessor (deprecated in 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 a `Struct.new` - it already is a new class. 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)] - -* - 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](http://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 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 - # ... - end - - private - - def private_method - # ... - end - - def another_private_method - # ... - end - end - ``` - -* - Use `def self.method` to define singleton methods. This makes the code - easier to refactor since the class name is not repeated. -[[link](#def-self-singletons)] - - ```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 singleton methods. - class << self - def first_method - # body omitted - end - - def second_method_etc - # body omitted - end - end - end - ``` - -## Exceptions - -* - Signal exceptions using the `fail` method. Use `raise` only when catching an - exception and re-raising it (because here you're not failing, but explicitly - and purposefully raising an exception). -[[link](#fail-method)] - - ```Ruby - begin - fail 'Oops' - rescue => error - raise if error.message != 'Oops' - end - ``` - -* - Don't specify `RuntimeError` explicitly in the two argument version of - `fail/raise`. -[[link](#no-explicit-runtimeerror)] - - ```Ruby - # bad - fail RuntimeError, 'message' - - # good - signals a RuntimeError by default - fail 'message' - ``` - -* - Prefer supplying an exception class and a message as two separate arguments - to `fail/raise`, instead of an exception instance. -[[link](#exception-class-messages)] - - ```Ruby - # bad - fail SomeException.new('message') - # Note that there is no way to do `fail SomeException.new('message'), backtrace`. - - # good - fail SomeException, 'message' - # Consistent with `fail 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 - def foo - begin - fail - ensure - return 'very bad idea' - end - 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 Exception => e - # some handling - rescue StandardError => e - # some handling that will never be executed - end - - # good - begin - # some code - rescue StandardError => e - # some handling - rescue Exception => e - # some handling - end - ``` - -* - Release external resources obtained by your program in an ensure block. -[[link](#file-close)] - - ```Ruby - f = File.open('testfile') - begin - # .. process - rescue - # .. handle error - ensure - f.close if f - end - ``` - -* - Favor the use of exceptions for 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 = [] - hash = {} - ``` - -* - 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?`. As noted - [here](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/43765) by - Matz, the longer forms are considered deprecated. -[[link](#hash-key)] - - ```Ruby - # bad - hash.has_key?(:test) - hash.has_value?(value) - - # good - hash.key?(:test) - hash.value?(value) - ``` - -* - 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 work correctly with falsy values - batman.fetch(:is_evil, true) # => false - ``` - -* - Prefer the use of the block instead of the default value in `Hash#fetch`. -[[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, get_batman_powers) # get_batman_powers is an expensive call - - # good - blocks are lazy evaluated, so only triggered in case of KeyError exception - batman.fetch(:powers) { get_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'] - nickname = 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)] - -## 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) - ``` - -* - Consider padding string interpolation code with space. It more clearly sets - the code apart from the string. -[[link](#pad-string-interpolation)] - - ```Ruby - "#{ user.last_name }, #{ user.first_name }" - ``` - -* - 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" - - # good - name = 'Bozhidar' - ``` - - * **(Option B)** Prefer double-quotes unless your string literal - contains `"` or escape characters you want to suppress. - - ```Ruby - # bad - name = 'Bozhidar' - - # good - name = "Bozhidar" - ``` - - The second style is arguably a bit more popular in the Ruby - community. The string literals in this guide, however, are - aligned with the first style. - -* - Don't use the character literal syntax `?x`. Since Ruby 1.9 it's basically - redundant - `?x` would 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 - # good and also fast - html = '' - html << '

Page title

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

#{paragraph}

" - end - ``` - -* - 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" - ``` - -## 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 captured result of parentheses. -[[link](#non-capturing-regexp)] - - ```Ruby - /(first|second)/ # bad - /(?:first|second)/ # good - ``` - -* - 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 - ... - process Regexp.last_match[1] - - # good - /(?regexp)/ =~ string - ... - 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 block or hash. -[[link](#gsub-blocks)] - -## 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 `%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?"' - ``` - -* - Use `%r` only for regular expressions matching *more than* one '/' - character. -[[link](#percent-r)] - - ```Ruby - # 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](#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)] - -* - Prefer `()` as delimiters for all `%` literals, except `%r`. Since parentheses - often appear inside regular expressions in many scenarios a less common - character like `{` might be a better choice for a delimiter, depending on the - regexp's content. -[[link](#percent-literal-braces)] - - ```Ruby - # bad - %w[one two three] - %q{"Test's king!", John said.} - - # good - %w(one two three) - %q("Test's king!", John said.) - ``` - -## Metaprogramming - -* - Avoid needless metaprogramming. -[[link](#no-metaprogramming-masturbation)] - -* - 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. - when you use the string-interpolated form, always supply `__FILE__` - and `__LINE__`, so that your backtraces make sense: -[[link](#block-class-eval)] - - ```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}(*args, &block) # def capitalize(*args, &block) - to_str.#{unsafe_method}(*args, &block) # to_str.capitalize(*args, &block) - end # end - - def #{unsafe_method}!(*args) # def capitalize!(*args) - @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, *args, &block) - if /^find_by_(?.*)/ =~ meth - # ... lots of code to do a find_by - else - super - end - end - - # good - def method_missing?(meth, *args, &block) - if /^find_by_(?.*)/ =~ meth - find_by(prop, *args, &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)] - -## 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 - ``` - -* - Avoid `alias` when `alias_method` will do. -[[link](#alias-method)] - -* - Use `OptionParser` for parsing complex command line options and `ruby -s` - for trivial command line options. -[[link](#optionparser)] - -* - Prefer `Time.now` over `Time.new` when retrieving the current system time. -[[link](#time-now)] - -* - Code in a functional way, avoiding mutation when that makes sense. -[[link](#functional-code)] - -* - Do not mutate arguments unless that is the purpose of the method. -[[link](#no-arg-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's some tools to help you automatically check Ruby code against -this guide. - -### RuboCop - -[RuboCop][] is a Ruby code style -checker based on this style guide. RuboCop already covers a -significant portion of the Guide, supports both MRI 1.9 and MRI 2.0 -and has good Emacs integration. - -### 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 [gittip](https://www.gittip.com/bbatsov). - -[![Support via Gittip](https://rawgithub.com/twolfson/gittip-badge/0.2.0/dist/gittip.png)](https://www.gittip.com/bbatsov) - -## How to Contribute? - -It's easy, just follow the [contribution guidelines](https://github.com/bbatsov/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]: http://www.python.org/dev/peps/pep-0008/ -[rails-style-guide]: https://github.com/bbatsov/rails-style-guide -[pickaxe]: http://pragprog.com/book/ruby4/programming-ruby-1-9-2-0 -[trpl]: http://www.amazon.com/Ruby-Programming-Language-David-Flanagan/dp/0596516177 -[transmuter]: https://github.com/TechnoGate/transmuter -[RuboCop]: https://github.com/bbatsov/rubocop +[russian]: https://github.com/arbox/ruby-style-guide/blob/master/README-ruRU.md +[english]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md +[bbatsov]: https://github.com/bbatsov diff --git a/glossary.md b/glossary.md new file mode 100644 index 000000000..e8246824b --- /dev/null +++ b/glossary.md @@ -0,0 +1,3 @@ +# Glossary + +* "body ommited" - "некоторый код"