hp12c
27 April 2012

チルダがRubyのヒアドキュメントをもっと良くする

Rubyのヒアドキュメントは便利です。複数行に渡る整形文章を出力するときに、これを使わない手はありません。

class ATool
 def self.help
 lines = <<EOS
 Instruction of `#{self}`
 `#{self}` is one of a great tool in the world.
 This helps you a lot on your daily work.
 Your life will be changed with `#{self}`!!
 Everyone knows about `#{self}`.
 So, You can ask them to learn `#{self}`
 Just Use `#{self}`
 from Today!
EOS
 lines
 end
end
puts ATool.help
# >> Instruction of `ATool`
# >> 
# >> `ATool` is one of a great tool in the world.
# >> This helps you a lot on your daily work.
# >> Your life will be changed with `ATool`!!
# >> Everyone knows about `ATool`.
# >> So, You can ask them to learn `ATool`
# >> 
# >> Just Use `ATool`
# >> 
# >> from Today!

ただ、終端ラベル(EOS)のポジションが見苦しいです。これは<<に代えて<<-とすることで解決できます。

class ATool
 def self.help
 lines = <<-EOS
 Instruction of `#{self}`
 `#{self}` is one of a great tool in the world.
 This helps you a lot on your daily work.
 Your life will be changed with `#{self}`!!
 Everyone knows about `#{self}`.
 So, You can ask them to learn `#{self}`
 Just Use `#{self}`
 from Today!
 EOS
 lines
 end
end
puts ATool.help
# >> Instruction of `ATool`
# >> 
# >> `ATool` is one of a great tool in the world.
# >> This helps you a lot on your daily work.
# >> Your life will be changed with `ATool`!!
# >> Everyone knows about `ATool`.
# >> So, You can ask them to learn `ATool`
# >> 
# >> Just Use `ATool`
# >> 
# >> from Today!

EOSをオフセットできました。

ヒアドキュメントの問題点

しかし依然、問題があります。文章の先頭マージンを除去したい場合は、次のようにしなければなりません。

class ATool
 def self.help
 lines = <<-EOS
 Instruction of `#{self}`
`#{self}` is one of a great tool in the world.
 This helps you a lot on your daily work.
 Your life will be changed with `#{self}`!!
 Everyone knows about `#{self}`.
 So, You can ask them to learn `#{self}`
 Just Use `#{self}`
 from Today!
 EOS
 lines
 end
end
puts ATool.help
# >> Instruction of `ATool`
# >> 
# >> `ATool` is one of a great tool in the world.
# >> This helps you a lot on your daily work.
# >> Your life will be changed with `ATool`!!
# >> Everyone knows about `ATool`.
# >> So, You can ask them to learn `ATool`
# >> 
# >> Just Use `ATool`
# >> 
# >> from Today!

これは頂けません。可読性が下がります。Rubyistたちはこれに苦労しています。

How do I remove leading whitespace chars from Ruby HEREDOC? - Stack Overflow

しかし、スマートな解決策は見当たりません。

DATAによる解決策

そこで解決策を考えてみました。

まずはDATAの活用です。__END__以降に整形文章を先頭マージン無しで置き、これを読み込むようにします。

class ATool
 def self.help
 lines = <<-EOS#{DATA.read} EOS
 lines
 end
end
puts ATool.help
__END__
 Instruction of `#{self}`
`#{self}` is one of a great tool in the world.
 This helps you a lot on your daily work.
 Your life will be changed with `#{self}`!!
 Everyone knows about `#{self}`.
 So, You can ask them to learn `#{self}`
 Just Use `#{self}`
 from Today!
# >> 
# >> Instruction of `#{self}`
# >> 
# >> `#{self}` is one of a great tool in the world.
# >> This helps you a lot on your daily work.
# >> Your life will be changed with `#{self}`!!
# >> Everyone knows about `#{self}`.
# >> So, You can ask them to learn `#{self}`
# >> 
# >> Just Use `#{self}`
# >> 
# >> from Today!
# >>

可読性は上がりました。しかし、DATAはファイルをrequireすると読めないなどの問題があり、実用的ではありません。

~(チルダ)による解決策

そこで先日のトリビアで紹介した、単項演算子~(チルダ)を使う手を思いついたのです。~はそのレシーバがメソッドの後ろに来るというユニークな特徴があります。

まず、String#~を定義します。

class String
 def ~
 margin = scan(/^ +/).map(&:size).min
 gsub(/^ {#{margin}}/, '')
 end
end

このコードで先頭マージンが除去されます。

そしてヒアドキュメントにおける<<の前に、~を置いて~<<-EOSのようにすればいいのです。

class ATool
 def self.help
 lines = ~<<-EOS
 Instruction of `#{self}`
 `#{self}` is one of a great tool in the world.
 This helps you a lot on your daily work.
 Your life will be changed with `#{self}`!!
 Everyone knows about `#{self}`.
 So, You can ask them to learn `#{self}`
 Just Use `#{self}`
 from Today!
 EOS
 lines
 end
end
puts ATool.help
# >> Instruction of `ATool`
# >> 
# >> `ATool` is one of a great tool in the world.
# >> This helps you a lot on your daily work.
# >> Your life will be changed with `ATool`!!
# >> Everyone knows about `ATool`.
# >> So, You can ask them to learn `ATool`
# >> 
# >> Just Use `ATool`
# >> 
# >> from Today!

先頭マージンが除去されました。

~、いいですね!


自分を変える18の知恵 by D.L. チルダー


関連記事:第3弾!知って得する12のRubyのトリビアな記法

(追記:2012年04月27日) String#~にバグがあったので直しました。

(追記:2012年04月27日) @n0kadaさんのツイートを受けてString#~を変更しました。無駄なことしていました。(Twitter / @n0kada: gsub(/^ {#{self.scan(/^ +/ ...)

(修正前)
def ~
 indent = lines.map { |l| l[/^ +/] }.compact.map(&:size).min
 lines.map { |line| line[indent..-1] || "\n" }.join
end


Please enable JavaScript to view the comments powered by Disqus. blog comments powered by Disqus
ruby_pack8

100円〜で好評発売中!
M'ELBORNE BOOKS



AltStyle によって変換されたページ (->オリジナル) /