aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib')
-rw-r--r--actionpack/lib/abstract_controller/base.rb5
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb67
-rw-r--r--actionpack/lib/action_controller.rb1
-rw-r--r--actionpack/lib/action_controller/base.rb21
-rw-r--r--actionpack/lib/action_controller/metal/flash.rb2
-rw-r--r--actionpack/lib/action_controller/metal/force_ssl.rb2
-rw-r--r--actionpack/lib/action_controller/metal/head.rb4
-rw-r--r--actionpack/lib/action_controller/metal/helpers.rb6
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb12
-rw-r--r--actionpack/lib/action_controller/metal/redirecting.rb39
-rw-r--r--actionpack/lib/action_controller/metal/renderers.rb3
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb52
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb3
-rw-r--r--actionpack/lib/action_controller/metal/responder.rb2
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb19
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb23
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb6
-rw-r--r--actionpack/lib/action_dispatch/journey/gtg/transition_table.rb43
-rw-r--r--actionpack/lib/action_dispatch/journey/router/utils.rb5
-rw-r--r--actionpack/lib/action_dispatch/journey/visitors.rb50
-rw-r--r--actionpack/lib/action_dispatch/middleware/callbacks.rb12
-rw-r--r--actionpack/lib/action_dispatch/middleware/exception_wrapper.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/remote_ip.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb7
-rw-r--r--actionpack/lib/action_dispatch/request/session.rb12
-rw-r--r--actionpack/lib/action_dispatch/routing/inspector.rb3
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb62
-rw-r--r--actionpack/lib/action_dispatch/routing/polymorphic_routes.rb13
-rw-r--r--actionpack/lib/action_dispatch/routing/redirection.rb20
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb26
-rw-r--r--actionpack/lib/action_dispatch/routing/url_for.rb4
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/response.rb20
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb2
33 files changed, 318 insertions, 232 deletions
diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb
index a84ed17bd4..af5de815bb 100644
--- a/actionpack/lib/abstract_controller/base.rb
+++ b/actionpack/lib/abstract_controller/base.rb
@@ -114,11 +114,6 @@ module AbstractController
end
end
- # Define some internal variables that should not be propagated to the view.
- def self.default_protected_instance_vars
- []
- end
-
abstract!
# Calls the action going through the entire action dispatch stack.
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 41f19fba78..a6e230a088 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -1,5 +1,6 @@
require 'active_support/concern'
require 'active_support/core_ext/class/attribute'
+require 'set'
module AbstractController
class DoubleRenderError < Error
@@ -13,8 +14,14 @@ module AbstractController
module Rendering
extend ActiveSupport::Concern
- def self.default_protected_instance_vars
- super.concat [:@_action_name, :@_response_body, :@_formats, :@_prefixes, :@_config]
+ # Normalize arguments, options and then delegates render_to_body and
+ # sticks the result in self.response_body.
+ # :api: public
+ def render(*args, &block)
+ options = _normalize_render(*args, &block)
+ self.response_body = render_to_body(options)
+ _process_format(rendered_format)
+ self.response_body
end
# Raw rendering of a template to a string.
@@ -29,40 +36,49 @@ module AbstractController
# overridden in order to still return a string.
# :api: plugin
def render_to_string(*args, &block)
+ options = _normalize_render(*args, &block)
+ render_to_body(options)
end
- # Raw rendering of a template.
- # :api: plugin
- def render_to_body(options = {})
- end
-
- # Normalize arguments, options and then delegates render_to_body and
- # sticks the result in self.response_body.
+ # Performs the actual template rendering.
# :api: public
- def render(*args, &block)
+ def render_to_body(options = {})
end
- # Return Content-Type of rendered content
+ # Returns Content-Type of rendered content
# :api: public
def rendered_format
+ Mime::TEXT
end
+ DEFAULT_PROTECTED_INSTANCE_VARIABLES = Set.new %w(
+ @_action_name @_response_body @_formats @_prefixes @_config
+ @_view_context_class @_view_renderer @_lookup_context
+ @_routes @_db_runtime
+ ).map(&:to_sym)
+
# This method should return a hash with assigns.
# You can overwrite this configuration per controller.
# :api: public
def view_assigns
- hash = {}
- (instance_variables - self.class.default_protected_instance_vars).each do |name|
- hash[name[1..-1]] = instance_variable_get(name)
- end
- hash
+ protected_vars = _protected_ivars
+ variables = instance_variables
+
+ variables.reject! { |s| protected_vars.include? s }
+ variables.each_with_object({}) { |name, hash|
+ hash[name.slice(1, name.length)] = instance_variable_get(name)
+ }
end
# Normalize args by converting render "foo" to render :action => "foo" and
# render "foo/bar" to render :file => "foo/bar".
# :api: plugin
def _normalize_args(action=nil, options={})
- options
+ if action.is_a? Hash
+ action
+ else
+ options
+ end
end
# Normalize options.
@@ -76,5 +92,22 @@ module AbstractController
def _process_options(options)
options
end
+
+ # Process the rendered format.
+ # :api: private
+ def _process_format(format)
+ end
+
+ # Normalize args and options.
+ # :api: private
+ def _normalize_render(*args, &block)
+ options = _normalize_args(*args, &block)
+ _normalize_options(options)
+ options
+ end
+
+ def _protected_ivars # :nodoc:
+ DEFAULT_PROTECTED_INSTANCE_VARIABLES
+ end
end
end
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index d6b1908ccb..417d2efec2 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -13,7 +13,6 @@ module ActionController
autoload :Middleware
autoload_under "metal" do
- autoload :BasicRendering, 'action_controller/metal/rendering'
autoload :Compatibility
autoload :ConditionalGet
autoload :Cookies
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index a00eafc9ed..c84776ab7a 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -3,7 +3,7 @@ require "action_controller/metal/params_wrapper"
module ActionController
# The <tt>metal</tt> anonymous class was introduced to solve issue with including modules in <tt>ActionController::Base</tt>.
- # Modules needes to be included in particluar order. First wee need to have <tt>AbstractController::Rendering</tt> included,
+ # Modules needs to be included in particluar order. First we need to have <tt>AbstractController::Rendering</tt> included,
# next we should include actuall implementation which would be for example <tt>ActionView::Rendering</tt> and after that
# <tt>ActionController::Rendering</tt>. This order must be preserved and as we want to have middle module included dynamicaly
# <tt>metal</tt> class was introduced. It has <tt>AbstractController::Rendering</tt> included and is parent class of
@@ -14,7 +14,6 @@ module ActionController
#
metal = Class.new(Metal) do
include AbstractController::Rendering
- include ActionController::BasicRendering
end
# Action Controllers are the core of a web request in \Rails. They are made up of one or more actions that are executed
@@ -74,7 +73,7 @@ module ActionController
# <input type="text" name="post[address]" value="hyacintvej">
#
# A request stemming from a form holding these inputs will include <tt>{ "post" => { "name" => "david", "address" => "hyacintvej" } }</tt>.
- # If the address input had been named "post[address][street]", the params would have included
+ # If the address input had been named <tt>post[address][street]</tt>, the params would have included
# <tt>{ "post" => { "address" => { "street" => "hyacintvej" } } }</tt>. There's no limit to the depth of the nesting.
#
# == Sessions
@@ -261,11 +260,17 @@ module ActionController
include mod
end
- def self.default_protected_instance_vars
- super.concat [
- :@_status, :@_headers, :@_params, :@_env, :@_response, :@_request,
- :@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout
- ]
+ # Define some internal variables that should not be propagated to the view.
+ PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [
+ :@_status, :@_headers, :@_params, :@_env, :@_response, :@_request,
+ :@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout ]
+
+ def _protected_ivars # :nodoc:
+ PROTECTED_IVARS
+ end
+
+ def self.protected_instance_variables
+ PROTECTED_IVARS
end
ActiveSupport.run_load_hooks(:action_controller, self)
diff --git a/actionpack/lib/action_controller/metal/flash.rb b/actionpack/lib/action_controller/metal/flash.rb
index 1d77e331f8..65351284b9 100644
--- a/actionpack/lib/action_controller/metal/flash.rb
+++ b/actionpack/lib/action_controller/metal/flash.rb
@@ -37,7 +37,7 @@ module ActionController #:nodoc:
end
helper_method type
- _flash_types << type
+ self._flash_types += [type]
end
end
end
diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb
index b8afce42c9..a2cb6d1e66 100644
--- a/actionpack/lib/action_controller/metal/force_ssl.rb
+++ b/actionpack/lib/action_controller/metal/force_ssl.rb
@@ -48,7 +48,7 @@ module ActionController
# You can pass any of the following options to affect the redirect status and response
# * <tt>status</tt> - Redirect with a custom status (default is 301 Moved Permanently)
# * <tt>flash</tt> - Set a flash message when redirecting
- # * <tt>alert</tt> - Set a alert message when redirecting
+ # * <tt>alert</tt> - Set an alert message when redirecting
# * <tt>notice</tt> - Set a notice message when redirecting
#
# ==== Action Options
diff --git a/actionpack/lib/action_controller/metal/head.rb b/actionpack/lib/action_controller/metal/head.rb
index 8237db15ca..43407f5b78 100644
--- a/actionpack/lib/action_controller/metal/head.rb
+++ b/actionpack/lib/action_controller/metal/head.rb
@@ -1,8 +1,6 @@
module ActionController
module Head
- extend ActiveSupport::Concern
-
- # Return a response that has no content (merely headers). The options
+ # Returns a response that has no content (merely headers). The options
# argument is interpreted to be a hash of header names and values.
# This allows you to easily return a response that consists only of
# significant headers:
diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb
index b53ae7f29f..a9c3e438fb 100644
--- a/actionpack/lib/action_controller/metal/helpers.rb
+++ b/actionpack/lib/action_controller/metal/helpers.rb
@@ -73,7 +73,11 @@ module ActionController
# Provides a proxy to access helpers methods from outside the view.
def helpers
- @helper_proxy ||= ActionView::Base.new.extend(_helpers)
+ @helper_proxy ||= begin
+ proxy = ActionView::Base.new
+ proxy.config = config.inheritable_copy
+ proxy.extend(_helpers)
+ end
end
# Overwrite modules_for_helpers to accept :all as argument, which loads
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index 834d44f045..84ade41036 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -326,6 +326,7 @@ module ActionController #:nodoc:
if collector = retrieve_collector_from_mimes(&block)
options = resources.size == 1 ? {} : resources.extract_options!
+ options = options.clone
options[:default_response] = collector.response
(options.delete(:responder) || self.class.responder).call(self, resources, options)
end
@@ -364,9 +365,7 @@ module ActionController #:nodoc:
format = collector.negotiate_format(request)
if format
- self.content_type ||= format.to_s
- lookup_context.formats = [format.to_sym]
- lookup_context.rendered_format = lookup_context.formats.first
+ _process_format(format)
collector
else
raise ActionController::UnknownFormat
@@ -397,10 +396,10 @@ module ActionController #:nodoc:
# request, with this response then being accessible by calling #response.
class Collector
include AbstractController::Collector
- attr_accessor :order, :format
+ attr_accessor :format
def initialize(mimes)
- @order, @responses = [], {}
+ @responses = {}
mimes.each { |mime| send(mime) }
end
@@ -415,7 +414,6 @@ module ActionController #:nodoc:
def custom(mime_type, &block)
mime_type = Mime::Type.lookup(mime_type.to_s) unless mime_type.is_a?(Mime::Type)
- @order << mime_type
@responses[mime_type] ||= block
end
@@ -424,7 +422,7 @@ module ActionController #:nodoc:
end
def negotiate_format(request)
- @format = request.negotiate_mime(order)
+ @format = request.negotiate_mime(@responses.keys)
end
end
end
diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
index e9031f3fac..ab14a61b97 100644
--- a/actionpack/lib/action_controller/metal/redirecting.rb
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -71,6 +71,26 @@ module ActionController
self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.h(location)}\">redirected</a>.</body></html>"
end
+ def _compute_redirect_to_location(options) #:nodoc:
+ case options
+ # The scheme name consist of a letter followed by any combination of
+ # letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
+ # characters; and is terminated by a colon (":").
+ # See http://tools.ietf.org/html/rfc3986#section-3.1
+ # The protocol relative scheme starts with a double slash "//".
+ when /\A([a-z][a-z\d\-+\.]*:|\/\/).*/i
+ options
+ when String
+ request.protocol + request.host_with_port + options
+ when :back
+ request.headers["Referer"] or raise RedirectBackError
+ when Proc
+ _compute_redirect_to_location options.call
+ else
+ url_for(options)
+ end.delete("\0\r\n")
+ end
+
private
def _extract_redirect_to_status(options, response_status)
if options.is_a?(Hash) && options.key?(:status)
@@ -81,24 +101,5 @@ module ActionController
302
end
end
-
- def _compute_redirect_to_location(options)
- case options
- # The scheme name consist of a letter followed by any combination of
- # letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
- # characters; and is terminated by a colon (":").
- # The protocol relative scheme starts with a double slash "//"
- when %r{\A(\w[\w+.-]*:|//).*}
- options
- when String
- request.protocol + request.host_with_port + options
- when :back
- request.headers["Referer"] or raise RedirectBackError
- when Proc
- _compute_redirect_to_location options.call
- else
- url_for(options)
- end.delete("\0\r\n")
- end
end
end
diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb
index abed6e53cc..62a3844b04 100644
--- a/actionpack/lib/action_controller/metal/renderers.rb
+++ b/actionpack/lib/action_controller/metal/renderers.rb
@@ -8,8 +8,7 @@ module ActionController
class MissingRenderer < LoadError
def initialize(format)
- @format = format
- super("No renderer defined for format: #{@format}")
+ super "No renderer defined for format: #{format}"
end
end
diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb
index 21224b9c3b..5c48b4ab98 100644
--- a/actionpack/lib/action_controller/metal/rendering.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -1,36 +1,4 @@
module ActionController
- # Basic rendering implements the most minimal rendering layer.
- # It only supports rendering :text and :nothing. Passing any other option will
- # result in `UnsupportedOperationError` exception. For more functionality like
- # different formats, layouts etc. you should use `ActionView` gem.
- module BasicRendering
- extend ActiveSupport::Concern
-
- # Render text or nothing (empty string) to response_body
- # :api: public
- def render(*args, &block)
- super(*args, &block)
- opts = args.first
- if opts.has_key?(:text) && opts[:text].present?
- self.response_body = opts[:text]
- elsif opts.has_key?(:nothing) && opts[:nothing]
- self.response_body = " "
- else
- raise UnsupportedOperationError
- end
- end
-
- def rendered_format
- Mime::TEXT
- end
-
- class UnsupportedOperationError < StandardError
- def initialize
- super "Unsupported render operation. BasicRendering supports only :text and :nothing options. For more, you need to include ActionView."
- end
- end
- end
-
module Rendering
extend ActiveSupport::Concern
@@ -44,27 +12,31 @@ module ActionController
def render(*args) #:nodoc:
raise ::AbstractController::DoubleRenderError if self.response_body
super
- self.content_type ||= rendered_format.to_s
- self.response_body
end
# Overwrite render_to_string because body can now be set to a rack body.
def render_to_string(*)
- if self.response_body = super
+ result = super
+ if result.respond_to?(:each)
string = ""
- self.response_body.each { |r| string << r }
+ result.each { |r| string << r }
string
+ else
+ result
end
- ensure
- self.response_body = nil
end
- def render_to_body(*)
- super || " "
+ def render_to_body(options = {})
+ super || options[:text].presence || ' '
end
private
+ def _process_format(format)
+ super
+ self.content_type ||= format.to_s
+ end
+
# Normalize arguments by catching blocks and setting them on :update.
def _normalize_args(action=nil, options={}, &blk) #:nodoc:
options = super
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index 573c739da4..bd64b1f812 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -124,6 +124,9 @@ module ActionController #:nodoc:
@loaded = true
end
+ # no-op
+ def destroy; end
+
def exists?
true
end
diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb
index 66ff34a794..b4ba169e8f 100644
--- a/actionpack/lib/action_controller/metal/responder.rb
+++ b/actionpack/lib/action_controller/metal/responder.rb
@@ -144,7 +144,7 @@ module ActionController #:nodoc:
undef_method(:to_json) if method_defined?(:to_json)
undef_method(:to_yaml) if method_defined?(:to_yaml)
- # Initializes a new responder an invoke the proper format. If the format is
+ # Initializes a new responder and invokes the proper format. If the format is
# not defined, call to_format.
#
def self.call(*args)
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index ae600b1ebe..b4948d99a8 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -17,7 +17,7 @@ module ActionController
def initialize(param) # :nodoc:
@param = param
- super("param not found: #{param}")
+ super("param is missing or the value is empty: #{param}")
end
end
@@ -284,7 +284,14 @@ module ActionController
# params.fetch(:none, 'Francesco') # => "Francesco"
# params.fetch(:none) { 'Francesco' } # => "Francesco"
def fetch(key, *args)
- convert_hashes_to_parameters(key, super)
+ value = super
+ # Don't rely on +convert_hashes_to_parameters+
+ # so as to not mutate via a +fetch+
+ if value.is_a?(Hash)
+ value = self.class.new(value)
+ value.permit! if permitted?
+ end
+ value
rescue KeyError
raise ActionController::ParameterMissing.new(key)
end
@@ -334,7 +341,7 @@ module ActionController
def each_element(object)
if object.is_a?(Array)
object.map { |el| yield el }.compact
- elsif object.is_a?(Hash) && object.keys.all? { |k| k =~ /\A-?\d+\z/ }
+ elsif fields_for_style?(object)
hash = object.class.new
object.each { |k,v| hash[k] = yield v }
hash
@@ -343,6 +350,10 @@ module ActionController
end
end
+ def fields_for_style?(object)
+ object.is_a?(Hash) && object.all? { |k, v| k =~ /\A-?\d+\z/ && v.is_a?(Hash) }
+ end
+
def unpermitted_parameters!(params)
unpermitted_keys = unpermitted_keys(params)
if unpermitted_keys.any?
@@ -421,7 +432,7 @@ module ActionController
# Slicing filters out non-declared keys.
slice(*filter.keys).each do |key, value|
- return unless value
+ next unless value
if filter[key] == EMPTY_ARRAY
# Declaration { comment_ids: [] }.
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index aba8f66118..99b81c898f 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -299,17 +299,24 @@ module ActionDispatch
LOCALHOST =~ remote_addr && LOCALHOST =~ remote_ip
end
- protected
+ # Extracted into ActionDispatch::Request::Utils.deep_munge, but kept here for backwards compatibility.
+ def deep_munge(hash)
+ ActiveSupport::Deprecation.warn(
+ "This method has been extracted into ActionDispatch::Request::Utils.deep_munge. Please start using that instead."
+ )
- def parse_query(qs)
- Utils.deep_munge(super)
+ Utils.deep_munge(hash)
end
- private
+ protected
+ def parse_query(qs)
+ Utils.deep_munge(super)
+ end
- def check_method(name)
- HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
- name
- end
+ private
+ def check_method(name)
+ HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
+ name
+ end
end
end
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index d3696cbb8a..5247e61a23 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -41,7 +41,7 @@ module ActionDispatch # :nodoc:
# Get and set headers for this response.
attr_accessor :header
-
+
alias_method :headers=, :header=
alias_method :headers, :header
@@ -181,9 +181,9 @@ module ActionDispatch # :nodoc:
end
alias_method :status_message, :message
- def respond_to?(method)
+ def respond_to?(method, include_private = false)
if method.to_s == 'to_path'
- stream.respond_to?(:to_path)
+ stream.respond_to?(method)
else
super
end
diff --git a/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb b/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb
index da0cddd93c..5a79059ed6 100644
--- a/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb
+++ b/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb
@@ -9,8 +9,8 @@ module ActionDispatch
attr_reader :memos
def initialize
- @regexp_states = Hash.new { |h,k| h[k] = {} }
- @string_states = Hash.new { |h,k| h[k] = {} }
+ @regexp_states = {}
+ @string_states = {}
@accepting = {}
@memos = Hash.new { |h,k| h[k] = [] }
end
@@ -43,9 +43,7 @@ module ActionDispatch
move_string(t, a).concat(move_regexp(t, a))
end
- def to_json
- require 'json'
-
+ def as_json(options = nil)
simple_regexp = Hash.new { |h,k| h[k] = {} }
@regexp_states.each do |from, hash|
@@ -54,11 +52,11 @@ module ActionDispatch
end
end
- JSON.dump({
+ {
regexp_states: simple_regexp,
string_states: @string_states,
accepting: @accepting
- })
+ }
end
def to_svg
@@ -111,14 +109,8 @@ module ActionDispatch
end
def []=(from, to, sym)
- case sym
- when String
- @string_states[from][sym] = to
- when Regexp
- @regexp_states[from][sym] = to
- else
- raise ArgumentError, 'unknown symbol: %s' % sym.class
- end
+ to_mappings = states_hash_for(sym)[from] ||= {}
+ to_mappings[sym] = to
end
def states
@@ -137,18 +129,35 @@ module ActionDispatch
private
+ def states_hash_for(sym)
+ case sym
+ when String
+ @string_states
+ when Regexp
+ @regexp_states
+ else
+ raise ArgumentError, 'unknown symbol: %s' % sym.class
+ end
+ end
+
def move_regexp(t, a)
return [] if t.empty?
t.map { |s|
- @regexp_states[s].map { |re, v| re === a ? v : nil }
+ if states = @regexp_states[s]
+ states.map { |re, v| re === a ? v : nil }
+ end
}.flatten.compact.uniq
end
def move_string(t, a)
return [] if t.empty?
- t.map { |s| @string_states[s][a] }.compact
+ t.map do |s|
+ if states = @string_states[s]
+ states[a]
+ end
+ end.compact
end
end
end
diff --git a/actionpack/lib/action_dispatch/journey/router/utils.rb b/actionpack/lib/action_dispatch/journey/router/utils.rb
index 462f1a122d..d1a004af50 100644
--- a/actionpack/lib/action_dispatch/journey/router/utils.rb
+++ b/actionpack/lib/action_dispatch/journey/router/utils.rb
@@ -7,15 +7,18 @@ module ActionDispatch
# Normalizes URI path.
#
# Strips off trailing slash and ensures there is a leading slash.
+ # Also converts downcase url encoded string to uppercase.
#
# normalize_path("/foo") # => "/foo"
# normalize_path("/foo/") # => "/foo"
# normalize_path("foo") # => "/foo"
# normalize_path("") # => "/"
+ # normalize_path("/%ab") # => "/%AB"
def self.normalize_path(path)
path = "/#{path}"
path.squeeze!('/')
path.sub!(%r{/+\Z}, '')
+ path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
path = '/' if path == ''
path
end
@@ -35,7 +38,7 @@ module ActionDispatch
UNSAFE_FRAGMENT = Regexp.new("[^#{safe_fragment}]", false).freeze
end
- Parser = URI.const_defined?(:Parser) ? URI::Parser.new : URI
+ Parser = URI::Parser.new
def self.escape_path(path)
Parser.escape(path.to_s, UriEscape::UNSAFE_SEGMENT)
diff --git a/actionpack/lib/action_dispatch/journey/visitors.rb b/actionpack/lib/action_dispatch/journey/visitors.rb
index 0a8cb1b4d4..9e66cab052 100644
--- a/actionpack/lib/action_dispatch/journey/visitors.rb
+++ b/actionpack/lib/action_dispatch/journey/visitors.rb
@@ -1,9 +1,12 @@
# encoding: utf-8
+
+require 'thread_safe'
+
module ActionDispatch
module Journey # :nodoc:
module Visitors # :nodoc:
class Visitor # :nodoc:
- DISPATCH_CACHE = Hash.new { |h,k|
+ DISPATCH_CACHE = ThreadSafe::Cache.new { |h,k|
h[k] = :"visit_#{k}"
}
@@ -84,44 +87,43 @@ module ActionDispatch
# Used for formatting urls (url_for)
class Formatter < Visitor # :nodoc:
- attr_reader :options, :consumed
+ attr_reader :options
def initialize(options)
@options = options
- @consumed = {}
end
private
- def visit_GROUP(node)
- if consumed == options
- nil
- else
- route = visit(node.left)
- route.include?("\0") ? nil : route
+ def visit(node, optional = false)
+ case node.type
+ when :LITERAL, :SLASH, :DOT
+ node.left
+ when :STAR
+ visit(node.left)
+ when :GROUP
+ visit(node.left, true)
+ when :CAT
+ visit_CAT(node, optional)
+ when :SYMBOL
+ visit_SYMBOL(node)
end
end
- def terminal(node)
- node.left
- end
-
- def binary(node)
- [visit(node.left), visit(node.right)].join
- end
+ def visit_CAT(node, optional)
+ left = visit(node.left, optional)
+ right = visit(node.right, optional)
- def nary(node)
- node.children.map { |c| visit(c) }.join
+ if optional && !(right && left)
+ ""
+ else
+ [left, right].join
+ end
end
def visit_SYMBOL(node)
- key = node.to_sym
-
- if value = options[key]
- consumed[key] = value
+ if value = options[node.to_sym]
Router::Utils.escape_path(value)
- else
- "\0"
end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/callbacks.rb b/actionpack/lib/action_dispatch/middleware/callbacks.rb
index 852f1cf6f5..baf9d5779e 100644
--- a/actionpack/lib/action_dispatch/middleware/callbacks.rb
+++ b/actionpack/lib/action_dispatch/middleware/callbacks.rb
@@ -8,14 +8,14 @@ module ActionDispatch
class << self
delegate :to_prepare, :to_cleanup, :to => "ActionDispatch::Reloader"
- end
- def self.before(*args, &block)
- set_callback(:call, :before, *args, &block)
- end
+ def before(*args, &block)
+ set_callback(:call, :before, *args, &block)
+ end
- def self.after(*args, &block)
- set_callback(:call, :after, *args, &block)
+ def after(*args, &block)
+ set_callback(:call, :after, *args, &block)
+ end
end
def initialize(app)
diff --git a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
index 1de3d14530..37bf9c8c9f 100644
--- a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
+++ b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
@@ -96,7 +96,7 @@ module ActionDispatch
def source_fragment(path, line)
return unless Rails.respond_to?(:root) && Rails.root
full_path = Rails.root.join(path)
- if File.exists?(full_path)
+ if File.exist?(full_path)
File.open(full_path, "r") do |file|
start = [line - 3, 0].max
lines = file.each_line.drop(start).take(6)
diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
index 8879291dbd..57bc6d5cd0 100644
--- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb
+++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
@@ -143,7 +143,7 @@ module ActionDispatch
# proxies with incompatible IP header conventions, and there is no way
# for us to determine which header is the right one after the fact.
# Since we have no idea, we give up and explode.
- should_check_ip = @check_ip && client_ips.last
+ should_check_ip = @check_ip && client_ips.last && forwarded_ips.last
if should_check_ip && !forwarded_ips.include?(client_ips.last)
# We don't know which came from the proxy, and which from the user
raise IpSpoofAttackError, "IP spoofing attack?! " +
diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
index fcc5bc12c4..1d4f0f89a6 100644
--- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
@@ -29,8 +29,11 @@ module ActionDispatch
def call(env)
@app.call(env)
rescue Exception => exception
- raise exception if env['action_dispatch.show_exceptions'] == false
- render_exception(env, exception)
+ if env['action_dispatch.show_exceptions'] == false
+ raise exception
+ else
+ render_exception(env, exception)
+ end
end
private
diff --git a/actionpack/lib/action_dispatch/request/session.rb b/actionpack/lib/action_dispatch/request/session.rb
index 7bc812fd22..6d911a75f1 100644
--- a/actionpack/lib/action_dispatch/request/session.rb
+++ b/actionpack/lib/action_dispatch/request/session.rb
@@ -127,6 +127,18 @@ module ActionDispatch
@delegate.delete key.to_s
end
+ def fetch(key, default=nil)
+ if self.key?(key)
+ self[key]
+ elsif default
+ self[key] = default
+ elsif block_given?
+ self[key] = yield(key)
+ else
+ raise KeyError
+ end
+ end
+
def inspect
if loaded?
super
diff --git a/actionpack/lib/action_dispatch/routing/inspector.rb b/actionpack/lib/action_dispatch/routing/inspector.rb
index cffb814e1e..120bc54333 100644
--- a/actionpack/lib/action_dispatch/routing/inspector.rb
+++ b/actionpack/lib/action_dispatch/routing/inspector.rb
@@ -179,7 +179,8 @@ module ActionDispatch
private
def draw_section(routes)
- name_width, verb_width, path_width = widths(routes)
+ header_lengths = ['Prefix', 'Verb', 'URI Pattern'].map(&:length)
+ name_width, verb_width, path_width = widths(routes).zip(header_lengths).map(&:max)
routes.map do |r|
"#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs]}"
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 288ce3e867..846a6345cb 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -147,14 +147,16 @@ module ActionDispatch
@defaults.merge!(options[:defaults]) if options[:defaults]
options.each do |key, default|
- next if Regexp === default || IGNORE_OPTIONS.include?(key)
- @defaults[key] = default
+ unless Regexp === default || IGNORE_OPTIONS.include?(key)
+ @defaults[key] = default
+ end
end
if options[:constraints].is_a?(Hash)
options[:constraints].each do |key, default|
- next unless URL_OPTIONS.include?(key) && (String === default || Fixnum === default)
- @defaults[key] ||= default
+ if URL_OPTIONS.include?(key) && (String === default || Fixnum === default)
+ @defaults[key] ||= default
+ end
end
end
@@ -166,19 +168,21 @@ module ActionDispatch
end
def normalize_conditions!
- @conditions.merge!(:path_info => path)
+ @conditions[:path_info] = path
constraints.each do |key, condition|
- next if segment_keys.include?(key) || key == :controller
- @conditions[key] = condition
+ unless segment_keys.include?(key) || key == :controller
+ @conditions[key] = condition
+ end
end
- @conditions[:required_defaults] = []
+ required_defaults = []
options.each do |key, required_default|
- next if segment_keys.include?(key) || IGNORE_OPTIONS.include?(key)
- next if Regexp === required_default
- @conditions[:required_defaults] << key
+ unless segment_keys.include?(key) || IGNORE_OPTIONS.include?(key) || Regexp === required_default
+ required_defaults << key
+ end
end
+ @conditions[:required_defaults] = required_defaults
via_all = options.delete(:via) if options[:via] == :all
@@ -192,8 +196,7 @@ module ActionDispatch
end
if via = options[:via]
- list = Array(via).map { |m| m.to_s.dasherize.upcase }
- @conditions.merge!(:request_method => list)
+ @conditions[:request_method] = Array(via).map { |m| m.to_s.dasherize.upcase }
end
end
@@ -226,11 +229,13 @@ module ActionDispatch
action = action.to_s unless action.is_a?(Regexp)
if controller.blank? && segment_keys.exclude?(:controller)
- raise ArgumentError, "missing :controller"
+ message = "Missing :controller key on routes definition, please check your routes."
+ raise ArgumentError, message
end
if action.blank? && segment_keys.exclude?(:action)
- raise ArgumentError, "missing :action"
+ message = "Missing :action key on routes definition, please check your routes."
+ raise ArgumentError, message
end
if controller.is_a?(String) && controller !~ /\A[a-z_0-9\/]*\z/
@@ -362,8 +367,9 @@ module ActionDispatch
# # Yes, controller actions are just rack endpoints
# match 'photos/:id', to: PhotosController.action(:show)
#
- # Because request various HTTP verbs with a single action has security
- # implications, is recommendable use HttpHelpers[rdoc-ref:HttpHelpers]
+ # Because requesting various HTTP verbs with a single action has security
+ # implications, you must either specify the actions in
+ # the via options or use one of the HtttpHelpers[rdoc-ref:HttpHelpers]
# instead +match+
#
# === Options
@@ -383,7 +389,7 @@ module ActionDispatch
# The namespace for :controller.
#
# match 'path', to: 'c#a', module: 'sekret', controller: 'posts'
- # #=> Sekret::PostsController
+ # # => Sekret::PostsController
#
# See <tt>Scoping#namespace</tt> for its scope equivalent.
#
@@ -432,10 +438,10 @@ module ActionDispatch
#
# match 'json_only', constraints: { format: 'json' }
#
- # class Blacklist
+ # class Whitelist
# def matches?(request) request.remote_ip == '1.2.3.4' end
# end
- # match 'path', to: 'c#a', constraints: Blacklist.new
+ # match 'path', to: 'c#a', constraints: Whitelist.new
#
# See <tt>Scoping#constraints</tt> for more examples with its scope
# equivalent.
@@ -1066,18 +1072,18 @@ module ActionDispatch
# a singular resource to map /profile (rather than /profile/:id) to
# the show action:
#
- # resource :geocoder
+ # resource :profile
#
# creates six different routes in your application, all mapping to
- # the +GeoCoders+ controller (note that the controller is named after
+ # the +Profiles+ controller (note that the controller is named after
# the plural):
#
- # GET /geocoder/new
- # POST /geocoder
- # GET /geocoder
- # GET /geocoder/edit
- # PATCH/PUT /geocoder
- # DELETE /geocoder
+ # GET /profile/new
+ # POST /profile
+ # GET /profile
+ # GET /profile/edit
+ # PATCH/PUT /profile
+ # DELETE /profile
#
# === Options
# Takes same options as +resources+.
diff --git a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
index 6d3f8da932..2fb03f2712 100644
--- a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
+++ b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
@@ -74,6 +74,19 @@ module ActionDispatch
# * <tt>:routing_type</tt> - Allowed values are <tt>:path</tt> or <tt>:url</tt>.
# Default is <tt>:url</tt>.
#
+ # Also includes all the options from <tt>url_for</tt>. These include such
+ # things as <tt>:anchor</tt> or <tt>:trailing_slash</tt>. Example usage
+ # is given below:
+ #
+ # polymorphic_url([blog, post], anchor: 'my_anchor')
+ # # => "http://example.com/blogs/1/posts/1#my_anchor"
+ # polymorphic_url([blog, post], anchor: 'my_anchor', script_name: "/my_app")
+ # # => "http://example.com/my_app/blogs/1/posts/1#my_anchor"
+ #
+ # For all of these options, see the documentation for <tt>url_for</tt>.
+ #
+ # ==== Functionality
+ #
# # an Article record
# polymorphic_url(record) # same as article_url(record)
#
diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb
index d751e04e6a..3e54c7e71c 100644
--- a/actionpack/lib/action_dispatch/routing/redirection.rb
+++ b/actionpack/lib/action_dispatch/routing/redirection.rb
@@ -17,7 +17,7 @@ module ActionDispatch
def call(env)
req = Request.new(env)
- # If any of the path parameters has a invalid encoding then
+ # If any of the path parameters has an invalid encoding then
# raise since it's likely to trigger errors further on.
req.symbolized_path_parameters.each do |key, value|
unless value.valid_encoding?
@@ -30,6 +30,10 @@ module ActionDispatch
uri.host ||= req.host
uri.port ||= req.port unless req.standard_port?
+ if relative_path?(uri.path)
+ uri.path = "#{req.script_name}/#{uri.path}"
+ end
+
body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
headers = {
@@ -48,6 +52,11 @@ module ActionDispatch
def inspect
"redirect(#{status})"
end
+
+ private
+ def relative_path?(path)
+ path && !path.empty? && path[0] != '/'
+ end
end
class PathRedirect < Redirect
@@ -81,6 +90,11 @@ module ActionDispatch
url_options[:path] = (url_options[:path] % escape_path(params))
end
+ if relative_path?(url_options[:path])
+ url_options[:path] = "/#{url_options[:path]}"
+ url_options[:script_name] = request.script_name
+ end
+
ActionDispatch::Http::URL.url_for url_options
end
@@ -104,6 +118,10 @@ module ActionDispatch
#
# get 'docs/:article', to: redirect('/wiki/%{article}')
#
+ # Note that if you return a path without a leading slash then the url is prefixed with the
+ # current SCRIPT_NAME environment variable. This is typically '/' but may be different in
+ # a mounted engine or where the application is deployed to a subdirectory of a website.
+ #
# Alternatively you can use one of the other syntaxes:
#
# The block version of redirect allows for the easy encapsulation of any logic associated with
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 0e5dc1fc6c..b8abdabca5 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -28,7 +28,7 @@ module ActionDispatch
def call(env)
params = env[PARAMETERS_KEY]
- # If any of the path parameters has a invalid encoding then
+ # If any of the path parameters has an invalid encoding then
# raise since it's likely to trigger errors further on.
params.each do |key, value|
next unless value.respond_to?(:valid_encoding?)
@@ -514,11 +514,12 @@ module ActionDispatch
@recall = recall.dup
@set = set
+ normalize_recall!
normalize_options!
normalize_controller_action_id!
use_relative_controller!
normalize_controller!
- handle_nil_action!
+ normalize_action!
end
def controller
@@ -537,6 +538,11 @@ module ActionDispatch
end
end
+ # Set 'index' as default action for recall
+ def normalize_recall!
+ @recall[:action] ||= 'index'
+ end
+
def normalize_options!
# If an explicit :controller was given, always make :action explicit
# too, so that action expiry works as expected for things like
@@ -552,8 +558,8 @@ module ActionDispatch
options[:controller] = options[:controller].to_s
end
- if options[:action]
- options[:action] = options[:action].to_s
+ if options.key?(:action)
+ options[:action] = (options[:action] || 'index').to_s
end
end
@@ -563,8 +569,6 @@ module ActionDispatch
# :controller, :action or :id is not found, don't pull any
# more keys from the recall.
def normalize_controller_action_id!
- @recall[:action] ||= 'index' if current_controller
-
use_recall_for(:controller) or return
use_recall_for(:action) or return
use_recall_for(:id)
@@ -586,13 +590,11 @@ module ActionDispatch
@options[:controller] = controller.sub(%r{^/}, '') if controller
end
- # This handles the case of action: nil being explicitly passed.
- # It is identical to action: "index"
- def handle_nil_action!
- if options.has_key?(:action) && options[:action].nil?
- options[:action] = 'index'
+ # Move 'index' action from options to recall
+ def normalize_action!
+ if @options[:action] == 'index'
+ @recall[:action] = @options.delete(:action)
end
- recall[:action] = options.delete(:action) if options[:action] == 'index'
end
# Generates a path from routes, returns [path, params].
diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb
index 8e19025722..4a0ef40873 100644
--- a/actionpack/lib/action_dispatch/routing/url_for.rb
+++ b/actionpack/lib/action_dispatch/routing/url_for.rb
@@ -20,7 +20,7 @@ module ActionDispatch
#
# <%= link_to('Click here', controller: 'users',
# action: 'new', message: 'Welcome!') %>
- # # => "/users/new?message=Welcome%21"
+ # # => <a href="/users/new?message=Welcome%21">Click here</a>
#
# link_to, and all other functions that require URL generation functionality,
# actually use ActionController::UrlFor under the hood. And in particular,
@@ -155,6 +155,8 @@ module ActionDispatch
_routes.url_for(options.symbolize_keys.reverse_merge!(url_options))
when String
options
+ when Array
+ polymorphic_url(options, options.extract_options!)
else
polymorphic_url(options)
end
diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb
index 44ed0ac1f3..93f9fab9c2 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/response.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb
@@ -67,21 +67,11 @@ module ActionDispatch
end
def normalize_argument_to_redirection(fragment)
- normalized = case fragment
- when Regexp
- fragment
- when %r{^\w[A-Za-z\d+.-]*:.*}
- fragment
- when String
- @request.protocol + @request.host_with_port + fragment
- when :back
- raise RedirectBackError unless refer = @request.headers["Referer"]
- refer
- else
- @controller.url_for(fragment)
- end
-
- normalized.respond_to?(:delete) ? normalized.delete("\0\r\n") : normalized
+ if Regexp === fragment
+ fragment
+ else
+ @controller._compute_redirect_to_location(fragment)
+ end
end
end
end
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 9beb30307b..0c2782e981 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -242,7 +242,7 @@ module ActionDispatch
@https = flag
end
- # Return +true+ if the session is mimicking a secure HTTPS request.
+ # Returns +true+ if the session is mimicking a secure HTTPS request.
#
# if session.https?
# ...