Changeset 8689
- Timestamp:
- 01/22/08 01:46:34 (9 months ago)
- Files:
-
- trunk/actionpack/lib/action_view.rb (modified) (1 diff)
- trunk/actionpack/lib/action_view/base.rb (modified) (5 diffs)
- trunk/actionpack/lib/action_view/template_handler.rb (modified) (2 diffs)
- trunk/actionpack/lib/action_view/template_handlers/builder.rb (modified) (2 diffs)
- trunk/actionpack/lib/action_view/template_handlers/compilable.rb (added)
- trunk/actionpack/lib/action_view/template_handlers/erb.rb (modified) (1 diff)
- trunk/actionpack/lib/action_view/template_handlers/rjs.rb (modified) (2 diffs)
- trunk/actionpack/test/template/compiled_templates_test.rb (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/actionpack/lib/action_view.rb
r8683 r8689 23 23 24 24 require 'action_view/template_handler' 25 require 'action_view/template_handlers/compilable' 25 26 require 'action_view/template_handlers/builder' 26 27 require 'action_view/template_handlers/erb' trunk/actionpack/lib/action_view/base.rb
r8683 r8689 197 197 include CompiledTemplates 198 198 199 # Maps inline templates to their method names 199 # Maps inline templates to their method names 200 cattr_accessor :method_names 200 201 @@method_names = {} 201 # Map method names to their compile time202 @@compile_time = {}203 202 # Map method names to the names passed in local assigns so far 204 203 @@template_args = {} 205 # Count the number of inline templates206 @@inline_template_count = 0207 204 208 205 # Cache public asset paths … … 366 363 compile_and_render_template(handler, template, file_path, local_assigns) 367 364 else 368 template ||= read_template_file(file_path, template_extension) # Make sure that a lazyily-read template is loaded.365 template ||= handler.read_template_file(file_path, template_extension) # Make sure that a lazyily-read template is loaded. 369 366 handler.render(template, local_assigns) 370 367 end … … 390 387 end 391 388 392 # This method reads a template file.393 def read_template_file(template_path, extension)394 File.read(template_path)395 end396 397 389 # Evaluate the local assigns and pushes them to the view. 398 390 def evaluate_assigns … … 406 398 def assign_variables_from_controller 407 399 @assigns.each { |key, value| instance_variable_set("@#{key}", value) } 408 end409 410 411 # Return true if the given template was compiled for a superset of the keys in local_assigns412 def supports_local_assigns?(render_symbol, local_assigns)413 local_assigns.empty? ||414 ((args = @@template_args[render_symbol]) && local_assigns.all? { |k,_| args.has_key?(k) })415 end416 417 # Method to check whether template compilation is necessary.418 # The template will be compiled if the inline template or file has not been compiled yet,419 # if local_assigns has a new key, which isn't supported by the compiled code yet,420 # or if the file has changed on disk and checking file mods hasn't been disabled.421 def compile_template?(template, file_name, local_assigns)422 method_key = file_name || template423 render_symbol = @@method_names[method_key]424 425 compile_time = @@compile_time[render_symbol]426 if compile_time && supports_local_assigns?(render_symbol, local_assigns)427 if file_name && !@@cache_template_loading428 template_changed_since?(file_name, compile_time)429 end430 else431 true432 end433 end434 435 # Method to handle checking a whether a template has changed since last compile; isolated so that templates436 # not stored on the file system can hook and extend appropriately.437 def template_changed_since?(file_name, compile_time)438 lstat = File.lstat(file_name)439 compile_time < lstat.mtime ||440 (lstat.symlink? && compile_time < File.stat(file_name).mtime)441 end442 443 # Method to create the source code for a given template.444 def create_template_source(handler, template, render_symbol, locals)445 body = handler.compile(template)446 447 @@template_args[render_symbol] ||= {}448 locals_keys = @@template_args[render_symbol].keys | locals449 @@template_args[render_symbol] = locals_keys.inject({}) { |h, k| h[k] = true; h }450 451 locals_code = ""452 locals_keys.each do |key|453 locals_code << "#{key} = local_assigns[:#{key}]\n"454 end455 456 "def #{render_symbol}(local_assigns)\n#{locals_code}#{body}\nend"457 end458 459 def assign_method_name(handler, template, file_name)460 method_key = file_name || template461 @@method_names[method_key] ||= compiled_method_name(handler, template, file_name)462 end463 464 def compiled_method_name(handler, template, file_name)465 ['_run', handler.class.to_s.demodulize.underscore, compiled_method_name_file_path_segment(file_name)].compact.join('_').to_sym466 end467 468 def compiled_method_name_file_path_segment(file_name)469 if file_name470 s = File.expand_path(file_name)471 s.sub!(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}/, '') if defined?(RAILS_ROOT)472 s.gsub!(/([^a-zA-Z0-9_])/) { $1.ord }473 s474 else475 (@@inline_template_count += 1).to_s476 end477 end478 479 # Compile and evaluate the template's code480 def compile_template(handler, template, file_name, local_assigns)481 render_symbol = assign_method_name(handler, template, file_name)482 render_source = create_template_source(handler, template, render_symbol, local_assigns.keys)483 line_offset = @@template_args[render_symbol].size + handler.line_offset484 485 begin486 file_name = 'compiled-template' if file_name.blank?487 CompiledTemplates.module_eval(render_source, file_name, -line_offset)488 rescue Exception => e # errors from template code489 if logger490 logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}"491 logger.debug "Function body: #{render_source}"492 logger.debug "Backtrace: #{e.backtrace.join("\n")}"493 end494 495 raise TemplateError.new(@finder.extract_base_path_from(file_name) ||496 @finder.view_paths.first, file_name || template, @assigns, template, e)497 end498 499 @@compile_time[render_symbol] = Time.now500 # logger.debug "Compiled template #{file_name || template}\n ==> #{render_symbol}" if logger501 400 end 502 401 … … 512 411 513 412 # compile the given template, if necessary 514 if compile_template?(template, file_path, local_assigns) 515 template ||= read_template_file(file_path, nil) 516 compile_template(handler, template, file_path, local_assigns) 517 end 413 handler.compile_template(template, file_path, local_assigns) 518 414 519 415 # Get the method name for this template and run it trunk/actionpack/lib/action_view/template_handler.rb
r8624 r8689 1 1 module ActionView 2 2 class TemplateHandler 3 # Map method names to their compile time 4 cattr_accessor :compile_time 5 @@compile_time = {} 6 7 # Map method names to the names passed in local assigns so far 8 cattr_accessor :template_args 9 @@template_args = {} 10 11 # Count the number of inline templates 12 cattr_accessor :inline_template_count 13 @@inline_template_count = 0 14 3 15 def self.line_offset 4 16 0 … … 30 42 def cache_fragment(block, name = {}, options = nil) 31 43 end 44 45 # This method reads a template file. 46 def read_template_file(template_path, extension) 47 File.read(template_path) 48 end 32 49 end 33 50 end trunk/actionpack/lib/action_view/template_handlers/builder.rb
r8624 r8689 4 4 module TemplateHandlers 5 5 class Builder < TemplateHandler 6 include Compilable 7 6 8 def self.line_offset 7 9 2 8 end9 10 def self.compilable?11 true12 10 end 13 11 … … 15 13 content_type_handler = (@view.send!(:controller).respond_to?(:response) ? "controller.response" : "controller") 16 14 "#{content_type_handler}.content_type ||= Mime::XML\n" + 17 "xml = Builder::XmlMarkup.new(:indent => 2)\n" +15 "xml = ::Builder::XmlMarkup.new(:indent => 2)\n" + 18 16 template + 19 17 "\nxml.target!\n" trunk/actionpack/lib/action_view/template_handlers/erb.rb
r8624 r8689 23 23 module TemplateHandlers 24 24 class ERB < TemplateHandler 25 include Compilable 26 25 27 def compile(template) 26 28 ::ERB.new(template, nil, @view.erb_trim_mode).src 27 end28 29 def self.compilable?30 true31 29 end 32 30 trunk/actionpack/lib/action_view/template_handlers/rjs.rb
r8624 r8689 2 2 module TemplateHandlers 3 3 class RJS < TemplateHandler 4 include Compilable 5 4 6 def self.line_offset 5 7 2 … … 9 11 "controller.response.content_type ||= Mime::JS\n" + 10 12 "update_page do |page|\n#{template}\nend" 11 end12 13 def self.compilable?14 true15 13 end 16 14 trunk/actionpack/test/template/compiled_templates_test.rb
r8624 r8689 88 88 v.cache_template_loading = false 89 89 90 @handler_class = ActionView::Base.handler_class_for_extension(:rhtml) 91 @handler = @handler_class.new(v) 92 90 93 # All templates were created at t+1 91 94 File::Stat.any_instance.expects(:mtime).times(windows ? 2 : 3).returns(t + 1.second) … … 93 96 # private methods template_changed_since? and compile_template? 94 97 # should report true for all since they have not been compiled 95 assert v.send(:template_changed_since?, @a, t)96 assert v.send(:template_changed_since?, @b, t)97 assert v.send(:template_changed_since?, @s, t) unless windows98 assert @handler.send(:template_changed_since?, @a, t) 99 assert @handler.send(:template_changed_since?, @b, t) 100 assert @handler.send(:template_changed_since?, @s, t) unless windows 98 101 99 assert v.send(:compile_template?, nil, @a, {})100 assert v.send(:compile_template?, nil, @b, {})101 assert v.send(:compile_template?, nil, @s, {}) unless windows102 assert @handler.send(:compile_template?, nil, @a, {}) 103 assert @handler.send(:compile_template?, nil, @b, {}) 104 assert @handler.send(:compile_template?, nil, @s, {}) unless windows 102 105 103 @handler_class = ActionView::Base.handler_class_for_extension(:rhtml)104 @handler = @handler_class.new(v)105 106 # All templates are rendered at t+2 106 107 Time.expects(:now).times(windows ? 2 : 3).returns(t + 2.seconds) … … 112 113 s_n = v.method_names[@s] unless windows 113 114 # all of the files have changed since last compile 114 assert v.compile_time[a_n] > t115 assert v.compile_time[b_n] > t116 assert v.compile_time[s_n] > t unless windows115 assert @handler.compile_time[a_n] > t 116 assert @handler.compile_time[b_n] > t 117 assert @handler.compile_time[s_n] > t unless windows 117 118 118 119 # private methods template_changed_since? and compile_template? 119 120 # should report false for all since none have changed since compile 120 121 File::Stat.any_instance.expects(:mtime).times(windows ? 6 : 12).returns(t + 1.second) 121 assert ! v.send(:template_changed_since?, @a, v.compile_time[a_n])122 assert ! v.send(:template_changed_since?, @b, v.compile_time[b_n])123 assert ! v.send(:template_changed_since?, @s, v.compile_time[s_n]) unless windows124 assert ! v.send(:compile_template?, nil, @a, {})125 assert ! v.send(:compile_template?, nil, @b, {})126 assert ! v.send(:compile_template?, nil, @s, {}) unless windows122 assert !@handler.send(:template_changed_since?, @a, @handler.compile_time[a_n]) 123 assert !@handler.send(:template_changed_since?, @b, @handler.compile_time[b_n]) 124 assert !@handler.send(:template_changed_since?, @s, @handler.compile_time[s_n]) unless windows 125 assert !@handler.send(:compile_template?, nil, @a, {}) 126 assert !@handler.send(:compile_template?, nil, @b, {}) 127 assert !@handler.send(:compile_template?, nil, @s, {}) unless windows 127 128 v.send(:compile_and_render_template, @handler, '', @a) 128 129 v.send(:compile_and_render_template, @handler, '', @b) 129 130 v.send(:compile_and_render_template, @handler, '', @s) unless windows 130 131 # none of the files have changed since last compile 131 assert v.compile_time[a_n] < t + 3.seconds132 assert v.compile_time[b_n] < t + 3.seconds133 assert v.compile_time[s_n] < t + 3.seconds unless windows132 assert @handler.compile_time[a_n] < t + 3.seconds 133 assert @handler.compile_time[b_n] < t + 3.seconds 134 assert @handler.compile_time[s_n] < t + 3.seconds unless windows 134 135 135 136 `rm #{@s}; ln -s #{@b} #{@s}` unless windows … … 141 142 *(windows ? [ t + 1.second, t + 1.second ] : 142 143 [ t + 1.second, t + 1.second, t + 3.second ]) * 3) 143 assert ! v.send(:template_changed_since?, @a, v.compile_time[a_n])144 assert ! v.send(:template_changed_since?, @b, v.compile_time[b_n])145 assert v.send(:template_changed_since?, @s, v.compile_time[s_n]) unless windows146 assert ! v.send(:compile_template?, nil, @a, {})147 assert ! v.send(:compile_template?, nil, @b, {})148 assert v.send(:compile_template?, nil, @s, {}) unless windows144 assert !@handler.send(:template_changed_since?, @a, @handler.compile_time[a_n]) 145 assert !@handler.send(:template_changed_since?, @b, @handler.compile_time[b_n]) 146 assert @handler.send(:template_changed_since?, @s, @handler.compile_time[s_n]) unless windows 147 assert !@handler.send(:compile_template?, nil, @a, {}) 148 assert !@handler.send(:compile_template?, nil, @b, {}) 149 assert @handler.send(:compile_template?, nil, @s, {}) unless windows 149 150 150 151 # Only the symlink template gets rendered at t+3 … … 154 155 v.send(:compile_and_render_template, @handler, '', @s) unless windows 155 156 # the symlink has changed since last compile 156 assert v.compile_time[a_n] < t + 3.seconds157 assert v.compile_time[b_n] < t + 3.seconds158 assert_equal v.compile_time[s_n], t + 3.seconds unless windows157 assert @handler.compile_time[a_n] < t + 3.seconds 158 assert @handler.compile_time[b_n] < t + 3.seconds 159 assert_equal @handler.compile_time[s_n], t + 3.seconds unless windows 159 160 160 161 FileUtils.touch @b … … 167 168 *(windows ? [ t + 1.second, t + 4.seconds ] : 168 169 [ t + 1.second, t + 4.seconds, t + 3.second, t + 4.seconds ]) * 3) 169 assert ! v.send(:template_changed_since?, @a, v.compile_time[a_n])170 assert v.send(:template_changed_since?, @b, v.compile_time[b_n])171 assert v.send(:template_changed_since?, @s, v.compile_time[s_n]) unless windows172 assert ! v.send(:compile_template?, nil, @a, {})173 assert v.send(:compile_template?, nil, @b, {})174 assert v.send(:compile_template?, nil, @s, {}) unless windows170 assert !@handler.send(:template_changed_since?, @a, @handler.compile_time[a_n]) 171 assert @handler.send(:template_changed_since?, @b, @handler.compile_time[b_n]) 172 assert @handler.send(:template_changed_since?, @s, @handler.compile_time[s_n]) unless windows 173 assert !@handler.send(:compile_template?, nil, @a, {}) 174 assert @handler.send(:compile_template?, nil, @b, {}) 175 assert @handler.send(:compile_template?, nil, @s, {}) unless windows 175 176 176 177 Time.expects(:now).times(windows ? 1 : 2).returns(t + 5.seconds) … … 180 181 # the file at the end of the symlink has changed since last compile 181 182 # both the symlink and the file at the end of it should be recompiled 182 assert v.compile_time[a_n] < t + 5.seconds183 assert_equal v.compile_time[b_n], t + 5.seconds184 assert_equal v.compile_time[s_n], t + 5.seconds unless windows183 assert @handler.compile_time[a_n] < t + 5.seconds 184 assert_equal @handler.compile_time[b_n], t + 5.seconds 185 assert_equal @handler.compile_time[s_n], t + 5.seconds unless windows 185 186 end 186 187 end 187 188 end 188 189 module ActionView190 class Base191 def compile_time192 @@compile_time193 end194 def method_names195 @@method_names196 end197 end198 end