Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 8689

Show
Ignore:
Timestamp:
01/22/08 01:46:34 (9 months ago)
Author:
nzkoz
Message:

Refactor template compilation from AV::Base into the template handlers. Closes #10888 [lifofifo]

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/actionpack/lib/action_view.rb

    r8683 r8689  
    2323 
    2424require 'action_view/template_handler' 
     25require 'action_view/template_handlers/compilable' 
    2526require 'action_view/template_handlers/builder' 
    2627require 'action_view/template_handlers/erb' 
  • trunk/actionpack/lib/action_view/base.rb

    r8683 r8689  
    197197    include CompiledTemplates 
    198198 
    199     # Maps inline templates to their method names  
     199    # Maps inline templates to their method names 
     200    cattr_accessor :method_names 
    200201    @@method_names = {} 
    201     # Map method names to their compile time 
    202     @@compile_time = {} 
    203202    # Map method names to the names passed in local assigns so far 
    204203    @@template_args = {} 
    205     # Count the number of inline templates 
    206     @@inline_template_count = 0 
    207204 
    208205    # Cache public asset paths 
     
    366363        compile_and_render_template(handler, template, file_path, local_assigns) 
    367364      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. 
    369366        handler.render(template, local_assigns) 
    370367      end 
     
    390387      end 
    391388 
    392       # This method reads a template file. 
    393       def read_template_file(template_path, extension) 
    394         File.read(template_path) 
    395       end 
    396  
    397389      # Evaluate the local assigns and pushes them to the view. 
    398390      def evaluate_assigns 
     
    406398      def assign_variables_from_controller 
    407399        @assigns.each { |key, value| instance_variable_set("@#{key}", value) } 
    408       end 
    409  
    410  
    411       # Return true if the given template was compiled for a superset of the keys in local_assigns 
    412       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       end 
    416  
    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 || template 
    423         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_loading 
    428             template_changed_since?(file_name, compile_time) 
    429           end 
    430         else 
    431           true 
    432         end 
    433       end 
    434  
    435       # Method to handle checking a whether a template has changed since last compile; isolated so that templates 
    436       # 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       end 
    442  
    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 | locals 
    449         @@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         end 
    455  
    456         "def #{render_symbol}(local_assigns)\n#{locals_code}#{body}\nend" 
    457       end 
    458  
    459       def assign_method_name(handler, template, file_name) 
    460         method_key = file_name || template 
    461         @@method_names[method_key] ||= compiled_method_name(handler, template, file_name) 
    462       end 
    463  
    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_sym 
    466       end 
    467  
    468       def compiled_method_name_file_path_segment(file_name) 
    469         if file_name 
    470           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           s 
    474         else 
    475           (@@inline_template_count += 1).to_s 
    476         end 
    477       end 
    478  
    479       # Compile and evaluate the template's code 
    480       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_offset 
    484  
    485         begin 
    486           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 code 
    489           if logger 
    490             logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}" 
    491             logger.debug "Function body: #{render_source}" 
    492             logger.debug "Backtrace: #{e.backtrace.join("\n")}" 
    493           end 
    494  
    495           raise TemplateError.new(@finder.extract_base_path_from(file_name) || 
    496                 @finder.view_paths.first, file_name || template, @assigns, template, e) 
    497         end 
    498  
    499         @@compile_time[render_symbol] = Time.now 
    500         # logger.debug "Compiled template #{file_name || template}\n  ==> #{render_symbol}" if logger 
    501400      end 
    502401 
     
    512411 
    513412        # 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) 
    518414 
    519415        # Get the method name for this template and run it 
  • trunk/actionpack/lib/action_view/template_handler.rb

    r8624 r8689  
    11module ActionView 
    22  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 
    315    def self.line_offset 
    416      0 
     
    3042    def cache_fragment(block, name = {}, options = nil) 
    3143    end 
     44 
     45    # This method reads a template file. 
     46    def read_template_file(template_path, extension) 
     47      File.read(template_path) 
     48    end 
    3249  end 
    3350end 
  • trunk/actionpack/lib/action_view/template_handlers/builder.rb

    r8624 r8689  
    44  module TemplateHandlers 
    55    class Builder < TemplateHandler 
     6      include Compilable 
     7 
    68      def self.line_offset 
    79        2 
    8       end 
    9  
    10       def self.compilable? 
    11         true 
    1210      end 
    1311 
     
    1513        content_type_handler = (@view.send!(:controller).respond_to?(:response) ? "controller.response" : "controller") 
    1614        "#{content_type_handler}.content_type ||= Mime::XML\n" + 
    17         "xml = Builder::XmlMarkup.new(:indent => 2)\n" + 
     15        "xml = ::Builder::XmlMarkup.new(:indent => 2)\n" + 
    1816        template + 
    1917        "\nxml.target!\n" 
  • trunk/actionpack/lib/action_view/template_handlers/erb.rb

    r8624 r8689  
    2323  module TemplateHandlers 
    2424    class ERB < TemplateHandler 
     25      include Compilable 
     26 
    2527      def compile(template) 
    2628        ::ERB.new(template, nil, @view.erb_trim_mode).src 
    27       end 
    28  
    29       def self.compilable? 
    30         true 
    3129      end 
    3230 
  • trunk/actionpack/lib/action_view/template_handlers/rjs.rb

    r8624 r8689  
    22  module TemplateHandlers 
    33    class RJS < TemplateHandler 
     4      include Compilable 
     5 
    46      def self.line_offset 
    57        2 
     
    911        "controller.response.content_type ||= Mime::JS\n" + 
    1012        "update_page do |page|\n#{template}\nend" 
    11       end 
    12  
    13       def self.compilable? 
    14         true 
    1513      end 
    1614 
  • trunk/actionpack/test/template/compiled_templates_test.rb

    r8624 r8689  
    8888      v.cache_template_loading = false 
    8989 
     90      @handler_class = ActionView::Base.handler_class_for_extension(:rhtml) 
     91      @handler       = @handler_class.new(v) 
     92 
    9093      # All templates were created at t+1 
    9194      File::Stat.any_instance.expects(:mtime).times(windows ? 2 : 3).returns(t + 1.second) 
     
    9396      # private methods template_changed_since? and compile_template? 
    9497      # 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 windows 
     98      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 
    98101 
    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 windows 
     102      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 
    102105 
    103       @handler_class = ActionView::Base.handler_class_for_extension(:rhtml) 
    104       @handler       = @handler_class.new(v) 
    105106      # All templates are rendered at t+2 
    106107      Time.expects(:now).times(windows ? 2 : 3).returns(t + 2.seconds) 
     
    112113      s_n = v.method_names[@s] unless windows 
    113114      # all of the files have changed since last compile 
    114       assert v.compile_time[a_n] > t 
    115       assert v.compile_time[b_n] > t 
    116       assert v.compile_time[s_n] > t unless windows 
     115      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 
    117118 
    118119      # private methods template_changed_since? and compile_template? 
    119120      # should report false for all since none have changed since compile 
    120121      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 windows 
    124       assert !v.send(:compile_template?, nil, @a, {}) 
    125       assert !v.send(:compile_template?, nil, @b, {}) 
    126       assert !v.send(:compile_template?, nil, @s, {}) unless windows 
     122      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 
    127128      v.send(:compile_and_render_template, @handler, '', @a) 
    128129      v.send(:compile_and_render_template, @handler, '', @b) 
    129130      v.send(:compile_and_render_template, @handler, '', @s)  unless windows 
    130131      # none of the files have changed since last compile 
    131       assert v.compile_time[a_n] < t + 3.seconds 
    132       assert v.compile_time[b_n] < t + 3.seconds 
    133       assert v.compile_time[s_n] < t + 3.seconds  unless windows 
     132      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 
    134135 
    135136      `rm #{@s}; ln -s #{@b} #{@s}` unless windows 
     
    141142        *(windows ? [ t + 1.second, t + 1.second ] : 
    142143          [ 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 windows 
    146       assert !v.send(:compile_template?, nil, @a, {}) 
    147       assert !v.send(:compile_template?, nil, @b, {}) 
    148       assert v.send(:compile_template?, nil, @s, {}) unless windows 
     144      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 
    149150 
    150151      # Only the symlink template gets rendered at t+3 
     
    154155      v.send(:compile_and_render_template, @handler, '', @s) unless windows 
    155156      # the symlink has changed since last compile 
    156       assert v.compile_time[a_n] < t + 3.seconds 
    157       assert v.compile_time[b_n] < t + 3.seconds 
    158       assert_equal v.compile_time[s_n], t + 3.seconds unless windows 
     157      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 
    159160 
    160161      FileUtils.touch @b 
     
    167168        *(windows ? [ t + 1.second, t + 4.seconds ] : 
    168169          [ 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 windows 
    172       assert !v.send(:compile_template?, nil, @a, {}) 
    173       assert v.send(:compile_template?, nil, @b, {}) 
    174       assert v.send(:compile_template?, nil, @s, {}) unless windows 
     170      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 
    175176 
    176177      Time.expects(:now).times(windows ? 1 : 2).returns(t + 5.seconds) 
     
    180181      # the file at the end of the symlink has changed since last compile 
    181182      # both the symlink and the file at the end of it should be recompiled 
    182       assert v.compile_time[a_n] < t + 5.seconds 
    183       assert_equal v.compile_time[b_n], t + 5.seconds 
    184       assert_equal v.compile_time[s_n], t + 5.seconds unless windows 
     183      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 
    185186    end 
    186187  end 
    187188end 
    188  
    189 module ActionView 
    190   class Base 
    191     def compile_time 
    192       @@compile_time 
    193     end 
    194     def method_names 
    195       @@method_names 
    196     end 
    197   end 
    198 end