aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib')
-rw-r--r--actionpack/lib/action_view.rb8
-rw-r--r--actionpack/lib/action_view/base.rb9
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb2
-rw-r--r--actionpack/lib/action_view/render/layouts.rb83
-rw-r--r--actionpack/lib/action_view/render/partials.rb177
-rw-r--r--actionpack/lib/action_view/render/rendering.rb101
-rw-r--r--actionpack/lib/action_view/renderer/abstract_renderer.rb36
-rw-r--r--actionpack/lib/action_view/renderer/partial_renderer.rb167
-rw-r--r--actionpack/lib/action_view/renderer/template_renderer.rb66
-rw-r--r--actionpack/lib/action_view/template.rb5
10 files changed, 348 insertions, 306 deletions
diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb
index 5f9dc70766..ad96f6c66d 100644
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -38,11 +38,17 @@ module ActionView
autoload :Helpers
autoload :Base
autoload :LookupContext
+ autoload :Render
autoload :PathSet, "action_view/paths"
autoload :TestCase, "action_view/test_case"
+ autoload_under "renderer" do
+ autoload :AbstractRenderer
+ autoload :PartialRenderer
+ autoload :TemplateRenderer
+ end
+
autoload_under "render" do
- autoload :Layouts
autoload :Partials
autoload :Rendering
end
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 0bef3e3a08..2a601c7cee 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -158,7 +158,7 @@ module ActionView #:nodoc:
module Subclasses
end
- include Helpers, Rendering, Partials, Layouts, ::ERB::Util, Context
+ include Helpers, Rendering, Partials, ::ERB::Util, Context
# Specify whether RJS responses should be wrapped in a try/catch block
# that alert()s the caught exception (and then re-raises it).
@@ -180,8 +180,7 @@ module ActionView #:nodoc:
attr_accessor :base_path, :assigns, :template_extension, :lookup_context
attr_internal :captures, :request, :controller, :template, :config
- delegate :find_template, :template_exists?, :formats, :formats=, :locale, :locale=,
- :view_paths, :view_paths=, :with_fallbacks, :update_details, :with_layout_format, :to => :lookup_context
+ delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, :to => :lookup_context
delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers,
:flash, :action_name, :controller_name, :to => :controller
@@ -220,6 +219,10 @@ module ActionView #:nodoc:
@lookup_context.formats = formats if formats
end
+ def store_content_for(key, value)
+ @_content_for[key] = value
+ end
+
def controller_path
@controller_path ||= controller && controller.controller_path
end
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
index 300207dfac..41cd8d5171 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -546,7 +546,7 @@ module ActionView
end
def with_formats(*args)
- @context ? @context.update_details(:formats => args) { yield } : yield
+ @context ? @context.lookup_context.update_details(:formats => args) { yield } : yield
end
def javascript_object_for(object)
diff --git a/actionpack/lib/action_view/render/layouts.rb b/actionpack/lib/action_view/render/layouts.rb
deleted file mode 100644
index 08162f7fd5..0000000000
--- a/actionpack/lib/action_view/render/layouts.rb
+++ /dev/null
@@ -1,83 +0,0 @@
-module ActionView
- # = Action View Layouts
- module Layouts
- # Returns the contents that are yielded to a layout, given a name or a block.
- #
- # You can think of a layout as a method that is called with a block. If the user calls
- # <tt>yield :some_name</tt>, the block, by default, returns <tt>content_for(:some_name)</tt>.
- # If the user calls simply +yield+, the default block returns <tt>content_for(:layout)</tt>.
- #
- # The user can override this default by passing a block to the layout:
- #
- # # The template
- # <%= render :layout => "my_layout" do %>
- # Content
- # <% end %>
- #
- # # The layout
- # <html>
- # <%= yield %>
- # </html>
- #
- # In this case, instead of the default block, which would return <tt>content_for(:layout)</tt>,
- # this method returns the block that was passed in to <tt>render :layout</tt>, and the response
- # would be
- #
- # <html>
- # Content
- # </html>
- #
- # Finally, the block can take block arguments, which can be passed in by +yield+:
- #
- # # The template
- # <%= render :layout => "my_layout" do |customer| %>
- # Hello <%= customer.name %>
- # <% end %>
- #
- # # The layout
- # <html>
- # <%= yield Struct.new(:name).new("David") %>
- # </html>
- #
- # In this case, the layout would receive the block passed into <tt>render :layout</tt>,
- # and the struct specified would be passed into the block as an argument. The result
- # would be
- #
- # <html>
- # Hello David
- # </html>
- #
- def _layout_for(*args, &block) #:nodoc:
- name = args.first
-
- if name.is_a?(Symbol)
- @_content_for[name].html_safe
- elsif block
- capture(*args, &block)
- else
- @_content_for[:layout].html_safe
- end
- end
-
- # This is the method which actually finds the layout using details in the lookup
- # context object. If no layout is found, it checks if at least a layout with
- # the given name exists across all details before raising the error.
- def find_layout(layout, keys)
- begin
- with_layout_format do
- layout =~ /^\// ?
- with_fallbacks { find_template(layout, nil, false, keys) } : find_template(layout, nil, false, keys)
- end
- rescue ActionView::MissingTemplate => e
- update_details(:formats => nil) do
- raise unless template_exists?(layout)
- end
- end
- end
-
- # Contains the logic that actually renders the layout.
- def _render_layout(layout, locals, &block) #:nodoc:
- layout.render(self, locals){ |*name| _layout_for(*name, &block) }
- end
- end
-end
diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb
index 4e03d43358..844c3e9572 100644
--- a/actionpack/lib/action_view/render/partials.rb
+++ b/actionpack/lib/action_view/render/partials.rb
@@ -210,181 +210,12 @@ module ActionView
# <%- end -%>
# <% end %>
module Partials
- extend ActiveSupport::Concern
-
- class PartialRenderer
- PARTIAL_NAMES = Hash.new {|h,k| h[k] = {} }
-
- def initialize(view_context, options, block)
- @view = view_context
- @partial_names = PARTIAL_NAMES[@view.controller.class.name]
- setup(options, block)
- end
-
- def setup(options, block)
- partial = options[:partial]
-
- @options = options
- @locals = options[:locals] || {}
- @block = block
-
- if String === partial
- @object = options[:object]
- @path = partial
- @collection = collection
- else
- @object = partial
-
- if @collection = collection_from_object || collection
- paths = @collection_data = @collection.map { |o| partial_path(o) }
- @path = paths.uniq.size == 1 ? paths.first : nil
- else
- @path = partial_path
- end
- end
-
- if @path
- @variable, @variable_counter = retrieve_variable(@path)
- else
- paths.map! { |path| retrieve_variable(path).unshift(path) }
- end
- end
-
- def retrieve_variable(path)
- variable = @options[:as] || path[%r'_?(\w+)(\.\w+)*$', 1].to_sym
- variable_counter = :"#{variable}_counter" if @collection
- [variable, variable_counter]
- end
-
- def render
- identifier = ((@template = find_partial) ? @template.identifier : @path)
-
- if @collection
- ActiveSupport::Notifications.instrument("render_collection.action_view",
- :identifier => identifier || "collection", :count => @collection.size) do
- render_collection
- end
- else
- if !@block && (layout = @options[:layout])
- layout = find_template(layout)
- end
-
- content = ActiveSupport::Notifications.instrument("render_partial.action_view",
- :identifier => identifier) do
- render_partial
- end
-
- content = @view._render_layout(layout, @locals){ content } if layout
- content
- end
- end
-
- def render_collection
- return nil if @collection.blank?
-
- if @options.key?(:spacer_template)
- spacer = find_template(@options[:spacer_template]).render(@view, @locals)
- end
-
- result = @template ? collection_with_template : collection_without_template
- result.join(spacer).html_safe
- end
-
- def collection_with_template
- segments, locals, template = [], @locals, @template
- as, counter = @variable, @variable_counter
-
- locals[counter] = -1
-
- @collection.each do |object|
- locals[counter] += 1
- locals[as] = object
- segments << template.render(@view, locals)
- end
-
- segments
- end
-
- def collection_without_template
- segments, locals, collection_data = [], @locals, @collection_data
- index, template, cache = -1, nil, {}
- keys = @locals.keys
-
- @collection.each_with_index do |object, i|
- path, *data = collection_data[i]
- template = (cache[path] ||= find_template(path, keys + data))
- locals[data[0]] = object
- locals[data[1]] = (index += 1)
- segments << template.render(@view, locals)
- end
-
- @template = template
- segments
- end
-
- def render_partial
- locals, view = @locals, @view
- object, as = @object, @variable
-
- object ||= locals[as]
- locals[as] = object
-
- @template.render(view, locals) do |*name|
- view._layout_for(*name, &@block)
- end
- end
-
- private
-
- def collection
- if @options.key?(:collection)
- @options[:collection] || []
- end
- end
-
- def collection_from_object
- if @object.respond_to?(:to_ary)
- @object
- end
- end
-
- def find_partial
- if path = @path
- locals = @locals.keys
- locals << @variable
- locals << @variable_counter if @collection
- find_template(path, locals)
- end
- end
-
- def find_template(path=@path, locals=@locals.keys)
- prefix = @view.controller_path unless path.include?(?/)
- @view.find_template(path, prefix, true, locals)
- end
-
- def partial_path(object = @object)
- @partial_names[object.class.name] ||= begin
- object = object.to_model if object.respond_to?(:to_model)
-
- object.class.model_name.partial_path.dup.tap do |partial|
- path = @view.controller_path
- partial.insert(0, "#{File.dirname(path)}/") if partial.include?(?/) && path.include?(?/)
- end
- end
- end
- end
-
def _render_partial(options, &block) #:nodoc:
- _wrap_formats(options[:partial]) do
- if defined?(@renderer)
- @renderer.setup(options, block)
- else
- @renderer = PartialRenderer.new(self, options, block)
- end
-
- @renderer.render
- end
+ _partial_renderer.setup(options, block).render
end
+ def _partial_renderer #:nodoc:
+ @_partial_renderer ||= PartialRenderer.new(self)
+ end
end
end
diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb
index 8e599c71df..adbb6bc626 100644
--- a/actionpack/lib/action_view/render/rendering.rb
+++ b/actionpack/lib/action_view/render/rendering.rb
@@ -21,11 +21,7 @@ module ActionView
elsif options.key?(:partial)
_render_partial(options)
else
- _wrap_formats(options[:template] || options[:file]) do
- template = _determine_template(options)
- lookup_context.freeze_formats(template.formats, true)
- _render_template(template, options[:layout], options)
- end
+ _render_template(options)
end
when :update
update_page(&block)
@@ -34,51 +30,70 @@ module ActionView
end
end
- # Checks if the given path contains a format and if so, change
- # the lookup context to take this new format into account.
- def _wrap_formats(value)
- return yield unless value.is_a?(String)
- @@formats_regexp ||= /\.(#{Mime::SET.symbols.join('|')})$/
+ # Returns the contents that are yielded to a layout, given a name or a block.
+ #
+ # You can think of a layout as a method that is called with a block. If the user calls
+ # <tt>yield :some_name</tt>, the block, by default, returns <tt>content_for(:some_name)</tt>.
+ # If the user calls simply +yield+, the default block returns <tt>content_for(:layout)</tt>.
+ #
+ # The user can override this default by passing a block to the layout:
+ #
+ # # The template
+ # <%= render :layout => "my_layout" do %>
+ # Content
+ # <% end %>
+ #
+ # # The layout
+ # <html>
+ # <%= yield %>
+ # </html>
+ #
+ # In this case, instead of the default block, which would return <tt>content_for(:layout)</tt>,
+ # this method returns the block that was passed in to <tt>render :layout</tt>, and the response
+ # would be
+ #
+ # <html>
+ # Content
+ # </html>
+ #
+ # Finally, the block can take block arguments, which can be passed in by +yield+:
+ #
+ # # The template
+ # <%= render :layout => "my_layout" do |customer| %>
+ # Hello <%= customer.name %>
+ # <% end %>
+ #
+ # # The layout
+ # <html>
+ # <%= yield Struct.new(:name).new("David") %>
+ # </html>
+ #
+ # In this case, the layout would receive the block passed into <tt>render :layout</tt>,
+ # and the struct specified would be passed into the block as an argument. The result
+ # would be
+ #
+ # <html>
+ # Hello David
+ # </html>
+ #
+ def _layout_for(*args, &block)
+ name = args.first
- if value.sub!(@@formats_regexp, "")
- update_details(:formats => [$1.to_sym]){ yield }
+ if name.is_a?(Symbol)
+ @_content_for[name].html_safe
+ elsif block
+ capture(*args, &block)
else
- yield
+ @_content_for[:layout].html_safe
end
end
- # Determine the template to be rendered using the given options.
- def _determine_template(options) #:nodoc:
- keys = (options[:locals] ||= {}).keys
-
- if options.key?(:inline)
- handler = Template.handler_class_for_extension(options[:type] || "erb")
- Template.new(options[:inline], "inline template", handler, { :locals => keys })
- elsif options.key?(:text)
- Template::Text.new(options[:text], formats.try(:first))
- elsif options.key?(:file)
- with_fallbacks { find_template(options[:file], options[:prefix], false, keys) }
- elsif options.key?(:template)
- options[:template].respond_to?(:render) ?
- options[:template] : find_template(options[:template], options[:prefix], false, keys)
- end
+ def _render_template(options) #:nodoc:
+ _template_renderer.render(options)
end
- # Renders the given template. An string representing the layout can be
- # supplied as well.
- def _render_template(template, layout = nil, options = {}) #:nodoc:
- locals = options[:locals] || {}
- layout = find_layout(layout, locals.keys) if layout
-
- ActiveSupport::Notifications.instrument("render_template.action_view",
- :identifier => template.identifier, :layout => layout.try(:virtual_path)) do
-
- content = template.render(self, locals) { |*name| _layout_for(*name) }
- @_content_for[:layout] = content if layout
-
- content = _render_layout(layout, locals) if layout
- content
- end
+ def _template_renderer #:nodoc:
+ @_template_renderer ||= TemplateRenderer.new(self)
end
end
end
diff --git a/actionpack/lib/action_view/renderer/abstract_renderer.rb b/actionpack/lib/action_view/renderer/abstract_renderer.rb
new file mode 100644
index 0000000000..f9fa63ce7f
--- /dev/null
+++ b/actionpack/lib/action_view/renderer/abstract_renderer.rb
@@ -0,0 +1,36 @@
+module ActionView
+ class AbstractRenderer #:nodoc:
+ attr_reader :vew, :lookup_context
+
+ delegate :find_template, :template_exists?, :with_fallbacks, :update_details,
+ :with_layout_format, :formats, :to => :lookup_context
+
+ def initialize(view)
+ @view = view
+ @lookup_context = view.lookup_context
+ end
+
+ def render
+ raise NotImplementedError
+ end
+
+ # Contains the logic that actually renders the layout.
+ def render_layout(layout, locals, &block) #:nodoc:
+ view = @view
+ layout.render(view, locals){ |*name| view._layout_for(*name, &block) }
+ end
+
+ # Checks if the given path contains a format and if so, change
+ # the lookup context to take this new format into account.
+ def wrap_formats(value)
+ return yield unless value.is_a?(String)
+ @@formats_regexp ||= /\.(#{Mime::SET.symbols.join('|')})$/
+
+ if value.sub!(@@formats_regexp, "")
+ update_details(:formats => [$1.to_sym]){ yield }
+ else
+ yield
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb
new file mode 100644
index 0000000000..3be1702f9e
--- /dev/null
+++ b/actionpack/lib/action_view/renderer/partial_renderer.rb
@@ -0,0 +1,167 @@
+require 'action_view/renderer/abstract_renderer'
+
+module ActionView
+ class PartialRenderer < AbstractRenderer #:nodoc:
+ N = ::ActiveSupport::Notifications
+ PARTIAL_NAMES = Hash.new {|h,k| h[k] = {} }
+
+ def initialize(view)
+ super
+ @partial_names = PARTIAL_NAMES[@view.controller.class.name]
+ end
+
+ def setup(options, block)
+ partial = options[:partial]
+
+ @options = options
+ @locals = options[:locals] || {}
+ @block = block
+
+ if String === partial
+ @object = options[:object]
+ @path = partial
+ @collection = collection
+ else
+ @object = partial
+
+ if @collection = collection_from_object || collection
+ paths = @collection_data = @collection.map { |o| partial_path(o) }
+ @path = paths.uniq.size == 1 ? paths.first : nil
+ else
+ @path = partial_path
+ end
+ end
+
+ if @path
+ @variable, @variable_counter = retrieve_variable(@path)
+ else
+ paths.map! { |path| retrieve_variable(path).unshift(path) }
+ end
+
+ self
+ end
+
+ def render
+ wrap_formats(@path) do
+ identifier = ((@template = find_partial) ? @template.identifier : @path)
+
+ if @collection
+ N.instrument("render_collection.action_view", :identifier => identifier || "collection", :count => @collection.size) do
+ render_collection
+ end
+ else
+ N.instrument("render_partial.action_view", :identifier => identifier) do
+ render_partial
+ end
+ end
+ end
+ end
+
+ def render_collection
+ return nil if @collection.blank?
+
+ if @options.key?(:spacer_template)
+ spacer = find_template(@options[:spacer_template]).render(@view, @locals)
+ end
+
+ result = @template ? collection_with_template : collection_without_template
+ result.join(spacer).html_safe
+ end
+
+ def render_partial
+ locals, view, block = @locals, @view, @block
+ object, as = @object, @variable
+
+ if !block && (layout = @options[:layout])
+ layout = find_template(layout)
+ end
+
+ object ||= locals[as]
+ locals[as] = object
+
+ content = @template.render(view, locals) do |*name|
+ view._layout_for(*name, &block)
+ end
+
+ content = render_layout(layout, locals){ content } if layout
+ content
+ end
+
+ private
+
+ def collection
+ if @options.key?(:collection)
+ @options[:collection] || []
+ end
+ end
+
+ def collection_from_object
+ if @object.respond_to?(:to_ary)
+ @object
+ end
+ end
+
+ def find_partial
+ if path = @path
+ locals = @locals.keys
+ locals << @variable
+ locals << @variable_counter if @collection
+ find_template(path, locals)
+ end
+ end
+
+ def find_template(path=@path, locals=@locals.keys)
+ prefix = @view.controller_path unless path.include?(?/)
+ @lookup_context.find_template(path, prefix, true, locals)
+ end
+
+ def collection_with_template
+ segments, locals, template = [], @locals, @template
+ as, counter = @variable, @variable_counter
+
+ locals[counter] = -1
+
+ @collection.each do |object|
+ locals[counter] += 1
+ locals[as] = object
+ segments << template.render(@view, locals)
+ end
+
+ segments
+ end
+
+ def collection_without_template
+ segments, locals, collection_data = [], @locals, @collection_data
+ index, template, cache = -1, nil, {}
+ keys = @locals.keys
+
+ @collection.each_with_index do |object, i|
+ path, *data = collection_data[i]
+ template = (cache[path] ||= find_template(path, keys + data))
+ locals[data[0]] = object
+ locals[data[1]] = (index += 1)
+ segments << template.render(@view, locals)
+ end
+
+ @template = template
+ segments
+ end
+
+ def partial_path(object = @object)
+ @partial_names[object.class.name] ||= begin
+ object = object.to_model if object.respond_to?(:to_model)
+
+ object.class.model_name.partial_path.dup.tap do |partial|
+ path = @view.controller_path
+ partial.insert(0, "#{File.dirname(path)}/") if partial.include?(?/) && path.include?(?/)
+ end
+ end
+ end
+
+ def retrieve_variable(path)
+ variable = @options[:as] || path[%r'_?(\w+)(\.\w+)*$', 1].to_sym
+ variable_counter = :"#{variable}_counter" if @collection
+ [variable, variable_counter]
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb
new file mode 100644
index 0000000000..3acc68dcac
--- /dev/null
+++ b/actionpack/lib/action_view/renderer/template_renderer.rb
@@ -0,0 +1,66 @@
+require 'action_view/renderer/abstract_renderer'
+
+module ActionView
+ class TemplateRenderer < AbstractRenderer #:nodoc:
+ def render(options)
+ wrap_formats(options[:template] || options[:file]) do
+ template = determine_template(options)
+ lookup_context.freeze_formats(template.formats, true)
+ render_template(template, options[:layout], options)
+ end
+ end
+
+ # Determine the template to be rendered using the given options.
+ def determine_template(options) #:nodoc:
+ keys = (options[:locals] ||= {}).keys
+
+ if options.key?(:inline)
+ handler = Template.handler_class_for_extension(options[:type] || "erb")
+ Template.new(options[:inline], "inline template", handler, { :locals => keys })
+ elsif options.key?(:text)
+ Template::Text.new(options[:text], formats.try(:first))
+ elsif options.key?(:file)
+ with_fallbacks { find_template(options[:file], options[:prefix], false, keys) }
+ elsif options.key?(:template)
+ options[:template].respond_to?(:render) ?
+ options[:template] : find_template(options[:template], options[:prefix], false, keys)
+ end
+ end
+
+ # Renders the given template. An string representing the layout can be
+ # supplied as well.
+ def render_template(template, layout = nil, options = {}) #:nodoc:
+ view, locals = @view, options[:locals] || {}
+ layout = find_layout(layout, locals.keys) if layout
+
+ ActiveSupport::Notifications.instrument("render_template.action_view",
+ :identifier => template.identifier, :layout => layout.try(:virtual_path)) do
+
+ content = template.render(view, locals) { |*name| view._layout_for(*name) }
+
+ if layout
+ view.store_content_for(:layout, content)
+ content = render_layout(layout, locals)
+ end
+
+ content
+ end
+ end
+
+ # This is the method which actually finds the layout using details in the lookup
+ # context object. If no layout is found, it checks if at least a layout with
+ # the given name exists across all details before raising the error.
+ def find_layout(layout, keys)
+ begin
+ with_layout_format do
+ layout =~ /^\// ?
+ with_fallbacks { find_template(layout, nil, false, keys) } : find_template(layout, nil, false, keys)
+ end
+ rescue ActionView::MissingTemplate => e
+ update_details(:formats => nil) do
+ raise unless template_exists?(layout)
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index 3cc9bb2710..074daa5d28 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -155,11 +155,12 @@ module ActionView
# virtual path set (true just for inline templates).
def refresh(view)
raise "A template need to have a virtual path in order to be refreshed" unless @virtual_path
+ lookup = view.lookup_context
pieces = @virtual_path.split("/")
name = pieces.pop
partial = name.sub!(/^_/, "")
- view.lookup_context.disable_cache do
- view.find_template(name, pieces.join, partial || false, @locals)
+ lookup.disable_cache do
+ lookup.find_template(name, pieces.join, partial || false, @locals)
end
end