前提
- ヒアドキュメントという言葉すら知らないケツの青い少年です。
環境
- ruby 2.5.0
TL;DR
<<[(-|~)]["'`]識別子["'`]
...
識別子
詳細
ヒアドキュメントをざっくり言うと、複数行にまたがる文字列を出力したいときに使う文法です。よく見るのは、長くなりがちな生 SQL を書く時とか、マークダウンの出力結果をテストする時とかに使うって感じでしょうか。たぶんもっとたくさん使い道あると思います。
<<FOO
<div class="foo">
<p>Hello World!</p>
</div>
FOO
# => " <div class=\"foo\">\n <p>Hello World!</p>\n </div>\n"
<<SQL
select count(*) from users where created_at between '1970-01-01' and '2018-10-11'
SQL
# => " select count(*) from users where created_at between '1970-01-01' and '2018-10-11'\n"
各パターンの特徴
TL;DR に書いたように、いくつか書き方のパターンがあるので、それぞれのパターンの特徴を列挙していきたいと思います。
<<
と <<-
と <<~
<<FOO
FOO
- 中身のコードのインデントは省略されず、そのまま出力されます。
- 終端の
FOO
にインデントは許可されていません。必ず行頭にべったりくっついてる必要があります。
<<-FOO
FOO
# 例)
def foo
var = <<-FOO
some_variable
FOO
# ↑ インデントしていてもOK!
end
- 中身のコードのインデントは省略されず、そのまま出力されます。
- 終端の
FOO
はインデントすることができます。つまり、上記の例のように、ネストしたコードの中に書いてもOKです。
<<~FOO
FOO
# 例)
foo = <<~FOO
hoge
hoge
hoge
hoge
FOO
puts foo
# ながーいインデントが省略されます。
hoge
hoge
hoge
hoge
=> nil
- 一番インデントの短い行を基準として、全ての行のインデントが省略されます。
- 終端の
FOO
はインデントすることができます。つまり、上記の例のように、ネストしたコードの中に書いてもOKです。
<<"FOO"
と <<'FOO'
と <<`FOO`
<<"FOO"
FOO
# 例)
<<"FOO"
昨日のこの時間は、 #{1.day.ago} です。
FOO
# => " 昨日のこの時間は、 2018-10-11 00:32:59 +0900 です。\n"
- 識別子(FOO)で囲まれたコードは
""
で囲まれた文字列と同じ扱いになります。つまり、#{}
を使って式展開することができます。 - ちなみに
<<FOO
のように何も囲わなかった場合、ダブルクオートで囲まれたのと同じ扱いになります。
<<'FOO'
FOO
# 例)
<<'FOO'
hoge\n hoge\n hoge\n hoge\n
FOO
=> " hoge\\n hoge\\n hoge\\n hoge\\n\n"
- 識別子(FOO)で囲まれたコードは
''
で囲まれた文字列と同じ扱いになります。つまり、書いた文字列がそのまま出力されます。逆にこの場合、#{}
による式展開は無視されます。
<<`FOO`
FOO
# 例)
<<`FOO`
echo #{Time.current}
FOO
# => "2018-10-12 00:08:53 +0900\n"
- 識別子(FOO)で囲まれたコードは
``
で囲まれた文字列と同じ扱いになります。つまり、中身のコードはコマンドとして実行され、結果の標準出力が文字列として返されます。なお、コマンド実行の前に式展開が行われるので、#{}
を使うことができます。
引数としてのヒアドキュメント
メソッドの引数に開始ラベルを書くことによって、ヒアドキュメント全体を引数として代入することができます。以下の例の場合、生 SQL を第1引数として find_by_sql
メソッドに渡しています。
users_in_first_quarter = Users.find_by_sql([<<~SQL, { from: '2018-01-01', to: '2018-03-31' }])
select count(*) as number_of_users from users where created_at between :from and :to
SQL
puts users_in_first_quarter.number_of_users
複数のヒアドキュメント
(2018/10/12追記)
以下のように、複数のヒアドキュメントを重ねて書くこともできます。仮に変数に代入した場合は、配列として保存されます。
foo = <<~FOO, <<~BAR, <<~BAZ
this is foo
FOO
this is bar
BAR
this is baz
BAZ
# => ["this is foo\n", "this is bar\n", "this is baz\n"]
参考文献
公式ドキュメント is 最高。
https://docs.ruby-lang.org/ja/latest/doc/spec=2fliteral.html