diff options
Diffstat (limited to 'actionpack/lib/action_view/template/template.rb')
-rw-r--r-- | actionpack/lib/action_view/template/template.rb | 216 |
1 files changed, 54 insertions, 162 deletions
diff --git a/actionpack/lib/action_view/template/template.rb b/actionpack/lib/action_view/template/template.rb index e541336613..dcc5006103 100644 --- a/actionpack/lib/action_view/template/template.rb +++ b/actionpack/lib/action_view/template/template.rb @@ -1,188 +1,80 @@ +# encoding: utf-8 +# This is so that templates compiled in this file are UTF-8 + require 'set' require "action_view/template/path" -module ActionView #:nodoc: +module ActionView class Template extend TemplateHandlers - extend ActiveSupport::Memoizable - - module Loading - def load! - @cached = true - # freeze - end - end - include Loading - - include Renderable - - # Templates that are exempt from layouts - @@exempt_from_layout = Set.new([/\.rjs$/]) - - # Don't render layouts for templates with the given extensions. - def self.exempt_from_layout(*extensions) - regexps = extensions.collect do |extension| - extension.is_a?(Regexp) ? extension : /\.#{Regexp.escape(extension.to_s)}$/ - end - @@exempt_from_layout.merge(regexps) - end - - attr_accessor :template_path, :filename, :load_path, :base_path - attr_accessor :locale, :name, :format, :extension - delegate :to_s, :to => :path - - def initialize(template_path, load_paths = []) - template_path = template_path.dup - @load_path, @filename = find_full_path(template_path, load_paths) - @base_path, @name, @locale, @format, @extension = split(template_path) - @base_path.to_s.gsub!(/\/$/, '') # Push to split method - - # Extend with partial super powers - extend RenderablePartial if @name =~ /^_/ - end + attr_reader :source, :identifier, :handler - def accessible_paths - paths = [] - - if valid_extension?(extension) - paths << path - paths << path_without_extension - if multipart? - formats = format.split(".") - paths << "#{path_without_format_and_extension}.#{formats.first}" - paths << "#{path_without_format_and_extension}.#{formats.second}" - end - else - # template without explicit template handler should only be reachable through its exact path - paths << template_path - end - - paths + def initialize(source, identifier, handler, details) + @source = source + @identifier = identifier + @handler = handler + @details = details end - def relative_path - path = File.expand_path(filename) - path.sub!(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '') if defined?(RAILS_ROOT) - path + def render(view, locals, &blk) + method_name = compile(locals, view) + view.send(method_name, locals, &blk) end - memoize :relative_path - def source - File.read(filename) + # TODO: Figure out how to abstract this + def variable_name + identifier[%r'_?(\w+)(\.\w+)*$', 1].to_sym end - memoize :source - - def exempt_from_layout? - @@exempt_from_layout.any? { |exempted| path =~ exempted } - end - - def path_without_extension - [base_path, [name, locale, format].compact.join('.')].compact.join('/') - end - memoize :path_without_extension - def path_without_format_and_extension - [base_path, [name, locale].compact.join('.')].compact.join('/') + # TODO: Figure out how to abstract this + def counter_name + "#{variable_name}_counter".to_sym end - memoize :path_without_format_and_extension - def path - [base_path, [name, locale, format, extension].compact.join('.')].compact.join('/') + # TODO: kill hax + def partial? + @details[:partial] end - memoize :path + # TODO: Move out of Template def mime_type - Mime::Type.lookup_by_extension(format) if format && defined?(::Mime) + Mime::Type.lookup_by_extension(@details[:format].to_s) if @details[:format] end - memoize :mime_type - - def multipart? - format && format.include?('.') - end - - def content_type - format && format.gsub('.', '/') - end private - - def format_and_extension - (extensions = [format, extension].compact.join(".")).blank? ? nil : extensions - end - memoize :format_and_extension - - def mtime - File.mtime(filename) - end - memoize :mtime - - def method_segment - relative_path.to_s.gsub(/([^a-zA-Z0-9_])/) { $1.ord } - end - memoize :method_segment - - def stale? - File.mtime(filename) > mtime - end - - def recompile? - !@cached - end - - def valid_extension?(extension) - !Template.registered_template_handler(extension).nil? - end - def valid_locale?(locale) - I18n.available_locales.include?(locale.to_sym) - end + def compile(locals, view) + method_name = build_method_name(locals) + + return method_name if view.respond_to?(method_name) + + locals_code = locals.keys.map! { |key| "#{key} = local_assigns[:#{key}];" }.join + + source = <<-end_src + def #{method_name}(local_assigns) + old_output_buffer = output_buffer;#{locals_code};#{@handler.call(self)} + ensure + self.output_buffer = old_output_buffer + end + end_src + + begin + ActionView::Base::CompiledTemplates.module_eval(source, identifier, 0) + method_name + rescue Exception => e # errors from template code + if logger = (view && view.logger) + logger.debug "ERROR: compiling #{method_name} RAISED #{e}" + logger.debug "Function body: #{source}" + logger.debug "Backtrace: #{e.backtrace.join("\n")}" + end - def find_full_path(path, load_paths) - load_paths = Array(load_paths) + [nil] - load_paths.each do |load_path| - file = load_path ? "#{load_path.to_str}/#{path}" : path - return load_path, file if File.file?(file) + raise ActionView::TemplateError.new(self, {}, e) end - raise MissingTemplate.new(load_paths, path) end - - # Returns file split into an array - # [base_path, name, locale, format, extension] - def split(file) - if m = file.to_s.match(/^(.*\/)?([^\.]+)\.(.*)$/) - base_path = m[1] - name = m[2] - extensions = m[3] - else - return - end - - locale = nil - format = nil - extension = nil - - if m = extensions.split(".") - if valid_locale?(m[0]) && m[1] && valid_extension?(m[2]) # All three - locale = m[0] - format = m[1] - extension = m[2] - elsif m[0] && m[1] && valid_extension?(m[2]) # Multipart formats - format = "#{m[0]}.#{m[1]}" - extension = m[2] - elsif valid_locale?(m[0]) && valid_extension?(m[1]) # locale and extension - locale = m[0] - extension = m[1] - elsif valid_extension?(m[1]) # format and extension - format = m[0] - extension = m[1] - elsif valid_extension?(m[0]) # Just extension - extension = m[0] - else # No extension - format = m[0] - end - end - - [base_path, name, locale, format, extension] + + def build_method_name(locals) + # TODO: is locals.keys.hash reliably the same? + "_render_template_#{@identifier.hash}_#{__id__}_#{locals.keys.hash}".gsub('-', "_") end end -end +end
\ No newline at end of file |