Skip to content

Commit

Permalink
Local variables assigned as actual local variables, rather than metho…
Browse files Browse the repository at this point in the history
…ds. Based on a patch by Tom Bagby.

git-svn-id: svn://hamptoncatlin.com/haml/trunk@511 7063305b-7217-0410-af8c-cdc13e5119b9
  • Loading branch information
nex3 committed May 10, 2007
1 parent 3a6f6d0 commit 98b3131
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 18 deletions.
36 changes: 21 additions & 15 deletions lib/haml/engine.rb
Expand Up @@ -118,7 +118,7 @@ class Engine
# to README!
#++
#
def initialize(template, options = {})
def initialize(template, l_options = {})
@options = {
:suppress_eval => false,
:attr_wrapper => "'",
Expand All @@ -141,15 +141,15 @@ def initialize(template, options = {})
@options[:filters]['markdown'] = Haml::Filters::Markdown
end

@options.rec_merge! options
@options.rec_merge! l_options

unless @options[:suppress_eval]
@options[:filters].merge!({
'erb' => ERB,
'ruby' => Haml::Filters::Ruby
})
end
@options[:filters].rec_merge! options[:filters] if options[:filters]
@options[:filters].rec_merge! l_options[:filters] if l_options[:filters]

@template = template.strip #String
@to_close_stack = []
Expand All @@ -164,7 +164,13 @@ def initialize(template, options = {})
begin
# Only do the first round of pre-compiling if we really need to.
# They might be passing in the precompiled string.
do_precompile unless @@method_names[@template]
requires_precompile = true
if @@method_names[@template]
# Check that the compiled method supports a superset of the local assigns we want to do
supported_assigns = @@supported_local_assigns[@template]
requires_precompile = !@options[:locals].keys.all? {|var| supported_assigns.include? var}
end
do_precompile if requires_precompile
rescue Haml::Error => e
e.add_backtrace_entry(@index, @options[:filename])
raise e
Expand All @@ -176,10 +182,6 @@ def render(scope = Object.new, &block)
@scope_object = scope
@buffer = Haml::Buffer.new(@options)

local_mod = Module.new()
@options[:locals].each { |key, val| local_mod.send(:define_method, key) { val } }
@scope_object.extend(local_mod)

# Compile the @precompiled buffer
compile &block

Expand All @@ -196,12 +198,19 @@ def do_precompile
@precompiled = ''
method_name = assign_method_name(@template, options[:filename])
push_silent <<-END
def #{method_name}
def #{method_name}(_haml_local_assigns)
@haml_is_haml = true
_hamlout = @haml_stack[-1]
_erbout = _hamlout.buffer
END

supported_local_assigns = {}
@@supported_local_assigns[@template] = supported_local_assigns
@options[:locals].each do |k,v|
supported_local_assigns[k] = true
push_silent "#{k} = _haml_local_assigns[:#{k}]"
end

old_line = nil
old_index = nil
old_spaces = nil
Expand Down Expand Up @@ -375,6 +384,7 @@ def is_multiline?(line) # ' '[0] == 32
# checking compile times to decide whether to recompile a template belongs in here or
# out in template.rb
@@method_names = {}
@@supported_local_assigns = {}
@@render_method_count = 0
def assign_method_name(template, file_name)
@@render_method_count += 1
Expand Down Expand Up @@ -406,16 +416,12 @@ class << self
end

begin
if false#options[:filename]
method_name = @@method_names[options[:filename]]
else
method_name = @@method_names[@template]
end
method_name = @@method_names[@template]

unless @scope_object.respond_to?(method_name)
CompiledTemplates.module_eval @precompiled
end
@scope_object.send(method_name, &block)
@scope_object.send(method_name, options[:locals], &block)
rescue Exception => e
class << e
include Haml::Error
Expand Down
13 changes: 13 additions & 0 deletions test/haml/engine_test.rb
Expand Up @@ -86,6 +86,19 @@ def test_locals
assert_equal("<p>Paragraph!</p>\n", render("%p= text", :locals => { :text => "Paragraph!" }))
end

def test_recompile_with_new_locals
template = "%p= (text == 'first time') ? text : new_text"
assert_equal("<p>first time</p>\n", render(template, :locals => { :text => "first time" }))
assert_equal("<p>second time</p>\n", render(template, :locals => { :text => "recompile", :new_text => "second time" }))

# Make sure the method called will return junk unless recompiled
method_name = Haml::Engine.send(:class_variable_get, '@@method_names')[template]
Haml::Engine::CompiledTemplates.module_eval "def #{method_name}(stuff); @haml_stack[-1].push_text 'NOT RECOMPILED', 0; end"

assert_equal("NOT RECOMPILED\n", render(template, :locals => { :text => "first time" }))
assert_equal("<p>first time</p>\n", render(template, :locals => { :text => "first time", :foo => 'bar' }))
end

def test_comps
assert_equal(-1, "foo" <=> nil)
assert_equal(1, nil <=> "foo")
Expand Down
6 changes: 4 additions & 2 deletions test/haml/helper_test.rb
Expand Up @@ -17,7 +17,8 @@ def render(text, options = {})
if options == :action_view
@base.render :inline => text, :type => :haml
else
Haml::Engine.new(text, options).to_html
scope = options.delete :scope_object
Haml::Engine.new(text, options).to_html(scope ? scope : Object.new)
end
end

Expand Down Expand Up @@ -109,7 +110,8 @@ def test_is_haml

def test_page_class
controller = Struct.new(:controller_name, :action_name).new('troller', 'tion')
result = render("%div{:class => page_class} MyDiv", :locals => { :controller => controller })
scope = Struct.new(:controller).new(controller)
result = render("%div{:class => page_class} MyDiv", :scope_object => scope)
expected = "<div class='troller tion'>MyDiv</div>\n"
assert_equal expected, result
end
Expand Down
6 changes: 5 additions & 1 deletion test/haml/template_test.rb
Expand Up @@ -49,7 +49,11 @@ def assert_renders_correctly(name)
end
end
test.call(@base.render(name))
test.call(@base.render(:file => "partialize", :locals => { :name => name }))

# If eval's suppressed, the partial won't render anyway :p.
unless Haml::Template.options[:suppress_eval]
test.call(@base.render(:file => "partialize", :locals => { :name => name }))
end
end

def test_empty_render_should_remain_empty
Expand Down

0 comments on commit 98b3131

Please sign in to comment.