ruby-changes:9745
From: seki <ko1@a...>
Date: Sun, 4 Jan 2009 00:36:34 +0900 (JST)
Subject: [ruby-changes:9745] Ruby:r21286 (trunk): merged r20850, r17881, r16811, r16763, r16748, r15829, r15794 and r15698 from ruby_1_8.
seki 2009-01-04 00:36:14 +0900 (Sun, 04 Jan 2009) New Revision: 21286 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=21286 Log: merged r20850, r17881, r16811, r16763, r16748, r15829, r15794 and r15698 from ruby_1_8. Modified files: trunk/ChangeLog trunk/lib/erb.rb trunk/test/erb/test_erb.rb Index: ChangeLog =================================================================== --- ChangeLog (revision 21285) +++ ChangeLog (revision 21286) @@ -1,3 +1,10 @@ +Sun Jan 4 00:30:50 2009 Masatoshi SEKI <m_seki@m...> + + * lib/erb.rb: merged r20850, r17881, r16811, r16763, r16748, r15829, + r15794 and r15698 from ruby_1_8. + + * test/erb/test_erb.rb: ditto. + Sat Jan 3 22:24:36 2009 NAKAMURA Usaku <usa@r...> * common.mk, Makefile.in, win32/Makefile.sub (INSNS): move the macro Index: lib/erb.rb =================================================================== --- lib/erb.rb (revision 21285) +++ lib/erb.rb (revision 21286) @@ -258,7 +258,7 @@ # Returns revision information for the erb.rb module. def self.version - "erb.rb [2.0.4 #{ERB::Revision.split[1]}]" + "erb.rb [2.1.0 #{ERB::Revision.split[1]}]" end end @@ -272,11 +272,13 @@ end attr_reader :value alias :to_s :value + + def empty? + @value.empty? + end end class Scanner # :nodoc: - SplitRegexp = /(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>)|(\n)/ - @scanner_map = {} def self.regist_scanner(klass, trim_mode, percent) @scanner_map[[trim_mode, percent]] = klass @@ -301,8 +303,6 @@ end class TrimScanner < Scanner # :nodoc: - TrimSplitRegexp = /(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>\n)|(%>)|(\n)/ - def initialize(src, trim_mode, percent) super @trim_mode = trim_mode @@ -326,9 +326,7 @@ percent_line(line, &block) end else - @src.each_line do |line| - @scan_line.call(line, &block) - end + @scan_line.call(@src, &block) end nil end @@ -347,57 +345,66 @@ end def scan_line(line) - line.split(SplitRegexp).each do |token| - next if token.empty? - yield(token) + line.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>|\n|\z)/m) do |tokens| + tokens.each do |token| + next if token.empty? + yield(token) + end end end def trim_line1(line) - line.split(TrimSplitRegexp).each do |token| - next if token.empty? - if token == "%>\n" - yield('%>') - yield(:cr) - break - end - yield(token) + line.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>\n|%>|\n|\z)/m) do |tokens| + tokens.each do |token| + next if token.empty? + if token == "%>\n" + yield('%>') + yield(:cr) + else + yield(token) + end + end end end def trim_line2(line) head = nil - line.split(TrimSplitRegexp).each do |token| - next if token.empty? - head = token unless head - if token == "%>\n" - yield('%>') - if is_erb_stag?(head) - yield(:cr) - else - yield("\n") - end - break - end - yield(token) + line.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>\n|%>|\n|\z)/m) do |tokens| + tokens.each do |token| + next if token.empty? + head = token unless head + if token == "%>\n" + yield('%>') + if is_erb_stag?(head) + yield(:cr) + else + yield("\n") + end + head = nil + else + yield(token) + head = nil if token == "\n" + end + end end end - ExplicitTrimRegexp = /(^[ \t]*<%-)|(-%>\n?\z)|(<%-)|(-%>)|(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>)|(\n)/ def explicit_trim_line(line) - line.split(ExplicitTrimRegexp).each do |token| - next if token.empty? - if @stag.nil? && /[ \t]*<%-/ =~ token - yield('<%') - elsif @stag && /-%>\n/ =~ token - yield('%>') - yield(:cr) - elsif @stag && token == '-%>' - yield('%>') - else - yield(token) - end - end + line.scan(/(.*?)(^[ \t]*<%\-|<%\-|<%%|%%>|<%=|<%#|<%|-%>\n|-%>|%>|\z)/m) do |tokens| + tokens.each do |token| + next if token.empty? + if @stag.nil? && /[ \t]*<%-/ =~ token + yield('<%') + elsif @stag && token == "-%>\n" + yield('%>') + yield(:cr) + elsif @stag && token == '-%>' + yield('%>') + else + yield(token) + end + end + end end ERB_STAG = %w(<%= <%# <%) @@ -410,11 +417,11 @@ class SimpleScanner < Scanner # :nodoc: def scan - @src.each_line do |line| - line.split(SplitRegexp).each do |token| - next if token.empty? - yield(token) - end + @src.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>|\n|\z)/m) do |tokens| + tokens.each do |token| + next if token.empty? + yield(token) + end end end end @@ -425,75 +432,69 @@ require 'strscan' class SimpleScanner2 < Scanner # :nodoc: def scan - stag_reg = /(.*?)(<%%|<%=|<%#|<%|\n|\z)/ - etag_reg = /(.*?)(%%>|%>|\n|\z)/ + stag_reg = /(.*?)(<%%|<%=|<%#|<%|\z)/m + etag_reg = /(.*?)(%%>|%>|\z)/m scanner = StringScanner.new(@src) while ! scanner.eos? scanner.scan(@stag ? etag_reg : stag_reg) - text = scanner[1] - elem = scanner[2] - yield(text) unless text.empty? - yield(elem) unless elem.empty? + yield(scanner[1]) + yield(scanner[2]) end end end Scanner.regist_scanner(SimpleScanner2, nil, false) class PercentScanner < Scanner # :nodoc: - def scan - new_line = true - stag_reg = /(.*?)(<%%|<%=|<%#|<%|\n|\z)/ - etag_reg = /(.*?)(%%>|%>|\n|\z)/ + def scan(&blk) + stag_reg = /(.*?)(^%%|^%|<%%|<%=|<%#|<%|\z)/m + etag_reg = /(.*?)(%%>|%>|\z)/m scanner = StringScanner.new(@src) while ! scanner.eos? - if new_line && @stag.nil? - if scanner.scan(/%%/) - yield('%') - new_line = false - next - elsif scanner.scan(/%/) - yield(PercentLine.new(scanner.scan(/.*?(\n|\z)/).chomp)) - next - end - end scanner.scan(@stag ? etag_reg : stag_reg) - text = scanner[1] + yield(scanner[1]) + elem = scanner[2] - yield(text) unless text.empty? - yield(elem) unless elem.empty? - new_line = (elem == "\n") + if elem == '%%' + yield('%') + inline_scan(scanner.scan(/.*?(\n|\z)/), &blk) + elsif elem == '%' + yield(PercentLine.new(scanner.scan(/.*?(\n|\z)/).chomp)) + else + yield(elem) + end end end + + def inline_scan(line) + stag_reg = /(.*?)(<%%|<%=|<%#|<%|\z)/m + etag_reg = /(.*?)(%%>|%>|\z)/m + scanner = StringScanner.new(line) + while ! scanner.eos? + scanner.scan(@stag ? etag_reg : stag_reg) + yield(scanner[1]) + yield(scanner[2]) + end + end end Scanner.regist_scanner(PercentScanner, nil, true) class ExplicitScanner < Scanner # :nodoc: def scan - new_line = true - stag_reg = /(.*?)(<%%|<%=|<%#|<%-|<%|\n|\z)/ - etag_reg = /(.*?)(%%>|-%>|%>|\n|\z)/ + stag_reg = /(.*?)(^[ \t]*<%-|<%%|<%=|<%#|<%-|<%|\z)/m + etag_reg = /(.*?)(%%>|-%>|%>|\z)/m scanner = StringScanner.new(@src) while ! scanner.eos? - if new_line && @stag.nil? && scanner.scan(/[ \t]*<%-/) - yield('<%') - new_line = false - next - end scanner.scan(@stag ? etag_reg : stag_reg) - text = scanner[1] + yield(scanner[1]) + elem = scanner[2] - new_line = (elem == "\n") - yield(text) unless text.empty? - if elem == '-%>' + if /[ \t]*<%-/ =~ elem + yield('<%') + elsif elem == '-%>' yield('%>') - if scanner.scan(/(\n|\z)/) - yield(:cr) - new_line = true - end - elsif elem == '<%-' - yield('<%') + yield(:cr) if scanner.scan(/(\n|\z)/) else - yield(elem) unless elem.empty? + yield(elem) end end end @@ -534,6 +535,15 @@ end end + def content_dump(s) + n = s.count("\n") + if n > 0 + s.dump + "\n" * n + else + s.dump + end + end + def compile(s) enc = s.encoding raise ArgumentError, "#{enc} is not ASCII compatible" if enc.dummy? @@ -542,12 +552,14 @@ out = Buffer.new(self, enc) content = '' - scanner = make_scanner(s) + scanner = make_scanner(s) scanner.scan do |token| + next if token.nil? + next if token == '' if scanner.stag.nil? case token when PercentLine - out.push("#{@put_cmd} #{content.dump}") if content.size > 0 + out.push("#{@put_cmd} #{content_dump(content)}") if content.size > 0 content = '' out.push(token.to_s) out.cr @@ -555,12 +567,11 @@ out.cr when '<%', '<%=', '<%#' scanner.stag = token - out.push("#{@put_cmd} #{content.dump}") if content.size > 0 + out.push("#{@put_cmd} #{content_dump(content)}") if content.size > 0 content = '' when "\n" content << "\n" - out.push("#{@put_cmd} #{content.dump}") - out.cr + out.push("#{@put_cmd} #{content_dump(content)}") content = '' when '<%%' content << '<%' @@ -582,8 +593,7 @@ when '<%=' out.push("#{@insert_cmd}((#{content}).to_s)") when '<%#' - # content = content.force_encoding(@enc) - # out.push("# #{content.dump}") + # out.push("# #{content_dump(content)}") end scanner.stag = nil content = '' @@ -594,7 +604,7 @@ end end end - out.push("#{@put_cmd} #{content.dump}") if content.size > 0 + out.push("#{@put_cmd} #{content_dump(content)}") if content.size > 0 out.close return out.script, enc end @@ -769,17 +779,23 @@ # def result(b=TOPLEVEL_BINDING) if @safe_level - th = Thread.start { + proc { $SAFE = @safe_level eval(@src, b, (@filename || '(erb)'), 0) - } - return th.value + }.call else - return eval(@src, b, (@filename || '(erb)'), 0) + eval(@src, b, (@filename || '(erb)'), 0) end end - def def_method(mod, methodname, fname='(ERB)') # :nodoc: + # Define _methodname_ as instance method of _mod_ from compiled ruby source. + # + # example: + # filename = 'example.rhtml' # 'arg1' and 'arg2' are used in example.rhtml + # erb = ERB.new(File.read(filename)) + # erb.def_method(MyClass, 'render(arg1, arg2)', filename) + # print MyClass.new.render('foo', 123) + def def_method(mod, methodname, fname='(ERB)') src = self.src magic_comment = "#coding:#{@enc}\n" mod.module_eval do @@ -787,15 +803,38 @@ end end - def def_module(methodname='erb') # :nodoc: + # Create unnamed module, define _methodname_ as instance method of it, and return it. + # + # example: + # filename = 'example.rhtml' # 'arg1' and 'arg2' are used in example.rhtml + # erb = ERB.new(File.read(filename)) + # erb.filename = filename + # MyModule = erb.def_module('render(arg1, arg2)') + # class MyClass + # include MyModule + # end + def def_module(methodname='erb') mod = Module.new - def_method(mod, methodname) + def_method(mod, methodname, @filename || '(ERB)') mod end - def def_class(superklass=Object, methodname='result') # :nodoc: + # Define unnamed class which has _methodname_ as instance method, and return it. + # + # example: + # class MyClass_ + # def initialize(arg1, arg2) + # @arg1 = arg1; @arg2 = arg2 + # end + # end + # filename = 'example.rhtml' # @arg1 and @arg2 are used in example.rhtml + # erb = ERB.new(File.read(filename)) + # erb.filename = filename + # MyClass = erb.def_class(MyClass_, 'render()') + # print MyClass.new('foo', 123).render() + def def_class(superklass=Object, methodname='result') cls = Class.new(superklass) - def_method(cls, methodname) + def_method(cls, methodname, @filename || '(ERB)') cls end end @@ -851,15 +890,45 @@ #-- # ERB::DefMethod class ERB - module DefMethod # :nodoc: + # Utility module to define eRuby script as instance method. + # + # === Example + # + # example.rhtml: + # <% for item in @items %> + # <b><%= item %></b> + # <% end %> + # + # example.rb: + # require 'erb' + # class MyClass + # extend ERB::DefMethod + # def_erb_method('render()', 'example.rhtml') + # def initialize(items) + # @items = items + # end + # end + # print MyClass.new([10,20,30]).render() + # + # result: + # + # <b>10</b> + # + # <b>20</b> + # + # <b>30</b> + # + module DefMethod public - def def_erb_method(methodname, erb) - if erb.kind_of? String - fname = erb - File.open(fname) {|f| erb = ERB.new(f.read) } - erb.def_method(self, methodname, fname) + # define _methodname_ as instance method of current module, using ERB object or eRuby file + def def_erb_method(methodname, erb_or_fname) + if erb_or_fname.kind_of? String + fname = erb_or_fname + erb = ERB.new(File.read(fname)) + erb.def_method(self, methodname, fname) else - erb.def_method(self, methodname) + erb = erb_or_fname + erb.def_method(self, methodname, erb.filename || '(ERB)') end end module_function :def_erb_method Index: test/erb/test_erb.rb =================================================================== --- test/erb/test_erb.rb (revision 21285) +++ test/erb/test_erb.rb (revision 21286) @@ -209,55 +209,54 @@ n.times do |i|%> %% %%><%%<%= i%><% end%> +%%% EOS ans = <<EOS % % %%><%0 % %%><%1 +%% EOS assert_equal(ans, ERB.new(src, nil, '%').result) end - class Bar; end - def test_def_erb_method - assert(! Bar.new.respond_to?('hello')) - Bar.module_eval do + klass = Class.new + klass.module_eval do extend ERB::DefMethod fname = File.join(File.dirname(File.expand_path(__FILE__)), 'hello.erb') def_erb_method('hello', fname) end - assert(Bar.new.respond_to?('hello')) + assert(klass.new.respond_to?('hello')) - assert(! Bar.new.respond_to?('hello_world')) + assert(! klass.new.respond_to?('hello_world')) erb = @erb.new('hello, world') - Bar.module_eval do + klass.module_eval do def_erb_method('hello_world', erb) end - assert(Bar.new.respond_to?('hello_world')) + assert(klass.new.respond_to?('hello_world')) end - class DefMethodWithoutFname; end - class DefMethodWithFname; end - def test_def_method_without_filename + klass = Class.new erb = ERB.new("<% raise ::TestERB::MyError %>") erb.filename = "test filename" - assert(! DefMethodWithoutFname.new.respond_to?('my_error')) - erb.def_method(DefMethodWithoutFname, 'my_error') + assert(! klass.new.respond_to?('my_error')) + erb.def_method(klass, 'my_error') e = assert_raise(::TestERB::MyError) { - DefMethodWithoutFname.new.my_error + klass.new.my_error } assert_match(/\A\(ERB\):1\b/, e.backtrace[0]) end def test_def_method_with_fname + klass = Class.new erb = ERB.new("<% raise ::TestERB::MyError %>") erb.filename = "test filename" - assert(! DefMethodWithFname.new.respond_to?('my_error')) - erb.def_method(DefMethodWithFname, 'my_error', 'test fname') + assert(! klass.new.respond_to?('my_error')) + erb.def_method(klass, 'my_error', 'test fname') e = assert_raise(::TestERB::MyError) { - DefMethodWithFname.new.my_error + klass.new.my_error } assert_match(/\Atest fname:1\b/, e.backtrace[0]) end @@ -444,3 +443,16 @@ ERB::Util.url_encode("\xA5\xB5\xA5\xF3\xA5\xD7\xA5\xEB".force_encoding("EUC-JP"))) end end + +class TestERBCoreWOStrScan < TestERBCore + def setup + @save_map = ERB::Compiler::Scanner.instance_variable_get('@scanner_map') + map = {[nil, false]=>ERB::Compiler::SimpleScanner} + ERB::Compiler::Scanner.instance_variable_set('@scanner_map', map) + super + end + + def teardown + ERB::Compiler::Scanner.instance_variable_set('@scanner_map', @save_map) + end +end -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/