02 November 2014

Вступление

Role models are important.
– Офицер Алекс Мёрфи / Робот-полицейский

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

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

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

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

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

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

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

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

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

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

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

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

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

Оглавление

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

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)

  • Используйте UTF-8 в качестве кодировки для исходного кода.[ссылка]

  • Используйте два пробела на уровень отступа (т.е. мягкую табуляцию). Никаких знаков табуляции. [ссылка]

    ```Ruby # плохо (четыре пробела) def some_method do_something end

    хорошо

    def some_method do_something end ```

  • Используйте стиль Unix для строк (пользователи *BSD/Solaris/Linux/OS X используют их по умолчанию, пользователям Windows нужно обратить особое внимание).[ссылка]

    • Если вы используете Git, вы можете добавить следующие настройки в вашу конфигурацию, чтобы предотвратить ненамеренное проникновение в ваш код строк, оканчивающихся в стиле Windows:

      bash $ git config --global core.autocrlf true

  • Не используйте ; для разделения директив и выражений. Отсюда непосредсвенно следует, что каждая директива должна занимать свою отдельную строку.[ссылка]

    ```Ruby # плохо (точка с запятой избыточна) puts ‘foobar’;

    puts ‘foo’; puts ‘bar’ # две директивы на одной строке

    хорошо

    puts ‘foobar’

    puts ‘foo’ puts ‘bar’

    puts ‘foo’, ‘bar’ # это частное правило для puts ```

  • Используйте преимущественно однострочный формат для определений классов с пустым телом. [ссылка]

    ```Ruby # плохо class FooError < StandardError end

    сносно

    class FooError < StandardError; end

    хорошо

    FooError = Class.new(StandardError) ```

  • Избегайте однострочных методов. И хотя они достаточно популярны в среде программистов, существует множество неприятных мелочей, связанных с синтаксисом их определения, которые делают применение таких методов нежелательным. В любом случае однострочные методы не должны содержать больше одного выражения. [ссылка]

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

  • Вставляйте пробелы вокруг операторов, после запятых, двоеточий и точек с запятыми, вокруг { и перед }. Пробелы (по большей части) игнорируются интерпретатором Руби, но их правильное использование является ключом к написанию легко читаемого кода. [ссылка]

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

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

    В случае включаемых в строки выражений существует также два приемлемых варианта:

    ```Ruby # хорошо (без пробелов) “string#{expr}”

    ok (наверное, читается лучше)

    “string#{ expr }” ```

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

  • Не используйте пробел после (, [ или перед ], ).[ссылка]

    Ruby some(arg).other [1, 2, 3].size

  • Не используйте пробел после !.[ссылка]

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

    хорошо

    !something ```

  • Записывайте литералы диапазонов без пробелов.[link]

    ```Ruby # плохо 1 .. 3 ‘a’ … ‘z’

    # хорошо 1..3 ‘a’..’z’ ```

  • Делайте отступ для when таким же, как и для case. Я знаю, что многие не согласятся с этим, то этот стиль предписывается как “Языком программирования Ruby”, так и “Programming Ruby”.[ссылка]

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

  • Присваивая результат условного выражения переменной, сохраняйте соответствие уровней отступа.[ссылка]

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

  • Используйте пустые строки для разделения определений методов и выделения логических частей определений внутри них.[ссылка]

    ```Ruby def some_method data = initialize(options)

    data.manipulate!

    data.result end

    def some_method result end ```

  • Избегайте запятых после последнего параметра в вызове метода, особенно когда параметры расположены в отдельных строках.[ссылка]

    ```Ruby # плохо, хотя проще перемещать/добавлять/удалять строки some_method( size, count, color, )

    плохо

    some_method(size, count, color, )

    хорошо

    some_method(size, count, color) ```

  • Вставляйте пробелы вокруг оператора присваивания =, когда назначаете параметрам метода значения по умолчанию:[ссылка]

    ```Ruby # плохо def some_method(arg1=:default, arg2=nil, arg3=[]) # do something… end

    хорошо

    def some_method(arg1 = :default, arg2 = nil, arg3 = []) # do something… end ```

    Хотя в некоторых книгах по Ruby рекомендуют первый стиль, второй гораздо более нагляден.

  • Не используйте символ продления строк \ везде, где можно обойтись без него. Практически не используйте его нигде, кроме как при конкатенации строк.[ссылка]

    ```Ruby # плохо result = 1 - \ 2

    возможно (но ужасно)

    result = 1 \ - 2

    long_string = ‘First part of the long string’ \ ‘ and second part of the long string’ ```

  • Используйте единый стиль многострочных последовательных цепочек вызовов методов. В сообществе Руби популярны два взаимоисключающих стиля их оформления: с точкой в начале (вариант A) и с точкой в конце (вариант B). [ссылка]

    • A Когда продолжаете цепочку вызовов методов на следующую строку, начинайте её с точки.

      ```Ruby # плохо - нужно посмотреть на предыдущую строку, чтобы понять # смысл последующей one.two.three. four

      хорошо - сразу ясно, что происходит во второй строке

      one.two.three .four ```

    • B Соответственно, наоборот, когда продолжаете цепочку вызовов на следующей строке, завершайте строку точкой ., давая понять, что продолжение выражения следует

      ```Ruby # плохо - чтобы понять, что выражение не окончено, необходимо # посмотреть на следующую строку. one.two.three .four

      хорошо - сразу видно, что выражение будет продолжено на

      # следующей строке one.two.three. four ```

C аргументами за и против обоих стилей можно ознакомиться в дискуссии здесь.

  • Выравнивайте параметры вызова метода, если вызов занимает более одной строки. Если выравнивание невозможно из-за ограничений на длину строки, то используйте одинарный отступ. [ссылка]

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

  • Выравнивайте элементы литералов массива, если они занимают несколько строк.[ссылка]

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

  • Добавляйте символ подеркивания в большие числовые константы для улучшения их восприятия. [ссылка]

    ```Ruby # плохо (Сколько тут нолей?) num = 1000000

    хорошо (число воспринимается гораздо легче)

    num = 1_000_000 ```

  • Используйте устоявшиеся правила RDoc для описания интерфейсов. Не отделяйте блок комментария от начала определения метода def пустой строкой.[ссылка]

  • Ограничивайте длину строк 80-ю символами.[ссылка]

  • Не оставляйте пробелы в конце строки. [ссылка]

  • Завершайте каждый файл переводом строки. [ссылка]

  • Не пользуйтесь блочными комментариями. Их нельзя разместить на необходимом уровне отступа. К тому же их сложнее воспринимать, чем обычные комментарии.[ссылка]

    ```Ruby # плохо =begin строка комментария еще одна строка комментария =end

    хорошо

    # строка комментария # другая строка комментария ```

Синтаксис

  • Используйте :: только для обращения к константам (в том числе к классам и модулям) и конструкторам класса (например, Array() or Nokogiri::HTML()). Никогда не используйте :: для обычного вызова методов.[ссылка]

    ```Ruby # плохо SomeClass::some_method some_object::some_method

    хорошо

    SomeClass.some_method some_object.some_method SomeModule::SomeClass::SOME_CONST SomeModule::SomeClass() ```

  • Используйте def со скобками, когда у метода есть аргументы. Опускайте скобки, когда метод не принимает аргументов. [ссылка]

    ```Ruby # плохо def some_method() # body omitted end

    # хорошо def some_method # body omitted end

    # плохо def some_method_with_arguments arg1, arg2 # body omitted end

    # хорошо def some_method_with_arguments(arg1, arg2) # body omitted end ```

  • Используйте оператор for только в случаях, когда вы точно знаете, зачем вы это делаете. В подавляющем большинстве остальных случаев стоит применять итераторы. Оператор for реализуюется при помощи each (таким образом вы добавляете еще один уровень абстракции), но с некоторыми отличиями: не создается отдельная область видимости (в отличии от each) и переменные, объявленные в теле for, будут видны за пределами блока. [ссылка]

    ```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, объявленных на нескольких строках.[ссылка]

    ```Ruby # плохо if some_condition then # некоторое действие end

    хорошо

    if some_condition # некоторое действие end ```

  • Всегда записывайте условие для if/unless на той же строке, что содержит if/then в многострочном условии. [ссылка]

    ```Ruby # плохо if some_condition do_something do_something_else end

    хорошо

    if some_condition do_something do_something_else end ```

  • Предпочитайте тернарный оператор (?:) конструкциям с if/then/else/end. Он используется чаще и по определению более краток.[ссылка]

    ```Ruby # плохо result = if some_condition then something else something_else end

    хорошо

    result = some_condition ? something : something_else ```

  • Используйте только одно выражение в каждой ветви тернарного оператора. Отсюда следует, что лучше избегать вложенных тернарных операторов. При возникновении такой необходимости применяйте конструкции с if/else.[ссылка]

    ```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 эту синтаксическую конструкцию удалии, используйте вместо нее тернарные операторы.[ссылка]

    ```Ruby # плохо result = if some_condition: something else something_else end

    хорошо

    result = some_condition ? something : something_else ```

  • Не используйте точку с запятой в if x; .... Применяйте тернарные операторы.[ссылка]

  • Применяйте на пользу тот факт, что if and case являются выражениями, возвращающими результирующие значения. [ссылка]

    ```Ruby # плохо if condition result = x else result = y end

    хорошо

    result = if condition x else y end ```

  • Применяйте when x then ... для однострочных выражений. Вариант записи when x: ... был удален, начиная с Руби 1.9. [ссылка]

  • Не используйте when x; ... по аналогии с предыдущим правилом.[ссылка]

  • Используйте ! вместо not. [ссылка]

    ```Ruby # плохо (необходимы скобки из-за неоднозначности приоритетов операторов) x = (not something)

    хорошо

    x = !something ```

  • Не используйте !!. [ссылка]

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

    x = false # двойное отрицание бессмысленно для булевых значений !!x # => false

    хорошо

    x = ‘test’ unless x.nil? # некоторое выражение end ```

  • Ключевые слова and и 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.[ссылка]

  • Для однострочных выражений по возможности модификатор if/unless. Другим хорошим вариантом являются операторы управления потоком исполнения &&/||.[ссылка]

    ```Ruby # плохо if some_condition do_something end

    хорошо

    do_something if some_condition

    еще хороший вариант

    some_condition && do_something ```

  • Избегайте if/unless в конце нетривиального многострочного блока. [ссылка]

    ```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 для отрицательных условий (или || для управления потоком исполнения). [ссылка]

    ```Ruby # плохо do_something if !some_condition

    плохо

    do_something if not some_condition

    хорошо

    do_something unless some_condition

    тоже хорошо

    some_condition || do_something ```

  • Не используйте unless вместе с else. Перепишите такие выражение с положительной проверкой. [ссылка]

    ```Ruby # плохо unless success? puts ‘failure’ else puts ‘success’ end

    хорошо

    if success? puts ‘success’ else puts ‘failure’ end ```

  • Не используйте скобки для ограничения условных выражений в if/unless/while/until.[ссылка]

    ```Ruby # плохо if (x > 10) # код опущен для краткости end

    хорошо

    if x > 10 # код опущен для краткости end ``` Однако в этом правиле есть некоторые исключения, например, надежные присвоения в условных выражениях.

  • Не используйте while/until УСЛОВИЕ do для многострочных циклов с while/until. [ссылка]

    ```Ruby # плохо while x > 5 do # код опущен для краткости end

    until x > 5 do # код опущен для краткости end

    хорошо

    while x > 5 # код опущен для краткости end

    until x > 5 # код опущен для краткости end ```

  • Используйте while/until для однострочный выражений.[ссылка]

    ```Ruby # плохо while some_condition do_something end

    хорошо

    do_something while some_condition ```

  • Используйте until вместо while для условий на отрицания.[ссылка]

    ```Ruby # плохо do_something while !some_condition

    хорошо

    do_something until some_condition ```

  • Используйте Kernel#loop вместо while/until для бесконечного цикла.[ссылка]

    ```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 для циклов с постусловием. [ссылка]

    ```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 и при вызове аксессоров. Используйте скобки при вызове прочих методов. [ссылка]

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

  • Не используйте фигурные скобки для ограничения хешей, передаваемых методу.[ссылка]

    ```Ruby # плохо user.set({ name: ‘John’, age: 45, permissions: { read: true } })

    хорошо

    user.set(name: ‘John’, age: 45, permissions: { read: true }) ```

  • Не используйте фигурные скобки для ограничения хешей, передаваемых методу, и скобки вокруг параметров для методов, являющихся частью DSL.[ссылка]

    ```Ruby class Person < ActiveRecord::Base # плохо validates(:name, { presence: true, length: { within: 1..10 } })

    # хорошо validates :name, presence: true, length: { within: 1..10 } end ```

  • Опускайте скобки при вызове метода без параметров. [ссылка]

    ```Ruby # плохо Kernel.exit!() 2.even?() fork() ‘test’.upcase()

    хорошо

    Kernel.exit! 2.even? fork ‘test’.upcase ```

  • Используйте преимущественно {...} в случае одностроных блоков, а do...end в случае многострочных блоков (многострочные последовательности вызовов методов всегда выглядят ужасно). Старайтесь применять do...end для логических операций и определений методов (например, для Rakefile и некоторых DSL). Не используйте do...end в цепочках вызовов. [ссылка]

    ```Ruby names = [‘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 { |name| name.upcase } ```

    Некоторые из нас поспорят, что многострочные последовательные вызовы с блоками при использовании {…} выглядят неплохо, но тогда стоит себя спросить, а читается ли такой код и не стоит ли выделить эти блоки в отдельные специальные методы.

  • 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. [ссылка]

    ```Ruby require ‘tempfile’

    плохо

    def with_tmp_dir Dir.mktmpdir do |tmp_dir| Dir.chdir(tmp_dir) { |dir| yield dir } # block just passes arguments 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 is accessible as a parameter and pwd is set: #{dir}” end ```

  • Avoid return where not required for flow of control.[ссылка]

    ```Ruby # плохо def some_method(some_arr) return some_arr.size end

    хорошо

    def some_method(some_arr) some_arr.size end ```

  • Avoid self where not required. (It is only required when calling a self write accessor.) [ссылка]

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

  • As a corollary, avoid shadowing methods with local variables unless they are both equivalent.[ссылка]

    ```Ruby class Foo attr_accessor :options

    # ok def initialize(options) self.options = options # both options and self.options are equivalent here 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 ```

  • 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.[ссылка]

    ```Ruby # плохо (+ a warning) if v = array.grep(/foo/) do_something(v) … end

    хорошо (MRI would still complain, but RuboCop won’t)

    if (v = array.grep(/foo/)) do_something(v) … end

    хорошо

    v = array.grep(/foo/) if v do_something(v) … end ```

  • Use shorthand self assignment operators whenever applicable.[ссылка]

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

  • Use ||= to initialize variables only if they’re not already initialized.[ссылка]

    ```Ruby # плохо name = name ? name : ‘Bozhidar’

    плохо

    name = ‘Bozhidar’ unless name

    хорошо - 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.) [ссылка]

    ```Ruby # плохо - would set enabled to true even if it was false enabled ||= true

    хорошо

    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.[ссылка]

    ```Ruby # плохо if something something = something.downcase end

    плохо

    something = something ? something.downcase : nil

    сносно

    something = something.downcase if something

    хорошо

    something = something && something.downcase

    еще лучше

    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. [ссылка]

    ```Ruby # плохо Array === something (1..100) === 7 /something/ === some_string

    хорошо

    something.is_a?(Array) (1..100).include?(7) some_string =~ /something/ ```

  • 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. [ссылка]

    ```Ruby # плохо $:.unshift File.dirname(FILE)

    хорошо

    require ‘English’ $LOAD_PATH.unshift File.dirname(FILE) ```

  • Never put a space between a method name and the opening parenthesis.[ссылка]

    ```Ruby # плохо f (3 + 2) + 1

    хорошо

    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). [ссылка]

  • Always run the Ruby interpreter with the -w option so it will warn you if you forget either of the rules above! [ссылка]

  • Use the new lambda literal syntax for single line body blocks. Use the lambda method for multi-line blocks. [ссылка]

    ```Ruby # плохо 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

    хорошо

    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.[ссылка]

    ```Ruby # плохо p = Proc.new { |n| puts n }

    хорошо

    p = proc { |n| puts n } ```

  • Prefer proc.call() over proc[] or proc.() for both lambdas and procs.[ссылка]

    ```Ruby # плохо - looks similar to Enumeration access l = ->(v) { puts v } l[1]

    also плохо - uncommon syntax

    l = ->(v) { puts v } l.(1)

    хорошо

    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. [ссылка]

    ```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 являются константами, поэтому при их переопределении (вы это можете сделать, например, для перенаправления ввода-вывода) интерпретатор будет выдавать предупреждения. [ссылка]

  • Используйте warn вместо $stderr.puts. Это не только короче, но и позволит вам скрыть все предупреждения, если вам это понадобится (для этого задайте уроверь предупреждений равный 0 при помощи опции -W0). [ссылка]

  • Используйте sprintf и его алиас format вместо довольно запутанного метода String#%.[ссылка]

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

  • Favor the use of Array#join over the fairly cryptic Array#* with a string argument. [ссылка]

    ```Ruby # плохо %w(one two three) * ‘, ‘ # => ‘one, two, three’

    хорошо

    %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. [ссылка]

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

  • Use ranges or Comparable#between? instead of complex comparison logic when possible.[ссылка]

    ```Ruby # плохо do_something if x >= 1000 && x <= 2000

    хорошо

    do_something if (1000..2000).include?(x)

    хорошо

    do_something if x.between?(1000, 2000) ```

  • Favor the use of predicate methods to explicit comparisons with ==. Numeric comparisons are OK. [ссылка]

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

  • Don’t do explicit non-nil checks unless you’re dealing with boolean values.[ссылка]

    ```Ruby # плохо do_something if !something.nil? do_something if something != nil

    # хорошо do_something if something

    # хорошо - dealing with a boolean def value_set? !@some_boolean.nil? end ```

  • Avoid the use of BEGIN blocks.[ссылка]

  • Never use END blocks. Use Kernel#at_exit instead.[ссылка]

    ```Ruby # плохо END { puts ‘Goodbye!’ }

    хорошо

    at_exit { puts ‘Goodbye!’ } ```

  • Avoid the use of flip-flops.[ссылка]

  • Avoid use of nested conditionals for flow of control.[ссылка]

    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 # плохо 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 ```

    Prefer next in loops instead of conditional blocks.

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

Наименование

The only real difficulties in programming are cache invalidation and naming things.
– Phil Karlton

  • Name identifiers in English.[ссылка]

    ```Ruby # плохо - identifier using non-ascii characters заплата = 1_000

    плохо - identifier is a Bulgarian word, written with Latin letters (instead of Cyrillic)

    zaplata = 1_000

    хорошо

    salary = 1_000 ```

  • Use snake_case for symbols, methods and variables.[ссылка]

    ```Ruby # плохо :’some symbol’ :SomeSymbol :someSymbol

    someVar = 5

    def someMethod … end

    def SomeMethod … end

    хорошо

    :some_symbol

    def some_method … end ```

  • Use CamelCase for classes and modules. (Keep acronyms like HTTP, RFC, XML uppercase.) [ссылка]

    ```Ruby # плохо class Someclass … end

    class Some_Class … end

    class SomeXml … end

    хорошо

    class SomeClass … end

    class SomeXML … end ```

  • Use snake_case for naming files, e.g. hello_world.rb.[ссылка]

  • Use snake_case for naming directories, e.g. lib/hello_world/hello_world.rb.[ссылка]

  • 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. [ссылка]

  • Используйте SCREAMING_SNAKE_CASE для констант.[ссылка]

    ```Ruby # плохо SomeConst = 5

    хорошо

    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. [ссылка]

  • 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. [ссылка]

    ```Ruby # плохо - there is no matching ‘safe’ method class Person def update! end end

    хорошо

    class Person def update end end

    хорошо

    class Person def update! end

    def update end end ```

  • Define the non-bang (safe) method in terms of the bang (dangerous) one if possible. [ссылка]

    ```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). [ссылка]

  • When defining binary operators, name the argument other(<< and [] are exceptions to the rule, since their semantics are different). [ссылка]

    Ruby def +(other) # body omitted 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. [ссылка]

  • 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. [ссылка]

    ```Ruby # плохо some_hash.count

    хорошо

    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. [ссылка]

    ```Ruby # плохо all_songs = users.map(&:songs).flatten.uniq

    хорошо

    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. [ссылка]

    ```Ruby # плохо array.reverse.each { … }

    хорошо

    array.reverse_each { … } ```

Комментарии

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

  • Пишите говорящий за себя код и смело пропускайте все остальное в этом разделе. Серьезно! [ссылка]

  • Пишите комментарии по-английски. [ссылка]

  • Используйте один пробел между символом # в начале и текстом самого комментария.[ссылка]

  • Комментарии длиной больше одного слова должны оформляться в виде законченных предложений (с большой буквы и со знаками препинания). Разделяйте предложения одним пробелом. [ссылка]

  • Избегайте избыточного комментирования. [ссылка]

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

  • Актуализируйте существующие комментарии. Устаревший комментарий гораздо хуже отсутствующего комментария. [ссылка]

Good code is like a good joke - it needs no explanation.
– Russ Olsen

  • Не пишите комментарии для объяснения плохого кода. Перепишите код, чтобы он говорил сам за себя. [ссылка]

Do or do not - there is no try.
– Yoda

Пометки в комментариях

  • Обычно пометки следует записывать на предшествующей описываемому коду строке.[ссылка]

  • Пометка отделяется двоеточием и пробелом, потом следует примечание, описывающее проблему.[ссылка]

  • Если для описания проблемы потребуются несколько строк, то на каждой последующей строке следует сделать отступ в три пробела после символа #.[ссылка]

    Ruby def bar # FIXME: This has crashed occasionally since v3.2.1. It may # be related to the BarBazUtil upgrade. baz(:quux) end

  • В тех случаях, когда проблема настолько очевидна, что любые описания покажутся избыточными, пометки можно поставить в конце вызывающей проблему строки. Однако такое применение должно быть исключением, а не правилом.[ссылка]

    Ruby def bar sleep 100 # OPTIMIZE end

  • Используйте TODO, чтобы пометить отсутствующие возможности или функционал, которые должны быть добавлены позже.[ссылка]

  • Используйте FIXME, чтобы пометить код с ошибками, который должен быть исправлен.[ссылка]

  • Используйте OPTIMIZE, чтобы пометить медленный или неэффективный код, который может вызвать проблемы с производительностью. [ссылка]

  • Используйте HACK, чтобы пометить кода “с душком”, который должен быть переработан и использует сомнительные практики разработки. [ссылка]

  • Используйте REVIEW, чтобы пометить все, что должно быть проверено на работоспособность. Например, REVIEW: Are we sure this is how the client does X currently?.[ссылка]

  • Используйте персональные пометки, если это подходит по месту, но обязательно опишите их смысл в файле README (или похожем) для вашего проекта.[ссылка]

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

  • Структурируйте ваши классы единообразно.[ссылка]

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

    # вложенные классы CustomErrorKlass = Class.new(StandardError)

    # после этого константы SOME_CONSTANT = 20

    # после этого макросы методов доступа к атрибутам attr_reader :name

    # и все прочие макросы (если имеются) 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. [ссылка]

    ```Ruby # плохо

    foo.rb

    class Foo class Bar # 30 methods inside end

    class Car # 20 methods inside end

    # 30 methods inside end

    хорошо

    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. [ссылка]

    ```Ruby # плохо class SomeClass def self.some_method # body omitted end

    def self.some_other_method end end

    хорошо

    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. [ссылка]

    ```Ruby # плохо module Utilities extend self

    def parse_something(string) # do stuff here end

    def other_utility_method(number, string) # do some more stuff end end

    хорошо

    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. [ссылка]

  • Try to make your classes as SOLID as possible. [ссылка]

  • Always supply a proper to_s method for classes that represent domain objects. [ссылка]

    ```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.[ссылка]

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

  • Avoid the use of attr. Use attr_reader and attr_accessor instead.[ссылка]

    ```Ruby # плохо - creates a single attribute accessor (deprecated in 1.9) attr :something, true attr :one, :two, :three # behaves as attr_reader

    хорошо

    attr_accessor :something attr_reader :one, :two, :three ```

  • Consider using Struct.new, which defines the trivial accessors, constructor and comparison operators for you. [ссылка]

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

    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. [ссылка]

  • Consider adding factory methods to provide additional sensible ways to create instances of a particular class. [ссылка]

    Ruby class Person def self.create(options_hash) # body omitted end end

  • Prefer duck-typing over inheritance.[ссылка]

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

  • Avoid the usage of class (@@) variables due to their “nasty” behavior in inheritance.[ссылка]

    ```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. [ссылка]

  • 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. [ссылка]

    ```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. [ссылка]

    ```Ruby class TestClass # плохо def TestClass.some_method # body omitted end

    # хорошо 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 ```
    

Исключения

  • 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). [ссылка]

    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.[ссылка]

    ```Ruby # плохо fail RuntimeError, ‘message’

    хорошо - 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. [ссылка]

    ```Ruby # плохо fail SomeException.new(‘message’) # Note that there is no way to do fail SomeException.new('message'), backtrace.

    хорошо

    fail SomeException, ‘message’ # Consistent with fail SomeException, 'message', backtrace. ```

  • Never 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. [ссылка]

    Ruby def foo begin fail ensure return 'very bad idea' end end

  • Используйте неявный begin где возможно.[ссылка]

    ```Ruby # плохо def foo begin # Основной функционал rescue # Обработка ошибок end end

    хорошо

    def foo # Основной функционал rescue # Обработка ошибок end ```

  • Mitigate the proliferation of begin blocks by using[ссылка]

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

  • Don’t suppress exceptions.[ссылка]

    ```Ruby # плохо begin # an exception occurs here rescue SomeError # the rescue clause does absolutely nothing end

    плохо

    do_something rescue nil ```

  • Avoid using rescue in its modifier form.[ссылка]

    ```Ruby # плохо - this catches exceptions of StandardError class and its descendant classes read_file rescue handle_error($!)

    хорошо - 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.[ссылка]

    ```Ruby # плохо begin n / d rescue ZeroDivisionError puts ‘Cannot divide by 0!’ end

    хорошо

    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. [ссылка]

    ```Ruby # плохо 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

    хорошо

    begin # a blind rescue rescues from StandardError, not Exception as many # programmers assume. rescue => e # exception handling end

    also хорошо

    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. [ссылка]

    ```Ruby # плохо begin # some code rescue Exception => e # some handling rescue StandardError => e # some handling end

    хорошо

    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.[ссылка]

    Ruby f = File.open('testfile') begin # .. process rescue # .. handle error ensure f.close unless f.nil? end

  • Favor the use of exceptions for the standard library over introducing new exception classes. [ссылка]

Коллекции

  • Prefer literal array and hash creation notation (unless you need to pass parameters to their constructors, that is). [ссылка]

    ```Ruby # плохо arr = Array.new hash = Hash.new

    хорошо

    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. [ссылка]

    ```Ruby # плохо STATES = [‘draft’, ‘open’, ‘closed’]

    хорошо

    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. [ссылка]

    ```Ruby # плохо STATES = [:draft, :open, :closed]

    хорошо

    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. [ссылка]

    ```Ruby # плохо - easier to move/add/remove items, but still not preferred VALUES = [ 1001, 2020, 3333, ]

    плохо

    VALUES = [1001, 2020, 3333, ]

    хорошо

    VALUES = [1001, 2020, 3333] ```

  • Не создавайте массивы с большими пробелами.[ссылка]

    Ruby arr = [] arr[100] = 1 # получаем массив с кучей нулей

  • При доступе к первому или последнему эллементу массива используйте, first или last нежели [0] или [-1].[ссылка]

  • 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. [ссылка]

  • Prefer symbols instead of strings as hash keys.[ссылка]

    ```Ruby # плохо hash = { ‘one’ => 1, ‘two’ => 2, ‘three’ => 3 }

    хорошо

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

  • Avoid the use of mutable objects as hash keys.[ссылка]

  • Use the Ruby 1.9 hash literal syntax when your hash keys are symbols.[ссылка]

    ```Ruby # плохо hash = { :one => 1, :two => 2, :three => 3 }

    хорошо

    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. [ссылка]

    ```Ruby # плохо { a: 1, ‘b’ => 2 }

    хорошо

    { :a => 1, ‘b’ => 2 } ```

  • Use Hash#key? instead of Hash#has_key? and Hash#value? instead of Hash#has_value?. As noted here by Matz, the longer forms are considered deprecated. [ссылка]

    ```Ruby # плохо hash.has_key?(:test) hash.has_value?(value)

    хорошо

    hash.key?(:test) hash.value?(value) ```

  • Use Hash#fetch when dealing with hash keys that should be present.[ссылка]

    ```Ruby heroes = { batman: ‘Bruce Wayne’, superman: ‘Clark Kent’ } # плохо - if we make a mistake we might not spot it right away heroes[:batman] # => “Bruce Wayne” heroes[:supermann] # => nil

    хорошо - 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.[ссылка]

    ```Ruby batman = { name: ‘Bruce Wayne’, is_evil: false }

    плохо - if we just use || operator with falsy value we won’t get the expected result

    batman[:is_evil] || true # => true

    хорошо - 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.[ссылка]

    ```Ruby batman = { name: ‘Bruce Wayne’ }

    плохо - 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

    хорошо - 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.[ссылка]

    ```Ruby # плохо email = data[‘email’] nickname = data[‘nickname’]

    хорошо

    email, username = data.values_at(‘email’, ‘nickname’) ```

  • Rely on the fact that as of Ruby 1.9 hashes are ordered.[ссылка]

  • Never modify a collection while traversing it.[ссылка]

Строки

  • Используйте интерполяцию строк и форматные шаблоны, а не конкатенацию строк:[ссылка]

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

  • Постарайтесь отделять код интерполяции пробелами. Это отделит код интерполяции более четко от окружающего кода. [ссылка]

    Ruby "#{ user.last_name }, #{ user.first_name }"

  • Постарайтесь внедрить единообразных стиль кавычек для строчных литералов. В среде программистов на Руби есть два популярных стиля, оба из них считаются приемлемыми. Стиль А подразумевает одинарные кавычки по умолчанию, а стиль B двойные кавычки. [ссылка]

    • (A) Используйте одинарные кавычки, если вам не нужна интерполяция строк или специальные символы вроде \t, \n, ' и т.д.

      ```Ruby # плохо name = “Bozhidar”

      хорошо

      name = ‘Bozhidar’ ```

    • (B) Используйте двойные кавычки в ваших строчных литералах, если они не содержат " или экранируйте символы, которые не должны интерполироваться.

      ```Ruby # плохо name = ‘Bozhidar’

      хорошо

      name = “Bozhidar” ```

    Второй стиль, по некоторым мнениям, более распространен среди разработчиков на Руби. Однако в этом руководстве оформление строк следует первому правилу.

  • Не используйте запись для литералов алфавитных символов ?x. Начиная с версии Руби 1.9, этот вариант записи избыточен: ?x будет интерпретироваться в виде 'x' (строка с единственным символом в ней).[ссылка]

    ```Ruby # плохо char = ?c

    хорошо

    char = ‘c’ ```

  • Всегда применяйте фигурные скобки {} вокруг глобальных переменных и переменных экземпляров класса при интерполяции строк.[ссылка]

    ```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 для интерполируемых объектов, он вызывается автоматически при интерполяции. [ссылка]

    ```Ruby # плохо message = “This is the #{result.to_s}.”

    хорошо

    message = “This is the #{result}.” ```

  • Не применяйте метод String#+, когда вам нужно собрать вместе большие отрезки строк. Вместо этого используйте String#<<. Конкатенация изменяет экземпляр строки и всегда работает быстрее, чем String#+, который создает целую кучу новых строковых объектов. [ссылка]

    ```Ruby # хорошо и быстро html = ‘’ html « ‘<h1>Page title</h1>’

    paragraphs.each do |paragraph| html « “<p>#{paragraph}</p>” end ```

  • При использовании многострочных HEREDOC не забывайте, что пробелы в начале строк тоже являются частью создаваемой строки. Примером хорошего стиля является применение техник, основывающихся на ограничителях, для удаления ненужных пробелов.[ссылка]

    Ruby code = <<-END.gsub(/^\s+\|/, '') |def test | some_method | other_method |end END #=> "def test\n some_method\n other_method\nend\n"

Регулярные выражения

Some people, when confronted with a problem, think “I know, I’ll use regular expressions.” Now they have two problems.
– Jamie Zawinski

  • Не используйте регулярные выражения, когда вам нужно просто найди в строке подстроку: string['text'] [ссылка]

  • For simple constructions you can use regexp directly through 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.[ссылка]

    Ruby /(first|second)/ # плохо /(?:first|second)/ # хорошо

  • Don’t use the cryptic Perl-legacy variables denoting last regexp group matches ($1, $2, etc). Use Regexp.last_match[n] instead. [ссылка]

    ```Ruby /(regexp)/ =~ string …

    плохо

    process $1

    хорошо

    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. [ссылка]

    ```Ruby # плохо /(regexp)/ =~ string … process Regexp.last_match[1]

    хорошо

    /(?regexp)/ =~ string ... process meaningful_var ```

  • Character classes have only a few special characters you should care about: ^, -, \, ], so don’t escape . or brackets in []. [ссылка]

  • 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/). [ссылка]

    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. [ссылка]

    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.[ссылка]

Процентные литералы

  • Используйте %() (это сокращение от %Q) для строк без переносов, в которых реализуется интерполяция и присутствуют двойные кавычки. Для строк с переносами лучше используйте формат HERE Doc. [ссылка]

    ```Ruby # плохо (интерполяция не нужна) %(<div class="text">Some text</div>) # должно быть ‘<div class="text">Some text</div>’

    плохо (нет двойных кавычек)

    %(This is #{quality} style) # должно быть “This is #{quality} style”

    плохо (строка с переносами)

    %(<div>\n#{exclamation}\n</div>) # лучше применить HERE Doc

    хорошо (необходима интерполяция, присутствуют кавычки, нет переносов)

    %(<tr><td class="name">#{name}</td>) ```

  • Избегайте %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 только для регулярных выражений, которые обрабатывают больше одного знака ‘/’.[ссылка]

    ```Ruby # плохо %r(\s+)

    все еще плохо

    %r(^/(.)$) # должно быть /^\/(.)$/

    хорошо

    %r(^/blog/2011/(.*)$) ```

  • Откажитесь от использования %x, если вы не хотите вызывать внешнюю команду с обратными кавычками в теле (что само по себе маловероятно). [ссылка]

    ```Ruby # плохо date = %x(date)

    хорошо

    date = date echo = %x(echo date) ```

  • Старайтесь избегать %s. По общепринятому мнению, предпочтительным способом определения символа с пробелами в имени является :"some string" [ссылка]

  • Используйте () в качестве ограничителей для всех литералов со знаком % кроме %r. Так как круглые скобки очень часто используются в самих регулярных выражениях, во многих случаях менее частый символ { может быть лучшим выбором для ограничителя (разумеется, с учетом смысла регулярного выражения). [ссылка]

    ```Ruby # плохо %w[one two three] %q{“Test’s king!”, John said.}

    хорошо

    %w(one two three) %q(“Test’s king!”, John said.) ```

Метапрограммирование

  • Откажитесь от метапрограммирования ради метапрограммирования как такового. [ссылка]

  • Не разводите беспорядок в базовых классах при написании библиотек (не используйте “monkey patching”). [ссылка]

  • Используйте #class_eval с блоком вместно интерполяции значений в строке. Если вы используете интерполяцию, то всегда указывайте дополнительно __FILE__ и __LINE__, чтобы информация о стеке вызова была осмысленной:[ссылка]

    Ruby class_eval 'def use_relative_model_naming?; true; end', __FILE__, __LINE__

    • #define_method предпочтительнее, чем #class_eval { def ... }
  • При использовании #class_eval (или других #eval) с интерполяцией строк обязательно добавляйте комментарий, который будет наглядно показывать, как интерполированные значения будут выглядеть (примеры, используемые в исходном коде Rails):[ссылка]

    ```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}(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 ```
    
  • Избегайте #method_missing для целей метапрограммирования, так как стек вызова становится нечитаемым, метод не виден в #methods, опечатки в вызовах методов пройдут незамеченными, например, nukes.launch_state = false. Используйте делегирование, проксирование или же #define_method. Если вы используете #method_missing: [ссылка]

    • обязательно задайте #respond_to_missing?;
    • перехватывайте вызовы только с четко определенными префиксами, например, #find_by_* – задайте в своем коде наиболее узкие рамки для неопределенностей;
    • вызывайте #super в конце ваших выражений;
    • делегируйте вызовы понятным, “немагическим” методам:

      ```Ruby # плохо def method_missing?(meth, *args, &block) if /^find_by_(?.*)/ =~ meth # ... lots of code to do a find_by else super end end

      хорошо

      def method_missing?(meth, *args, &block) if /^find_by_(?.*)/ =~ meth find_by(prop, *args, &block) else super end end

      Самым лучшим будет все же использование #define_method,

      # так как каждый видимый аргумент будет определен. ```

Разное

  • Пишите код, не дающий предупреждений при вызове ruby -w.[ссылка]

  • Не используйте хеши в качестве необязательных параметров. Возможно, ваш метод просто делает слишком много. Это не касается, однако, методов инициализации объектов. [ссылка]

  • Старайтесь не писать методы длиннее 10 строк. В идеальном случае большинство методов должны быть короче 5 строк. Пустные строки не подсчитываются.[ссылка]

  • Не создаваете методы с более чем тремя-четырьмя параметрами.[ссылка]

  • Если вам действительно нужны глобальные функции, включайте их в модуль Kernel и сделайте их приватными. [ссылка]

  • Используйте переменные модулей вместо глобальных переменных.[ссылка]

    ```Ruby # плохо $foo_bar = 1

    хорошо

    module Foo class « self attr_accessor :bar end end

    Foo.bar = 1 ```

  • Избегайте использования alias, если достаточно использовать alias_method.[ссылка]

  • Используйте OptionParser для анализа сложных аргуметов командрой строки и ruby -s для элеметарных случаев. [ссылка]

  • Используйте вариант Time.now, а не Time.new, когда хотите получить текущее значение системного времени. [ссылка]

  • Пишите код в функциональном стиле без изменения значений, когда это подходит по смыслу.[ссылка]

  • Не изменяйте значения аргументов, если только это не есть цель метода.[ссылка]

  • Старайтесь не создавать вложенные структуры с уровнем вложения больше третьего. [ссылка]

  • Будьте последовательны. В идеальном мире последовательно придерживайтесь данного руководства. [ссылка]

  • Руководствуйтесь здравым смыслом. [ссылка]

Инструментарий

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

РубоКоп

RuboCop — это утилита проверки стиля программного кода на Руби, который основывается на этом руководстве. РубоКоп уже реализует большую часть этого руководства, поддерживает MRI 1.9 и MRI 2.0 и хорошо интегрируется с редактором Емакс.

RubyMine

Модуль проверки кода RubyMine частично основывается на этом руководстве.

Сотрудничество

Ничто, описанное в этом руководстве, не высечено в камне. И я очень хотел бы сотрудничать со всеми, кто интересуется стилистикой оформления кода на Руби, чтобы мы смогли вместе создать ресурс, который был бы полезен для всего сообщества программистов на Руби.

Не стесняйтесь создавать отчеты об ошибках и присылать мне запросы на интеграцию вашего кода. И заранее большое спасибо за вашу помощь!

Вы можете поддержать проект (и РубоКоп) денежным взносом при помощи gittip.

Дай Gittip

Как сотрудничать в проекте?

Это просто! Следуйте руководству по сотрудничеству.

Лицензирование

Creative Commons License Данная работа опубликована на условиях лицензии Creative Commons Attribution 3.0 Unported License

Расскажи другому

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

Всего,
Божидар