読者です 読者をやめる 読者になる 読者になる

ヒアドキュメントを便利にする String#undent メソッド

元ネタは Homebrew の String#undent メソッドです.
ここで紹介するものはそれを少し賢くしています.

class String
    def undent
        min_space_num = self.split("\n").delete_if{|s| s=~ /^\s*$/ }.map{|s| s[/^\s+/].length }.min
        gsub(/^[ \t]{,#{min_space_num}}/, '')
    end
end

Ruby のヒアドキュメントは,

class Notifier
    def warning
        <<-EOS
    This is warning.
your action is invalid.

        check your input.
        EOS
    end
end

という風に,インデントを無視して書かなければなりません.
なぜなら,<<-EOS と EOS で囲まれている部分すべてが文字列になるので,インデントも文字列の一部に入ってしまうからです.
しかし,String#undent メソッドを使うと,次のように自然に書けます.

class Notifier
    def warning
        <<-EOS.undent
            This is warning.
        your action is invalid.

                check your input.
        EOS
    end
end

Notifier#warning の結果は前のものと同じです.
やっていることは単純で,

  • 行頭のスペースの中で一番スペース数が少ないものをインデント数と判断して,全行のインデントを削除
  • ただし,空行やスペースのみの行は除く

というものです.
スペースの判断を \s ではなく [ \t] で行なっているのは,\s だと改行文字にもマッチしてしまうからです.
なお,タブとスペースが混在しているとうまく動かないので注意して下さい(無いとは思いますが)

ヒアドキュメントのインデントの問題は前から気になっていたので,良い感じに書けるようになって良かったです.