From 0d4301d4d00667a66b5bfe0a5d545b781a542f66 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Fri, 1 Jan 2016 14:07:32 +0900 Subject: [PATCH] Allow ERB subclass to add token easily --- lib/erb.rb | 131 ++++++++++++++++++++++++++++++--------------------- test/erb/test_erb.rb | 53 +++++++++++++++++++++ 2 files changed, 130 insertions(+), 54 deletions(-) diff --git a/lib/erb.rb b/lib/erb.rb index 73fc574..5223a28 100644 --- a/lib/erb.rb +++ b/lib/erb.rb @@ -371,8 +371,11 @@ def self.make_scanner(src, trim_mode, percent) def initialize(src, trim_mode, percent) @src = src @stag = nil + @stags = %w(<%% <%= <%# <%).freeze + @etags = %w(%%> %>).freeze end attr_accessor :stag + attr_reader :stags, :etags def scan; end end @@ -383,12 +386,16 @@ def initialize(src, trim_mode, percent) @trim_mode = trim_mode @percent = percent if @trim_mode == '>' + @scan_reg = /(.*?)(%>\n|#{(stags + etags).join('|')}|\n|\z)/m @scan_line = self.method(:trim_line1) elsif @trim_mode == '<>' + @scan_reg = /(.*?)(%>\n|#{(stags + etags).join('|')}|\n|\z)/m @scan_line = self.method(:trim_line2) elsif @trim_mode == '-' + @scan_reg = /(.*?)(^[ \t]*<%\-|<%\-|-%>\n|-%>|#{(stags + etags).join('|')}|\z)/m @scan_line = self.method(:explicit_trim_line) else + @scan_reg = /(.*?)(#{(stags + etags).join('|')}|\n|\z)/m @scan_line = self.method(:scan_line) end end @@ -420,7 +427,7 @@ def percent_line(line, &block) end def scan_line(line) - line.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>|\n|\z)/m) do |tokens| + line.scan(@scan_reg) do |tokens| tokens.each do |token| next if token.empty? yield(token) @@ -429,7 +436,7 @@ def scan_line(line) end def trim_line1(line) - line.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>\n|%>|\n|\z)/m) do |tokens| + line.scan(@scan_reg) do |tokens| tokens.each do |token| next if token.empty? if token == "%>\n" @@ -444,7 +451,7 @@ def trim_line1(line) def trim_line2(line) head = nil - line.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>\n|%>|\n|\z)/m) do |tokens| + line.scan(@scan_reg) do |tokens| tokens.each do |token| next if token.empty? head = token unless head @@ -465,7 +472,7 @@ def trim_line2(line) end def explicit_trim_line(line) - line.scan(/(.*?)(^[ \t]*<%\-|<%\-|<%%|%%>|<%=|<%#|<%|-%>\n|-%>|%>|\z)/m) do |tokens| + line.scan(@scan_reg) do |tokens| tokens.each do |token| next if token.empty? if @stag.nil? && /[ \t]*<%-/ =~ token @@ -492,7 +499,7 @@ def is_erb_stag?(s) class SimpleScanner < Scanner # :nodoc: def scan - @src.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>|\n|\z)/m) do |tokens| + @src.scan(/(.*?)(#{(stags + etags).join('|')}|\n|\z)/m) do |tokens| tokens.each do |token| next if token.empty? yield(token) @@ -507,8 +514,8 @@ def scan require 'strscan' class SimpleScanner2 < Scanner # :nodoc: def scan - stag_reg = /(.*?)(<%[%=#]?|\z)/m - etag_reg = /(.*?)(%%?>|\z)/m + stag_reg = /(.*?)(#{stags.join('|')}|\z)/m + etag_reg = /(.*?)(#{etags.join('|')}|\z)/m scanner = StringScanner.new(@src) while ! scanner.eos? scanner.scan(@stag ? etag_reg : stag_reg) @@ -521,8 +528,8 @@ def scan class ExplicitScanner < Scanner # :nodoc: def scan - stag_reg = /(.*?)(^[ \t]*<%-|<%%|<%=|<%#|<%-|<%|\z)/m - etag_reg = /(.*?)(%%>|-%>|%>|\z)/m + stag_reg = /(.*?)(^[ \t]*<%-|<%-|#{stags.join('|')}|\z)/m + etag_reg = /(.*?)(-%>|#{etags.join('|')}|\z)/m scanner = StringScanner.new(@src) while ! scanner.eos? scanner.scan(@stag ? etag_reg : stag_reg) @@ -602,57 +609,15 @@ def compile(s) enc = detect_magic_comment(s) || enc out = Buffer.new(self, enc) - content = '' + self.content = '' scanner = make_scanner(s) scanner.scan do |token| next if token.nil? next if token == '' if scanner.stag.nil? - case token - when PercentLine - add_put_cmd(out, content) if content.size > 0 - content = '' - out.push(token.to_s) - out.cr - when :cr - out.cr - when '<%', '<%=', '<%#' - scanner.stag = token - add_put_cmd(out, content) if content.size > 0 - content = '' - when "\n" - content << "\n" - add_put_cmd(out, content) - content = '' - when '<%%' - content << '<%' - else - content << token - end + compile_stag(token, out, scanner) else - case token - when '%>' - case scanner.stag - when '<%' - if content[-1] == ?\n - content.chop! - out.push(content) - out.cr - else - out.push(content) - end - when '<%=' - add_insert_cmd(out, content) - when '<%#' - # out.push("# #{content_dump(content)}") - end - scanner.stag = nil - content = '' - when '%%>' - content << '%>' - else - content << token - end + compile_etag(token, out, scanner) end end add_put_cmd(out, content) if content.size > 0 @@ -660,6 +625,60 @@ def compile(s) return out.script, enc end + def compile_stag(stag, out, scanner) + case stag + when PercentLine + add_put_cmd(out, content) if content.size > 0 + self.content = '' + out.push(stag.to_s) + out.cr + when :cr + out.cr + when '<%', '<%=', '<%#' + scanner.stag = stag + add_put_cmd(out, content) if content.size > 0 + self.content = '' + when "\n" + content << "\n" + add_put_cmd(out, content) + self.content = '' + when '<%%' + content << '<%' + else + content << stag + end + end + + def compile_etag(etag, out, scanner) + case etag + when '%>' + compile_content(scanner.stag, out) + scanner.stag = nil + self.content = '' + when '%%>' + content << '%>' + else + content << etag + end + end + + def compile_content(stag, out) + case stag + when '<%' + if content[-1] == ?\n + content.chop! + out.push(content) + out.cr + else + out.push(content) + end + when '<%=' + add_insert_cmd(out, content) + when '<%#' + # out.push("# #{content_dump(content)}") + end + end + def prepare_trim_mode(mode) # :nodoc: case mode when 1 @@ -712,6 +731,10 @@ def initialize(trim_mode) attr_accessor :post_cmd private + + # A buffered text in #compile + attr_accessor :content + def detect_magic_comment(s) if /\A<%#(.*)%>/ =~ s or (@percent and /\A%#(.*)/ =~ s) comment = $1 diff --git a/test/erb/test_erb.rb b/test/erb/test_erb.rb index 4e2d49a..ee3333f 100644 --- a/test/erb/test_erb.rb +++ b/test/erb/test_erb.rb @@ -481,6 +481,59 @@ def test_url_encode def test_percent_after_etag assert_equal("1%", @erb.new("<%= 1 %>%", nil, "%").result) end + + def test_token_extension + extended_erb = Class.new(ERB) + extended_erb.module_eval do + def make_compiler(trim_mode) + compiler = Class.new(ERB::Compiler) + compiler.module_eval do + def compile_stag(stag, out, scanner) + case stag + when '<%==' + scanner.stag = stag + add_put_cmd(out, content) if content.size > 0 + self.content = '' + else + super + end + end + + def compile_content(stag, out) + case stag + when '<%==' + out.push("#{@insert_cmd}(::ERB::Util.html_escape(#{content}))") + else + super + end + end + + def make_scanner(src) + scanner = Class.new(ERB::Compiler::SimpleScanner) + scanner.module_eval do + def stags + ['<%=='] + super + end + end + scanner.new(src, @trim_mode, @percent) + end + end + compiler.new(trim_mode) + end + end + + src = <<~EOS + <% tag = '<>' %> + <%= tag %> + <%== tag %> + EOS + ans = <<~EOS + + <> + <> + EOS + assert_equal(ans, extended_erb.new(src).result) + end end class TestERBCoreWOStrScan < TestERBCore -- 2.6.4