diff options
Diffstat (limited to 'actionpack/lib/action_view')
-rw-r--r-- | actionpack/lib/action_view/context.rb | 4 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/asset_tag_helper.rb | 24 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/form_helper.rb | 4 | ||||
-rw-r--r-- | actionpack/lib/action_view/render/partials.rb | 140 | ||||
-rw-r--r-- | actionpack/lib/action_view/render/rendering.rb | 104 | ||||
-rw-r--r-- | actionpack/lib/action_view/test_case.rb | 6 |
6 files changed, 135 insertions, 147 deletions
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/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 081003bcf3..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 @@ -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/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 2d1d19d5f3..6e2990e084 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -932,6 +932,10 @@ 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 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/render/partials.rb b/actionpack/lib/action_view/render/partials.rb index ccb14d513a..b7b14e9007 100644 --- a/actionpack/lib/action_view/render/partials.rb +++ b/actionpack/lib/action_view/render/partials.rb @@ -172,131 +172,89 @@ module ActionView module Partials extend ActiveSupport::Memoizable extend ActiveSupport::Concern - + included do - attr_accessor :_partial + attr_accessor :_partial + end + + module ClassMethods + def _partial_names + @_partial_names ||= ActiveSupport::ConcurrentHash.new + end end - def _render_partial_from_controller(*args) + def render_partial(options) @assigns_added = false - _render_partial(*args) + _render_partial(options) end - def _render_partial(options = {}) #:nodoc: + def _render_partial(options, &block) #:nodoc: options[:locals] ||= {} - 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)) + path = partial = options[:partial] + + if partial.respond_to?(:to_ary) + return _render_partial_collection(partial, options, &block) + elsif !partial.is_a?(String) + options[:object] = object = partial + path = _partial_path(object) end + + _render_partial_object(_pick_partial_template(path), options, &block) end private - def partial_parts(name, options) - segments = name.split("/") - parts = segments.pop.split(".") - - case parts.size - when 1 - parts - when 2, 3 - extension = parts.delete_at(1).to_sym - if formats.include?(extension) - self.formats.replace [extension] + def _partial_path(object) + self.class._partial_names[[controller.class, object.class]] ||= begin + name = object.class.model_name + if controller_path && controller_path.include?("/") + File.join(File.dirname(controller_path), name.partial_path) + else + name.partial_path end - parts.pop if parts.size == 2 end - - path = parts.join(".") - prefix = segments[0..-1].join("/") - prefix = prefix.blank? ? controller_path : prefix - parts = [path, formats, prefix] - parts.push options[:object] || true - 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 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) - end - content = _render_partial(options) - return _render_content_with_layout(content, layout, options[:locals]) - end + def _render_partial_template(template, locals, object, options = {}, &block) + options[:_template] = template + locals[:object] = locals[template.variable_name] = object + locals[options[:as]] = object if options[:as] - def _array_like_objects - array_like = [Array] - if defined?(ActiveRecord) - array_like.push(ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope) - end - array_like + _render_single_template(template, locals, &block) end - def _render_partial_object(template, options, object = nil) + def _render_partial_object(template, options, &block) if options.key?(:collection) - _render_partial_collection(options.delete(:collection), options, template) + _render_partial_collection(options.delete(:collection), options, template, &block) else locals = (options[:locals] ||= {}) - object ||= locals[:object] || locals[template.variable_name] - - _set_locals(object, locals, template, options) - - options[:_template] = template - - _render_template(template, locals) - end - end + object = options[:object] || locals[:object] || locals[template.variable_name] - def _set_locals(object, locals, template, options) - locals[:object] = locals[template.variable_name] = object - locals[options[:as]] = object if options[:as] + _render_partial_template(template, locals, object, options, &block) + end end - def _render_partial_collection(collection, options = {}, passed_template = nil) #:nodoc: + def _render_partial_collection(collection, options = {}, template = nil, &block) #:nodoc: return nil if collection.blank? - - spacer = options[:spacer_template] ? _render_partial(:partial => options[:spacer_template]) : '' - 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) - end + if options.key?(:spacer_template) + spacer = _render_partial(:partial => options[:spacer_template]) + end - _set_locals(object, locals, template, options) - locals[template.counter_name] = index - + locals, index = options[:locals] || {}, 0 + + collection.map do |object| + tmp = template || _pick_partial_template(_partial_path(object)) + locals[tmp.counter_name] = index index += 1 - - _render_template(template, locals) + + _render_partial_template(tmp, locals, object, options, &block) end.join(spacer) end def _pick_partial_template(partial_path) #:nodoc: - prefix = controller_path unless partial_path.include?('/') + 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..9c25fab6bb 100644 --- a/actionpack/lib/action_view/render/rendering.rb +++ b/actionpack/lib/action_view/render/rendering.rb @@ -10,24 +10,25 @@ 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? - + + if block_given? + return concat(_render_partial(options.merge(:partial => layout), &block)) + elsif options.key?(:partial) + layout = _pick_partial_template(layout) if layout + return _render_content(_render_partial(options), layout, options[:locals]) + end + layout = find_by_parts(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]) + _render_template(template, layout, :locals => options[:locals] || {}) elsif inline = options[:inline] _render_inline(inline, layout, options) elsif text = options[:text] @@ -35,35 +36,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 +91,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 +127,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/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 |