aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib')
-rw-r--r--actionpack/lib/action_controller/log_subscriber.rb51
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb2
-rw-r--r--actionpack/lib/action_controller/metal/redirecting.rb1
-rw-r--r--actionpack/lib/action_controller/metal/renderers.rb7
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb8
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb25
-rw-r--r--actionpack/lib/action_controller/railtie.rb4
-rw-r--r--actionpack/lib/action_controller/test_case.rb31
-rw-r--r--actionpack/lib/action_dispatch/http/mime_negotiation.rb12
-rw-r--r--actionpack/lib/action_dispatch/http/parameters.rb18
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb4
-rw-r--r--actionpack/lib/action_dispatch/http/upload.rb10
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb52
-rw-r--r--actionpack/lib/action_dispatch/journey/formatter.rb6
-rw-r--r--actionpack/lib/action_dispatch/journey/router.rb3
-rw-r--r--actionpack/lib/action_dispatch/journey/router/utils.rb12
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb8
-rw-r--r--actionpack/lib/action_dispatch/middleware/flash.rb9
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/cookie_store.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb1
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb48
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb77
-rw-r--r--actionpack/lib/action_dispatch/routing/url_for.rb4
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb6
-rw-r--r--actionpack/lib/action_dispatch/testing/test_request.rb2
25 files changed, 228 insertions, 175 deletions
diff --git a/actionpack/lib/action_controller/log_subscriber.rb b/actionpack/lib/action_controller/log_subscriber.rb
index b1acca2435..89fa75f025 100644
--- a/actionpack/lib/action_controller/log_subscriber.rb
+++ b/actionpack/lib/action_controller/log_subscriber.rb
@@ -16,50 +16,51 @@ module ActionController
end
def process_action(event)
- return unless logger.info?
-
- payload = event.payload
- additions = ActionController::Base.log_process_action(payload)
-
- status = payload[:status]
- if status.nil? && payload[:exception].present?
- exception_class_name = payload[:exception].first
- status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
+ info do
+ payload = event.payload
+ additions = ActionController::Base.log_process_action(payload)
+
+ status = payload[:status]
+ if status.nil? && payload[:exception].present?
+ exception_class_name = payload[:exception].first
+ status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
+ end
+ message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
+ message << " (#{additions.join(" | ")})" unless additions.blank?
+ message
end
- message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
- message << " (#{additions.join(" | ")})" unless additions.blank?
-
- info(message)
end
def halted_callback(event)
- info("Filter chain halted as #{event.payload[:filter].inspect} rendered or redirected")
+ info { "Filter chain halted as #{event.payload[:filter].inspect} rendered or redirected" }
end
def send_file(event)
- info("Sent file #{event.payload[:path]} (#{event.duration.round(1)}ms)")
+ info { "Sent file #{event.payload[:path]} (#{event.duration.round(1)}ms)" }
end
def redirect_to(event)
- info("Redirected to #{event.payload[:location]}")
+ info { "Redirected to #{event.payload[:location]}" }
end
def send_data(event)
- info("Sent data #{event.payload[:filename]} (#{event.duration.round(1)}ms)")
+ info { "Sent data #{event.payload[:filename]} (#{event.duration.round(1)}ms)" }
end
def unpermitted_parameters(event)
- unpermitted_keys = event.payload[:keys]
- debug("Unpermitted parameter#{'s' if unpermitted_keys.size > 1}: #{unpermitted_keys.join(", ")}")
+ debug do
+ unpermitted_keys = event.payload[:keys]
+ "Unpermitted parameter#{'s' if unpermitted_keys.size > 1}: #{unpermitted_keys.join(", ")}"
+ end
end
def deep_munge(event)
- message = "Value for params[:#{event.payload[:keys].join('][:')}] was set "\
- "to nil, because it was one of [], [null] or [null, null, ...]. "\
- "Go to http://guides.rubyonrails.org/security.html#unsafe-query-generation "\
- "for more information."\
-
- debug(message)
+ debug do
+ "Value for params[:#{event.payload[:keys].join('][:')}] was set "\
+ "to nil, because it was one of [], [null] or [null, null, ...]. "\
+ "Go to http://guides.rubyonrails.org/security.html#unsafe-query-generation "\
+ "for more information."\
+ end
end
%w(write_fragment read_fragment exist_fragment?
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index 5b52c19802..25c123edf7 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -471,7 +471,7 @@ module ActionController
# pairs by the standardized `:`, `;`, or `\t` delimiters defined in
# `AUTHN_PAIR_DELIMITERS`.
def raw_params(auth)
- auth.sub(TOKEN_REGEX, '').split(/"\s*#{AUTHN_PAIR_DELIMITERS}\s*/)
+ auth.sub(TOKEN_REGEX, '').split(/\s*#{AUTHN_PAIR_DELIMITERS}\s*/)
end
# Encodes the given token and options into an Authorization header value.
diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
index 3feb737277..ca8c0278d0 100644
--- a/actionpack/lib/action_controller/metal/redirecting.rb
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -68,6 +68,7 @@ module ActionController
# <tt>ActionController::RedirectBackError</tt>.
def redirect_to(options = {}, response_status = {}) #:doc:
raise ActionControllerError.new("Cannot redirect to nil!") unless options
+ raise ActionControllerError.new("Cannot redirect to a parameter hash!") if options.is_a?(ActionController::Parameters)
raise AbstractController::DoubleRenderError if response_body
self.status = _extract_redirect_to_status(options, response_status)
diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb
index 46405cef55..02c4e563f5 100644
--- a/actionpack/lib/action_controller/metal/renderers.rb
+++ b/actionpack/lib/action_controller/metal/renderers.rb
@@ -112,8 +112,11 @@ module ActionController
json = json.to_json(options) unless json.kind_of?(String)
if options[:callback].present?
- self.content_type ||= Mime::JS
- "#{options[:callback]}(#{json})"
+ if self.content_type.nil? || self.content_type == Mime::JSON
+ self.content_type = Mime::JS
+ end
+
+ "/**/#{options[:callback]}(#{json})"
else
self.content_type ||= Mime::JSON
json
diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb
index 93e7d6954c..7bbff0450a 100644
--- a/actionpack/lib/action_controller/metal/rendering.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -67,8 +67,8 @@ module ActionController
options[:html] = ERB::Util.html_escape(options[:html])
end
- if options.delete(:nothing) || _any_render_format_is_nil?(options)
- options[:body] = " "
+ if options.delete(:nothing)
+ options[:body] = nil
end
if options[:status]
@@ -86,10 +86,6 @@ module ActionController
end
end
- def _any_render_format_is_nil?(options)
- RENDER_FORMATS_IN_PRIORITY.any? { |format| options.key?(format) && options[format].nil? }
- end
-
# Process controller specific options, as status, content-type and location.
def _process_options(options) #:nodoc:
status, content_type, location = options.values_at(:status, :content_type, :location)
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index b70962cf44..bc27ecaa20 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -1,5 +1,6 @@
require 'active_support/core_ext/hash/indifferent_access'
require 'active_support/core_ext/array/wrap'
+require 'active_support/deprecation'
require 'active_support/rescuable'
require 'action_dispatch/http/upload'
require 'stringio'
@@ -39,7 +40,7 @@ module ActionController
# == Action Controller \Parameters
#
# Allows to choose which attributes should be whitelisted for mass updating
- # and thus prevent accidentally exposing that which shouldn’t be exposed.
+ # and thus prevent accidentally exposing that which shouldn't be exposed.
# Provides two methods for this purpose: #require and #permit. The former is
# used to mark parameters as required. The latter is used to set the parameter
# as permitted and limit which attributes should be allowed for mass updating.
@@ -101,9 +102,23 @@ module ActionController
cattr_accessor :permit_all_parameters, instance_accessor: false
cattr_accessor :action_on_unpermitted_parameters, instance_accessor: false
- # Never raise an UnpermittedParameters exception because of these params
- # are present. They are added by Rails and it's of no concern.
- NEVER_UNPERMITTED_PARAMS = %w( controller action )
+ # By default, never raise an UnpermittedParameters exception if these
+ # params are present. The default includes both 'controller' and 'action'
+ # because they are added by Rails and should be of no concern. One way
+ # to change these is to specify `always_permitted_parameters` in your
+ # config. For instance:
+ #
+ # config.always_permitted_parameters = %w( controller action format )
+ cattr_accessor :always_permitted_parameters
+ self.always_permitted_parameters = %w( controller action )
+
+ def self.const_missing(const_name)
+ super unless const_name == :NEVER_UNPERMITTED_PARAMS
+ ActiveSupport::Deprecation.warn "`ActionController::Parameters::NEVER_UNPERMITTED_PARAMS`"\
+ " has been deprecated. Use "\
+ "`ActionController::Parameters.always_permitted_parameters` instead."
+ self.always_permitted_parameters
+ end
# Returns a new instance of <tt>ActionController::Parameters</tt>.
# Also, sets the +permitted+ attribute to the default value of
@@ -389,7 +404,7 @@ module ActionController
end
def unpermitted_keys(params)
- self.keys - params.keys - NEVER_UNPERMITTED_PARAMS
+ self.keys - params.keys - self.always_permitted_parameters
end
#
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index a2fc814221..28b20052b5 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -23,6 +23,10 @@ module ActionController
options = app.config.action_controller
ActionController::Parameters.permit_all_parameters = options.delete(:permit_all_parameters) { false }
+ if app.config.action_controller[:always_permitted_parameters]
+ ActionController::Parameters.always_permitted_parameters =
+ app.config.action_controller.delete(:always_permitted_parameters)
+ end
ActionController::Parameters.action_on_unpermitted_parameters = options.delete(:action_on_unpermitted_parameters) do
(Rails.env.test? || Rails.env.development?) ? :log : false
end
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index b117170514..a18c35e3e9 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -30,25 +30,21 @@ module ActionController
end
@_subscribers << ActiveSupport::Notifications.subscribe("!render_template.action_view") do |_name, _start, _finish, _id, payload|
- path = payload[:virtual_path]
- next unless path
- partial = path =~ /^.*\/_[^\/]*$/
+ if virtual_path = payload[:virtual_path]
+ partial = virtual_path =~ /^.*\/_[^\/]*$/
- if partial
- @_partials[path] += 1
- @_partials[path.split("/").last] += 1
- end
-
- @_templates[path] += 1
- end
-
- @_subscribers << ActiveSupport::Notifications.subscribe("!render_template.action_view") do |_name, _start, _finish, _id, payload|
- next if payload[:virtual_path] # files don't have virtual path
+ if partial
+ @_partials[virtual_path] += 1
+ @_partials[virtual_path.split("/").last] += 1
+ end
- path = payload[:identifier]
- if path
- @_files[path] += 1
- @_files[path.split("/").last] += 1
+ @_templates[virtual_path] += 1
+ else
+ path = payload[:identifier]
+ if path
+ @_files[path] += 1
+ @_files[path.split("/").last] += 1
+ end
end
end
end
@@ -233,7 +229,6 @@ module ActionController
@formats = nil
@env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
@env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
- @symbolized_path_params = nil
@method = @request_method = nil
@fullpath = @ip = @remote_ip = @protocol = nil
@env['action_dispatch.request.query_parameters'] = {}
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
index 0b2b60d2e4..9c8f65deac 100644
--- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb
+++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
@@ -54,8 +54,14 @@ module ActionDispatch
end
def formats
- @env["action_dispatch.request.formats"] ||=
- if parameters[:format]
+ @env["action_dispatch.request.formats"] ||= begin
+ params_readable = begin
+ parameters[:format]
+ rescue ActionController::BadRequest
+ false
+ end
+
+ if params_readable
Array(Mime[parameters[:format]])
elsif use_accept_header && valid_accept_header
accepts
@@ -64,8 +70,8 @@ module ActionDispatch
else
[Mime::HTML]
end
+ end
end
-
# Sets the \variant for template.
def variant=(variant)
if variant.is_a?(Symbol)
diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb
index 5f7627cf96..20ae48d458 100644
--- a/actionpack/lib/action_dispatch/http/parameters.rb
+++ b/actionpack/lib/action_dispatch/http/parameters.rb
@@ -1,5 +1,6 @@
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/hash/indifferent_access'
+require 'active_support/deprecation'
module ActionDispatch
module Http
@@ -24,8 +25,10 @@ module ActionDispatch
@env[PARAMETERS_KEY] = parameters
end
- # The same as <tt>path_parameters</tt> with explicitly symbolized keys.
def symbolized_path_parameters
+ ActiveSupport::Deprecation.warn(
+ "`symbolized_path_parameters` is deprecated. Please use `path_parameters`"
+ )
path_parameters
end
@@ -33,31 +36,22 @@ module ActionDispatch
# Returned hash keys are strings:
#
# {'action' => 'my_action', 'controller' => 'my_controller'}
- #
- # See <tt>symbolized_path_parameters</tt> for symbolized keys.
def path_parameters
@env[PARAMETERS_KEY] ||= {}
end
private
- # Convert nested Hash to HashWithIndifferentAccess
- # and UTF-8 encode both keys and values in nested Hash.
+ # Convert nested Hash to HashWithIndifferentAccess.
#
- # TODO: Validate that the characters are UTF-8. If they aren't,
- # you'll get a weird error down the road, but our form handling
- # should really prevent that from happening
def normalize_encode_params(params)
case params
- when String
- params.force_encoding(Encoding::UTF_8).encode!
when Hash
if params.has_key?(:tempfile)
UploadedFile.new(params)
else
params.each_with_object({}) do |(key, val), new_hash|
- new_key = key.is_a?(String) ? key.dup.force_encoding(Encoding::UTF_8).encode! : key
- new_hash[new_key] = if val.is_a?(Array)
+ new_hash[key] = if val.is_a?(Array)
val.map! { |el| normalize_encode_params(el) }
else
normalize_encode_params(val)
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 4d4b443fb4..01f117be99 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -209,8 +209,8 @@ module ActionDispatch
end
# Returns true if the "X-Requested-With" header contains "XMLHttpRequest"
- # (case-insensitive). All major JavaScript libraries send this header with
- # every Ajax request.
+ # (case-insensitive), which may need to be manually added depending on the
+ # choice of JavaScript libraries and frameworks.
def xml_http_request?
@env['HTTP_X_REQUESTED_WITH'] =~ /XMLHttpRequest/i
end
diff --git a/actionpack/lib/action_dispatch/http/upload.rb b/actionpack/lib/action_dispatch/http/upload.rb
index 45bf751d09..540e11a4a0 100644
--- a/actionpack/lib/action_dispatch/http/upload.rb
+++ b/actionpack/lib/action_dispatch/http/upload.rb
@@ -27,7 +27,8 @@ module ActionDispatch
@tempfile = hash[:tempfile]
raise(ArgumentError, ':tempfile is required') unless @tempfile
- @original_filename = encode_filename(hash[:filename])
+ @original_filename = hash[:filename]
+ @original_filename &&= @original_filename.encode "UTF-8"
@content_type = hash[:type]
@headers = hash[:head]
end
@@ -66,13 +67,6 @@ module ActionDispatch
def eof?
@tempfile.eof?
end
-
- private
-
- def encode_filename(filename)
- # Encode the filename in the utf8 encoding, unless it is nil
- filename.force_encoding(Encoding::UTF_8).encode! if filename
- end
end
end
end
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index 3997c6ee98..473f692b05 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -29,36 +29,53 @@ module ActionDispatch
end
def url_for(options)
- unless options[:host] || options[:only_path]
+ if options[:only_path]
+ path_for options
+ else
+ full_url_for options
+ end
+ end
+
+ def full_url_for(options)
+ host = options[:host]
+ protocol = options[:protocol]
+ port = options[:port]
+
+ unless host
raise ArgumentError, 'Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true'
end
- path = options[:script_name].to_s.chomp("/")
- path << options[:path].to_s
+ build_host_url(host, port, protocol, options, path_for(options))
+ end
- path = add_trailing_slash(path) if options[:trailing_slash]
+ def path_for(options)
+ result = options[:script_name].to_s.chomp("/")
+ result << options[:path].to_s
- result = if options[:only_path]
- path
- else
- build_host_url(options).concat path
- end
+ result = add_trailing_slash(result) if options[:trailing_slash]
+
+ result = add_params options, result
+ add_anchor options, result
+ end
+ private
+
+ def add_params(options, result)
if options.key? :params
- params = options[:params].is_a?(Hash) ?
- options[:params] :
- { params: options[:params] }
+ param = options[:params]
+ params = param.is_a?(Hash) ? param : { params: param }
params.reject! { |_,v| v.to_param.nil? }
result << "?#{params.to_query}" unless params.empty?
end
+ result
+ end
+ def add_anchor(options, result)
result << "##{Journey::Router::Utils.escape_fragment(options[:anchor].to_param.to_s)}" if options[:anchor]
result
end
- private
-
def extract_domain_from(host, tld_length)
host.split('.').last(1 + tld_length).join('.')
end
@@ -80,10 +97,7 @@ module ActionDispatch
path
end
- def build_host_url(options)
- protocol = options[:protocol]
- host = options[:host]
- port = options[:port]
+ def build_host_url(host, port, protocol, options, path)
if match = host.match(HOST_REGEXP)
protocol ||= match[1] unless protocol == false
host = match[2]
@@ -104,7 +118,7 @@ module ActionDispatch
result << ":#{normalized_port}"
}
- result
+ result.concat path
end
def named_host?(host)
diff --git a/actionpack/lib/action_dispatch/journey/formatter.rb b/actionpack/lib/action_dispatch/journey/formatter.rb
index 6d58323789..59b353b1b7 100644
--- a/actionpack/lib/action_dispatch/journey/formatter.rb
+++ b/actionpack/lib/action_dispatch/journey/formatter.rb
@@ -12,12 +12,12 @@ module ActionDispatch
@cache = nil
end
- def generate(name, options, recall = {}, parameterize = nil)
- constraints = recall.merge(options)
+ def generate(name, options, path_parameters, parameterize = nil)
+ constraints = path_parameters.merge(options)
missing_keys = []
match_route(name, constraints) do |route|
- parameterized_parts = extract_parameterized_parts(route, options, recall, parameterize)
+ parameterized_parts = extract_parameterized_parts(route, options, path_parameters, parameterize)
# Skip this route unless a name has been provided or it is a
# standard Rails route since we can't determine whether an options
diff --git a/actionpack/lib/action_dispatch/journey/router.rb b/actionpack/lib/action_dispatch/journey/router.rb
index fe3fc0a9fa..21817b374c 100644
--- a/actionpack/lib/action_dispatch/journey/router.rb
+++ b/actionpack/lib/action_dispatch/journey/router.rb
@@ -105,7 +105,8 @@ module ActionDispatch
routes.concat get_routes_as_head(routes)
end
- routes.sort_by!(&:precedence).select! { |r| r.matches?(req) }
+ routes.select! { |r| r.matches?(req) }
+ routes.sort_by!(&:precedence)
routes.map! { |r|
match_data = r.path.match(req.path_info)
diff --git a/actionpack/lib/action_dispatch/journey/router/utils.rb b/actionpack/lib/action_dispatch/journey/router/utils.rb
index ac4ecb1e65..2b0a6575d4 100644
--- a/actionpack/lib/action_dispatch/journey/router/utils.rb
+++ b/actionpack/lib/action_dispatch/journey/router/utils.rb
@@ -25,9 +25,10 @@ module ActionDispatch
# http://tools.ietf.org/html/rfc3986
class UriEncoder # :nodoc:
ENCODE = "%%%02X".freeze
- ENCODING = Encoding::US_ASCII
- EMPTY = "".force_encoding(ENCODING).freeze
- DEC2HEX = (0..255).to_a.map{ |i| ENCODE % i }.map{ |s| s.force_encoding(ENCODING) }
+ US_ASCII = Encoding::US_ASCII
+ UTF_8 = Encoding::UTF_8
+ EMPTY = "".force_encoding(US_ASCII).freeze
+ DEC2HEX = (0..255).to_a.map{ |i| ENCODE % i }.map{ |s| s.force_encoding(US_ASCII) }
ALPHA = "a-zA-Z".freeze
DIGIT = "0-9".freeze
@@ -53,12 +54,13 @@ module ActionDispatch
end
def unescape_uri(uri)
- uri.gsub(ESCAPED) { [$&[1, 2].hex].pack('C') }.force_encoding(uri.encoding)
+ encoding = uri.encoding == US_ASCII ? UTF_8 : uri.encoding
+ uri.gsub(ESCAPED) { [$&[1, 2].hex].pack('C') }.force_encoding(encoding)
end
protected
def escape(component, pattern)
- component.gsub(pattern){ |unsafe| percent_encode(unsafe) }.force_encoding(ENCODING)
+ component.gsub(pattern){ |unsafe| percent_encode(unsafe) }.force_encoding(US_ASCII)
end
def percent_encode(unsafe)
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index 22b16b628d..ac9e5effe2 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -289,8 +289,8 @@ module ActionDispatch
end
end
- # Sets the cookie named +name+. The second argument may be the very cookie
- # value, or a hash of options as documented above.
+ # Sets the cookie named +name+. The second argument may be the cookie's
+ # value or a hash of options as documented above.
def []=(name, options)
if options.is_a?(Hash)
options.symbolize_keys!
@@ -468,7 +468,7 @@ module ActionDispatch
options = { :value => @verifier.generate(serialize(name, options)) }
end
- raise CookieOverflow if options[:value].size > MAX_COOKIE_SIZE
+ raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE
@parent_jar[name] = options
end
@@ -526,7 +526,7 @@ module ActionDispatch
options[:value] = @encryptor.encrypt_and_sign(serialize(name, options[:value]))
- raise CookieOverflow if options[:value].size > MAX_COOKIE_SIZE
+ raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE
@parent_jar[name] = options
end
diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb
index 4821d2a899..e90f8b9ce6 100644
--- a/actionpack/lib/action_dispatch/middleware/flash.rb
+++ b/actionpack/lib/action_dispatch/middleware/flash.rb
@@ -10,7 +10,7 @@ module ActionDispatch
end
end
- # The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed
+ # The flash provides a way to pass temporary primitive-types (String, Array, Hash) between actions. Anything you place in the flash will be exposed
# to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
# action that sets <tt>flash[:notice] = "Post successfully created"</tt> before redirecting to a display action that can
# then expose the flash to its template. Actually, that exposure is automatically done.
@@ -37,8 +37,11 @@ module ActionDispatch
# flash.alert = "You must be logged in"
# flash.notice = "Post successfully created"
#
- # This example just places a string in the flash, but you can put any object in there. And of course, you can put as
- # many as you like at a time too. Just remember: They'll be gone by the time the next action has been performed.
+ # This example places a string in the flash. And of course, you can put as many as you like at a time too. If you want to pass
+ # non-primitive types, you will have to handle that in your application. Example: To show messages with links, you will have to
+ # use sanitize helper.
+ #
+ # Just remember: They'll be gone by the time the next action has been performed.
#
# See docs on the FlashHash class for more details about the flash.
class Flash
diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
index 0864e7ef2a..ed25c67ae5 100644
--- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
@@ -49,7 +49,7 @@ module ActionDispatch
# reasonably sure that your upgrade is otherwise complete. Additionally,
# you should take care to make sure you are not relying on the ability to
# decode signed cookies generated by your app in external applications or
- # Javascript before upgrading.
+ # JavaScript before upgrading.
#
# Note that changing the secret key will invalidate all existing sessions!
class CookieStore < Rack::Session::Abstract::ID
diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
index 1d4f0f89a6..f0779279c1 100644
--- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
@@ -42,6 +42,7 @@ module ActionDispatch
wrapper = ExceptionWrapper.new(env, exception)
status = wrapper.status_code
env["action_dispatch.exception"] = wrapper.exception
+ env["action_dispatch.original_path"] = env["PATH_INFO"]
env["PATH_INFO"] = "/#{status}"
response = @exceptions_app.call(env)
response[1]['X-Cascade'] == 'pass' ? pass_response(status) : response
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index aac5546aa1..32d963ba76 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -66,7 +66,7 @@ module ActionDispatch
attr_reader :requirements, :conditions, :defaults
attr_reader :to, :default_controller, :default_action, :as, :anchor
- def self.build(scope, path, options)
+ def self.build(scope, set, path, options)
options = scope[:options].merge(options) if scope[:options]
options.delete :only
@@ -77,12 +77,13 @@ module ActionDispatch
defaults = (scope[:defaults] || {}).merge options.delete(:defaults) || {}
- new scope, path, defaults, options
+ new scope, set, path, defaults, options
end
- def initialize(scope, path, defaults, options)
+ def initialize(scope, set, path, defaults, options)
@requirements, @conditions = {}, {}
@defaults = defaults
+ @set = set
@to = options.delete :to
@default_controller = options.delete(:controller) || scope[:controller]
@@ -249,9 +250,9 @@ module ActionDispatch
Constraints.new(to, blocks, false)
else
if blocks.any?
- Constraints.new(dispatcher, blocks, true)
+ Constraints.new(dispatcher(defaults), blocks, true)
else
- dispatcher
+ dispatcher(defaults)
end
end
end
@@ -348,8 +349,8 @@ module ActionDispatch
parser.parse path
end
- def dispatcher
- Routing::RouteSet::Dispatcher.new(defaults)
+ def dispatcher(defaults)
+ @set.dispatcher defaults
end
end
@@ -576,13 +577,21 @@ module ActionDispatch
raise "A rack application must be specified" unless path
- options[:as] ||= app_name(app)
+ rails_app = rails_app? app
+
+ if rails_app
+ options[:as] ||= app.railtie_name
+ else
+ # non rails apps can't have an :as
+ options[:as] = nil
+ end
+
target_as = name_for_action(options[:as], path)
options[:via] ||= :all
match(path, options.merge(:to => app, :anchor => false, :format => false))
- define_generate_prefix(app, target_as)
+ define_generate_prefix(app, target_as) if rails_app
self
end
@@ -603,20 +612,11 @@ module ActionDispatch
end
private
- def app_name(app)
- return unless app.respond_to?(:routes)
-
- if app.respond_to?(:railtie_name)
- app.railtie_name
- else
- class_name = app.class.is_a?(Class) ? app.name : app.class.name
- ActiveSupport::Inflector.underscore(class_name).tr("/", "_")
- end
+ def rails_app?(app)
+ app.is_a?(Class) && app < Rails::Railtie
end
def define_generate_prefix(app, name)
- return unless app.respond_to?(:routes) && app.routes.respond_to?(:define_mounted_helper)
-
_route = @set.named_routes.routes[name.to_sym]
_routes = @set
app.routes.define_mounted_helper(name)
@@ -717,7 +717,7 @@ module ActionDispatch
# resources :posts, module: "admin"
#
# If you want to route /admin/posts to +PostsController+
- # (without the Admin:: module prefix), you could use
+ # (without the <tt>Admin::</tt> module prefix), you could use
#
# scope "/admin" do
# resources :posts
@@ -1434,7 +1434,7 @@ module ActionDispatch
end
with_scope_level(:nested) do
- if shallow? && shallow_nesting_depth > 1
+ if shallow? && shallow_nesting_depth >= 1
shallow_scope(parent_resource.nested_scope, nested_options) { yield }
else
scope(parent_resource.nested_scope, nested_options) { yield }
@@ -1545,13 +1545,13 @@ module ActionDispatch
action = nil
end
- if !options.fetch(:as, true)
+ if !options.fetch(:as, true) # if it's set to nil or false
options.delete(:as)
else
options[:as] = name_for_action(options[:as], action)
end
- mapping = Mapping.build(@scope, URI.parser.escape(path), options)
+ mapping = Mapping.build(@scope, @set, URI.parser.escape(path), options)
app, conditions, requirements, defaults, as, anchor = mapping.to_route
@set.add_route(app, conditions, requirements, defaults, as, anchor)
end
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 69535faabd..80c705608d 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -134,11 +134,11 @@ module ActionDispatch
end
class UrlHelper # :nodoc:
- def self.create(route, options)
+ def self.create(route, options, route_name, url_strategy)
if optimize_helper?(route)
- OptimizedUrlHelper.new(route, options)
+ OptimizedUrlHelper.new(route, options, route_name, url_strategy)
else
- new route, options
+ new route, options, route_name, url_strategy
end
end
@@ -146,20 +146,22 @@ module ActionDispatch
!route.glob? && route.path.requirements.empty?
end
+ attr_reader :url_strategy, :route_name
+
class OptimizedUrlHelper < UrlHelper # :nodoc:
attr_reader :arg_size
- def initialize(route, options)
+ def initialize(route, options, route_name, url_strategy)
super
@required_parts = @route.required_parts
@arg_size = @required_parts.size
end
- def call(t, args)
- if args.size == arg_size && !args.last.is_a?(Hash) && optimize_routes_generation?(t)
+ def call(t, args, inner_options)
+ if args.size == arg_size && !inner_options && optimize_routes_generation?(t)
options = t.url_options.merge @options
options[:path] = optimized_helper(args)
- ActionDispatch::Http::URL.url_for(options)
+ url_strategy.call options
else
super
end
@@ -201,21 +203,27 @@ module ActionDispatch
end
end
- def initialize(route, options)
+ def initialize(route, options, route_name, url_strategy)
@options = options
@segment_keys = route.segment_keys.uniq
@route = route
+ @url_strategy = url_strategy
+ @route_name = route_name
end
- def call(t, args)
+ def call(t, args, inner_options)
controller_options = t.url_options
options = controller_options.merge @options
- hash = handle_positional_args(controller_options, args, options, @segment_keys)
- t._routes.url_for(hash)
+ hash = handle_positional_args(controller_options,
+ inner_options || {},
+ args,
+ options,
+ @segment_keys)
+
+ t._routes.url_for(hash, route_name, url_strategy)
end
- def handle_positional_args(controller_options, args, result, path_params)
- inner_options = args.extract_options!
+ def handle_positional_args(controller_options, inner_options, args, result, path_params)
if args.size > 0
if args.size < path_params.size - 1 # take format into account
@@ -245,13 +253,15 @@ module ActionDispatch
#
# foo_url(bar, baz, bang, sort_by: 'baz')
#
- def define_url_helper(route, name, options)
- helper = UrlHelper.create(route, options.dup)
+ def define_url_helper(route, name, opts, route_key, url_strategy)
+ helper = UrlHelper.create(route, opts, route_key, url_strategy)
@module.remove_possible_method name
@module.module_eval do
define_method(name) do |*args|
- helper.call self, args
+ options = nil
+ options = args.pop if args.last.is_a? Hash
+ helper.call self, args, options
end
end
@@ -259,13 +269,18 @@ module ActionDispatch
end
def define_named_route_methods(name, route)
- define_url_helper route, :"#{name}_path",
- route.defaults.merge(:use_route => name, :only_path => true)
- define_url_helper route, :"#{name}_url",
- route.defaults.merge(:use_route => name, :only_path => false)
+ define_url_helper route, :"#{name}_path", route.defaults, name, PATH
+ define_url_helper route, :"#{name}_url", route.defaults, name, FULL
end
end
+ # :stopdoc:
+ # strategy for building urls to send to the client
+ PATH = ->(options) { ActionDispatch::Http::URL.path_for(options) }
+ FULL = ->(options) { ActionDispatch::Http::URL.full_url_for(options) }
+ UNKNOWN = ->(options) { ActionDispatch::Http::URL.url_for(options) }
+ # :startdoc:
+
attr_accessor :formatter, :set, :named_routes, :default_scope, :router
attr_accessor :disable_clear_and_finalize, :resources_path_names
attr_accessor :default_url_options, :request_class
@@ -334,6 +349,10 @@ module ActionDispatch
@prepend.each { |blk| eval_block(blk) }
end
+ def dispatcher(defaults)
+ Routing::RouteSet::Dispatcher.new(defaults)
+ end
+
module MountedHelpers #:nodoc:
extend ActiveSupport::Concern
include UrlFor
@@ -491,8 +510,8 @@ module ActionDispatch
attr_reader :options, :recall, :set, :named_route
- def initialize(options, recall, set)
- @named_route = options.delete(:use_route)
+ def initialize(named_route, options, recall, set)
+ @named_route = named_route
@options = options.dup
@recall = recall.dup
@set = set
@@ -608,13 +627,15 @@ module ActionDispatch
end
def generate_extras(options, recall={})
- path, params = generate(options, recall)
+ route_key = options.delete :use_route
+ path, params = generate(route_key, options, recall)
return path, params.keys
end
- def generate(options, recall = {})
- Generator.new(options, recall, self).generate
+ def generate(route_key, options, recall = {})
+ Generator.new(route_key, options, recall, self).generate
end
+ private :generate
RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length,
:trailing_slash, :anchor, :params, :only_path, :script_name,
@@ -633,7 +654,7 @@ module ActionDispatch
end
# The +options+ argument must be a hash whose keys are *symbols*.
- def url_for(options)
+ def url_for(options, route_name = nil, url_strategy = UNKNOWN)
options = default_url_options.merge options
user = password = nil
@@ -655,7 +676,7 @@ module ActionDispatch
path_options = options.dup
RESERVED_OPTIONS.each { |ro| path_options.delete ro }
- path, params = generate(path_options, recall)
+ path, params = generate(route_name, path_options, recall)
if options.key? :params
params.merge! options[:params]
@@ -667,7 +688,7 @@ module ActionDispatch
options[:user] = user
options[:password] = password
- ActionDispatch::Http::URL.url_for(options)
+ url_strategy.call options
end
def call(env)
diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb
index e624fe3c4a..e1c73f8f07 100644
--- a/actionpack/lib/action_dispatch/routing/url_for.rb
+++ b/actionpack/lib/action_dispatch/routing/url_for.rb
@@ -152,7 +152,9 @@ module ActionDispatch
when nil
_routes.url_for(url_options.symbolize_keys)
when Hash
- _routes.url_for(options.symbolize_keys.reverse_merge!(url_options))
+ route_name = options.delete :use_route
+ _routes.url_for(options.symbolize_keys.reverse_merge!(url_options),
+ route_name)
when String
options
when Symbol
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 17765d851b..cb2cb10870 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -188,8 +188,8 @@ module ActionDispatch
# This makes app.url_for and app.foo_path available in the console
if app.respond_to?(:routes)
singleton_class.class_eval do
- include app.routes.url_helpers if app.routes.respond_to?(:url_helpers)
- include app.routes.mounted_helpers if app.routes.respond_to?(:mounted_helpers)
+ include app.routes.url_helpers
+ include app.routes.mounted_helpers
end
end
@@ -200,7 +200,7 @@ module ActionDispatch
@url_options ||= default_url_options.dup.tap do |url_options|
url_options.reverse_merge!(controller.url_options) if controller
- if @app.respond_to?(:routes) && @app.routes.respond_to?(:default_url_options)
+ if @app.respond_to?(:routes)
url_options.reverse_merge!(@app.routes.default_url_options)
end
diff --git a/actionpack/lib/action_dispatch/testing/test_request.rb b/actionpack/lib/action_dispatch/testing/test_request.rb
index 57c678843b..de3dc5f924 100644
--- a/actionpack/lib/action_dispatch/testing/test_request.rb
+++ b/actionpack/lib/action_dispatch/testing/test_request.rb
@@ -39,7 +39,7 @@ module ActionDispatch
end
def action=(action_name)
- path_parameters["action"] = action_name.to_s
+ path_parameters[:action] = action_name.to_s
end
def if_modified_since=(last_modified)