こんにちは、hachi8833です。
Rubyスタイルガイドを読む、第2回目です。今さらですが、スタイルガイドをただ読むよりも頭に入りますね。
- Rubyスタイルガイドを読む: 総もくじ
- 前回: Rubyスタイルガイドを読む: ソースコードレイアウト(1)エンコード、クラス定義、スペース
- 次回: Rubyスタイルガイドを読む: 文法(1)メソッド定義、引数、多重代入
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
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番目では
case
、when
、if
、else
、end
の縦が揃っていて、見ていてキモチイイですね。このスタイルは多くの言語で採用されています。
- 「良好」の2番目では、さらに等号
=
で改行することで、条件式の結果の代入で幅を取りすぎないようにできます。
面白いのは、どちらの場合もcase
やif
に対応するend
まで縦が揃っていることです。深入りはしませんが、Rubyのcase
やif
は例に示されているとおり結果の値を返すので、case
やif
全体で値を返すことを強調するという意図なのだろうと理解しました。
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.
原則、以下の上図のようにパラメータの縦をキモチヨク揃えますが、表示幅が狭い場合は下図でもよいということですね。
# この長すぎるメソッドを分割するとする
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)。
関連記事
- Rubyスタイルガイドを読む: ソースコードレイアウト(1)エンコード、クラス定義、スペース
- Rubyスタイルガイドを読む: ソースコードレイアウト(2)インデント、記号
- Rubyスタイルガイドを読む: 文法(1)メソッド定義、引数、多重代入
- Rubyスタイルガイドを読む: 文法(2)アンダースコア、多重代入、三項演算子、if/unless
- Rubyスタイルガイドを読む: 文法(3)演算子とif/unless
- Rubyスタイルガイドを読む: 文法(4)ループ
- Rubyスタイルガイドを読む: 文法(5)ブロック、proc
- Rubyスタイルガイドを読む: 文法(6)演算子など
- Rubyスタイルガイドを読む: 文法(7)lambda、標準入出力など
- Rubyスタイルガイドを読む: 文法(8)配列や論理値など
- Rubyスタイルガイドを読む: 命名
- Rubyスタイルガイドを読む: コメント、アノテーション、マジックコメント
- Rubyスタイルガイドを読む: クラスとモジュール(1)構造
- Rubyスタイルガイドを読む: クラスとモジュール(2)クラス設計・アクセサ・ダックタイピングなど
- Rubyスタイルガイドを読む: クラスとモジュール(3)クラスメソッド、スコープ、エイリアスなど
- Rubyスタイルガイドを読む: 例外処理
- Rubyスタイルガイドを読む: コレクション(Array、Hash、Setなど)
- Rubyスタイルガイドを読む: 数値、文字列、日時(日付・時刻・時間)
- Rubyスタイルガイドを読む: 正規表現、%リテラル、メタプログラミング(最終回)