aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_view
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_view')
-rw-r--r--actionpack/lib/action_view/base.rb42
-rw-r--r--actionpack/lib/action_view/context.rb4
-rw-r--r--actionpack/lib/action_view/helpers/active_model_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb26
-rw-r--r--actionpack/lib/action_view/helpers/atom_feed_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb12
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb9
-rw-r--r--actionpack/lib/action_view/helpers/number_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/tag_helper.rb21
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb32
-rw-r--r--actionpack/lib/action_view/paths.rb10
-rw-r--r--actionpack/lib/action_view/render/partials.rb190
-rw-r--r--actionpack/lib/action_view/render/rendering.rb107
-rw-r--r--actionpack/lib/action_view/template/resolver.rb2
-rw-r--r--actionpack/lib/action_view/template/template.rb12
-rw-r--r--actionpack/lib/action_view/template/text.rb4
-rw-r--r--actionpack/lib/action_view/test_case.rb6
17 files changed, 267 insertions, 218 deletions
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 9e696af83b..c171a5a8f5 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -164,6 +164,9 @@ module ActionView #:nodoc:
#
# See the ActionView::Helpers::PrototypeHelper::GeneratorMethods documentation for more details.
class Base
+ module Subclasses
+ end
+
include Helpers, Rendering, Partials, ::ERB::Util
extend ActiveSupport::Memoizable
@@ -195,14 +198,16 @@ module ActionView #:nodoc:
attr_internal :request, :layout
- delegate :controller_path, :to => :controller, :allow_nil => true
+ def controller_path
+ @controller_path ||= controller && controller.controller_path
+ end
delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers,
:flash, :action_name, :controller_name, :to => :controller
delegate :logger, :to => :controller, :allow_nil => true
- delegate :find_by_parts, :to => :view_paths
+ delegate :find, :to => :view_paths
include Context
@@ -210,30 +215,35 @@ module ActionView #:nodoc:
ActionView::PathSet.new(Array(value))
end
+ extlib_inheritable_accessor :helpers
attr_reader :helpers
- class ProxyModule < Module
- def initialize(receiver)
- @receiver = receiver
- end
+ def self.for_controller(controller)
+ @views ||= {}
- def include(*args)
- super(*args)
- @receiver.extend(*args)
- end
- end
+ # TODO: Decouple this so helpers are a separate concern in AV just like
+ # they are in AC.
+ if controller.class.respond_to?(:_helper_serial)
+ klass = @views[controller.class._helper_serial] ||= Class.new(self) do
+ Subclasses.const_set(controller.class.name.gsub(/::/, '__'), self)
- def self.for_controller(controller)
- new(controller.class.view_paths, {}, controller).tap do |view|
- view.helpers.include(controller._helpers) if controller.respond_to?(:_helpers)
+ if controller.respond_to?(:_helpers)
+ include controller._helpers
+ self.helpers = controller._helpers
+ end
+ end
+ else
+ klass = self
end
+
+ klass.new(controller.class.view_paths, {}, controller)
end
def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil, formats = nil)#:nodoc:
@formats = formats || [:html]
@assigns = assigns_for_first_render.each { |key, value| instance_variable_set("@#{key}", value) }
@controller = controller
- @helpers = ProxyModule.new(self)
+ @helpers = self.class.helpers || Module.new
@_content_for = Hash.new {|h,k| h[k] = "" }
self.view_paths = view_paths
end
@@ -248,7 +258,7 @@ module ActionView #:nodoc:
def with_template(current_template)
_evaluate_assigns_and_ivars
last_template, self.template = template, current_template
- last_formats, self.formats = formats, [current_template.mime_type.to_sym] + Mime::SET.symbols
+ last_formats, self.formats = formats, current_template.formats
yield
ensure
self.template, self.formats = last_template, last_formats
diff --git a/actionpack/lib/action_view/context.rb b/actionpack/lib/action_view/context.rb
index f212fe25eb..df078a7151 100644
--- a/actionpack/lib/action_view/context.rb
+++ b/actionpack/lib/action_view/context.rb
@@ -12,11 +12,11 @@ module ActionView
#
# Context.for_controller[controller] Create a new ActionView instance for a
# controller
- # Context#_render_partial_from_controller[options]
+ # Context#render_partial[options]
# - responsible for setting options[:_template]
# - Returns String with the rendered partial
# options<Hash>:: see _render_partial in ActionView::Base
- # Context#_render_template_from_controller[template, layout, options, partial]
+ # Context#render_template[template, layout, options, partial]
# - Returns String with the rendered template
# template<ActionView::Template>:: The template to render
# layout<ActionView::Template>:: The layout to render around the template
diff --git a/actionpack/lib/action_view/helpers/active_model_helper.rb b/actionpack/lib/action_view/helpers/active_model_helper.rb
index 4fd7f7d83c..3e6e62237d 100644
--- a/actionpack/lib/action_view/helpers/active_model_helper.rb
+++ b/actionpack/lib/action_view/helpers/active_model_helper.rb
@@ -278,9 +278,7 @@ module ActionView
end
%w(tag content_tag to_date_select_tag to_datetime_select_tag to_time_select_tag).each do |meth|
- define_method meth do |*|
- error_wrapping(super)
- end
+ module_eval "def #{meth}(*) error_wrapping(super) end"
end
def error_wrapping(html_tag)
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 3fde79dfa4..c71840d41f 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -171,7 +171,7 @@ module ActionView
end
# Computes the path to a javascript asset in the public javascripts directory.
- # If the +source+ filename has no extension, .js will be appended.
+ # If the +source+ filename has no extension, .js will be appended (except for explicit URIs)
# Full paths from the document root will be passed through.
# Used internally by javascript_include_tag to build the script path.
#
@@ -179,7 +179,7 @@ module ActionView
# javascript_path "xmlhr" # => /javascripts/xmlhr.js
# javascript_path "dir/xmlhr.js" # => /javascripts/dir/xmlhr.js
# javascript_path "/dir/xmlhr" # => /dir/xmlhr.js
- # javascript_path "http://www.railsapplication.com/js/xmlhr" # => http://www.railsapplication.com/js/xmlhr.js
+ # javascript_path "http://www.railsapplication.com/js/xmlhr" # => http://www.railsapplication.com/js/xmlhr
# javascript_path "http://www.railsapplication.com/js/xmlhr.js" # => http://www.railsapplication.com/js/xmlhr.js
def javascript_path(source)
compute_public_path(source, 'javascripts', 'js')
@@ -337,7 +337,7 @@ module ActionView
end
# Computes the path to a stylesheet asset in the public stylesheets directory.
- # If the +source+ filename has no extension, <tt>.css</tt> will be appended.
+ # If the +source+ filename has no extension, <tt>.css</tt> will be appended (except for explicit URIs).
# Full paths from the document root will be passed through.
# Used internally by +stylesheet_link_tag+ to build the stylesheet path.
#
@@ -345,8 +345,8 @@ module ActionView
# stylesheet_path "style" # => /stylesheets/style.css
# stylesheet_path "dir/style.css" # => /stylesheets/dir/style.css
# stylesheet_path "/dir/style.css" # => /dir/style.css
- # stylesheet_path "http://www.railsapplication.com/css/style" # => http://www.railsapplication.com/css/style.css
- # stylesheet_path "http://www.railsapplication.com/css/style.js" # => http://www.railsapplication.com/css/style.css
+ # stylesheet_path "http://www.railsapplication.com/css/style" # => http://www.railsapplication.com/css/style
+ # stylesheet_path "http://www.railsapplication.com/css/style.css" # => http://www.railsapplication.com/css/style.css
def stylesheet_path(source)
compute_public_path(source, 'stylesheets', 'css')
end
@@ -557,7 +557,7 @@ module ActionView
# video_tag("trailer.ogg") # =>
# <video src="/videos/trailer.ogg" />
# video_tag("trailer.ogg", :controls => true, :autobuffer => true) # =>
- # <video autobuffer="true" controls="true" src="/videos/trailer.ogg" />
+ # <video autobuffer="autobuffer" controls="controls" src="/videos/trailer.ogg" />
# video_tag("trailer.m4v", :size => "16x10", :poster => "screenshot.png") # =>
# <video src="/videos/trailer.m4v" width="16" height="10" poster="/images/screenshot.png" />
# video_tag("/trailers/hd.avi", :size => "16x16") # =>
@@ -629,11 +629,11 @@ module ActionView
has_request = @controller.respond_to?(:request)
source_ext = File.extname(source)[1..-1]
- if ext && (source_ext.blank? || (ext != source_ext && File.exist?(File.join(ASSETS_DIR, dir, "#{source}.#{ext}"))))
+ if ext && !is_uri?(source) && (source_ext.blank? || (ext != source_ext && File.exist?(File.join(ASSETS_DIR, dir, "#{source}.#{ext}"))))
source += ".#{ext}"
end
- unless source =~ %r{^[-a-z]+://}
+ unless is_uri?(source)
source = "/#{dir}/#{source}" unless source[0] == ?/
source = rewrite_asset_path(source)
@@ -645,10 +645,10 @@ module ActionView
end
end
- if include_host && source !~ %r{^[-a-z]+://}
+ if include_host && !is_uri?(source)
host = compute_asset_host(source)
- if has_request && !host.blank? && host !~ %r{^[-a-z]+://}
+ if has_request && !host.blank? && !is_uri?(host)
host = "#{@controller.request.protocol}#{host}"
end
@@ -658,6 +658,10 @@ module ActionView
end
end
+ def is_uri?(path)
+ path =~ %r{^[-a-z]+://}
+ end
+
# Pick an asset host for this source. Returns +nil+ if no host is set,
# the host if no wildcard is set, the host interpolated with the
# numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4),
@@ -798,7 +802,7 @@ module ActionView
end
def asset_file_path!(path)
- unless path =~ %r{^[-a-z]+://}
+ unless is_uri?(path)
absolute_path = asset_file_path(path)
raise(Errno::ENOENT, "Asset file not found at '#{absolute_path}'" ) unless File.exist?(absolute_path)
return absolute_path
diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb
index dc4497581c..9951e11a37 100644
--- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb
+++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb
@@ -98,7 +98,7 @@ module ActionView
options[:schema_date] = "2005" # The Atom spec copyright date
end
- xml = options[:xml] || eval("xml", block.binding)
+ xml = options.delete(:xml) || eval("xml", block.binding)
xml.instruct!
if options[:instruct]
options[:instruct].each do |target,attrs|
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 2d1d19d5f3..72f162cb20 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -872,8 +872,8 @@ module ActionView
private
def add_default_name_and_id_for_value(tag_value, options)
- if tag_value
- pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase
+ unless tag_value.nil?
+ pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase
specified_id = options["id"]
add_default_name_and_id(options)
options["id"] += "_#{pretty_tag_value}" unless specified_id
@@ -932,6 +932,14 @@ module ActionView
attr_accessor :object_name, :object, :options
+ def self.model_name
+ @model_name ||= Struct.new(:partial_path).new(name.demodulize.underscore.sub!(/_builder$/, ''))
+ end
+
+ def to_model
+ self
+ end
+
def initialize(object_name, object, template, options, proc)
@nested_child_index = {}
@object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index e126b35e90..1d851ecbd7 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -79,6 +79,13 @@ module ActionView
# # <option>Paris</option><option>Rome</option></select>
def select_tag(name, option_tags = nil, options = {})
html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name
+ if blank = options.delete(:include_blank)
+ if blank.kind_of?(String)
+ option_tags = "<option value=\"\">#{blank}</option>" + option_tags
+ else
+ option_tags = "<option value=\"\"></option>" + option_tags
+ end
+ end
content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
end
@@ -263,7 +270,7 @@ module ActionView
escape = options.key?("escape") ? options.delete("escape") : true
content = html_escape(content) if escape
- content_tag :textarea, content, { "name" => name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
+ content_tag :textarea, content, { "name" => name, "id" => sanitize_to_id(name) }.update(options)
end
# Creates a check box form input tag.
diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb
index 999d5b34fc..897a7cc348 100644
--- a/actionpack/lib/action_view/helpers/number_helper.rb
+++ b/actionpack/lib/action_view/helpers/number_helper.rb
@@ -215,7 +215,7 @@ module ActionView
delimiter ||= (options[:delimiter] || defaults[:delimiter])
begin
- rounded_number = (Float(number) * (10 ** precision)).round.to_f / 10 ** precision
+ rounded_number = BigDecimal.new((Float(number) * (10 ** precision)).to_s).round.to_f / 10 ** precision
number_with_delimiter("%01.#{precision}f" % rounded_number,
:separator => separator,
:delimiter => delimiter)
diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb
index 66d7592874..ff5a2134ff 100644
--- a/actionpack/lib/action_view/helpers/tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/tag_helper.rb
@@ -8,7 +8,10 @@ module ActionView
module TagHelper
include ERB::Util
- BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked).to_set
+ BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked autobuffer
+ autoplay controls loop selected hidden scoped async
+ defer reversed ismap seemless muted required
+ autofocus novalidate formnovalidate open).to_set
BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map {|attr| attr.to_sym })
# Returns an empty HTML tag of type +name+ which by default is XHTML
@@ -131,16 +134,14 @@ module ActionView
def tag_options(options, escape = true)
unless options.blank?
attrs = []
- if escape
- options.each_pair do |key, value|
- if BOOLEAN_ATTRIBUTES.include?(key)
- attrs << %(#{key}="#{key}") if value
- else
- attrs << %(#{key}="#{escape_once(value)}") if !value.nil?
- end
+ options.each_pair do |key, value|
+ if BOOLEAN_ATTRIBUTES.include?(key)
+ attrs << %(#{key}="#{key}") if value
+ elsif !value.nil?
+ final_value = value.is_a?(Array) ? value.join(" ") : value
+ final_value = escape_once(final_value) if escape
+ attrs << %(#{key}="#{final_value}")
end
- else
- attrs = options.map { |key, value| %(#{key}="#{value}") }
end
" #{attrs.sort * ' '}" unless attrs.empty?
end
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index c3ce4c671e..1d92bcb763 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -33,13 +33,15 @@ module ActionView
end
# Truncates a given +text+ after a given <tt>:length</tt> if +text+ is longer than <tt>:length</tt>
- # (defaults to 30). The last characters will be replaced with the <tt>:omission</tt> (defaults to "...").
+ # (defaults to 30). The last characters will be replaced with the <tt>:omission</tt> (defaults to "...")
+ # for a total length not exceeding <tt>:length</tt>.
+ #
# Pass a <tt>:separator</tt> to truncate +text+ at a natural break.
#
# ==== Examples
#
# truncate("Once upon a time in a world far far away")
- # # => Once upon a time in a world f...
+ # # => Once upon a time in a world...
#
# truncate("Once upon a time in a world far far away", :separator => ' ')
# # => Once upon a time in a world...
@@ -48,19 +50,19 @@ module ActionView
# # => Once upon a...
#
# truncate("And they found that many people were sleeping better.", :length => 25, "(clipped)")
- # # => And they found that many (clipped)
+ # # => And they found t(clipped)
#
- # truncate("And they found that many people were sleeping better.", :omission => "... (continued)", :length => 15)
- # # => And they found... (continued)
+ # truncate("And they found that many people were sleeping better.", :omission => "... (continued)", :length => 25)
+ # # => And they f... (continued)
#
# You can still use <tt>truncate</tt> with the old API that accepts the
# +length+ as its optional second and the +ellipsis+ as its
# optional third parameter:
# truncate("Once upon a time in a world far far away", 14)
- # # => Once upon a time in a world f...
+ # # => Once upon a...
#
- # truncate("And they found that many people were sleeping better.", 15, "... (continued)")
- # # => And they found... (continued)
+ # truncate("And they found that many people were sleeping better.", 25, "... (continued)")
+ # # => And they f... (continued)
def truncate(text, *args)
options = args.extract_options!
unless args.empty?
@@ -239,12 +241,20 @@ module ActionView
#
# textilize("Visit the Rails website "here":http://www.rubyonrails.org/.)
# # => "<p>Visit the Rails website <a href="http://www.rubyonrails.org/">here</a>.</p>"
- def textilize(text)
+ #
+ # textilize("This is worded <strong>strongly</strong>")
+ # # => "<p>This is worded <strong>strongly</strong></p>"
+ #
+ # textilize("This is worded <strong>strongly</strong>", :filter_html)
+ # # => "<p>This is worded &lt;strong&gt;strongly&lt;/strong&gt;</p>"
+ #
+ def textilize(text, *options)
+ options ||= [:hard_breaks]
+
if text.blank?
""
else
- textilized = RedCloth.new(text, [ :hard_breaks ])
- textilized.hard_breaks = true if textilized.respond_to?(:hard_breaks=)
+ textilized = RedCloth.new(text, options)
textilized.to_html
end
end
diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb
index 074b475819..4001757a9b 100644
--- a/actionpack/lib/action_view/paths.rb
+++ b/actionpack/lib/action_view/paths.rb
@@ -33,12 +33,12 @@ module ActionView #:nodoc:
super(*objs.map { |obj| self.class.type_cast(obj) })
end
- def find_by_parts(path, details = {}, prefix = nil, partial = false)
+ def find(path, details = {}, prefix = nil, partial = false)
# template_path = path.sub(/^\//, '')
template_path = path
each do |load_path|
- if template = load_path.find_by_parts(template_path, details, prefix, partial)
+ if template = load_path.find(template_path, details, prefix, partial)
return template
end
end
@@ -48,11 +48,11 @@ module ActionView #:nodoc:
raise ActionView::MissingTemplate.new(self, "#{prefix}/#{path} - #{details.inspect} - partial: #{!!partial}")
end
- def find_by_parts?(path, extension = nil, prefix = nil, partial = false)
+ def exists?(path, extension = nil, prefix = nil, partial = false)
template_path = path.sub(/^\//, '')
each do |load_path|
- return true if template = load_path.find_by_parts(template_path, extension, prefix, partial)
+ return true if template = load_path.find(template_path, extension, prefix, partial)
end
false
end
@@ -62,7 +62,7 @@ module ActionView #:nodoc:
template_path = original_template_path.sub(/^\//, '')
each do |load_path|
- if template = load_path.find_by_parts(template_path, format)
+ if template = load_path.find(template_path, format)
return template
# Try to find html version if the format is javascript
elsif format == :js && html_fallback && template = load_path["#{template_path}.#{I18n.locale}.html"]
diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb
index ccb14d513a..64f08c447d 100644
--- a/actionpack/lib/action_view/render/partials.rb
+++ b/actionpack/lib/action_view/render/partials.rb
@@ -170,133 +170,117 @@ module ActionView
# <%- end -%>
# <% end %>
module Partials
- extend ActiveSupport::Memoizable
extend ActiveSupport::Concern
-
- included do
- attr_accessor :_partial
- end
- def _render_partial_from_controller(*args)
- @assigns_added = false
- _render_partial(*args)
- end
-
- def _render_partial(options = {}) #:nodoc:
- options[:locals] ||= {}
+ class PartialRenderer
+ def self.partial_names
+ @partial_names ||= Hash.new {|h,k| h[k] = ActiveSupport::ConcurrentHash.new }
+ end
- case path = partial = options[:partial]
- when *_array_like_objects
- return _render_partial_collection(partial, options)
- else
- if partial.is_a?(ActionView::Helpers::FormBuilder)
- path = partial.class.to_s.demodulize.underscore.sub(/_builder$/, '')
- options[:locals].merge!(path.to_sym => partial)
- elsif !partial.is_a?(String)
- options[:object] = object = partial
- path = ActionController::RecordIdentifier.partial_path(object, controller_path)
- end
- _, _, prefix, object = parts = partial_parts(path, options)
- parts[1] = {:formats => parts[1]}
- template = find_by_parts(*parts)
- _render_partial_object(template, options, (object unless object == true))
+ def self.formats
+ @formats ||= Hash.new {|h,k| h[k] = Hash.new{|h,k| h[k] = Hash.new {|h,k| h[k] = {}}}}
end
- end
- private
- def partial_parts(name, options)
- segments = name.split("/")
- parts = segments.pop.split(".")
+ def initialize(view_context, options, block)
+ partial = options[:partial]
- case parts.size
- when 1
- parts
- when 2, 3
- extension = parts.delete_at(1).to_sym
- if formats.include?(extension)
- self.formats.replace [extension]
- end
- parts.pop if parts.size == 2
- end
+ @view = view_context
+ @options = options
+ @locals = options[:locals] || {}
+ @block = block
+
+ # Set up some instance variables to speed up memoizing
+ @partial_names = self.class.partial_names[@view.controller.class]
+ @templates = self.class.formats
+ @format = view_context.formats
- path = parts.join(".")
- prefix = segments[0..-1].join("/")
- prefix = prefix.blank? ? controller_path : prefix
- parts = [path, formats, prefix]
- parts.push options[:object] || true
+ # Set up the object and path
+ @object = partial.is_a?(String) ? options[:object] : partial
+ @path = partial_path(partial)
end
- def _render_partial_with_block(layout, block, options)
- @_proc_for_layout = block
- concat(_render_partial(options.merge(:partial => layout)))
- ensure
- @_proc_for_layout = nil
+ def render
+ return render_collection if collection
+
+ template = find_template
+ render_template(template, @object || @locals[template.variable_name])
end
- def _render_partial_with_layout(layout, options)
- if layout
- prefix = controller && !layout.include?("/") ? controller.controller_path : nil
- layout = find_by_parts(layout, {:formats => formats}, prefix, true)
+ def render_collection
+ # Even if no template is rendered, this will ensure that the MIME type
+ # for the empty response is the same as the provided template
+ @options[:_template] = default_template = find_template
+
+ return nil if collection.blank?
+
+ if @options.key?(:spacer_template)
+ spacer = find_template(@options[:spacer_template]).render(@view, @locals)
end
- content = _render_partial(options)
- return _render_content_with_layout(content, layout, options[:locals])
- end
- def _array_like_objects
- array_like = [Array]
- if defined?(ActiveRecord)
- array_like.push(ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope)
+ segments = []
+
+ collection.each_with_index do |object, index|
+ template = default_template || find_template(partial_path(object))
+ @locals[template.counter_name] = index
+ segments << render_template(template, object)
end
- array_like
+
+ segments.join(spacer)
end
- def _render_partial_object(template, options, object = nil)
- if options.key?(:collection)
- _render_partial_collection(options.delete(:collection), options, template)
- else
- locals = (options[:locals] ||= {})
- object ||= locals[:object] || locals[template.variable_name]
-
- _set_locals(object, locals, template, options)
-
- options[:_template] = template
-
- _render_template(template, locals)
+ def render_template(template, object = @object)
+ @options[:_template] ||= template
+
+ # TODO: is locals[:object] really necessary?
+ @locals[:object] = @locals[template.variable_name] = object
+ @locals[@options[:as]] = object if @options[:as]
+
+ content = @view._render_single_template(template, @locals, &@block)
+ return content if @block || !@options[:layout]
+ find_template(@options[:layout]).render(@view, @locals) { content }
+ end
+
+
+ private
+ def collection
+ @collection ||= if @object.respond_to?(:to_ary)
+ @object
+ elsif @options.key?(:collection)
+ @options[:collection] || []
end
end
- def _set_locals(object, locals, template, options)
- locals[:object] = locals[template.variable_name] = object
- locals[options[:as]] = object if options[:as]
+ def find_template(path = @path)
+ return if !path
+ @templates[path][@view.controller_path][@format][I18n.locale] ||= begin
+ prefix = @view.controller.controller_path unless path.include?(?/)
+ @view.find(path, {:formats => @view.formats}, prefix, true)
+ end
end
- def _render_partial_collection(collection, options = {}, passed_template = nil) #:nodoc:
- return nil if collection.blank?
-
- spacer = options[:spacer_template] ? _render_partial(:partial => options[:spacer_template]) : ''
+ def partial_path(object = @object)
+ return object if object.is_a?(String)
+ @partial_names[object.class] ||= begin
+ return nil unless object.respond_to?(:to_model)
- locals = (options[:locals] ||= {})
- index, @_partial_path = 0, nil
- collection.map do |object|
- options[:_template] = template = passed_template || begin
- _partial_path =
- ActionController::RecordIdentifier.partial_path(object, controller_path)
- template = _pick_partial_template(_partial_path)
+ object.to_model.class.model_name.partial_path.dup.tap do |partial|
+ path = @view.controller_path
+ partial.insert(0, "#{File.dirname(path)}/") if path.include?(?/)
end
-
- _set_locals(object, locals, template, options)
- locals[template.counter_name] = index
-
- index += 1
-
- _render_template(template, locals)
- end.join(spacer)
+ end
end
+ end
+
+ def render_partial(options)
+ @assigns_added = false
+ # TODO: Handle other details here.
+ self.formats = options[:_details][:formats] if options[:_details]
+ _render_partial(options)
+ end
+
+ def _render_partial(options, &block) #:nodoc:
+ PartialRenderer.new(self, options, block).render
+ end
- def _pick_partial_template(partial_path) #:nodoc:
- prefix = controller_path unless partial_path.include?('/')
- find_by_parts(partial_path, {:formats => formats}, prefix, true)
- end
- memoize :_pick_partial_template
end
end
diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb
index 162e38c484..c7afc56e3b 100644
--- a/actionpack/lib/action_view/render/rendering.rb
+++ b/actionpack/lib/action_view/render/rendering.rb
@@ -10,24 +10,24 @@ module ActionView
#
# If no options hash is passed or :update specified, the default is to render a partial and use the second parameter
# as the locals hash.
- def render(options = {}, local_assigns = {}, &block) #:nodoc:
- local_assigns ||= {}
-
- @exempt_from_layout = true
-
+ def render(options = {}, locals = {}, &block) #:nodoc:
case options
+ when String, NilClass
+ _render_partial(:partial => options, :locals => locals || {})
when Hash
- options[:locals] ||= {}
layout = options[:layout]
-
- return _render_partial_with_layout(layout, options) if options.key?(:partial)
- return _render_partial_with_block(layout, block, options) if block_given?
-
- layout = find_by_parts(layout, {:formats => formats}) if layout
-
+
+ if block_given?
+ return concat(_render_partial(options.merge(:partial => layout), &block))
+ elsif options.key?(:partial)
+ return _render_partial(options)
+ end
+
+ layout = find(layout, {:formats => formats}) if layout
+
if file = options[:file]
- template = find_by_parts(file, {:formats => formats})
- _render_template_with_layout(template, layout, :locals => options[:locals])
+ template = find(file, {:formats => formats})
+ _render_template(template, layout, :locals => options[:locals] || {})
elsif inline = options[:inline]
_render_inline(inline, layout, options)
elsif text = options[:text]
@@ -35,35 +35,33 @@ module ActionView
end
when :update
update_page(&block)
- when String, NilClass
- _render_partial(:partial => options, :locals => local_assigns)
end
end
-
- def _render_content_with_layout(content, layout, locals)
+
+ def _render_content(content, layout, locals)
return content unless layout
-
+
locals ||= {}
if controller && layout
@_layout = layout.identifier
logger.info("Rendering template within #{layout.identifier}") if logger
end
-
+
begin
old_content, @_content_for[:layout] = @_content_for[:layout], content
@cached_content_for_layout = @_content_for[:layout]
- _render_template(layout, locals)
+ _render_single_template(layout, locals)
ensure
@_content_for[:layout] = old_content
end
end
- # You can think of a layout as a method that is called with a block. This method
- # returns the block that the layout is called with. If the user calls yield :some_name,
- # the block, by default, returns content_for(:some_name). If the user calls yield,
- # the default block returns content_for(:layout).
+ # You can think of a layout as a method that is called with a block. _layout_for
+ # returns the contents that are yielded to the layout. If the user calls yield
+ # :some_name, the block, by default, returns content_for(:some_name). If the user
+ # calls yield, the default block returns content_for(:layout).
#
# The user can override this default by passing a block to the layout.
#
@@ -92,15 +90,28 @@ module ActionView
# In this case, the layout would receive the block passed into <tt>render :layout</tt>,
# and the Struct specified in the layout would be passed into the block. The result
# would be <html>Hello David</html>.
- def layout_proc(name)
- @_default_layout ||= proc { |*names| @_content_for[names.first || :layout] }
- !@_content_for.key?(name) && @_proc_for_layout || @_default_layout
+ def _layout_for(names, &block)
+ with_output_buffer do
+ # This is due to the potentially ambiguous use of yield when
+ # a block is passed in to a template *and* there is a content_for()
+ # of the same name. Suggested solution: require explicit use of content_for
+ # in these ambiguous cases.
+ #
+ # We would be able to continue supporting yield in all non-ambiguous
+ # cases. Question: should we deprecate yield in favor of content_for
+ # and reserve yield for cases where there is a yield into a real block?
+ if @_content_for.key?(names.first) || !block_given?
+ return @_content_for[names.first || :layout]
+ else
+ return yield(names)
+ end
+ end
end
- def _render_template(template, local_assigns = {})
+ def _render_single_template(template, locals = {}, &block)
with_template(template) do
- template.render(self, local_assigns) do |*names|
- capture(*names, &layout_proc(names.first))
+ template.render(self, locals) do |*names|
+ _layout_for(names, &block)
end
end
rescue Exception => e
@@ -115,32 +126,42 @@ module ActionView
def _render_inline(inline, layout, options)
handler = Template.handler_class_for_extension(options[:type] || "erb")
template = Template.new(options[:inline], "inline #{options[:inline].inspect}", handler, {})
- content = _render_template(template, options[:locals] || {})
- layout ? _render_content_with_layout(content, layout, options[:locals]) : content
+ content = _render_single_template(template, options[:locals] || {})
+ layout ? _render_content(content, layout, options[:locals]) : content
end
def _render_text(text, layout, options)
- layout ? _render_content_with_layout(text, layout, options[:locals]) : text
+ layout ? _render_content(text, layout, options[:locals]) : text
end
- def _render_template_from_controller(*args)
+ # This is the API to render a ViewContext's template from a controller.
+ #
+ # Internal Options:
+ # _template:: The Template object to render
+ # _layout:: The layout, if any, to wrap the Template in
+ # _partial:: true if the template is a partial
+ def render_template(options)
@assigns_added = nil
- _render_template_with_layout(*args)
+ template, layout, partial = options.values_at(:_template, :_layout, :_partial)
+ _render_template(template, layout, options, partial)
end
- def _render_template_with_layout(template, layout = nil, options = {}, partial = false)
- logger && logger.info("Rendering #{template.identifier}#{' (#{options[:status]})' if options[:status]}")
+ def _render_template(template, layout = nil, options = {}, partial = nil)
+ logger && logger.info do
+ msg = "Rendering #{template.identifier}"
+ msg << " (#{options[:status]})" if options[:status]
+ msg
+ end
locals = options[:locals] || {}
content = if partial
- object = partial unless partial == true
- _render_partial_object(template, options, object)
+ _render_partial_object(template, options)
else
- _render_template(template, locals)
+ _render_single_template(template, locals)
end
-
- layout ? _render_content_with_layout(content, layout, locals) : content
+
+ _render_content(content, layout, locals)
end
end
end \ No newline at end of file
diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb
index d15f53a11b..ebfc6cc8ce 100644
--- a/actionpack/lib/action_view/template/resolver.rb
+++ b/actionpack/lib/action_view/template/resolver.rb
@@ -10,7 +10,7 @@ module ActionView
end
# Normalizes the arguments and passes it on to find_template
- def find_by_parts(*args)
+ def find(*args)
find_all_by_parts(*args).first
end
diff --git a/actionpack/lib/action_view/template/template.rb b/actionpack/lib/action_view/template/template.rb
index 4145045e2d..33d3f79ad3 100644
--- a/actionpack/lib/action_view/template/template.rb
+++ b/actionpack/lib/action_view/template/template.rb
@@ -7,19 +7,22 @@ require "action_view/template/resolver"
module ActionView
class Template
extend TemplateHandlers
- attr_reader :source, :identifier, :handler, :mime_type, :details
+ attr_reader :source, :identifier, :handler, :mime_type, :formats, :details
def initialize(source, identifier, handler, details)
@source = source
@identifier = identifier
@handler = handler
@details = details
+ @method_names = {}
format = details.delete(:format) || begin
# TODO: Clean this up
handler.respond_to?(:default_format) ? handler.default_format.to_sym.to_s : "html"
end
@mime_type = Mime::Type.lookup_by_extension(format.to_s)
+ @formats = [format.to_sym]
+ @formats << :html if format == :js
@details[:formats] = Array.wrap(format.to_sym)
end
@@ -30,12 +33,12 @@ module ActionView
# TODO: Figure out how to abstract this
def variable_name
- identifier[%r'_?(\w+)(\.\w+)*$', 1].to_sym
+ @variable_name ||= identifier[%r'_?(\w+)(\.\w+)*$', 1].to_sym
end
# TODO: Figure out how to abstract this
def counter_name
- "#{variable_name}_counter".to_sym
+ @counter_name ||= "#{variable_name}_counter".to_sym
end
# TODO: kill hax
@@ -90,7 +93,8 @@ module ActionView
def build_method_name(locals)
# TODO: is locals.keys.hash reliably the same?
- "_render_template_#{@identifier.hash}_#{__id__}_#{locals.keys.hash}".gsub('-', "_")
+ @method_names[locals.keys.hash] ||=
+ "_render_template_#{@identifier.hash}_#{__id__}_#{locals.keys.hash}".gsub('-', "_")
end
end
end
diff --git a/actionpack/lib/action_view/template/text.rb b/actionpack/lib/action_view/template/text.rb
index 81944ff546..9f12e5e0a8 100644
--- a/actionpack/lib/action_view/template/text.rb
+++ b/actionpack/lib/action_view/template/text.rb
@@ -15,7 +15,9 @@ module ActionView #:nodoc:
def render(*) self end
def mime_type() @content_type end
-
+
+ def formats() [mime_type] end
+
def partial?() false end
end
end
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index 3f3951509a..e51744d095 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -9,14 +9,14 @@ module ActionView
end
attr_internal :rendered
- alias_method :_render_template_without_template_tracking, :_render_template
- def _render_template(template, local_assigns = {})
+ alias_method :_render_template_without_template_tracking, :_render_single_template
+ def _render_single_template(template, local_assigns, &block)
if template.respond_to?(:identifier) && template.present?
@_rendered[:partials][template] += 1 if template.partial?
@_rendered[:template] ||= []
@_rendered[:template] << template
end
- _render_template_without_template_tracking(template, local_assigns)
+ _render_template_without_template_tracking(template, local_assigns, &block)
end
end