From 212b1ef06f0859e34ebfc209e74b32accefce52c Mon Sep 17 00:00:00 2001 From: Stefan Kaes Date: Wed, 16 Jul 2008 13:26:51 +0200 Subject: [PATCH] fixed broken template recompilation logic; [#630 state:resolved] --- actionpack/lib/action_view/renderable.rb | 48 +++++++++++--------- .../test/template/compiled_templates_test.rb | 35 ++++++++++++++ 2 files changed, 62 insertions(+), 21 deletions(-) create mode 100644 actionpack/test/template/compiled_templates_test.rb diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb index badb2b1..d06d435 100644 --- a/actionpack/lib/action_view/renderable.rb +++ b/actionpack/lib/action_view/renderable.rb @@ -35,35 +35,42 @@ module ActionView end private - # Compile and evaluate the template's code + # Compile and evaluate the template's code (if necessary) def compile(local_assigns) render_symbol = method(local_assigns) @@mutex.synchronize do return false unless recompile?(render_symbol) + compile!(render_symbol, local_assigns) + end + end - locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join + def compile!(render_symbol, local_assigns) + locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join - source = <<-end_src - def #{render_symbol}(local_assigns) - old_output_buffer = output_buffer;#{locals_code};#{compiled_source} - ensure - self.output_buffer = old_output_buffer - end - end_src + source = <<-end_src + def #{render_symbol}(local_assigns) + old_output_buffer = output_buffer;#{locals_code};#{compiled_source} + ensure + self.output_buffer = old_output_buffer + end + end_src - begin - file_name = respond_to?(:filename) ? filename : 'compiled-template' - ActionView::Base::CompiledTemplates.module_eval(source, file_name, 0) - rescue Exception => e # errors from template code - if logger = ActionController::Base.logger - logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}" - logger.debug "Function body: #{source}" - logger.debug "Backtrace: #{e.backtrace.join("\n")}" - end + begin + logger = ActionController::Base.logger + logger.debug "Compiling template #{render_symbol}" if logger - raise ActionView::TemplateError.new(self, {}, e) + file_name = respond_to?(:filename) ? filename : 'compiled-template' + ActionView::Base::CompiledTemplates.module_eval(source, file_name, 0) + + rescue Exception => e # errors from template code + if logger + logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}" + logger.debug "Function body: #{source}" + logger.debug "Backtrace: #{e.backtrace.join("\n")}" end + + raise ActionView::TemplateError.new(self, {}, e) end end @@ -71,8 +78,7 @@ module ActionView # The template will be compiled if the file has not been compiled yet, or # if local_assigns has a new key, which isn't supported by the compiled code yet. def recompile?(symbol) - meth = Base::CompiledTemplates.instance_method(template.method) rescue nil - !(meth && frozen?) + !(Base::CompiledTemplates.method_defined?(symbol) && frozen?) end end end diff --git a/actionpack/test/template/compiled_templates_test.rb b/actionpack/test/template/compiled_templates_test.rb new file mode 100644 index 0000000..03c67d1 --- /dev/null +++ b/actionpack/test/template/compiled_templates_test.rb @@ -0,0 +1,35 @@ +require 'abstract_unit' +require 'controller/fake_models' + +class CompiledTemplatesTest < Test::Unit::TestCase + def setup + @view = ActionView::Base.new(ActionController::Base.view_paths, {}) + @compiled_templates = ActionView::Base::CompiledTemplates + @compiled_templates.instance_methods.each do |m| + @compiled_templates.send(:remove_method, m) if m =~ /^_run_/ + end + end + + uses_mocha('TestTemplateRecompilation') do + def test_template_gets_compiled + assert_equal 0, @compiled_templates.instance_methods.size + assert_equal "Hello world!", @view.render("test/hello_world.erb") + assert_equal 1, @compiled_templates.instance_methods.size + end + + def test_template_gets_recompiled_when_using_different_keys_in_local_assigns + assert_equal 0, @compiled_templates.instance_methods.size + assert_equal "Hello world!", @view.render("test/hello_world.erb") + assert_equal "Hello world!", @view.render("test/hello_world.erb", {:foo => "bar"}) + assert_equal 2, @compiled_templates.instance_methods.size + end + + def test_compiled_template_will_not_be_recompiled_when_rendered_with_identical_local_assigns + assert_equal 0, @compiled_templates.instance_methods.size + assert_equal "Hello world!", @view.render("test/hello_world.erb") + ActionView::Template.any_instance.expects(:compile!).times(1) + assert_equal "Hello world!", @view.render("test/hello_world.erb") + end + end + +end -- 1.5.4.3