Tech Racho エンジニアの「?」を「!」に。
  • 開発

Rubyスタイルガイドを読む: ソースコードレイアウト(2)インデント、記号

こんにちは、hachi8833です。

Rubyスタイルガイドを読む、第2回目です。今さらですが、スタイルガイドをただ読むよりも頭に入りますね。

1. ソースコードレイアウト(2)インデント、記号など

1-09 【統一】感嘆符!の後ろにはスペースを置かない

No space after !.

原文では他に記述がありませんが、この感嘆符はRubyの単独の否定演算子!を指しています。

# 不可
 ! something

# 良好
 !something

以下は否定演算子ではないので、この項には該当しません。

  • !=などの記号の組み合わせによる演算子
  • 破壊的メソッド名の末尾に付ける習慣になっている!(例: String#gsub!

1-10 【統一】範囲演算子.....の前後にはスペースを置かない

No space inside range literals.

範囲演算子の前後にスペースがあると、範囲演算子であることがわかりにくくなるためです。

# 不可
1 .. 3
'a' ... 'z'

# 良好
1..3
'a'...'z'

1-11 【統一】case文内部のwhenのインデントの深さはcaseと同じにする

Indent when as deep as case. This is the style established in both "The Ruby Programming Language" and "Programming Ruby".

これは意見が割れそうなスタイルですが、『The Ruby Programming Language』と『Programming Ruby』の2冊の書籍を論拠にしています。繰り返しになりますが、重要なのは「どちらかに決める」ことです。

# 不可
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

なお、このスタイルはEiffelの影響です(kazz)。

1-12【統一】変数に代入する条件式の後続文は、条件式と同じ位置にインデントを揃える

When assigning the result of a conditional expression to a variable, preserve the usual alignment of its branches.


1-12a【選択】この場合のインデントスタイルは、以下の「良好1」「良好2」から選べます。

# 不可 - 文がわかりにくい
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

# 良好1 - 処理内容が明確
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

# 良好2 - コードの表示幅を節約できる
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

「良好」が2つあるのは、議論で決着がつかなかったようです。

  • 「良好」例の1番目ではcasewhenifelseendの縦が揃っていて、見ていてキモチイイですね。このスタイルは多くの言語で採用されています。

case indentation

  • 「良好」の2番目では、さらに等号=で改行することで、条件式の結果の代入で幅を取りすぎないようにできます。

case indentation 2

面白いのは、どちらの場合もcaseifに対応するendまで縦が揃っていることです。深入りはしませんが、Rubyのcaseifは例に示されているとおり結果の値を返すので、caseif全体で値を返すことを強調するという意図なのだろうと理解しました。

morimorihogeコメント:
Ruby styleなcase-whenインデントは、ソフトウェア解析分野での条件分岐命令のネスト深度測定とも一貫する(インデントの深さが条件分岐の深さとそろう)ので、そこがよいですね。

補足

コメント中の「不可」で使われていたconvolutedは、ここでは「わかりにくい」「込み入っている」程度の意味です。最近だと機械学習方面のConvolutional neural networkなどでよく見かけますね。

1-13 【統一】メソッド定義とメソッド定義の間には空行を置き、論理上のパラグラフ区切りにも空行を置く

Use empty lines between method definitions and also to break up methods into logical paragraphs internally.

「lines」と複数形になっているので、1行より多く空行を置いてもよいということになります。

def some_method
  data = initialize(options)

  data.manipulate!

  data.result
end

def some_method
  result
end

1-14 【統一】メソッド呼び出しの末尾パラメーターの後ろにはカンマを置かない(特にメソッド呼び出しが1行の場合)

Avoid comma after the last parameter in a method call, especially when the parameters are not on separate lines.

「不可」のコメントについて詳しくは「ケツカンマ」でググるのがよいと思います。

# 不可
#(末尾にもカンマがあればパラメーターの追加や削除が楽になるのはわかるが、それでも推奨しない)
some_method(
  size,
  count,
  color,
)

# 不可
some_method(size, count, color, )

# 良好
some_method(size, count, color)

1-15 【統一】メソッドのパラメータでデフォルト値を与える場合、=の前後にはスペースを置く

Use spaces around the = operator when assigning default values to method parameters:

# 不可
def some_method(arg1=:default, arg2=nil, arg3=[])
  # do something...
end

# 良好
def some_method(arg1 = :default, arg2 = nil, arg3 = [])
  # do something...
end

While several Ruby books suggest the first style, the second is much more prominent in practice (and arguably a bit more readable).

多くのRuby書籍では前者のスタイルが採用されていますが、後者の方がやや読みやすく明快であるという判断がなされています。

なお、前者のスタイルが多くの書籍で使われているということは、他の言語のスタイルからの影響など何らかの理由があると考えられます。

1-16 【統一】\で行を継続するのは避ける(文字列の結合での利用のみ認める)

Avoid line continuation \ where not required. In practice, avoid using line continuations for anything but string concatenation.

特に英語圏にはエディタの行折り返しを好まない開発者が意外にいるので、そうした設定での行分割に\を使うと考えられます。

# 不可
result = 1 - \
         2

# 少しマシ (でもまだまだひどい)
result = 1 \
         - 2

long_string = 'First part of the long string' \
              ' and second part of the long string'

長い文字列ならヒアドキュメントにする方がよいでしょう。

1-17 【選択】ドット.によるメソッドチェーンの行分割はOption AとOption Bのいずれかに揃える

Adopt a consistent multi-line method chaining style. There are two popular styles in the Ruby community, both of which are considered good—leading . (Option A) and trailing . (Option B).
A discussion on the merits of both alternative styles can be found here.

Option AとOption Bのどちらがよりよいというものではありません。重要なのはどちらかに揃えることです。

以下のコメントで両陣営の言い分がよくわかります。

1-17a Option A

When continuing a chained method invocation on another line keep the . on the second line.

ドット.を次の行の最初に置くスタイルです。

# 不可
one.two.three.   # 1行目を読まないと2行目がメソッドチェーンであることがわからない
  four

# 良好
one.two.three    # 2行目がメソッドチェーンであることがひと目でわかる
  .four

1-17b Option B

When continuing a chained method invocation on another line, include the . on the first line to indicate that the expression continues.

ドット.を前の行に残すスタイルです。

# 不可
one.two.three   # 2行目を読まないとメソッドチェーンが継続しているかどうかがわからない
  .four

# 良好
one.two.three.  # 1行目のメソッドチェーンが継続していることがひと目でわかる
  four

両方にドットを残せる言語仕様であればよかったのかなと一瞬思ったりしましたが、改行がなくなったときに範囲演算子..と誤認されそうなのでよくありませんね。

宗論はどちらが負けても釈迦の恥』という言葉をなぜか思い出してしまいました。もっとも釈迦は本を一冊も書いていないそうですが。

1-18 【統一】メソッド呼び出しを2行以上に分割する場合はパラメータを揃える


1-18a【選択】幅が足りない場合は「2インデント」でもよい

Align the parameters of a method call if they span more than one line. When aligning parameters is not appropriate due to line-length constraints, single indent for the lines after the first is also acceptable.

原則、以下の上図のようにパラメータの縦をキモチヨク揃えますが、表示幅が狭い場合は下図でもよいということですね。

alignment 1

alignment 2

# この長すぎるメソッドを分割するとする
def send_mail(source)
  Mailer.deliver(to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: source.text)
end

# 不可(4スペースインデント)
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

# 良好 (通常の2スペースインデント)
def send_mail(source)
  Mailer.deliver(
    to: 'bob@example.com',
    from: 'us@example.com',
    subject: 'Important message',
    body: source.text
  )
end

1-19【統一】配列リテラルの要素を複数行に分割する場合は縦を揃える

Align the elements of array literals spanning multiple lines.

# 不可 - シングルインデント
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]


1-19a【選択】ブラケット[も次の行に送ると縦が揃ってないように見える気もしますが、これも認められています。

1-20 【統一】桁数の多い数値はアンダースコア_で区切る

Add underscores to large numeric literals to improve their readability.

この記法は他の言語では見かけないような気がしますが、カンマを使わなくてよいので便利ですね。

# 不可 - ゼロがいくつあるかわかりにくい
num = 1000000

# 良好 - 目に優しい
num = 1_000_000

1-21【統一】数値のprefixは小文字で書く

Prefer smallcase letters for numeric literal prefixes. 0o for octal, 0x for hexadecimal and 0b for binary. Do not use 0d prefix for decimal literals.

8進数
0o
16進数
0x
2進数
0b

【統一】通常の10進リテラルにも0dというprefixが一応ありますが、これは使ってはならないというスタイルになっています。

Rubyでは大文字でも書けることを初めて知りました。

# 不可
num = 01234
num = 0O1234
num = 0X12AB
num = 0B10101
num = 0D1234
num = 0d1234

# 良好 - prefixを見分けやすい
num = 0o1234
num = 0x12AB
num = 0b10101
num = 1234

1-22【統一】APIドキュメントにはYARDを使う

Use YARD and its conventions for API documentation.

YARDは標準ではないんですね。

577765bの更新を反映しました。

1-23【統一】1行は80文字以内とする

Limit lines to 80 characters.

もちろん半角英数字(alphanumeric)での80文字です。1行にそんなにたくさん押し込めたことがないのでわかりませんが、個人的にはエディタのワードラップアラウンドにおまかせしたいところです。

1-24【統一】行末にスペース文字(ホワイトスペース)を置かない

Avoid trailing whitespace.

trailingといえば通常は対象の末尾を指します。

1-25【統一】ファイルの末尾には空行を1つ置く

End each file with a newline.

補足: ファイル末尾に空行を置く理由(morimorihoge

Unix系環境でのテキスト処理における一種のマナーとして、EOF前に空行を置く慣習があります。

末尾空行がないと、たとえばソースをcatしたときの出力が以下のようにプロンプト行と重なってしまいます。

# 改行なし
[morimori] $ cat .ruby-version
2.3.3[morimori] $

# 改行あり
[morimori] $ cat .ruby-version
2.3.3
[morimori] $

また、行指向なプログラムのの中には「行頭から行末(改行コードの手前)まで」を1行と見なすものがありますので、EOL前改行がないと最終行だけ処理されないことがあります。

逆に言えば、EOL前改行を置くことで「すべての行が改行で終わる」ので処理が一貫します。

1-26【統一】=begin=endによるブロックコメントは使わない

Don't use block comments. They cannot be preceded by whitespace and are not as easy to spot as regular comments.

なお、=begin=endの間のコメント行の先頭にはスペースを置けません。

# 不可
=begin
comment line
another comment line
=end

# 良好
# comment line
# another comment line

=begin=endによるブロックコメントはデバッグで特定の処理を抑制するのには便利ですが、読みにくいのでコミットには残してほしくないですね(morimorihoge)。

関連記事



CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。