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

こんにちは、hachi8833です。

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

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

!の後ろにはスペースを置かない

No space after !.

他に何にも書いていませんが、これはRubyの単独の否定演算子!ですね。以下のようなものは該当しないと考えることにします。

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

# 良好
 !something

範囲演算子.....の前後にはスペースを置かない

No space inside range literals.

私の感覚でも、範囲演算子の前後にスペースがあると意味が弱まってしまうように思えます。

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

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

case文内部のwhenのインデントの深さはcaseと同じにする

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

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

このスタイルについて、弊社Webチームのkazzさんから以下の記事をお知らせいただきました。Eiffelの影響だったんですね。ありがとうございます!

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

変数に条件式の結果を代入する場合、条件式の後続文は条件式と同じ位置になるようインデントする

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

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

case indentation

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

case indentation 2

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

「良好」が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

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

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

追伸

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

メソッド定義とメソッド定義の間には空行を置く、論理上パラグラフとして区切れる部分にも空行を置く

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行の場合)

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)

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

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

1番目はここでは「不可」となっていますが、多くの書籍で使われているのでそれなりに理由があるはずです。

\で行を継続するのは避ける(文字列の結合での利用のみ認める)

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'

ドット.によるメソッドチェーンを行分割する場合は、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のどちらがよりよいというものではない、ということですね。重要なのはどちらかに揃えることです。

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

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

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

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

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

メソッド呼び出しを2行以上に分割する場合はパラメータを揃える(幅が足りない場合は「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

配列リテラルの要素を複数行に分割する場合は縦を揃える

Align the elements of array literals spanning multiple lines.

# 不可 - single indent
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']

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

桁数の多い数値はアンダースコア_で区切る

Add underscores to large numeric literals to improve their readability.

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

# 不可 - how many 0s are there?
num = 1000000

# 良好 - much easier to parse for the human brain
num = 1_000_000

数値の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.

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

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

# 良好 - easier to separate digits from the prefix
num = 0o1234
num = 0x12AB
num = 0b10101
num = 1234

APIドキュメントにはRDocを使い、RDocコメント行defの間に空行を置かないようにする

Use Rdoc and its conventions for API documentation. Don’t put an empty line between the comment block and the def.

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

1行は80文字以内とする。

Limit lines to 80 characters.

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

行末にスペース文字(ホワイトスペース)を置かない

Avoid trailing whitespace.

行の末尾のことですね。trailingといえば通常対象の末尾を指します。

ファイルの末尾には空行を置くこと

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前改行がないと最終行だけ処理されないことがあります。

言われてみれば、末尾の空行がなければコードの最終行だけ改行がないことになり、一貫しなくなってしまいますね。

=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

追記

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

今回は以上です。次回は「Syntax」です。ご期待ください。

関連記事

Ruby on RailsによるWEBシステム開発、Android/iPhoneアプリ開発、電子書籍配信のことならお任せください この記事を書いた人と働こう! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

hachi8833

Twitter: @hachi8833 コボラー、ITコンサル、ローカライズ業界を経てなぜかWeb開発者志願。 これまでにRuby on Rails チュートリアルの大半、Railsガイドのほぼすべてを翻訳。 かと思うと、正規表現の粋を尽くした日本語エラーチェックサービス enno.jpを運営。 仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。

hachi8833の書いた記事

BPSアドベントカレンダー

人気の記事