#Ruby コーデング規約(標準スタイル編)
##バージョン
- なるべく最新のものを利用する。
- rbenv 、もしくは rvm を利用する。
- SCMを利用するときは .ruby-version も一緒にコミットし、処理系のバージョンを統一する。
##レイアウト
##エンコーディング
-
UTF-8以外は使用しない。
-
基本的にスクリプトエンコーディングのマジックコメントが必要なソースコードにしない。
理由
-
UTF-8がcomputer/webの世界で事実上の標準エンコードであるため。
-
2バイト文字は基本的にユーザーのために記述するものであり、それはソースコードに埋め込まれずにlocaleファイル等に記述されるべきものであるため。
##基本
- インデントはホワイトスペース2個
- ハードタブは使用しない
- 行の最後に無駄なホワイトスペースは付けない
- 演算子の前後、コロンの前後、カンマの後ろとセミコロンの後ろににホワイトスペースを1個置く
- カンマ、セミコロンの前にはホワイトスペースは置かない
sum = 1
a, b = 1, 2
1 > 2 ? true : false; puts 'Hi'
-
1行の文字数は80文字以下にする
-
80文字を超える時は、以下のルールで改行を入れる
- メソッドチェーンを途中で改行するときは
.
(ドット)を改行後の先頭に持ってくる
"one string".something_long_long_method(arg1) .other_cool_long_method(arg2) .another_awsome_long_method(arg3)
- メソッドの定義を途中で改行するときは Syntax Error にならないよう適宜
()
を用いて改行をする
def long_method_name(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, options)
- メソッドチェーンを途中で改行するときは
-
長い文字列リテラルのせいで 80文字を越えてしまう場合のみ、80文字を越えることを許可する
# ok foo = "This is a very very long string that can't be broken down and may contain #{variable}"
-
[] () {} 全ての前後にホワイトスペースは置かない
a = [1, 2, 3] #"[" の左のスペースは = の後ろのもの
[1, 2, 3].each{|num| puts num * 2}
def method(a, b, c)
- 改行を含む長い文字列にはヒアドキュメントを使う。ただし、短いメッセージを定義する時や、メソッドチェーンしたいときには、文字列リテラルを使っても良い。
# good
foo = <<-EOS
From this valley they say you are going,
We will miss your bright eyes and sweet smile,
For they say you are taking the sunshine
That has brightened our pathways a while.
EOS
# ok
foo = "Hi, Johnny.
How are you?"
# bad
foo = "From this valley they say you are going,\nWe will miss your bright eyes and sweet smile,\nFor they say you are taking the sunshine\nThat has brightened our pathways a while."
- 仮引数の後にホワイトスペースを入れる
# good
arr.each{|elem| puts elem}
# bad
arr.each{|elem|puts elem}
- Hash は基本的に1.9の省略記法で記載する
# bad
h = {:key => :value}
# good
h = {key: :value}
do
と仮引数の間にホワイトスペースを入れる
# good
arr.each do |elem|
puts elem
end
# bad
arr.each do|elem|
puts elem
end
- コメントアウトの
#
の後にはホワイトスペースを置く
#this is bad comment
# this is good comment
- コメントアウトの
=begin
の行には何も書いてはいけない
=begin # bad style
do
somthing
end
=end
=begin
# good style
do
somthing
end
=end
when
とcase
のインデントは同じ深さにする
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
の行のインデントをcase
の行に対して 1 つ下げる。
foo = 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
def
の後には空行を入れる。
def method1
# some proccesses
end
def some_method2
# some proccesses
end
##文法
- メソッドの定義には
()
を使わない。ただし以下の場合は適宜()
を使っても良い。- メソッドの引数が多い等で80文字に納まらず、改行をする。
def method1
# some proccesses
end
def method2 arg1, arg2
# some proccesses
end
-
メソッドの呼び出しには
()
を使わない。ただし、以下の場合は適宜()
を使っても良い。- 引数に演算子が着いている、またはメソッド前後に演算子が着いている。ハッシュが引数の時も含む。
- 引数が2個以上である。
- メソッドの引数が多い等で80文字に納まらず、改行をする。
-
for
は使用禁止
arr = [1, 2, 3]
# bad
for elem in arr do
puts elem
end
# good
arr.each{|elem| puts elem}
then
は使用禁止
# bad
if some_condition then
# some proccesses
end
# good
if some_condition
# some proccesses
end
- 三項演算子(
? :
)を使って良いのは1行で全てが納まる時のみとし、その場合はif then else
を使わない。
# good
weather = sun.shiny? ? 'well' : 'cloud'
# bad
weather = sun.shin? ?
'well'
:
'cloud'
# bad
weather = if sun.shiny? then 'well' else 'cloud' end # you must also not use 'then' keyword.
- 三項演算子を重ねて利用してはいけない
# bad
some_condition ? (nested_condition ? nested_something : nested_something_else) : something_else
# good
if some_condition
nested_condition ? nested_something : nested_something_else
else
something_else
end
-
and
とor
は&&
と||
で代用できる場合は代用する -
1行で
if 〜 end
またはunless 〜 end
が納まる場合は後置にする。 -
後置の
if
やunless
は、それらの右辺と左辺を含めて一行80文字以内に収まるときのみ使って良い。
# bad
if some_condition
foo = "This is a short string"
end
# good
foo = "This is a short string" if some_condition
# bad
foo = "This is a very very long string that can not be broken down and may contain #{variable}" unless some_condition
# good
unless some_condition
foo = "This is a very very long string that can not be broken down and may contain #{variable}"
end
unless
はelse
と共に使ってはいけない
# bad
unless success?
puts 'failure'
else
puts 'success'
end
# good
if success?
puts 'success'
else
puts 'failure'
end
if
の条件に否定演算子!
を用いてはいけない。その場合はunless
を使う。ただし、&&
や||
と組み合わせる場合は用いても良い。その場合もド・モルガンの法則を利用する等で簡潔な記述を心がけること。
# bad
if !user.nil?
user.greeting
end
# good
unless user.nil?
user.greeting
end
# better
if user
user.greeting
end
# best
user.greeting if user
# OK
if !user.nil? && !user.suspended?
user.greeting
end
# not bad, but a little too complex
unless user.nil? || user.suspended?
user.greeting
end
# should be
if user && user.active?
user.greeting
end
if/unless/while
の条件に()
は使わない
# bad
if (x > 10)
# body omitted
end
# good
if x > 10
# body omitted
end
- 1行で納まるblockは
{}
を使う。そうでない場合はdo 〜 end
を使う。method chain する場合もこのルールに従う。
names = ["Bozhidar", "Steve", "Sarah"]
# good
names.each{|name| puts name}
# bad
names.each do |name|
puts name
end
# good
[1, 2, 3].map{|num| num * 2}.reduce{|double, sum| sum += double}
# also good
[1, 2, 3].map do |num|
num * 2
end.reduce do |double, sum|
sum += double
end
return
は省略できる場合は省略する
# bad
def some_method(some_arr)
return some_arr.size
end
# good
def some_method(some_arr)
some_arr.size
end
if
の条件式を代入する場合、()
でくくる
# good
if (v = array.grep(/foo/)) ...
# bad
if v = array.grep(/foo/) ...
# also good - has correct precedence.
if (v = next_value) == "hello" ...
理由
==
メソッドとの混同を防ぐため
- 変数の初期化に
||=
の使用を推奨する。ただし、booleanの変数については、falseの値が上書きされるので注意すること。
# set name to Bozhidar, only if it iss nil or false
name ||= 'Bozhidar'
# bad - would set enabled to true even if it was false
enabled ||= true
# good
enabled = true if enabled.nil?
- メソッド名と引数の間にはホワイトスペースを入れない
# bad
f (3 + 2) + 1
# good
f(3 + 2) + 1
- block の引数で使用しないものは
_
で受け取る
# bad
result = hash.map {|k, v| v + 1}
# good
result = hash.map {|_, v| v + 1}
- block の仮引数は変数名に省略した単語を利用して良い。
# good
products.each {|product| product.maintain!}
# OK
products.each {|prod| prod.maintain!}
- 変数の初期値として 空の Array 、空の Hash を代入する場合には
Array.new
、Hash.new
と記述する。
#bad
@users = []
#good
@users = Array.new
#also good
@months_of_birth_date = User.all.inject([]){|months, user| months << user.birth_date.month}
理由
オブジェクトの新規作成という意図が明確であるため、「初期化」という意図が伝わりやすい。
##名付け
-
メソッド名や変数名には
snake_case
を使う -
クラス名やモジュール名は
CamelCase
を使う -
一般的な定数には
SCREAMING_SNAKE_CASE
を使う -
boolean を返却する method は
Array#empty?
のように最後を?
にする -
破壊的メソッドや危険なメソッドは
Array#flatten!
のように最後を!
にする。破壊的メソッドを定義する際にはArray#flatten
のように非破壊的メソッドも用意する。
##クラス
- クラス変数
@@
の利用は本当に必要な時以外は避ける。
class Parent
@@class_var = 'parent'
def self.print_class_var
puts @@class_var
end
end
class Child < Parent
@@class_var = 'child'
end
Parent.print_class_var # => will print "child"
- クラスインスタンス変数で実装できないかを検討する。
class Parent
@class_instance_var = 'parent'
def self.print_class_instance_var
puts @class_instance_var
end
end
class Child < Parent
@class_instance_var = 'child'
end
Parent.print_class_var # => will print "parent"
- 特異メソッドを定義するときは
def self.method
やdef ClassName.method
を使用しない。
class TestClass
# bad
def TestClass.some_method
# body omitted
end
# bad
def self.some_other_method
# body omitted
end
end
- 特異メソッド定義や特異クラスマクロは
class << self
で記述する。
class TestClass
class << self
attr_accessor :per_page
alias_method :nwo, :find_by_name_with_owner
def find_by_name_with_owner
# body omitted
end
def first_method
# body omitted
end
def second_method_etc
# body omitted
end
end
end
-
public
メソッドについては クラス定義の先頭に持ってきて、 publicのキーワードで宣言しない -
protected
メソッドはprivate
メソッドの前に書く。その時、protected 、 private メソッドの定義は public メソッドと同じ深さのインデントにし、protected 、 private メソッドの上に空行を置き、下には置かない。
class SomeClass
def public_method
# ...
end
protected
def protected_method
# ...
end
private
def private_method
# ...
end
end
- private メソッドの呼び出し以外で自分自身を指す時は
self
を記載する。
class SomeClass
attr_accessor :message
def set_name name
self.message = "Hi #{name}"
end
def greeting
puts self.message
end
end
- Exception をフロー制御に利用せずに、避けられる Exception は全て避ける。大域脱出には
throw / catch
を利用してよい。
理由
Exception を発生させると StackTraceを生成するために高い負荷が掛かる。正常処理の中で無用に StackTrace を生成するようなことをしてはいけない。
# bad
begin
n / d
rescue ZeroDivisionError
puts "Cannot divide by 0!"
end
# good
if d.zero?
puts "Cannot divide by 0!"
else
n / d
end
理由
rescue
の引数に何も指定しないと RuntimeError
を捕捉するので、それより大きい StandardError
や Exception
を捕捉してはいけない。
特に Exception
は例外の基底クラスなので、これを rescue
すると、(意図していないかもしれない)全ての例外が捕捉されるので禁止とする。
Exception
クラスをrescue
してはいけない。
# bad
begin
# an exception occurs here
rescue
# exception handling
end
# still bad
begin
# an exception occurs here
rescue Exception
# exception handling
end
- 文字列の配列を生成する際に、
%w( )
を積極的に利用する。
# bad
STATES = ['draft', 'open', 'closed']
# good
STATES = %w(draft open closed)
- Hash のキーは文字列を使わず、できる限りシンボルにする。
# bad
hash = { 'one' => 1, 'two' => 2, 'three' => 3 }
# good
hash = { one: 1, two: 2, three: 3 }
- 文字列への変数を混在する時は連結ではなく、展開を利用する。
# bad
email_with_name = user.name + ' <' + user.email + '>'
# good
email_with_name = "#{user.name} <#{user.email}>"
'
は「式展開をしない」、「"
が文字列に含まれる」、「\
(バックスラッシュ)が文字列に含まれる」等の理由が無い限り使用しない。
# bad
name = 'Bozhidar'
# good
name = "Bozhidar"
- 再代入する場合、
String#+
メソッドは使用しない。代わりにString#<<
を利用する。
# good and also fast
html = ''
html << '<h1>Page title</h1>'
paragraphs.each do |paragraph|
html << "<p>#{paragraph}</p>"
end
- ヒアドキュメントを利用するときには、デリミタを代入文と同じ深さにインデントする。
module AttrComparable
module ClassMethods
def attr_comparable *attrs
class_eval <<-DELIM
attrs.each do |attr|
define_method(attr.to_s<<'?'){|param| self.send(attr) == param }
end
DELIM
end
end
#...後略
$1 〜 9
は利用しない。マッチした文字列を利用する場合は名前を付ける。
# bad
/(regexp)/ =~ string
...
process $1
# good
/(?<meaningful_var>regexp)/ =~ string
...
process meaningful_var
- 改行を含む文字列全体の行頭と行末を指定する時には
\A
と\Z
を利用する。
string = "some injection\nusername"
string[/^username$/] # matches
string[/\Ausername\Z/] # don't match
- 複雑な正規表現パターンを記述する時には
x
オプションを積極的に利用する。ただし、それを適用すると空白文字が全て無視されることに注意すること。
regexp = %r{
start # some text
\s # white space char
(group) # first group
(?:alt1|alt2) # some alternation
end
}x
%()
は文字列自体に"
を記載する必要がある時にのみ利用する。
message = %(注意:'と"は区別されます)
%r()
は正規表現のパターン内に/
を記載する必要がある時にのみ領する。
# bad
%r(\s+)
# still bad
%r(^/(.*)$)
# should be /^\/(.*)$/
# good
%r(^/blog/2011/(.*)$)
- 変数と実数、定数等を比較するときは左辺に実数、右辺に変数とする。
greeting = "Hello!"
# bad
if greeting == "Hola!"
...
end
# good
if "Hola!" == greeting
...
end
** 理由 **
==
を誤って =
と記述した際に代入されずに SyntaxError として検知できるため。
__END__
は利用しない。