rubocopを使うに当たって把握しておく
ソース
レイアウト
{の前後、}の前にはスペース
[1, 2, 3].each { |e| puts e }
メソッドの引数に初期値を割り当てるとき、 =演算子の周りにはスペース
def some_method(arg1 = :default, arg2 = nil, arg3 = [])
メソッド呼び出しが複数行に及ぶときは、引数は揃える
- ")"の置き場に悩むことがあったので、このようにする
def send_mail(source)
Mailer.deliver(
to: 'bob@example.com',
from: 'us@example.com',
subject: 'Important message',
body: source.text
)
end
可読性のため、大きな数値にはアンダースコア
num = 1_000_000
構文
複数行にまたがるif/unlessでは、条件式は常にif/unlessと同じ行に
if
some_condition
do_something
do_something_else
end
if some_condition
do_something
do_something_else
end
1行のcase文ではwhen x then ...を使う
- ";" でやりがちだけど、英文的にはthenの方がしっくりくるのかしら
case x
when 100; p "100"
when 200 then p "200"
end
andとorの使用は禁止
代入部分を()で囲まずに、=の返り値を条件式に用いてはいけません
if (v = array.grep(/foo/))
do_something(v)
...
end
値が入っているかわからない変数の前処理のは&&=。 &&=を使えば変数が存在するときのみ値を変更するので、 存在確認に用いている不要なifを除去できる
if something
something = something.downcase
end
something = something ? something.downcase : nil
something = something.downcase if something
something = something && something.downcase
something &&= something.downcase
Perlスタイルの($:や$;などのような)特別な変数の使用は避ける。 Englishライブラリを使う
1行の本文を持つラムダには新しいリテラルを使う。 lambdaは複数行にまたがるときのみ
l = ->(a, b) { a + b }
Proc.newよりproc
p = proc { |n| puts n }
使わないブロック引数やローカル変数の先頭にはを付ける。 単にを用いてもOK
- rubocopなどで使用されていない変数エラーを回避できる
result = hash.map { |_, v| v + 1 }
def something(x)
_, used_var = something_else(x)
end
複雑な比較ロジックの代わりに、 可能な限りRangeやComparable#between?を使う
do_something if x >= 1000 && x <= 2000
do_something if (1000..2000).include?(x)
do_something if x.between?(1000, 2000)
ループ内では条件判定ブロックよりもnext
# 悪い例
[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
mapとflattenの組み合わせの代わりに、flat_map
- 深さが2以上の配列には適用できないので注意
- flat_map知らなかった...
reverse.eachの代わりにreverse_each
注釈
- TODO : あとで追加されるべき、今はない機能や関数
- FIXME : 直す必要がある
- OPTIMIZE* : パフォーマンスに問題を及ぼすような、遅い、または非効率な
- HACK : 疑わしくリファクタリングで除去すべきコード
- REVIEW : 意図したとおりに動くか確認する必要がある箇所
- 例: REVIEW: Are we sure this is how the client does X currently?
クラス
- クラスメソッドしかないクラスはモジュールであることが好まれます
- モジュールのインスタンスメソッドをクラスメソッドにしたいときは、 extend selfより
もmodule_functionが好まれます。
Struct.newの使用を考えましょう、 それは、単純なアクセサ、コンストラクタや比較演算子を定義してくれます
# 良い例
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
- truct.newで初期化されたインスタンスを拡張してはいけません。 それは余分なクラスレベルをもたらし、 複数回requireされた時に、奇妙なエラーの原因にもなります
- 継承での振る舞いが"扱いづらい"ので、クラス変数(@@)の使用は避けましょう
例外
例外発生にはfail。 raiseは例外をキャッチして、再度発生させるときにのみ
不確実性のあるメソッド(Avdi Grimmによって作られた言葉です) を用いてbeginの蔓延を和らげる
# 悪い例
begin
something_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 }
Collections
- シンボルの配列が必要なときは%iが好まれます
- 要素が一意のものを扱うときは、Arrayの代わりにSet
- Hash#has_key?よりHash#key?を、 Hash#has_value?よりHash#value?。Matzが述べているように、長い記法は廃止が検討されています
コレクションに対するアクセサを提供するとき、 コレクション内の要素にアクセスする前に、 nilでアクセスするのを防ぐための代替のアクセス方法を提供しましょう
def awesome_things
@awesome_things
end
def awesome_things(index = nil)
if index && @awesome_things
@awesome_things[index]
else
@awesome_things
end
end
速い代替手段がある場合、String#gsubは使わない
捕捉した結果を使う必要のないとき、捕捉しないグループを用いる
/(first|second)/
/(?:first|second)/
Perlレガシーの暗号的な変数は使わない
Regexp.last_match(1)
/(?<meaningful_var>regexp)/ =~ string
puts meaningful_var
複雑な正規表現にはx識別子を用いる
regexp = /
start # some text
\s # white space char
(group) # first group
(?:alt1|alt2) # some alternation
end
/x