元ネタは 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 だと改行文字にもマッチしてしまうからです.
なお,タブとスペースが混在しているとうまく動かないので注意して下さい(無いとは思いますが)
ヒアドキュメントのインデントの問題は前から気になっていたので,良い感じに書けるようになって良かったです.