diff --git a/lib/haml/compiler.rb b/lib/haml/compiler.rb index 14fa2a58..e93b56a7 100755 --- a/lib/haml/compiler.rb +++ b/lib/haml/compiler.rb @@ -1,10 +1,51 @@ require 'cgi' module Haml - module Compiler + class Compiler include Haml::Util - private + attr_accessor :options + + def initialize(options) + @options = options + @output_tabs = 0 + @to_merge = [] + @precompiled = '' + end + + def compile(node) + parent = instance_variable_defined?('@node') ? @node : nil + @node = node + if node.children.empty? + send(:"compile_#{node.type}") + else + send(:"compile_#{node.type}") {node.children.each {|c| compile c}} + end + ensure + @node = parent + end + + if RUBY_VERSION < "1.9" + # The source code that is evaluated to produce the Haml document. + # + # In Ruby 1.9, this is automatically converted to the correct encoding + # (see {file:REFERENCE.md#encodings the `:encoding` option}). + # + # @return [String] + def precompiled + @precompiled + end + else + def precompiled + encoding = Encoding.find(@options[:encoding]) + return @precompiled.force_encoding(encoding) if encoding == Encoding::BINARY + return @precompiled.encode(encoding) + end + end + + def precompiled_with_return_value + precompiled + ";" + precompiled_method_return_value + end # Returns the precompiled string with the preamble and postamble def precompiled_with_ambles(local_names) @@ -23,6 +64,8 @@ END preamble + locals_code(local_names) + precompiled + postamble end + private + # Returns the string used as the return value of the precompiled method. # This method exists so it can be monkeypatched to return modified values. def precompiled_method_return_value @@ -260,7 +303,7 @@ END # does not output the result. def push_silent(text, can_suppress = false) flush_merged_text - return if can_suppress && options[:suppress_eval] + return if can_suppress && @options.suppress_eval? @precompiled << "#{resolve_newlines}#{text}\n" @output_line += text.count("\n") + 1 end @@ -321,7 +364,7 @@ END # If `opts[:preserve_script]` is true, Haml::Helpers#find_and_flatten is run on # the result before it is added to `@buffer` def push_script(text, opts = {}) - return if options[:suppress_eval] + return if @options.suppress_eval? args = %w[preserve_script in_tag preserve_tag escape_html nuke_inner_whitespace] args.map! {|name| opts[name.to_sym]} @@ -479,17 +522,5 @@ END raise SyntaxError.new("[HAML BUG] Undefined entry in Haml::Compiler@to_merge.") end end - - def compile(node) - parent = instance_variable_defined?('@node') ? @node : nil - @node = node - if node.children.empty? - send(:"compile_#{node.type}") - else - send(:"compile_#{node.type}") {node.children.each {|c| compile c}} - end - ensure - @node = parent - end end end diff --git a/lib/haml/engine.rb b/lib/haml/engine.rb index 1b9c7a77..e68e0a15 100644 --- a/lib/haml/engine.rb +++ b/lib/haml/engine.rb @@ -17,7 +17,7 @@ module Haml # output = haml_engine.render # puts output class Engine - include Compiler + include Haml::Util # The Haml::Options instance. # See {file:REFERENCE.md#options the Haml options documentation}. @@ -32,23 +32,8 @@ module Haml # @return [String] attr_accessor :indentation - if RUBY_VERSION < "1.9" - # The source code that is evaluated to produce the Haml document. - # - # In Ruby 1.9, this is automatically converted to the correct encoding - # (see {file:REFERENCE.md#encodings the `:encoding` option}). - # - # @return [String] - def precompiled - @precompiled - end - else - def precompiled - encoding = Encoding.find(@options[:encoding]) - return @precompiled.force_encoding(encoding) if encoding == Encoding::BINARY - return @precompiled.encode(encoding) - end - end + attr_accessor :compiler + attr_accessor :parser # Precompiles the Haml template. @@ -58,7 +43,6 @@ module Haml # see {file:REFERENCE.md#options the Haml options documentation} # @raise [Haml::Error] if there's a Haml syntax error in the template def initialize(template, options = {}) - @index = nil # explicitily initialize to avoid warnings @options = Options.new(options) @template = check_haml_encoding(template) do |msg, line| @@ -67,20 +51,10 @@ module Haml initialize_encoding options[:encoding] - @index = 0 + @parser = Parser.new(@template, @options) + @compiler = Compiler.new(@options) - @parser = Parser.new(@template, @options) - - @output_tabs = 0 - @to_merge = [] - @precompiled = '' - - compile(@parser.parse) - rescue Haml::Error => e - if @index || e.line - e.backtrace.unshift "#{@options[:filename]}:#{(e.line ? e.line + 1 : @index) + @options[:line] - 1}" - end - raise + @compiler.compile(@parser.parse) end # Processes the template and returns the result as a string. @@ -143,8 +117,7 @@ module Haml @haml_buffer = buffer end - eval(precompiled + ";" + precompiled_method_return_value, - scope, @options[:filename], @options[:line]) + eval(@compiler.precompiled_with_return_value, scope, @options[:filename], @options[:line]) ensure # Get rid of the current buffer scope_object.instance_eval do @@ -186,7 +159,7 @@ module Haml end eval("Proc.new { |*_haml_locals| _haml_locals = _haml_locals[0] || {};" + - precompiled_with_ambles(local_names) + "}\n", scope, @options[:filename], @options[:line]) + compiler.precompiled_with_ambles(local_names) + "}\n", scope, @options[:filename], @options[:line]) end # Defines a method on `object` with the given name @@ -230,7 +203,7 @@ module Haml def def_method(object, name, *local_names) method = object.is_a?(Module) ? :module_eval : :instance_eval - object.send(method, "def #{name}(_haml_locals = {}); #{precompiled_with_ambles(local_names)}; end", + object.send(method, "def #{name}(_haml_locals = {}); #{compiler.precompiled_with_ambles(local_names)}; end", @options[:filename], @options[:line]) end diff --git a/lib/haml/parser.rb b/lib/haml/parser.rb index 73c84c52..d67950cb 100644 --- a/lib/haml/parser.rb +++ b/lib/haml/parser.rb @@ -80,6 +80,7 @@ module Haml @template = (template.rstrip).split(/\r\n|\r|\n/) + [:eod, :eod] @options = options @flat = false + @index = 0 @template_index = 0 @template_tabs = 0 end @@ -120,6 +121,9 @@ module Haml # Close all the open tags close until @parent.type == :root @root + rescue Haml::Error => e + e.backtrace.unshift "#{@options[:filename]}:#{(e.line ? e.line + 1 : @index) + @options[:line] - 1}" + raise end diff --git a/lib/haml/template.rb b/lib/haml/template.rb index dc5fd794..abeb957e 100644 --- a/lib/haml/template.rb +++ b/lib/haml/template.rb @@ -5,7 +5,7 @@ require 'haml/helpers/action_view_extensions' require 'haml/helpers/xss_mods' module Haml - module Compiler + class Compiler def precompiled_method_return_value_with_haml_xss "::Haml::Util.html_safe(#{precompiled_method_return_value_without_haml_xss})" end diff --git a/lib/haml/template/plugin.rb b/lib/haml/template/plugin.rb index e7b39844..4cf5f07a 100644 --- a/lib/haml/template/plugin.rb +++ b/lib/haml/template/plugin.rb @@ -18,7 +18,7 @@ module Haml options = Haml::Template.options.dup options[:mime_type] = template.mime_type if template.respond_to? :mime_type options[:filename] = template.identifier - Haml::Engine.new(template.source, options).send(:precompiled_with_ambles, []) + Haml::Engine.new(template.source, options).compiler.precompiled_with_ambles([]) end # In Rails 3.1+, #call takes the place of #compile diff --git a/test/filters_test.rb b/test/filters_test.rb index 50aabfa8..450734a3 100644 --- a/test/filters_test.rb +++ b/test/filters_test.rb @@ -41,9 +41,9 @@ class FiltersTest < MiniTest::Unit::TestCase test "should pass options to Tilt filters that precompile" do haml = ":erb\n <%= 'foo' %>" - refute_match('TEST_VAR', Haml::Engine.new(haml).precompiled) + refute_match('TEST_VAR', Haml::Engine.new(haml).compiler.precompiled) Haml::Filters::Erb.options = {:outvar => 'TEST_VAR'} - assert_match('TEST_VAR', Haml::Engine.new(haml).precompiled) + assert_match('TEST_VAR', Haml::Engine.new(haml).compiler.precompiled) end test "should pass options to Tilt filters that don't precompile" do