Skip to content

Instantly share code, notes, and snippets.

@labocho
Last active March 8, 2023 04:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save labocho/4e106cbd04423f7f4e71821ec4a8eb9c to your computer and use it in GitHub Desktop.
Save labocho/4e106cbd04423f7f4e71821ec4a8eb9c to your computer and use it in GitHub Desktop.
Plain text template helper for Rails
# メール文面向けにプレーンテキストを生成する
#
# t = TextBuilder.new
#
# t << "Variables"
# t.indent {
# t.map(["foo", "bar", "baz"]) {|v|
# t << v
# }
# }
#
# makes
#
# Variables
#
# foo
#
# bar
#
# baz
#
class TextBuilder
attr_reader :delimiter, :indent_string
def initialize(delimiter: "\n", indent_string: " ")
@lines = []
@delimiter = delimiter
@indent_string = indent_string
@depth = 0
end
def to_s
@lines.join(delimiter)
end
def indent
if block_given?
@depth += 1
yield
@depth -= 1
else
@depth += 1
end
end
# 空行を追加
def br
self << ""
end
def hr
self << "------------------------------------------"
end
def outdent!
@depth -= 1
end
# line を出力に追加する
# line は delimiter で区切られた複数行の文字列でもよい
# 出力時は各 line のあとに自動で delimiter がつく
# indent が 0 でなければ indent する
def add(line, indented: false)
lines = line.to_s.split(delimiter)
lines << "" if lines.empty?
# 最初の行のインデントを元にインデントを解除する
if indented
lines.first =~ /\A( *)/
depth = ::Regexp.last_match(1).size
pattern = Regexp.compile("^ {,#{depth}}")
lines.each {|l| l.gsub!(pattern, "") }
end
@lines << lines.map {|s| ((indent_string * @depth) + s).gsub(/^ +$/, "") }.join(delimiter)
end
alias_method :<<, :add
# collection の要素ごとに TextBuilder を渡して block を実行し、TextBuilder の内容を self に追加する。
# collection が空でなく、insert_before が truthy なら insert_before のみの行を最初に追加する。
# insert_after_item が truthy なら要素ごとの出力のあとに insert_after_item のみの行を追加する
# TextBuilder.new.map(["foo", "bar"]){|t, e| t << e } #=> "\nfoo\n\nbar\n\n"
# TextBuilder.new.map(["foo", "bar"], insert_before: false){|t, e| t << e } #=> "foo\n\nbar\n\n"
# TextBuilder.new.map(["foo", "bar"], insert_after_item: false){|t, e| t << e } #=> "\nfoo\nbar\n"
# TextBuilder.new.map([]){|t, e| t << e } #=> ""
def map(collection, insert_before: nil, insert_after_item: nil)
return if collection.empty?
self << insert_before if insert_before
t = TextBuilder.new(delimiter:, indent_string:)
collection.each do |e|
yield t, e
t << insert_after_item if insert_after_item
end
self << t
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment