From 3e91e604000a9d9d5e90e4d488726d55ea545175 Mon Sep 17 00:00:00 2001 From: nex3 Date: Fri, 23 Nov 2007 12:14:02 +0000 Subject: [PATCH] A little more tweaking of Engine#compile. git-svn-id: svn://hamptoncatlin.com/haml/trunk@643 7063305b-7217-0410-af8c-cdc13e5119b9 --- lib/haml/engine.rb | 35 ++++++++++++++++++++++++++++------- test/haml/engine_test.rb | 8 ++++++-- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/lib/haml/engine.rb b/lib/haml/engine.rb index 4b464bc0..ea39c17a 100644 --- a/lib/haml/engine.rb +++ b/lib/haml/engine.rb @@ -72,6 +72,26 @@ module Haml end # Processes the template and returns the result as a string. + # + # +scope+ is the context in which the template is evaluated. + # If it's a Binding or Proc object, + # Haml uses it as the second argument to Kernel#eval; + # otherwise, Haml just uses its #instance_eval context. + # Note that Haml modifies the context, + # extending it with Haml::Helpers + # and performing various other modifications. + # + # If a block is passed to render, + # that block is run when +yield+ is called + # within the template. + # + # Note that due to some Ruby quirks, + # if scope is a Binding or Proc object and a block is given, + # the evaluation context may not be quite what the user expects. + # In particular, it's equivalent to passing eval("self", scope) as scope. + # This won't have an effect in most cases, + # but if you're relying on local variables defined in the context of scope, + # they won't work. def render(scope = Object.new, &block) @buffer = Haml::Buffer.new(@options) compile scope, &block @@ -86,13 +106,18 @@ module Haml # The code in @precompiled populates # @buffer with the compiled XHTML code. def compile(scope, &block) - if Binding === scope + if scope.is_a?(Binding) || scope.is_a?(Proc) scope_object = eval("self", scope) + scope = scope_object.instance_eval{binding} if block_given? else scope_object = scope - scope = scope.instance_eval{binding} + scope = scope_object.instance_eval{binding} end + scope_object.send(:instance_variable_set, '@_haml_locals', @options[:locals]) + set_locals = @options[:locals].keys.map { |k| "#{k} = @_haml_locals[#{k.inspect}]" }.join("\n") + eval(set_locals, scope) + scope_object.extend Haml::Helpers buffer = @buffer scope_object.instance_eval do @@ -100,12 +125,8 @@ module Haml @haml_stack.push(buffer) end - scope_object.send(:instance_variable_set, '@_haml_locals', @options[:locals]) - set_locals = @options[:locals].keys.map { |k| "#{k} = @_haml_locals[#{k.inspect}]" }.join("\n") - eval(set_locals, scope) - begin - eval(@precompiled, scope, '(haml-eval)', &block) + eval(@precompiled, scope, '(haml-eval)') rescue Exception => e raise add_exception_info(e, scope_object) end diff --git a/test/haml/engine_test.rb b/test/haml/engine_test.rb index b790c766..72c09ad7 100644 --- a/test/haml/engine_test.rb +++ b/test/haml/engine_test.rb @@ -11,8 +11,8 @@ require 'haml/engine' class EngineTest < Test::Unit::TestCase - def render(text, options = {}) - Haml::Engine.new(text, options).to_html(options.delete(:scope) || Object.new) + def render(text, options = {}, &block) + Haml::Engine.new(text, options).to_html(options.delete(:scope) || Object.new, &block) end def test_empty_render_should_remain_empty @@ -310,4 +310,8 @@ class EngineTest < Test::Unit::TestCase assert_equal("

THIS IS A STRING!

\n

Instance variable

\n

Local variable

\n", render("%p= upcase\n%p= @var\n%p= var", :scope => b)) end + + def test_yield_should_work_with_binding + assert_equal("12\nFOO\n", render("= yield\n= upcase", :scope => "foo".instance_eval{binding}) { 12 }) + end end