aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack')
-rw-r--r--actionpack/CHANGELOG.md329
-rw-r--r--actionpack/lib/abstract_controller/caching/fragments.rb2
-rw-r--r--actionpack/lib/action_controller/api.rb7
-rw-r--r--actionpack/lib/action_controller/base.rb8
-rw-r--r--actionpack/lib/action_controller/metal.rb4
-rw-r--r--actionpack/lib/action_controller/metal/etag_with_flash.rb4
-rw-r--r--actionpack/lib/action_controller/metal/force_ssl.rb14
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb2
-rw-r--r--actionpack/lib/action_controller/metal/implicit_render.rb6
-rw-r--r--actionpack/lib/action_controller/metal/instrumentation.rb2
-rw-r--r--actionpack/lib/action_controller/metal/live.rb8
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb4
-rw-r--r--actionpack/lib/action_controller/metal/parameter_encoding.rb2
-rw-r--r--actionpack/lib/action_controller/metal/params_wrapper.rb14
-rw-r--r--actionpack/lib/action_controller/metal/redirecting.rb9
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb2
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb10
-rw-r--r--actionpack/lib/action_controller/metal/rescue.rb2
-rw-r--r--actionpack/lib/action_controller/metal/streaming.rb2
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb104
-rw-r--r--actionpack/lib/action_controller/metal/url_for.rb2
-rw-r--r--actionpack/lib/action_controller/railtie.rb2
-rw-r--r--actionpack/lib/action_controller/renderer.rb7
-rw-r--r--actionpack/lib/action_controller/test_case.rb11
-rw-r--r--actionpack/lib/action_dispatch.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/mime_negotiation.rb4
-rw-r--r--actionpack/lib/action_dispatch/http/mime_type.rb18
-rw-r--r--actionpack/lib/action_dispatch/http/parameters.rb11
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb14
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb4
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb2
-rw-r--r--actionpack/lib/action_dispatch/journey/formatter.rb2
-rw-r--r--actionpack/lib/action_dispatch/journey/gtg/transition_table.rb1
-rw-r--r--actionpack/lib/action_dispatch/journey/route.rb9
-rw-r--r--actionpack/lib/action_dispatch/journey/router/utils.rb2
-rw-r--r--actionpack/lib/action_dispatch/journey/visitors.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb10
-rw-r--r--actionpack/lib/action_dispatch/middleware/flash.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/remote_ip.rb6
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/abstract_store.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb4
-rw-r--r--actionpack/lib/action_dispatch/request/session.rb8
-rw-r--r--actionpack/lib/action_dispatch/request/utils.rb3
-rw-r--r--actionpack/lib/action_dispatch/routing.rb2
-rw-r--r--actionpack/lib/action_dispatch/routing/inspector.rb2
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb151
-rw-r--r--actionpack/lib/action_dispatch/routing/polymorphic_routes.rb36
-rw-r--r--actionpack/lib/action_dispatch/routing/redirection.rb6
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb130
-rw-r--r--actionpack/lib/action_dispatch/routing/url_for.rb12
-rw-r--r--actionpack/lib/action_dispatch/system_test_case.rb126
-rw-r--r--actionpack/lib/action_dispatch/system_testing/driver.rb34
-rw-r--r--actionpack/lib/action_dispatch/system_testing/server.rb32
-rw-r--r--actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb94
-rw-r--r--actionpack/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb20
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/response.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/routing.rb8
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb19
-rw-r--r--actionpack/lib/action_dispatch/testing/test_request.rb2
-rw-r--r--actionpack/lib/action_pack/gem_version.rb2
-rw-r--r--actionpack/test/abstract_unit.rb8
-rw-r--r--actionpack/test/controller/live_stream_test.rb1
-rw-r--r--actionpack/test/controller/parameters/accessors_test.rb63
-rw-r--r--actionpack/test/controller/parameters/parameters_permit_test.rb25
-rw-r--r--actionpack/test/controller/params_wrapper_test.rb13
-rw-r--r--actionpack/test/controller/redirect_test.rb4
-rw-r--r--actionpack/test/controller/renderer_test.rb24
-rw-r--r--actionpack/test/controller/test_case_test.rb25
-rw-r--r--actionpack/test/dispatch/routing/custom_url_helpers_test.rb325
-rw-r--r--actionpack/test/dispatch/routing_test.rb110
-rw-r--r--actionpack/test/dispatch/static_test.rb2
-rw-r--r--actionpack/test/dispatch/system_testing/driver_test.rb21
-rw-r--r--actionpack/test/dispatch/system_testing/screenshot_helper_test.rb41
-rw-r--r--actionpack/test/dispatch/system_testing/server_test.rb17
-rw-r--r--actionpack/test/dispatch/system_testing/system_test_case_test.rb13
76 files changed, 1512 insertions, 523 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index a7a4aabc98..c5b679207a 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,328 +1 @@
-* Remove deprecated `.to_prepare`, `.to_cleanup`, `.prepare!` and `.cleanup!` from `ActionDispatch::Reloader`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `ActionDispatch::Callbacks.to_prepare` and `ActionDispatch::Callbacks.to_cleanup`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `ActionController::Metal.call`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `ActionController::Metal#env`.
-
- *Rafael Mendonça França*
-
-* Make `with_routing` test helper work when testing controllers inheriting from `ActionController::API`
-
- *Julia López*
-
-* Use accept header in integration tests with `as: :json`
-
- Instead of appending the `format` to the request path, Rails will figure
- out the format from the header instead.
-
- This allows devs to use `:as` on routes that don't have a format.
-
- Fixes #27144.
-
- *Kasper Timm Hansen*
-
-* Reset a new session directly after its creation in `ActionDispatch::IntegrationTest#open_session`.
-
- Fixes #22742.
-
- *Tawan Sierek*
-
-* Fixes incorrect output from rails routes when using singular resources.
-
- Fixes #26606.
-
- *Erick Reyna*
-
-* Fixes multiple calls to `logger.fatal` instead of a single call,
- for every line in an exception backtrace, when printing trace
- from `DebugExceptions` middleware.
-
- Fixes #26134.
-
- *Vipul A M*
-
-* Add support for arbitrary hashes in strong parameters:
-
- ```ruby
- params.permit(preferences: {})
- ```
-
- *Xavier Noria*
-
-* Add `ActionController::Parameters#merge!`, which behaves the same as `Hash#merge!`.
-
- *Yuji Yaginuma*
-
-* Allow keys not found in `RACK_KEY_TRANSLATION` for setting the environment when rendering
- arbitrary templates.
-
- *Sammy Larbi*
-
-* Remove deprecated support to non-keyword arguments in `ActionDispatch::IntegrationTest#process`,
- `#get`, `#post`, `#patch`, `#put`, `#delete`, and `#head`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `ActionDispatch::IntegrationTest#*_via_redirect`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `ActionDispatch::IntegrationTest#xml_http_request`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated support for passing `:path` and route path as strings in `ActionDispatch::Routing::Mapper#match`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated support for passing path as `nil` in `ActionDispatch::Routing::Mapper#match`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `cache_control` argument from `ActionDispatch::Static#initialize`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated support to passing strings or symbols to the middleware stack.
-
- *Rafael Mendonça França*
-
-* Change HSTS subdomain to true.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `host` and `port` ssl options.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `const_error` argument in
- `ActionDispatch::Session::SessionRestoreError#initialize`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `#original_exception` in `ActionDispatch::Session::SessionRestoreError`.
-
- *Rafael Mendonça França*
-
-* Deprecate `ActionDispatch::ParamsParser::ParseError` in favor of
- `ActionDispatch::Http::Parameters::ParseError`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `ActionDispatch::ParamsParser`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `original_exception` and `message` arguments in
- `ActionDispatch::ParamsParser::ParseError#initialize`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `#original_exception` in `ActionDispatch::ParamsParser::ParseError`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated access to mime types through constants.
-
- *Rafael Mendonça França*
-
-* Remove deprecated support to non-keyword arguments in `ActionController::TestCase#process`,
- `#get`, `#post`, `#patch`, `#put`, `#delete`, and `#head`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `xml_http_request` and `xhr` methods in `ActionController::TestCase`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated methods in `ActionController::Parameters`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated support to comparing a `ActionController::Parameters`
- with a `Hash`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated support to `:text` in `render`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated support to `:nothing` in `render`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated support to `:back` in `redirect_to`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated support to passing status as option `head`.
-
- *Rafael Mendonça França*
-
-* Remove deprecated support to passing original exception to `ActionController::BadRequest`
- and the `ActionController::BadRequest#original_exception` method.
-
- *Rafael Mendonça França*
-
-* Remove deprecated methods `skip_action_callback`, `skip_filter`, `before_filter`,
- `prepend_before_filter`, `skip_before_filter`, `append_before_filter`, `around_filter`
- `prepend_around_filter`, `skip_around_filter`, `append_around_filter`, `after_filter`,
- `prepend_after_filter`, `skip_after_filter` and `append_after_filter`.
-
- *Rafael Mendonça França*
-
-* Show an "unmatched constraints" error when params fail to match constraints
- on a matched route, rather than a "missing keys" error.
-
- Fixes #26470.
-
- *Chris Carter*
-
-* Fix adding implicitly rendered template digests to ETags.
-
- Fixes a case when modifying an implicitly rendered template for a
- controller action using `fresh_when` or `stale?` would not result in a new
- `ETag` value.
-
- *Javan Makhmali*
-
-* Make `fixture_file_upload` work in integration tests.
-
- *Yuji Yaginuma*
-
-* Add `to_param` to `ActionController::Parameters` deprecations.
-
- In the future `ActionController::Parameters` are discouraged from being used
- in URLs without explicit whitelisting. Go through `to_h` to use `to_param`.
-
- *Kir Shatrov*
-
-* Fix nested multiple roots
-
- The PR #20940 enabled the use of multiple roots with different constraints
- at the top level but unfortunately didn't work when those roots were inside
- a namespace and also broke the use of root inside a namespace after a top
- level root was defined because the check for the existence of the named route
- used the global :root name and not the namespaced name.
-
- This is fixed by using the name_for_action method to expand the :root name to
- the full namespaced name. We can pass nil for the second argument as we're not
- dealing with resource definitions so don't need to handle the cases for edit
- and new routes.
-
- Fixes #26148.
-
- *Ryo Hashimoto*, *Andrew White*
-
-* Include the content of the flash in the auto-generated etag. This solves the following problem:
-
- 1. POST /messages
- 2. redirect_to messages_url, notice: 'Message was created'
- 3. GET /messages/1
- 4. GET /messages
-
- Step 4 would before still include the flash message, even though it's no longer relevant,
- because the etag cache was recorded with the flash in place and didn't change when it was gone.
-
- *DHH*
-
-* SSL: Changes redirect behavior for all non-GET and non-HEAD requests
- (like POST/PUT/PATCH etc) to `http://` resources to redirect to `https://`
- with a [307 status code](http://tools.ietf.org/html/rfc7231#section-6.4.7) instead of [301 status code](http://tools.ietf.org/html/rfc7231#section-6.4.2).
-
- 307 status code instructs the HTTP clients to preserve the original
- request method while redirecting. It has been part of HTTP RFC since
- 1999 and is implemented/recognized by most (if not all) user agents.
-
- # Before
- POST http://example.com/articles (i.e. ArticlesContoller#create)
- redirects to
- GET https://example.com/articles (i.e. ArticlesContoller#index)
-
- # After
- POST http://example.com/articles (i.e. ArticlesContoller#create)
- redirects to
- POST https://example.com/articles (i.e. ArticlesContoller#create)
-
- *Chirag Singhal*
-
-* Add `:as` option to `ActionController:TestCase#process` and related methods.
-
- Specifying `as: mime_type` allows the `CONTENT_TYPE` header to be specified
- in controller tests without manually doing this through `@request.headers['CONTENT_TYPE']`.
-
- *Everest Stefan Munro-Zeisberger*
-
-* Show cache hits and misses when rendering partials.
-
- Partials using the `cache` helper will show whether a render hit or missed
- the cache:
-
- ```
- Rendered messages/_message.html.erb in 1.2 ms [cache hit]
- Rendered recordings/threads/_thread.html.erb in 1.5 ms [cache miss]
- ```
-
- This removes the need for the old fragment cache logging:
-
- ```
- Read fragment views/v1/2914079/v1/2914079/recordings/70182313-20160225015037000000/d0bdf2974e1ef6d31685c3b392ad0b74 (0.6ms)
- Rendered messages/_message.html.erb in 1.2 ms [cache hit]
- Write fragment views/v1/2914079/v1/2914079/recordings/70182313-20160225015037000000/3b4e249ac9d168c617e32e84b99218b5 (1.1ms)
- Rendered recordings/threads/_thread.html.erb in 1.5 ms [cache miss]
- ```
-
- Though that full output can be reenabled with
- `config.action_controller.enable_fragment_cache_logging = true`.
-
- *Stan Lo*
-
-* Don't override the `Accept` header in integration tests when called with `xhr: true`.
-
- Fixes #25859.
-
- *David Chen*
-
-* Fix `defaults` option for root route.
-
- A regression from some refactoring for the 5.0 release, this change
- fixes the use of `defaults` (default parameters) in the `root` routing method.
-
- *Chris Arcand*
-
-* Check `request.path_parameters` encoding at the point they're set.
-
- Check for any non-UTF8 characters in path parameters at the point they're
- set in `env`. Previously they were checked for when used to get a controller
- class, but this meant routes that went directly to a Rack app, or skipped
- controller instantiation for some other reason, had to defend against
- non-UTF8 characters themselves.
-
- *Grey Baker*
-
-* Don't raise `ActionController::UnknownHttpMethod` from `ActionDispatch::Static`.
-
- Pass `Rack::Request` objects to `ActionDispatch::FileHandler` to avoid it
- raising `ActionController::UnknownHttpMethod`. If an unknown method is
- passed, it should pass exception higher in the stack instead, once we've had a
- chance to define exception handling behaviour.
-
- *Grey Baker*
-
-* Handle `Rack::QueryParser` errors in `ActionDispatch::ExceptionWrapper`.
-
- Updated `ActionDispatch::ExceptionWrapper` to handle the Rack 2.0 namespace
- for `ParameterTypeError` and `InvalidParameterError` errors.
-
- *Grey Baker*
-
-Please check [5-0-stable](https://github.com/rails/rails/blob/5-0-stable/actionpack/CHANGELOG.md) for previous changes.
+Please check [5-1-stable](https://github.com/rails/rails/blob/5-1-stable/actionpack/CHANGELOG.md) for previous changes.
diff --git a/actionpack/lib/abstract_controller/caching/fragments.rb b/actionpack/lib/abstract_controller/caching/fragments.rb
index 13fa2b393d..c85b4adba1 100644
--- a/actionpack/lib/abstract_controller/caching/fragments.rb
+++ b/actionpack/lib/abstract_controller/caching/fragments.rb
@@ -136,7 +136,7 @@ module AbstractController
def instrument_fragment_cache(name, key) # :nodoc:
payload = instrument_payload(key)
- ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}".freeze, payload) { yield }
+ ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}", payload) { yield }
end
end
end
diff --git a/actionpack/lib/action_controller/api.rb b/actionpack/lib/action_controller/api.rb
index 5cd8d77ddb..0d1af0d0bd 100644
--- a/actionpack/lib/action_controller/api.rb
+++ b/actionpack/lib/action_controller/api.rb
@@ -81,10 +81,9 @@ module ActionController
# end
# end
#
- # Quite straightforward. Make sure to check the modules included in
- # <tt>ActionController::Base</tt> if you want to use any other
- # functionality that is not provided by <tt>ActionController::API</tt>
- # out of the box.
+ # Make sure to check the modules included in <tt>ActionController::Base</tt>
+ # if you want to use any other functionality that is not provided
+ # by <tt>ActionController::API</tt> out of the box.
class API < Metal
abstract!
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index ca8066cd82..0fe0853da3 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -8,7 +8,7 @@ module ActionController
# on the controller, which will automatically be made accessible to the web-server through \Rails Routes.
#
# By default, only the ApplicationController in a \Rails application inherits from <tt>ActionController::Base</tt>. All other
- # controllers in turn inherit from ApplicationController. This gives you one class to configure things such as
+ # controllers inherit from ApplicationController. This gives you one class to configure things such as
# request forgery protection and filtering of sensitive request parameters.
#
# A sample controller could look like this:
@@ -30,7 +30,7 @@ module ActionController
#
# Unlike index, the create action will not render a template. After performing its main purpose (creating a
# new post), it initiates a redirect instead. This redirect works by returning an external
- # "302 Moved" HTTP response that takes the user to the index action.
+ # <tt>302 Moved</tt> HTTP response that takes the user to the index action.
#
# These two methods represent the two basic action archetypes used in Action Controllers: Get-and-show and do-and-redirect.
# Most actions are variations on these themes.
@@ -59,7 +59,7 @@ module ActionController
# <input type="text" name="post[name]" value="david">
# <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>.
+ # A request coming from a form holding these inputs will include <tt>{ "post" => { "name" => "david", "address" => "hyacintvej" } }</tt>.
# If the address input had been named <tt>post[address][street]</tt>, the <tt>params</tt> would have included
# <tt>{ "post" => { "address" => { "street" => "hyacintvej" } } }</tt>. There's no limit to the depth of the nesting.
#
@@ -74,7 +74,7 @@ module ActionController
#
# session[:person] = Person.authenticate(user_name, password)
#
- # And retrieved again through the same hash:
+ # You can retrieve it again through the same hash:
#
# Hello #{session[:person]}
#
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index 337718afc0..74c4153cd2 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -138,7 +138,7 @@ module ActionController
false
end
- # Delegates to the class' <tt>controller_name</tt>
+ # Delegates to the class' <tt>controller_name</tt>.
def controller_name
self.class.controller_name
end
@@ -244,7 +244,7 @@ module ActionController
end
end
- # Direct dispatch to the controller. Instantiates the controller, then
+ # Direct dispatch to the controller. Instantiates the controller, then
# executes the action named +name+.
def self.dispatch(name, req, res)
if middleware_stack.any?
diff --git a/actionpack/lib/action_controller/metal/etag_with_flash.rb b/actionpack/lib/action_controller/metal/etag_with_flash.rb
index 474d75f02e..7bd338bd7c 100644
--- a/actionpack/lib/action_controller/metal/etag_with_flash.rb
+++ b/actionpack/lib/action_controller/metal/etag_with_flash.rb
@@ -1,9 +1,9 @@
module ActionController
# When you're using the flash, it's generally used as a conditional on the view.
# This means the content of the view depends on the flash. Which in turn means
- # that the etag for a response should be computed with the content of the flash
+ # that the ETag for a response should be computed with the content of the flash
# in mind. This does that by including the content of the flash as a component
- # in the etag that's generated for a response.
+ # in the ETag that's generated for a response.
module EtagWithFlash
extend ActiveSupport::Concern
diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb
index 9d43e752ac..73e67573ca 100644
--- a/actionpack/lib/action_controller/metal/force_ssl.rb
+++ b/actionpack/lib/action_controller/metal/force_ssl.rb
@@ -2,17 +2,17 @@ require "active_support/core_ext/hash/except"
require "active_support/core_ext/hash/slice"
module ActionController
- # This module provides a method which will redirect the browser to use HTTPS
- # protocol. This will ensure that user's sensitive information will be
+ # This module provides a method which will redirect the browser to use the secured HTTPS
+ # protocol. This will ensure that users' sensitive information will be
# transferred safely over the internet. You _should_ always force the browser
# to use HTTPS when you're transferring sensitive information such as
# user authentication, account information, or credit card information.
#
# Note that if you are really concerned about your application security,
# you might consider using +config.force_ssl+ in your config file instead.
- # That will ensure all the data transferred via HTTPS protocol and prevent
- # the user from getting their session hijacked when accessing the site over
- # unsecured HTTP protocol.
+ # That will ensure all the data is transferred via HTTPS, and will
+ # prevent the user from getting their session hijacked when accessing the
+ # site over unsecured HTTP protocol.
module ForceSSL
extend ActiveSupport::Concern
include AbstractController::Callbacks
@@ -23,7 +23,7 @@ module ActionController
module ClassMethods
# Force the request to this particular controller or specified actions to be
- # under HTTPS protocol.
+ # through the HTTPS protocol.
#
# If you need to disable this for any reason (e.g. development) then you can use
# an +:if+ or +:unless+ condition.
@@ -71,7 +71,7 @@ module ActionController
# Redirect the existing request to use the HTTPS protocol.
#
# ==== Parameters
- # * <tt>host_or_options</tt> - Either a host name or any of the url &
+ # * <tt>host_or_options</tt> - Either a host name or any of the url and
# redirect options available to the <tt>force_ssl</tt> method.
def force_ssl_redirect(host_or_options = nil)
unless request.ssl?
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index 0575360068..d8bc895265 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -445,7 +445,7 @@ module ActionController
end
end
- # Parses the token and options out of the token authorization header.
+ # Parses the token and options out of the token Authorization header.
# The value for the Authorization header is expected to have the prefix
# <tt>"Token"</tt> or <tt>"Bearer"</tt>. If the header looks like this:
# Authorization: Token token="abc", nonce="def"
diff --git a/actionpack/lib/action_controller/metal/implicit_render.rb b/actionpack/lib/action_controller/metal/implicit_render.rb
index dde924e682..eeb27f99f4 100644
--- a/actionpack/lib/action_controller/metal/implicit_render.rb
+++ b/actionpack/lib/action_controller/metal/implicit_render.rb
@@ -2,11 +2,11 @@ module ActionController
# Handles implicit rendering for a controller action that does not
# explicitly respond with +render+, +respond_to+, +redirect+, or +head+.
#
- # For API controllers, the implicit response is always 204 No Content.
+ # For API controllers, the implicit response is always <tt>204 No Content</tt>.
#
# For all other controllers, we use these heuristics to decide whether to
# render a template, raise an error for a missing template, or respond with
- # 204 No Content:
+ # <tt>204 No Content</tt>:
#
# First, if we DO find a template, it's rendered. Template lookup accounts
# for the action name, locales, format, variant, template handlers, and more
@@ -23,7 +23,7 @@ module ActionController
# <tt>ActionView::UnknownFormat</tt> with an explanation.
#
# Finally, if we DON'T find a template AND the request isn't a browser page
- # load, then we implicitly respond with 204 No Content.
+ # load, then we implicitly respond with <tt>204 No Content</tt>.
module ImplicitRender
# :stopdoc:
include BasicImplicitRender
diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb
index 924686218f..2485d27cec 100644
--- a/actionpack/lib/action_controller/metal/instrumentation.rb
+++ b/actionpack/lib/action_controller/metal/instrumentation.rb
@@ -3,7 +3,7 @@ require "abstract_controller/logger"
module ActionController
# Adds instrumentation to several ends in ActionController::Base. It also provides
- # some hooks related with process_action, this allows an ORM like Active Record
+ # some hooks related with process_action. This allows an ORM like Active Record
# and/or DataMapper to plug in ActionController and show related information.
#
# Check ActiveRecord::Railties::ControllerRuntime for an example.
diff --git a/actionpack/lib/action_controller/metal/live.rb b/actionpack/lib/action_controller/metal/live.rb
index fed99e6c82..a607ee2309 100644
--- a/actionpack/lib/action_controller/metal/live.rb
+++ b/actionpack/lib/action_controller/metal/live.rb
@@ -239,8 +239,8 @@ module ActionController
error = nil
# This processes the action in a child thread. It lets us return the
- # response code and headers back up the rack stack, and still process
- # the body in parallel with sending data to the client
+ # response code and headers back up the Rack stack, and still process
+ # the body in parallel with sending data to the client.
new_controller_thread {
ActiveSupport::Dependencies.interlock.running do
t2 = Thread.current
@@ -278,9 +278,9 @@ module ActionController
raise error if error
end
- # Spawn a new thread to serve up the controller in. This is to get
+ # Spawn a new thread to serve up the controller in. This is to get
# around the fact that Rack isn't based around IOs and we need to use
- # a thread to stream data from the response bodies. Nobody should call
+ # a thread to stream data from the response bodies. Nobody should call
# this method except in Rails internals. Seriously!
def new_controller_thread # :nodoc:
Thread.new {
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index f6aabcb102..96bd548268 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -181,8 +181,8 @@ module ActionController #:nodoc:
#
# request.variant = [:tablet, :phone]
#
- # which will work similarly to formats and MIME types negotiation. If there will be no
- # +:tablet+ variant declared, +:phone+ variant will be picked:
+ # This will work similarly to formats and MIME types negotiation. If there
+ # is no +:tablet+ variant declared, the +:phone+ variant will be used:
#
# respond_to do |format|
# format.html.none
diff --git a/actionpack/lib/action_controller/metal/parameter_encoding.rb b/actionpack/lib/action_controller/metal/parameter_encoding.rb
index 962532ff09..ecc691619e 100644
--- a/actionpack/lib/action_controller/metal/parameter_encoding.rb
+++ b/actionpack/lib/action_controller/metal/parameter_encoding.rb
@@ -39,7 +39,7 @@ module ActionController
# end
#
# The show action in the above controller would have all parameter values
- # encoded as ASCII-8BIT. This is useful in the case where an application
+ # encoded as ASCII-8BIT. This is useful in the case where an application
# must handle data but encoding of the data is unknown, like file system data.
def skip_parameter_encoding(action)
@_parameter_encodings[action.to_s] = true
diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb
index 7fc898f034..a89fc1678b 100644
--- a/actionpack/lib/action_controller/metal/params_wrapper.rb
+++ b/actionpack/lib/action_controller/metal/params_wrapper.rb
@@ -105,7 +105,11 @@ module ActionController
unless super || exclude
if m.respond_to?(:attribute_names) && m.attribute_names.any?
- self.include = m.attribute_names
+ if m.respond_to?(:stored_attributes) && !m.stored_attributes.empty?
+ self.include = m.attribute_names + m.stored_attributes.values.flatten.map(&:to_s)
+ else
+ self.include = m.attribute_names
+ end
end
end
end
@@ -213,7 +217,7 @@ module ActionController
end
# Sets the default wrapper key or model which will be used to determine
- # wrapper key and attribute names. Will be called automatically when the
+ # wrapper key and attribute names. Called automatically when the
# module is inherited.
def inherited(klass)
if klass._wrapper_options.format.any?
@@ -225,7 +229,7 @@ module ActionController
end
end
- # Performs parameters wrapping upon the request. Will be called automatically
+ # Performs parameters wrapping upon the request. Called automatically
# by the metal call stack.
def process_action(*args)
if _wrapper_enabled?
@@ -238,11 +242,11 @@ module ActionController
wrapped_keys = request.request_parameters.keys
wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)
- # This will make the wrapped hash accessible from controller and view
+ # This will make the wrapped hash accessible from controller and view.
request.parameters.merge! wrapped_hash
request.request_parameters.merge! wrapped_hash
- # This will display the wrapped hash in the log file
+ # This will display the wrapped hash in the log file.
request.filtered_parameters.merge! wrapped_filtered_hash
end
super
diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
index 4dfcf4da28..fdfe82f96b 100644
--- a/actionpack/lib/action_controller/metal/redirecting.rb
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -22,7 +22,7 @@ module ActionController
# redirect_to posts_url
# redirect_to proc { edit_post_url(@post) }
#
- # The redirection happens as a "302 Found" header unless otherwise specified using the <tt>:status</tt> option:
+ # The redirection happens as a <tt>302 Found</tt> header unless otherwise specified using the <tt>:status</tt> option:
#
# redirect_to post_url(@post), status: :found
# redirect_to action: 'atom', status: :moved_permanently
@@ -36,7 +36,7 @@ module ActionController
# If you are using XHR requests other than GET or POST and redirecting after the
# request then some browsers will follow the redirect using the original request
# method. This may lead to undesirable behavior such as a double DELETE. To work
- # around this you can return a <tt>303 See Other</tt> status code which will be
+ # around this you can return a <tt>303 See Other</tt> status code which will be
# followed using a GET request.
#
# redirect_to posts_url, status: :see_other
@@ -50,13 +50,16 @@ module ActionController
# redirect_to post_url(@post), status: 301, flash: { updated_post_id: @post.id }
# redirect_to({ action: 'atom' }, alert: "Something serious happened")
#
+ # Statements after +redirect_to+ in our controller get executed, so +redirect_to+ doesn't stop the execution of the function.
+ # To terminate the execution of the function immediately after the +redirect_to+, use return.
+ # redirect_to post_url(@post) and return
def redirect_to(options = {}, response_status = {})
raise ActionControllerError.new("Cannot redirect to nil!") unless options
raise AbstractController::DoubleRenderError if response_body
self.status = _extract_redirect_to_status(options, response_status)
self.location = _compute_redirect_to_location(request, options)
- self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(location)}\">redirected</a>.</body></html>"
+ self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(response.location)}\">redirected</a>.</body></html>"
end
# Redirects the browser to the page that issued the request (the referrer)
diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb
index 6b17719381..67f207afc2 100644
--- a/actionpack/lib/action_controller/metal/rendering.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -36,7 +36,7 @@ module ActionController
super
end
- # Overwrite render_to_string because body can now be set to a rack body.
+ # Overwrite render_to_string because body can now be set to a Rack body.
def render_to_string(*)
result = super
if result.respond_to?(:each)
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index e8965a6561..d9a8b9c12d 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -262,9 +262,9 @@ module ActionController #:nodoc:
# Returns true or false if a request is verified. Checks:
#
- # * Is it a GET or HEAD request? Gets should be safe and idempotent
+ # * Is it a GET or HEAD request? GETs should be safe and idempotent
# * Does the form_authenticity_token match the given token value from the params?
- # * Does the X-CSRF-Token header match the form_authenticity_token
+ # * Does the X-CSRF-Token header match the form_authenticity_token?
def verified_request? # :doc:
!protect_against_forgery? || request.get? || request.head? ||
(valid_request_origin? && any_authenticity_token_valid?)
@@ -327,7 +327,7 @@ module ActionController #:nodoc:
if masked_token.length == AUTHENTICITY_TOKEN_LENGTH
# This is actually an unmasked token. This is expected if
# you have just upgraded to masked tokens, but should stop
- # happening shortly after installing this gem
+ # happening shortly after installing this gem.
compare_with_real_token masked_token, session
elsif masked_token.length == AUTHENTICITY_TOKEN_LENGTH * 2
@@ -336,13 +336,13 @@ module ActionController #:nodoc:
compare_with_real_token(csrf_token, session) ||
valid_per_form_csrf_token?(csrf_token, session)
else
- false # Token is malformed
+ false # Token is malformed.
end
end
def unmask_token(masked_token) # :doc:
# Split the token into the one-time pad and the encrypted
- # value and decrypt it
+ # value and decrypt it.
one_time_pad = masked_token[0...AUTHENTICITY_TOKEN_LENGTH]
encrypted_csrf_token = masked_token[AUTHENTICITY_TOKEN_LENGTH..-1]
xor_byte_strings(one_time_pad, encrypted_csrf_token)
diff --git a/actionpack/lib/action_controller/metal/rescue.rb b/actionpack/lib/action_controller/metal/rescue.rb
index 2d99e4045b..25757938f5 100644
--- a/actionpack/lib/action_controller/metal/rescue.rb
+++ b/actionpack/lib/action_controller/metal/rescue.rb
@@ -10,7 +10,7 @@ module ActionController #:nodoc:
# exceptions must be shown. This method is only called when
# consider_all_requests_local is false. By default, it returns
# false, but someone may set it to `request.local?` so local
- # requests in production still shows the detailed exception pages.
+ # requests in production still show the detailed exception pages.
def show_detailed_exceptions?
false
end
diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index 877a08b222..58cf60ad2a 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -3,7 +3,7 @@ require "rack/chunked"
module ActionController #:nodoc:
# Allows views to be streamed back to the client as they are rendered.
#
- # The default way Rails renders views is by first rendering the template
+ # By default, Rails renders views by first rendering the template
# and then the layout. The response is sent to the client after the whole
# template is rendered, all queries are made, and the layout is processed.
#
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index d304dcf468..1190e0ed69 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -112,6 +112,77 @@ module ActionController
cattr_accessor :action_on_unpermitted_parameters, instance_accessor: false
+ ##
+ # :method: as_json
+ #
+ # :call-seq:
+ # as_json(options=nil)
+ #
+ # Returns a hash that can be used as the JSON representation for the parameters.
+
+ ##
+ # :method: empty?
+ #
+ # :call-seq:
+ # empty?()
+ #
+ # Returns true if the parameters have no key/value pairs.
+
+ ##
+ # :method: has_key?
+ #
+ # :call-seq:
+ # has_key?(key)
+ #
+ # Returns true if the given key is present in the parameters.
+
+ ##
+ # :method: has_value?
+ #
+ # :call-seq:
+ # has_value?(value)
+ #
+ # Returns true if the given value is present for some key in the parameters.
+
+ ##
+ # :method: include?
+ #
+ # :call-seq:
+ # include?(key)
+ #
+ # Returns true if the given key is present in the parameters.
+
+ ##
+ # :method: key?
+ #
+ # :call-seq:
+ # key?(key)
+ #
+ # Returns true if the given key is present in the parameters.
+
+ ##
+ # :method: keys
+ #
+ # :call-seq:
+ # keys()
+ #
+ # Returns a new array of the keys of the parameters.
+
+ ##
+ # :method: value?
+ #
+ # :call-seq:
+ # value?(value)
+ #
+ # Returns true if the given value is present for some key in the parameters.
+
+ ##
+ # :method: values
+ #
+ # :call-seq:
+ # values()
+ #
+ # Returns a new array of the values of the parameters.
delegate :keys, :key?, :has_key?, :values, :has_value?, :value?, :empty?, :include?,
:as_json, to: :@parameters
@@ -191,7 +262,7 @@ module ActionController
alias_method :to_unsafe_hash, :to_unsafe_h
# Convert all hashes in values into parameters, then yield each pair in
- # the same way as <tt>Hash#each_pair</tt>
+ # the same way as <tt>Hash#each_pair</tt>.
def each_pair(&block)
@parameters.each_pair do |key, value|
yield key, convert_hashes_to_parameters(key, value)
@@ -339,7 +410,7 @@ module ActionController
#
# params.permit(preferences: {})
#
- # but be careful because this opens the door to arbitrary input. In this
+ # Be careful because this opens the door to arbitrary input. In this
# case, +permit+ ensures values in the returned structure are permitted
# scalars and filters out anything else.
#
@@ -575,20 +646,35 @@ module ActionController
end
# Returns a new <tt>ActionController::Parameters</tt> with all keys from
- # +other_hash+ merges into current hash.
+ # +other_hash+ merged into current hash.
def merge(other_hash)
new_instance_with_inherited_permitted_status(
@parameters.merge(other_hash.to_h)
)
end
- # Returns current <tt>ActionController::Parameters</tt> instance which
- # +other_hash+ merges into current hash.
+ # Returns current <tt>ActionController::Parameters</tt> instance with
+ # +other_hash+ merged into current hash.
def merge!(other_hash)
@parameters.merge!(other_hash.to_h)
self
end
+ # Returns a new <tt>ActionController::Parameters</tt> with all keys from
+ # current hash merged into +other_hash+.
+ def reverse_merge(other_hash)
+ new_instance_with_inherited_permitted_status(
+ other_hash.to_h.merge(@parameters)
+ )
+ end
+
+ # Returns current <tt>ActionController::Parameters</tt> instance with
+ # current hash merged into +other_hash+.
+ def reverse_merge!(other_hash)
+ @parameters.merge!(other_hash.to_h) { |key, left, right| left }
+ self
+ end
+
# This is required by ActiveModel attribute assignment, so that user can
# pass +Parameters+ to a mass assignment methods in a model. It should not
# matter as we are using +HashWithIndifferentAccess+ internally.
@@ -629,7 +715,7 @@ module ActionController
undef_method :to_param
- # Returns duplicate of object including all parameters
+ # Returns duplicate of object including all parameters.
def deep_dup
self.class.new(@parameters.deep_dup).tap do |duplicate|
duplicate.permitted = @permitted
@@ -849,7 +935,7 @@ module ActionController
# whitelisted.
#
# In addition, parameters can be marked as required and flow through a
- # predefined raise/rescue flow to end up as a 400 Bad Request with no
+ # predefined raise/rescue flow to end up as a <tt>400 Bad Request</tt> with no
# effort.
#
# class PeopleController < ActionController::Base
@@ -862,7 +948,7 @@ module ActionController
# end
#
# # This will pass with flying colors as long as there's a person key in the
- # # parameters, otherwise it'll raise an ActionController::MissingParameter
+ # # parameters, otherwise it'll raise an ActionController::ParameterMissing
# # exception, which will get caught by ActionController::Base and turned
# # into a 400 Bad Request reply.
# def update
@@ -873,7 +959,7 @@ module ActionController
#
# private
# # Using a private method to encapsulate the permissible parameters is
- # # just a good pattern since you'll be able to reuse the same permit
+ # # a good pattern since you'll be able to reuse the same permit
# # list between create and update. Also, you can specialize this method
# # with per-user checking of permissible attributes.
# def person_params
diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb
index 9f3cc099d6..21ed5b4ec8 100644
--- a/actionpack/lib/action_controller/metal/url_for.rb
+++ b/actionpack/lib/action_controller/metal/url_for.rb
@@ -3,7 +3,7 @@ module ActionController
# 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
+ # URL options like the +host+. In order to do so, this module requires the host class
# to implement +env+ which needs to be Rack-compatible and +request+
# which is either an instance of +ActionDispatch::Request+ or an object
# that responds to the +host+, +optional_port+, +protocol+ and
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index a7cdfe6a98..fadfc8de60 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -42,7 +42,7 @@ module ActionController
options.javascripts_dir ||= paths["public/javascripts"].first
options.stylesheets_dir ||= paths["public/stylesheets"].first
- # Ensure readers methods get compiled
+ # Ensure readers methods get compiled.
options.asset_host ||= app.config.asset_host
options.relative_url_root ||= app.config.relative_url_root
diff --git a/actionpack/lib/action_controller/renderer.rb b/actionpack/lib/action_controller/renderer.rb
index acb400cd15..cbb719d8b2 100644
--- a/actionpack/lib/action_controller/renderer.rb
+++ b/actionpack/lib/action_controller/renderer.rb
@@ -5,7 +5,7 @@ module ActionController
# without requirement of being in controller actions.
#
# You get a concrete renderer class by invoking ActionController::Base#renderer.
- # For example,
+ # For example:
#
# ApplicationController.renderer
#
@@ -18,7 +18,7 @@ module ActionController
# ApplicationController.render template: '...'
#
# #render allows you to use the same options that you can use when rendering in a controller.
- # For example,
+ # For example:
#
# FooController.render :action, locals: { ... }, assigns: { ... }
#
@@ -56,7 +56,7 @@ module ActionController
# Create a new renderer for the same controller but with new defaults.
def with_defaults(defaults)
- self.class.new controller, env, self.defaults.merge(defaults)
+ self.class.new controller, @env, self.defaults.merge(defaults)
end
# Accepts a custom Rack environment to render templates in.
@@ -85,6 +85,7 @@ module ActionController
def normalize_keys(env)
new_env = {}
env.each_pair { |k, v| new_env[rack_key_for(k)] = rack_value_for(k, v) }
+ new_env["rack.url_scheme"] = new_env["HTTPS"] == "on" ? "https" : "http"
new_env
end
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 7229c67f30..bc42d50205 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -13,10 +13,10 @@ module ActionController
end
module Live
- # Disable controller / rendering threads in tests. User tests can access
+ # Disable controller / rendering threads in tests. User tests can access
# the database on the main thread, so they could open a txn, then the
# controller thread will open a new connection and try to access data
- # that's only visible to the main thread's txn. This is the problem in #23483
+ # that's only visible to the main thread's txn. This is the problem in #23483.
remove_method :new_controller_thread
def new_controller_thread # :nodoc:
yield
@@ -35,7 +35,7 @@ module ActionController
attr_reader :controller_class
- # Create a new test request with default `env` values
+ # Create a new test request with default `env` values.
def self.create(controller_class)
env = {}
env = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application
@@ -131,7 +131,7 @@ module ActionController
include Rack::Test::Utils
def should_multipart?(params)
- # FIXME: lifted from Rack-Test. We should push this separation upstream
+ # FIXME: lifted from Rack-Test. We should push this separation upstream.
multipart = false
query = lambda { |value|
case value
@@ -300,7 +300,7 @@ module ActionController
# assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave"
# assert flash.empty? # makes sure that there's nothing in the flash
#
- # On top of the collections, you have the complete url that a given action redirected to available in <tt>redirect_to_url</tt>.
+ # On top of the collections, you have the complete URL that a given action redirected to available in <tt>redirect_to_url</tt>.
#
# For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another
# action call which can then be asserted against.
@@ -534,7 +534,6 @@ module ActionController
@request.delete_header "HTTP_ACCEPT"
end
@request.query_string = ""
- @request.env.delete "PATH_INFO"
@response.sent!
end
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb
index 028177ace2..303790e96d 100644
--- a/actionpack/lib/action_dispatch.rb
+++ b/actionpack/lib/action_dispatch.rb
@@ -97,6 +97,8 @@ module ActionDispatch
autoload :TestResponse
autoload :AssertionResponse
end
+
+ autoload :SystemTestCase, "action_dispatch/system_test_case"
end
autoload :Mime, "action_dispatch/http/mime_type"
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
index c4fe3a5c09..19f89edbc1 100644
--- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb
+++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
@@ -135,9 +135,7 @@ module ActionDispatch
}
end
- # Receives an array of mimes and return the first user sent mime that
- # matches the order array.
- #
+ # Returns the first MIME type that matches the provided array of MIME types.
def negotiate_mime(order)
formats.each do |priority|
if priority == Mime::ALL
diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb
index 1583a8f87f..0cf010f59e 100644
--- a/actionpack/lib/action_dispatch/http/mime_type.rb
+++ b/actionpack/lib/action_dispatch/http/mime_type.rb
@@ -46,7 +46,7 @@ module Mime
end
end
- # Encapsulates the notion of a mime type. Can be used at render time, for example, with:
+ # Encapsulates the notion of a MIME type. Can be used at render time, for example, with:
#
# class PostsController < ActionController::Base
# def show
@@ -64,7 +64,7 @@ module Mime
@register_callbacks = []
- # A simple helper class used in parsing the accept header
+ # A simple helper class used in parsing the accept header.
class AcceptItem #:nodoc:
attr_accessor :index, :name, :q
alias :to_s :name
@@ -72,7 +72,7 @@ module Mime
def initialize(index, name, q = nil)
@index = index
@name = name
- q ||= 0.0 if @name == "*/*".freeze # default wildcard match to end of list
+ q ||= 0.0 if @name == "*/*".freeze # Default wildcard match to end of list.
@q = ((q || 1.0).to_f * 100).to_i
end
@@ -90,22 +90,22 @@ module Mime
text_xml_idx = find_item_by_name list, "text/xml"
app_xml_idx = find_item_by_name list, Mime[:xml].to_s
- # Take care of the broken text/xml entry by renaming or deleting it
+ # Take care of the broken text/xml entry by renaming or deleting it.
if text_xml_idx && app_xml_idx
app_xml = list[app_xml_idx]
text_xml = list[text_xml_idx]
- app_xml.q = [text_xml.q, app_xml.q].max # set the q value to the max of the two
- if app_xml_idx > text_xml_idx # make sure app_xml is ahead of text_xml in the list
+ app_xml.q = [text_xml.q, app_xml.q].max # Set the q value to the max of the two.
+ if app_xml_idx > text_xml_idx # Make sure app_xml is ahead of text_xml in the list.
list[app_xml_idx], list[text_xml_idx] = text_xml, app_xml
app_xml_idx, text_xml_idx = text_xml_idx, app_xml_idx
end
- list.delete_at(text_xml_idx) # delete text_xml from the list
+ list.delete_at(text_xml_idx) # Delete text_xml from the list.
elsif text_xml_idx
list[text_xml_idx].name = Mime[:xml].to_s
end
- # Look for more specific XML-based types and sort them ahead of app/xml
+ # Look for more specific XML-based types and sort them ahead of app/xml.
if app_xml_idx
app_xml = list[app_xml_idx]
idx = app_xml_idx
@@ -147,7 +147,7 @@ module Mime
EXTENSION_LOOKUP[extension.to_s]
end
- # Registers an alias that's not used on mime type lookup, but can be referenced directly. Especially useful for
+ # Registers an alias that's not used on MIME type lookup, but can be referenced directly. Especially useful for
# rendering different HTML versions depending on the user agent, like an iPhone.
def register_alias(string, symbol, extension_synonyms = [])
register(string, symbol, [], extension_synonyms, true)
diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb
index 8f21eca440..79a2ef965b 100644
--- a/actionpack/lib/action_dispatch/http/parameters.rb
+++ b/actionpack/lib/action_dispatch/http/parameters.rb
@@ -13,7 +13,7 @@ module ActionDispatch
}
# Raised when raw data from the request cannot be parsed by the parser
- # defined for request's content mime type.
+ # defined for request's content MIME type.
class ParseError < StandardError
def initialize
super($!.message)
@@ -30,9 +30,9 @@ module ActionDispatch
end
module ClassMethods
- # Configure the parameter parser for a given mime type.
+ # Configure the parameter parser for a given MIME type.
#
- # It accepts a hash where the key is the symbol of the mime type
+ # It accepts a hash where the key is the symbol of the MIME type
# and the value is a proc.
#
# original_parsers = ActionDispatch::Request.parameter_parsers
@@ -100,7 +100,7 @@ module ActionDispatch
begin
strategy.call(raw_post)
- rescue # JSON or Ruby code block errors
+ rescue # JSON or Ruby code block errors.
my_logger = logger || ActiveSupport::Logger.new($stderr)
my_logger.debug "Error occurred while parsing request parameters.\nContents:\n\n#{raw_post}"
@@ -115,6 +115,7 @@ module ActionDispatch
end
module ParamsParser
- ParseError = ActiveSupport::Deprecation::DeprecatedConstantProxy.new("ActionDispatch::ParamsParser::ParseError", "ActionDispatch::Http::Parameters::ParseError")
+ include ActiveSupport::Deprecation::DeprecatedConstantAccessor
+ deprecate_constant "ParseError", "ActionDispatch::Http::Parameters::ParseError"
end
end
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 19fa42ce12..6d42404a98 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -114,7 +114,7 @@ module ActionDispatch
HTTP_METHOD_LOOKUP = {}
- # Populate the HTTP method lookup cache
+ # Populate the HTTP method lookup cache.
HTTP_METHODS.each { |method|
HTTP_METHOD_LOOKUP[method] = method.underscore.to_sym
}
@@ -165,12 +165,12 @@ module ActionDispatch
def show_exceptions? # :nodoc:
# We're treating `nil` as "unset", and we want the default setting to be
- # `true`. This logic should be extracted to `env_config` and calculated
+ # `true`. This logic should be extracted to `env_config` and calculated
# once.
!(get_header("action_dispatch.show_exceptions".freeze) == false)
end
- # Returns a symbol form of the #request_method
+ # Returns a symbol form of the #request_method.
def request_method_symbol
HTTP_METHOD_LOOKUP[request_method]
end
@@ -182,7 +182,7 @@ module ActionDispatch
@method ||= check_method(get_header("rack.methodoverride.original_method") || get_header("REQUEST_METHOD"))
end
- # Returns a symbol form of the #method
+ # Returns a symbol form of the #method.
def method_symbol
HTTP_METHOD_LOOKUP[method]
end
@@ -267,7 +267,7 @@ module ActionDispatch
# (which sets the action_dispatch.request_id environment variable).
#
# This unique ID is useful for tracing a request from end-to-end as part of logging or debugging.
- # This relies on the rack variable set by the ActionDispatch::RequestId middleware.
+ # This relies on the Rack variable set by the ActionDispatch::RequestId middleware.
def request_id
get_header ACTION_DISPATCH_REQUEST_ID
end
@@ -339,7 +339,7 @@ module ActionDispatch
Session::Options.set self, options
end
- # Override Rack's GET method to support indifferent access
+ # Override Rack's GET method to support indifferent access.
def GET
fetch_header("action_dispatch.request.query_parameters") do |k|
rack_query_params = super || {}
@@ -352,7 +352,7 @@ module ActionDispatch
end
alias :query_parameters :GET
- # Override Rack's POST method to support indifferent access
+ # Override Rack's POST method to support indifferent access.
def POST
fetch_header("action_dispatch.request.request_parameters") do
pr = parse_formatted_parameters(params_parsers) do |params|
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index dc159596c4..2ee52eeb48 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -85,7 +85,7 @@ module ActionDispatch # :nodoc:
cattr_accessor(:default_headers)
include Rack::Response::Helpers
- # Aliasing these off because AD::Http::Cache::Response defines them
+ # Aliasing these off because AD::Http::Cache::Response defines them.
alias :_cache_control :cache_control
alias :_cache_control= :cache_control=
@@ -142,7 +142,7 @@ module ActionDispatch # :nodoc:
private
def each_chunk(&block)
- @buf.each(&block) # extract into own method
+ @buf.each(&block)
end
end
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index a6937d54ff..b6be48a48b 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -101,10 +101,8 @@ module ActionDispatch
end
def add_trailing_slash(path)
- # includes querysting
if path.include?("?")
path.sub!(/\?/, '/\&')
- # does not have a .format
elsif !path.include?(".")
path.sub!(/[^\/]\z|\A\z/, '\&/')
end
diff --git a/actionpack/lib/action_dispatch/journey/formatter.rb b/actionpack/lib/action_dispatch/journey/formatter.rb
index f3b8e82d32..326f4e52f9 100644
--- a/actionpack/lib/action_dispatch/journey/formatter.rb
+++ b/actionpack/lib/action_dispatch/journey/formatter.rb
@@ -15,7 +15,7 @@ module ActionDispatch
def generate(name, options, path_parameters, parameterize = nil)
constraints = path_parameters.merge(options)
- missing_keys = nil # need for variable scope
+ missing_keys = nil
match_route(name, constraints) do |route|
parameterized_parts = extract_parameterized_parts(route, options, path_parameters, parameterize)
diff --git a/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb b/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb
index beb9f1ef3b..e1ac2c873e 100644
--- a/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb
+++ b/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb
@@ -109,7 +109,6 @@ module ActionDispatch
svg = to_svg
javascripts = [states, fsm_js]
- # Annoying hack warnings
fun_routes = fun_routes
stylesheets = stylesheets
svg = svg
diff --git a/actionpack/lib/action_dispatch/journey/route.rb b/actionpack/lib/action_dispatch/journey/route.rb
index 927fd369c4..7bc15aa6b3 100644
--- a/actionpack/lib/action_dispatch/journey/route.rb
+++ b/actionpack/lib/action_dispatch/journey/route.rb
@@ -89,8 +89,15 @@ module ActionDispatch
end
end
+ # Needed for `rails routes`. Picks up succinctly defined requirements
+ # for a route, for example route
+ #
+ # get 'photo/:id', :controller => 'photos', :action => 'show',
+ # :id => /[A-Z]\d{5}/
+ #
+ # will have {:controller=>"photos", :action=>"show", :id=>/[A-Z]\d{5}/}
+ # as requirements.
def requirements
- # needed for rails `rails routes`
@defaults.merge(path.requirements).delete_if { |_, v|
/.+?/ == v
}
diff --git a/actionpack/lib/action_dispatch/journey/router/utils.rb b/actionpack/lib/action_dispatch/journey/router/utils.rb
index d641642338..ffcd875b4d 100644
--- a/actionpack/lib/action_dispatch/journey/router/utils.rb
+++ b/actionpack/lib/action_dispatch/journey/router/utils.rb
@@ -5,7 +5,7 @@ module ActionDispatch
# Normalizes URI path.
#
# Strips off trailing slash and ensures there is a leading slash.
- # Also converts downcase url encoded string to uppercase.
+ # Also converts downcase URL encoded string to uppercase.
#
# normalize_path("/foo") # => "/foo"
# normalize_path("/foo/") # => "/foo"
diff --git a/actionpack/lib/action_dispatch/journey/visitors.rb b/actionpack/lib/action_dispatch/journey/visitors.rb
index 1c50192867..335797f4b9 100644
--- a/actionpack/lib/action_dispatch/journey/visitors.rb
+++ b/actionpack/lib/action_dispatch/journey/visitors.rb
@@ -154,7 +154,7 @@ module ActionDispatch
end
end
- # Loop through the requirements AST
+ # Loop through the requirements AST.
class Each < FunctionalVisitor # :nodoc:
def visit(node, block)
block.call(node)
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index c61cb3fd68..e565c03a8a 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -160,7 +160,7 @@ module ActionDispatch
# Raised when storing more than 4K of session data.
CookieOverflow = Class.new StandardError
- # Include in a cookie jar to allow chaining, e.g. cookies.permanent.signed
+ # Include in a cookie jar to allow chaining, e.g. cookies.permanent.signed.
module ChainedCookieJars
# Returns a jar that'll automatically set the assigned cookies to have an expiration date 20 years from now. Example:
#
@@ -345,16 +345,16 @@ module ActionDispatch
options[:path] ||= "/"
if options[:domain] == :all || options[:domain] == "all"
- # if there is a provided tld length then we use it otherwise default domain regexp
+ # If there is a provided tld length then we use it otherwise default domain regexp.
domain_regexp = options[:tld_length] ? /([^.]+\.?){#{options[:tld_length]}}$/ : DOMAIN_REGEXP
- # if host is not ip and matches domain regexp
+ # If host is not ip and matches domain regexp.
# (ip confirms to domain regexp so we explicitly check for ip)
options[:domain] = if (request.host !~ /^[\d.]+$/) && (request.host =~ domain_regexp)
".#{$&}"
end
elsif options[:domain].is_a? Array
- # if host matches one of the supplied domains without a dot in front of it
+ # If host matches one of the supplied domains without a dot in front of it.
options[:domain] = options[:domain].find { |domain| request.host.include? domain.sub(/^\./, "") }
end
end
@@ -404,7 +404,7 @@ module ActionDispatch
@delete_cookies[name.to_s] == options
end
- # Removes all cookies on the client machine by calling <tt>delete</tt> for each cookie
+ # Removes all cookies on the client machine by calling <tt>delete</tt> for each cookie.
def clear(options = {})
@cookies.each_key { |k| delete(k, options) }
end
diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb
index cbe2f4be4d..6b29ce63ba 100644
--- a/actionpack/lib/action_dispatch/middleware/flash.rb
+++ b/actionpack/lib/action_dispatch/middleware/flash.rb
@@ -65,7 +65,7 @@ module ActionDispatch
self.flash = flash_hash.dup
end
- if (!session.respond_to?(:loaded?) || session.loaded?) && # (reset_session uses {}, which doesn't implement #loaded?)
+ if (!session.respond_to?(:loaded?) || session.loaded?) && # reset_session uses {}, which doesn't implement #loaded?
session.key?("flash") && session["flash"].nil?
session.delete("flash")
end
diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
index 8bae5bfeff..53d5a4918c 100644
--- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb
+++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
@@ -157,13 +157,13 @@ module ActionDispatch
def ips_from(header) # :doc:
return [] unless header
- # Split the comma-separated list into an array of strings
+ # Split the comma-separated list into an array of strings.
ips = header.strip.split(/[,\s]+/)
ips.select do |ip|
begin
- # Only return IPs that are valid according to the IPAddr#new method
+ # Only return IPs that are valid according to the IPAddr#new method.
range = IPAddr.new(ip).to_range
- # we want to make sure nobody is sneaking a netmask in
+ # We want to make sure nobody is sneaking a netmask in.
range.begin == range.end
rescue ArgumentError
nil
diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
index d9f018c8ac..21ccf5a097 100644
--- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
@@ -53,7 +53,7 @@ module ActionDispatch
rescue ArgumentError => argument_error
if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
begin
- # Note that the regexp does not allow $1 to end with a ':'
+ # Note that the regexp does not allow $1 to end with a ':'.
$1.constantize
rescue LoadError, NameError
raise ActionDispatch::Session::SessionRestoreError
diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
index 90f26a1c33..5a99714ec2 100644
--- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
@@ -8,7 +8,7 @@ module ActionDispatch
# The exceptions app should be passed as parameter on initialization
# of ShowExceptions. Every time there is an exception, ShowExceptions will
# store the exception in env["action_dispatch.exception"], rewrite the
- # PATH_INFO to the exception status code and call the rack app.
+ # PATH_INFO to the exception status code and call the Rack app.
#
# If the application returns a "X-Cascade" pass response, this middleware
# will send an empty response as result with the correct status code.
diff --git a/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb b/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb
index 429ea7057c..2d21ae63f5 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb
@@ -60,7 +60,7 @@
<%= link_to "Path", "#", 'data-route-helper' => '_path',
title: "Returns a relative path (without the http or domain)" %> /
<%= link_to "Url", "#", 'data-route-helper' => '_url',
- title: "Returns an absolute url (with the http and domain)" %>
+ title: "Returns an absolute URL (with the http and domain)" %>
</th>
<th><%# HTTP Verb %>
</th>
@@ -93,7 +93,7 @@
}
}
- // get JSON from url and invoke callback with result
+ // get JSON from URL and invoke callback with result
function getJSON(url, success) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
diff --git a/actionpack/lib/action_dispatch/request/session.rb b/actionpack/lib/action_dispatch/request/session.rb
index a2a80f39fc..74ba6466cf 100644
--- a/actionpack/lib/action_dispatch/request/session.rb
+++ b/actionpack/lib/action_dispatch/request/session.rb
@@ -7,10 +7,10 @@ module ActionDispatch
ENV_SESSION_KEY = Rack::RACK_SESSION # :nodoc:
ENV_SESSION_OPTIONS_KEY = Rack::RACK_SESSION_OPTIONS # :nodoc:
- # Singleton object used to determine if an optional param wasn't specified
+ # Singleton object used to determine if an optional param wasn't specified.
Unspecified = Object.new
- # Creates a session hash, merging the properties of the previous session if any
+ # Creates a session hash, merging the properties of the previous session if any.
def self.create(store, req, default_options)
session_was = find req
session = Request::Session.new(store, req)
@@ -63,7 +63,7 @@ module ActionDispatch
@req = req
@delegate = {}
@loaded = false
- @exists = nil # we haven't checked yet
+ @exists = nil # We haven't checked yet.
end
def id
@@ -79,7 +79,7 @@ module ActionDispatch
options = self.options || {}
@by.send(:delete_session, @req, options.id(@req), options)
- # Load the new sid to be written with the response
+ # Load the new sid to be written with the response.
@loaded = false
load_for_write!
end
diff --git a/actionpack/lib/action_dispatch/request/utils.rb b/actionpack/lib/action_dispatch/request/utils.rb
index 01bc871e5f..3615e4b1d8 100644
--- a/actionpack/lib/action_dispatch/request/utils.rb
+++ b/actionpack/lib/action_dispatch/request/utils.rb
@@ -40,7 +40,6 @@ module ActionDispatch
class ParamEncoder # :nodoc:
# Convert nested Hash to HashWithIndifferentAccess.
- #
def self.normalize_encode_params(params)
case params
when Array
@@ -63,7 +62,7 @@ module ActionDispatch
end
end
- # Remove nils from the params hash
+ # Remove nils from the params hash.
class NoNilParamEncoder < ParamEncoder # :nodoc:
def self.handle_array(params)
list = super
diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb
index c554ce98bc..60d4789a63 100644
--- a/actionpack/lib/action_dispatch/routing.rb
+++ b/actionpack/lib/action_dispatch/routing.rb
@@ -120,7 +120,7 @@ module ActionDispatch
# controller :blog do
# get 'blog/show' => :list
# get 'blog/delete' => :delete
- # get 'blog/edit' => :edit
+ # get 'blog/edit' => :edit
# end
#
# # provides named routes for show, delete, and edit
diff --git a/actionpack/lib/action_dispatch/routing/inspector.rb b/actionpack/lib/action_dispatch/routing/inspector.rb
index b91ffb8419..9aa4b92df2 100644
--- a/actionpack/lib/action_dispatch/routing/inspector.rb
+++ b/actionpack/lib/action_dispatch/routing/inspector.rb
@@ -196,7 +196,7 @@ module ActionDispatch
@buffer << @view.render(partial: "routes/route", collection: routes)
end
- # the header is part of the HTML page, so we don't construct it here.
+ # The header is part of the HTML page, so we don't construct it here.
def header(routes)
end
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 8d9f70e3c6..8ad17504ae 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -17,9 +17,9 @@ module ActionDispatch
CALL = ->(app, req) { app.call req.env }
def initialize(app, constraints, strategy)
- # Unwrap Constraints objects. I don't actually think it's possible
+ # Unwrap Constraints objects. I don't actually think it's possible
# to pass a Constraints object to this constructor, but there were
- # multiple places that kept testing children of this object. I
+ # multiple places that kept testing children of this object. I
# *think* they were just being defensive, but I have no idea.
if app.is_a?(self.class)
constraints += app.constraints
@@ -218,7 +218,7 @@ module ActionDispatch
private
def add_wildcard_options(options, formatted, path_ast)
# Add a constraint for wildcard route to make it non-greedy and match the
- # optional format part of the route by default
+ # optional format part of the route by default.
if formatted != false
path_ast.grep(Journey::Nodes::Star).each_with_object({}) { |node, hash|
hash[node.name.to_sym] ||= /.+?/
@@ -396,7 +396,7 @@ module ActionDispatch
end
module Base
- # Matches a url pattern to one or more routes.
+ # Matches a URL pattern to one or more routes.
#
# You should not use the +match+ method in your router
# without specifying an HTTP method.
@@ -406,7 +406,7 @@ module ActionDispatch
# # sets :controller, :action and :id in params
# match ':controller/:action/:id', via: [:get, :post]
#
- # Note that +:controller+, +:action+ and +:id+ are interpreted as url
+ # Note that +:controller+, +:action+ and +:id+ are interpreted as URL
# query parameters and thus available through +params+ in an action.
#
# If you want to expose your action to GET, use +get+ in the router:
@@ -455,7 +455,7 @@ module ActionDispatch
#
# === Options
#
- # Any options not seen here are passed on as params with the url.
+ # Any options not seen here are passed on as params with the URL.
#
# [:controller]
# The route's controller.
@@ -660,7 +660,7 @@ module ActionDispatch
else
prefix_options = options.slice(*_route.segment_keys)
prefix_options[:relative_url_root] = "".freeze
- # we must actually delete prefix segment keys to avoid passing them to next url_for
+ # We must actually delete prefix segment keys to avoid passing them to next url_for.
_route.segment_keys.each { |k| options.delete(k) }
_routes.url_helpers.send("#{name}_path", prefix_options)
end
@@ -1238,7 +1238,7 @@ module ActionDispatch
#
# resource :profile
#
- # creates six different routes in your application, all mapping to
+ # This creates six different routes in your application, all mapping to
# the +Profiles+ controller (note that the controller is named after
# the plural):
#
@@ -1323,14 +1323,14 @@ module ActionDispatch
#
# resources :posts, path_names: { new: "brand_new" }
#
- # The above example will now change /posts/new to /posts/brand_new
+ # The above example will now change /posts/new to /posts/brand_new.
#
# [:path]
# Allows you to change the path prefix for the resource.
#
# resources :posts, path: 'postings'
#
- # The resource and all segments will now route to /postings instead of /posts
+ # The resource and all segments will now route to /postings instead of /posts.
#
# [:only]
# Only generate routes for the given actions.
@@ -1525,7 +1525,7 @@ module ActionDispatch
end
end
- # See ActionDispatch::Routing::Mapper::Scoping#namespace
+ # See ActionDispatch::Routing::Mapper::Scoping#namespace.
def namespace(path, options = {})
if resource_scope?
nested { super }
@@ -1545,7 +1545,7 @@ module ActionDispatch
!parent_resource.singleton? && @scope[:shallow]
end
- # Matches a url pattern to one or more routes.
+ # Matches a URL pattern to one or more routes.
# For more information, see match[rdoc-ref:Base#match].
#
# match 'path' => 'controller#action', via: patch
@@ -1904,7 +1904,7 @@ module ActionDispatch
ast = Journey::Parser.parse path
mapping = Mapping.build(@scope, @set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options)
- @set.add_route(mapping, ast, as, anchor)
+ @set.add_route(mapping, as)
end
def match_root_route(options)
@@ -2003,7 +2003,7 @@ module ActionDispatch
# concerns :commentable
# end
#
- # concerns also work in any routes helper that you want to use:
+ # Concerns also work in any routes helper that you want to use:
#
# namespace :posts do
# concerns :commentable
@@ -2020,6 +2020,120 @@ module ActionDispatch
end
end
+ module CustomUrls
+ # Define custom url helpers that will be added to the application's
+ # routes. This allows you to override and/or replace the default behavior
+ # of routing helpers, e.g:
+ #
+ # direct :homepage do
+ # "http://www.rubyonrails.org"
+ # end
+ #
+ # direct :commentable do |model|
+ # [ model, anchor: model.dom_id ]
+ # end
+ #
+ # direct :main do
+ # { controller: "pages", action: "index", subdomain: "www" }
+ # end
+ #
+ # The return value from the block passed to `direct` must be a valid set of
+ # arguments for `url_for` which will actually build the URL string. This can
+ # be one of the following:
+ #
+ # * A string, which is treated as a generated URL
+ # * A hash, e.g. { controller: "pages", action: "index" }
+ # * An array, which is passed to `polymorphic_url`
+ # * An Active Model instance
+ # * An Active Model class
+ #
+ # NOTE: Other URL helpers can be called in the block but be careful not to invoke
+ # your custom URL helper again otherwise it will result in a stack overflow error.
+ #
+ # You can also specify default options that will be passed through to
+ # your URL helper definition, e.g:
+ #
+ # direct :browse, page: 1, size: 10 do |options|
+ # [ :products, options.merge(params.permit(:page, :size).to_h.symbolize_keys) ]
+ # end
+ #
+ # In this instance the `params` object comes from the context in which the the
+ # block is executed, e.g. generating a URL inside a controller action or a view.
+ # If the block is executed where there isn't a params object such as this:
+ #
+ # Rails.application.routes.url_helpers.browse_path
+ #
+ # then it will raise a `NameError`. Because of this you need to be aware of the
+ # context in which you will use your custom URL helper when defining it.
+ #
+ # NOTE: The `direct` method can't be used inside of a scope block such as
+ # `namespace` or `scope` and will raise an error if it detects that it is.
+ def direct(name, options = {}, &block)
+ unless @scope.root?
+ raise RuntimeError, "The direct method can't be used inside a routes scope block"
+ end
+
+ @set.add_url_helper(name, options, &block)
+ end
+
+ # Define custom polymorphic mappings of models to URLs. This alters the
+ # behavior of `polymorphic_url` and consequently the behavior of
+ # `link_to` and `form_for` when passed a model instance, e.g:
+ #
+ # resource :basket
+ #
+ # resolve "Basket" do
+ # [:basket]
+ # end
+ #
+ # This will now generate "/basket" when a `Basket` instance is passed to
+ # `link_to` or `form_for` instead of the standard "/baskets/:id".
+ #
+ # NOTE: This custom behavior only applies to simple polymorphic URLs where
+ # a single model instance is passed and not more complicated forms, e.g:
+ #
+ # # config/routes.rb
+ # resource :profile
+ # namespace :admin do
+ # resources :users
+ # end
+ #
+ # resolve("User") { [:profile] }
+ #
+ # # app/views/application/_menu.html.erb
+ # link_to "Profile", @current_user
+ # link_to "Profile", [:admin, @current_user]
+ #
+ # The first `link_to` will generate "/profile" but the second will generate
+ # the standard polymorphic URL of "/admin/users/1".
+ #
+ # You can pass options to a polymorphic mapping - the arity for the block
+ # needs to be two as the instance is passed as the first argument, e.g:
+ #
+ # resolve "Basket", anchor: "items" do |basket, options|
+ # [:basket, options]
+ # end
+ #
+ # This generates the URL "/basket#items" because when the last item in an
+ # array passed to `polymorphic_url` is a hash then it's treated as options
+ # to the URL helper that gets called.
+ #
+ # NOTE: The `resolve` method can't be used inside of a scope block such as
+ # `namespace` or `scope` and will raise an error if it detects that it is.
+ def resolve(*args, &block)
+ unless @scope.root?
+ raise RuntimeError, "The resolve method can't be used inside a routes scope block"
+ end
+
+ options = args.extract_options!
+ args = args.flatten(1)
+
+ args.each do |klass|
+ @set.add_polymorphic_mapping(klass, options, &block)
+ end
+ end
+ end
+
class Scope # :nodoc:
OPTIONS = [:path, :shallow_path, :as, :shallow_prefix, :module,
:controller, :action, :path_names, :constraints,
@@ -2040,6 +2154,14 @@ module ActionDispatch
scope_level == :nested
end
+ def null?
+ @hash.nil? && @parent.nil?
+ end
+
+ def root?
+ @parent.null?
+ end
+
def resources?
scope_level == :resources
end
@@ -2113,6 +2235,7 @@ module ActionDispatch
include Scoping
include Concerns
include Resources
+ include CustomUrls
end
end
end
diff --git a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
index 432b9bf4c1..e89ea8b21d 100644
--- a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
+++ b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
@@ -40,7 +40,7 @@ module ActionDispatch
#
# Example usage:
#
- # edit_polymorphic_path(@post) # => "/posts/1/edit"
+ # edit_polymorphic_path(@post) # => "/posts/1/edit"
# polymorphic_path(@post, format: :pdf) # => "/posts/1.pdf"
#
# == Usage with mounted engines
@@ -103,6 +103,10 @@ module ActionDispatch
return polymorphic_url record, options
end
+ if mapping = polymorphic_mapping(record_or_hash_or_array)
+ return mapping.call(self, [record_or_hash_or_array, options], false)
+ end
+
opts = options.dup
action = opts.delete :action
type = opts.delete(:routing_type) || :url
@@ -123,6 +127,10 @@ module ActionDispatch
return polymorphic_path record, options
end
+ if mapping = polymorphic_mapping(record_or_hash_or_array)
+ return mapping.call(self, [record_or_hash_or_array, options], true)
+ end
+
opts = options.dup
action = opts.delete :action
type = :path
@@ -156,6 +164,14 @@ module ActionDispatch
polymorphic_path(record_or_hash, options.merge(action: action))
end
+ def polymorphic_mapping(record)
+ if record.respond_to?(:to_model)
+ _routes.polymorphic_mappings[record.to_model.model_name.name]
+ else
+ _routes.polymorphic_mappings[record.class.name]
+ end
+ end
+
class HelperMethodBuilder # :nodoc:
CACHE = { "path" => {}, "url" => {} }
@@ -255,9 +271,13 @@ module ActionDispatch
[named_route, args]
end
- def handle_model_call(target, model)
- method, args = handle_model model
- target.send(method, *args)
+ def handle_model_call(target, record)
+ if mapping = polymorphic_mapping(target, record)
+ mapping.call(target, [record], suffix == "path")
+ else
+ method, args = handle_model(record)
+ target.send(method, *args)
+ end
end
def handle_list(list)
@@ -303,6 +323,14 @@ module ActionDispatch
private
+ def polymorphic_mapping(target, record)
+ if record.respond_to?(:to_model)
+ target._routes.polymorphic_mappings[record.to_model.model_name.name]
+ else
+ target._routes.polymorphic_mappings[record.class.name]
+ end
+ end
+
def get_method_for_class(klass)
name = @key_strategy.call klass.model_name
get_method_for_string name
diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb
index dabc045007..3bcb341758 100644
--- a/actionpack/lib/action_dispatch/routing/redirection.rb
+++ b/actionpack/lib/action_dispatch/routing/redirection.rb
@@ -36,6 +36,8 @@ module ActionDispatch
uri.host ||= req.host
uri.port ||= req.port unless req.standard_port?
+ req.commit_flash
+
body = %(<html><body>You are being <a href="#{ERB::Util.unwrapped_html_escape(uri.to_s)}">redirected</a>.</body></html>)
headers = {
@@ -144,7 +146,7 @@ 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
+ # 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.
#
@@ -163,7 +165,7 @@ module ActionDispatch
# Note that the +do end+ syntax for the redirect block wouldn't work, as Ruby would pass
# the block to +get+ instead of +redirect+. Use <tt>{ ... }</tt> instead.
#
- # The options version of redirect allows you to supply only the parts of the url which need
+ # The options version of redirect allows you to supply only the parts of the URL which need
# to change, it also supports interpolation of the path similar to the first example.
#
# get 'stores/:name', to: redirect(subdomain: 'stores', path: '/%{name}')
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 5b873aeab7..129e90037e 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -73,6 +73,7 @@ module ActionDispatch
@routes = {}
@path_helpers = Set.new
@url_helpers = Set.new
+ @custom_helpers = Set.new
@url_helpers_module = Module.new
@path_helpers_module = Module.new
end
@@ -88,16 +89,30 @@ module ActionDispatch
def clear!
@path_helpers.each do |helper|
- @path_helpers_module.send :undef_method, helper
+ @path_helpers_module.send :remove_method, helper
end
@url_helpers.each do |helper|
- @url_helpers_module.send :undef_method, helper
+ @url_helpers_module.send :remove_method, helper
+ end
+
+ @custom_helpers.each do |helper|
+ path_name = :"#{helper}_path"
+ url_name = :"#{helper}_url"
+
+ if @path_helpers_module.method_defined?(path_name)
+ @path_helpers_module.send :remove_method, path_name
+ end
+
+ if @url_helpers_module.method_defined?(url_name)
+ @url_helpers_module.send :remove_method, url_name
+ end
end
@routes.clear
@path_helpers.clear
@url_helpers.clear
+ @custom_helpers.clear
end
def add(name, route)
@@ -143,6 +158,23 @@ module ActionDispatch
routes.length
end
+ def add_url_helper(name, defaults, &block)
+ @custom_helpers << name
+ helper = CustomUrlHelper.new(name, defaults, &block)
+
+ @path_helpers_module.module_eval do
+ define_method(:"#{name}_path") do |*args|
+ helper.call(self, args, true)
+ end
+ end
+
+ @url_helpers_module.module_eval do
+ define_method(:"#{name}_url") do |*args|
+ helper.call(self, args, false)
+ end
+ end
+ end
+
class UrlHelper
def self.create(route, options, route_name, url_strategy)
if optimize_helper?(route)
@@ -263,7 +295,7 @@ module ActionDispatch
end
private
- # Create a url helper allowing ordered parameters to be associated
+ # Create a URL helper allowing ordered parameters to be associated
# with corresponding dynamic segments, so you can do:
#
# foo_url(bar, baz, bang)
@@ -305,7 +337,7 @@ module ActionDispatch
attr_accessor :formatter, :set, :named_routes, :default_scope, :router
attr_accessor :disable_clear_and_finalize, :resources_path_names
attr_accessor :default_url_options
- attr_reader :env_key
+ attr_reader :env_key, :polymorphic_mappings
alias :routes :set
@@ -347,6 +379,7 @@ module ActionDispatch
@set = Journey::Routes.new
@router = Journey::Router.new @set
@formatter = Journey::Formatter.new self
+ @polymorphic_mappings = {}
end
def eager_load!
@@ -408,6 +441,7 @@ module ActionDispatch
named_routes.clear
set.clear
formatter.clear
+ @polymorphic_mappings.clear
@prepend.each { |blk| eval_block(blk) }
end
@@ -452,17 +486,50 @@ module ActionDispatch
# Define url_for in the singleton level so one can do:
# Rails.application.routes.url_helpers.url_for(args)
- @_routes = routes
+ proxy_class = Class.new do
+ include UrlFor
+ include routes.named_routes.path_helpers_module
+ include routes.named_routes.url_helpers_module
+
+ attr_reader :_routes
+
+ def initialize(routes)
+ @_routes = routes
+ end
+
+ def optimize_routes_generation?
+ @_routes.optimize_routes_generation?
+ end
+ end
+
+ @_proxy = proxy_class.new(routes)
+
class << self
def url_for(options)
- @_routes.url_for(options)
+ @_proxy.url_for(options)
+ end
+
+ def full_url_for(options)
+ @_proxy.full_url_for(options)
+ end
+
+ def route_for(name, *args)
+ @_proxy.route_for(name, *args)
end
def optimize_routes_generation?
- @_routes.optimize_routes_generation?
+ @_proxy.optimize_routes_generation?
end
- attr_reader :_routes
+ def polymorphic_url(record_or_hash_or_array, options = {})
+ @_proxy.polymorphic_url(record_or_hash_or_array, options)
+ end
+
+ def polymorphic_path(record_or_hash_or_array, options = {})
+ @_proxy.polymorphic_path(record_or_hash_or_array, options)
+ end
+
+ def _routes; @_proxy._routes; end
def url_options; {}; end
end
@@ -506,7 +573,7 @@ module ActionDispatch
routes.empty?
end
- def add_route(mapping, path_ast, name, anchor)
+ def add_route(mapping, name)
raise ArgumentError, "Invalid route name: '#{name}'" unless name.blank? || name.to_s.match(/^[_a-z]\w*$/i)
if name && named_routes[name]
@@ -523,20 +590,58 @@ module ActionDispatch
if route.segment_keys.include?(:controller)
ActiveSupport::Deprecation.warn(<<-MSG.squish)
Using a dynamic :controller segment in a route is deprecated and
- will be removed in Rails 5.1.
+ will be removed in Rails 5.2.
MSG
end
if route.segment_keys.include?(:action)
ActiveSupport::Deprecation.warn(<<-MSG.squish)
Using a dynamic :action segment in a route is deprecated and
- will be removed in Rails 5.1.
+ will be removed in Rails 5.2.
MSG
end
route
end
+ def add_polymorphic_mapping(klass, options, &block)
+ @polymorphic_mappings[klass] = CustomUrlHelper.new(klass, options, &block)
+ end
+
+ def add_url_helper(name, options, &block)
+ named_routes.add_url_helper(name, options, &block)
+ end
+
+ class CustomUrlHelper
+ attr_reader :name, :defaults, :block
+
+ def initialize(name, defaults, &block)
+ @name = name
+ @defaults = defaults
+ @block = block
+ end
+
+ def call(t, args, only_path = false)
+ options = args.extract_options!
+ url = t.full_url_for(eval_block(t, args, options))
+
+ if only_path
+ "/" + url.partition(%r{(?<!/)/(?!/)}).last
+ else
+ url
+ end
+ end
+
+ private
+ def eval_block(t, args, options)
+ t.instance_exec(*args, merge_defaults(options), &block)
+ end
+
+ def merge_defaults(options)
+ defaults ? defaults.merge(options) : options
+ end
+ end
+
class Generator
PARAMETERIZE = lambda do |name, value|
if name == :controller
@@ -751,8 +856,7 @@ module ActionDispatch
params[key] = URI.parser.unescape(value)
end
end
- old_params = req.path_parameters
- req.path_parameters = old_params.merge params
+ req.path_parameters = params
app = route.app
if app.matches?(req) && app.dispatcher?
begin
diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb
index 3e564f13d8..008216cc80 100644
--- a/actionpack/lib/action_dispatch/routing/url_for.rb
+++ b/actionpack/lib/action_dispatch/routing/url_for.rb
@@ -113,10 +113,10 @@ module ActionDispatch
default_url_options
end
- # Generate a url based on the options provided, default_url_options and the
+ # Generate a URL based on the options provided, default_url_options and the
# routes defined in routes.rb. The following options are supported:
#
- # * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+.
+ # * <tt>:only_path</tt> - If true, the relative URL is returned. Defaults to +false+.
# * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
# * <tt>:host</tt> - Specifies the host the link should be targeted at.
# If <tt>:only_path</tt> is false, this option must be
@@ -164,6 +164,10 @@ module ActionDispatch
# implicitly used by +url_for+ can always be overwritten like shown on the
# last +url_for+ calls.
def url_for(options = nil)
+ full_url_for(options)
+ end
+
+ def full_url_for(options = nil) # :nodoc:
case options
when nil
_routes.url_for(url_options.symbolize_keys)
@@ -192,6 +196,10 @@ module ActionDispatch
end
end
+ def route_for(name, *args) # :nodoc:
+ public_send(:"#{name}_url", *args)
+ end
+
protected
def optimize_routes_generation?
diff --git a/actionpack/lib/action_dispatch/system_test_case.rb b/actionpack/lib/action_dispatch/system_test_case.rb
new file mode 100644
index 0000000000..9cc3d0757f
--- /dev/null
+++ b/actionpack/lib/action_dispatch/system_test_case.rb
@@ -0,0 +1,126 @@
+require "capybara/dsl"
+require "capybara/minitest"
+require "action_controller"
+require "action_dispatch/system_testing/driver"
+require "action_dispatch/system_testing/server"
+require "action_dispatch/system_testing/test_helpers/screenshot_helper"
+require "action_dispatch/system_testing/test_helpers/setup_and_teardown"
+
+module ActionDispatch
+ # = System Testing
+ #
+ # System tests let you test applications in the browser. Because system
+ # tests use a real browser experience, you can test all of your JavaScript
+ # easily from your test suite.
+ #
+ # To create a system test in your application, extend your test class
+ # from <tt>ApplicationSystemTestCase</tt>. System tests use Capybara as a
+ # base and allow you to configure the settings through your
+ # <tt>application_system_test_case.rb</tt> file that is generated with a new
+ # application or scaffold.
+ #
+ # Here is an example system test:
+ #
+ # require 'application_system_test_case'
+ #
+ # class Users::CreateTest < ApplicationSystemTestCase
+ # test "adding a new user" do
+ # visit users_path
+ # click_on 'New User'
+ #
+ # fill_in 'Name', with: 'Arya'
+ # click_on 'Create User'
+ #
+ # assert_text 'Arya'
+ # end
+ # end
+ #
+ # When generating an application or scaffold, an +application_system_test_case.rb+
+ # file will also be generated containing the base class for system testing.
+ # This is where you can change the driver, add Capybara settings, and other
+ # configuration for your system tests.
+ #
+ # require "test_helper"
+ #
+ # class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
+ # driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
+ # end
+ #
+ # By default, <tt>ActionDispatch::SystemTestCase</tt> is driven by the
+ # Selenium driver, with the Chrome browser, and a browser size of 1400x1400.
+ #
+ # Changing the driver configuration options is easy. Let's say you want to use
+ # the Firefox browser instead of Chrome. In your +application_system_test_case.rb+
+ # file add the following:
+ #
+ # require "test_helper"
+ #
+ # class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
+ # driven_by :selenium, using: :firefox
+ # end
+ #
+ # +driven_by+ has a required argument for the driver name. The keyword
+ # arguments are +:using+ for the browser and +:screen_size+ to change the
+ # size of the browser screen. These two options are not applicable for
+ # headless drivers and will be silently ignored if passed.
+ #
+ # To use a headless driver, like Poltergeist, update your Gemfile to use
+ # Poltergeist instead of Selenium and then declare the driver name in the
+ # +application_system_test_case.rb+ file. In this case you would leave out the +:using+
+ # option because the driver is headless.
+ #
+ # require "test_helper"
+ # require "capybara/poltergeist"
+ #
+ # class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
+ # driven_by :poltergeist
+ # end
+ #
+ # Because <tt>ActionDispatch::SystemTestCase</tt> is a shim between Capybara
+ # and Rails, any driver that is supported by Capybara is supported by system
+ # tests as long as you include the required gems and files.
+ class SystemTestCase < IntegrationTest
+ include Capybara::DSL
+ include Capybara::Minitest::Assertions
+ include SystemTesting::TestHelpers::SetupAndTeardown
+ include SystemTesting::TestHelpers::ScreenshotHelper
+
+ def initialize(*) # :nodoc:
+ super
+ self.class.superclass.driver.use
+ end
+
+ def self.start_application # :nodoc:
+ Capybara.app = Rack::Builder.new do
+ map "/" do
+ run Rails.application
+ end
+ end
+
+ SystemTesting::Server.new.run
+ end
+
+ # System Test configuration options
+ #
+ # The default settings are Selenium, using Chrome, with a screen size
+ # of 1400x1400.
+ #
+ # Examples:
+ #
+ # driven_by :poltergeist
+ #
+ # driven_by :selenium, using: :firefox
+ #
+ # driven_by :selenium, screen_size: [800, 800]
+ def self.driven_by(driver, using: :chrome, screen_size: [1400, 1400], options: {})
+ @driver = SystemTesting::Driver.new(driver, using: using, screen_size: screen_size, options: options)
+ end
+
+ # Returns the driver object for the initialized system test
+ def self.driver
+ @driver ||= SystemTestCase.driven_by(:selenium)
+ end
+ end
+
+ SystemTestCase.start_application
+end
diff --git a/actionpack/lib/action_dispatch/system_testing/driver.rb b/actionpack/lib/action_dispatch/system_testing/driver.rb
new file mode 100644
index 0000000000..5cf17883f7
--- /dev/null
+++ b/actionpack/lib/action_dispatch/system_testing/driver.rb
@@ -0,0 +1,34 @@
+module ActionDispatch
+ module SystemTesting
+ class Driver # :nodoc:
+ def initialize(name, **options)
+ @name = name
+ @browser = options[:using]
+ @screen_size = options[:screen_size]
+ @options = options[:options]
+ end
+
+ def use
+ register if selenium?
+ setup
+ end
+
+ private
+ def selenium?
+ @name == :selenium
+ end
+
+ def register
+ Capybara.register_driver @name do |app|
+ Capybara::Selenium::Driver.new(app, { browser: @browser }.merge(@options)).tap do |driver|
+ driver.browser.manage.window.size = Selenium::WebDriver::Dimension.new(*@screen_size)
+ end
+ end
+ end
+
+ def setup
+ Capybara.current_driver = @name
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/system_testing/server.rb b/actionpack/lib/action_dispatch/system_testing/server.rb
new file mode 100644
index 0000000000..4a214ef713
--- /dev/null
+++ b/actionpack/lib/action_dispatch/system_testing/server.rb
@@ -0,0 +1,32 @@
+require "rack/handler/puma"
+
+module ActionDispatch
+ module SystemTesting
+ class Server # :nodoc:
+ def run
+ register
+ setup
+ end
+
+ private
+ def register
+ Capybara.register_server :rails_puma do |app, port, host|
+ Rack::Handler::Puma.run(app, Port: port, Threads: "0:1")
+ end
+ end
+
+ def setup
+ set_server
+ set_port
+ end
+
+ def set_server
+ Capybara.server = :rails_puma
+ end
+
+ def set_port
+ Capybara.always_include_port = true
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb b/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb
new file mode 100644
index 0000000000..859d68e475
--- /dev/null
+++ b/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb
@@ -0,0 +1,94 @@
+module ActionDispatch
+ module SystemTesting
+ module TestHelpers
+ # Screenshot helper for system testing.
+ module ScreenshotHelper
+ # Takes a screenshot of the current page in the browser.
+ #
+ # +take_screenshot+ can be used at any point in your system tests to take
+ # a screenshot of the current state. This can be useful for debugging or
+ # automating visual testing.
+ #
+ # The screenshot will be displayed in your console, if supported.
+ #
+ # You can set the +RAILS_SYSTEM_TESTING_SCREENSHOT+ environment variable to
+ # control the output. Possible values are:
+ # * [+inline+ (default)] display the screenshot in the terminal using the
+ # iTerm image protocol (http://iterm2.com/documentation-images.html).
+ # * [+simple+] only display the screenshot path.
+ # This is the default value if the +CI+ environment variables
+ # is defined.
+ # * [+artifact+] display the screenshot in the terminal, using the terminal
+ # artifact format (http://buildkite.github.io/terminal/inline-images/).
+ def take_screenshot
+ save_image
+ puts display_image
+ end
+
+ # Takes a screenshot of the current page in the browser if the test
+ # failed.
+ #
+ # +take_failed_screenshot+ is included in <tt>application_system_test_case.rb</tt>
+ # that is generated with the application. To take screenshots when a test
+ # fails add +take_failed_screenshot+ to the teardown block before clearing
+ # sessions.
+ def take_failed_screenshot
+ take_screenshot if failed? && supports_screenshot?
+ end
+
+ private
+ def image_name
+ failed? ? "failures_#{method_name}" : method_name
+ end
+
+ def image_path
+ "tmp/screenshots/#{image_name}.png"
+ end
+
+ def save_image
+ page.save_screenshot(Rails.root.join(image_path))
+ end
+
+ def output_type
+ # Environment variables have priority
+ output_type = ENV["RAILS_SYSTEM_TESTING_SCREENSHOT"] || ENV["CAPYBARA_INLINE_SCREENSHOT"]
+
+ # If running in a CI environment, default to simple
+ output_type ||= "simple" if ENV["CI"]
+
+ # Default
+ output_type ||= "inline"
+
+ output_type
+ end
+
+ def display_image
+ message = "[Screenshot]: #{image_path}\n"
+
+ case output_type
+ when "artifact"
+ message << "\e]1338;url=artifact://#{image_path}\a\n"
+ when "inline"
+ name = inline_base64(File.basename(image_path))
+ image = inline_base64(File.read(image_path))
+ message << "\e]1337;File=name=#{name};height=400px;inline=1:#{image}\a\n"
+ end
+
+ message
+ end
+
+ def inline_base64(path)
+ Base64.encode64(path).gsub("\n", "")
+ end
+
+ def failed?
+ !passed? && !skipped?
+ end
+
+ def supports_screenshot?
+ Capybara.current_driver != :rack_test
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb b/actionpack/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb
new file mode 100644
index 0000000000..187ba2cc5f
--- /dev/null
+++ b/actionpack/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb
@@ -0,0 +1,20 @@
+module ActionDispatch
+ module SystemTesting
+ module TestHelpers
+ module SetupAndTeardown # :nodoc:
+ DEFAULT_HOST = "127.0.0.1"
+
+ def before_setup
+ host! DEFAULT_HOST
+ super
+ end
+
+ def after_teardown
+ take_failed_screenshot
+ Capybara.reset_sessions!
+ super
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb
index 817737341c..1baf979ac9 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/response.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb
@@ -45,7 +45,7 @@ module ActionDispatch
# # Asserts that the redirection was to the named route login_url
# assert_redirected_to login_url
#
- # # Asserts that the redirection was to the url for @customer
+ # # Asserts that the redirection was to the URL for @customer
# assert_redirected_to @customer
#
# # Asserts that the redirection matches the regular expression
diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
index 37c1ca02b6..8645df4370 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
@@ -18,8 +18,8 @@ module ActionDispatch
# assert_recognizes({controller: 'items', action: 'create'}, {path: 'items', method: :post})
#
# You can also pass in +extras+ with a hash containing URL parameters that would normally be in the query string. This can be used
- # to assert that values in the query string will end up in the params hash correctly. To test query strings you must use the
- # extras argument, appending the query string on the path directly will not work. For example:
+ # to assert that values in the query string will end up in the params hash correctly. To test query strings you must use the extras
+ # argument because appending the query string on the path directly will not work. For example:
#
# # Asserts that a path of '/items/list/1?view=print' returns the correct options
# assert_recognizes({controller: 'items', action: 'list', id: '1', view: 'print'}, 'items/list/1', { view: "print" })
@@ -132,8 +132,7 @@ module ActionDispatch
end
# A helper to make it easier to test different route configurations.
- # This method temporarily replaces @routes
- # with a new RouteSet instance.
+ # This method temporarily replaces @routes with a new RouteSet instance.
#
# The new instance is yielded to the passed block. Typically the block
# will create some routes using <tt>set.draw { match ... }</tt>:
@@ -186,7 +185,6 @@ module ActionDispatch
method = :get
end
- # Assume given controller
request = ActionController::TestRequest.create @controller.class
if path =~ %r{://}
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 5fa0b727ab..2e2db98ad6 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -192,11 +192,10 @@ module ActionDispatch
# HTTP methods in integration tests. +#process+ is only required when using a
# request method that doesn't have a method defined in the integration tests.
#
- # This method returns a Response object, which one can use to
- # inspect the details of the response. Furthermore, if this method was
- # called from an ActionDispatch::IntegrationTest object, then that
- # object's <tt>@response</tt> instance variable will point to the same
- # response object.
+ # This method returns the response status, after performing the request.
+ # Furthermore, if this method was called from an ActionDispatch::IntegrationTest object,
+ # then that object's <tt>@response</tt> instance variable will point to a Response object
+ # which one can use to inspect the details of the response.
#
# Example:
# process :get, '/author', params: { since: 201501011400 }
@@ -247,7 +246,7 @@ module ActionDispatch
wrapped_headers["HTTP_ACCEPT"] ||= [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(", ")
end
- # this modifies the passed request_env directly
+ # This modifies the passed request_env directly.
if wrapped_headers.present?
Http::Headers.from_hash(request_env).merge!(wrapped_headers)
end
@@ -258,7 +257,7 @@ module ActionDispatch
session = Rack::Test::Session.new(_mock_session)
# NOTE: rack-test v0.5 doesn't build a default uri correctly
- # Make sure requested path is always a full uri
+ # Make sure requested path is always a full URI.
session.request(build_full_uri(path, request_env), request_env)
@request_count += 1
@@ -325,8 +324,8 @@ module ActionDispatch
def create_session(app)
klass = APP_SESSIONS[app] ||= Class.new(Integration::Session) {
- # If the app is a Rails app, make url_helpers available on the session
- # This makes app.url_for and app.foo_path available in the console
+ # If the app is a Rails app, make url_helpers available on the session.
+ # This makes app.url_for and app.foo_path available in the console.
if app.respond_to?(:routes)
include app.routes.url_helpers
include app.routes.mounted_helpers
@@ -572,7 +571,7 @@ module ActionDispatch
# end
#
# assert_response :success
- # assert_equal({ id: Arcticle.last.id, title: "Ahoy!" }, response.parsed_body)
+ # assert_equal({ id: Article.last.id, title: "Ahoy!" }, response.parsed_body)
# end
# end
#
diff --git a/actionpack/lib/action_dispatch/testing/test_request.rb b/actionpack/lib/action_dispatch/testing/test_request.rb
index 91b25ec155..ec949c869b 100644
--- a/actionpack/lib/action_dispatch/testing/test_request.rb
+++ b/actionpack/lib/action_dispatch/testing/test_request.rb
@@ -9,7 +9,7 @@ module ActionDispatch
"HTTP_USER_AGENT" => "Rails Testing",
)
- # Create a new test request with default `env` values
+ # Create a new test request with default `env` values.
def self.create(env = {})
env = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application
env["rack.request.cookie_hash"] ||= {}.with_indifferent_access
diff --git a/actionpack/lib/action_pack/gem_version.rb b/actionpack/lib/action_pack/gem_version.rb
index d8f86630b1..fddc3033d5 100644
--- a/actionpack/lib/action_pack/gem_version.rb
+++ b/actionpack/lib/action_pack/gem_version.rb
@@ -6,7 +6,7 @@ module ActionPack
module VERSION
MAJOR = 5
- MINOR = 1
+ MINOR = 2
TINY = 0
PRE = "alpha"
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index 459b0d6c54..4185ce1a1f 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -439,3 +439,11 @@ class ActiveSupport::TestCase
skip message if defined?(JRUBY_VERSION)
end
end
+
+class DrivenByRackTest < ActionDispatch::SystemTestCase
+ driven_by :rack_test
+end
+
+class DrivenBySeleniumWithChrome < ActionDispatch::SystemTestCase
+ driven_by :selenium, using: :chrome
+end
diff --git a/actionpack/test/controller/live_stream_test.rb b/actionpack/test/controller/live_stream_test.rb
index e76628b936..581081dd07 100644
--- a/actionpack/test/controller/live_stream_test.rb
+++ b/actionpack/test/controller/live_stream_test.rb
@@ -1,4 +1,5 @@
require "abstract_unit"
+require "timeout"
require "concurrent/atomic/count_down_latch"
Thread.abort_on_exception = true
diff --git a/actionpack/test/controller/parameters/accessors_test.rb b/actionpack/test/controller/parameters/accessors_test.rb
index 2893eb7b91..7725c25e22 100644
--- a/actionpack/test/controller/parameters/accessors_test.rb
+++ b/actionpack/test/controller/parameters/accessors_test.rb
@@ -53,6 +53,15 @@ class ParametersAccessorsTest < ActiveSupport::TestCase
@params.each_pair { |key, value| assert_not(value.permitted?) if key == "person" }
end
+ test "empty? returns true when params contains no key/value pairs" do
+ params = ActionController::Parameters.new
+ assert params.empty?
+ end
+
+ test "empty? returns false when any params are present" do
+ refute @params.empty?
+ end
+
test "except retains permitted status" do
@params.permit!
assert @params.except(:person).permitted?
@@ -75,6 +84,45 @@ class ParametersAccessorsTest < ActiveSupport::TestCase
assert_not @params[:person].fetch(:name).permitted?
end
+ test "has_key? returns true if the given key is present in the params" do
+ assert @params.has_key?(:person)
+ end
+
+ test "has_key? returns false if the given key is not present in the params" do
+ refute @params.has_key?(:address)
+ end
+
+ test "has_value? returns true if the given value is present in the params" do
+ params = ActionController::Parameters.new(city: "Chicago", state: "Illinois")
+ assert params.has_value?("Chicago")
+ end
+
+ test "has_value? returns false if the given value is not present in the params" do
+ params = ActionController::Parameters.new(city: "Chicago", state: "Illinois")
+ refute params.has_value?("New York")
+ end
+
+ test "include? returns true if the given key is present in the params" do
+ assert @params.include?(:person)
+ end
+
+ test "include? returns false if the given key is not present in the params" do
+ refute @params.include?(:address)
+ end
+
+ test "key? returns true if the given key is present in the params" do
+ assert @params.key?(:person)
+ end
+
+ test "key? returns false if the given key is not present in the params" do
+ refute @params.key?(:address)
+ end
+
+ test "keys returns an array of the keys of the params" do
+ assert_equal ["person"], @params.keys
+ assert_equal ["age", "name", "addresses"], @params[:person].keys
+ end
+
test "reject retains permitted status" do
assert_not @params.reject { |k| k == "person" }.permitted?
end
@@ -120,6 +168,21 @@ class ParametersAccessorsTest < ActiveSupport::TestCase
assert_not @params.transform_values { |v| v }.permitted?
end
+ test "value? returns true if the given value is present in the params" do
+ params = ActionController::Parameters.new(city: "Chicago", state: "Illinois")
+ assert params.value?("Chicago")
+ end
+
+ test "value? returns false if the given value is not present in the params" do
+ params = ActionController::Parameters.new(city: "Chicago", state: "Illinois")
+ refute params.value?("New York")
+ end
+
+ test "values returns an array of the values of the params" do
+ params = ActionController::Parameters.new(city: "Chicago", state: "Illinois")
+ assert_equal ["Chicago", "Illinois"], params.values
+ end
+
test "values_at retains permitted status" do
@params.permit!
assert @params.values_at(:person).first.permitted?
diff --git a/actionpack/test/controller/parameters/parameters_permit_test.rb b/actionpack/test/controller/parameters/parameters_permit_test.rb
index 8920914af1..9f3025587e 100644
--- a/actionpack/test/controller/parameters/parameters_permit_test.rb
+++ b/actionpack/test/controller/parameters/parameters_permit_test.rb
@@ -302,6 +302,31 @@ class ParametersPermitTest < ActiveSupport::TestCase
assert_equal "32", @params[:person][:age]
end
+ test "#reverse_merge with parameters" do
+ default_params = ActionController::Parameters.new(id: "1234", person: {}).permit!
+ merged_params = @params.reverse_merge(default_params)
+
+ assert_equal "1234", merged_params[:id]
+ refute_predicate merged_params[:person], :empty?
+ end
+
+ test "not permitted is sticky beyond reverse_merge" do
+ refute_predicate @params.reverse_merge(a: "b"), :permitted?
+ end
+
+ test "permitted is sticky beyond reverse_merge" do
+ @params.permit!
+ assert_predicate @params.reverse_merge(a: "b"), :permitted?
+ end
+
+ test "#reverse_merge! with parameters" do
+ default_params = ActionController::Parameters.new(id: "1234", person: {}).permit!
+ @params.reverse_merge!(default_params)
+
+ assert_equal "1234", @params[:id]
+ refute_predicate @params[:person], :empty?
+ end
+
test "modifying the parameters" do
@params[:person][:hometown] = "Chicago"
@params[:person][:family] = { brother: "Jonas" }
diff --git a/actionpack/test/controller/params_wrapper_test.rb b/actionpack/test/controller/params_wrapper_test.rb
index faa57c4559..1eb92abae4 100644
--- a/actionpack/test/controller/params_wrapper_test.rb
+++ b/actionpack/test/controller/params_wrapper_test.rb
@@ -35,6 +35,10 @@ class ParamsWrapperTest < ActionController::TestCase
end
class Person
+ def self.stores_attributes
+ { settings: [:color, :size] }
+ end
+
def self.attribute_names
[]
end
@@ -62,6 +66,15 @@ class ParamsWrapperTest < ActionController::TestCase
end
end
+ def test_store_accessors_wrapped
+ with_default_wrapper_options do
+ @request.env["CONTENT_TYPE"] = "application/json"
+ post :parse, params: { "username" => "sikachu", "color" => "blue", "size" => "large" }
+ assert_parameters("username" => "sikachu", "color" => "blue", "size" => "large",
+ "user" => { "username" => "sikachu", "color" => "blue", "size" => "large" })
+ end
+ end
+
def test_specify_wrapper_name
with_default_wrapper_options do
UsersController.wrap_parameters :person
diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb
index e4e968dfdb..f06a1f4d23 100644
--- a/actionpack/test/controller/redirect_test.rb
+++ b/actionpack/test/controller/redirect_test.rb
@@ -21,8 +21,8 @@ end
class RedirectController < ActionController::Base
# empty method not used anywhere to ensure methods like
# `status` and `location` aren't called on `redirect_to` calls
- def status; render plain: "called status"; end
- def location; render plain: "called location"; end
+ def status; raise "Should not be called!"; end
+ def location; raise "Should not be called!"; end
def simple_redirect
redirect_to action: "hello_world"
diff --git a/actionpack/test/controller/renderer_test.rb b/actionpack/test/controller/renderer_test.rb
index 866600b935..052c974d68 100644
--- a/actionpack/test/controller/renderer_test.rb
+++ b/actionpack/test/controller/renderer_test.rb
@@ -19,6 +19,16 @@ class RendererTest < ActiveSupport::TestCase
assert_equal controller, renderer.controller
end
+ test "creating with new defaults" do
+ renderer = ApplicationController.renderer
+
+ new_defaults = { https: true }
+ new_renderer = renderer.with_defaults(new_defaults).new
+ content = new_renderer.render(inline: "<%= request.ssl? %>")
+
+ assert_equal "true", content
+ end
+
test "rendering with a class renderer" do
renderer = ApplicationController.renderer
content = renderer.render template: "ruby_template"
@@ -103,6 +113,20 @@ class RendererTest < ActiveSupport::TestCase
assert_equal "true", content
end
+ test "return valid asset url with defaults" do
+ renderer = ApplicationController.renderer
+ content = renderer.render inline: "<%= asset_url 'asset.jpg' %>"
+
+ assert_equal "http://example.org/asset.jpg", content
+ end
+
+ test "return valid asset url when https is true" do
+ renderer = ApplicationController.renderer.new https: true
+ content = renderer.render inline: "<%= asset_url 'asset.jpg' %>"
+
+ assert_equal "https://example.org/asset.jpg", content
+ end
+
private
def render
@render ||= ApplicationController.renderer.method(:render)
diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb
index e084e45373..3a4307b64b 100644
--- a/actionpack/test/controller/test_case_test.rb
+++ b/actionpack/test/controller/test_case_test.rb
@@ -134,7 +134,7 @@ XML
end
def create
- head :created, location: "created resource"
+ head :created, location: "/resource"
end
def render_cookie
@@ -679,6 +679,11 @@ XML
assert_equal "baz", @request.filtered_parameters[:foo]
end
+ def test_path_is_kept_after_the_request
+ get :test_params, params: { id: "foo" }
+ assert_equal "/test_case_test/test/test_params/foo", @request.path
+ end
+
def test_path_params_reset_between_request
get :test_params, params: { id: "foo" }
assert_equal "foo", @request.path_parameters[:id]
@@ -728,20 +733,6 @@ XML
assert_equal "text/html", @response.body
end
- def test_request_path_info_and_format_reset
- get :test_format, format: "json"
- assert_equal "application/json", @response.body
-
- get :test_uri, format: "json"
- assert_equal "/test_case_test/test/test_uri.json", @response.body
-
- get :test_format
- assert_equal "text/html", @response.body
-
- get :test_uri
- assert_equal "/test_case_test/test/test_uri", @response.body
- end
-
def test_request_format_kwarg_overrides_params
get :test_format, format: "json", params: { format: "html" }
assert_equal "application/json", @response.body
@@ -893,12 +884,12 @@ XML
assert_response :created
# Redirect url doesn't care that it wasn't a :redirect response.
- assert_equal "created resource", @response.redirect_url
+ assert_equal "/resource", @response.redirect_url
assert_equal @response.redirect_url, redirect_to_url
# Must be a :redirect response.
assert_raise(ActiveSupport::TestCase::Assertion) do
- assert_redirected_to "created resource"
+ assert_redirected_to "/resource"
end
end
diff --git a/actionpack/test/dispatch/routing/custom_url_helpers_test.rb b/actionpack/test/dispatch/routing/custom_url_helpers_test.rb
new file mode 100644
index 0000000000..cb5ca5888b
--- /dev/null
+++ b/actionpack/test/dispatch/routing/custom_url_helpers_test.rb
@@ -0,0 +1,325 @@
+require "abstract_unit"
+
+class TestCustomUrlHelpers < ActionDispatch::IntegrationTest
+ class Linkable
+ attr_reader :id
+
+ def self.name
+ super.demodulize
+ end
+
+ def initialize(id)
+ @id = id
+ end
+
+ def linkable_type
+ self.class.name.underscore
+ end
+ end
+
+ class Category < Linkable; end
+ class Collection < Linkable; end
+ class Product < Linkable; end
+ class Manufacturer < Linkable; end
+
+ class Model
+ extend ActiveModel::Naming
+ include ActiveModel::Conversion
+
+ attr_reader :id
+
+ def initialize(id = nil)
+ @id = id
+ end
+
+ remove_method :model_name
+ def model_name
+ @_model_name ||= ActiveModel::Name.new(self.class, nil, self.class.name.demodulize)
+ end
+
+ def persisted?
+ false
+ end
+ end
+
+ class Basket < Model; end
+ class User < Model; end
+ class Video < Model; end
+
+ class Article
+ attr_reader :id
+
+ def self.name
+ "Article"
+ end
+
+ def initialize(id)
+ @id = id
+ end
+ end
+
+ class Page
+ attr_reader :id
+
+ def self.name
+ super.demodulize
+ end
+
+ def initialize(id)
+ @id = id
+ end
+ end
+
+ class CategoryPage < Page; end
+ class ProductPage < Page; end
+
+ Routes = ActionDispatch::Routing::RouteSet.new
+ Routes.draw do
+ default_url_options host: "www.example.com"
+
+ root to: "pages#index"
+ get "/basket", to: "basket#show", as: :basket
+ get "/posts/:id", to: "posts#show", as: :post
+ get "/profile", to: "users#profile", as: :profile
+ get "/media/:id", to: "media#show", as: :media
+ get "/pages/:id", to: "pages#show", as: :page
+
+ resources :categories, :collections, :products, :manufacturers
+
+ namespace :admin do
+ get "/dashboard", to: "dashboard#index"
+ end
+
+ direct(:website) { "http://www.rubyonrails.org" }
+ direct("string") { "http://www.rubyonrails.org" }
+ direct(:helper) { basket_url }
+ direct(:linkable) { |linkable| [:"#{linkable.linkable_type}", { id: linkable.id }] }
+ direct(:nested) { |linkable| route_for(:linkable, linkable) }
+ direct(:params) { |params| params }
+ direct(:symbol) { :basket }
+ direct(:hash) { { controller: "basket", action: "show" } }
+ direct(:array) { [:admin, :dashboard] }
+ direct(:options) { |options| [:products, options] }
+ direct(:defaults, size: 10) { |options| [:products, options] }
+
+ direct(:browse, page: 1, size: 10) do |options|
+ [:products, options.merge(params.permit(:page, :size).to_h.symbolize_keys)]
+ end
+
+ resolve("Article") { |article| [:post, { id: article.id }] }
+ resolve("Basket") { |basket| [:basket] }
+ resolve("Manufacturer") { |manufacturer| route_for(:linkable, manufacturer) }
+ resolve("User", anchor: "details") { |user, options| [:profile, options] }
+ resolve("Video") { |video| [:media, { id: video.id }] }
+ resolve(%w[Page CategoryPage ProductPage]) { |page| [:page, { id: page.id }] }
+ end
+
+ APP = build_app Routes
+
+ def app
+ APP
+ end
+
+ include Routes.url_helpers
+
+ def setup
+ @category = Category.new("1")
+ @collection = Collection.new("2")
+ @product = Product.new("3")
+ @manufacturer = Manufacturer.new("apple")
+ @basket = Basket.new
+ @user = User.new
+ @video = Video.new("4")
+ @article = Article.new("5")
+ @page = Page.new("6")
+ @category_page = CategoryPage.new("7")
+ @product_page = ProductPage.new("8")
+ @path_params = { "controller" => "pages", "action" => "index" }
+ @unsafe_params = ActionController::Parameters.new(@path_params)
+ @safe_params = ActionController::Parameters.new(@path_params).permit(:controller, :action)
+ end
+
+ def params
+ ActionController::Parameters.new(page: 2, size: 25)
+ end
+
+ def test_direct_paths
+ assert_equal "/", website_path
+ assert_equal "/", Routes.url_helpers.website_path
+
+ assert_equal "/", string_path
+ assert_equal "/", Routes.url_helpers.string_path
+
+ assert_equal "/basket", helper_path
+ assert_equal "/basket", Routes.url_helpers.helper_path
+
+ assert_equal "/categories/1", linkable_path(@category)
+ assert_equal "/categories/1", Routes.url_helpers.linkable_path(@category)
+ assert_equal "/collections/2", linkable_path(@collection)
+ assert_equal "/collections/2", Routes.url_helpers.linkable_path(@collection)
+ assert_equal "/products/3", linkable_path(@product)
+ assert_equal "/products/3", Routes.url_helpers.linkable_path(@product)
+
+ assert_equal "/categories/1", nested_path(@category)
+ assert_equal "/categories/1", Routes.url_helpers.nested_path(@category)
+
+ assert_equal "/", params_path(@safe_params)
+ assert_equal "/", Routes.url_helpers.params_path(@safe_params)
+ assert_raises(ArgumentError) { params_path(@unsafe_params) }
+ assert_raises(ArgumentError) { Routes.url_helpers.params_path(@unsafe_params) }
+
+ assert_equal "/basket", symbol_path
+ assert_equal "/basket", Routes.url_helpers.symbol_path
+ assert_equal "/basket", hash_path
+ assert_equal "/basket", Routes.url_helpers.hash_path
+ assert_equal "/admin/dashboard", array_path
+ assert_equal "/admin/dashboard", Routes.url_helpers.array_path
+
+ assert_equal "/products?page=2", options_path(page: 2)
+ assert_equal "/products?page=2", Routes.url_helpers.options_path(page: 2)
+ assert_equal "/products?size=10", defaults_path
+ assert_equal "/products?size=10", Routes.url_helpers.defaults_path
+ assert_equal "/products?size=20", defaults_path(size: 20)
+ assert_equal "/products?size=20", Routes.url_helpers.defaults_path(size: 20)
+
+ assert_equal "/products?page=2&size=25", browse_path
+ assert_raises(NameError) { Routes.url_helpers.browse_path }
+ end
+
+ def test_direct_urls
+ assert_equal "http://www.rubyonrails.org", website_url
+ assert_equal "http://www.rubyonrails.org", Routes.url_helpers.website_url
+
+ assert_equal "http://www.rubyonrails.org", string_url
+ assert_equal "http://www.rubyonrails.org", Routes.url_helpers.string_url
+
+ assert_equal "http://www.example.com/basket", helper_url
+ assert_equal "http://www.example.com/basket", Routes.url_helpers.helper_url
+
+ assert_equal "http://www.example.com/categories/1", linkable_url(@category)
+ assert_equal "http://www.example.com/categories/1", Routes.url_helpers.linkable_url(@category)
+ assert_equal "http://www.example.com/collections/2", linkable_url(@collection)
+ assert_equal "http://www.example.com/collections/2", Routes.url_helpers.linkable_url(@collection)
+ assert_equal "http://www.example.com/products/3", linkable_url(@product)
+ assert_equal "http://www.example.com/products/3", Routes.url_helpers.linkable_url(@product)
+
+ assert_equal "http://www.example.com/categories/1", nested_url(@category)
+ assert_equal "http://www.example.com/categories/1", Routes.url_helpers.nested_url(@category)
+
+ assert_equal "http://www.example.com/", params_url(@safe_params)
+ assert_equal "http://www.example.com/", Routes.url_helpers.params_url(@safe_params)
+ assert_raises(ArgumentError) { params_url(@unsafe_params) }
+ assert_raises(ArgumentError) { Routes.url_helpers.params_url(@unsafe_params) }
+
+ assert_equal "http://www.example.com/basket", symbol_url
+ assert_equal "http://www.example.com/basket", Routes.url_helpers.symbol_url
+ assert_equal "http://www.example.com/basket", hash_url
+ assert_equal "http://www.example.com/basket", Routes.url_helpers.hash_url
+ assert_equal "http://www.example.com/admin/dashboard", array_url
+ assert_equal "http://www.example.com/admin/dashboard", Routes.url_helpers.array_url
+
+ assert_equal "http://www.example.com/products?page=2", options_url(page: 2)
+ assert_equal "http://www.example.com/products?page=2", Routes.url_helpers.options_url(page: 2)
+ assert_equal "http://www.example.com/products?size=10", defaults_url
+ assert_equal "http://www.example.com/products?size=10", Routes.url_helpers.defaults_url
+ assert_equal "http://www.example.com/products?size=20", defaults_url(size: 20)
+ assert_equal "http://www.example.com/products?size=20", Routes.url_helpers.defaults_url(size: 20)
+
+ assert_equal "http://www.example.com/products?page=2&size=25", browse_url
+ assert_raises(NameError) { Routes.url_helpers.browse_url }
+ end
+
+ def test_resolve_paths
+ assert_equal "/basket", polymorphic_path(@basket)
+ assert_equal "/basket", Routes.url_helpers.polymorphic_path(@basket)
+
+ assert_equal "/profile#details", polymorphic_path(@user)
+ assert_equal "/profile#details", Routes.url_helpers.polymorphic_path(@user)
+
+ assert_equal "/profile#password", polymorphic_path(@user, anchor: "password")
+ assert_equal "/profile#password", Routes.url_helpers.polymorphic_path(@user, anchor: "password")
+
+ assert_equal "/media/4", polymorphic_path(@video)
+ assert_equal "/media/4", Routes.url_helpers.polymorphic_path(@video)
+ assert_equal "/media/4", ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.path.handle_model_call(self, @video)
+
+ assert_equal "/posts/5", polymorphic_path(@article)
+ assert_equal "/posts/5", Routes.url_helpers.polymorphic_path(@article)
+ assert_equal "/posts/5", ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.path.handle_model_call(self, @article)
+
+ assert_equal "/pages/6", polymorphic_path(@page)
+ assert_equal "/pages/6", Routes.url_helpers.polymorphic_path(@page)
+ assert_equal "/pages/6", ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.path.handle_model_call(self, @page)
+
+ assert_equal "/pages/7", polymorphic_path(@category_page)
+ assert_equal "/pages/7", Routes.url_helpers.polymorphic_path(@category_page)
+ assert_equal "/pages/7", ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.path.handle_model_call(self, @category_page)
+
+ assert_equal "/pages/8", polymorphic_path(@product_page)
+ assert_equal "/pages/8", Routes.url_helpers.polymorphic_path(@product_page)
+ assert_equal "/pages/8", ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.path.handle_model_call(self, @product_page)
+
+ assert_equal "/manufacturers/apple", polymorphic_path(@manufacturer)
+ assert_equal "/manufacturers/apple", Routes.url_helpers.polymorphic_path(@manufacturer)
+ end
+
+ def test_resolve_urls
+ assert_equal "http://www.example.com/basket", polymorphic_url(@basket)
+ assert_equal "http://www.example.com/basket", Routes.url_helpers.polymorphic_url(@basket)
+ assert_equal "http://www.example.com/basket", polymorphic_url(@basket)
+ assert_equal "http://www.example.com/basket", Routes.url_helpers.polymorphic_url(@basket)
+
+ assert_equal "http://www.example.com/profile#details", polymorphic_url(@user)
+ assert_equal "http://www.example.com/profile#details", Routes.url_helpers.polymorphic_url(@user)
+
+ assert_equal "http://www.example.com/profile#password", polymorphic_url(@user, anchor: "password")
+ assert_equal "http://www.example.com/profile#password", Routes.url_helpers.polymorphic_url(@user, anchor: "password")
+
+ assert_equal "http://www.example.com/media/4", polymorphic_url(@video)
+ assert_equal "http://www.example.com/media/4", Routes.url_helpers.polymorphic_url(@video)
+ assert_equal "http://www.example.com/media/4", ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.url.handle_model_call(self, @video)
+
+ assert_equal "http://www.example.com/posts/5", polymorphic_url(@article)
+ assert_equal "http://www.example.com/posts/5", Routes.url_helpers.polymorphic_url(@article)
+ assert_equal "http://www.example.com/posts/5", ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.url.handle_model_call(self, @article)
+
+ assert_equal "http://www.example.com/pages/6", polymorphic_url(@page)
+ assert_equal "http://www.example.com/pages/6", Routes.url_helpers.polymorphic_url(@page)
+ assert_equal "http://www.example.com/pages/6", ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.url.handle_model_call(self, @page)
+
+ assert_equal "http://www.example.com/pages/7", polymorphic_url(@category_page)
+ assert_equal "http://www.example.com/pages/7", Routes.url_helpers.polymorphic_url(@category_page)
+ assert_equal "http://www.example.com/pages/7", ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.url.handle_model_call(self, @category_page)
+
+ assert_equal "http://www.example.com/pages/8", polymorphic_url(@product_page)
+ assert_equal "http://www.example.com/pages/8", Routes.url_helpers.polymorphic_url(@product_page)
+ assert_equal "http://www.example.com/pages/8", ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.url.handle_model_call(self, @product_page)
+
+ assert_equal "http://www.example.com/manufacturers/apple", polymorphic_url(@manufacturer)
+ assert_equal "http://www.example.com/manufacturers/apple", Routes.url_helpers.polymorphic_url(@manufacturer)
+ end
+
+ def test_defining_direct_inside_a_scope_raises_runtime_error
+ routes = ActionDispatch::Routing::RouteSet.new
+
+ assert_raises RuntimeError do
+ routes.draw do
+ namespace :admin do
+ direct(:rubyonrails) { "http://www.rubyonrails.org" }
+ end
+ end
+ end
+ end
+
+ def test_defining_resolve_inside_a_scope_raises_runtime_error
+ routes = ActionDispatch::Routing::RouteSet.new
+
+ assert_raises RuntimeError do
+ routes.draw do
+ namespace :admin do
+ resolve("User") { "/profile" }
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 53758a4fbc..64818e6ca1 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -4913,3 +4913,113 @@ class TestInternalRoutingParams < ActionDispatch::IntegrationTest
)
end
end
+
+class FlashRedirectTest < ActionDispatch::IntegrationTest
+ SessionKey = "_myapp_session"
+ Generator = ActiveSupport::LegacyKeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33")
+
+ class KeyGeneratorMiddleware
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ env["action_dispatch.key_generator"] ||= Generator
+ @app.call(env)
+ end
+ end
+
+ class FooController < ActionController::Base
+ def bar
+ render plain: (flash[:foo] || "foo")
+ end
+ end
+
+ Routes = ActionDispatch::Routing::RouteSet.new
+ Routes.draw do
+ get "/foo", to: redirect { |params, req| req.flash[:foo] = "bar"; "/bar" }
+ get "/bar", to: "flash_redirect_test/foo#bar"
+ end
+
+ APP = build_app Routes do |middleware|
+ middleware.use KeyGeneratorMiddleware
+ middleware.use ActionDispatch::Session::CookieStore, key: SessionKey
+ middleware.use ActionDispatch::Flash
+ middleware.delete ActionDispatch::ShowExceptions
+ end
+
+ def app
+ APP
+ end
+
+ include Routes.url_helpers
+
+ def test_block_redirect_commits_flash
+ get "/foo", env: { "action_dispatch.key_generator" => Generator }
+ assert_response :redirect
+
+ follow_redirect!
+ assert_equal "bar", response.body
+ end
+end
+
+class TestRecognizePath < ActionDispatch::IntegrationTest
+ class PageConstraint
+ attr_reader :key, :pattern
+
+ def initialize(key, pattern)
+ @key = key
+ @pattern = pattern
+ end
+
+ def matches?(request)
+ request.path_parameters[key] =~ pattern
+ end
+ end
+
+ stub_controllers do |routes|
+ Routes = routes
+ routes.draw do
+ get "/hash/:foo", to: "pages#show", constraints: { foo: /foo/ }
+ get "/hash/:bar", to: "pages#show", constraints: { bar: /bar/ }
+
+ get "/proc/:foo", to: "pages#show", constraints: proc { |r| r.path_parameters[:foo] =~ /foo/ }
+ get "/proc/:bar", to: "pages#show", constraints: proc { |r| r.path_parameters[:bar] =~ /bar/ }
+
+ get "/class/:foo", to: "pages#show", constraints: PageConstraint.new(:foo, /foo/)
+ get "/class/:bar", to: "pages#show", constraints: PageConstraint.new(:bar, /bar/)
+ end
+ end
+
+ APP = build_app Routes
+ def app
+ APP
+ end
+
+ def test_hash_constraints_dont_leak_between_routes
+ expected_params = { controller: "pages", action: "show", bar: "bar" }
+ actual_params = recognize_path("/hash/bar")
+
+ assert_equal expected_params, actual_params
+ end
+
+ def test_proc_constraints_dont_leak_between_routes
+ expected_params = { controller: "pages", action: "show", bar: "bar" }
+ actual_params = recognize_path("/proc/bar")
+
+ assert_equal expected_params, actual_params
+ end
+
+ def test_class_constraints_dont_leak_between_routes
+ expected_params = { controller: "pages", action: "show", bar: "bar" }
+ actual_params = recognize_path("/class/bar")
+
+ assert_equal expected_params, actual_params
+ end
+
+ private
+
+ def recognize_path(*args)
+ Routes.recognize_path(*args)
+ end
+end
diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb
index bd8318f5f6..3082d1072b 100644
--- a/actionpack/test/dispatch/static_test.rb
+++ b/actionpack/test/dispatch/static_test.rb
@@ -224,7 +224,7 @@ module StaticTests
def assert_gzip(file_name, response)
expected = File.read("#{FIXTURE_LOAD_PATH}/#{public_path}" + file_name)
- actual = Zlib::GzipReader.new(StringIO.new(response.body)).read
+ actual = ActiveSupport::Gzip.decompress(response.body)
assert_equal expected, actual
end
diff --git a/actionpack/test/dispatch/system_testing/driver_test.rb b/actionpack/test/dispatch/system_testing/driver_test.rb
new file mode 100644
index 0000000000..814e1d707b
--- /dev/null
+++ b/actionpack/test/dispatch/system_testing/driver_test.rb
@@ -0,0 +1,21 @@
+require "abstract_unit"
+require "action_dispatch/system_testing/driver"
+
+class DriverTest < ActiveSupport::TestCase
+ test "initializing the driver" do
+ driver = ActionDispatch::SystemTesting::Driver.new(:selenium)
+ assert_equal :selenium, driver.instance_variable_get(:@name)
+ end
+
+ test "initializing the driver with a browser" do
+ driver = ActionDispatch::SystemTesting::Driver.new(:selenium, using: :chrome, screen_size: [1400, 1400], options: { url: "http://example.com/wd/hub" })
+ assert_equal :selenium, driver.instance_variable_get(:@name)
+ assert_equal :chrome, driver.instance_variable_get(:@browser)
+ assert_equal [1400, 1400], driver.instance_variable_get(:@screen_size)
+ assert_equal ({ url: "http://example.com/wd/hub" }), driver.instance_variable_get(:@options)
+ end
+
+ test "selenium? returns false if driver is poltergeist" do
+ assert_not ActionDispatch::SystemTesting::Driver.new(:poltergeist).send(:selenium?)
+ end
+end
diff --git a/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb b/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb
new file mode 100644
index 0000000000..a83818fd80
--- /dev/null
+++ b/actionpack/test/dispatch/system_testing/screenshot_helper_test.rb
@@ -0,0 +1,41 @@
+require "abstract_unit"
+require "action_dispatch/system_testing/test_helpers/screenshot_helper"
+require "capybara/dsl"
+
+class ScreenshotHelperTest < ActiveSupport::TestCase
+ test "image path is saved in tmp directory" do
+ new_test = DrivenBySeleniumWithChrome.new("x")
+
+ assert_equal "tmp/screenshots/x.png", new_test.send(:image_path)
+ end
+
+ test "image path includes failures text if test did not pass" do
+ new_test = DrivenBySeleniumWithChrome.new("x")
+
+ new_test.stub :passed?, false do
+ assert_equal "tmp/screenshots/failures_x.png", new_test.send(:image_path)
+ end
+ end
+
+ test "image path does not include failures text if test skipped" do
+ new_test = DrivenBySeleniumWithChrome.new("x")
+
+ new_test.stub :passed?, false do
+ new_test.stub :skipped?, true do
+ assert_equal "tmp/screenshots/x.png", new_test.send(:image_path)
+ end
+ end
+ end
+end
+
+class RackTestScreenshotsTest < DrivenByRackTest
+ test "rack_test driver does not support screenshot" do
+ assert_not self.send(:supports_screenshot?)
+ end
+end
+
+class SeleniumScreenshotsTest < DrivenBySeleniumWithChrome
+ test "selenium driver supports screenshot" do
+ assert self.send(:supports_screenshot?)
+ end
+end
diff --git a/actionpack/test/dispatch/system_testing/server_test.rb b/actionpack/test/dispatch/system_testing/server_test.rb
new file mode 100644
index 0000000000..10412d6367
--- /dev/null
+++ b/actionpack/test/dispatch/system_testing/server_test.rb
@@ -0,0 +1,17 @@
+require "abstract_unit"
+require "capybara/dsl"
+require "action_dispatch/system_testing/server"
+
+class ServerTest < ActiveSupport::TestCase
+ setup do
+ ActionDispatch::SystemTesting::Server.new.run
+ end
+
+ test "initializing the server port" do
+ assert_includes Capybara.servers, :rails_puma
+ end
+
+ test "port is always included" do
+ assert Capybara.always_include_port, "expected Capybara.always_include_port to be true"
+ end
+end
diff --git a/actionpack/test/dispatch/system_testing/system_test_case_test.rb b/actionpack/test/dispatch/system_testing/system_test_case_test.rb
new file mode 100644
index 0000000000..1a9421c098
--- /dev/null
+++ b/actionpack/test/dispatch/system_testing/system_test_case_test.rb
@@ -0,0 +1,13 @@
+require "abstract_unit"
+
+class SetDriverToRackTestTest < DrivenByRackTest
+ test "uses rack_test" do
+ assert_equal :rack_test, Capybara.current_driver
+ end
+end
+
+class SetDriverToSeleniumTest < DrivenBySeleniumWithChrome
+ test "uses selenium" do
+ assert_equal :selenium, Capybara.current_driver
+ end
+end