aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_controller
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_controller')
-rw-r--r--actionpack/lib/action_controller/base.rb11
-rw-r--r--actionpack/lib/action_controller/caching/actions.rb12
-rw-r--r--actionpack/lib/action_controller/caching/pages.rb2
-rw-r--r--actionpack/lib/action_controller/caching/sweeping.rb7
-rw-r--r--actionpack/lib/action_controller/metal/conditional_get.rb11
-rw-r--r--actionpack/lib/action_controller/metal/force_ssl.rb21
-rw-r--r--actionpack/lib/action_controller/metal/helpers.rb1
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb6
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb203
-rw-r--r--actionpack/lib/action_controller/metal/redirecting.rb2
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb2
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb20
-rw-r--r--actionpack/lib/action_controller/metal/responder.rb27
-rw-r--r--actionpack/lib/action_controller/metal/session_management.rb9
-rw-r--r--actionpack/lib/action_controller/metal/url_for.rb43
-rw-r--r--actionpack/lib/action_controller/railtie.rb33
-rw-r--r--actionpack/lib/action_controller/railties/helpers.rb22
-rw-r--r--actionpack/lib/action_controller/railties/paths.rb25
-rw-r--r--actionpack/lib/action_controller/record_identifier.rb4
-rw-r--r--actionpack/lib/action_controller/test_case.rb26
-rw-r--r--actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb11
21 files changed, 352 insertions, 146 deletions
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 3b82231b15..71425cd542 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -171,6 +171,16 @@ module ActionController
class Base < Metal
abstract!
+ # Shortcut helper that returns all the ActionController::Base modules except the ones passed in the argument:
+ #
+ # class MetalController
+ # ActionController::Base.without_modules(:ParamsWrapper, :Streaming).each do |left|
+ # include left
+ # end
+ # end
+ #
+ # This gives better control over what you want to exclude and makes it easier
+ # to create a bare controller class, instead of listing the modules required manually.
def self.without_modules(*modules)
modules = modules.map do |m|
m.is_a?(Symbol) ? ActionController.const_get(m) : m
@@ -192,7 +202,6 @@ module ActionController
Renderers::All,
ConditionalGet,
RackDelegation,
- SessionManagement,
Caching,
MimeResponds,
ImplicitRender,
diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb
index bd3b0b5df3..59ec197347 100644
--- a/actionpack/lib/action_controller/caching/actions.rb
+++ b/actionpack/lib/action_controller/caching/actions.rb
@@ -40,7 +40,7 @@ module ActionController #:nodoc:
#
# You can modify the default action cache path by passing a
# <tt>:cache_path</tt> option. This will be passed directly to
- # <tt>ActionCachePath.path_for</tt>. This is handy for actions with
+ # <tt>ActionCachePath.new</tt>. This is handy for actions with
# multiple possible routes that should be cached differently. If a
# block is given, it is called with the current controller instance.
#
@@ -103,8 +103,10 @@ module ActionController #:nodoc:
end
def _save_fragment(name, options)
- content = response_body
- content = content.join if content.is_a?(Array)
+ content = ""
+ response_body.each do |parts|
+ content << parts
+ end
if caching_allowed?
write_fragment(name, content, options)
@@ -168,14 +170,14 @@ module ActionController #:nodoc:
options.reverse_merge!(:format => @extension) if options.is_a?(Hash)
end
- path = controller.url_for(options).split(%r{://}).last
+ path = controller.url_for(options).split('://', 2).last
@path = normalize!(path)
end
private
def normalize!(path)
path << 'index' if path[-1] == ?/
- path << ".#{extension}" if extension and !path.split('?').first.ends_with?(".#{extension}")
+ path << ".#{extension}" if extension and !path.split('?', 2).first.ends_with?(".#{extension}")
URI.parser.unescape(path)
end
end
diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb
index 159f718029..307594d54a 100644
--- a/actionpack/lib/action_controller/caching/pages.rb
+++ b/actionpack/lib/action_controller/caching/pages.rb
@@ -99,7 +99,7 @@ module ActionController #:nodoc:
# caches_page :index
#
# # cache the index action except for JSON requests
- # caches_page :index, :if => Proc.new { |c| !c.request.format.json? }
+ # caches_page :index, :if => Proc.new { !request.format.json? }
#
# # don't gzip images
# caches_page :image, :gzip => false
diff --git a/actionpack/lib/action_controller/caching/sweeping.rb b/actionpack/lib/action_controller/caching/sweeping.rb
index 49cf70ec21..bb176ca3f9 100644
--- a/actionpack/lib/action_controller/caching/sweeping.rb
+++ b/actionpack/lib/action_controller/caching/sweeping.rb
@@ -54,6 +54,11 @@ module ActionController #:nodoc:
class Sweeper < ActiveRecord::Observer #:nodoc:
attr_accessor :controller
+ def initialize(*args)
+ super
+ @controller = nil
+ end
+
def before(controller)
self.controller = controller
callback(:before) if controller.perform_caching
@@ -88,7 +93,7 @@ module ActionController #:nodoc:
end
def method_missing(method, *arguments, &block)
- return unless @controller
+ super unless @controller
@controller.__send__(method, *arguments, &block)
end
end
diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb
index 1645400693..5b25a0d303 100644
--- a/actionpack/lib/action_controller/metal/conditional_get.rb
+++ b/actionpack/lib/action_controller/metal/conditional_get.rb
@@ -111,15 +111,22 @@ module ActionController
# Examples:
# expires_in 20.minutes
# expires_in 3.hours, :public => true
- # expires_in 3.hours, 'max-stale' => 5.hours, :public => true
+ # expires_in 3.hours, :public => true, :must_revalidate => true
#
# This method will overwrite an existing Cache-Control header.
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
+ #
+ # The method will also ensure a HTTP Date header for client compatibility.
def expires_in(seconds, options = {}) #:doc:
- response.cache_control.merge!(:max_age => seconds, :public => options.delete(:public))
+ response.cache_control.merge!(
+ :max_age => seconds,
+ :public => options.delete(:public),
+ :must_revalidate => options.delete(:must_revalidate)
+ )
options.delete(:private)
response.cache_control[:extras] = options.map {|k,v| "#{k}=#{v}"}
+ response.date = Time.now unless response.date?
end
# Sets a HTTP 1.1 Cache-Control header of <tt>no-cache</tt> so no caching should occur by the browser or
diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb
index b45f211e83..ac12cbb625 100644
--- a/actionpack/lib/action_controller/metal/force_ssl.rb
+++ b/actionpack/lib/action_controller/metal/force_ssl.rb
@@ -18,18 +18,33 @@ module ActionController
# Force the request to this particular controller or specified actions to be
# under HTTPS protocol.
#
- # Note that this method will not be effective on development environment.
+ # If you need to disable this for any reason (e.g. development) then you can use
+ # an +:if+ or +:unless+ condition.
+ #
+ # class AccountsController < ApplicationController
+ # force_ssl :if => :ssl_configured?
+ #
+ # def ssl_configured?
+ # !Rails.env.development?
+ # end
+ # end
#
# ==== Options
+ # * <tt>host</tt> - Redirect to a different host name
# * <tt>only</tt> - The callback should be run only for this action
# * <tt>except<tt> - The callback should be run for all actions except this action
+ # * <tt>if</tt> - A symbol naming an instance method or a proc; the callback
+ # will be called only when it returns a true value.
+ # * <tt>unless</tt> - A symbol naming an instance method or a proc; the callback
+ # will be called only when it returns a false value.
def force_ssl(options = {})
host = options.delete(:host)
before_filter(options) do
- if !request.ssl? && !Rails.env.development?
+ unless request.ssl?
redirect_options = {:protocol => 'https://', :status => :moved_permanently}
redirect_options.merge!(:host => host) if host
- flash.keep
+ redirect_options.merge!(:params => request.query_parameters)
+ flash.keep if respond_to?(:flash)
redirect_to redirect_options
end
end
diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb
index d070eaae5d..1a4bca12d2 100644
--- a/actionpack/lib/action_controller/metal/helpers.rb
+++ b/actionpack/lib/action_controller/metal/helpers.rb
@@ -52,6 +52,7 @@ module ActionController
module Helpers
extend ActiveSupport::Concern
+ class << self; attr_accessor :helpers_path; end
include AbstractController::Helpers
included do
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index 3d46163b74..44d2f740e6 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -263,7 +263,7 @@ module ActionController
# The quality of the implementation depends on a good choice.
# A nonce might, for example, be constructed as the base 64 encoding of
#
- # => time-stamp H(time-stamp ":" ETag ":" private-key)
+ # time-stamp H(time-stamp ":" ETag ":" private-key)
#
# where time-stamp is a server-generated time or other non-repeating value,
# ETag is the value of the HTTP ETag header associated with the requested entity,
@@ -279,7 +279,7 @@ module ActionController
#
# An implementation might choose not to accept a previously used nonce or a previously used digest, in order to
# protect against a replay attack. Or, an implementation might choose to use one-time nonces or digests for
- # POST or PUT requests and a time-stamp for GET requests. For more details on the issues involved see Section 4
+ # POST, PUT, or PATCH requests and a time-stamp for GET requests. For more details on the issues involved see Section 4
# of this document.
#
# The nonce is opaque to the client. Composed of Time, and hash of Time with secret
@@ -293,7 +293,7 @@ module ActionController
end
# Might want a shorter timeout depending on whether the request
- # is a PUT or POST, and if client is browser or web service.
+ # is a PATCH, PUT, or POST, and if client is browser or web service.
# Can be much shorter if the Stale directive is implemented. This would
# allow a user to use new nonce without prompting user again for their
# username and password.
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index ca383be76b..fbb5d01e86 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -6,8 +6,6 @@ module ActionController #:nodoc:
module MimeResponds
extend ActiveSupport::Concern
- include ActionController::ImplicitRender
-
included do
class_attribute :responder, :mimes_for_respond_to
self.responder = ActionController::Responder
@@ -58,7 +56,7 @@ module ActionController #:nodoc:
# Clear all mime types in <tt>respond_to</tt>.
#
def clear_respond_to
- self.mimes_for_respond_to = ActiveSupport::OrderedHash.new.freeze
+ self.mimes_for_respond_to = Hash.new.freeze
end
end
@@ -191,25 +189,112 @@ module ActionController #:nodoc:
def respond_to(*mimes, &block)
raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
- if response = retrieve_response_from_mimes(mimes, &block)
- response.call(nil)
+ if collector = retrieve_collector_from_mimes(mimes, &block)
+ response = collector.response
+ response ? response.call : render({})
end
end
- # respond_with wraps a resource around a responder for default representation.
- # First it invokes respond_to, if a response cannot be found (ie. no block
- # for the request was given and template was not available), it instantiates
- # an ActionController::Responder with the controller and resource.
+ # For a given controller action, respond_with generates an appropriate
+ # response based on the mime-type requested by the client.
#
- # ==== Example
+ # If the method is called with just a resource, as in this example -
#
- # def index
- # @users = User.all
- # respond_with(@users)
+ # class PeopleController < ApplicationController
+ # respond_to :html, :xml, :json
+ #
+ # def index
+ # @people = Person.all
+ # respond_with @people
+ # end
# end
#
- # It also accepts a block to be given. It's used to overwrite a default
- # response:
+ # then the mime-type of the response is typically selected based on the
+ # request's Accept header and the set of available formats declared
+ # by previous calls to the controller's class method +respond_to+. Alternatively
+ # the mime-type can be selected by explicitly setting <tt>request.format</tt> in
+ # the controller.
+ #
+ # If an acceptable format is not identified, the application returns a
+ # '406 - not acceptable' status. Otherwise, the default response is to render
+ # a template named after the current action and the selected format,
+ # e.g. <tt>index.html.erb</tt>. If no template is available, the behavior
+ # depends on the selected format:
+ #
+ # * for an html response - if the request method is +get+, an exception
+ # is raised but for other requests such as +post+ the response
+ # depends on whether the resource has any validation errors (i.e.
+ # assuming that an attempt has been made to save the resource,
+ # e.g. by a +create+ action) -
+ # 1. If there are no errors, i.e. the resource
+ # was saved successfully, the response +redirect+'s to the resource
+ # i.e. its +show+ action.
+ # 2. If there are validation errors, the response
+ # renders a default action, which is <tt>:new</tt> for a
+ # +post+ request or <tt>:edit</tt> for +put+.
+ # Thus an example like this -
+ #
+ # respond_to :html, :xml
+ #
+ # def create
+ # @user = User.new(params[:user])
+ # flash[:notice] = 'User was successfully created.' if @user.save
+ # respond_with(@user)
+ # end
+ #
+ # is equivalent, in the absence of <tt>create.html.erb</tt>, to -
+ #
+ # def create
+ # @user = User.new(params[:user])
+ # respond_to do |format|
+ # if @user.save
+ # flash[:notice] = 'User was successfully created.'
+ # format.html { redirect_to(@user) }
+ # format.xml { render :xml => @user }
+ # else
+ # format.html { render :action => "new" }
+ # format.xml { render :xml => @user }
+ # end
+ # end
+ # end
+ #
+ # * for a javascript request - if the template isn't found, an exception is
+ # raised.
+ # * for other requests - i.e. data formats such as xml, json, csv etc, if
+ # the resource passed to +respond_with+ responds to <code>to_<format></code>,
+ # the method attempts to render the resource in the requested format
+ # directly, e.g. for an xml request, the response is equivalent to calling
+ # <code>render :xml => resource</code>.
+ #
+ # === Nested resources
+ #
+ # As outlined above, the +resources+ argument passed to +respond_with+
+ # can play two roles. It can be used to generate the redirect url
+ # for successful html requests (e.g. for +create+ actions when
+ # no template exists), while for formats other than html and javascript
+ # it is the object that gets rendered, by being converted directly to the
+ # required format (again assuming no template exists).
+ #
+ # For redirecting successful html requests, +respond_with+ also supports
+ # the use of nested resources, which are supplied in the same way as
+ # in <code>form_for</code> and <code>polymorphic_url</code>. For example -
+ #
+ # def create
+ # @project = Project.find(params[:project_id])
+ # @task = @project.comments.build(params[:task])
+ # flash[:notice] = 'Task was successfully created.' if @task.save
+ # respond_with(@project, @task)
+ # end
+ #
+ # This would cause +respond_with+ to redirect to <code>project_task_url</code>
+ # instead of <code>task_url</code>. For request formats other than html or
+ # javascript, if multiple resources are passed in this way, it is the last
+ # one specified that is rendered.
+ #
+ # === Customizing response behavior
+ #
+ # Like +respond_to+, +respond_with+ may also be called with a block that
+ # can be used to overwrite any of the default responses, e.g. -
#
# def create
# @user = User.new(params[:user])
@@ -220,21 +305,32 @@ module ActionController #:nodoc:
# end
# end
#
- # All options given to respond_with are sent to the underlying responder,
- # except for the option :responder itself. Since the responder interface
- # is quite simple (it just needs to respond to call), you can even give
- # a proc to it.
- #
- # In order to use respond_with, first you need to declare the formats your
- # controller responds to in the class level with a call to <tt>respond_to</tt>.
+ # The argument passed to the block is an ActionController::MimeResponds::Collector
+ # object which stores the responses for the formats defined within the
+ # block. Note that formats with responses defined explicitly in this way
+ # do not have to first be declared using the class method +respond_to+.
+ #
+ # Also, a hash passed to +respond_with+ immediately after the specified
+ # resource(s) is interpreted as a set of options relevant to all
+ # formats. Any option accepted by +render+ can be used, e.g.
+ # respond_with @people, :status => 200
+ # However, note that these options are ignored after an unsuccessful attempt
+ # to save a resource, e.g. when automatically rendering <tt>:new</tt>
+ # after a post request.
+ #
+ # Two additional options are relevant specifically to +respond_with+ -
+ # 1. <tt>:location</tt> - overwrites the default redirect location used after
+ # a successful html +post+ request.
+ # 2. <tt>:action</tt> - overwrites the default render action used after an
+ # unsuccessful html +post+ request.
#
def respond_with(*resources, &block)
raise "In order to use respond_with, first you need to declare the formats your " <<
"controller responds to in the class level" if self.class.mimes_for_respond_to.empty?
- if response = retrieve_response_from_mimes(&block)
+ if collector = retrieve_collector_from_mimes(&block)
options = resources.size == 1 ? {} : resources.extract_options!
- options.merge!(:default_response => response)
+ options[:default_response] = collector.response
(options.delete(:responder) || self.class.responder).call(self, resources, options)
end
end
@@ -260,30 +356,59 @@ module ActionController #:nodoc:
end
end
- # Collects mimes and return the response for the negotiated format. Returns
- # nil if :not_acceptable was sent to the client.
+ # Returns a Collector object containing the appropriate mime-type response
+ # for the current request, based on the available responses defined by a block.
+ # In typical usage this is the block passed to +respond_with+ or +respond_to+.
#
- def retrieve_response_from_mimes(mimes=nil, &block) #:nodoc:
+ # Sends :not_acceptable to the client and returns nil if no suitable format
+ # is available.
+ #
+ def retrieve_collector_from_mimes(mimes=nil, &block) #:nodoc:
mimes ||= collect_mimes_from_class_level
- collector = Collector.new(mimes) { |options| default_render(options || {}) }
+ collector = Collector.new(mimes)
block.call(collector) if block_given?
+ format = collector.negotiate_format(request)
- if format = request.negotiate_mime(collector.order)
+ if format
self.content_type ||= format.to_s
- lookup_context.freeze_formats([format.to_sym])
- collector.response_for(format)
+ lookup_context.formats = [format.to_sym]
+ lookup_context.rendered_format = lookup_context.formats.first
+ collector
else
head :not_acceptable
nil
end
end
- class Collector #:nodoc:
+ # A container for responses available from the current controller for
+ # requests for different mime-types sent to a particular action.
+ #
+ # The public controller methods +respond_with+ and +respond_to+ may be called
+ # with a block that is used to define responses to different mime-types, e.g.
+ # for +respond_to+ :
+ #
+ # respond_to do |format|
+ # format.html
+ # format.xml { render :xml => @people.to_xml }
+ # end
+ #
+ # In this usage, the argument passed to the block (+format+ above) is an
+ # instance of the ActionController::MimeResponds::Collector class. This
+ # object serves as a container in which available responses can be stored by
+ # calling any of the dynamically generated, mime-type-specific methods such
+ # as +html+, +xml+ etc on the Collector. Each response is represented by a
+ # corresponding block if present.
+ #
+ # A subsequent call to #negotiate_format(request) will enable the Collector
+ # to determine which specific mime-type it should respond with for the current
+ # request, with this response then being accessible by calling #response.
+ #
+ class Collector
include AbstractController::Collector
- attr_accessor :order
+ attr_accessor :order, :format
- def initialize(mimes, &block)
- @order, @responses, @default_response = [], {}, block
+ def initialize(mimes)
+ @order, @responses = [], {}
mimes.each { |mime| send(mime) }
end
@@ -302,8 +427,12 @@ module ActionController #:nodoc:
@responses[mime_type] ||= block
end
- def response_for(mime)
- @responses[mime] || @responses[Mime::ALL] || @default_response
+ def response
+ @responses[format] || @responses[Mime::ALL]
+ end
+
+ def negotiate_format(request)
+ @format = request.negotiate_mime(order)
end
end
end
diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
index b07742e0e1..3ffb7ef426 100644
--- a/actionpack/lib/action_controller/metal/redirecting.rb
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -93,7 +93,7 @@ module ActionController
_compute_redirect_to_location options.call
else
url_for(options)
- end.gsub(/[\r\n]/, '')
+ end.gsub(/[\0\r\n]/, '')
end
end
end
diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb
index a677cdf15d..c5e7d4e357 100644
--- a/actionpack/lib/action_controller/metal/rendering.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -14,7 +14,7 @@ module ActionController
def render(*args) #:nodoc:
raise ::AbstractController::DoubleRenderError if response_body
super
- self.content_type ||= Mime[formats.first].to_s
+ self.content_type ||= Mime[lookup_context.rendered_format].to_s
response_body
end
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index afa9243f02..3081c14c09 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -37,6 +37,10 @@ module ActionController #:nodoc:
config_accessor :request_forgery_protection_token
self.request_forgery_protection_token ||= :authenticity_token
+ # Controls how unverified request will be handled
+ config_accessor :request_forgery_protection_method
+ self.request_forgery_protection_method ||= :reset_session
+
# Controls whether request forgery protection is turned on or not. Turned off by default only in test mode.
config_accessor :allow_forgery_protection
self.allow_forgery_protection = true if allow_forgery_protection.nil?
@@ -64,8 +68,10 @@ module ActionController #:nodoc:
# Valid Options:
#
# * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified.
+ # * <tt>:with</tt> - Set the method to handle unverified request. Valid values: <tt>:exception</tt> and <tt>:reset_session</tt> (default).
def protect_from_forgery(options = {})
self.request_forgery_protection_token ||= :authenticity_token
+ self.request_forgery_protection_method = options.delete(:with) if options.key?(:with)
prepend_before_filter :verify_authenticity_token, options
end
end
@@ -80,9 +86,19 @@ module ActionController #:nodoc:
end
# This is the method that defines the application behavior when a request is found to be unverified.
- # By default, \Rails resets the session when it finds an unverified request.
+ # By default, \Rails uses <tt>request_forgery_protection_method</tt> when it finds an unverified request:
+ #
+ # * <tt>:reset_session</tt> - Resets the session.
+ # * <tt>:exception</tt>: - Raises ActionController::InvalidAuthenticityToken exception.
def handle_unverified_request
- reset_session
+ case request_forgery_protection_method
+ when :exception
+ raise ActionController::InvalidAuthenticityToken
+ when :reset_session
+ reset_session
+ else
+ raise ArgumentError, 'Invalid request forgery protection method, use :exception or :reset_session'
+ end
end
# Returns true or false if a request is verified. Checks:
diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb
index 9500a349cb..1e8990495c 100644
--- a/actionpack/lib/action_controller/metal/responder.rb
+++ b/actionpack/lib/action_controller/metal/responder.rb
@@ -53,7 +53,7 @@ module ActionController #:nodoc:
# end
# end
#
- # The same happens for PUT and DELETE requests.
+ # The same happens for PATCH/PUT and DELETE requests.
#
# === Nested resources
#
@@ -116,8 +116,9 @@ module ActionController #:nodoc:
class Responder
attr_reader :controller, :request, :format, :resource, :resources, :options
- ACTIONS_FOR_VERBS = {
+ DEFAULT_ACTIONS_FOR_VERBS = {
:post => :new,
+ :patch => :edit,
:put => :edit
}
@@ -133,7 +134,7 @@ module ActionController #:nodoc:
end
delegate :head, :render, :redirect_to, :to => :controller
- delegate :get?, :post?, :put?, :delete?, :to => :request
+ delegate :get?, :post?, :patch?, :put?, :delete?, :to => :request
# Undefine :to_json and :to_yaml since it's defined on Object
undef_method(:to_json) if method_defined?(:to_json)
@@ -172,7 +173,7 @@ module ActionController #:nodoc:
# responds to :to_format and display it.
#
def to_format
- if get? || !has_errors?
+ if get? || !has_errors? || response_overridden?
default_render
else
display_errors
@@ -226,7 +227,11 @@ module ActionController #:nodoc:
# controller.
#
def default_render
- @default_response.call(options)
+ if @default_response
+ @default_response.call(options)
+ else
+ controller.default_render(options)
+ end
end
# Display is just a shortcut to render a resource with the current format.
@@ -260,19 +265,23 @@ module ActionController #:nodoc:
resource.respond_to?(:errors) && !resource.errors.empty?
end
- # By default, render the <code>:edit</code> action for HTML requests with failure, unless
- # the verb is POST.
+ # By default, render the <code>:edit</code> action for HTML requests with errors, unless
+ # the verb was POST.
#
def default_action
- @action ||= ACTIONS_FOR_VERBS[request.request_method_symbol]
+ @action ||= DEFAULT_ACTIONS_FOR_VERBS[request.request_method_symbol]
end
def resource_errors
- respond_to?("#{format}_resource_errors") ? send("#{format}_resource_errors") : resource.errors
+ respond_to?("#{format}_resource_errors", true) ? send("#{format}_resource_errors") : resource.errors
end
def json_resource_errors
{:errors => resource.errors}
end
+
+ def response_overridden?
+ @default_response.present?
+ end
end
end
diff --git a/actionpack/lib/action_controller/metal/session_management.rb b/actionpack/lib/action_controller/metal/session_management.rb
deleted file mode 100644
index 91d89ff9a4..0000000000
--- a/actionpack/lib/action_controller/metal/session_management.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-module ActionController #:nodoc:
- module SessionManagement #:nodoc:
- extend ActiveSupport::Concern
-
- module ClassMethods
-
- end
- end
-end
diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb
index 0b40b1fc4c..8e7b56dbcc 100644
--- a/actionpack/lib/action_controller/metal/url_for.rb
+++ b/actionpack/lib/action_controller/metal/url_for.rb
@@ -1,25 +1,25 @@
-# Includes +url_for+ into the host class. The class has to provide a +RouteSet+ by implementing
-# the <tt>_routes</tt> method. Otherwise, an exception will be raised.
-#
-# In addition to <tt>AbstractController::UrlFor</tt>, this module accesses the HTTP layer to define
-# url options like the +host+. In order to do so, this module requires the host class
-# to implement +env+ and +request+, which need to be a Rack-compatible.
-#
-# Example:
-#
-# class RootUrl
-# include ActionController::UrlFor
-# include Rails.application.routes.url_helpers
-#
-# delegate :env, :request, :to => :controller
-#
-# def initialize(controller)
-# @controller = controller
-# @url = root_path # named route from the application.
-# end
-# end
-#
module ActionController
+ # Includes +url_for+ into the host class. The class has to provide a +RouteSet+ by implementing
+ # the <tt>_routes</tt> method. Otherwise, an exception will be raised.
+ #
+ # In addition to <tt>AbstractController::UrlFor</tt>, this module accesses the HTTP layer to define
+ # url options like the +host+. In order to do so, this module requires the host class
+ # to implement +env+ and +request+, which need to be a Rack-compatible.
+ #
+ # Example:
+ #
+ # class RootUrl
+ # include ActionController::UrlFor
+ # include Rails.application.routes.url_helpers
+ #
+ # delegate :env, :request, :to => :controller
+ #
+ # def initialize(controller)
+ # @controller = controller
+ # @url = root_path # named route from the application.
+ # end
+ # end
+ #
module UrlFor
extend ActiveSupport::Concern
@@ -42,6 +42,5 @@ module ActionController
@_url_options
end
end
-
end
end
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index a288e69649..851a2c4aee 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -3,33 +3,32 @@ require "action_controller"
require "action_dispatch/railtie"
require "action_view/railtie"
require "abstract_controller/railties/routes_helpers"
-require "action_controller/railties/paths"
+require "action_controller/railties/helpers"
module ActionController
- class Railtie < Rails::Railtie
+ class Railtie < Rails::Railtie #:nodoc:
config.action_controller = ActiveSupport::OrderedOptions.new
- initializer "action_controller.logger" do
- ActiveSupport.on_load(:action_controller) { self.logger ||= Rails.logger }
- end
-
- initializer "action_controller.initialize_framework_caches" do
- ActiveSupport.on_load(:action_controller) { self.cache_store ||= Rails.cache }
- end
-
initializer "action_controller.assets_config", :group => :all do |app|
app.config.action_controller.assets_dir ||= app.config.paths["public"].first
end
+ initializer "action_controller.set_helpers_path" do |app|
+ ActionController::Helpers.helpers_path = app.helpers_paths
+ end
+
initializer "action_controller.set_configs" do |app|
paths = app.config.paths
options = app.config.action_controller
+ options.logger ||= Rails.logger
+ options.cache_store ||= Rails.cache
+
options.javascripts_dir ||= paths["public/javascripts"].first
options.stylesheets_dir ||= paths["public/stylesheets"].first
options.page_cache_directory ||= paths["public"].first
- # make sure readers methods get compiled
+ # Ensure readers methods get compiled
options.asset_path ||= app.config.asset_path
options.asset_host ||= app.config.asset_host
options.relative_url_root ||= app.config.relative_url_root
@@ -37,8 +36,16 @@ module ActionController
ActiveSupport.on_load(:action_controller) do
include app.routes.mounted_helpers
extend ::AbstractController::Railties::RoutesHelpers.with(app.routes)
- extend ::ActionController::Railties::Paths.with(app)
- options.each { |k,v| send("#{k}=", v) }
+ extend ::ActionController::Railties::Helpers
+
+ options.each do |k,v|
+ k = "#{k}="
+ if respond_to?(k)
+ send(k, v)
+ elsif !Base.respond_to?(k)
+ raise "Invalid option key: #{k}"
+ end
+ end
end
end
diff --git a/actionpack/lib/action_controller/railties/helpers.rb b/actionpack/lib/action_controller/railties/helpers.rb
new file mode 100644
index 0000000000..3985c6b273
--- /dev/null
+++ b/actionpack/lib/action_controller/railties/helpers.rb
@@ -0,0 +1,22 @@
+module ActionController
+ module Railties
+ module Helpers
+ def inherited(klass)
+ super
+ return unless klass.respond_to?(:helpers_path=)
+
+ if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_helpers_paths) }
+ paths = namespace.railtie_helpers_paths
+ else
+ paths = ActionController::Helpers.helpers_path
+ end
+
+ klass.helpers_path = paths
+
+ if klass.superclass == ActionController::Base && ActionController::Base.include_all_helpers
+ klass.helper :all
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/railties/paths.rb b/actionpack/lib/action_controller/railties/paths.rb
deleted file mode 100644
index bbe63149ad..0000000000
--- a/actionpack/lib/action_controller/railties/paths.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-module ActionController
- module Railties
- module Paths
- def self.with(app)
- Module.new do
- define_method(:inherited) do |klass|
- super(klass)
-
- if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_helpers_paths) }
- paths = namespace.railtie_helpers_paths
- else
- paths = app.helpers_paths
- end
-
- klass.helpers_path = paths
-
- if klass.superclass == ActionController::Base && ActionController::Base.include_all_helpers
- klass.helper :all
- end
- end
- end
- end
- end
- end
-end
diff --git a/actionpack/lib/action_controller/record_identifier.rb b/actionpack/lib/action_controller/record_identifier.rb
index 9c38ff44d8..18dda978b3 100644
--- a/actionpack/lib/action_controller/record_identifier.rb
+++ b/actionpack/lib/action_controller/record_identifier.rb
@@ -2,8 +2,8 @@ require 'active_support/core_ext/module'
module ActionController
# The record identifier encapsulates a number of naming conventions for dealing with records, like Active Records or
- # Active Resources or pretty much any other model type that has an id. These patterns are then used to try elevate
- # the view actions to a higher logical level. Example:
+ # pretty much any other model type that has an id. These patterns are then used to try elevate the view actions to
+ # a higher logical level. Example:
#
# # routes
# resources :posts
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 1e226fc336..9bd2e622ad 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -56,6 +56,9 @@ module ActionController
# # assert that the "new" view template was rendered
# assert_template "new"
#
+ # # assert that the exact template "admin/posts/new" was rendered
+ # assert_template %r{\Aadmin/posts/new\Z}
+ #
# # assert that the "_customer" partial was rendered twice
# assert_template :partial => '_customer', :count => 2
#
@@ -69,12 +72,16 @@ module ActionController
# assert_template :partial => '_customer', :locals => { :customer => @customer }
#
def assert_template(options = {}, message = nil)
+ # Force body to be read in case the
+ # template is being streamed
+ response.body
+
case options
- when NilClass, String, Symbol
+ when NilClass, String, Symbol, Regexp
options = options.to_s if Symbol === options
rendered = @templates
msg = message || sprintf("expecting <%s> but rendering with <%s>",
- options, rendered.keys)
+ options.inspect, rendered.keys)
assert_block(msg) do
if options
rendered.any? { |t,num| t.match(options) }
@@ -117,6 +124,8 @@ module ActionController
assert @partials.empty?,
"Expected no partials to be rendered"
end
+ else
+ raise ArgumentError, "assert_template only accepts a String, Symbol, Hash, Regexp, or nil"
end
end
end
@@ -225,7 +234,7 @@ module ActionController
# == Basic example
#
# Functional tests are written as follows:
- # 1. First, one uses the +get+, +post+, +put+, +delete+ or +head+ method to simulate
+ # 1. First, one uses the +get+, +post+, +patch+, +put+, +delete+ or +head+ method to simulate
# an HTTP request.
# 2. Then, one asserts whether the current state is as expected. "State" can be anything:
# the controller's HTTP response, the database contents, etc.
@@ -392,6 +401,11 @@ module ActionController
process(action, "POST", *args)
end
+ # Executes a request simulating PATCH HTTP method and set/volley the response
+ def patch(action, *args)
+ process(action, "PATCH", *args)
+ end
+
# Executes a request simulating PUT HTTP method and set/volley the response
def put(action, *args)
process(action, "PUT", *args)
@@ -465,7 +479,6 @@ module ActionController
@request.session["flash"].sweep
@controller.request = @request
- @controller.params.merge!(parameters)
build_request_uri(action, parameters)
@controller.class.class_eval { include Testing }
@controller.recycle!
@@ -491,11 +504,6 @@ module ActionController
end
end
- # Cause the action to be rescued according to the regular rules for rescue_action when the visitor is not local
- def rescue_action_in_public!
- @request.remote_addr = '208.77.188.166' # example.com
- end
-
included do
include ActionController::TemplateAssertions
include ActionDispatch::Assertions
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
index 24ffc28710..e9b50ff8ce 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
+++ b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
@@ -5,6 +5,7 @@ require 'active_support/core_ext/class/attribute'
module HTML
class Sanitizer
def sanitize(text, options = {})
+ validate_options(options)
return text unless sanitizeable?(text)
tokenize(text, options).join
end
@@ -27,6 +28,16 @@ module HTML
def process_node(node, result, options)
result << node.to_s
end
+
+ def validate_options(options)
+ if options[:tags] && !options[:tags].is_a?(Enumerable)
+ raise ArgumentError, "You should pass :tags as an Enumerable"
+ end
+
+ if options[:attributes] && !options[:attributes].is_a?(Enumerable)
+ raise ArgumentError, "You should pass :attributes as an Enumerable"
+ end
+ end
end
class FullSanitizer < Sanitizer