From 0d6e8edc2a47a4b4c6824936632bfb83850db343 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Sat, 4 May 2013 15:09:22 +0200 Subject: Move actionpack/lib/action_view* into actionview/lib --- .../lib/action_view/renderer/abstract_renderer.rb | 47 -- .../lib/action_view/renderer/partial_renderer.rb | 492 --------------------- actionpack/lib/action_view/renderer/renderer.rb | 50 --- .../renderer/streaming_template_renderer.rb | 103 ----- .../lib/action_view/renderer/template_renderer.rb | 96 ---- 5 files changed, 788 deletions(-) delete mode 100644 actionpack/lib/action_view/renderer/abstract_renderer.rb delete mode 100644 actionpack/lib/action_view/renderer/partial_renderer.rb delete mode 100644 actionpack/lib/action_view/renderer/renderer.rb delete mode 100644 actionpack/lib/action_view/renderer/streaming_template_renderer.rb delete mode 100644 actionpack/lib/action_view/renderer/template_renderer.rb (limited to 'actionpack/lib/action_view/renderer') diff --git a/actionpack/lib/action_view/renderer/abstract_renderer.rb b/actionpack/lib/action_view/renderer/abstract_renderer.rb deleted file mode 100644 index 73c19a0ae2..0000000000 --- a/actionpack/lib/action_view/renderer/abstract_renderer.rb +++ /dev/null @@ -1,47 +0,0 @@ -module ActionView - # This class defines the interface for a renderer. Each class that - # subclasses +AbstractRenderer+ is used by the base +Renderer+ class to - # render a specific type of object. - # - # The base +Renderer+ class uses its +render+ method to delegate to the - # renderers. These currently consist of - # - # PartialRenderer - Used for rendering partials - # TemplateRenderer - Used for rendering other types of templates - # StreamingTemplateRenderer - Used for streaming - # - # Whenever the +render+ method is called on the base +Renderer+ class, a new - # renderer object of the correct type is created, and the +render+ method on - # that new object is called in turn. This abstracts the setup and rendering - # into a separate classes for partials and templates. - class AbstractRenderer #:nodoc: - delegate :find_template, :template_exists?, :with_fallbacks, :with_layout_format, :formats, :to => :@lookup_context - - def initialize(lookup_context) - @lookup_context = lookup_context - end - - def render - raise NotImplementedError - end - - protected - - def extract_details(options) - @lookup_context.registered_details.each_with_object({}) do |key, details| - next unless value = options[key] - details[key] = Array(value) - end - end - - def instrument(name, options={}) - ActiveSupport::Notifications.instrument("render_#{name}.action_view", options){ yield } - end - - def prepend_formats(formats) - formats = Array(formats) - return if formats.empty? || @lookup_context.html_fallback_for_js - @lookup_context.formats = formats | @lookup_context.formats - end - end -end diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb deleted file mode 100644 index 821026268a..0000000000 --- a/actionpack/lib/action_view/renderer/partial_renderer.rb +++ /dev/null @@ -1,492 +0,0 @@ -require 'thread_safe' - -module ActionView - # = Action View Partials - # - # There's also a convenience method for rendering sub templates within the current controller that depends on a - # single object (we call this kind of sub templates for partials). It relies on the fact that partials should - # follow the naming convention of being prefixed with an underscore -- as to separate them from regular - # templates that could be rendered on their own. - # - # In a template for Advertiser#account: - # - # <%= render partial: "account" %> - # - # This would render "advertiser/_account.html.erb". - # - # In another template for Advertiser#buy, we could have: - # - # <%= render partial: "account", locals: { account: @buyer } %> - # - # <% @advertisements.each do |ad| %> - # <%= render partial: "ad", locals: { ad: ad } %> - # <% end %> - # - # This would first render "advertiser/_account.html.erb" with @buyer passed in as the local variable +account+, then - # render "advertiser/_ad.html.erb" and pass the local variable +ad+ to the template for display. - # - # == The :as and :object options - # - # By default ActionView::PartialRenderer doesn't have any local variables. - # The :object option can be used to pass an object to the partial. For instance: - # - # <%= render partial: "account", object: @buyer %> - # - # would provide the @buyer object to the partial, available under the local variable +account+ and is - # equivalent to: - # - # <%= render partial: "account", locals: { account: @buyer } %> - # - # With the :as option we can specify a different name for said local variable. For example, if we - # wanted it to be +user+ instead of +account+ we'd do: - # - # <%= render partial: "account", object: @buyer, as: 'user' %> - # - # This is equivalent to - # - # <%= render partial: "account", locals: { user: @buyer } %> - # - # == Rendering a collection of partials - # - # The example of partial use describes a familiar pattern where a template needs to iterate over an array and - # render a sub template for each of the elements. This pattern has been implemented as a single method that - # accepts an array and renders a partial by the same name as the elements contained within. So the three-lined - # example in "Using partials" can be rewritten with a single line: - # - # <%= render partial: "ad", collection: @advertisements %> - # - # This will render "advertiser/_ad.html.erb" and pass the local variable +ad+ to the template for display. An - # iteration counter will automatically be made available to the template with a name of the form - # +partial_name_counter+. In the case of the example above, the template would be fed +ad_counter+. - # - # The :as option may be used when rendering partials. - # - # You can specify a partial to be rendered between elements via the :spacer_template option. - # The following example will render advertiser/_ad_divider.html.erb between each ad partial: - # - # <%= render partial: "ad", collection: @advertisements, spacer_template: "ad_divider" %> - # - # If the given :collection is nil or empty, render will return nil. This will allow you - # to specify a text which will displayed instead by using this form: - # - # <%= render(partial: "ad", collection: @advertisements) || "There's no ad to be displayed" %> - # - # NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also - # just keep domain objects, like Active Records, in there. - # - # == Rendering shared partials - # - # Two controllers can share a set of partials and render them like this: - # - # <%= render partial: "advertisement/ad", locals: { ad: @advertisement } %> - # - # This will render the partial "advertisement/_ad.html.erb" regardless of which controller this is being called from. - # - # == Rendering objects that respond to `to_partial_path` - # - # Instead of explicitly naming the location of a partial, you can also let PartialRenderer do the work - # and pick the proper path by checking `to_partial_path` method. - # - # # @account.to_partial_path returns 'accounts/account', so it can be used to replace: - # # <%= render partial: "accounts/account", locals: { account: @account} %> - # <%= render partial: @account %> - # - # # @posts is an array of Post instances, so every post record returns 'posts/post' on `to_partial_path`, - # # that's why we can replace: - # # <%= render partial: "posts/post", collection: @posts %> - # <%= render partial: @posts %> - # - # == Rendering the default case - # - # If you're not going to be using any of the options like collections or layouts, you can also use the short-hand - # defaults of render to render partials. Examples: - # - # # Instead of <%= render partial: "account" %> - # <%= render "account" %> - # - # # Instead of <%= render partial: "account", locals: { account: @buyer } %> - # <%= render "account", account: @buyer %> - # - # # @account.to_partial_path returns 'accounts/account', so it can be used to replace: - # # <%= render partial: "accounts/account", locals: { account: @account} %> - # <%= render @account %> - # - # # @posts is an array of Post instances, so every post record returns 'posts/post' on `to_partial_path`, - # # that's why we can replace: - # # <%= render partial: "posts/post", collection: @posts %> - # <%= render @posts %> - # - # == Rendering partials with layouts - # - # Partials can have their own layouts applied to them. These layouts are different than the ones that are - # specified globally for the entire action, but they work in a similar fashion. Imagine a list with two types - # of users: - # - # <%# app/views/users/index.html.erb &> - # Here's the administrator: - # <%= render partial: "user", layout: "administrator", locals: { user: administrator } %> - # - # Here's the editor: - # <%= render partial: "user", layout: "editor", locals: { user: editor } %> - # - # <%# app/views/users/_user.html.erb &> - # Name: <%= user.name %> - # - # <%# app/views/users/_administrator.html.erb &> - #
- # Budget: $<%= user.budget %> - # <%= yield %> - #
- # - # <%# app/views/users/_editor.html.erb &> - #
- # Deadline: <%= user.deadline %> - # <%= yield %> - #
- # - # ...this will return: - # - # Here's the administrator: - #
- # Budget: $<%= user.budget %> - # Name: <%= user.name %> - #
- # - # Here's the editor: - #
- # Deadline: <%= user.deadline %> - # Name: <%= user.name %> - #
- # - # If a collection is given, the layout will be rendered once for each item in - # the collection. Just think these two snippets have the same output: - # - # <%# app/views/users/_user.html.erb %> - # Name: <%= user.name %> - # - # <%# app/views/users/index.html.erb %> - # <%# This does not use layouts %> - # - # - # <%# app/views/users/_li_layout.html.erb %> - #
  • - # <%= yield %> - #
  • - # - # <%# app/views/users/index.html.erb %> - # - # - # Given two users whose names are Alice and Bob, these snippets return: - # - # - # - # The current object being rendered, as well as the object_counter, will be - # available as local variables inside the layout template under the same names - # as available in the partial. - # - # You can also apply a layout to a block within any template: - # - # <%# app/views/users/_chief.html.erb &> - # <%= render(layout: "administrator", locals: { user: chief }) do %> - # Title: <%= chief.title %> - # <% end %> - # - # ...this will return: - # - #
    - # Budget: $<%= user.budget %> - # Title: <%= chief.name %> - #
    - # - # As you can see, the :locals hash is shared between both the partial and its layout. - # - # If you pass arguments to "yield" then this will be passed to the block. One way to use this is to pass - # an array to layout and treat it as an enumerable. - # - # <%# app/views/users/_user.html.erb &> - #
    - # Budget: $<%= user.budget %> - # <%= yield user %> - #
    - # - # <%# app/views/users/index.html.erb &> - # <%= render layout: @users do |user| %> - # Title: <%= user.title %> - # <% end %> - # - # This will render the layout for each user and yield to the block, passing the user, each time. - # - # You can also yield multiple times in one layout and use block arguments to differentiate the sections. - # - # <%# app/views/users/_user.html.erb &> - #
    - # <%= yield user, :header %> - # Budget: $<%= user.budget %> - # <%= yield user, :footer %> - #
    - # - # <%# app/views/users/index.html.erb &> - # <%= render layout: @users do |user, section| %> - # <%- case section when :header -%> - # Title: <%= user.title %> - # <%- when :footer -%> - # Deadline: <%= user.deadline %> - # <%- end -%> - # <% end %> - class PartialRenderer < AbstractRenderer - PREFIXED_PARTIAL_NAMES = ThreadSafe::Cache.new do |h, k| - h[k] = ThreadSafe::Cache.new - end - - def initialize(*) - super - @context_prefix = @lookup_context.prefixes.first - end - - def render(context, options, block) - setup(context, options, block) - identifier = (@template = find_partial) ? @template.identifier : @path - - @lookup_context.rendered_format ||= begin - if @template && @template.formats.present? - @template.formats.first - else - formats.first - end - end - - if @collection - instrument(:collection, :identifier => identifier || "collection", :count => @collection.size) do - render_collection - end - else - instrument(:partial, :identifier => identifier) do - render_partial - end - end - end - - def render_collection - return nil if @collection.blank? - - if @options.key?(:spacer_template) - spacer = find_template(@options[:spacer_template], @locals.keys).render(@view, @locals) - end - - result = @template ? collection_with_template : collection_without_template - result.join(spacer).html_safe - end - - def render_partial - view, locals, block = @view, @locals, @block - object, as = @object, @variable - - if !block && (layout = @options[:layout]) - layout = find_template(layout.to_s, @template_keys) - end - - object ||= locals[as] - locals[as] = object - - content = @template.render(view, locals) do |*name| - view._layout_for(*name, &block) - end - - content = layout.render(view, locals){ content } if layout - content - end - - private - - # Sets up instance variables needed for rendering a partial. This method - # finds the options and details and extracts them. The method also contains - # logic that handles the type of object passed in as the partial. - # - # If +options[:partial]+ is a string, then the +@path+ instance variable is - # set to that string. Otherwise, the +options[:partial]+ object must - # respond to +to_partial_path+ in order to setup the path. - def setup(context, options, block) - @view = context - partial = options[:partial] - - @options = options - @locals = options[:locals] || {} - @block = block - @details = extract_details(options) - - prepend_formats(options[:formats]) - - 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 as = options[:as] - raise_invalid_identifier(as) unless as.to_s =~ /\A[a-z_]\w*\z/ - as = as.to_sym - end - - if @path - @variable, @variable_counter = retrieve_variable(@path, as) - @template_keys = retrieve_template_keys - else - paths.map! { |path| retrieve_variable(path, as).unshift(path) } - end - - self - end - - def collection - if @options.key?(:collection) - collection = @options[:collection] - collection.respond_to?(:to_ary) ? collection.to_ary : [] - end - end - - def collection_from_object - @object.to_ary if @object.respond_to?(:to_ary) - end - - def find_partial - if path = @path - find_template(path, @template_keys) - end - end - - def find_template(path, locals) - prefixes = path.include?(?/) ? [] : @lookup_context.prefixes - @lookup_context.find_template(path, prefixes, true, locals, @details) - end - - def collection_with_template - view, locals, template = @view, @locals, @template - as, counter = @variable, @variable_counter - - if layout = @options[:layout] - layout = find_template(layout, @template_keys) - end - - index = -1 - @collection.map do |object| - locals[as] = object - locals[counter] = (index += 1) - - content = template.render(view, locals) - content = layout.render(view, locals) { content } if layout - content - end - end - - def collection_without_template - view, locals, collection_data = @view, @locals, @collection_data - cache = {} - keys = @locals.keys - - index = -1 - @collection.map do |object| - index += 1 - path, as, counter = collection_data[index] - - locals[as] = object - locals[counter] = index - - template = (cache[path] ||= find_template(path, keys + [as, counter])) - template.render(view, locals) - end - end - - # Obtains the path to where the object's partial is located. If the object - # responds to +to_partial_path+, then +to_partial_path+ will be called and - # will provide the path. If the object does not respond to +to_partial_path+, - # then an +ArgumentError+ is raised. - # - # If +prefix_partial_path_with_controller_namespace+ is true, then this - # method will prefix the partial paths with a namespace. - def partial_path(object = @object) - object = object.to_model if object.respond_to?(:to_model) - - path = if object.respond_to?(:to_partial_path) - object.to_partial_path - else - raise ArgumentError.new("'#{object.inspect}' is not an ActiveModel-compatible object. It must implement :to_partial_path.") - end - - if @view.prefix_partial_path_with_controller_namespace - prefixed_partial_names[path] ||= merge_prefix_into_object_path(@context_prefix, path.dup) - else - path - end - end - - def prefixed_partial_names - @prefixed_partial_names ||= PREFIXED_PARTIAL_NAMES[@context_prefix] - end - - def merge_prefix_into_object_path(prefix, object_path) - if prefix.include?(?/) && object_path.include?(?/) - prefixes = [] - prefix_array = File.dirname(prefix).split('/') - object_path_array = object_path.split('/')[0..-3] # skip model dir & partial - - prefix_array.each_with_index do |dir, index| - break if dir == object_path_array[index] - prefixes << dir - end - - (prefixes << object_path).join("/") - else - object_path - end - end - - def retrieve_template_keys - keys = @locals.keys - keys << @variable if @object || @collection - keys << @variable_counter if @collection - keys - end - - def retrieve_variable(path, as) - variable = as || begin - base = path[-1] == "/" ? "" : File.basename(path) - raise_invalid_identifier(path) unless base =~ /\A_?([a-z]\w*)(\.\w+)*\z/ - $1.to_sym - end - variable_counter = :"#{variable}_counter" if @collection - [variable, variable_counter] - end - - IDENTIFIER_ERROR_MESSAGE = "The partial name (%s) is not a valid Ruby identifier; " + - "make sure your partial name starts with a lowercase letter or underscore, " + - "and is followed by any combination of letters, numbers and underscores." - - def raise_invalid_identifier(path) - raise ArgumentError.new(IDENTIFIER_ERROR_MESSAGE % (path)) - end - end -end diff --git a/actionpack/lib/action_view/renderer/renderer.rb b/actionpack/lib/action_view/renderer/renderer.rb deleted file mode 100644 index 964b18337e..0000000000 --- a/actionpack/lib/action_view/renderer/renderer.rb +++ /dev/null @@ -1,50 +0,0 @@ -module ActionView - # This is the main entry point for rendering. It basically delegates - # to other objects like TemplateRenderer and PartialRenderer which - # actually renders the template. - # - # The Renderer will parse the options from the +render+ or +render_body+ - # method and render a partial or a template based on the options. The - # +TemplateRenderer+ and +PartialRenderer+ objects are wrappers which do all - # the setup and logic necessary to render a view and a new object is created - # each time +render+ is called. - class Renderer - attr_accessor :lookup_context - - def initialize(lookup_context) - @lookup_context = lookup_context - end - - # Main render entry point shared by AV and AC. - def render(context, options) - if options.key?(:partial) - render_partial(context, options) - else - render_template(context, options) - end - end - - # Render but returns a valid Rack body. If fibers are defined, we return - # a streaming body that renders the template piece by piece. - # - # Note that partials are not supported to be rendered with streaming, - # so in such cases, we just wrap them in an array. - def render_body(context, options) - if options.key?(:partial) - [render_partial(context, options)] - else - StreamingTemplateRenderer.new(@lookup_context).render(context, options) - end - end - - # Direct accessor to template rendering. - def render_template(context, options) #:nodoc: - TemplateRenderer.new(@lookup_context).render(context, options) - end - - # Direct access to partial rendering. - def render_partial(context, options, &block) #:nodoc: - PartialRenderer.new(@lookup_context).render(context, options, block) - end - end -end diff --git a/actionpack/lib/action_view/renderer/streaming_template_renderer.rb b/actionpack/lib/action_view/renderer/streaming_template_renderer.rb deleted file mode 100644 index 9cf6eb0c65..0000000000 --- a/actionpack/lib/action_view/renderer/streaming_template_renderer.rb +++ /dev/null @@ -1,103 +0,0 @@ -require 'fiber' - -module ActionView - # == TODO - # - # * Support streaming from child templates, partials and so on. - # * Integrate exceptions with exceptron - # * Rack::Cache needs to support streaming bodies - class StreamingTemplateRenderer < TemplateRenderer #:nodoc: - # A valid Rack::Body (i.e. it responds to each). - # It is initialized with a block that, when called, starts - # rendering the template. - class Body #:nodoc: - def initialize(&start) - @start = start - end - - def each(&block) - begin - @start.call(block) - rescue Exception => exception - log_error(exception) - block.call ActionView::Base.streaming_completion_on_exception - end - self - end - - private - - # This is the same logging logic as in ShowExceptions middleware. - # TODO Once "exceptron" is in, refactor this piece to simply re-use exceptron. - def log_error(exception) #:nodoc: - logger = ActionView::Base.logger - return unless logger - - message = "\n#{exception.class} (#{exception.message}):\n" - message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code) - message << " " << exception.backtrace.join("\n ") - logger.fatal("#{message}\n\n") - end - end - - # For streaming, instead of rendering a given a template, we return a Body - # object that responds to each. This object is initialized with a block - # that knows how to render the template. - def render_template(template, layout_name = nil, locals = {}) #:nodoc: - return [super] unless layout_name && template.supports_streaming? - - locals ||= {} - layout = layout_name && find_layout(layout_name, locals.keys) - - Body.new do |buffer| - delayed_render(buffer, template, layout, @view, locals) - end - end - - private - - def delayed_render(buffer, template, layout, view, locals) - # Wrap the given buffer in the StreamingBuffer and pass it to the - # underlying template handler. Now, everytime something is concatenated - # to the buffer, it is not appended to an array, but streamed straight - # to the client. - output = ActionView::StreamingBuffer.new(buffer) - yielder = lambda { |*name| view._layout_for(*name) } - - instrument(:template, :identifier => template.identifier, :layout => layout.try(:virtual_path)) do - fiber = Fiber.new do - if layout - layout.render(view, locals, output, &yielder) - else - # If you don't have a layout, just render the thing - # and concatenate the final result. This is the same - # as a layout with just <%= yield %> - output.safe_concat view._layout_for - end - end - - # Set the view flow to support streaming. It will be aware - # when to stop rendering the layout because it needs to search - # something in the template and vice-versa. - view.view_flow = StreamingFlow.new(view, fiber) - - # Yo! Start the fiber! - fiber.resume - - # If the fiber is still alive, it means we need something - # from the template, so start rendering it. If not, it means - # the layout exited without requiring anything from the template. - if fiber.alive? - content = template.render(view, locals, &yielder) - - # Once rendering the template is done, sets its content in the :layout key. - view.view_flow.set(:layout, content) - - # In case the layout continues yielding, we need to resume - # the fiber until all yields are handled. - fiber.resume while fiber.alive? - end - end - end - end -end diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb deleted file mode 100644 index 4d5c5db80c..0000000000 --- a/actionpack/lib/action_view/renderer/template_renderer.rb +++ /dev/null @@ -1,96 +0,0 @@ -require 'active_support/core_ext/object/try' - -module ActionView - class TemplateRenderer < AbstractRenderer #:nodoc: - def render(context, options) - @view = context - @details = extract_details(options) - template = determine_template(options) - context = @lookup_context - - prepend_formats(template.formats) - - unless context.rendered_format - context.rendered_format = template.formats.first || formats.last - end - - render_template(template, options[:layout], options[:locals]) - end - - # Determine the template to be rendered using the given options. - def determine_template(options) #:nodoc: - keys = options.fetch(:locals, {}).keys - - if options.key?(:text) - Template::Text.new(options[:text], formats.first) - elsif options.key?(:file) - with_fallbacks { find_template(options[:file], nil, false, keys, @details) } - elsif options.key?(:inline) - handler = Template.handler_for_extension(options[:type] || "erb") - Template.new(options[:inline], "inline template", handler, :locals => keys) - elsif options.key?(:template) - if options[:template].respond_to?(:render) - options[:template] - else - find_template(options[:template], options[:prefixes], false, keys, @details) - end - else - raise ArgumentError, "You invoked render but did not give any of :partial, :template, :inline, :file or :text option." - end - end - - # Renders the given template. A string representing the layout can be - # supplied as well. - def render_template(template, layout_name = nil, locals = nil) #:nodoc: - view, locals = @view, locals || {} - - render_with_layout(layout_name, locals) do |layout| - instrument(:template, :identifier => template.identifier, :layout => layout.try(:virtual_path)) do - template.render(view, locals) { |*name| view._layout_for(*name) } - end - end - end - - def render_with_layout(path, locals) #:nodoc: - layout = path && find_layout(path, locals.keys) - content = yield(layout) - - if layout - view = @view - view.view_flow.set(:layout, content) - layout.render(view, locals){ |*name| view._layout_for(*name) } - else - 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) - with_layout_format { resolve_layout(layout, keys) } - end - - def resolve_layout(layout, keys) - case layout - when String - begin - if layout =~ /^\// - with_fallbacks { find_template(layout, nil, false, keys, @details) } - else - find_template(layout, nil, false, keys, @details) - end - rescue ActionView::MissingTemplate - all_details = @details.merge(:formats => @lookup_context.default_formats) - raise unless template_exists?(layout, nil, false, keys, all_details) - end - when Proc - resolve_layout(layout.call, keys) - when FalseClass - nil - else - layout - end - end - end -end -- cgit v1.2.3