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


blog comments powered by Disqus
ruby_pack8

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