Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Ruby: ヒアドキュメントの引数やメソッド呼び出しは「開始行」に置こう


github.com/rubocop-hq/rubocopより

「Rubyスタイルガイド」記事の元であるRubocopスタイルガイドをチェックしたところ、いくつか追加更新があったのですが、今更のようにこの書き方に気づきました。

Rubyのヒアドキュメントでは引数やメソッド呼び出しを「開始行」に置ける

スタイルガイドのコード例のままでは芸がないので、別記事の正規表現を使って試してみました。

1. ヒアドキュメントに対するメソッド呼び出し

regex = <<~MOBILE_SUITS.tr("\n", "")
\b(
YMS-(15|14|07B)|
MSN-(X2|02)|
MSM-(10|07S?|04F?|03)|
MS-(X16|X10|R09|14S|14A|14|11|09R?|07B?|06S|06J|06F|06|05B)|
MAX-03|
MAN-(X8|X3|08|07|03)|
MAM-07|
MA-(08|05H?|04X)
)\b
MOBILE_SUITS

#» "\b(YMS-(15|14|07B)|MSN-(X2|02)|MSM-(10|07S?|04F?|03)|MS-(X16|X10|R09|14S|14A|14|11|09R?|07B?|06S|06J|06F|06|05B)|MAX-03|MAN-(X8|X3|08|07|03)|MAM-07|MA-(08|05H?|04X))\b"

1行目のregex = <<~MOBILE_SUITS.tr("\n", "")というメソッドを適用して、正規表現の改行を取り除いています。

もちろん、今までどおり以下のようにメソッド呼び出しをヒアドキュメントの末尾に書くことも一応できますが、追加されたRuboCopスタイルガイドではこの書き方は「エラーを誘発する」として非推奨になっています。

regex = <<~MOBILE_SUITS
\b(
YMS-(15|14|07B)|
MSN-(X2|02)|
MSM-(10|07S?|04F?|03)|
MS-(X16|X10|R09|14S|14A|14|11|09R?|07B?|06S|06J|06F|06|05B)|
MAX-03|
MAN-(X8|X3|08|07|03)|
MAM-07|
MA-(08|05H?|04X)
)\b
MOBILE_SUITS
.tr("\n", "")

#» "\b(YMS-(15|14|07B)|MSN-(X2|02)|MSM-(10|07S?|04F?|03)|MS-(X16|X10|R09|14S|14A|14|11|09R?|07B?|06S|06J|06F|06|05B)|MAX-03|MAN-(X8|X3|08|07|03)|MAM-07|MA-(08|05H?|04X))\b"

たしかに見た目にも末尾の.tr("\n", "")メソッドがぶらぶらしていて落ち着きがありませんし、これを見落としてコードを書き換えてしまいそうです。さらに、後者の非推奨の書き方はirbやpryでは使えません(実行するとエラーになります)。

# pryで実行(プロンプトは変更しています)
» regex = <<~MOBILE_SUITS
» \b(
» YMS-(15|14|07B)|
» MSN-(X2|02)|
» MSM-(10|07S?|04F?|03)|
» MS-(X16|X10|R09|14S|14A|14|11|09R?|07B?|06S|06J|06F|06|05B)|
» MAX-03|
» MAN-(X8|X3|08|07|03)|
» MAM-07|
» MA-(08|05H?|04X)
» )\b
» MOBILE_SUITS
#» "\b(\nYMS-(15|14|07B)|\nMSN-(X2|02)|\nMSM-(10|07S?|04F?|03)|\nMS-(X16|X10|R09|14S|14A|14|11|09R?|07B?|06S|06J|06F|06|05B)|\nMAX-03|\nMAN-(X8|X3|08|07|03)|\nMAM-07|\nMA-(08|05H?|04X)\n)\b\n"
» .tr("\n", "")
sh: -c: line 0: syntax error near unexpected token `"\n",'
sh: -c: line 0: `tr("\n", "")'
Error: there was a problem executing system command: tr("\n", "")
»

2. ヒアドキュメントを引数で囲む場合

こちらのコード例はRuboCopスタイルガイドそのままです。ヒアドキュメントそのものを引数にする場合、ヒアドキュメント全体を丸かっこ()で囲まなくても、以下の「good」のように、ヒアドキュメントの開始部分だけを丸かっこ()で囲んでも同じ動作になります。かつ、RuboCopスタイルガイドでもこの書き方が推奨されています。

# bad
foo(<<~SQL
  select foo from bar
SQL
)

# good
foo(<<~SQL)
  select foo from bar
SQL

一瞬「それってありなの?」という気持ちにさせられるシンタックスシュガーですが、メソッド呼び出しや引数の丸かっこを1行目にまとめて書く方がpryでも安定して使えますし、すっきりすると思えてきました😋。

ヒアドキュメントの区切り文字列には「意味のある言葉」を使おう

RuboCopスタイルガイドではSUMMARYなどの意味のある言葉を区切り文字列に使うことというスタイルが追加されていました。たしかに、ヒアドキュメントのサンプルコードで十年一日のごとく使われているEOSなどをそのまま使うよりも意味が明確ですね。

message = <<~GREETING
  こんにちは、[hachi8833](/author/hachi8833)です。
GREETING

上の追加スタイルはRubyスタイルガイドにも反映します。

3. ヒアドキュメントの式展開を無効にする方法

この項はRuboCop作者のブログで知りました。

参考: Weird Ruby: Single-quoted Heredocs | Meta Redux

ご存知の通り、Rubyの変数の式展開#{}は、二重引用符" "では効きますが、一重引用符' 'の中では効きません。

ヒアドキュメントを使って、式展開そのものを含むメッセージを渡そうとしても、そのままでは展開が発動してしまいます。

name = "hachi8833"
message = <<~GREETING
  こんにちは、#{name}です。
GREETING

p message  # "こんにちは、hachi8833です。\n"

これも2.と同じ要領でできます。ヒアドキュメントの開始の区切り文字列を'GREETING'のように一重引用符で囲めば、期待どおりに式展開を無効にしたままヒアドキュメントを渡せます。

name = "hachi8833"
message = <<~'GREETING'
  こんにちは、#{name}です。
GREETING

p message  # "こんにちは、\#{name}です。\n"

ところで、本記事で紹介した一連のヒアドキュメントのシンタックスシュガーは、もしかするとBashの*展開をエスケープするときの記法の影響ではないかという話も聞きました。

参考: sh での変数とワイルドカードの落とし穴|てくめも@ecoop.net

# 同記事より
foo="SELECT * FROM table"
echo "$foo"
#=> SELECT * FROM table

関連記事

Rubyの式展開(string interpolation)についてまとめ: `#{}`、`%`、Railsの`?`

【保存版】Rubyスタイルガイド(日本語・解説付き)総もくじ


CONTACT

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