diff options
Diffstat (limited to 'actionpack/lib/action_view')
-rw-r--r-- | actionpack/lib/action_view/base.rb | 29 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/prototype_helper.rb | 5 | ||||
-rw-r--r-- | actionpack/lib/action_view/render/partials.rb | 133 | ||||
-rw-r--r-- | actionpack/lib/action_view/render/rendering.rb | 79 | ||||
-rw-r--r-- | actionpack/lib/action_view/template/handler.rb | 6 | ||||
-rw-r--r-- | actionpack/lib/action_view/template/resolver.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_view/template/template.rb | 28 |
7 files changed, 142 insertions, 140 deletions
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index c171a5a8f5..ec1b07797b 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -175,6 +175,17 @@ module ActionView #:nodoc: attr_accessor :controller attr_internal :captures + def reset_formats(formats) + @formats = formats + + if defined?(ActionController) + # This is expensive, but we need to reset this when the format is updated, + # which currently only happens + Thread.current[:format_locale_key] = + ActionController::HashKey.get(self.class, formats, I18n.locale) + end + end + class << self delegate :erb_trim_mode=, :to => 'ActionView::TemplateHandlers::ERB' delegate :logger, :to => 'ActionController::Base', :allow_nil => true @@ -240,7 +251,7 @@ module ActionView #:nodoc: end def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil, formats = nil)#:nodoc: - @formats = formats || [:html] + @formats = formats @assigns = assigns_for_first_render.each { |key, value| instance_variable_set("@#{key}", value) } @controller = controller @helpers = self.class.helpers || Module.new @@ -255,15 +266,6 @@ module ActionView #:nodoc: @view_paths = self.class.process_view_paths(paths) end - def with_template(current_template) - _evaluate_assigns_and_ivars - last_template, self.template = template, current_template - last_formats, self.formats = formats, current_template.formats - yield - ensure - self.template, self.formats = last_template, last_formats - end - def punctuate_body!(part) flush_output_buffer response.body_parts << part @@ -272,18 +274,11 @@ module ActionView #:nodoc: # Evaluates the local assigns and controller ivars, pushes them to the view. def _evaluate_assigns_and_ivars #:nodoc: - @assigns_added ||= _copy_ivars_from_controller - end - - private - - def _copy_ivars_from_controller #:nodoc: if @controller variables = @controller.instance_variable_names variables -= @controller.protected_instance_variables if @controller.respond_to?(:protected_instance_variables) variables.each { |name| instance_variable_set(name, @controller.instance_variable_get(name)) } end - true end end diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 624b537ad2..03f1dabb4e 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -991,12 +991,13 @@ module ActionView def render(*options_for_render) old_formats = @context && @context.formats - @context.formats = [:html] if @context + + @context.reset_formats([:html]) if @context Hash === options_for_render.first ? @context.render(*options_for_render) : options_for_render.first.to_s ensure - @context.formats = old_formats if @context + @context.reset_formats(old_formats) if @context end def javascript_object_for(object) diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb index 83175ab4cf..7f10f54d2e 100644 --- a/actionpack/lib/action_view/render/partials.rb +++ b/actionpack/lib/action_view/render/partials.rb @@ -173,44 +173,50 @@ module ActionView extend ActiveSupport::Concern class PartialRenderer - def self.partial_names - @partial_names ||= Hash.new {|h,k| h[k] = ActiveSupport::ConcurrentHash.new } - end + PARTIAL_NAMES = Hash.new {|h,k| h[k] = {} } + TEMPLATES = Hash.new {|h,k| h[k] = {} } - def self.formats - @formats ||= Hash.new {|h,k| h[k] = Hash.new{|h,k| h[k] = Hash.new {|h,k| h[k] = {}}}} - end + attr_reader :template def initialize(view_context, options, block) - partial = options[:partial] - - @memo = {} @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 - - # Set up the object and path - @object = partial.is_a?(String) ? options[:object] : partial - @path = partial_path(partial) + @partial_names = PARTIAL_NAMES[@view.controller.class] + + key = Thread.current[:format_locale_key] + @templates = TEMPLATES[key] if key + + 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 + else + @object = partial + @path = partial_path(partial) + end end def render - return render_collection if collection - - template = find_template - render_template(template, @object || @locals[template.variable_name]) + if @collection = collection + render_collection + else + @template = template = find_template + render_template(template, @object || @locals[template.variable_name]) + end end def render_collection - @options[:_template] = template = find_template + @template = template = find_template - return nil if collection.blank? + return nil if @collection.blank? if @options.key?(:spacer_template) spacer = find_template(@options[:spacer_template]).render(@view, @locals) @@ -223,57 +229,58 @@ module ActionView def collection_with_template(template) options = @options - segments, locals, as = [], @locals, options[:as] || :object + segments, locals, as = [], @locals, options[:as] || template.variable_name - variable_name = template.variable_name counter_name = template.counter_name locals[counter_name] = -1 - collection.each do |object| + @collection.each do |object| locals[counter_name] += 1 - locals[variable_name] = object - locals[as] = object if as + locals[as] = object segments << template.render(@view, locals) end + + @template = template segments end def collection_without_template options = @options - segments, locals, as = [], @locals, options[:as] || :object + segments, locals, as = [], @locals, options[:as] index, template = -1, nil - collection.each do |object| + @collection.each do |object| template = find_template(partial_path(object)) locals[template.counter_name] = (index += 1) locals[template.variable_name] = object - locals[as] = object if as segments << template.render(@view, locals) end - @options[:_template] = template + @template = template segments end def render_template(template, object = @object) - @options[:_template] ||= template + options, locals, view = @options, @locals, @view + locals[options[:as] || template.variable_name] = object - # TODO: is locals[:object] really necessary? - @locals[:object] = @locals[template.variable_name] = object - @locals[@options[:as]] = object if @options[:as] + content = template.render(view, locals) do |*name| + @view._layout_for(*name, &@block) + end - content = @view._render_single_template(template, @locals, &@block) - return content if @block || !@options[:layout] - find_template(@options[:layout]).render(@view, @locals) { content } + if @block || !options[:layout] + content + else + find_template(options[:layout]).render(@view, @locals) { content } + end end - private def collection - @collection ||= if @object.respond_to?(:to_ary) + if @object.respond_to?(:to_ary) @object elsif @options.key?(:collection) @options[:collection] || [] @@ -281,15 +288,19 @@ module ActionView end 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) + unless @templates + path && _find_template(path) + else + path && @templates[path] ||= _find_template(path) end end + + def _find_template(path) + prefix = @view.controller.controller_path unless path.include?(?/) + @view.find(path, {:formats => @view.formats}, prefix, true) + end 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) @@ -302,14 +313,26 @@ module ActionView end def render_partial(options) - @assigns_added = false - # TODO: Handle other details here. - self.formats = options[:_details][:formats] if options[:_details] - _render_partial(options) + _evaluate_assigns_and_ivars + + details = options[:_details] + + # Is this needed + self.formats = details[:formats] if details + renderer = PartialRenderer.new(self, options, nil) + text = renderer.render + options[:_template] = renderer.template + text end def _render_partial(options, &block) #:nodoc: - PartialRenderer.new(self, options, block).render + if @renderer + @renderer.setup(options, block) + else + @renderer = PartialRenderer.new(self, options, block) + end + + @renderer.render end end diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb index c7afc56e3b..b0b75918b7 100644 --- a/actionpack/lib/action_view/render/rendering.rb +++ b/actionpack/lib/action_view/render/rendering.rb @@ -12,8 +12,6 @@ module ActionView # as the locals hash. def render(options = {}, locals = {}, &block) #:nodoc: case options - when String, NilClass - _render_partial(:partial => options, :locals => locals || {}) when Hash layout = options[:layout] @@ -35,26 +33,8 @@ module ActionView end when :update update_page(&block) - end - end - - 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_single_template(layout, locals) - ensure - @_content_for[:layout] = old_content + else + _render_partial(:partial => options, :locals => locals) end end @@ -90,48 +70,25 @@ 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_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 _layout_for(name = nil) + return @_content_for[name || :layout] if !block_given? || name - def _render_single_template(template, locals = {}, &block) - with_template(template) do - template.render(self, locals) do |*names| - _layout_for(names, &block) - end - end - rescue Exception => e - if e.is_a?(TemplateError) - e.sub_template_of(template) - raise e - else - raise TemplateError.new(template, assigns, e) + with_output_buffer do + return yield end end 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_single_template(template, options[:locals] || {}) - layout ? _render_content(content, layout, options[:locals]) : content + locals = options[:locals] || {} + content = template.render(self, locals) + content = layout.render(self, locals) {|*name| _layout_for(*name) { content } } if layout + content end def _render_text(text, layout, options) - layout ? _render_content(text, layout, options[:locals]) : text + text = layout.render(self, options[:locals]) { text } if layout end # This is the API to render a ViewContext's template from a controller. @@ -141,7 +98,7 @@ module ActionView # _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 + _evaluate_assigns_and_ivars template, layout, partial = options.values_at(:_template, :_layout, :_partial) _render_template(template, layout, options, partial) end @@ -158,10 +115,18 @@ module ActionView content = if partial _render_partial_object(template, options) else - _render_single_template(template, locals) + template.render(self, locals) end - _render_content(content, layout, locals) + @cached_content_for_layout = content + @_content_for[:layout] = content + + if layout + @_layout = layout.identifier + logger.info("Rendering template within #{layout.identifier}") if logger + content = layout.render(self, locals) {|*name| _layout_for(*name) } + end + content end end end
\ No newline at end of file diff --git a/actionpack/lib/action_view/template/handler.rb b/actionpack/lib/action_view/template/handler.rb index 3071c78174..4bf58b9fa8 100644 --- a/actionpack/lib/action_view/template/handler.rb +++ b/actionpack/lib/action_view/template/handler.rb @@ -26,11 +26,7 @@ module ActionView self.default_format = Mime::HTML def self.call(template) - "#{name}.new(self).render(template, local_assigns)" - end - - def initialize(view = nil) - @view = view + raise "Need to implement #{self.class.name}#call(template)" end def render(template, local_assigns) diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index 10f664736f..fe657166d5 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -42,7 +42,7 @@ module ActionView def handler_glob @handler_glob ||= begin - e = TemplateHandlers.extensions.map{|h| ".#{h},"}.join + e = TemplateHandlers.extensions.map{|h| ".#{h}"}.join(",") "{#{e}}" end end diff --git a/actionpack/lib/action_view/template/template.rb b/actionpack/lib/action_view/template/template.rb index 33d3f79ad3..7d6964e3e3 100644 --- a/actionpack/lib/action_view/template/template.rb +++ b/actionpack/lib/action_view/template/template.rb @@ -26,9 +26,16 @@ module ActionView @details[:formats] = Array.wrap(format.to_sym) end - def render(view, locals, &blk) + def render(view, locals, &block) method_name = compile(locals, view) - view.send(method_name, locals, &blk) + view.send(method_name, locals, &block) + rescue Exception => e + if e.is_a?(TemplateError) + e.sub_template_of(self) + raise e + else + raise TemplateError.new(self, view.assigns, e) + end end # TODO: Figure out how to abstract this @@ -90,7 +97,22 @@ module ActionView raise ActionView::TemplateError.new(self, {}, e) end end - + + class LocalsKey + @hash_keys = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = {} } } + + def self.get(*locals) + @hash_keys[*locals] ||= new(klass, format, locale) + end + + attr_accessor :hash + def initialize(klass, format, locale) + @hash = locals.hash + end + + alias_method :eql?, :equal? + end + def build_method_name(locals) # TODO: is locals.keys.hash reliably the same? @method_names[locals.keys.hash] ||= |