aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_view/template/template.rb
diff options
context:
space:
mode:
authorYehuda Katz + Carl Lerche <ykatz+clerche@engineyard.com>2009-04-23 15:58:38 -0700
committerYehuda Katz + Carl Lerche <ykatz+clerche@engineyard.com>2009-04-27 11:49:11 -0700
commitcecafc52ee0a4a53c903ddbaba95683261f88e5f (patch)
tree4cc1f0d246a313b2a2fa78747b3265bdbd901c22 /actionpack/lib/action_view/template/template.rb
parentda3c21ead59cb47b8f4c69c6bd95f225a9c8b479 (diff)
downloadrails-cecafc52ee0a4a53c903ddbaba95683261f88e5f.tar.gz
rails-cecafc52ee0a4a53c903ddbaba95683261f88e5f.tar.bz2
rails-cecafc52ee0a4a53c903ddbaba95683261f88e5f.zip
Refactor ActionView::Template
ActionView::Template is now completely independent from template storage, which allows different back ends such as the database. ActionView::Template's only responsibility is to take in the template source (passed in from ActionView::Path), compile it, and render it.
Diffstat (limited to 'actionpack/lib/action_view/template/template.rb')
-rw-r--r--actionpack/lib/action_view/template/template.rb336
1 files changed, 189 insertions, 147 deletions
diff --git a/actionpack/lib/action_view/template/template.rb b/actionpack/lib/action_view/template/template.rb
index e541336613..ce6268729a 100644
--- a/actionpack/lib/action_view/template/template.rb
+++ b/actionpack/lib/action_view/template/template.rb
@@ -1,188 +1,230 @@
+# 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
+ attr_reader :source, :identifier, :handler
- module Loading
- def load!
- @cached = true
- # freeze
- end
+ def initialize(source, identifier, handler, details)
+ @source = source
+ @identifier = identifier
+ @handler = handler
+ @details = details
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)
+ def render(view, locals, &blk)
+ method_name = compile(locals, view)
+ view.send(method_name, locals, &blk)
+ end
+
+ # TODO: Figure out how to abstract this
+ def variable_name
+ identifier[%r'_?(\w+)(\.\w+)*$', 1].to_sym
end
- attr_accessor :template_path, :filename, :load_path, :base_path
- attr_accessor :locale, :name, :format, :extension
- delegate :to_s, :to => :path
+ # TODO: Figure out how to abstract this
+ def counter_name
+ "#{variable_name}_counter".to_sym
+ end
+
+ # TODO: kill hax
+ def partial?
+ @details[:partial]
+ end
+
+ # TODO: Move out of Template
+ def mime_type
+ Mime::Type.lookup_by_extension(@details[:format]) if @details[:format]
+ end
+
+ private
- 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
+ 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
- # Extend with partial super powers
- extend RenderablePartial if @name =~ /^_/
+ raise ActionView::TemplateError.new(self, {}, e)
+ end
+ end
+
+ 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
+
+if false
+ module ActionView #:nodoc:
+ class Template
+ extend TemplateHandlers
+ extend ActiveSupport::Memoizable
- def accessible_paths
- paths = []
+ module Loading
+ def load!
+ @cached = true
+ # freeze
+ end
+ end
+ include Loading
+
+ include Renderable
- 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}"
+ # 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
- else
- # template without explicit template handler should only be reachable through its exact path
- paths << template_path
+ @@exempt_from_layout.merge(regexps)
end
- paths
- 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)
+ @name = template_path.to_s.split("/").last.split(".").first
+ # @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
- def relative_path
- path = File.expand_path(filename)
- path.sub!(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '') if defined?(RAILS_ROOT)
- path
- end
- memoize :relative_path
+ 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
+ end
- def source
- File.read(filename)
- end
- memoize :source
+ def relative_path
+ path = File.expand_path(filename)
+ path.sub!(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '') if defined?(RAILS_ROOT)
+ path
+ end
+ memoize :relative_path
- def exempt_from_layout?
- @@exempt_from_layout.any? { |exempted| path =~ exempted }
- end
+ def source
+ File.read(filename)
+ end
+ memoize :source
- def path_without_extension
- [base_path, [name, locale, format].compact.join('.')].compact.join('/')
- end
- memoize :path_without_extension
+ 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('/')
- end
- memoize :path_without_format_and_extension
+ def path_without_format_and_extension
+ [base_path, [name, locale].compact.join('.')].compact.join('/')
+ end
+ memoize :path_without_format_and_extension
- def path
- [base_path, [name, locale, format, extension].compact.join('.')].compact.join('/')
- end
- memoize :path
+ def path
+ [base_path, [name, locale, format, extension].compact.join('.')].compact.join('/')
+ end
+ memoize :path
- def mime_type
- Mime::Type.lookup_by_extension(format) if format && defined?(::Mime)
- end
- memoize :mime_type
+ def mime_type
+ Mime::Type.lookup_by_extension(format) if format && defined?(::Mime)
+ end
+ memoize :mime_type
- def multipart?
- format && format.include?('.')
- end
+ def multipart?
+ format && format.include?('.')
+ end
- def content_type
- format && format.gsub('.', '/')
- end
+ def content_type
+ format && format.gsub('.', '/')
+ end
- private
+ 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 format_and_extension
+ (extensions = [format, extension].compact.join(".")).blank? ? nil : extensions
+ end
+ memoize :format_and_extension
- def method_segment
- relative_path.to_s.gsub(/([^a-zA-Z0-9_])/) { $1.ord }
- end
- memoize :method_segment
+ def mtime
+ File.mtime(filename)
+ end
+ memoize :mtime
- def stale?
- File.mtime(filename) > mtime
- end
+ def method_segment
+ relative_path.to_s.gsub(/([^a-zA-Z0-9_])/) { $1.ord }
+ end
+ memoize :method_segment
- def recompile?
- !@cached
- end
+ def stale?
+ File.mtime(filename) > mtime
+ end
- def valid_extension?(extension)
- !Template.registered_template_handler(extension).nil?
- end
+ def recompile?
+ !@cached
+ end
- def valid_locale?(locale)
- I18n.available_locales.include?(locale.to_sym)
- end
+ def valid_extension?(extension)
+ !Template.registered_template_handler(extension).nil?
+ 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)
+ def valid_locale?(locale)
+ I18n.available_locales.include?(locale.to_sym)
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]
+ 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)
end
+ raise MissingTemplate.new(load_paths, path)
end
-
- [base_path, name, locale, format, extension]
end
end
-end
+end \ No newline at end of file