Permalink
Switch branches/tags
Nothing to show
Find file Copy path
636 lines (498 sloc) 23.7 KB

目次

Ruby コーディング規準

はじめに

本文書は、クックパッド株式会社での Ruby コードのスタイル規準を定めるものである。 Ruby プログラマとしての素養をある程度備えている者なら誰もが読みやすいコードになるよう、可読性と一貫性を重視して規準を定めている。 「bad」として例示されている記法の中には、特に悪いと思えないようなものも存在しているが、その理由はコードの一貫性を保つためである。

Ruby バージョン

  • [SHOULD] Ruby 2.0 のプロジェクトでは、キーワード引数、シンボルリテラル(%i)など、Ruby 2.0 で新しく加わった Syntax を推奨する。

インデント

  • [MUST] 1レベルのインデントに2つの空白を使用する。水平タブを使用してはならない。

  • [MUST] 長いメソッドチェインの最後のメソッド呼び出しでブロックを渡す場合、最後のメソッド呼び出しのレシーバをローカル変数として抽出し、ブロック付きメソッド呼び出しを独立した式として書くこと。

    # good
    posts = Post.joins(:user).
      merge(User.paid).
      where(created_at: target_date)
    posts.each do |post|
      next if stuff_ids.include?(post.user_id)
      comment_count += post.comments.size
    end
    
    # bad
    posts = Post.joins(:user).
      merge(User.paid).
      where(created_at: target_date).each do |post|
        next if stuff_ids.include?(post.user_id)
        comment_count += post.comments.size
      end
  • [SHOULD] 式の途中で改行する場合は、2行目以降を1行目より1段深くインデントすること。

    # good
    User.active.
      some_scope(foo).
      other_scope(bar)
    
    # bad
    User.active.
    some_scope(foo).
    other_scope(bar)

空白

  • [MUST] 行末に空白を置いてはならない。

空行

  • [MUST] ファイルの末尾に空行を置いてはならない。

文字エンコーディングとマジックコメント

  • [MUST] 特別な理由がない場合はスクリプトのエンコーディングは UTF-8 にすること。

  • [SHOULD] Ruby 2.0 以降は UTF-8 がデフォルトになったので、スクリプトのエンコーディングが UTF-8 の場合はマジックコメントを書かない。

  • [MUST] マジックコメントが必要な場合は以下の形式で書くこと。

    # coding: utf-8

1行の文字数

  • [SHOULD] 特殊な事情が無い限りは1行の文字数を128文字以内に納めること (全角文字は2文字と数えること)。

数値

  • [SHOULD] 桁数が大きな10進法の数値リテラルは3桁毎に下線を入れて読みやすくする。
    • 例: 1_000_000.001_023
  • [SHOULD] 桁数が大きな2進法および16進法の数値リテラルは4桁毎に下線を入れて読みやすくする。
    • 例: 0xABCD_1234
  • [SHOULD] 16進法はアルファベットの大文字で書いても小文字で書いても良いが、1つのファイルの中では統一すること。
  • [SHOULD] Ruby 2.1 以上を使用している場合、分数は r サフィックスを用いて書くこと。
    • 例: 1/2r #=> (1/2)
  • [SHOULD] Ruby 2.0.0 以前を使用している場合、分数は Integer#quo メソッドを使って書くこと。
    • 例: 1.quo(2) #=> (1/2)
  • [SHOULD] Ruby 2.1 以上を使用している場合、複素数は i または ri サフィックスを用いて書くこと。
    • 例: 1 + 2i #=> (1+2i)

文字列

  • [SHOULD] 空文字列は '' と書くこと。

  • [SHOULD] 理由が無い限り引数なしの String.new を使ってはならない。

  • [MUST] 文字列内部のエスケープシーケンスが最も少なくなるよう、適切な区切り記号を選択すること。

  • [SHOULD] % 記法で文字列リテラルを書く場合は、括弧を区切り記号に使用すること。括弧の種類はどれでも良い。ただし、次のような特殊な場合は括弧以外を区切り記号として良い:

    OPEN_PARENTHESES = %!({[!
  • [MUST] 式展開の中で Object#to_s だけを呼び出してはならない。たとえばこういうの→"#{obj.to_s}"

  • [MUST] 式展開の中括弧を省略してはならない。

  • [SHOULD] (Ruby 1.9+) Unicode 文字をエスケープシーケンスで書く場合は、"\xE3\x8C\xB3" ではなく "\u{3333}" のように、\u を使うこと。ただし、Ruby 1.8 系でも動作する必要があるスクリプトではこの限りではない。

  • [SHOULD] ループ内で文字列リテラルを書いてはならない。ここでループとは whileuntilfor、およびイテレータ (each などのブロックを何度も呼び出すブロック付きメソッド呼び出しのブロック内) である。

  • [SHOULD] 文字列リテラルに対して String#+ を用いて他の文字列を連結してはならない。式展開を使用すること。

  • [MUST] 文字列の破壊的追記を += で行ってはならない。String#<< または String#concat を使用すること。

正規表現

  • [SHOULD] 必要のない後方参照グループを作成してはならない。(?: ... ) を使うこと。
  • [SHOULD] 複雑な正規表現を記述する場合は、x オプションを利用し、改行、空白、コメント ((?# ... )) を使用して可読性を向上させること。

配列

  • [MUST] 代入の後ろに配列リテラルを複数行で書く場合は、[ の後ろで改行し、要素行のインデントを1レベル下げ、] は独立した行に置いて [ を書いた行の行頭にインデントを揃える。

    # good
    array = [
      :foo,
      :bar,
      :baz,
    ]
    
    # bad
    array = [ :foo,
              :bar,
              :baz, ]
    
    # bad
    array = [ :foo,
              :bar,
              :baz,
            ]
    
    # bad
    array = [
      :foo,
      :bar,
      :baz, ]
  • [SHOULD] 複数行の配列リテラルでは、最後の要素の後ろにも , を書くこと。

  • [SHOULD] 文字列配列は%記法 %w(...) or %W(...) を用いて書くこと。

    # good
    words = %w(foo bar baz)
    
    # bad
    words = ['foo', 'bar', 'baz']
  • [MUST] 空配列は [] と書くこと。

  • [MUST] 引数なしの Array.new を使ってはならない。

  • [SHOULD] 同じ要素を n 個持つ配列を初期化するときは Array.new(n, obj) を使用すること。[obj] * n と書いてはならない。

  • [SHOULD] 範囲リテラルを配列に変換するときは、Range#to_a ではなく [*range] を使うこと。

    # good
    [*1..10]  #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    
    # bad
    (1..10).to_a  #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

ハッシュ

  • [MUST] ハッシュリテラルを一行に書くときは、{ と第一要素のキーとの間、および最終要素の値と } との間にそれぞれ空白を一つずつ入れること。

    # good
    { hoge: 1, fuga: 2 }
    # bad
    {hoge: 1, fuga: 2}
  • [MUST] ハッシュリテラルは、Symbol を key にするときは HashRocket (Ruby 1.8 までで使われていた => の記法{ :foo => 42 })を使わない。

    # good
    { first: 42,
      second: 'foo',
    }
    # bad
    { :first => 42,
      :second => 'foo',
    }
  • [SHOULD] ハッシュリテラルの key が symbol だが、., -等を含む場合、HashRocket に統一するのが望ましい。

    # good
    { :cookpad => 42,
      :'cookpad.com' => 'foo',
    }
    # bad
    { cookpad: 42,
      :'cookpad.com' => 'foo',
    }
  • [MUST] 空ハッシュは {} と書くこと。

  • [MUST] 代入の後ろにハッシュリテラルを複数行で書く場合は、{ の後で改行し、要素行のインデントを1レベル下げ、} は独立した行に置いて { を書いた行の行頭にインデントを揃える。

    # good
    hash = {
      first: 42,
      second: 'foo',
    }
    
    # bad
    hash = { first: 42,
             second: 'foo',
           }
    
    # bad
    hash = { first: 42,
             second: 'foo',
    }
    
    # bad
    hash = { first: 42,
      second: 'foo',
    }
    
    # bad
    hash = {
    first: 42,
    second: 'foo',
    }
  • [SHOULD] 複数行のハッシュリテラルでは、最後の要素の後ろにも , を書くこと。

  • [SHOULD] ハッシュのキーを Symbol にして良い場合は、文字列よりも高速に lookup できるので積極的に Symbol を使うこと。

    # good
    { foo: 1, bar: 2 }
    
    # bad
    { 'foo' => 1, 'bar' => 2 }
  • [SHOULD] (Ruby 1.9+) ハッシュリテラルのキーがすべて Symbol リテラルのときは { key: value } の記法を使うこと。このとき : の後ろに空白を入れること。

演算式

  • [SHOULD] 演算子の両側に空白を入れること。ただし ** の両側には空白は入れないこと。

  • [MUST] andor、および not は使用しないこと。

    • ただし、式 or raise 'message'の時のみorを使っても良い。
  • [MUST] 条件演算子を入れ子にしてはならない。

  • [MUST] 条件演算子を複数行で書いてはならない。

    # good
    fizzbuzz = if n % 3 == 0
        n % 5 == 0 ? 'fizzbuzz' : 'fizz'
      else
        n % 5 == 0 ? 'buzz' : "#{n}"
      end
    
    # bad
    fizzbuzz = n % 3 == 0 ? (n % 5 == 0 ? 'fizzbuzz' : 'fizz') : (n % 5 == 0 ? 'buzz' : "#{n}")
    
    # bad
    fizzbuzz = n % 3 == 0 ?
      (n % 5 == 0 ? 'fizzbuzz' : 'fizz') :
      (n % 5 == 0 ? 'buzz' : "#{n}")

代入式

  • [MUST] 複合代入はリテラルまたは引数なしのメソッド呼び出しの結果を代入する場合、および2つの変数または属性の値を交換する場合のみ使ってよい。
  • [MUST] 代入記号の両側に空白を入れること。
  • [MUST] 条件式で代入式を書かないこと。

制御構造

  • [SHOULD] if !condition の代わりに unless condition と書くこと。

  • [SHOULD] while !condition の代わりに until condition と書くこと。

  • [SHOULD] unless に対して else を使ってはならない。

  • [MUST] ifunless、および case の条件式の後ろは改行すること。同じ行に本体コードを続けて書いてはならない。

  • [MUST] ifunless、および casethen: を使用してはならない。

  • [MUST] while および until の条件式の後ろは改行すること。同じ行に本体コードを続けて書いてはならない。

  • [MUST] while および untildo: を使用してはならない。

  • [SHOULD] unless および until の条件式に複数の項を || で結合した論理式 (加法標準形) を書いてはならない。

  • [SHOULD] 条件式と本体コードが短い場合は修飾子記法を使用すること。

  • [SHOULD] 条件式が長く複数行になってしまう場合は、その条件式を述語メソッドとして抽出し、適切な名前を付けて使用すること。

  • [MUST] 制御構造が代入の右辺に来る場合は、本体コードのインデントを2レベル下げ、制御構造の終わりの end 行のインデントを本体コードより1レベル戻すこと。

    # good
    result = if condition
        body_code
      end
    
    # good
    result =
      if condition
        body_code
      end
    
    # bad
    result = if condition
      body_code
    end
    
    # bad
    result = if condition
               body_code
             end
  • [MUST] return, next, break が意味のある値を返さない場合は、後ろに式を書いてはならない。

    • コントローラのアクションの内部で redirect_to や render などの継続処理を宣言するメソッドを呼び出す場合は、return や next の後ろにそれらのメソッド呼び出しを記述してよい。

メソッド呼び出し

  • [MUST] メソッド呼び出しの括弧は、省略を許可された場合を除いて、省略してはならない。

  • [MUST] 引数なしのメソッド呼び出しは括弧を省略すること。

  • [MUST] DSL-like メソッドの呼び出しでは、メソッド呼び出しの括弧を省略して良い。ただし、第一引数全体が括弧で囲まれる場合は警告が出てしまうため省略してはならない。DSL-like メソッドとは以下のメソッドである。

    • p, print, puts, require などのグローバル関数。
    • attr_reader や private など、クラスやモジュールの定義で用いる宣言的メソッド (Rails のようなフレームワークが提供しているものも含む)。
    • redirect_to や render などの ActionController が提供する、アクションに対する継続処理を宣言するためのメソッド。
    • その他、DSL のために用意されていると考えられるメソッド。
  • メソッド呼び出しを入れ子にする場合、他の規則で禁止されている場合を除いて、最も外側のメソッド呼び出しの括弧を省略してもよい。

  • [MUST] メソッド名とメソッド呼び出しの括弧の間に空白を入れてはならない。

  • [SHOULD] パラメータリストの末尾にハッシュリテラルを書く場合は、ハッシュリテラルの括弧を省略すること。

    # good
    foo(1, 2, foo: :bar, baz: 42)
    
    # bad
    foo(1, 2, { foo: :bar, baz: 42 })
  • [MUST] ブロック付きメソッド呼び出しでは、do/end 記法でブロックを書くこと。i.e. blockの副作用が目的

  • [MUST] ブロック付きメソッド呼び出しの戻り値に対して処理を行う場合は、ブロックを中括弧で書くこと。

    # good
    puts [1, 2, 3].map {|i|
      i * i
    }
    
    # bad
    puts [1, 2, 3].map do |i|
      i * i
    end
    
    # good
    [1, 2, 3].map {|n|
      n * n
    }.each {|n|
      puts Math.sqrt(n)
    }
    
    # bad
    [1, 2, 3].map do |n|
      n * n
    end.each do |n|
      puts Math.sqrt(n)
    end
  • [MUST] ブロック付きメソッド呼び出しを1行で書く場合は、ブロックを中括弧で書くこと。

  • [MUST] do/end によるブロックでは、doの前後に空白を1つ入れ、ブロックパラメータの後で改行し、end は独立した行に書くこと。ブロック本体のインデントは1レベル下げ、end のインデントはメソッド呼び出しの1行目にあわせること。

    # good
    [1, 2, 3].each do |num|
      puts num
    end
    
    # bad
    [1, 2, 3].each do |num|
        puts num
      end
    
    # bad
    [1, 2, 3].each do |num|
                     puts num
                   end
    
    # bad
    [1, 2, 3].each do |num| puts num end
  • [MUST] 中括弧によるブロックでは、{ の前に空白を1つ入れること。

  • [MUST] 中括弧によるブロックを1行で書く場合は、{ またはブロックパラメータと本体コードの間、および本体コードと } の間に空白を1つずつ入れること。

    # good
    [1, 2, 3].each {|num| puts num }
    [1, 2, 3].each { |num| puts num }
    
    # bad
    [1, 2, 3].each {|num| puts num}
    
    # bad
    [1, 2, 3].each { |num| puts num}
    
    # good
    10.times { puts 'Hello world' }
    
    # bad
    10.times {puts 'Hello world' }
    
    # bad
    10.times {puts 'Hello world'}
    
    # bad
    10.times { puts 'Hello world'}
  • [SHOULD] メソッド呼び出しにおいて、実引数の前の式が長い場合は以下の規則に基いて複数行に分けて書くこと。

    • 実引数が長い場合はメソッド呼び出しの開き括弧 ( の直後で改行し、次の行からインデントレベルを下げて実引数を1行に1つずつ書き、メソッド呼び出しの閉じ括弧 ) を独立した行にインデントレベルを戻して書くこと。

      Foo.new(
        arg,
        long_argument,
        key: value,
        long_key: long_value,
        pretty_so_much_very_long_key:
          pretty_so_much_very_tooooooooooooooooooooo_long_value,
      )
    • 実引数が長くない場合はメソッド呼び出しの開き括弧 ( に続けて第1引数を書き、次の行からインデントレベルを第1引数に合わせて第2引数以降を1行に1つずつ書き、メソッド呼び出しの閉じ括弧 ) を最後の引数の直後に書くこと。

      Foo.new(arg,
              long_argument,
              key: value,
              long_key: long_value)
    • DSL 的なメソッドを複数行で書く場合は、第1引数をメソッド名の直後に書き、次の行から第2引数以降を1行に1つずつインデントレベルを1つ下げて書くこと。

      ActionMailer::Base.delivery_method :smtp,
        host: 'localhost',
        port: 25

BEGIN と END

  • [MUST] BEGIN ブロックと END ブロックは使ってはならない。

モジュールとクラスの定義

  • [MUST] メソッドのエイリアスを定義する場合は表記揺れを防止するため alias ではなく alias_method を使用する。

  • [MUST] アクセサを定義する場合は attr_accessorattr_readerattr_writer を使用し、attr は使用しない。

  • [MUST] クラスメソッドの定義では、無闇にインデントレベルを深くしないために self. を使用すること。但し、privateなものとpublicなものを同時に定義したい場合等は、class << selfを利用しても構わない。

    class Foo
      # good
      def self.foo
      end
    
      # bad
      def Foo.foo
      end
    end
  • [MUST] プライベートまたはプロテクテッドなクラスメソッドを定義する場合は class << self/end の中でメソッドを定義し、可視性を変更すること。

     class Foo
       # good
       class << self
         def foo
         end
         private :foo
       end
    
       # bad
       def self.foo
       end
       class <<self
         private :foo
       end
     end
  • [MUST] メソッド定義の後で、そのメソッドの可視性を変更するために privateprotectedpublic を引数付きで呼び出す場合は、メソッド定義とこれらのメソッド呼び出しの間に空行を入れてはならない。

    class Foo
      # good
      def foo
      end
      private :foo
    
      # bad
      def foo
      end
    
      private :foo
    end
  • [MUST] privateprotectedpublic を引数なしで使用する場合、インデントレベルはメソッド定義と同じレベルとし、前後に1行ずつ空白を入れること。

    # good
    class Foo
      def foo
      end
    
      private
    
      def bar
      end
    end
    
    # bad
    class Foo
      def foo
      end
    
    private
    
      def bar
      end
    end
    
    # bad
    class Foo
      def foo
      end
    
      private
    
        def bar
        end
    end
    
    # bad
    class Foo
      def foo
      end
    
      private
      def bar
      end
    end
  • [SHOULD] モジュールやクラスが外部に公開するメソッドとアクセサに対してドキュメンテーションコメントを Markdown 形式で書くこと。

  • [SHOULD] ドキュメンテーションコメントはYARDのフォーマットを推奨。

  • [MUST] ドキュメンテーションコメントとメソッド定義の間に空行を書いてはならない。

  • [MUST] 一つのクラスに複数の異なる責務を持たせないこと。

メソッドの定義

  • [MUST] メソッド定義において、引数リストの括弧は省略してはならない。ただし、引数なしメソッドを定義する場合は括弧を省略すること。

  • [MUST] メソッドと引数リストの括弧の間に空白を置いてはならない。

  • [SHOULD] メソッドの本体コードにコメントを書かなければ理解できないようなコードを書いてはならない。

    • メソッド本体内にコメントを書くよりも、別のメソッドに分けて適切な名前を付ける方が可読性が向上する。
    • ただし、数式に対する補足や出展などはコード本体中にコメントとして書いても良い。
  • [MUST] 一つのメソッドに複数の異なる責務を持たせないこと。

  • [MUST] 引数を破壊してはいけない

    # good
    def your_method(str)
      new_str = str.sub('xxx', 'yyy')
    end
    
    # bad
    def your_method(str)
      str.sub!('xxx', 'yyy')
    end

変数

  • [MUST] グローバル変数 ($foo) を新たに導入してはならない。
  • [MUST] クラス変数 (@@foo) を使用してはならない。代わりに class_attribute を使用すること。
  • [MUST] 変数名は英単語を省略せずに使って名付けること。ただし、変数名が長くなってしまう場合は、単語の頭文字を除く母音を削除したり、一般的な略語を選択するなどの方法で短縮化してよい。また、慣習的な名前 (ループカウンタとしての ij など) は許可する。
  • [SHOULD] ひとつの変数を異なる役割のために使い回してはならない。そうしたくなる場合は、メソッドを複数のメソッドに分割できる。
  • [SHOULD] ローカル変数のスコープ (有効範囲) を可能な限り小さくすること。ローカル変数が存在しないメソッドは良いメソッドである。

その他 (分類の仕方が難しいやつ)

  • [MUST] 破壊的メソッドを使用する際は、その副作用を最小限の範囲に留めること。