aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib')
-rw-r--r--actionpack/lib/abstract_controller/base.rb38
-rw-r--r--actionpack/lib/abstract_controller/callbacks.rb4
-rw-r--r--actionpack/lib/abstract_controller/helpers.rb2
-rw-r--r--actionpack/lib/abstract_controller/layouts.rb6
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb72
-rw-r--r--actionpack/lib/abstract_controller/view_paths.rb29
-rw-r--r--actionpack/lib/action_controller.rb1
-rw-r--r--actionpack/lib/action_controller/base.rb10
-rw-r--r--actionpack/lib/action_controller/log_subscriber.rb4
-rw-r--r--actionpack/lib/action_controller/metal/compatibility.rb11
-rw-r--r--actionpack/lib/action_controller/metal/implicit_render.rb16
-rw-r--r--actionpack/lib/action_controller/metal/instrumentation.rb4
-rw-r--r--actionpack/lib/action_controller/metal/params_wrapper.rb224
-rw-r--r--actionpack/lib/action_controller/metal/responder.rb7
-rw-r--r--actionpack/lib/action_controller/metal/streaming.rb206
-rw-r--r--actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/mime_negotiation.rb27
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb9
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/cookie_store.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/static.rb17
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb6
-rw-r--r--actionpack/lib/action_dispatch/railtie.rb2
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb10
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/response.rb2
-rw-r--r--actionpack/lib/action_pack/version.rb2
-rw-r--r--actionpack/lib/action_view.rb3
-rw-r--r--actionpack/lib/action_view/base.rb80
-rw-r--r--actionpack/lib/action_view/context.rb44
-rw-r--r--actionpack/lib/action_view/flows.rb2
-rw-r--r--actionpack/lib/action_view/helpers.rb4
-rw-r--r--actionpack/lib/action_view/helpers/asset_paths.rb1
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb3
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb7
-rw-r--r--actionpack/lib/action_view/helpers/capture_helper.rb6
-rw-r--r--actionpack/lib/action_view/helpers/controller_helper.rb21
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb17
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb7
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb22
-rw-r--r--actionpack/lib/action_view/helpers/rendering_helper.rb90
-rw-r--r--actionpack/lib/action_view/helpers/sprockets_helper.rb6
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb125
-rw-r--r--actionpack/lib/action_view/helpers/translation_helper.rb4
-rw-r--r--actionpack/lib/action_view/lookup_context.rb5
-rw-r--r--actionpack/lib/action_view/partials.rb226
-rw-r--r--actionpack/lib/action_view/railtie.rb2
-rw-r--r--actionpack/lib/action_view/renderer/abstract_renderer.rb5
-rw-r--r--actionpack/lib/action_view/renderer/partial_renderer.rb290
-rw-r--r--actionpack/lib/action_view/renderer/renderer.rb54
-rw-r--r--actionpack/lib/action_view/renderer/streaming_template_renderer.rb62
-rw-r--r--actionpack/lib/action_view/renderer/template_renderer.rb8
-rw-r--r--actionpack/lib/action_view/rendering.rb114
-rw-r--r--actionpack/lib/action_view/template.rb14
-rw-r--r--actionpack/lib/action_view/test_case.rb21
57 files changed, 1189 insertions, 775 deletions
diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb
index 0951267fea..f67d0e558e 100644
--- a/actionpack/lib/abstract_controller/base.rb
+++ b/actionpack/lib/abstract_controller/base.rb
@@ -130,27 +130,39 @@ module AbstractController
self.class.action_methods
end
- # Returns true if the name can be considered an action. This can
- # be overridden in subclasses to modify the semantics of what
- # can be considered an action.
+ # Returns true if a method for the action is available and
+ # can be dispatched, false otherwise.
#
- # For instance, this is overriden by ActionController to add
- # the implicit rendering feature.
- #
- # ==== Parameters
- # * <tt>name</tt> - The name of an action to be tested
- #
- # ==== Returns
- # * <tt>TrueClass</tt>, <tt>FalseClass</tt>
- def action_method?(name)
- self.class.action_methods.include?(name)
+ # Notice that <tt>action_methods.include?("foo")</tt> may return
+ # false and <tt>available_action?("foo")</tt> returns true because
+ # available action consider actions that are also available
+ # through other means, for example, implicit render ones.
+ def available_action?(action_name)
+ method_for_action(action_name).present?
end
private
+ # Returns true if the name can be considered an action because
+ # it has a method defined in the controller.
+ #
+ # ==== Parameters
+ # * <tt>name</tt> - The name of an action to be tested
+ #
+ # ==== Returns
+ # * <tt>TrueClass</tt>, <tt>FalseClass</tt>
+ #
+ # :api: private
+ def action_method?(name)
+ self.class.action_methods.include?(name)
+ end
+
# Call the action. Override this in a subclass to modify the
# behavior around processing an action. This, and not #process,
# is the intended way to override action dispatching.
+ #
+ # Notice that the first argument is the method to be dispatched
+ # which is *not* necessarily the same as the action name.
def process_action(method_name, *args)
send_action(method_name, *args)
end
diff --git a/actionpack/lib/abstract_controller/callbacks.rb b/actionpack/lib/abstract_controller/callbacks.rb
index f7b2b7ff53..e8426bc52b 100644
--- a/actionpack/lib/abstract_controller/callbacks.rb
+++ b/actionpack/lib/abstract_controller/callbacks.rb
@@ -13,8 +13,8 @@ module AbstractController
# Override AbstractController::Base's process_action to run the
# process_action callbacks around the normal behavior.
- def process_action(method_name, *args)
- run_callbacks(:process_action, method_name) do
+ def process_action(*args)
+ run_callbacks(:process_action, action_name) do
super
end
end
diff --git a/actionpack/lib/abstract_controller/helpers.rb b/actionpack/lib/abstract_controller/helpers.rb
index 0ff1c0491a..dc9778a416 100644
--- a/actionpack/lib/abstract_controller/helpers.rb
+++ b/actionpack/lib/abstract_controller/helpers.rb
@@ -4,8 +4,6 @@ module AbstractController
module Helpers
extend ActiveSupport::Concern
- include Rendering
-
included do
class_attribute :_helpers
self._helpers = Module.new
diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb
index d1b87b67ee..8f73e244d7 100644
--- a/actionpack/lib/abstract_controller/layouts.rb
+++ b/actionpack/lib/abstract_controller/layouts.rb
@@ -292,15 +292,15 @@ module AbstractController
end
end
- attr_writer :action_has_layout
+ attr_internal_writer :action_has_layout
def initialize(*)
- @action_has_layout = true
+ @_action_has_layout = true
super
end
def action_has_layout?
- @action_has_layout
+ @_action_has_layout
end
private
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 306bd41e2d..ab2c532859 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -32,9 +32,13 @@ module AbstractController
module Rendering
extend ActiveSupport::Concern
-
include AbstractController::ViewPaths
+ included do
+ config_accessor :protected_instance_variables, :instance_reader => false
+ self.protected_instance_variables = []
+ end
+
# Overwrite process to setup I18n proxy.
def process(*) #:nodoc:
old_config, I18n.config = I18n.config, I18nProxy.new(I18n.config, lookup_context)
@@ -46,46 +50,27 @@ module AbstractController
module ClassMethods
def view_context_class
@view_context_class ||= begin
- controller = self
- Class.new(ActionView::Base) do
- if controller.respond_to?(:_routes) && controller._routes
- include controller._routes.url_helpers
- include controller._routes.mounted_helpers
- end
-
- if controller.respond_to?(:_helpers)
- include controller._helpers
-
- # TODO: Fix RJS to not require this
- self.helpers = controller._helpers
- end
- end
+ routes = _routes if respond_to?(:_routes)
+ helpers = _helpers if respond_to?(:_helpers)
+ ActionView::Base.prepare(routes, helpers)
end
end
+ end
- def parent_prefixes
- @parent_prefixes ||= begin
- parent_controller = superclass
- prefixes = []
-
- until parent_controller.abstract?
- prefixes << parent_controller.controller_path
- parent_controller = parent_controller.superclass
- end
+ attr_internal_writer :view_context_class
- prefixes
- end
- end
+ # Explicitly define protected_instance_variables so it can be
+ # inherited and overwritten by other modules if needed.
+ def protected_instance_variables
+ config.protected_instance_variables
end
- attr_writer :view_context_class
-
def view_context_class
- @view_context_class || self.class.view_context_class
+ @_view_context_class || self.class.view_context_class
end
def initialize(*)
- @view_context_class = nil
+ @_view_context_class = nil
super
end
@@ -99,7 +84,12 @@ module AbstractController
#
# Override this method in a module to change the default behavior.
def view_context
- view_context_class.new(lookup_context, view_assigns, self)
+ view_context_class.new(view_renderer, view_assigns, self)
+ end
+
+ # Returns an object that is able to render templates.
+ def view_renderer
+ @_view_renderer ||= ActionView::Renderer.new(lookup_context)
end
# Normalize arguments, options and then delegates render_to_body and
@@ -127,26 +117,24 @@ module AbstractController
# Find and renders a template based on the options given.
# :api: private
def _render_template(options) #:nodoc:
- view_context.render(options)
- end
-
- # The prefixes used in render "foo" shortcuts.
- def _prefixes
- @_prefixes ||= begin
- parent_prefixes = self.class.parent_prefixes
- parent_prefixes.dup.unshift(controller_path)
- end
+ view_renderer.render(view_context, options)
end
private
+ DEFAULT_PROTECTED_INSTANCE_VARIABLES = %w(
+ @_action_name @_response_body @_formats @_prefixes @_config
+ @_view_context_class @_view_renderer @_lookup_context
+ )
+
# This method should return a hash with assigns.
# You can overwrite this configuration per controller.
# :api: public
def view_assigns
hash = {}
variables = instance_variable_names
- variables -= protected_instance_variables if respond_to?(:protected_instance_variables)
+ variables -= protected_instance_variables
+ variables -= DEFAULT_PROTECTED_INSTANCE_VARIABLES
variables.each { |name| hash[name.to_s[1, name.length]] = instance_variable_get(name) }
hash
end
diff --git a/actionpack/lib/abstract_controller/view_paths.rb b/actionpack/lib/abstract_controller/view_paths.rb
index cea0f5ad1e..6b7aae8c74 100644
--- a/actionpack/lib/abstract_controller/view_paths.rb
+++ b/actionpack/lib/abstract_controller/view_paths.rb
@@ -11,11 +11,36 @@ module AbstractController
delegate :find_template, :template_exists?, :view_paths, :formats, :formats=,
:locale, :locale=, :to => :lookup_context
+ module ClassMethods
+ def parent_prefixes
+ @parent_prefixes ||= begin
+ parent_controller = superclass
+ prefixes = []
+
+ until parent_controller.abstract?
+ prefixes << parent_controller.controller_path
+ parent_controller = parent_controller.superclass
+ end
+
+ prefixes
+ end
+ end
+ end
+
+ # The prefixes used in render "foo" shortcuts.
+ def _prefixes
+ @_prefixes ||= begin
+ parent_prefixes = self.class.parent_prefixes
+ parent_prefixes.dup.unshift(controller_path)
+ end
+ end
+
# LookupContext is the object responsible to hold all information required to lookup
# templates, i.e. view paths and details. Check ActionView::LookupContext for more
# information.
def lookup_context
- @lookup_context ||= ActionView::LookupContext.new(self.class._view_paths, details_for_lookup)
+ @_lookup_context ||=
+ ActionView::LookupContext.new(self.class._view_paths, details_for_lookup, _prefixes)
end
def details_for_lookup
@@ -67,4 +92,4 @@ module AbstractController
end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index aab2b9dc25..eba5e9377b 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -23,6 +23,7 @@ module ActionController
autoload :ImplicitRender
autoload :Instrumentation
autoload :MimeResponds
+ autoload :ParamsWrapper
autoload :RackDelegation
autoload :Redirecting
autoload :Renderers
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index ca0dccf575..c03c77cb4a 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -206,13 +206,17 @@ module ActionController
HttpAuthentication::Digest::ControllerMethods,
HttpAuthentication::Token::ControllerMethods,
+ # Before callbacks should also be executed the earliest as possible, so
+ # also include them at the bottom.
+ AbstractController::Callbacks,
+
# Add instrumentations hooks at the bottom, to ensure they instrument
# all the methods properly.
Instrumentation,
- # Before callbacks should also be executed the earliest as possible, so
- # also include them at the bottom.
- AbstractController::Callbacks,
+ # Params wrapper should come before instrumentation so they are
+ # properly showed in logs
+ ParamsWrapper,
# The same with rescue, append it at the end to wrap as much as possible.
Rescue
diff --git a/actionpack/lib/action_controller/log_subscriber.rb b/actionpack/lib/action_controller/log_subscriber.rb
index 3fae697cc3..8d813a8e38 100644
--- a/actionpack/lib/action_controller/log_subscriber.rb
+++ b/actionpack/lib/action_controller/log_subscriber.rb
@@ -7,8 +7,10 @@ module ActionController
def start_processing(event)
payload = event.payload
params = payload[:params].except(*INTERNAL_PARAMS)
+ format = payload[:format]
+ format = format.to_s.upcase if format.is_a?(Symbol)
- info " Processing by #{payload[:controller]}##{payload[:action]} as #{payload[:formats].first.to_s.upcase}"
+ info " Processing by #{payload[:controller]}##{payload[:action]} as #{format}"
info " Parameters: #{params.inspect}" unless params.empty?
end
diff --git a/actionpack/lib/action_controller/metal/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb
index 006b9fd456..05dca445a4 100644
--- a/actionpack/lib/action_controller/metal/compatibility.rb
+++ b/actionpack/lib/action_controller/metal/compatibility.rb
@@ -18,13 +18,10 @@ module ActionController
delegate :default_charset=, :to => "ActionDispatch::Response"
end
- # TODO: Update protected instance variables list
- config_accessor :protected_instance_variables
- self.protected_instance_variables = %w(@assigns @performed_redirect @performed_render
- @variables_added @request_origin @url
- @parent_controller @action_name
- @before_filter_chain_aborted @_headers @_params
- @_response)
+ self.protected_instance_variables = %w(
+ @_status @_headers @_params @_env @_response @_request
+ @_view_runtime @_stream @_url_options @_action_has_layout
+ )
def rescue_action(env)
raise env["action_dispatch.rescue.exception"]
diff --git a/actionpack/lib/action_controller/metal/implicit_render.rb b/actionpack/lib/action_controller/metal/implicit_render.rb
index 3ec0c4c6a4..e8e465d3ba 100644
--- a/actionpack/lib/action_controller/metal/implicit_render.rb
+++ b/actionpack/lib/action_controller/metal/implicit_render.rb
@@ -1,21 +1,19 @@
module ActionController
module ImplicitRender
def send_action(method, *args)
- if respond_to?(method, true)
- ret = super
- default_render unless response_body
- ret
- else
- default_render
- end
+ ret = super
+ default_render unless response_body
+ ret
end
def default_render(*args)
render(*args)
end
- def action_method?(action_name)
- super || template_exists?(action_name.to_s, _prefixes)
+ def method_for_action(action_name)
+ super || if template_exists?(action_name.to_s, _prefixes)
+ "default_render"
+ end
end
end
end
diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb
index dc3ea939e6..16cbbce2fb 100644
--- a/actionpack/lib/action_controller/metal/instrumentation.rb
+++ b/actionpack/lib/action_controller/metal/instrumentation.rb
@@ -14,12 +14,12 @@ module ActionController
attr_internal :view_runtime
- def process_action(action, *args)
+ def process_action(*args)
raw_payload = {
:controller => self.class.name,
:action => self.action_name,
:params => request.filtered_parameters,
- :formats => request.formats.map(&:to_sym),
+ :format => request.format.ref,
:method => request.method,
:path => (request.fullpath rescue "unknown")
}
diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb
new file mode 100644
index 0000000000..881af74147
--- /dev/null
+++ b/actionpack/lib/action_controller/metal/params_wrapper.rb
@@ -0,0 +1,224 @@
+require 'active_support/core_ext/class/attribute'
+require 'active_support/core_ext/hash/slice'
+require 'active_support/core_ext/hash/except'
+require 'active_support/core_ext/array/wrap'
+require 'action_dispatch/http/mime_types'
+
+module ActionController
+ # Wraps parameters hash into nested hash. This will allow client to submit
+ # POST request without having to specify a root element in it.
+ #
+ # By default this functionality won't be enabled. You can enable
+ # it globally by setting +ActionController::Base.wrap_parameters+:
+ #
+ # ActionController::Base.wrap_parameters = [:json]
+ #
+ # You could also turn it on per controller by setting the format array to
+ # non-empty array:
+ #
+ # class UsersController < ApplicationController
+ # wrap_parameters :format => [:json, :xml]
+ # end
+ #
+ # If you enable +ParamsWrapper+ for +:json+ format. Instead of having to
+ # send JSON parameters like this:
+ #
+ # {"user": {"name": "Konata"}}
+ #
+ # You can now just send a parameters like this:
+ #
+ # {"name": "Konata"}
+ #
+ # And it will be wrapped into a nested hash with the key name matching
+ # controller's name. For example, if you're posting to +UsersController+,
+ # your new +params+ hash will look like this:
+ #
+ # {"name" => "Konata", "user" => {"name" => "Konata"}}
+ #
+ # You can also specify the key in which the parameters should be wrapped to,
+ # and also the list of attributes it should wrap by using either +:only+ or
+ # +:except+ options like this:
+ #
+ # class UsersController < ApplicationController
+ # wrap_parameters :person, :only => [:username, :password]
+ # end
+ #
+ # If you're going to pass the parameters to an +ActiveModel+ object (such as
+ # +User.new(params[:user])+), you might consider passing the model class to
+ # the method instead. The +ParamsWrapper+ will actually try to determine the
+ # list of attribute names from the model and only wrap those attributes:
+ #
+ # class UsersController < ApplicationController
+ # wrap_parameters Person
+ # end
+ #
+ # You still could pass +:only+ and +:except+ to set the list of attributes
+ # you want to wrap.
+ #
+ # By default, if you don't specify the key in which the parameters would be
+ # wrapped to, +ParamsWrapper+ will actually try to determine if there's
+ # a model related to it or not. This controller, for example:
+ #
+ # class Admin::UsersController < ApplicationController
+ # end
+ #
+ # will try to check if +Admin::User+ or +User+ model exists, and use it to
+ # determine the wrapper key respectively. If both of the model doesn't exists,
+ # it will then fallback to use +user+ as the key.
+ module ParamsWrapper
+ extend ActiveSupport::Concern
+
+ EXCLUDE_PARAMETERS = %w(authenticity_token _method utf8)
+
+ included do
+ class_attribute :_wrapper_options
+ self._wrapper_options = {:format => []}
+ end
+
+ module ClassMethods
+ # Sets the name of the wrapper key, or the model which +ParamsWrapper+
+ # would use to determine the attribute names from.
+ #
+ # ==== Examples
+ # wrap_parameters :format => :xml
+ # # enables the parmeter wrapper for XML format
+ #
+ # wrap_parameters :person
+ # # wraps parameters into +params[:person]+ hash
+ #
+ # wrap_parameters Person
+ # # wraps parameters by determine the wrapper key from Person class
+ # (+person+, in this case) and the list of attribute names
+ #
+ # wrap_parameters :only => [:username, :title]
+ # # wraps only +:username+ and +:title+ attributes from parameters.
+ #
+ # wrap_parameters false
+ # # disable parameters wrapping for this controller altogether.
+ #
+ # ==== Options
+ # * <tt>:format</tt> - The list of formats in which the parameters wrapper
+ # will be enabled.
+ # * <tt>:only</tt> - The list of attribute names which parameters wrapper
+ # will wrap into a nested hash.
+ # * <tt>:except</tt> - The list of attribute names which parameters wrapper
+ # will exclude from a nested hash.
+ def wrap_parameters(name_or_model_or_options, options = {})
+ model = nil
+
+ case name_or_model_or_options
+ when Hash
+ options = name_or_model_or_options
+ when false
+ options = options.merge(:format => [])
+ when Symbol, String
+ options = options.merge(:name => name_or_model_or_options)
+ else
+ model = name_or_model_or_options
+ end
+
+ _set_wrapper_defaults(_wrapper_options.slice(:format).merge(options), model)
+ end
+
+ # Sets the default wrapper key or model which will be used to determine
+ # wrapper key and attribute names. Will be called automatically when the
+ # module is inherited.
+ def inherited(klass)
+ if klass._wrapper_options[:format].present?
+ klass._set_wrapper_defaults(klass._wrapper_options.slice(:format))
+ end
+ super
+ end
+
+ protected
+
+ # Determine the wrapper model from the controller's name. By convention,
+ # this could be done by trying to find the defined model that has the
+ # same singularize name as the controller. For example, +UsersController+
+ # will try to find if the +User+ model exists.
+ def _default_wrap_model
+ model_name = self.name.sub(/Controller$/, '').singularize
+
+ begin
+ model_klass = model_name.constantize
+ rescue NameError => e
+ unscoped_model_name = model_name.split("::", 2).last
+ break if unscoped_model_name == model_name
+ model_name = unscoped_model_name
+ end until model_klass
+
+ model_klass
+ end
+
+ def _set_wrapper_defaults(options, model=nil)
+ options = options.dup
+
+ unless options[:only] || options[:except]
+ model ||= _default_wrap_model
+ if model.respond_to?(:column_names)
+ options[:only] = model.column_names
+ end
+ end
+
+ unless options[:name]
+ model ||= _default_wrap_model
+ options[:name] = model ? model.to_s.demodulize.underscore :
+ controller_name.singularize
+ end
+
+ options[:only] = Array.wrap(options[:only]).collect(&:to_s) if options[:only]
+ options[:except] = Array.wrap(options[:except]).collect(&:to_s) if options[:except]
+ options[:format] = Array.wrap(options[:format])
+
+ self._wrapper_options = options
+ end
+ end
+
+ # Performs parameters wrapping upon the request. Will be called automatically
+ # by the metal call stack.
+ def process_action(*args)
+ if _wrapper_enabled?
+ wrapped_hash = _wrap_parameters request.request_parameters
+ wrapped_filtered_hash = _wrap_parameters request.filtered_parameters
+
+ # This will make the wrapped hash accessible from controller and view
+ request.parameters.merge! wrapped_hash
+ request.request_parameters.merge! wrapped_hash
+
+ # This will make the wrapped hash displayed in the log file
+ request.filtered_parameters.merge! wrapped_filtered_hash
+ end
+ super
+ end
+
+ private
+
+ # Returns the wrapper key which will use to stored wrapped parameters.
+ def _wrapper_key
+ _wrapper_options[:name]
+ end
+
+ # Returns the list of enabled formats.
+ def _wrapper_formats
+ _wrapper_options[:format]
+ end
+
+ # Returns the list of parameters which will be selected for wrapped.
+ def _wrap_parameters(parameters)
+ value = if only = _wrapper_options[:only]
+ parameters.slice(*only)
+ else
+ except = _wrapper_options[:except] || []
+ parameters.except(*(except + EXCLUDE_PARAMETERS))
+ end
+
+ { _wrapper_key => value }
+ end
+
+ # Checks if we should perform parameters wrapping.
+ def _wrapper_enabled?
+ ref = request.content_mime_type.try(:ref)
+ _wrapper_formats.include?(ref) && !request.request_parameters[_wrapper_key]
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb
index 59a3621f72..ebadb29ea7 100644
--- a/actionpack/lib/action_controller/metal/responder.rb
+++ b/actionpack/lib/action_controller/metal/responder.rb
@@ -68,7 +68,7 @@ module ActionController #:nodoc:
# respond_with(@project, @task)
# end
#
- # Giving an array of resources, you ensure that the responder will redirect to
+ # Giving several resources ensures that the responder will redirect to
# <code>project_task_url</code> instead of <code>task_url</code>.
#
# Namespaced and singleton resources require a symbol to be given, as in
@@ -77,6 +77,11 @@ module ActionController #:nodoc:
#
# respond_with(@project, :manager, @task)
#
+ # Note that if you give an array, it will be treated as a collection,
+ # so the following is not equivalent:
+ #
+ # respond_with [@project, :manager, @task]
+ #
# === Custom options
#
# <code>respond_with</code> also allow you to pass options that are forwarded
diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index b9bd49f670..3892a12407 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -2,7 +2,209 @@ require 'active_support/core_ext/file/path'
require 'rack/chunked'
module ActionController #:nodoc:
- # Methods for sending streaming templates back to the client.
+ # Allows views to be streamed back to the client as they are rendered.
+ #
+ # The default way Rails renders views is by first rendering the template
+ # and then the layout. The response is sent to the client after the whole
+ # template is rendered, all queries are made, and the layout is processed.
+ #
+ # Streaming inverts the rendering flow by rendering the layout first and
+ # streaming each part of the layout as they are processed. This allows the
+ # header of the HTML (which is usually in the layout) to be streamed back
+ # to client very quickly, allowing JavaScripts and stylesheets to be loaded
+ # earlier than usual.
+ #
+ # This approach was introduced in Rails 3.1 and is still improving. Several
+ # Rack middlewares may not work and you need to be careful when streaming.
+ # Those points are going to be addressed soon.
+ #
+ # In order to use streaming, you will need to use a Ruby version that
+ # supports fibers (fibers are supported since version 1.9.2 of the main
+ # Ruby implementation).
+ #
+ # == Examples
+ #
+ # Streaming can be added to a controller easily, all you need to do is
+ # call +stream+ in the controller class:
+ #
+ # class PostsController
+ # stream
+ # end
+ #
+ # The +stream+ method accepts the same options as +before_filter+ and friends:
+ #
+ # class PostsController
+ # stream :only => :index
+ # end
+ #
+ # You can also selectively turn on streaming for specific actions:
+ #
+ # class PostsController
+ # def index
+ # @posts = Post.scoped
+ # render :stream => true
+ # end
+ # end
+ #
+ # == When to use streaming
+ #
+ # Streaming may be considered to be overkill for lightweight actions like
+ # +new+ or +edit+. The real benefit of streaming is on expensive actions
+ # that, for example, do a lot of queries on the database.
+ #
+ # In such actions, you want to delay queries execution as much as you can.
+ # For example, imagine the following +dashboard+ action:
+ #
+ # def dashboard
+ # @posts = Post.all
+ # @pages = Page.all
+ # @articles = Article.all
+ # end
+ #
+ # Most of the queries here are happening in the controller. In order to benefit
+ # from streaming you would want to rewrite it as:
+ #
+ # def dashboard
+ # # Allow lazy execution of the queries
+ # @posts = Post.scoped
+ # @pages = Page.scoped
+ # @articles = Article.scoped
+ # render :stream => true
+ # end
+ #
+ # == Communication between layout and template
+ #
+ # When streaming, rendering happens top-down instead of inside-out.
+ # Rails starts with the layout, and the template is rendered later,
+ # when its +yield+ is reached.
+ #
+ # This means that, if your application currently relies on instance
+ # variables set in the template to be used in the layout, they won't
+ # work once you move to streaming. The proper way to communicate
+ # between layout and template, regardless of whether you use streaming
+ # or not, is by using +content_for+, +provide+ and +yield+.
+ #
+ # Take a simple example where the layout expects the template to tell
+ # which title to use:
+ #
+ # <html>
+ # <head><title><%= yield :title %></title></head>
+ # <body><%= yield %></body>
+ # </html>
+ #
+ # You would use +content_for+ in your template to specify the title:
+ #
+ # <%= content_for :title, "Main" %>
+ # Hello
+ #
+ # And the final result would be:
+ #
+ # <html>
+ # <head><title>Main</title></head>
+ # <body>Hello</body>
+ # </html>
+ #
+ # However, if +content_for+ is called several times, the final result
+ # would have all calls concatenated. For instance, if we have the following
+ # template:
+ #
+ # <%= content_for :title, "Main" %>
+ # Hello
+ # <%= content_for :title, " page" %>
+ #
+ # The final result would be:
+ #
+ # <html>
+ # <head><title>Main page</title></head>
+ # <body>Hello</body>
+ # </html>
+ #
+ # This means that, if you have <code>yield :title</code> in your layout
+ # and you want to use streaming, you would have to render the whole template
+ # (and eventually trigger all queries) before streaming the title and all
+ # assets, which kills the purpose of streaming. For this reason Rails 3.1
+ # introduces a new helper called +provide+ that does the same as +content_for+
+ # but tells the layout to stop searching for other entries and continue rendering.
+ #
+ # For instance, the template above using +provide+ would be:
+ #
+ # <%= provide :title, "Main" %>
+ # Hello
+ # <%= content_for :title, " page" %>
+ #
+ # Giving:
+ #
+ # <html>
+ # <head><title>Main</title></head>
+ # <body>Hello</body>
+ # </html>
+ #
+ # That said, when streaming, you need to properly check your templates
+ # and choose when to use +provide+ and +content_for+.
+ #
+ # == Headers, cookies, session and flash
+ #
+ # When streaming, the HTTP headers are sent to the client right before
+ # it renders the first line. This means that, modifying headers, cookies,
+ # session or flash after the template starts rendering will not propagate
+ # to the client.
+ #
+ # If you try to modify cookies, session or flash, an +ActionDispatch::ClosedError+
+ # will be raised, showing those objects are closed for modification.
+ #
+ # == Middlewares
+ #
+ # Middlewares that need to manipulate the body won't work with streaming.
+ # You should disable those middlewares whenever streaming in development
+ # or production. For instance, +Rack::Bug+ won't work when streaming as it
+ # needs to inject contents in the HTML body.
+ #
+ # Also +Rack::Cache+ won't work with streaming as it does not support
+ # streaming bodies yet. Whenever streaming Cache-Control is automatically
+ # set to "no-cache".
+ #
+ # == Errors
+ #
+ # When it comes to streaming, exceptions get a bit more complicated. This
+ # happens because part of the template was already rendered and streamed to
+ # the client, making it impossible to render a whole exception page.
+ #
+ # Currently, when an exception happens in development or production, Rails
+ # will automatically stream to the client:
+ #
+ # "><script type="text/javascript">window.location = "/500.html"</script></html>
+ #
+ # The first two characters (">) are required in case the exception happens
+ # while rendering attributes for a given tag. You can check the real cause
+ # for the exception in your logger.
+ #
+ # == Web server support
+ #
+ # Not all web servers support streaming out-of-the-box. You need to check
+ # the instructions for each of them.
+ #
+ # ==== Unicorn
+ #
+ # Unicorn supports streaming but it needs to be configured. For this, you
+ # need to create a config file as follow:
+ #
+ # # unicorn.config.rb
+ # listen 3000, :tcp_nopush => false
+ #
+ # And use it on initialization:
+ #
+ # unicorn_rails --config-file unicorn.config.rb
+ #
+ # You may also want to configure other parameters like <tt>:tcp_nodelay</tt>.
+ # Please check its documentation for more information: http://unicorn.bogomips.org/Unicorn/Configurator.html#method-i-listen
+ #
+ # If you are using Unicorn with Nginx, you may need to tweak Nginx.
+ # Streaming should work out of the box on Rainbows.
+ #
+ # ==== Passenger
+ #
+ # To be described.
+ #
module Streaming
extend ActiveSupport::Concern
@@ -51,7 +253,7 @@ module ActionController #:nodoc:
# Call render_to_body if we are streaming instead of usual +render+.
def _render_template(options) #:nodoc:
if options.delete(:stream)
- Rack::Chunked::Body.new view_context.render_body(options)
+ Rack::Chunked::Body.new view_renderer.render_body(view_context, options)
else
super
end
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
index 0fe2e6d1a6..1eadfc0390 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
+++ b/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
@@ -128,6 +128,8 @@ module HTML
# (no parent element).
# * <tt>:empty</tt> -- Match the element only if it has no child elements,
# and no text content.
+ # * <tt>:content(string)</tt> -- Match the element only if it has <tt>string</tt>
+ # as its text content (ignoring leading and trailing whitespace).
# * <tt>:only-child</tt> -- Match the element if it is the only child (element)
# of its parent element.
# * <tt>:only-of-type</tt> -- Match the element if it is the only child (element)
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
index 68ba1a81b5..980c658ab7 100644
--- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb
+++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
@@ -1,6 +1,13 @@
module ActionDispatch
module Http
module MimeNegotiation
+ extend ActiveSupport::Concern
+
+ included do
+ mattr_accessor :ignore_accept_header
+ self.ignore_accept_header = false
+ end
+
# The MIME type of the HTTP request, such as Mime::XML.
#
# For backward compatibility, the post \format is extracted from the
@@ -42,16 +49,14 @@ module ActionDispatch
formats.first
end
- BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/
-
def formats
- accept = @env['HTTP_ACCEPT']
-
@env["action_dispatch.request.formats"] ||=
if parameters[:format]
Array(Mime[parameters[:format]])
- elsif xhr? || (accept && accept !~ BROWSER_LIKE_ACCEPTS)
+ elsif use_accept_header && valid_accept_header
accepts
+ elsif xhr?
+ [Mime::JS]
else
[Mime::HTML]
end
@@ -87,6 +92,18 @@ module ActionDispatch
order.include?(Mime::ALL) ? formats.first : nil
end
+
+ protected
+
+ BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/
+
+ def valid_accept_header
+ xhr? || (accept && accept !~ BROWSER_LIKE_ACCEPTS)
+ end
+
+ def use_accept_header
+ !self.class.ignore_accept_header
+ end
end
end
end
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index f07ac44f7a..ccb866f4f7 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -17,16 +17,17 @@ module ActionDispatch
include ActionDispatch::Http::Upload
include ActionDispatch::Http::URL
- LOCALHOST = [/^127\.0\.0\.\d{1,3}$/, "::1", /^0:0:0:0:0:0:0:1(%.*)?$/].freeze
-
- %w[ AUTH_TYPE GATEWAY_INTERFACE
+ LOCALHOST = [/^127\.0\.0\.\d{1,3}$/, "::1", /^0:0:0:0:0:0:0:1(%.*)?$/].freeze
+ ENV_METHODS = %w[ AUTH_TYPE GATEWAY_INTERFACE
PATH_TRANSLATED REMOTE_HOST
REMOTE_IDENT REMOTE_USER REMOTE_ADDR
SERVER_NAME SERVER_PROTOCOL
HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
- HTTP_NEGOTIATE HTTP_PRAGMA ].each do |env|
+ HTTP_NEGOTIATE HTTP_PRAGMA ].freeze
+
+ ENV_METHODS.each do |env|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
def #{env.sub(/^HTTP_/n, '').downcase}
@env["#{env}"]
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index ac0fd9607d..d9c07d6ca3 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -165,7 +165,7 @@ module ActionDispatch
# such as 2 to catch <tt>["www"]</tt> instead of <tt>"www.rubyonrails"</tt>
# in "www.rubyonrails.co.uk".
def subdomain(tld_length = @@tld_length)
- subdomains(tld_length)
+ subdomains(tld_length).join(".")
end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
index 9c9ccc62f5..8ebf870b95 100644
--- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
@@ -59,7 +59,7 @@ module ActionDispatch
end
def set_session(env, sid, session_data, options)
- persistent_session_id!(session_data, sid)
+ session_data.merge("session_id" => sid)
end
def set_cookie(env, session_id, cookie)
diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
index b1adf3d2d1..c17c746096 100644
--- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
@@ -116,7 +116,7 @@ module ActionDispatch
end
def render(status, body)
- [status, {'Content-Type' => 'text/html', 'Content-Length' => body.bytesize.to_s}, [body]]
+ [status, {'Content-Type' => "text/html; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
end
def public_path
diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb
index 348f7b86b8..404943d720 100644
--- a/actionpack/lib/action_dispatch/middleware/static.rb
+++ b/actionpack/lib/action_dispatch/middleware/static.rb
@@ -2,10 +2,10 @@ require 'rack/utils'
module ActionDispatch
class FileHandler
- def initialize(root)
+ def initialize(root, cache_control)
@root = root.chomp('/')
@compiled_root = /^#{Regexp.escape(root)}/
- @file_server = ::Rack::File.new(@root)
+ @file_server = ::Rack::File.new(@root, cache_control)
end
def match?(path)
@@ -35,18 +35,15 @@ module ActionDispatch
end
class Static
- FILE_METHODS = %w(GET HEAD).freeze
-
- def initialize(app, path)
+ def initialize(app, path, cache_control=nil)
@app = app
- @file_handler = FileHandler.new(path)
+ @file_handler = FileHandler.new(path, cache_control)
end
def call(env)
- path = env['PATH_INFO'].chomp('/')
- method = env['REQUEST_METHOD']
-
- if FILE_METHODS.include?(method)
+ case env['REQUEST_METHOD']
+ when 'GET', 'HEAD'
+ path = env['PATH_INFO'].chomp('/')
if match = @file_handler.match?(path)
env["PATH_INFO"] = match
return @file_handler.call(env)
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
index 97f7cf0bbe..0c5bafa666 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
@@ -24,7 +24,7 @@
<div id="session_dump" style="display:none"><pre><%= debug_hash @request.session %></pre></div>
<p><a href="#" onclick="document.getElementById('env_dump').style.display='block'; return false;">Show env dump</a></p>
-<div id="env_dump" style="display:none"><pre><%= debug_hash @request.env %></pre></div>
+<div id="env_dump" style="display:none"><pre><%= debug_hash @request.env.slice(*@request.class::ENV_METHODS) %></pre></div>
<h2 style="margin-top: 30px">Response</h2>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb
index 2099fd069a..4b9d3141d5 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb
@@ -1,7 +1,7 @@
<h1>
<%=h @exception.class.to_s %>
<% if @request.parameters['controller'] %>
- in <%=h @request.parameters['controller'].classify.pluralize %>Controller<% if @request.parameters['action'] %>#<%=h @request.parameters['action'] %><% end %>
+ in <%=h @request.parameters['controller'].camelize %>Controller<% if @request.parameters['action'] %>#<%=h @request.parameters['action'] %><% end %>
<% end %>
</h1>
<pre><%=h @exception.message %></pre>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb
index 6c32fb17b8..6e71fd7ddc 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb
@@ -1,11 +1,13 @@
-<html xmlns="http://www.w3.org/1999/xhtml">
+<!DOCTYPE html>
+<html lang="en">
<head>
+ <meta charset="utf-8" />
<title>Action Controller: Exception caught</title>
<style>
body { background-color: #fff; color: #333; }
body, p, ol, ul, td {
- font-family: verdana, arial, helvetica, sans-serif;
+ font-family: helvetica, verdana, arial, sans-serif;
font-size: 13px;
line-height: 18px;
}
diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb
index 0a3bd5fe40..f51cc3711b 100644
--- a/actionpack/lib/action_dispatch/railtie.rb
+++ b/actionpack/lib/action_dispatch/railtie.rb
@@ -9,10 +9,12 @@ module ActionDispatch
config.action_dispatch.show_exceptions = true
config.action_dispatch.best_standards_support = true
config.action_dispatch.tld_length = 1
+ config.action_dispatch.ignore_accept_header = false
config.action_dispatch.rack_cache = {:metastore => "rails:/", :entitystore => "rails:/", :verbose => true}
initializer "action_dispatch.configure" do |app|
ActionDispatch::Http::URL.tld_length = app.config.action_dispatch.tld_length
+ ActionDispatch::Request.ignore_accept_header = app.config.action_dispatch.ignore_accept_header
end
end
end
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 1d09091dc7..97e8ccc9a5 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -3,6 +3,7 @@ require 'forwardable'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/object/to_query'
require 'active_support/core_ext/hash/slice'
+require 'active_support/core_ext/module/remove_method'
module ActionDispatch
module Routing
@@ -160,7 +161,7 @@ module ActionDispatch
# We use module_eval to avoid leaks
@module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
- remove_method :#{selector} if method_defined?(:#{selector})
+ remove_possible_method :#{selector}
def #{selector}(*args)
options = args.extract_options!
@@ -194,7 +195,7 @@ module ActionDispatch
hash_access_method = hash_access_name(name, kind)
@module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
- remove_method :#{selector} if method_defined?(:#{selector})
+ remove_possible_method :#{selector}
def #{selector}(*args)
url_for(#{hash_access_method}(*args))
end
@@ -240,6 +241,11 @@ module ActionDispatch
end
def eval_block(block)
+ if block.arity == 1
+ raise "You are using the old router DSL which has been removed in Rails 3.1. " <<
+ "Please check how to update your routes file at: http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/ " <<
+ "or add the rails_legacy_mapper gem to your Gemfile"
+ end
mapper = Mapper.new(self)
if default_scope
mapper.with_default_scope(default_scope, &block)
diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb
index e209978fb7..3335742d47 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/response.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb
@@ -42,7 +42,7 @@ module ActionDispatch
elsif type.is_a?(Symbol) && @response.response_code == Rack::Utils::SYMBOL_TO_STATUS_CODE[type]
assert_block("") { true } # to count the assertion
else
- assert(false, build_message(message, "Expected response to be a <?>, but was <?>", type, @response.response_code))
+ flunk(build_message(message, "Expected response to be a <?>, but was <?>", type, @response.response_code))
end
end
diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb
index 170ceb299a..584e5c3791 100644
--- a/actionpack/lib/action_pack/version.rb
+++ b/actionpack/lib/action_pack/version.rb
@@ -3,7 +3,7 @@ module ActionPack
MAJOR = 3
MINOR = 1
TINY = 0
- PRE = "beta"
+ PRE = "beta1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb
index 4547aceb28..92b6f7c770 100644
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -34,13 +34,12 @@ module ActionView
autoload :Context
autoload :Helpers
autoload :LookupContext
- autoload :Partials
autoload :PathSet
- autoload :Rendering
autoload :Template
autoload :TestCase
autoload_under "renderer" do
+ autoload :Renderer
autoload :AbstractRenderer
autoload :PartialRenderer
autoload :TemplateRenderer
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 87501d5b88..c98110353f 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -131,7 +131,7 @@ module ActionView #:nodoc:
#
# More builder documentation can be found at http://builder.rubyforge.org.
class Base
- include Helpers, Rendering, Partials, ::ERB::Util, Context
+ include Helpers, ::ERB::Util, Context
# Specify the proc used to decorate input tags that refer to attributes with errors.
cattr_accessor :field_error_proc
@@ -139,7 +139,6 @@ module ActionView #:nodoc:
# How to complete the streaming when an exception occurs.
# This is our best guess: first try to close the attribute, then the tag.
- # Currently this is private API and may be changed at *any* time.
cattr_accessor :streaming_completion_on_exception
@@streaming_completion_on_exception = %("><script type="text/javascript">window.location = "/500.html"</script></html>)
@@ -157,56 +156,61 @@ module ActionView #:nodoc:
def cache_template_loading=(value)
ActionView::Resolver.caching = value
end
- end
-
- attr_accessor :_template, :_view_flow
- attr_internal :request, :controller, :config, :assigns, :lookup_context
-
- delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, :to => :lookup_context
- delegate :request_forgery_protection_token, :params, :session, :cookies, :response, :headers,
- :flash, :action_name, :controller_name, :to => :controller
+ def process_view_paths(value)
+ value.is_a?(PathSet) ?
+ value.dup : ActionView::PathSet.new(Array.wrap(value))
+ end
- delegate :logger, :to => :controller, :allow_nil => true
+ def xss_safe? #:nodoc:
+ true
+ end
- def self.xss_safe? #:nodoc:
- true
+ # This method receives routes and helpers from the controller
+ # and return a subclass ready to be used as view context.
+ def prepare(routes, helpers) #:nodoc:
+ Class.new(self) do
+ if routes
+ include routes.url_helpers
+ include routes.mounted_helpers
+ end
+
+ if helpers
+ include helpers
+ self.helpers = helpers
+ end
+ end
+ end
end
- def self.process_view_paths(value)
- value.is_a?(PathSet) ?
- value.dup : ActionView::PathSet.new(Array.wrap(value))
- end
+ attr_accessor :view_renderer
+ attr_internal :config, :assigns
+
+ delegate :lookup_context, :to => :view_renderer
+ delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, :to => :lookup_context
def assign(new_assigns) # :nodoc:
@_assigns = new_assigns.each { |key, value| instance_variable_set("@#{key}", value) }
end
- def initialize(lookup_context = nil, assigns_for_first_render = {}, controller = nil, formats = nil) #:nodoc:
- assign(assigns_for_first_render)
- self.helpers = Module.new unless self.class.helpers
-
+ def initialize(context = nil, assigns = {}, controller = nil, formats = nil) #:nodoc:
@_config = {}
- @_virtual_path = nil
- @_view_flow = OutputFlow.new
- @output_buffer = nil
- if @_controller = controller
- @_request = controller.request if controller.respond_to?(:request)
- @_config = controller.config.inheritable_copy if controller.respond_to?(:config)
+ # Handle all these for backwards compatibility.
+ # TODO Provide a new API for AV::Base and deprecate this one.
+ if context.is_a?(ActionView::Renderer)
+ @view_renderer = context
+ elsif
+ lookup_context = context.is_a?(ActionView::LookupContext) ?
+ context : ActionView::LookupContext.new(context)
+ lookup_context.formats = formats if formats
+ lookup_context.prefixes = controller._prefixes if controller
+ @view_renderer = ActionView::Renderer.new(lookup_context)
end
- @_lookup_context = lookup_context.is_a?(ActionView::LookupContext) ?
- lookup_context : ActionView::LookupContext.new(lookup_context)
- @_lookup_context.formats = formats if formats
- end
-
- def controller_path
- @controller_path ||= controller && controller.controller_path
- end
-
- def controller_prefixes
- @controller_prefixes ||= controller && controller._prefixes
+ assign(assigns)
+ assign_controller(controller)
+ _prepare_context
end
ActiveSupport.run_load_hooks(:action_view, self)
diff --git a/actionpack/lib/action_view/context.rb b/actionpack/lib/action_view/context.rb
index a2a64de206..083856b2ca 100644
--- a/actionpack/lib/action_view/context.rb
+++ b/actionpack/lib/action_view/context.rb
@@ -8,33 +8,29 @@ module ActionView
# Action View contexts are supplied to Action Controller to render template.
# The default Action View context is ActionView::Base.
#
- # In order to work with ActionController, a Context must implement:
- #
- # 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[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
- # options<Hash>:: See _render_template_with_layout in ActionView::Base
- # partial<Boolean>:: Whether or not the template to render is a partial
- #
- # An Action View context can also mix in Action View's helpers. In order to
- # mix in helpers, a context must implement:
- #
- # Context#controller
- # - Returns an instance of AbstractController
- #
- # In any case, a context must mix in ActionView::Context, which stores compiled
- # template and provides the output buffer.
+ # In order to work with ActionController, a Context must just include this module.
+ # The initialization of the variables used by the context (@output_buffer, @view_flow,
+ # and @virtual_path) is responsibility of the object that includes this module
+ # (although you can call _prepare_context defined below).
module Context
include CompiledTemplates
- attr_accessor :output_buffer
+ attr_accessor :output_buffer, :view_flow
+
+ # Prepares the context by setting the appropriate instance variables.
+ # :api: plugin
+ def _prepare_context
+ @view_flow = OutputFlow.new
+ @output_buffer = nil
+ @virtual_path = nil
+ end
- def convert_to_model(object)
- object.respond_to?(:to_model) ? object.to_model : object
+ # Encapsulates the interaction with the view flow so it
+ # returns the correct buffer on yield. This is usually
+ # overwriten by helpers to add more behavior.
+ # :api: plugin
+ def _layout_for(name=nil)
+ name ||= :layout
+ view_flow.get(name).html_safe
end
end
end \ No newline at end of file
diff --git a/actionpack/lib/action_view/flows.rb b/actionpack/lib/action_view/flows.rb
index 386a06511f..a8f740713f 100644
--- a/actionpack/lib/action_view/flows.rb
+++ b/actionpack/lib/action_view/flows.rb
@@ -34,7 +34,7 @@ module ActionView
@view = view
@parent = nil
@child = view.output_buffer
- @content = view._view_flow.content
+ @content = view.view_flow.content
@fiber = fiber
@root = Fiber.current.object_id
end
diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb
index 205116f610..78a68db282 100644
--- a/actionpack/lib/action_view/helpers.rb
+++ b/actionpack/lib/action_view/helpers.rb
@@ -9,6 +9,7 @@ module ActionView #:nodoc:
autoload :AtomFeedHelper
autoload :CacheHelper
autoload :CaptureHelper
+ autoload :ControllerHelper
autoload :CsrfHelper
autoload :DateHelper
autoload :DebugHelper
@@ -19,6 +20,7 @@ module ActionView #:nodoc:
autoload :NumberHelper
autoload :OutputSafetyHelper
autoload :RecordTagHelper
+ autoload :RenderingHelper
autoload :SanitizeHelper
autoload :SprocketsHelper
autoload :TagHelper
@@ -38,6 +40,7 @@ module ActionView #:nodoc:
include AtomFeedHelper
include CacheHelper
include CaptureHelper
+ include ControllerHelper
include CsrfHelper
include DateHelper
include DebugHelper
@@ -48,6 +51,7 @@ module ActionView #:nodoc:
include NumberHelper
include OutputSafetyHelper
include RecordTagHelper
+ include RenderingHelper
include SanitizeHelper
include SprocketsHelper
include TagHelper
diff --git a/actionpack/lib/action_view/helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_paths.rb
index cb6737b94e..958f0e0a10 100644
--- a/actionpack/lib/action_view/helpers/asset_paths.rb
+++ b/actionpack/lib/action_view/helpers/asset_paths.rb
@@ -21,7 +21,6 @@ module ActionView
return source if is_uri?(source)
source = rewrite_extension(source, dir, ext) if ext
- source = "/#{dir}/#{source}" unless source[0] == ?/
source = rewrite_asset_path(source, dir)
if controller && include_host
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb
index 38860431b4..cd0f8c8878 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb
@@ -41,7 +41,8 @@ module ActionView
# Break out the asset path rewrite in case plugins wish to put the asset id
# someplace other than the query string.
- def rewrite_asset_path(source, path = nil)
+ def rewrite_asset_path(source, dir)
+ source = "/#{dir}/#{source}" unless source[0] == ?/
path = config.asset_path
if path && path.respond_to?(:call)
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
index 3d815b5e1f..e1ee0d0e1a 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
@@ -99,10 +99,9 @@ module ActionView
#
# When passing paths, the ".js" extension is optional.
#
- # To include the default JavaScript expansion pass <tt>:defaults</tt> as source.
- # By default, <tt>:defaults</tt> loads jQuery. If the application was generated
- # with "-j prototype" the libraries Prototype and Scriptaculous are loaded instead.
- # In any case, the defaults can be overridden in <tt>config/application.rb</tt>:
+ # If the application is not using the asset pipeline, to include the default JavaScript
+ # expansion pass <tt>:defaults</tt> as source. By default, <tt>:defaults</tt> loads jQuery,
+ # and that can be overridden in <tt>config/application.rb</tt>:
#
# config.action_view.javascript_expansions[:defaults] = %w(foo.js bar.js)
#
diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb
index 0139714240..ead7feb091 100644
--- a/actionpack/lib/action_view/helpers/capture_helper.rb
+++ b/actionpack/lib/action_view/helpers/capture_helper.rb
@@ -135,7 +135,7 @@ module ActionView
# for elements that will be fragment cached.
def content_for(name, content = nil, &block)
content = capture(&block) if block_given?
- result = @_view_flow.append(name, content) if content
+ result = @view_flow.append(name, content) if content
result unless content
end
@@ -146,7 +146,7 @@ module ActionView
# the layout to stop looking for more contents.
def provide(name, content = nil, &block)
content = capture(&block) if block_given?
- result = @_view_flow.append!(name, content) if content
+ result = @view_flow.append!(name, content) if content
result unless content
end
@@ -169,7 +169,7 @@ module ActionView
# </body>
# </html>
def content_for?(name)
- @_view_flow.get(name).present?
+ @view_flow.get(name).present?
end
# Use an alternate output buffer for the duration of the block.
diff --git a/actionpack/lib/action_view/helpers/controller_helper.rb b/actionpack/lib/action_view/helpers/controller_helper.rb
new file mode 100644
index 0000000000..e22331cb3c
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/controller_helper.rb
@@ -0,0 +1,21 @@
+module ActionView
+ module Helpers
+ # This module keeps all methods and behavior in ActionView
+ # that simply delegates to the controller.
+ module ControllerHelper #:nodoc:
+ attr_internal :controller, :request
+
+ delegate :request_forgery_protection_token, :params, :session, :cookies, :response, :headers,
+ :flash, :action_name, :controller_name, :controller_path, :to => :controller
+
+ delegate :logger, :to => :controller, :allow_nil => true
+
+ def assign_controller(controller)
+ if @_controller = controller
+ @_request = controller.request if controller.respond_to?(:request)
+ @_config = controller.config.inheritable_copy if controller.respond_to?(:config)
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 9277359d5c..eb8c96a6ae 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -94,9 +94,20 @@ module ActionView
when 43200..86399 then locale.t :about_x_months, :count => 1
when 86400..525599 then locale.t :x_months, :count => (distance_in_minutes.to_f / 43200.0).round
else
- distance_in_years = distance_in_minutes / 525600
- minute_offset_for_leap_year = (distance_in_years / 4) * 1440
- remainder = ((distance_in_minutes - minute_offset_for_leap_year) % 525600)
+ fyear = from_time.year
+ fyear += 1 if from_time.month >= 3
+ tyear = to_time.year
+ tyear -= 1 if to_time.month < 3
+ leap_years = (fyear > tyear) ? 0 : (fyear..tyear).count{|x| Date.leap?(x)}
+ minute_offset_for_leap_year = leap_years * 1440
+ # Discount the leap year days when calculating year distance.
+ # e.g. if there are 20 leap year days between 2 dates having the same day
+ # and month then the based on 365 days calculation
+ # the distance in years will come out to over 80 years when in written
+ # english it would read better as about 80 years.
+ minutes_with_offset = distance_in_minutes - minute_offset_for_leap_year
+ remainder = (minutes_with_offset % 525600)
+ distance_in_years = (minutes_with_offset / 525600)
if remainder < 131400
locale.t(:about_x_years, :count => distance_in_years)
elsif remainder < 394200
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index efe30441b1..b6bb90a3ce 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -102,6 +102,11 @@ module ActionView
include FormTagHelper
include UrlHelper
+ # Converts the given object to an ActiveModel compliant one.
+ def convert_to_model(object)
+ object.respond_to?(:to_model) ? object.to_model : object
+ end
+
# Creates a form and a scope around a specific model object that is used
# as a base for questioning about values for the fields.
#
@@ -1167,7 +1172,7 @@ module ActionView
class FormBuilder
# The methods which wrap a form helper call.
class_attribute :field_helpers
- self.field_helpers = (FormHelper.instance_method_names - ['form_for'])
+ self.field_helpers = FormHelper.instance_method_names - %w(form_for convert_to_model)
attr_accessor :object_name, :object, :options
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 49aa434020..9e0f8f32b7 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -74,6 +74,8 @@ module ActionView
# ==== Options
# * <tt>:multiple</tt> - If set to true the selection will allow multiple choices.
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
+ # * <tt>:include_blank</tt> - If set to true, an empty option will be create
+ # * <tt>:prompt</tt> - Create a prompt option with blank value and the text asking user to select something
# * Any other key creates standard HTML attributes for the tag.
#
# ==== Examples
@@ -99,18 +101,26 @@ module ActionView
# # => <select class="form_input" id="access" multiple="multiple" name="access[]"><option>Read</option>
# # <option>Write</option></select>
#
+ # select_tag "people", options_from_collection_for_select(@people, "id", "name"), :include_blank => true
+ # # => <select id="people" name="people"><option value=""></option><option value="1">David</option></select>
+ #
+ # select_tag "people", options_from_collection_for_select(@people, "id", "name"), :prompt => "Select something"
+ # # => <select id="people" name="people"><option value="">Select something</option><option value="1">David</option></select>
+ #
# select_tag "destination", "<option>NYC</option><option>Paris</option><option>Rome</option>", :disabled => true
# # => <select disabled="disabled" id="destination" name="destination"><option>NYC</option>
# # <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>".html_safe + option_tags
- else
- option_tags = "<option value=\"\"></option>".html_safe + option_tags
- end
+
+ if options.delete(:include_blank)
+ option_tags = "<option value=\"\"></option>".html_safe + option_tags
+ end
+
+ if prompt = options.delete(:prompt)
+ option_tags = "<option value=\"\">#{prompt}</option>".html_safe + option_tags
end
+
content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
end
diff --git a/actionpack/lib/action_view/helpers/rendering_helper.rb b/actionpack/lib/action_view/helpers/rendering_helper.rb
new file mode 100644
index 0000000000..47efdded42
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/rendering_helper.rb
@@ -0,0 +1,90 @@
+module ActionView
+ module Helpers
+ # = Action View Rendering
+ #
+ # Implements methods that allow rendering from a view context.
+ # In order to use this module, all you need is to implement
+ # view_renderer that returns an ActionView::Renderer object.
+ module RenderingHelper
+ # Returns the result of a render that's dictated by the options hash. The primary options are:
+ #
+ # * <tt>:partial</tt> - See ActionView::Partials.
+ # * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add :locals to pass in those.
+ # * <tt>:inline</tt> - Renders an inline template similar to how it's done in the controller.
+ # * <tt>:text</tt> - Renders the text passed in out.
+ #
+ # 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 = {}, locals = {}, &block)
+ case options
+ when Hash
+ if block_given?
+ view_renderer.render_partial(self, options.merge(:partial => options[:layout]), &block)
+ else
+ view_renderer.render(self, options)
+ end
+ else
+ view_renderer.render_partial(self, :partial => options, :locals => locals)
+ end
+ end
+
+ # Overwrites _layout_for in the context object so it supports the case a block is
+ # passed to a partial. 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 block && !name.is_a?(Symbol)
+ capture(*args, &block)
+ else
+ super
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/helpers/sprockets_helper.rb b/actionpack/lib/action_view/helpers/sprockets_helper.rb
index b43b91178c..ab98da9624 100644
--- a/actionpack/lib/action_view/helpers/sprockets_helper.rb
+++ b/actionpack/lib/action_view/helpers/sprockets_helper.rb
@@ -40,10 +40,10 @@ module ActionView
class AssetPaths < ActionView::Helpers::AssetPaths #:nodoc:
def rewrite_asset_path(source, dir)
- if source =~ /^\/#{dir}\/(.+)/
- assets.path($1, performing_caching?, dir)
- else
+ if source[0] == ?/
source
+ else
+ assets.path(source, performing_caching?, dir)
end
end
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index 06e2b027da..ca09c77b5c 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -265,60 +265,6 @@ module ActionView
text.html_safe.safe_concat("</p>")
end
- # Turns all URLs and e-mail addresses into clickable links. The <tt>:link</tt> option
- # will limit what should be linked. You can add HTML attributes to the links using
- # <tt>:html</tt>. Possible values for <tt>:link</tt> are <tt>:all</tt> (default),
- # <tt>:email_addresses</tt>, and <tt>:urls</tt>. If a block is given, each URL and
- # e-mail address is yielded and the result is used as the link text.
- #
- # ==== Examples
- # auto_link("Go to http://www.rubyonrails.org and say hello to david@loudthinking.com")
- # # => "Go to <a href=\"http://www.rubyonrails.org\">http://www.rubyonrails.org</a> and
- # # say hello to <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"
- #
- # auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :link => :urls)
- # # => "Visit <a href=\"http://www.loudthinking.com/\">http://www.loudthinking.com/</a>
- # # or e-mail david@loudthinking.com"
- #
- # auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :link => :email_addresses)
- # # => "Visit http://www.loudthinking.com/ or e-mail <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"
- #
- # post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com."
- # auto_link(post_body, :html => { :target => '_blank' }) do |text|
- # truncate(text, :length => 15)
- # end
- # # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.m...</a>.
- # Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."
- #
- #
- # You can still use <tt>auto_link</tt> with the old API that accepts the
- # +link+ as its optional second parameter and the +html_options+ hash
- # as its optional third parameter:
- # post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com."
- # auto_link(post_body, :urls)
- # # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\">http://www.myblog.com</a>.
- # Please e-mail me at me@email.com."
- #
- # auto_link(post_body, :all, :target => "_blank")
- # # => "Welcome to my new blog at <a href=\"http://www.myblog.com/\" target=\"_blank\">http://www.myblog.com</a>.
- # Please e-mail me at <a href=\"mailto:me@email.com\">me@email.com</a>."
- def auto_link(text, *args, &block)#link = :all, html = {}, &block)
- return '' if text.blank?
-
- options = args.size == 2 ? {} : args.extract_options! # this is necessary because the old auto_link API has a Hash as its last parameter
- unless args.empty?
- options[:link] = args[0] || :all
- options[:html] = args[1] || {}
- end
- options.reverse_merge!(:link => :all, :html => {})
-
- case options[:link].to_sym
- when :all then auto_link_email_addresses(auto_link_urls(text, options[:html], options, &block), options[:html], &block)
- when :email_addresses then auto_link_email_addresses(text, options[:html], &block)
- when :urls then auto_link_urls(text, options[:html], options, &block)
- end
- end
-
# Creates a Cycle object whose _to_s_ method cycles through elements of an
# array every time it is called. This can be used for example, to alternate
# classes for table rows. You can use named cycles to allow nesting in loops.
@@ -464,77 +410,6 @@ module ActionView
@_cycles = Hash.new unless defined?(@_cycles)
@_cycles[name] = cycle_object
end
-
- AUTO_LINK_RE = %r{
- (?: ([0-9A-Za-z+.:-]+:)// | www\. )
- [^\s<]+
- }x
-
- # regexps for determining context, used high-volume
- AUTO_LINK_CRE = [/<[^>]+$/, /^[^>]*>/, /<a\b.*?>/i, /<\/a>/i]
-
- AUTO_EMAIL_RE = /[\w.!#\$%+-]+@[\w-]+(?:\.[\w-]+)+/
-
- BRACKETS = { ']' => '[', ')' => '(', '}' => '{' }
-
- # Turns all urls into clickable links. If a block is given, each url
- # is yielded and the result is used as the link text.
- def auto_link_urls(text, html_options = {}, options = {})
- link_attributes = html_options.stringify_keys
- text.gsub(AUTO_LINK_RE) do
- scheme, href = $1, $&
- punctuation = []
-
- if auto_linked?($`, $')
- # do not change string; URL is already linked
- href
- else
- # don't include trailing punctuation character as part of the URL
- while href.sub!(/[^\w\/-]$/, '')
- punctuation.push $&
- if opening = BRACKETS[punctuation.last] and href.scan(opening).size > href.scan(punctuation.last).size
- href << punctuation.pop
- break
- end
- end
-
- link_text = block_given?? yield(href) : href
- href = 'http://' + href unless scheme
-
- unless options[:sanitize] == false
- link_text = sanitize(link_text)
- href = sanitize(href)
- end
- content_tag(:a, link_text, link_attributes.merge('href' => href), !!options[:sanitize]) + punctuation.reverse.join('')
- end
- end
- end
-
- # Turns all email addresses into clickable links. If a block is given,
- # each email is yielded and the result is used as the link text.
- def auto_link_email_addresses(text, html_options = {}, options = {})
- text.gsub(AUTO_EMAIL_RE) do
- text = $&
-
- if auto_linked?($`, $')
- text.html_safe
- else
- display_text = (block_given?) ? yield(text) : text
-
- unless options[:sanitize] == false
- text = sanitize(text)
- display_text = sanitize(display_text) unless text == display_text
- end
- mail_to text, display_text, html_options
- end
- end
- end
-
- # Detects already linked context or position in the middle of a tag
- def auto_linked?(left, right)
- (left =~ AUTO_LINK_CRE[0] and right =~ AUTO_LINK_CRE[1]) or
- (left.rindex(AUTO_LINK_CRE[2]) and $' !~ AUTO_LINK_CRE[3])
- end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb
index 26ebae6546..ec9bdd5320 100644
--- a/actionpack/lib/action_view/helpers/translation_helper.rb
+++ b/actionpack/lib/action_view/helpers/translation_helper.rb
@@ -63,8 +63,8 @@ module ActionView
private
def scope_key_by_partial(key)
if key.to_s.first == "."
- if (path = @_template && @_template.virtual_path)
- path.gsub(%r{/_?}, ".") + key.to_s
+ if @virtual_path
+ @virtual_path.gsub(%r{/_?}, ".") + key.to_s
else
raise "Cannot use t(#{key.inspect}) shortcut because path is not available"
end
diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb
index 06c607931d..06975ffa2f 100644
--- a/actionpack/lib/action_view/lookup_context.rb
+++ b/actionpack/lib/action_view/lookup_context.rb
@@ -9,6 +9,8 @@ module ActionView
# generate a key, given to view paths, used in the resolver cache lookup. Since
# this key is generated just once during the request, it speeds up all cache accesses.
class LookupContext #:nodoc:
+ attr_accessor :prefixes
+
mattr_accessor :fallbacks
@@fallbacks = FallbackFileSystemResolver.instances
@@ -58,10 +60,11 @@ module ActionView
end
end
- def initialize(view_paths, details = {})
+ def initialize(view_paths, details = {}, prefixes = [])
@details, @details_key = { :handlers => default_handlers }, nil
@frozen_formats, @skip_default_locale = false, false
@cache = true
+ @prefixes = prefixes
self.view_paths = view_paths
self.registered_detail_setters.each do |key, setter|
diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb
deleted file mode 100644
index c181689e62..0000000000
--- a/actionpack/lib/action_view/partials.rb
+++ /dev/null
@@ -1,226 +0,0 @@
-require 'active_support/core_ext/object/blank'
-
-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" and pass the instance variable @account in as a local variable
- # +account+ to the template for display.
- #
- # 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 <tt>ActionView::Partials::PartialRenderer</tt> has its object in a local variable with the same
- # name as the template. So, given
- #
- # <%= render :partial => "contract" %>
- #
- # within contract we'll get <tt>@contract</tt> in the local variable +contract+, as if we had written
- #
- # <%= render :partial => "contract", :locals => { :contract => @contract } %>
- #
- # With the <tt>:as</tt> option we can specify a different name for said local variable. For example, if we
- # wanted it to be +agreement+ instead of +contract+ we'd do:
- #
- # <%= render :partial => "contract", :as => 'agreement' %>
- #
- # The <tt>:object</tt> option can be used to directly specify which object is rendered into the partial;
- # useful when the template's object is elsewhere, in a different ivar or in a local variable for instance.
- #
- # Revisiting a previous example we could have written this code:
- #
- # <%= render :partial => "account", :object => @buyer %>
- #
- # <% @advertisements.each do |ad| %>
- # <%= render :partial => "ad", :object => ad %>
- # <% end %>
- #
- # The <tt>:object</tt> and <tt>:as</tt> options can be used together.
- #
- # == 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 <tt>:as</tt> option may be used when rendering partials.
- #
- # You can specify a partial to be rendered between elements via the <tt>:spacer_template</tt> option.
- # The following example will render <tt>advertiser/_ad_divider.html.erb</tt> between each ad partial:
- #
- # <%= render :partial => "ad", :collection => @advertisements, :spacer_template => "ad_divider" %>
- #
- # If the given <tt>:collection</tt> is nil or empty, <tt>render</tt> 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 with the RecordIdentifier
- #
- # Instead of explicitly naming the location of a partial, you can also let the RecordIdentifier do the work if
- # you're following its conventions for RecordIdentifier#partial_path. Examples:
- #
- # # @account is an Account instance, so it uses the RecordIdentifier to replace
- # # <%= render :partial => "accounts/account", :locals => { :account => @account} %>
- # <%= render :partial => @account %>
- #
- # # @posts is an array of Post instances, so it uses the RecordIdentifier to 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 is an Account instance, so it uses the RecordIdentifier to replace
- # # <%= render :partial => "accounts/account", :locals => { :account => @account } %>
- # <%= render(@account) %>
- #
- # # @posts is an array of Post instances, so it uses the RecordIdentifier to 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 &>
- # <div id="administrator">
- # Budget: $<%= user.budget %>
- # <%= yield %>
- # </div>
- #
- # <%# app/views/users/_editor.html.erb &>
- # <div id="editor">
- # Deadline: <%= user.deadline %>
- # <%= yield %>
- # </div>
- #
- # ...this will return:
- #
- # Here's the administrator:
- # <div id="administrator">
- # Budget: $<%= user.budget %>
- # Name: <%= user.name %>
- # </div>
- #
- # Here's the editor:
- # <div id="editor">
- # Deadline: <%= user.deadline %>
- # Name: <%= user.name %>
- # </div>
- #
- # 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:
- #
- # <div id="administrator">
- # Budget: $<%= user.budget %>
- # Title: <%= chief.name %>
- # </div>
- #
- # As you can see, the <tt>:locals</tt> 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 &>
- # <div class="user">
- # Budget: $<%= user.budget %>
- # <%= yield user %>
- # </div>
- #
- # <%# 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 &>
- # <div class="user">
- # <%= yield user, :header %>
- # Budget: $<%= user.budget %>
- # <%= yield user, :footer %>
- # </div>
- #
- # <%# 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 %>
- module Partials
- def _render_partial(options, &block) #:nodoc:
- _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/railtie.rb b/actionpack/lib/action_view/railtie.rb
index f20ba7e6d3..80391d72cc 100644
--- a/actionpack/lib/action_view/railtie.rb
+++ b/actionpack/lib/action_view/railtie.rb
@@ -6,7 +6,7 @@ module ActionView
class Railtie < Rails::Railtie
config.action_view = ActiveSupport::OrderedOptions.new
config.action_view.stylesheet_expansions = {}
- config.action_view.javascript_expansions = { :defaults => %w(jquery rails) }
+ config.action_view.javascript_expansions = { :defaults => %w(jquery jquery_ujs) }
initializer "action_view.cache_asset_ids" do |app|
unless app.config.cache_classes
diff --git a/actionpack/lib/action_view/renderer/abstract_renderer.rb b/actionpack/lib/action_view/renderer/abstract_renderer.rb
index 4a52b3172e..60c527beeb 100644
--- a/actionpack/lib/action_view/renderer/abstract_renderer.rb
+++ b/actionpack/lib/action_view/renderer/abstract_renderer.rb
@@ -3,9 +3,8 @@ module ActionView
delegate :find_template, :template_exists?, :with_fallbacks, :update_details,
:with_layout_format, :formats, :freeze_formats, :to => :@lookup_context
- def initialize(view)
- @view = view
- @lookup_context = view.lookup_context
+ def initialize(lookup_context)
+ @lookup_context = lookup_context
end
def render
diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb
index 10cd37d56f..a351fbc04f 100644
--- a/actionpack/lib/action_view/renderer/partial_renderer.rb
+++ b/actionpack/lib/action_view/renderer/partial_renderer.rb
@@ -1,44 +1,230 @@
+require 'active_support/core_ext/object/blank'
+
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" and pass the instance variable @account in as a local variable
+ # +account+ to the template for display.
+ #
+ # 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 <tt>ActionView::Partials::PartialRenderer</tt> has its object in a local variable with the same
+ # name as the template. So, given
+ #
+ # <%= render :partial => "contract" %>
+ #
+ # within contract we'll get <tt>@contract</tt> in the local variable +contract+, as if we had written
+ #
+ # <%= render :partial => "contract", :locals => { :contract => @contract } %>
+ #
+ # With the <tt>:as</tt> option we can specify a different name for said local variable. For example, if we
+ # wanted it to be +agreement+ instead of +contract+ we'd do:
+ #
+ # <%= render :partial => "contract", :as => 'agreement' %>
+ #
+ # The <tt>:object</tt> option can be used to directly specify which object is rendered into the partial;
+ # useful when the template's object is elsewhere, in a different ivar or in a local variable for instance.
+ #
+ # Revisiting a previous example we could have written this code:
+ #
+ # <%= render :partial => "account", :object => @buyer %>
+ #
+ # <% @advertisements.each do |ad| %>
+ # <%= render :partial => "ad", :object => ad %>
+ # <% end %>
+ #
+ # The <tt>:object</tt> and <tt>:as</tt> options can be used together.
+ #
+ # == 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 <tt>:as</tt> option may be used when rendering partials.
+ #
+ # You can specify a partial to be rendered between elements via the <tt>:spacer_template</tt> option.
+ # The following example will render <tt>advertiser/_ad_divider.html.erb</tt> between each ad partial:
+ #
+ # <%= render :partial => "ad", :collection => @advertisements, :spacer_template => "ad_divider" %>
+ #
+ # If the given <tt>:collection</tt> is nil or empty, <tt>render</tt> 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 with the RecordIdentifier
+ #
+ # Instead of explicitly naming the location of a partial, you can also let the RecordIdentifier do the work if
+ # you're following its conventions for RecordIdentifier#partial_path. Examples:
+ #
+ # # @account is an Account instance, so it uses the RecordIdentifier to replace
+ # # <%= render :partial => "accounts/account", :locals => { :account => @account} %>
+ # <%= render :partial => @account %>
+ #
+ # # @posts is an array of Post instances, so it uses the RecordIdentifier to 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 is an Account instance, so it uses the RecordIdentifier to replace
+ # # <%= render :partial => "accounts/account", :locals => { :account => @account } %>
+ # <%= render(@account) %>
+ #
+ # # @posts is an array of Post instances, so it uses the RecordIdentifier to 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 &>
+ # <div id="administrator">
+ # Budget: $<%= user.budget %>
+ # <%= yield %>
+ # </div>
+ #
+ # <%# app/views/users/_editor.html.erb &>
+ # <div id="editor">
+ # Deadline: <%= user.deadline %>
+ # <%= yield %>
+ # </div>
+ #
+ # ...this will return:
+ #
+ # Here's the administrator:
+ # <div id="administrator">
+ # Budget: $<%= user.budget %>
+ # Name: <%= user.name %>
+ # </div>
+ #
+ # Here's the editor:
+ # <div id="editor">
+ # Deadline: <%= user.deadline %>
+ # Name: <%= user.name %>
+ # </div>
+ #
+ # 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:
+ #
+ # <div id="administrator">
+ # Budget: $<%= user.budget %>
+ # Title: <%= chief.name %>
+ # </div>
+ #
+ # As you can see, the <tt>:locals</tt> 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 &>
+ # <div class="user">
+ # Budget: $<%= user.budget %>
+ # <%= yield user %>
+ # </div>
+ #
+ # <%# 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 &>
+ # <div class="user">
+ # <%= yield user, :header %>
+ # Budget: $<%= user.budget %>
+ # <%= yield user, :footer %>
+ # </div>
+ #
+ # <%# 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 #:nodoc:
PARTIAL_NAMES = Hash.new {|h,k| h[k] = {} }
- def initialize(view)
+ def initialize(*)
super
- @partial_names = PARTIAL_NAMES[@view.controller.class.name]
+ @partial_names = PARTIAL_NAMES[@lookup_context.prefixes.first]
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(context, options, block)
+ setup(context, options, block)
- def render
wrap_formats(@path) do
identifier = ((@template = find_partial) ? @template.identifier : @path)
@@ -77,7 +263,7 @@ module ActionView
locals[as] = object
content = @template.render(view, locals) do |*name|
- view._block_layout_for(*name, &block)
+ view._layout_for(*name, &block)
end
content = layout.render(view, locals){ content } if layout
@@ -86,6 +272,38 @@ module ActionView
private
+ def setup(context, options, block)
+ @view = context
+ 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 collection
if @options.key?(:collection)
collection = @options[:collection]
@@ -109,7 +327,7 @@ module ActionView
end
def find_template(path=@path, locals=@locals.keys)
- prefixes = path.include?(?/) ? [] : @view.controller_prefixes
+ prefixes = path.include?(?/) ? [] : @lookup_context.prefixes
@lookup_context.find_template(path, prefixes, true, locals)
end
@@ -150,7 +368,7 @@ module ActionView
object = object.to_model if object.respond_to?(:to_model)
object.class.model_name.partial_path.dup.tap do |partial|
- path = @view.controller_prefixes.first
+ path = @lookup_context.prefixes.first
partial.insert(0, "#{File.dirname(path)}/") if partial.include?(?/) && path.include?(?/)
end
end
diff --git a/actionpack/lib/action_view/renderer/renderer.rb b/actionpack/lib/action_view/renderer/renderer.rb
new file mode 100644
index 0000000000..bf1b5a7d22
--- /dev/null
+++ b/actionpack/lib/action_view/renderer/renderer.rb
@@ -0,0 +1,54 @@
+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.
+ 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:
+ _template_renderer.render(context, options)
+ end
+
+ # Direct access to partial rendering.
+ def render_partial(context, options, &block) #:nodoc:
+ _partial_renderer.render(context, options, block)
+ end
+
+ private
+
+ def _template_renderer #:nodoc:
+ @_template_renderer ||= TemplateRenderer.new(@lookup_context)
+ end
+
+ def _partial_renderer #:nodoc:
+ @_partial_renderer ||= PartialRenderer.new(@lookup_context)
+ 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
index 03aab444f8..1ccf5a8ddb 100644
--- a/actionpack/lib/action_view/renderer/streaming_template_renderer.rb
+++ b/actionpack/lib/action_view/renderer/streaming_template_renderer.rb
@@ -4,50 +4,11 @@
require 'fiber' if defined?(Fiber)
module ActionView
- # Consider the following layout:
- #
- # <%= yield :header %>
- # 2
- # <%= yield %>
- # 5
- # <%= yield :footer %>
- #
- # And template:
- #
- # <%= provide :header, "1" %>
- # 3
- # 4
- # <%= provide :footer, "6" %>
- #
- # It will stream:
- #
- # "1\n", "2\n", "3\n4\n", "5\n", "6\n"
- #
- # Notice that once you <%= yield %>, it will render the whole template
- # before streaming again. In the future, we can also support streaming
- # from the template and not only the layout.
- #
- # Also, notice we use +provide+ instead of +content_for+, as +provide+
- # gives the control back to the layout as soon as it is called.
- # With +content_for+, it would render all the template to find all
- # +content_for+ calls. For instance, consider this layout:
- #
- # <%= yield :header %>
- #
- # With this template:
- #
- # <%= content_for :header, "1" %>
- # <%= provide :header, "2" %>
- # <%= provide :header, "3" %>
- #
- # It will return "12\n" because +content_for+ continues rendering the
- # template but it is returns back to the layout as soon as it sees the
- # first +provide+.
- #
# == 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
@@ -60,11 +21,26 @@ module ActionView
def each(&block)
begin
@start.call(block)
- rescue
+ 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 = ActionController::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
@@ -106,7 +82,7 @@ module ActionView
# 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)
+ view.view_flow = StreamingFlow.new(view, fiber)
# Yo! Start the fiber!
fiber.resume
@@ -118,7 +94,7 @@ module ActionView
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)
+ view.view_flow.set(:layout, content)
# In case the layout continues yielding, we need to resume
# the fiber until all yields are handled.
diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb
index 6b5ead463f..a09cef8fef 100644
--- a/actionpack/lib/action_view/renderer/template_renderer.rb
+++ b/actionpack/lib/action_view/renderer/template_renderer.rb
@@ -3,7 +3,9 @@ require 'active_support/core_ext/array/wrap'
module ActionView
class TemplateRenderer < AbstractRenderer #:nodoc:
- def render(options)
+ def render(context, options)
+ @view = context
+
wrap_formats(options[:template] || options[:file]) do
template = determine_template(options)
freeze_formats(template.formats, true)
@@ -18,7 +20,7 @@ module ActionView
if options.key?(:text)
Template::Text.new(options[:text], formats.try(:first))
elsif options.key?(:file)
- with_fallbacks { find_template(options[:file], options[:prefixes], false, keys) }
+ with_fallbacks { find_template(options[:file], nil, false, keys) }
elsif options.key?(:inline)
handler = Template.handler_for_extension(options[:type] || "erb")
Template.new(options[:inline], "inline template", handler, :locals => keys)
@@ -46,7 +48,7 @@ module ActionView
if layout
view = @view
- view._view_flow.set(:layout, content)
+ view.view_flow.set(:layout, content)
layout.render(view, locals){ |*name| view._layout_for(*name) }
else
content
diff --git a/actionpack/lib/action_view/rendering.rb b/actionpack/lib/action_view/rendering.rb
deleted file mode 100644
index 2bce2fb045..0000000000
--- a/actionpack/lib/action_view/rendering.rb
+++ /dev/null
@@ -1,114 +0,0 @@
-require 'active_support/core_ext/object/try'
-
-module ActionView
- # = Action View Rendering
- module Rendering
- # Returns the result of a render that's dictated by the options hash. The primary options are:
- #
- # * <tt>:partial</tt> - See ActionView::Partials.
- # * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add :locals to pass in those.
- # * <tt>:inline</tt> - Renders an inline template similar to how it's done in the controller.
- # * <tt>:text</tt> - Renders the text passed in out.
- #
- # 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 = {}, locals = {}, &block)
- case options
- when Hash
- if block_given?
- _render_partial(options.merge(:partial => options[:layout]), &block)
- elsif options.key?(:partial)
- _render_partial(options)
- else
- _render_template(options)
- end
- else
- _render_partial(:partial => options, :locals => locals)
- 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(options)
- if options.key?(:partial)
- [_render_partial(options)]
- else
- StreamingTemplateRenderer.new(self).render(options)
- end
- end
-
- # 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)
- name = args.first
- name = :layout unless name.is_a?(Symbol)
- @_view_flow.get(name).html_safe
- end
-
- # Handle layout for calls from partials that supports blocks.
- def _block_layout_for(*args, &block)
- name = args.first
-
- if !name.is_a?(Symbol) && block
- capture(*args, &block)
- else
- _layout_for(*args)
- end
- end
-
- def _render_template(options) #:nodoc:
- _template_renderer.render(options)
- end
-
- def _template_renderer #:nodoc:
- @_template_renderer ||= TemplateRenderer.new(self)
- end
- end
-end
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index 6dfc4f68ae..98ecd15aa0 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -139,15 +139,12 @@ module ActionView
# we use a bang in this instrumentation because you don't want to
# consume this in production. This is only slow if it's being listened to.
def render(view, locals, buffer=nil, &block)
- old_template, view._template = view._template, self
ActiveSupport::Notifications.instrument("!render_template.action_view", :virtual_path => @virtual_path) do
compile!(view)
view.send(method_name, locals, buffer, &block)
end
rescue Exception => e
handle_render_error(view, e)
- ensure
- view._template = old_template
end
def mime_type
@@ -174,12 +171,7 @@ module ActionView
end
def inspect
- @inspect ||=
- if defined?(Rails.root)
- identifier.sub("#{Rails.root}/", '')
- else
- identifier
- end
+ @inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", '') : identifier
end
protected
@@ -264,9 +256,9 @@ module ActionView
# encoding of the code
source = <<-end_src
def #{method_name}(local_assigns, output_buffer)
- _old_output_buffer = @output_buffer;#{locals_code};#{code}
+ _old_virtual_path, @virtual_path = @virtual_path, #{@virtual_path.inspect};_old_output_buffer = @output_buffer;#{locals_code};#{code}
ensure
- @output_buffer = _old_output_buffer
+ @virtual_path, @output_buffer = _old_virtual_path, _old_output_buffer
end
end_src
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index 5c74bf843a..d0317a148b 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -1,4 +1,6 @@
require 'active_support/core_ext/object/blank'
+require 'active_support/core_ext/module/delegation'
+require 'active_support/core_ext/module/remove_method'
require 'action_controller'
require 'action_controller/test_case'
require 'action_view'
@@ -43,6 +45,7 @@ module ActionView
include AbstractController::Helpers
include ActionView::Helpers
+ delegate :lookup_context, :to => :controller
attr_accessor :controller, :output_buffer, :rendered
module ClassMethods
@@ -127,7 +130,7 @@ module ActionView
def say_no_to_protect_against_forgery!
_helpers.module_eval do
- remove_method :protect_against_forgery? if method_defined?(:protect_against_forgery?)
+ remove_possible_method :protect_against_forgery?
def protect_against_forgery?
false
end
@@ -147,9 +150,19 @@ module ActionView
module Locals
attr_accessor :locals
- def _render_partial(options)
- locals[options[:partial]] = options[:locals]
- super(options)
+ def render(options = {}, local_assigns = {})
+ case options
+ when Hash
+ if block_given?
+ locals[options[:layout]] = options[:locals]
+ elsif options.key?(:partial)
+ locals[options[:partial]] = options[:locals]
+ end
+ else
+ locals[options] = local_assigns
+ end
+
+ super
end
end