- 開発
READ MORE
こんにちは、hachi8833です。「Rubyスタイルガイドを読む」シリーズもやっと終わりが見えてきました。
まとまりのよさから、今回は「数値、文字列、日付・時間編」といたしました。
Integer
を使うことUse
Integer
check type of an integer number. SinceFixnum
is platform-dependent, checking against it will return different results on 32-bit and 64-bit machines.
Fixnum
の結果はプラットフォームに依存するため、32ビット環境と64ビット環境で結果が同じにならないことがあるためです。
Ruby 2.4の整数はIntegerに一元化されましたが、2.3以前のRubyを使う場合は引き続き注意が必要ですね。
timestamp = Time.now.to_i
# 不可
timestamp.is_a? Fixnum
timestamp.is_a? Bignum
# 良好
timestamp.is_a? Integer
...
の利用が望ましいPrefer to use ranges when generating random numbers instead of integers with offsets, since it clearly states your intentions. Imagine simulating a role of a dice:
以下のサイコロのシミュレーションの例でわかるように、整数値にオフセット値を足すよりも意図が明確になるというのが理由です。
# 不可
rand(6) + 1
# 良好
rand(1..6)
+
ではなく、式展開と文字列フォーマットが望ましいPrefer string interpolation and string formatting instead of string concatenation:
+
で結合するのは一見わかりやすいのですが、「Rubyでの文字列出力に「#+」ではなく式展開「#{}」を使うべき理由」で解説したようにレシーバーによって動作が影響されるので思わぬ地雷を踏むかもしれません。
# 不可
email_with_name = user.name + ' <' + user.email + '>'
# 良好
email_with_name = "#{user.name} <#{user.email}>"
# 良好
email_with_name = format('%s <%s>', user.name, user.email)
式展開(interpolation)という訳語はRuby方面でよく使われますが、それにしてもうまい訳ですね。数学方面で「補間」と訳されてしまったせいか補間が使われるのをよく見かけますが、式展開はRubyでの動作に即していて明快だと思います。
今回の内容の多くが、上の記事でも言及されています。
'
)と二重引用符(ダブルクォート: "
)の用法をいずれかに統一することAdopt a consistent string literal quoting style. There are two popular styles in the Ruby community, both of which are considered good—single quotes by default (Option A) and double quotes by default (Option B).
Rubyコミュニティでは以下の2つのスタイルが見られますが、それぞれに利点があるので、どちらが正しいとか間違っているということではありません。
プロジェクトで変更点が少ない方を採用するとよいでしょう。
'
を優先するスタイル式展開を使わない場合と、特殊なシンボル(\t
、\n
、'
など)の表記には一重引用符を使う
(二重引用符は式展開が必要な場合のみ使うことになる)
# 不可
name = "Bozhidar"
# 良好
name = 'Bozhidar'
"
を優先するスタイル二重引用符そのものを含む文字列や、エスケープしたい文字がある場合にのみ、一重引用符を使う
# 不可
name = 'Bozhidar'
# 良好
name = "Bozhidar"
なお、本スタイルガイドではオプションAを採用しているそうです。
?x
のような文字列リテラル構文は使わないことDon’t use the character literal syntax
?x
. Since Ruby 1.9 it’s basically redundant—?x
would interpreted as'x'
(a string with a single character in it).
?x
という文字列リテラルの書き方があること自体初めて知りました。見かけた覚えがありません。
# 不可
char = ?c
# 良好
char = 'c'
{}
を省略しないことDon’t leave out
{}
around instance and global variables being interpolated into a string.
{}
を省略するとわかりにくくなるからだと思います。
やったことがありませんでしたが、インスタンス変数やグローバル変数は{}
なしでも式展開できてしまうんですね。
class Person
attr_reader :first_name, :last_name
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
# 不可 - 間違いではないが見苦しい
def to_s
"#@first_name #@last_name"
end
# 良好
def to_s
"#{@first_name} #{@last_name}"
end
end
$global = 0
# 不可
puts "$global = #$global"
# 良好
puts "$global = #{$global}"
Object#to_s
を呼ばないことDon’t use
Object#to_s
on interpolated objects. It’s invoked on them automatically.
式展開ではObject#to_s
が自動で呼ばれるので、明示的に呼ぶ必要はありません。
# 不可
message = "This is the #{result.to_s}."
# 良好
message = "This is the #{result}."
String#+
ではなく、String#<<
を使うことAvoid using
String#+
when you need to construct large data chunks. Instead, useString#<<
.
Concatenation mutates the string instance in-place and is always faster thanString#+
, which creates a bunch of new string objects.
String#<<
は文字列のインスタンス自体を変更するので、実行のたびに文字列オブジェクトを作成するString#+
よりも速度面で常に有利です。
ループ内で#+
を使うのは完全に悪手ですね。
# 不可
html = ''
html += '<h1>Page title</h1>'
paragraphs.each do |paragraph|
html += "<p>#{paragraph}</p>"
end
# 良好(しかも速い)
html = ''
html << '<h1>Page title</h1>'
paragraphs.each do |paragraph|
html << "<p>#{paragraph}</p>"
end
String#gsub
以外のメソッドを使うことDon’t use
String#gsub
in scenarios in which you can use a faster more specialized alternative.
Writing Fast Rubyというスライドに、#gsub
がパフォーマンス上不利であることが記載されており、これに基づいているようです。
#gsub
は正規表現が必要な場合にのみとどめ、#tr
などの適切かつ高速なメソッドを使いましょう。
url = 'http://example.com'
str = 'lisp-case-rules'
# 不可
url.gsub('http://', 'https://')
str.gsub('-', '_')
# 良好
url.sub('http://', 'https://')
str.tr('-', '_')
When using heredocs for multi-line strings keep in mind the fact that they preserve leading whitespace. It’s a good practice to employ some margin based on which to trim the excessive whitespace.
以下はgsub(/^\s+\|/, '')
を使って、|
とその前のリーディングスペース文字を削除しています。
code = <<-END.gsub(/^\s+\|/, '')
|def test
| some_method
| other_method
|end
END
# => "def test\n some_method\n other_method\nend\n"
ヒアドキュメントに余分なスペースを含めたくない場合は検討するとよさそうです。
<<~終了文字列
」記法を使うことUse Ruby 2.3’s squiggly heredocs for nicely indented multi-line strings.
最初の2つの不可例は従来の手法です。いろいろと涙ぐましいことしていたんですね。
# 不可 - PowerpackのString#strip_marginによる手法
code = <<-END.strip_margin('|')
|def test
| some_method
| other_method
|end
END
# 同じく不可
code = <<-END
def test
some_method
other_method
end
END
# 良好
code = <<~END
def test
some_method
other_method
end
END
<<~終了文字列
記法は通常のコードインデントとの整合性が取れているのがありがたいです。
Time.now
が望ましいTime.now
over Time.new
when retrieving the current system time.RubyリファレンスマニュアルのTimeではどちらも動作が同じです。
その時点の現在時刻を取得するという意図を明確にするためにTime.now
を使う、と理解しました。
#new
だと単なるTimeオブジェクトの生成と思われそうです。それが必要な場合もあると思うので、意図に応じて使い分けましょう。nowとnewはぱっと見に似てるといえば似ていますが。
上のTime.now
を使うルールはRubyのみの場合です。
Railsでは、ActiveSupportで用意されている、よりtimezoneコンシャスなTimeWithZoneを使うために以下のいずれかを使うのが定番です。これらを使うと、実際にはTimeWithZoneオブジェクトが使われます。
Time::zone#now
Time::current#now
— Time.zone.now
を返すので上と同じ逆に、RailsでRubyのTime#nowやDateTime#nowを使うと、せっかくRailsに用意されているTimeWithZoneが使われないので、避けましょう。
特にRubyのDateTimeについては、次の項目でもみだりに使うべきでないというスタイルになっています。
参考: RubyとRailsにおけるTime, Date, DateTime, TimeWithZoneの違い — Rail 4の日付・時刻の扱いを中心とする良記事です。特に環境変数とRails設定でのタイムゾーンの注意点について非常に参考になりました。ありがとうございます!
DateTime
はカレンダーを過去に遡って再編成する必要がない限り使わないことDon’t use
DateTime
unless you need to account for historical calendar reform — and if you do, explicitly specify thestart
argument to clearly state your intentions.
DateTime
を使いたい場合はstart
引数で意図を明確にすること、だそうです。
これについても上記記事が参考になりました。RubyのDateTimeは暦法を変える場合に使うのが適切であるようです。
# 不可 - 現在時刻の取得にDateTimeを使っている
DateTime.now
# 良好 - 現在時刻の取得にTimeを使っている
Time.now
# 不可 - 現代の日付取得にDateTimeを使っている
DateTime.iso8601('2016-06-29')
# 良好 - 現代の日付取得にDateを使っている
Date.iso8601('2016-06-29')
# 良好 - 歴史的な過去のカレンダーにDateTimeとstart引数を使っている
DateTime.iso8601('1751-04-23', Date::ENGLAND)
今回はここまでです。最終回「正規表現、%リテラル、メタプログラミング」編にご期待ください。