kasei_sanのブログ

かせいさんのIT系のおぼえがきです。胡乱の方はnoteとtwitterへ

rubyスタイルガイドななめ読み

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"     # NG
when 200 then p "200" # OK
end

andとorの使用は禁止

  • andを使いがち

代入部分を()で囲まずに、=の返り値を条件式に用いてはいけません

if (v = array.grep(/foo/))
  do_something(v)
  ...
end
  • そもそも条件式内で代入するなよ的な気持ちが...

値が入っているかわからない変数の前処理のは&&=。 &&=を使えば変数が存在するときのみ値を変更するので、 存在確認に用いている不要なifを除去できる

  • 知らなかった...
if something # NG
  something = something.downcase
end

something = something ? something.downcase : nil # NG
something = something.downcase if something      # OK
something = something && something.downcase      # OK
something &&= something.downcase                 # More better

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

  • 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

  • reverse_each知らなかった...

注釈

  • TODO : あとで追加されるべき、今はない機能や関数
  • FIXME : 直す必要がある
  • OPTIMIZE* : パフォーマンスに問題を及ぼすような、遅い、または非効率な
  • HACK : 疑わしくリファクタリングで除去すべきコード
  • REVIEW : 意図したとおりに動くか確認する必要がある箇所
    • 例: REVIEW: Are we sure this is how the client does X currently?

クラス

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は使わない

  • subやtrで代替する

捕捉した結果を使う必要のないとき、捕捉しないグループを用いる

/(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