From 692dbbf79387b56e241e1acd05f74f7d71ff79a6 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Wed, 6 Feb 2008 04:26:40 +0000 Subject: Introduce a Template class to ActionView. Closes #11024 [lifofifo] git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@8805 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- actionpack/lib/action_view/base.rb | 68 ++++++---------------- actionpack/lib/action_view/template.rb | 65 +++++++++++++++++++++ actionpack/lib/action_view/template_error.rb | 8 +-- actionpack/lib/action_view/template_handler.rb | 5 -- .../action_view/template_handlers/compilable.rb | 40 ++++++------- 5 files changed, 104 insertions(+), 82 deletions(-) create mode 100644 actionpack/lib/action_view/template.rb (limited to 'actionpack/lib/action_view') diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index b362bb9555..9217d5ae0c 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -150,8 +150,8 @@ module ActionView #:nodoc: class Base include ERB::Util - attr_reader :first_render, :finder - attr_accessor :base_path, :assigns, :template_extension + attr_reader :finder + attr_accessor :base_path, :assigns, :template_extension, :first_render attr_accessor :controller attr_reader :logger, :response, :headers @@ -173,12 +173,6 @@ module ActionView #:nodoc: # Should be +false+ for development environments. Defaults to +true+. @@cache_template_extensions = true cattr_accessor :cache_template_extensions - - # Specify whether local_assigns should be able to use string keys. - # Defaults to +true+. String keys are deprecated and will be removed - # shortly. - @@local_assigns_support_string_keys = true - cattr_accessor :local_assigns_support_string_keys # Specify whether RJS responses should be wrapped in a try/catch block # that alert()s the caught exception (and then re-raises it). @@ -282,41 +276,16 @@ If you are rendering a subtemplate, you must now use controller-like partial syn END_ERROR end - # Clear the forward slash at the beginning if exists - template_path = template_path.sub(/^\//, '') if use_full_path - - @first_render ||= template_path - template_path_without_extension, template_extension = @finder.path_and_extension(template_path) - if use_full_path - if template_extension - template_file_name = @finder.pick_template(template_path_without_extension, template_extension) - else - template_extension = @finder.pick_template_extension(template_path).to_s - unless template_extension - raise ActionViewError, "No template found for #{template_path} in #{@finder.view_paths.inspect}" - end - template_file_name = @finder.pick_template(template_path, template_extension) - template_extension = template_extension.gsub(/^.+\./, '') # strip off any formats - end - else - template_file_name = template_path - end - - template_source = nil # Don't read the source until we know that it is required - - if template_file_name.blank? - raise ActionViewError, "Couldn't find template file for #{template_path} in #{@finder.view_paths.inspect}" - end + template = Template.new(self, template_path, use_full_path, local_assigns) begin - render_template(template_extension, template_source, template_file_name, local_assigns) + render_template(template) rescue Exception => e if TemplateError === e - e.sub_template_of(template_file_name) + e.sub_template_of(template.filename) raise e else - raise TemplateError.new(@finder.find_base_path_for("#{template_path_without_extension}.#{template_extension}") || - @finder.view_paths.first, template_file_name, @assigns, template_source, e) + raise TemplateError.new(template, @assigns, e) end end end @@ -350,22 +319,22 @@ If you are rendering a subtemplate, you must now use controller-like partial syn elsif options[:partial] render_partial(options[:partial], ActionView::Base::ObjectWrapper.new(options[:object]), options[:locals]) elsif options[:inline] - render_template(options[:type], options[:inline], nil, options[:locals]) + template = Template.new(self, options[:inline], false, options[:locals], true, options[:type]) + render_template(template) end end end # Renders the +template+ which is given as a string as either erb or builder depending on template_extension. # The hash in local_assigns is made available as local variables. - def render_template(template_extension, template, file_path = nil, local_assigns = {}) #:nodoc: - handler = self.class.handler_class_for_extension(template_extension).new(self) - @current_render_extension = template_extension + def render_template(template) #:nodoc: + handler = template.handler + @current_render_extension = template.extension if handler.compilable? - compile_and_render_template(handler, template, file_path, local_assigns) + compile_and_render_template(handler, template) else - template ||= handler.read_template_file(file_path, template_extension) # Make sure that a lazyily-read template is loaded. - handler.render(template, local_assigns) + handler.render(template.source, template.locals) end end @@ -407,18 +376,15 @@ If you are rendering a subtemplate, you must now use controller-like partial syn # Either, but not both, of template and file_path may be nil. If file_path is given, the template # will only be read if it has to be compiled. # - def compile_and_render_template(handler, template = nil, file_path = nil, local_assigns = {}) #:nodoc: - # convert string keys to symbols if requested - local_assigns = local_assigns.symbolize_keys if @@local_assigns_support_string_keys - + def compile_and_render_template(handler, template) #:nodoc: # compile the given template, if necessary - handler.compile_template(template, file_path, local_assigns) + handler.compile_template(template) # Get the method name for this template and run it - method_name = @@method_names[file_path || template] + method_name = @@method_names[template.method_key] evaluate_assigns - send(method_name, local_assigns) do |*name| + send(method_name, template.locals) do |*name| instance_variable_get "@content_for_#{name.first || 'layout'}" end end diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb new file mode 100644 index 0000000000..a814d7b4e5 --- /dev/null +++ b/actionpack/lib/action_view/template.rb @@ -0,0 +1,65 @@ +module ActionView #:nodoc: + class Template #:nodoc: + + attr_accessor :locals + attr_reader :handler, :path, :source, :extension, :filename, :path_without_extension + + def initialize(view, path_or_source, use_full_path, locals = {}, inline = false, inline_type = nil) + @view = view + @finder = @view.finder + + unless inline + # Clear the forward slash at the beginning if exists + @path = use_full_path ? path_or_source.sub(/^\//, '') : path_or_source + @view.first_render ||= @path + @source = nil # Don't read the source until we know that it is required + set_extension_and_file_name(use_full_path) + else + @source = path_or_source + @extension = inline_type + end + @locals = locals || {} + end + + def source + @source ||= File.read(self.filename) + end + + def method_key + @method_key ||= (@filename || @source) + end + + def handler + @handler ||= @view.class.handler_class_for_extension(@extension).new(@view) + end + + def base_path_for_exception + @finder.find_base_path_for("#{@path_without_extension}.#{@extension}") || @finder.view_paths.first + end + + private + + def set_extension_and_file_name(use_full_path) + @path_without_extension, @extension = @finder.path_and_extension(@path) + if use_full_path + if @extension + @filename = @finder.pick_template(@path_without_extension, @extension) + else + @extension = @finder.pick_template_extension(@path).to_s + unless @extension + raise ActionViewError, "No template found for #{@path} in #{@finder.view_paths.inspect}" + end + @filename = @finder.pick_template(@path, @extension) + @extension = @extension.gsub(/^.+\./, '') # strip off any formats + end + else + @filename = @path + end + + if @filename.blank? + raise ActionViewError, "Couldn't find template file for #{@path} in #{@finder.view_paths.inspect}" + end + end + + end +end diff --git a/actionpack/lib/action_view/template_error.rb b/actionpack/lib/action_view/template_error.rb index 54e4cf099d..33357d50cb 100644 --- a/actionpack/lib/action_view/template_error.rb +++ b/actionpack/lib/action_view/template_error.rb @@ -6,10 +6,10 @@ module ActionView attr_reader :original_exception - def initialize(base_path, file_path, assigns, source, original_exception) - @base_path, @assigns, @source, @original_exception = - base_path, assigns.dup, source, original_exception - @file_path = file_path + def initialize(template, assigns, original_exception) + @base_path = template.base_path_for_exception + @assigns, @source, @original_exception = assigns.dup, template.source, original_exception + @file_path = template.filename @backtrace = compute_backtrace end diff --git a/actionpack/lib/action_view/template_handler.rb b/actionpack/lib/action_view/template_handler.rb index 70fa02674f..687c0db4c6 100644 --- a/actionpack/lib/action_view/template_handler.rb +++ b/actionpack/lib/action_view/template_handler.rb @@ -30,10 +30,5 @@ module ActionView # Called by CacheHelper#cache def cache_fragment(block, name = {}, options = nil) end - - # This method reads a template file. - def read_template_file(template_path, extension) - File.read(template_path) - end end end diff --git a/actionpack/lib/action_view/template_handlers/compilable.rb b/actionpack/lib/action_view/template_handlers/compilable.rb index 6ab4f69558..110c90af0a 100644 --- a/actionpack/lib/action_view/template_handlers/compilable.rb +++ b/actionpack/lib/action_view/template_handlers/compilable.rb @@ -26,17 +26,15 @@ module ActionView end # Compile and evaluate the template's code - def compile_template(template, file_name, local_assigns) - return unless compile_template?(template, file_name, local_assigns) + def compile_template(template) + return unless compile_template?(template) - template ||= read_template_file(file_name, nil) - - render_symbol = assign_method_name(template, file_name) - render_source = create_template_source(template, render_symbol, local_assigns.keys) + render_symbol = assign_method_name(template) + render_source = create_template_source(template, render_symbol) line_offset = self.template_args[render_symbol].size + self.line_offset begin - file_name = 'compiled-template' if file_name.blank? + file_name = template.filename || 'compiled-template' ActionView::Base::CompiledTemplates.module_eval(render_source, file_name, -line_offset) rescue Exception => e # errors from template code if @view.logger @@ -45,8 +43,7 @@ module ActionView @view.logger.debug "Backtrace: #{e.backtrace.join("\n")}" end - raise ActionView::TemplateError.new(@view.finder.extract_base_path_from(file_name) || - @view.finder.view_paths.first, file_name || template, @view.assigns, template, e) + raise ActionView::TemplateError.new(template, @view.assigns, e) end self.compile_time[render_symbol] = Time.now @@ -59,27 +56,26 @@ module ActionView # The template will be compiled if the inline template or file has not been compiled yet, # if local_assigns has a new key, which isn't supported by the compiled code yet, # or if the file has changed on disk and checking file mods hasn't been disabled. - def compile_template?(template, file_name, local_assigns) - method_key = file_name || template + def compile_template?(template) + method_key = template.method_key render_symbol = @view.method_names[method_key] compile_time = self.compile_time[render_symbol] - if compile_time && supports_local_assigns?(render_symbol, local_assigns) - if file_name && !@view.cache_template_loading - template_changed_since?(file_name, compile_time) + if compile_time && supports_local_assigns?(render_symbol, template.locals) + if template.filename && !@view.cache_template_loading + template_changed_since?(template.filename, compile_time) end else true end end - def assign_method_name(template, file_name) - method_key = file_name || template - @view.method_names[method_key] ||= compiled_method_name(template, file_name) + def assign_method_name(template) + @view.method_names[template.method_key] ||= compiled_method_name(template) end - def compiled_method_name(template, file_name) - ['_run', self.class.to_s.demodulize.underscore, compiled_method_name_file_path_segment(file_name)].compact.join('_').to_sym + def compiled_method_name(template) + ['_run', self.class.to_s.demodulize.underscore, compiled_method_name_file_path_segment(template.filename)].compact.join('_').to_sym end def compiled_method_name_file_path_segment(file_name) @@ -94,11 +90,11 @@ module ActionView end # Method to create the source code for a given template. - def create_template_source(template, render_symbol, locals) - body = compile(template) + def create_template_source(template, render_symbol) + body = compile(template.source) self.template_args[render_symbol] ||= {} - locals_keys = self.template_args[render_symbol].keys | locals + locals_keys = self.template_args[render_symbol].keys | template.locals.keys self.template_args[render_symbol] = locals_keys.inject({}) { |h, k| h[k] = true; h } locals_code = "" -- cgit v1.2.3