An open API service indexing awesome lists of open source software.

https://github.com/arbox/ruby-style-guide

:blue_book: Russian Version: A community-driven Ruby coding style guide.
https://github.com/arbox/ruby-style-guide

bbatsov rubocop ruby russian style style-guide

Last synced: about 1 year ago
JSON representation

:blue_book: Russian Version: A community-driven Ruby coding style guide.

Awesome Lists containing this project

README

          

# Вступление

> Ролевые модели важны.

> -- Офицер Алекс Мёрфи / Робот-полицейский

Один из вопросов, который меня всегда беспокоил как разработчика на Руби,
— это то, что у разработчиков на Питоне есть великолепное руководство
по стилю оформления ([PEP-8][]), а у нас
никогда не было официального руководства, описывавшего бы стиль оформления кода
на Руби и дающего примеры его успешного применения. Я же уверен, что стиль
оформления крайне важен. Также я верю, что такое замечательное сообщество
разработчиков, которое есть у Руби, вполне имеет силы создать этот давно
назревший документ.

Это наставление появилось на свет в нашей фирме в виде внутреннего руководства
по оформлению кода на Руби (составленного вашим покорным слугой). И в какой-то
момент я решил, что данная работа, которой я тогда занимался, может быть
интересной и другим членам сообщества программистов на Руби и что миру вовсе
не нужно еще одно руководство для внутреннего пользования: окружающий мир может
получить пользу от совместно создаваемого и одобренного сообществом набора
практик, идиом и стилистических предписаний для программирования на Руби.

Со времени опубликования этого руководства я получил многочисленные отклики
от членов сообщества программистов на Руби из разных уголков со всего мира. Я
очень благодарен им за полезные предложения и поддержку! Нашими общими усилиями
мы сможем сделать этот ресурс полезным для всех и каждого разработчика на Руби.

И кстати, если вы работаете с Rails, вы можете взглянуть на дополняющее это
руководство [Ruby on Rails 3 & 4: Руководство по стилю оформления][rails-style-guide].

# :blue_book: Руби: руководство по стилю оформления

Это руководство по оформлению кода на Руби дает передовые рекомендации, так что
обычный программист на Руби сможет создавать код, который с легкостью смогут
поддерживать другие обычные программисты на Руби. Руководство по оформлению,
которое отражает повседневную практику, будет применяться постоянно, а руководство,
стремящееся к идеалу, который не принимается обычными людьми, подвергается риску
вообще быть забытым — не важно, насколько хорошим оно является.

Данное руководство разделено на несколько частей, состоящих из связанных по смыслу
правил. В каждом случае я попытался обосновать появление этих правил (объяснение
опущено в ситуациях, когда я посчитал его очевидным).

Все эти правила не появились из пустоты, они по большей части основываются на моем
собственном обширном профессиональном опыте в качестве разработчика ПО, отзывах
и предложениях других членов сообщества программистов на Руби и различных
общепризнанных источниках по программированию на Руби, например,
["Programming Ruby"][pickaxe] и ["Язык программирования Ruby"][trpl]
(в оригинале ["The Ruby Programming Language"][entrpl]).

Во многих областях до сих пор нет единого мнения в среде разработчиков на Руби
относительно конкретных аспектов стиля оформления (например, оформление строк
в кавычках, пробелы при оформлении хешей, месторасположение точки при
многострочном последовательном вызове методов и т.д.). В таких ситуациях мы
рассматривали все распространенные стили, вам же решать, какой из этих стилей
вы будете применять последовательно в вашем коде.

Это руководство все еще находится в процессе создания: у многих правил нет
примеров, у других нет примеров, достаточно ясно объясняющих эти правила. В свое
время каждое правило найдет свое объяснение, а пока просто примите их к сведению.

Вы можете создать копию этого руководства в форматах PDF или HTML при помощи
[Pandoc][].

[RuboCop][] — это анализатор кода, основывающийся на правилах этого
руководства по оформлению.

Переводы данного руководства доступны на следующих языках:

* [английский (исходная версия)](https://github.com/rubocop-hq/ruby-style-guide)
* [арабский (египетский)](https://github.com/HassanTC/ruby-style-guide/blob/master/README-EgAr.md)
* [вьетнамский](https://github.com/CQBinh/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/gauthier-delacroix/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)

## Оглавление

* [Организация исходного кода](#Организация-исходного-кода)
* [Синтаксис](#Синтаксис)
* [Наименование](#Наименование)
* [Комментарии](#Комментарии)
* [Пометки в комментариях](#Пометки-в-комментариях)
* [Магические комментарии](#Магические-комментарии)
* [Классы и модули](#Классы-и-модули)
* [Исключения](#Исключения)
* [Коллекции](#Коллекции)
* [Числа](#Числа)
* [Строки](#Строки)
* [Даты и время](#Даты-и-время)
* [Регулярные выражения](#Регулярные-выражения)
* [Процентные литералы](#Процентные-литералы)
* [Метапрограммирование](#Метапрограммирование)
* [Разное](#Разное)
* [Инструментарий](#Инструментарий)

## Организация исходного кода

> Почти все убеждены, что любой стиль кроме их собственного ужасен и нечитаем.
> Уберите отсюда "кроме их собственного" — и они будут, наверное, правы...

> -- Джерри Коффин (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/macOS
используют его по умолчанию, пользователям 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
class FooError < StandardError; end
```

Из этого правила есть несколько исключений. Одним из них является оператор возведения в степень:

```ruby
# плохо
e = M * c ** 2

# хорошо
e = M * c**2
```

Другим исключением является косая черта в литералах дробей:

```ruby
# плохо
o_scale = 1 / 48r

# хорошо
o_scale = 1/48r
```


Еще одним исключением является "safe navigation operator":

```ruby
# плохо
foo &. bar
foo &.bar
foo&. bar

# хорошо
foo&.bar
```

*
Не используйте пробел после `(`, `[` или перед `]`, `)`. Вставляйте
пробелы вокруг `{` и перед `}`.
[[ссылка](#spaces-braces)]

```ruby
# плохо
some( arg ).other
[ 1, 2, 3 ].each{|e| puts e}

# хорошо
some(arg).other
[1, 2, 3].each { |e| puts e }
```

Скобки `{` и `}` заслуживают некоторого пояснения, так как они
используются для обозначения блоков и литералов хешей, а также при
интерполяции строк.

Для литералов хешей два стиля являются общепринятыми. Первый
вариант несколько проще для чтения и, по всей вероятности, более
распространен среди членов сообщества программистов на Руби. Второй
вариант имеет преимущество в том, что создается видимое различие
между блоками и литералами хешей. Какой бы стиль вы ни выбрали,
применяйте его единообразно.

```ruby
# хорошо (пробел после { и до })
{ one: 1, two: 2 }

# хорошо (пробелы отсутствуют после { и перед })
{one: 1, two: 2}
```

В выражениях с интерполяцией избегайте лишних пробелов внутри скобок.

```ruby
# плохо
"From: #{ user.first_name }, #{ user.last_name }"

# хорошо
"From: #{user.first_name}, #{user.last_name}"
```

*
Не используйте пробел после `!`.
[[ссылка](#no-space-bang)]

```ruby
# плохо
! something

# хорошо
!something
```
*
Записывайте литералы диапазонов без пробелов.
[[ссылка](#no-space-inside-range-literals)]

```ruby
# плохо
1 .. 3
'a' ... 'z'

# хорошо
1..3
'a'...'z'
```

*
Делайте отступ для `when` таким же, как и для `case`. Этот стиль
предписывается как ["Языком программирования Ruby"][trpl], так и ["Programming Ruby"][pickaxe].
[[ссылка](#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
```

*
Не используйте несколько пустых строк подряд.
[[ссылка](#two-or-more-empty-lines)]

```ruby
# плохо (две пустые строки)
some_method

some_method

# хорошо
some_method

some_method
```

*
Отделяйте макросы доступа к данным пустой строкой.
[[ссылка](#empty-lines-around-access-modifier)]

```ruby
# плохо
class Foo
attr_reader :foo
def foo
# некоторый код
end
end

# хорошо
class Foo
attr_reader :foo

def foo
# некоторый код
end
end
```

*
Не оставляйте пустые строки вокруг тел методов, классов, модулей и блоков.
[[ссылка](#empty-lines-around-bodies)]

```ruby
# плохо
class Foo

def foo

begin

do_something do

something

end

rescue

something

end

end

end

# хорошо
class Foo
def foo
begin
do_something do
something
end
rescue
something
end
end
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=[])
# некоторый код
end

# хорошо
def some_method(arg1 = :default, arg2 = nil, arg3 = [])
# некоторый код
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/rubocop-hq/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 = %w[Spam Spam Spam Spam Spam Spam Spam Spam
Baked beans Spam Spam Spam Spam Spam]

# хорошо
menu_item = %w[
Spam Spam Spam Spam Spam Spam Spam Spam
Baked beans Spam Spam Spam Spam Spam
]

# хорошо
menu_item =
%w[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
```

*
Используйте строчные буквы в префиксах числовых записей: `0o` для
восьмеричных, `0x` для шестнадцатеричных и `0b` для двоичных чисел.
Не используйте `0d` префикс для десятичных литералов.
[[ссылка](#numeric-literal-prefixes)]

```ruby
# плохо
num = 01234
num = 0O1234
num = 0X12AB
num = 0B10101
num = 0D1234
num = 0d1234

# хорошо (проще визуально отделить числа от префикса)
num = 0o1234
num = 0x12AB
num = 0b10101
num = 1234
```

*
Используйте устоявшиеся правила [RDoc][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()
```

*
Не используйте `::` при определении методов класса.
[[ссылка](#colon-method-definition)]

```ruby
# плохо
class Foo
def self::some_method
end
end

# хорошо
class Foo
def self.some_method
end
end
```

*
Используйте `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
```

*
Используйте круглые скобки вокруг аргументов при вызове метода,
в особенности если первый аргумент начинается с символа `(`
(как например тут: `f((3 + 2) + 1)`)
[[ссылка](#method-invocation-parens)]

```ruby
# плохо
x = Math.sin y
# хорошо
x = Math.sin(y)

# плохо
array.delete e
# хорошо
array.delete(e)

# плохо
temperance = Person.new 'Temperance', 30
# хорошо
temperance = Person.new('Temperance', 30)
```

Всегда опускайте скобки в случаях,

* когда метод вызывается без аргументов:

```ruby
# плохо
Kernel.exit!()
2.even?()
fork()
'test'.upcase()

# хорошо
Kernel.exit!
2.even?
fork
'test'.upcase
```

* когда методы являются частью внутреннего DSL (т.е. `Rake`, `Rails`, `RSpec`):

```ruby
# плохо
validates(:name, presence: true)
# хорошо
validates :name, presence: true
```

* когда методы имеют статусы ключевых слов в Руби:

```ruby
class Person
# плохо
attr_reader(:name, :age)
# хорошо
attr_reader :name, :age

# некоторый код
end
```

Скобки можно опускать,

* когда методы имеют в Руби статус ключевого слова, но не являются декларативными:

```ruby
# хорошо
puts(temperance.age)
system('ls')
# тоже хорошо
puts temperance.age
system 'ls'
```

*
Определяйте необязательные аргументы в конце списка аргументов.
Способ, каким Руби обрабатывает необязательные аргументы при вызове метода,
может показаться вам неоднозначным, если они заданы в начале списка.
[[ссылка](#optional-arguments)]

```ruby
# плохо
def some_method(a = 1, b = 2, c, d)
puts "#{a}, #{b}, #{c}, #{d}"
end

some_method('w', 'x') # => '1, 2, w, x'
some_method('w', 'x', 'y') # => 'w, 2, x, y'
some_method('w', 'x', 'y', 'z') # => 'w, x, y, z'

# хорошо
def some_method(c, d, a = 1, b = 2)
puts "#{a}, #{b}, #{c}, #{d}"
end

some_method('w', 'x') # => '1, 2, w, x'
some_method('w', 'x', 'y') # => 'y, 2, w, x'
some_method('w', 'x', 'y', 'z') # => 'y, z, w, x'
```

*
Избегайте параллельного присвоения значений переменным. Параллельное
присвоение разрешается тогда, когда присваивается возвращаемое методом
значение совместно с оператором разобщения или значения переменных
взаимно переопределяются. Параллельное присвоение сложнее воспринимается, чем
обычная его форма записи.
[[ссылка](#parallel-assignment)]

```ruby
# плохо
a, b, c, d = 'foo', 'bar', 'baz', 'foobar'

# хорошо
a = 'foo'
b = 'bar'
c = 'baz'
d = 'foobar'

# хорошо (взаимное переопределение)
# Взаимное переопределение является особым случаем, так как помогает заместить
# оба задействованных значения.
a = 'foo'
b = 'bar'

a, b = b, a
puts a # => 'bar'
puts b # => 'foo'

# хорошо (возвращаемое значение)
def multi_return
[1, 2]
end

first, second = multi_return

# хорошо (применение оператора разобщения)
# first = 1, list = [2, 3, 4]
first, *list = [1, 2, 3, 4]

# ['Hello']
hello_array = *'Hello'

# [1, 2, 3]
a = *(1..3)
```
*
Избегайте ненужного использования нижних подчеркиваний в именах переменных
в конце параллельного присваивания. Именованные нижние подчеркивания
предпочтительнее безымянных, поскольку помогают понять контекст. Использовать
нижние подчеркивания в конце параллельного присваивания есть смысл, когда
в начале присваивания вы используете оператор разобщения в переменную
и хотите исключить какие-то значения.
[[ссылка]](#trailing-underscore-variables)

```ruby
# плохо
foo = 'one,two,three,four,five'
# Ненужное присваивание, не несущее к тому же полезной информации.
first, second, _ = foo.split(',')
first, _, _ = foo.split(',')
first, *_ = foo.split(',')

# хорошо
foo = 'one,two,three,four,five'
# Нижнее подчеркивание нужно, чтобы показать, что нам нужны все элементы
# кроме некоторого количества последних элементов.
*beginning, _ = foo.split(',')
*beginning, something, _ = foo.split(',')

a, = foo.split(',')
a, b, = foo.split(',')
# Ненужное присваивание значения неиспользуемой переменной, но это
# присваивание дает нам полезную информацию о данных.
first, _second = foo.split(',')
first, _second, = foo.split(',')
first, *_ending = foo.split(',')
```

*
Используйте оператор `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
x > 1
# некоторые действия
end

# хорошо
if x > 1
# некоторые действия
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 a == 1
result = x
else
result = y
end

# хорошо
result =
if a == 1
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)]

`!!` преобразует значение в логическое, однако зачастую в явном
преобразовании просто нет необходимости в контексте управляющего
выражения, его использование делает ваше намерение неявным.
Если вы хотите сделать проверку на `nil`, лучше используйте `nil?`.

```ruby
# плохо
x = 'test'
# неявная проверка на nil
if !!x
# некоторое выражение
end

# хорошо
x = 'test'
if x
# некоторое выражение
end
```

*
Ключевые слова `and` и `or` следует забыть. Минимальное улучшение ясности
написанного кода достигается за счет высокой вероятности сделать
сложнонаходимые ошибки. Для логических выражений всегда используйте `&&` и `||`
вместо них. Для управления ветвлением применяйте `if` и `unless`; `&&` и `||`
также допустимы, хотя и менее понятны.
[[ссылка](#no-and-or-or)]

```ruby
# плохо
# булево выражение
ok = got_needed_arguments and arguments_are_valid

# управление ветвлением
document.save or raise("Failed to save document!")

# хорошо
# булево выражение
ok = got_needed_arguments && arguments_are_valid

# управление ветвлением
raise("Failed to save document!") unless document.save

# сойдет
# управление ветвлением
document.save || raise("Failed to save document!")
```

*
Избегайте многострочных тернарных операторов `? :`.
Используйте вместо них `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
# некоторый код в несколько строк
end if some_condition

# хорошо
if some_condition
10.times do
# некоторый код в несколько строк
end
end
```

*
Избегайте вложенных модификаторов `if`/`unless`/`while`/`until`.
Используйте `&&`/`||` по необходимости.
[[ссылка](#no-nested-modifiers)]

```ruby
# плохо
do_something if other_condition if some_condition

# хорошо
do_something if some_condition && other_condition
```
*
Используйте `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
```

*
Не используйте скобки вокруг условных выражений в управляющих конструкциях.
[[ссылка](#no-parens-around-condition)]

```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
```

*
Не используйте фигурные скобки для ограничения хешей, передаваемых методу.
[[ссылка](#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
```

*
Используйте краткую форму для вызова `proc`, если вызываемый метод является
единственным в блоке.
[[ссылка](#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` везде, где оно не требуется. Оно необходимо
только при вызове методов доступа для записи, методов, совпадающих с ключевыми
словами, и перегружаемых операторов.
[[ссылка](#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

# сносно
# как 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?`, достаточно редко нужна на практике.
[[ссылка](#eql)]

```ruby
# плохо (`eql?` работает для строк, как и `==`)
'ruby'.eql? some_str

# хорошо
'ruby' == some_str
1.0.eql? x # здесь `eql?` имеет смысл, если вы хотите различать классы числа: `Integer` 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
```

*
Всегда вызывайте интерпретатор Руби с ключом `-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
```
*
Используйте скобки при определении `lambda` с аргументами.
[[ссылка](#stabby-lambda-with-args)]

```ruby
# плохо
l = ->x, y { something(x, y) }

# хорошо
l = ->(x, y) { something(x, y) }
```
*
Не используйте скобки при определении `lambda` без аргументов.
[[ссылка](#stabby-lambda-no-args)]

```ruby
# плохо
l = ->() { something }

# хорошо
l = -> { something }
```

*
Используйте `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('%d %d', first: 20, second: 10)
# => '20 10'

format('%d %d', 20, 10)
# => '20 10'

# хорошо
format('%d %d', first: 20, second: 10)
# => '20 10'
```

*
Используйте формат `%s` вместо `%{name}` для поименованных
переменных в шаблонах, это даст информацию о типе используемого значения.
[[ссылка](#named-format-tokens)]

```ruby
# плохо
format('Hello, %{name}', name: 'John')

# хорошо
format('Hello, %s', name: 'John')
```

*
Используйте `Array#join` вместо достаточно неочевидного `Array#*` со строковым
аргументом.
[[ссылка](#array-join)]

```ruby
# плохо
%w[one two three] * ', '
# => 'one, two, three'

# хорошо
%w[one two three].join(', ')
# => 'one, two, three'
```

*
Используйте явное приведение типов `Array()` (вместо явной проверки
на принадлежность к `Array` или `[*var]`), когда вам приходится
работать с переменной, которая по вашим ожиданиям должна быть
массивом, но вы в этом не полностью уверены.
[[ссылка](#array-coertion)]

```ruby
# плохо
paths = [paths] unless paths.is_a? Array
paths.each { |path| do_something(path) }

# плохо (всегда создает новый объект `Array`)
[*paths].each { |path| do_something(path) }

# хорошо (и более читаемо)
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 == 0
end

# хорошо
if x.even?
end

if x.odd?
end

if x.nil?
end

if x.zero?
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[:foo])
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
var_10 = 10

def someMethod
# некоторый код
end

def SomeMethod
# некоторый код
end

# хорошо
:some_symbol

some_var = 5
var10 = 10

def some_method
# некоторый код
end
```

*
Не разделяйте числа и буквы в именах символов, методов и переменных.
[[ссылка](#snake-case-symbols-methods-vars-with-numbers)]

```ruby
# плохо
:some_sym_1

some_var_1 = 1

def some_method_1
# некоторый код
end

# хорошо
:some_sym1

some_var1 = 1

def some_method1
# некоторый код
end
```

*
Используйте `CamelCase` для имен классов и модулей. Сокращения вроде `HTTP`,
`RFC`, `XML` набирайте заглавными буквами.
[[ссылка](#camelcase-classes)]

```ruby
# плохо
class Someclass
# некоторый код
end

class Some_Class
# некоторый код
end

class SomeXml
# некоторый код
end

class XmlSomething
# некоторый код
end

# хорошо
class SomeClass
# некоторый код
end

class SomeXML
# некоторый код
end

class XMLSomething
# некоторый код
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)]

*
Не используйте избыточные наименования предикатных методов вроде
`is_`, `does_` или `can_`. Такие префиксы не соответствуют
конвенциям, принятым в стандартной библиотеке Руби (`#empty?` или
`#include?`, например).
[[ссылка](#bool-methods-prefix)]

```ruby
# плохо
class Person
def is_tall?
true
end

def can_play_basketball?
false
end

def does_like_candy?
true
end
end

# хорошо
class Person
def tall?
true
end

def basketball_player?
false
end

def likes_candy?
true
end
end
```

*
Идентификаторы потенциально *опасных* методов, т.е. таких методов, которые
могут изменить `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
```

*
При определении бинарных операторов называйте параметр `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)]

*
Комментарии длиной больше одного слова должны оформляться в виде законченных
предложений (с большой буквы и со знаками препинания).
Разделяйте предложения [одним пробелом](https://en.wikipedia.org/wiki/Sentence_spacing).
[[ссылка](#english-syntax)]

*
Избегайте избыточного комментирования.
[[ссылка](#no-superfluous-comments)]

```ruby
# плохо
counter += 1 # Увеличивает счетчик на единицу.
```

*
Актуализируйте существующие комментарии. Устаревший комментарий гораздо хуже
отсутствующего комментария.
[[ссылка](#comment-upkeep)]

> Хороший код подобен хорошей шутке: он не нуждается в пояснениях.

> -- Рус Ольсен (Russ Olsen)
> — афоризм старого программиста, взято у [Руса Ольсена](http://eloquentruby.com/blog/2011/03/07/good-code-and-good-jokes/)

*
Не пишите комментарии для объяснения плохого кода. Перепишите код, чтобы он
говорил сам за себя.
[[ссылка](#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)]

### Магические комментарии

*
Размещайте "магические" комментарии над всем кодом и документацией. Исключением
является только строка вызовом интерпретатора (Shebang), о чем речь пойдет далее.
[[ссылка](#magic-comments-first)]

```ruby
# плохо
# Some documentation about Person

# frozen_string_literal: true
class Person
end

# хорошо
# frozen_string_literal: true

# Some documentation about Person
class Person
end
```
*
Размещайте магические комментарии под строкой вызова интерпретатора (Shebang),
если она есть в тексте.
[[ссылка](#below-shebang)]

```ruby
# хорошо
#!/usr/bin/env ruby
# frozen_string_literal: true

App.parse(ARGV)

# плохо
# frozen_string_literal: true
#!/usr/bin/env ruby

App.parse(ARGV)
```

*
Каждый "магический" комментарий должен быть на отдельной строке.
[[ссылка](#one-magic-comment-per-line)]

```ruby
# хорошо
# frozen_string_literal: true
# encoding: ascii-8bit

# плохо
# -*- frozen_string_literal: true; encoding: ascii-8bit -*-
```

*
Отделяйте "магические" комментарии пустой строкой от последующего кода или
комментариев.
[[ссылка](#separate-magic-comments-from-code)]

```ruby
# хорошо
# frozen_string_literal: true

# Some documentation for Person
class Person
# Some code
end

# плохо
# frozen_string_literal: true
# Some documentation for Person
class Person
# Some code
end
```

## Классы и модули

*
Придерживайтесь единообразной структуры классов.
[[ссылка](#consistent-classes)]

```ruby
class Person
# extend и include в начале
extend SomeModule
include AnotherModule

# вложенные классы
CustomError = 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
```

*
Каждый включаемый модуль должен быть включен отдельным выражением.
[[ссылка](#mixin-grouping)]

```ruby
# плохо
class Person
include Foo, Bar
end

# хорошо
class Person
# каждый миксин включается отдельным выражением
include Foo
include Bar
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
```

*
Определяйте (и открывайте заново) классы и модули, определенные в некотором
пространстве имен, с помощью явного вложения. Оператор разрешения пространства
(scope resolution) может создать трудные для понимания ситуации из-за
[лексического определения пространств имен](https://cirw.in/blog/constant-lookup.html)
в Руби. Пространство имен зависит для модуля от места его определения.
[[ссылка](#namespace-definition)]

```ruby
module Utilities
class Queue
end
end

# плохо
class Utilities::Store
Module.nesting # => [Utilities::Store]

def initialize
# Refers to the top level ::Queue class because Utilities isn't in the
# current nesting chain.
@queue = Queue.new
end
end

# хорошо
module Utilities
class WaitingList
Module.nesting # => [Utilities::WaitingList, Utilities]

def initialize
@queue = Queue.new # Refers to Utilities::Queue
end
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][SOLID],
если такая возможность есть.
[[ссылка](#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
```

*
Для методов доступа и назначения переменных избегайте наименований с
префиксами `get_` and `set_`. В Руби принято называть методы
доступа по именам соответствующих переменных, а методы назначения с
использованием `=`, например `attr_name=`.
[[ссылка](#accessor_mutator_method_names)]

```ruby
# плохо
class Person
def get_name
"#{@first_name} #{@last_name}"
end

def set_name(name)
@first_name, @last_name = name.split(' ')
end
end

# хорошо
class Person
def name
"#{@first_name} #{@last_name}"
end

def name=(name)
@first_name, @last_name = name.split(' ')
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`. В этом случае уже создается
новый класс. При дополнении вы создадите избыточный уровень абстракции, это
может п