Skip to content
Browse files

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
  • Loading branch information...
1 parent 8bc9018 commit 692dbbf79387b56e241e1acd05f74f7d71ff79a6 @NZKoz NZKoz committed
View
2 actionpack/CHANGELOG
@@ -1,5 +1,7 @@
*SVN*
+* Introduce a Template class to ActionView. #11024 [lifofifo]
+
* Introduce the :index option for form_for and fields_for to simplify multi-model forms (see http://railscasts.com/episodes/75). #9883 [rmm5t]
* Introduce map.resources :cards, :as => 'tarjetas' to use a custom resource name in the URL: cards_path == '/tarjetas'. #10578 [blj]
View
4 actionpack/lib/action_controller/base.rb
@@ -5,6 +5,7 @@
require 'action_controller/resources'
require 'action_controller/url_rewriter'
require 'action_controller/status_codes'
+require 'action_view/template'
require 'action_view/template_finder'
require 'drb'
require 'set'
@@ -866,7 +867,8 @@ def render(options = nil, &block) #:doc:
elsif inline = options[:inline]
add_variables_to_assigns
- render_for_text(@template.render_template(options[:type], inline, nil, options[:locals] || {}), options[:status])
+ tmpl = ActionView::Template.new(@template, options[:inline], false, options[:locals], true, options[:type])
+ render_for_text(@template.render_template(tmpl), options[:status])
elsif action_name = options[:action]
template = default_template_name(action_name.to_s)
View
1 actionpack/lib/action_view.rb
@@ -28,6 +28,7 @@
require 'action_view/template_handlers/rjs'
require 'action_view/template_finder'
+require 'action_view/template'
require 'action_view/base'
require 'action_view/partials'
View
68 actionpack/lib/action_view/base.rb
@@ -150,8 +150,8 @@ class ActionViewError < StandardError #: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 @@ class Base
# 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 @@ def render_file(template_path, use_full_path = true, local_assigns = {}) #:nodoc
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 @@ def render(options = {}, old_local_assigns = {}, &block) #:nodoc:
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 <tt>template_extension</tt>.
# The hash in <tt>local_assigns</tt> 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 @@ def assign_variables_from_controller
# 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
View
65 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
View
8 actionpack/lib/action_view/template_error.rb
@@ -6,10 +6,10 @@ class TemplateError < ActionViewError #:nodoc:
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
View
5 actionpack/lib/action_view/template_handler.rb
@@ -30,10 +30,5 @@ def line_offset
# 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
View
40 actionpack/lib/action_view/template_handlers/compilable.rb
@@ -26,17 +26,15 @@ def compilable?
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 @@ def compile_template(template, file_name, local_assigns)
@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 @@ def compile_template(template, file_name, local_assigns)
# 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 @@ def compiled_method_name_file_path_segment(file_name)
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 = ""
View
10 actionpack/test/controller/custom_handler_test.rb
@@ -20,14 +20,17 @@ def setup
end
def test_custom_render
- result = @view.render_template( "foo", "hello <%= one %>", nil, :one => "two" )
+ template = ActionView::Template.new(@view, "hello <%= one %>", false, { :one => "two" }, true, "foo")
+
+ result = @view.render_template(template)
assert_equal(
[ "hello <%= one %>", { :one => "two" }, @view ],
result )
end
def test_custom_render2
- result = @view.render_template( "foo2", "hello <%= one %>", nil, :one => "two" )
+ template = ActionView::Template.new(@view, "hello <%= one %>", false, { :one => "two" }, true, "foo2")
+ result = @view.render_template(template)
assert_equal(
[ "hello <%= one %>", { :one => "two" }, @view ],
result )
@@ -35,7 +38,8 @@ def test_custom_render2
def test_unhandled_extension
# uses the ERb handler by default if the extension isn't recognized
- result = @view.render_template( "bar", "hello <%= one %>", nil, :one => "two" )
+ template = ActionView::Template.new(@view, "hello <%= one %>", false, { :one => "two" }, true, "bar")
+ result = @view.render_template(template)
assert_equal "hello two", result
end
end
View
13 actionpack/test/controller/render_test.rb
@@ -140,14 +140,6 @@ def accessing_local_assigns_in_inline_template
:locals => { :local_name => name }
end
- def accessing_local_assigns_in_inline_template_with_string_keys
- name = params[:local_name]
- ActionView::Base.local_assigns_support_string_keys = true
- render :inline => "<%= 'Goodbye, ' + local_name %>",
- :locals => { "local_name" => name }
- ActionView::Base.local_assigns_support_string_keys = false
- end
-
def formatted_html_erb
end
@@ -387,11 +379,6 @@ def test_accessing_local_assigns_in_inline_template
assert_equal "Goodbye, Local David", @response.body
end
- def test_accessing_local_assigns_in_inline_template_with_string_keys
- get :accessing_local_assigns_in_inline_template_with_string_keys, :local_name => "Local David"
- assert_equal "Goodbye, Local David", @response.body
- end
-
def test_render_200_should_set_etag
get :render_hello_world_from_variable
assert_equal etag_for("hello david"), @response.headers['ETag']
View
52 actionpack/test/template/compiled_templates_test.rb
@@ -87,6 +87,10 @@ def test_compile_time
v.base_path = '.'
v.cache_template_loading = false
+ ta = ActionView::Template.new(v, @a, false, {})
+ tb = ActionView::Template.new(v, @b, false, {})
+ ts = ActionView::Template.new(v, @s, false, {})
+
@handler_class = ActionView::Base.handler_class_for_extension(:rhtml)
@handler = @handler_class.new(v)
@@ -99,15 +103,15 @@ def test_compile_time
assert @handler.send(:template_changed_since?, @b, t)
assert @handler.send(:template_changed_since?, @s, t) unless windows
- assert @handler.send(:compile_template?, nil, @a, {})
- assert @handler.send(:compile_template?, nil, @b, {})
- assert @handler.send(:compile_template?, nil, @s, {}) unless windows
+ assert @handler.send(:compile_template?, ta)
+ assert @handler.send(:compile_template?, tb)
+ assert @handler.send(:compile_template?, ts) unless windows
# All templates are rendered at t+2
Time.expects(:now).times(windows ? 2 : 3).returns(t + 2.seconds)
- v.send(:compile_and_render_template, @handler, '', @a)
- v.send(:compile_and_render_template, @handler, '', @b)
- v.send(:compile_and_render_template, @handler, '', @s) unless windows
+ v.send(:compile_and_render_template, @handler, ta)
+ v.send(:compile_and_render_template, @handler, tb)
+ v.send(:compile_and_render_template, @handler, ts) unless windows
a_n = v.method_names[@a]
b_n = v.method_names[@b]
s_n = v.method_names[@s] unless windows
@@ -122,12 +126,12 @@ def test_compile_time
assert !@handler.send(:template_changed_since?, @a, @handler.compile_time[a_n])
assert !@handler.send(:template_changed_since?, @b, @handler.compile_time[b_n])
assert !@handler.send(:template_changed_since?, @s, @handler.compile_time[s_n]) unless windows
- assert !@handler.send(:compile_template?, nil, @a, {})
- assert !@handler.send(:compile_template?, nil, @b, {})
- assert !@handler.send(:compile_template?, nil, @s, {}) unless windows
- v.send(:compile_and_render_template, @handler, '', @a)
- v.send(:compile_and_render_template, @handler, '', @b)
- v.send(:compile_and_render_template, @handler, '', @s) unless windows
+ assert !@handler.send(:compile_template?, ta)
+ assert !@handler.send(:compile_template?, tb)
+ assert !@handler.send(:compile_template?, ts) unless windows
+ v.send(:compile_and_render_template, @handler, ta)
+ v.send(:compile_and_render_template, @handler, tb)
+ v.send(:compile_and_render_template, @handler, ts) unless windows
# none of the files have changed since last compile
assert @handler.compile_time[a_n] < t + 3.seconds
assert @handler.compile_time[b_n] < t + 3.seconds
@@ -144,15 +148,15 @@ def test_compile_time
assert !@handler.send(:template_changed_since?, @a, @handler.compile_time[a_n])
assert !@handler.send(:template_changed_since?, @b, @handler.compile_time[b_n])
assert @handler.send(:template_changed_since?, @s, @handler.compile_time[s_n]) unless windows
- assert !@handler.send(:compile_template?, nil, @a, {})
- assert !@handler.send(:compile_template?, nil, @b, {})
- assert @handler.send(:compile_template?, nil, @s, {}) unless windows
+ assert !@handler.send(:compile_template?, ta)
+ assert !@handler.send(:compile_template?, tb)
+ assert @handler.send(:compile_template?, ts) unless windows
# Only the symlink template gets rendered at t+3
Time.stubs(:now).returns(t + 3.seconds) unless windows
- v.send(:compile_and_render_template, @handler, '', @a)
- v.send(:compile_and_render_template, @handler, '', @b)
- v.send(:compile_and_render_template, @handler, '', @s) unless windows
+ v.send(:compile_and_render_template, @handler, ta)
+ v.send(:compile_and_render_template, @handler, tb)
+ v.send(:compile_and_render_template, @handler, ts) unless windows
# the symlink has changed since last compile
assert @handler.compile_time[a_n] < t + 3.seconds
assert @handler.compile_time[b_n] < t + 3.seconds
@@ -170,14 +174,14 @@ def test_compile_time
assert !@handler.send(:template_changed_since?, @a, @handler.compile_time[a_n])
assert @handler.send(:template_changed_since?, @b, @handler.compile_time[b_n])
assert @handler.send(:template_changed_since?, @s, @handler.compile_time[s_n]) unless windows
- assert !@handler.send(:compile_template?, nil, @a, {})
- assert @handler.send(:compile_template?, nil, @b, {})
- assert @handler.send(:compile_template?, nil, @s, {}) unless windows
+ assert !@handler.send(:compile_template?, ta)
+ assert @handler.send(:compile_template?, tb)
+ assert @handler.send(:compile_template?, ts) unless windows
Time.expects(:now).times(windows ? 1 : 2).returns(t + 5.seconds)
- v.send(:compile_and_render_template, @handler, '', @a)
- v.send(:compile_and_render_template, @handler, '', @b)
- v.send(:compile_and_render_template, @handler, '', @s) unless windows
+ v.send(:compile_and_render_template, @handler, ta)
+ v.send(:compile_and_render_template, @handler, tb)
+ v.send(:compile_and_render_template, @handler, ts) unless windows
# the file at the end of the symlink has changed since last compile
# both the symlink and the file at the end of it should be recompiled
assert @handler.compile_time[a_n] < t + 5.seconds

0 comments on commit 692dbbf

Please sign in to comment.
Something went wrong with that request. Please try again.