aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack')
-rw-r--r--actionpack/CHANGELOG.md18
-rw-r--r--actionpack/actionpack.gemspec2
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb4
-rw-r--r--actionpack/lib/action_controller/base.rb2
-rw-r--r--actionpack/lib/action_controller/metal.rb59
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb2
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb10
-rw-r--r--actionpack/lib/action_controller/metal/streaming.rb2
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb2
-rw-r--r--actionpack/lib/action_controller/middleware.rb39
-rw-r--r--actionpack/lib/action_controller/test_case.rb33
-rw-r--r--actionpack/lib/action_dispatch/http/filter_redirect.rb11
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb43
-rw-r--r--actionpack/lib/action_dispatch/http/upload.rb8
-rw-r--r--actionpack/lib/action_dispatch/journey/formatter.rb2
-rw-r--r--actionpack/lib/action_dispatch/journey/nodes/node.rb14
-rw-r--r--actionpack/lib/action_dispatch/journey/parser_extras.rb4
-rw-r--r--actionpack/lib/action_dispatch/journey/path/pattern.rb65
-rw-r--r--actionpack/lib/action_dispatch/journey/route.rb77
-rw-r--r--actionpack/lib/action_dispatch/journey/router.rb5
-rw-r--r--actionpack/lib/action_dispatch/journey/router/strexp.rb27
-rw-r--r--actionpack/lib/action_dispatch/journey/routes.rb12
-rw-r--r--actionpack/lib/action_dispatch/journey/visitors.rb127
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb145
-rw-r--r--actionpack/lib/action_dispatch/middleware/debug_exceptions.rb6
-rw-r--r--actionpack/lib/action_dispatch/middleware/exception_wrapper.rb10
-rw-r--r--actionpack/lib/action_dispatch/middleware/flash.rb13
-rw-r--r--actionpack/lib/action_dispatch/middleware/params_parser.rb32
-rw-r--r--actionpack/lib/action_dispatch/middleware/remote_ip.rb22
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb10
-rw-r--r--actionpack/lib/action_dispatch/middleware/stack.rb87
-rw-r--r--actionpack/lib/action_dispatch/middleware/static.rb33
-rw-r--r--actionpack/lib/action_dispatch/request/session.rb4
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb665
-rw-r--r--actionpack/lib/action_dispatch/routing/redirection.rb6
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb117
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/routing.rb4
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb8
-rw-r--r--actionpack/lib/action_dispatch/testing/test_process.rb2
-rw-r--r--actionpack/test/abstract_unit.rb61
-rw-r--r--actionpack/test/controller/flash_test.rb2
-rw-r--r--actionpack/test/controller/http_basic_authentication_test.rb8
-rw-r--r--actionpack/test/controller/integration_test.rb19
-rw-r--r--actionpack/test/controller/new_base/metal_test.rb43
-rw-r--r--actionpack/test/controller/resources_test.rb105
-rw-r--r--actionpack/test/controller/routing_test.rb6
-rw-r--r--actionpack/test/dispatch/exception_wrapper_test.rb32
-rw-r--r--actionpack/test/dispatch/mapper_test.rb103
-rw-r--r--actionpack/test/dispatch/middleware_stack/middleware_test.rb77
-rw-r--r--actionpack/test/dispatch/middleware_stack_test.rb55
-rw-r--r--actionpack/test/dispatch/mount_test.rb2
-rw-r--r--actionpack/test/dispatch/request/multipart_params_parsing_test.rb11
-rw-r--r--actionpack/test/dispatch/request/session_test.rb2
-rw-r--r--actionpack/test/dispatch/routing_test.rb23
-rw-r--r--actionpack/test/dispatch/session/cache_store_test.rb2
-rw-r--r--actionpack/test/dispatch/session/cookie_store_test.rb2
-rw-r--r--actionpack/test/dispatch/session/mem_cache_store_test.rb2
-rw-r--r--actionpack/test/dispatch/session/test_session_test.rb10
-rw-r--r--actionpack/test/dispatch/static_test.rb2
-rw-r--r--actionpack/test/fixtures/multipart/utf8_filename10
-rw-r--r--actionpack/test/journey/nodes/symbol_test.rb2
-rw-r--r--actionpack/test/journey/path/pattern_test.rb72
-rw-r--r--actionpack/test/journey/route_test.rb27
-rw-r--r--actionpack/test/journey/router_test.rb308
-rw-r--r--actionpack/test/journey/routes_test.rb63
65 files changed, 1436 insertions, 1345 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 6a68c057ac..8eea4ccd41 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,21 @@
+* Using strings or symbols for middleware class names is deprecated. Convert
+ things like this:
+
+ middleware.use "Foo::Bar"
+
+ to this:
+
+ middleware.use Foo::Bar
+
+* ActionController::TestSession now accepts a default value as well as
+ a block for generating a default value based off the key provided.
+
+ This fixes calls to session#fetch in ApplicationController instances that
+ take more two arguments or a block from raising `ArgumentError: wrong
+ number of arguments (2 for 1)` when performing controller tests.
+
+ *Matthew Gerrior*
+
* Fix `ActionController::Parameters#fetch` overwriting `KeyError` returned by
default block.
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index 1bba9df969..28d8bc3091 100644
--- a/actionpack/actionpack.gemspec
+++ b/actionpack/actionpack.gemspec
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
s.add_dependency 'activesupport', version
- s.add_dependency 'rack', '~> 1.6'
+ s.add_dependency 'rack', '~> 2.x'
s.add_dependency 'rack-test', '~> 0.6.3'
s.add_dependency 'rails-html-sanitizer', '~> 1.0', '>= 1.0.2'
s.add_dependency 'rails-dom-testing', '~> 1.0', '>= 1.0.5'
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 5514213ad8..887196b3d2 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -54,11 +54,11 @@ module AbstractController
Mime::TEXT
end
- DEFAULT_PROTECTED_INSTANCE_VARIABLES = Set.new %w(
+ DEFAULT_PROTECTED_INSTANCE_VARIABLES = Set.new %i(
@_action_name @_response_body @_formats @_prefixes @_config
@_view_context_class @_view_renderer @_lookup_context
@_routes @_db_runtime
- ).map(&:to_sym)
+ )
# This method should return a hash with assigns.
# You can overwrite this configuration per controller.
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 17371a5392..55734b9774 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -252,7 +252,7 @@ module ActionController
# Define some internal variables that should not be propagated to the view.
PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [
- :@_status, :@_headers, :@_params, :@_env, :@_response, :@_request,
+ :@_status, :@_headers, :@_params, :@_response, :@_request,
:@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout ]
def _protected_ivars # :nodoc:
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index ae111e4951..914b0d4b30 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -1,5 +1,6 @@
require 'active_support/core_ext/array/extract_options'
require 'action_dispatch/middleware/stack'
+require 'active_support/deprecation'
module ActionController
# Extend ActionDispatch middleware stack to make it aware of options
@@ -11,22 +12,14 @@ module ActionController
#
class MiddlewareStack < ActionDispatch::MiddlewareStack #:nodoc:
class Middleware < ActionDispatch::MiddlewareStack::Middleware #:nodoc:
- def initialize(klass, *args, &block)
- options = args.extract_options!
- @only = Array(options.delete(:only)).map(&:to_s)
- @except = Array(options.delete(:except)).map(&:to_s)
- args << options unless options.empty?
- super
+ def initialize(klass, args, actions, strategy, block)
+ @actions = actions
+ @strategy = strategy
+ super(klass, args, block)
end
def valid?(action)
- if @only.present?
- @only.include?(action)
- elsif @except.present?
- !@except.include?(action)
- else
- true
- end
+ @strategy.call @actions, action
end
end
@@ -37,6 +30,32 @@ module ActionController
middleware.valid?(action) ? middleware.build(a) : a
end
end
+
+ private
+
+ INCLUDE = ->(list, action) { list.include? action }
+ EXCLUDE = ->(list, action) { !list.include? action }
+ NULL = ->(list, action) { true }
+
+ def build_middleware(klass, args, block)
+ options = args.extract_options!
+ only = Array(options.delete(:only)).map(&:to_s)
+ except = Array(options.delete(:except)).map(&:to_s)
+ args << options unless options.empty?
+
+ strategy = NULL
+ list = nil
+
+ if only.any?
+ strategy = INCLUDE
+ list = only
+ elsif except.any?
+ strategy = EXCLUDE
+ list = except
+ end
+
+ Middleware.new(get_class(klass), args, list, strategy, block)
+ end
end
# <tt>ActionController::Metal</tt> is the simplest possible controller, providing a
@@ -98,11 +117,10 @@ module ActionController
class Metal < AbstractController::Base
abstract!
- attr_internal_writer :env
-
def env
- @_env ||= {}
+ @_request.env
end
+ deprecate :env
# Returns the last part of the controller's name, underscored, without the ending
# <tt>Controller</tt>. For instance, PostsController returns <tt>posts</tt>.
@@ -197,8 +215,7 @@ module ActionController
def set_request!(request) #:nodoc:
@_request = request
- @_env = request.env
- @_env['action_controller.instance'] = self
+ @_request.controller_instance = self
end
def to_a #:nodoc:
@@ -232,13 +249,13 @@ module ActionController
end
# Returns a Rack endpoint for the given action name.
- def self.action(name, klass = ActionDispatch::Request)
+ def self.action(name)
if middleware_stack.any?
middleware_stack.build(name) do |env|
- new.dispatch(name, klass.new(env))
+ new.dispatch(name, ActionDispatch::Request.new(env))
end
else
- lambda { |env| new.dispatch(name, klass.new(env)) }
+ lambda { |env| new.dispatch(name, ActionDispatch::Request.new(env)) }
end
end
end
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index 032275ac64..bbb38cf8fc 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -94,7 +94,7 @@ module ActionController
end
def has_basic_credentials?(request)
- request.authorization.present? && (auth_scheme(request) == 'Basic')
+ request.authorization.present? && (auth_scheme(request).downcase == 'basic')
end
def user_name_and_password(request)
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index 9e09242872..d21a778d8d 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -139,7 +139,7 @@ module ActionController #:nodoc:
request.session = NullSessionHash.new(request.env)
request.env['action_dispatch.request.flash_hash'] = nil
request.env['rack.session.options'] = { skip: true }
- request.env['action_dispatch.cookies'] = NullCookieJar.build(request)
+ request.cookie_jar = NullCookieJar.build(request, {})
end
protected
@@ -160,14 +160,6 @@ module ActionController #:nodoc:
end
class NullCookieJar < ActionDispatch::Cookies::CookieJar #:nodoc:
- def self.build(request)
- key_generator = request.env[ActionDispatch::Cookies::GENERATOR_KEY]
- host = request.host
- secure = request.ssl?
-
- new(key_generator, host, secure, options_for_env({}))
- end
-
def write(*)
# nothing
end
diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index af31de1f3a..a6115674aa 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -199,7 +199,7 @@ module ActionController #:nodoc:
def _process_options(options) #:nodoc:
super
if options[:stream]
- if env["HTTP_VERSION"] == "HTTP/1.0"
+ if request.version == "HTTP/1.0"
options.delete(:stream)
else
headers["Cache-Control"] ||= "no-cache"
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index e78f1f0d7e..fc8e345d43 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -461,7 +461,7 @@ module ActionController
end
end
- # Performs keys transfomration and returns the altered
+ # Performs keys transformation and returns the altered
# <tt>ActionController::Parameters</tt> instance.
def transform_keys!(&block)
@parameters.transform_keys!(&block)
diff --git a/actionpack/lib/action_controller/middleware.rb b/actionpack/lib/action_controller/middleware.rb
deleted file mode 100644
index 437fec3dc6..0000000000
--- a/actionpack/lib/action_controller/middleware.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-module ActionController
- class Middleware < Metal
- class ActionMiddleware
- def initialize(controller, app)
- @controller, @app = controller, app
- end
-
- def call(env)
- request = ActionDispatch::Request.new(env)
- @controller.build(@app).dispatch(:index, request)
- end
- end
-
- class << self
- alias build new
-
- def new(app)
- ActionMiddleware.new(self, app)
- end
- end
-
- attr_internal :app
-
- def process(action)
- response = super
- self.status, self.headers, self.response_body = response if response.is_a?(Array)
- response
- end
-
- def initialize(app)
- super()
- @_app = app
- end
-
- def index
- call(env)
- end
- end
-end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index f922e134f7..e012fa617e 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -1,4 +1,5 @@
require 'rack/session/abstract/id'
+require 'active_support/core_ext/hash/conversions'
require 'active_support/core_ext/object/to_query'
require 'active_support/core_ext/module/anonymous'
require 'active_support/core_ext/hash/keys'
@@ -42,14 +43,12 @@ module ActionController
@env["action_dispatch.request.request_parameters"] = params
end
- def assign_parameters(routes, controller_path, action, parameters = {})
- parameters = parameters.symbolize_keys
- generated_path, extra_keys = routes.generate_extras(parameters.merge(:controller => controller_path, :action => action))
+ def assign_parameters(routes, controller_path, action, parameters, generated_path, query_string_keys)
non_path_parameters = {}
path_parameters = {}
parameters.each do |key, value|
- if extra_keys.include?(key) || key == :action || key == :controller
+ if query_string_keys.include?(key)
non_path_parameters[key] = value
else
if value.is_a?(Array)
@@ -171,6 +170,10 @@ module ActionController
clear
end
+ def fetch(*args, &block)
+ @data.fetch(*args, &block)
+ end
+
private
def load!
@@ -479,11 +482,13 @@ module ActionController
@request.env['REQUEST_METHOD'] = http_method
- controller_class_name = @controller.class.anonymous? ?
- "anonymous" :
- @controller.class.controller_path
+ parameters = parameters.symbolize_keys
+
+ generated_extras = @routes.generate_extras(parameters.merge(controller: controller_class_name, action: action.to_s))
+ generated_path = generated_path(generated_extras)
+ query_string_keys = query_parameter_names(generated_extras)
- @request.assign_parameters(@routes, controller_class_name, action.to_s, parameters)
+ @request.assign_parameters(@routes, controller_class_name, action.to_s, parameters, generated_path, query_string_keys)
@request.session.update(session) if session
@request.flash.update(flash || {})
@@ -526,6 +531,18 @@ module ActionController
@response
end
+ def controller_class_name
+ @controller.class.anonymous? ? "anonymous" : @controller.class.controller_path
+ end
+
+ def generated_path(generated_extras)
+ generated_extras[0]
+ end
+
+ def query_parameter_names(generated_extras)
+ generated_extras[1] + [:controller, :action]
+ end
+
def setup_controller_request_and_response
@controller = nil unless defined? @controller
diff --git a/actionpack/lib/action_dispatch/http/filter_redirect.rb b/actionpack/lib/action_dispatch/http/filter_redirect.rb
index bf79963351..94c1f2b41f 100644
--- a/actionpack/lib/action_dispatch/http/filter_redirect.rb
+++ b/actionpack/lib/action_dispatch/http/filter_redirect.rb
@@ -5,8 +5,7 @@ module ActionDispatch
FILTERED = '[FILTERED]'.freeze # :nodoc:
def filtered_location # :nodoc:
- filters = location_filter
- if !filters.empty? && location_filter_match?(filters)
+ if location_filter_match?
FILTERED
else
location
@@ -15,7 +14,7 @@ module ActionDispatch
private
- def location_filter
+ def location_filters
if request
request.env['action_dispatch.redirect_filter'] || []
else
@@ -23,12 +22,12 @@ module ActionDispatch
end
end
- def location_filter_match?(filters)
- filters.any? do |filter|
+ def location_filter_match?
+ location_filters.any? do |filter|
if String === filter
location.include?(filter)
elsif Regexp === filter
- location.match(filter)
+ location =~ filter
end
end
end
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 6985cec5f5..de28cd0998 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -34,12 +34,14 @@ module ActionDispatch
HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
- HTTP_NEGOTIATE HTTP_PRAGMA ].freeze
+ HTTP_NEGOTIATE HTTP_PRAGMA HTTP_CLIENT_IP
+ HTTP_X_FORWARDED_FOR HTTP_VERSION
+ ].freeze
ENV_METHODS.each do |env|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
def #{env.sub(/^HTTP_/n, '').downcase} # def accept_charset
- @env["#{env}"] # @env["HTTP_ACCEPT_CHARSET"]
+ @env["#{env}".freeze] # @env["HTTP_ACCEPT_CHARSET".freeze]
end # end
METHOD
end
@@ -103,13 +105,17 @@ module ActionDispatch
# the application should use), this \method returns the overridden
# value, not the original.
def request_method
- @request_method ||= check_method(env["REQUEST_METHOD"])
+ @request_method ||= check_method(super)
end
def routes # :nodoc:
env["action_dispatch.routes".freeze]
end
+ def routes=(routes) # :nodoc:
+ env["action_dispatch.routes".freeze] = routes
+ end
+
def original_script_name # :nodoc:
env['ORIGINAL_SCRIPT_NAME'.freeze]
end
@@ -118,12 +124,31 @@ module ActionDispatch
env[_routes.env_key]
end
+ def engine_script_name=(name) # :nodoc:
+ env[routes.env_key] = name.dup
+ end
+
def request_method=(request_method) #:nodoc:
if check_method(request_method)
@request_method = env["REQUEST_METHOD"] = request_method
end
end
+ def controller_instance # :nodoc:
+ env['action_controller.instance'.freeze]
+ end
+
+ def controller_instance=(controller) # :nodoc:
+ env['action_controller.instance'.freeze] = controller
+ end
+
+ 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
+ # once.
+ !(env['action_dispatch.show_exceptions'.freeze] == false)
+ end
+
# Returns a symbol form of the #request_method
def request_method_symbol
HTTP_METHOD_LOOKUP[request_method]
@@ -210,6 +235,10 @@ module ActionDispatch
@remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s
end
+ def remote_ip=(remote_ip)
+ @env["action_dispatch.remote_ip".freeze] = remote_ip
+ end
+
ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id".freeze # :nodoc:
# Returns the unique request id, which is based on either the X-Request-Id header that can
@@ -318,6 +347,14 @@ module ActionDispatch
LOCALHOST =~ remote_addr && LOCALHOST =~ remote_ip
end
+ def request_parameters=(params)
+ env["action_dispatch.request.request_parameters".freeze] = params
+ end
+
+ def logger
+ env["action_dispatch.logger".freeze]
+ end
+
private
def check_method(name)
HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS[0...-1].join(', ')}, and #{HTTP_METHODS[-1]}")
diff --git a/actionpack/lib/action_dispatch/http/upload.rb b/actionpack/lib/action_dispatch/http/upload.rb
index 540e11a4a0..a221f4c5af 100644
--- a/actionpack/lib/action_dispatch/http/upload.rb
+++ b/actionpack/lib/action_dispatch/http/upload.rb
@@ -28,7 +28,13 @@ module ActionDispatch
raise(ArgumentError, ':tempfile is required') unless @tempfile
@original_filename = hash[:filename]
- @original_filename &&= @original_filename.encode "UTF-8"
+ if @original_filename
+ begin
+ @original_filename.encode!(Encoding::UTF_8)
+ rescue EncodingError
+ @original_filename.force_encoding(Encoding::UTF_8)
+ end
+ end
@content_type = hash[:type]
@headers = hash[:head]
end
diff --git a/actionpack/lib/action_dispatch/journey/formatter.rb b/actionpack/lib/action_dispatch/journey/formatter.rb
index d8bb10ffab..c19ff0f4db 100644
--- a/actionpack/lib/action_dispatch/journey/formatter.rb
+++ b/actionpack/lib/action_dispatch/journey/formatter.rb
@@ -155,7 +155,7 @@ module ActionDispatch
def build_cache
root = { ___routes: [] }
- routes.each_with_index do |route, i|
+ routes.routes.each_with_index do |route, i|
leaf = route.required_defaults.inject(root) do |h, tuple|
h[tuple] ||= {}
end
diff --git a/actionpack/lib/action_dispatch/journey/nodes/node.rb b/actionpack/lib/action_dispatch/journey/nodes/node.rb
index cf6542b370..d069bf0205 100644
--- a/actionpack/lib/action_dispatch/journey/nodes/node.rb
+++ b/actionpack/lib/action_dispatch/journey/nodes/node.rb
@@ -14,15 +14,15 @@ module ActionDispatch
end
def each(&block)
- Visitors::Each.new(block).accept(self)
+ Visitors::Each::INSTANCE.accept(self, block)
end
def to_s
- Visitors::String.new.accept(self)
+ Visitors::String::INSTANCE.accept(self, '')
end
def to_dot
- Visitors::Dot.new.accept(self)
+ Visitors::Dot::INSTANCE.accept(self)
end
def to_sym
@@ -39,10 +39,14 @@ module ActionDispatch
def symbol?; false; end
def literal?; false; end
+ def terminal?; false; end
+ def star?; false; end
+ def cat?; false; end
end
class Terminal < Node # :nodoc:
alias :symbol :left
+ def terminal?; true; end
end
class Literal < Terminal # :nodoc:
@@ -69,11 +73,13 @@ module ActionDispatch
class Symbol < Terminal # :nodoc:
attr_accessor :regexp
alias :symbol :regexp
+ attr_reader :name
DEFAULT_EXP = /[^\.\/\?]+/
def initialize(left)
super
@regexp = DEFAULT_EXP
+ @name = left.tr '*:'.freeze, ''.freeze
end
def default_regexp?
@@ -92,6 +98,7 @@ module ActionDispatch
end
class Star < Unary # :nodoc:
+ def star?; true; end
def type; :STAR; end
def name
@@ -111,6 +118,7 @@ module ActionDispatch
end
class Cat < Binary # :nodoc:
+ def cat?; true; end
def type; :CAT; end
end
diff --git a/actionpack/lib/action_dispatch/journey/parser_extras.rb b/actionpack/lib/action_dispatch/journey/parser_extras.rb
index 14892f4321..fff0299812 100644
--- a/actionpack/lib/action_dispatch/journey/parser_extras.rb
+++ b/actionpack/lib/action_dispatch/journey/parser_extras.rb
@@ -6,6 +6,10 @@ module ActionDispatch
class Parser < Racc::Parser # :nodoc:
include Journey::Nodes
+ def self.parse(string)
+ new.parse string
+ end
+
def initialize
@scanner = Scanner.new
end
diff --git a/actionpack/lib/action_dispatch/journey/path/pattern.rb b/actionpack/lib/action_dispatch/journey/path/pattern.rb
index 64b48ca45f..e93970046c 100644
--- a/actionpack/lib/action_dispatch/journey/path/pattern.rb
+++ b/actionpack/lib/action_dispatch/journey/path/pattern.rb
@@ -1,5 +1,3 @@
-require 'action_dispatch/journey/router/strexp'
-
module ActionDispatch
module Journey # :nodoc:
module Path # :nodoc:
@@ -7,14 +5,20 @@ module ActionDispatch
attr_reader :spec, :requirements, :anchored
def self.from_string string
- new Journey::Router::Strexp.build(string, {}, ["/.?"], true)
+ build(string, {}, "/.?", true)
+ end
+
+ def self.build(path, requirements, separators, anchored)
+ parser = Journey::Parser.new
+ ast = parser.parse path
+ new ast, requirements, separators, anchored
end
- def initialize(strexp)
- @spec = strexp.ast
- @requirements = strexp.requirements
- @separators = strexp.separators.join
- @anchored = strexp.anchor
+ def initialize(ast, requirements, separators, anchored)
+ @spec = ast
+ @requirements = requirements
+ @separators = separators
+ @anchored = anchored
@names = nil
@optional_names = nil
@@ -28,12 +32,12 @@ module ActionDispatch
end
def ast
- @spec.grep(Nodes::Symbol).each do |node|
+ @spec.find_all(&:symbol?).each do |node|
re = @requirements[node.to_sym]
node.regexp = re if re
end
- @spec.grep(Nodes::Star).each do |node|
+ @spec.find_all(&:star?).each do |node|
node = node.left
node.regexp = @requirements[node.to_sym] || /(.+)/
end
@@ -55,31 +59,6 @@ module ActionDispatch
}.map(&:name).uniq
end
- class RegexpOffsets < Journey::Visitors::Visitor # :nodoc:
- attr_reader :offsets
-
- def initialize(matchers)
- @matchers = matchers
- @capture_count = [0]
- end
-
- def visit(node)
- super
- @capture_count
- end
-
- def visit_SYMBOL(node)
- node = node.to_sym
-
- if @matchers.key?(node)
- re = /#{@matchers[node]}|/
- @capture_count.push((re.match('').length - 1) + (@capture_count.last || 0))
- else
- @capture_count << (@capture_count.last || 0)
- end
- end
- end
-
class AnchoredRegexp < Journey::Visitors::Visitor # :nodoc:
def initialize(separator, matchers)
@separator = separator
@@ -189,8 +168,20 @@ module ActionDispatch
def offsets
return @offsets if @offsets
- viz = RegexpOffsets.new(@requirements)
- @offsets = viz.accept(spec)
+ @offsets = [0]
+
+ spec.find_all(&:symbol?).each do |node|
+ node = node.to_sym
+
+ if @requirements.key?(node)
+ re = /#{@requirements[node]}|/
+ @offsets.push((re.match('').length - 1) + @offsets.last)
+ else
+ @offsets << @offsets.last
+ end
+ end
+
+ @offsets
end
end
end
diff --git a/actionpack/lib/action_dispatch/journey/route.rb b/actionpack/lib/action_dispatch/journey/route.rb
index cbc985640a..f5c9abf1cc 100644
--- a/actionpack/lib/action_dispatch/journey/route.rb
+++ b/actionpack/lib/action_dispatch/journey/route.rb
@@ -1,36 +1,81 @@
module ActionDispatch
module Journey # :nodoc:
class Route # :nodoc:
- attr_reader :app, :path, :defaults, :name
+ attr_reader :app, :path, :defaults, :name, :precedence
attr_reader :constraints
alias :conditions :constraints
- attr_accessor :precedence
+ module VerbMatchers
+ VERBS = %w{ DELETE GET HEAD OPTIONS LINK PATCH POST PUT TRACE UNLINK }
+ VERBS.each do |v|
+ class_eval <<-eoc
+ class #{v}
+ def self.verb; name.split("::").last; end
+ def self.call(req); req.#{v.downcase}?; end
+ end
+ eoc
+ end
+
+ class Unknown
+ attr_reader :verb
+
+ def initialize(verb)
+ @verb = verb
+ end
+
+ def call(request); @verb === request.request_method; end
+ end
+
+ class All
+ def self.call(_); true; end
+ def self.verb; ''; end
+ end
+
+ VERB_TO_CLASS = VERBS.each_with_object({ :all => All }) do |verb, hash|
+ klass = const_get verb
+ hash[verb] = klass
+ hash[verb.downcase] = klass
+ hash[verb.downcase.to_sym] = klass
+ end
+
+ end
+
+ def self.verb_matcher(verb)
+ VerbMatchers::VERB_TO_CLASS.fetch(verb) do
+ VerbMatchers::Unknown.new verb.to_s.dasherize.upcase
+ end
+ end
+
+ def self.build(name, app, path, constraints, required_defaults, defaults)
+ request_method_match = verb_matcher(constraints.delete(:request_method))
+ new name, app, path, constraints, required_defaults, defaults, request_method_match, 0
+ end
##
# +path+ is a path constraint.
# +constraints+ is a hash of constraints to be applied to this route.
- def initialize(name, app, path, constraints, required_defaults, defaults)
+ def initialize(name, app, path, constraints, required_defaults, defaults, request_method_match, precedence)
@name = name
@app = app
@path = path
+ @request_method_match = request_method_match
@constraints = constraints
@defaults = defaults
@required_defaults = nil
- @_required_defaults = required_defaults || []
+ @_required_defaults = required_defaults
@required_parts = nil
@parts = nil
@decorated_ast = nil
- @precedence = 0
+ @precedence = precedence
@path_formatter = @path.build_formatter
end
def ast
@decorated_ast ||= begin
decorated_ast = path.ast
- decorated_ast.grep(Nodes::Terminal).each { |n| n.memo = self }
+ decorated_ast.find_all(&:terminal?).each { |n| n.memo = self }
decorated_ast
end
end
@@ -92,7 +137,8 @@ module ActionDispatch
end
def matches?(request)
- constraints.all? do |method, value|
+ match_verb(request) &&
+ constraints.all? { |method, value|
case value
when Regexp, String
value === request.send(method).to_s
@@ -105,15 +151,28 @@ module ActionDispatch
else
value === request.send(method)
end
- end
+ }
end
def ip
constraints[:ip] || //
end
+ def requires_matching_verb?
+ !@request_method_match.all? { |x| x == VerbMatchers::All }
+ end
+
def verb
- constraints[:request_method] || //
+ %r[^#{verbs.join('|')}$]
+ end
+
+ private
+ def verbs
+ @request_method_match.map(&:verb)
+ end
+
+ def match_verb(request)
+ @request_method_match.any? { |m| m.call request }
end
end
end
diff --git a/actionpack/lib/action_dispatch/journey/router.rb b/actionpack/lib/action_dispatch/journey/router.rb
index b84aad8eb6..f649588520 100644
--- a/actionpack/lib/action_dispatch/journey/router.rb
+++ b/actionpack/lib/action_dispatch/journey/router.rb
@@ -1,5 +1,4 @@
require 'action_dispatch/journey/router/utils'
-require 'action_dispatch/journey/router/strexp'
require 'action_dispatch/journey/routes'
require 'action_dispatch/journey/formatter'
@@ -102,7 +101,7 @@ module ActionDispatch
}
routes =
- if req.request_method == "HEAD"
+ if req.head?
match_head_routes(routes, req)
else
match_routes(routes, req)
@@ -121,7 +120,7 @@ module ActionDispatch
end
def match_head_routes(routes, req)
- verb_specific_routes = routes.reject { |route| route.verb == // }
+ verb_specific_routes = routes.select(&:requires_matching_verb?)
head_routes = match_routes(verb_specific_routes, req)
if head_routes.empty?
diff --git a/actionpack/lib/action_dispatch/journey/router/strexp.rb b/actionpack/lib/action_dispatch/journey/router/strexp.rb
deleted file mode 100644
index 4b7738f335..0000000000
--- a/actionpack/lib/action_dispatch/journey/router/strexp.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-module ActionDispatch
- module Journey # :nodoc:
- class Router # :nodoc:
- class Strexp # :nodoc:
- class << self
- alias :compile :new
- end
-
- attr_reader :path, :requirements, :separators, :anchor, :ast
-
- def self.build(path, requirements, separators, anchor = true)
- parser = Journey::Parser.new
- ast = parser.parse path
- new ast, path, requirements, separators, anchor
- end
-
- def initialize(ast, path, requirements, separators, anchor = true)
- @ast = ast
- @path = path
- @requirements = requirements
- @separators = separators
- @anchor = anchor
- end
- end
- end
- end
-end
diff --git a/actionpack/lib/action_dispatch/journey/routes.rb b/actionpack/lib/action_dispatch/journey/routes.rb
index 5990964b57..f7b009109e 100644
--- a/actionpack/lib/action_dispatch/journey/routes.rb
+++ b/actionpack/lib/action_dispatch/journey/routes.rb
@@ -5,11 +5,10 @@ module ActionDispatch
class Routes # :nodoc:
include Enumerable
- attr_reader :routes, :named_routes, :custom_routes, :anchored_routes
+ attr_reader :routes, :custom_routes, :anchored_routes
def initialize
@routes = []
- @named_routes = {}
@ast = nil
@anchored_routes = []
@custom_routes = []
@@ -37,7 +36,6 @@ module ActionDispatch
routes.clear
anchored_routes.clear
custom_routes.clear
- named_routes.clear
end
def partition_route(route)
@@ -62,13 +60,9 @@ module ActionDispatch
end
end
- # Add a route to the routing table.
- def add_route(app, path, conditions, required_defaults, defaults, name = nil)
- route = Route.new(name, app, path, conditions, required_defaults, defaults)
-
- route.precedence = routes.length
+ def add_route(name, mapping)
+ route = mapping.make_route name, routes.length
routes << route
- named_routes[name] = route if name && !named_routes[name]
partition_route(route)
clear_cache!
route
diff --git a/actionpack/lib/action_dispatch/journey/visitors.rb b/actionpack/lib/action_dispatch/journey/visitors.rb
index 52b4c8b489..537c9b2f5c 100644
--- a/actionpack/lib/action_dispatch/journey/visitors.rb
+++ b/actionpack/lib/action_dispatch/journey/visitors.rb
@@ -92,6 +92,45 @@ module ActionDispatch
end
end
+ class FunctionalVisitor # :nodoc:
+ DISPATCH_CACHE = {}
+
+ def accept(node, seed)
+ visit(node, seed)
+ end
+
+ def visit node, seed
+ send(DISPATCH_CACHE[node.type], node, seed)
+ end
+
+ def binary(node, seed)
+ visit(node.right, visit(node.left, seed))
+ end
+ def visit_CAT(n, seed); binary(n, seed); end
+
+ def nary(node, seed)
+ node.children.inject(seed) { |s, c| visit(c, s) }
+ end
+ def visit_OR(n, seed); nary(n, seed); end
+
+ def unary(node, seed)
+ visit(node.left, seed)
+ end
+ def visit_GROUP(n, seed); unary(n, seed); end
+ def visit_STAR(n, seed); unary(n, seed); end
+
+ def terminal(node, seed); seed; end
+ def visit_LITERAL(n, seed); terminal(n, seed); end
+ def visit_SYMBOL(n, seed); terminal(n, seed); end
+ def visit_SLASH(n, seed); terminal(n, seed); end
+ def visit_DOT(n, seed); terminal(n, seed); end
+
+ instance_methods(false).each do |pim|
+ next unless pim =~ /^visit_(.*)$/
+ DISPATCH_CACHE[$1.to_sym] = pim
+ end
+ end
+
class FormatBuilder < Visitor # :nodoc:
def accept(node); Journey::Format.new(super); end
def terminal(node); [node.left]; end
@@ -117,104 +156,110 @@ module ActionDispatch
end
# Loop through the requirements AST
- class Each < Visitor # :nodoc:
- attr_reader :block
-
- def initialize(block)
- @block = block
- end
-
- def visit(node)
+ class Each < FunctionalVisitor # :nodoc:
+ def visit(node, block)
block.call(node)
super
end
+
+ INSTANCE = new
end
- class String < Visitor # :nodoc:
+ class String < FunctionalVisitor # :nodoc:
private
- def binary(node)
- [visit(node.left), visit(node.right)].join
+ def binary(node, seed)
+ visit(node.right, visit(node.left, seed))
end
- def nary(node)
- node.children.map { |c| visit(c) }.join '|'
+ def nary(node, seed)
+ last_child = node.children.last
+ node.children.inject(seed) { |s, c|
+ string = visit(c, s)
+ string << "|".freeze unless last_child == c
+ string
+ }
end
- def terminal(node)
- node.left
+ def terminal(node, seed)
+ seed + node.left
end
- def visit_GROUP(node)
- "(#{visit(node.left)})"
+ def visit_GROUP(node, seed)
+ visit(node.left, seed << "(".freeze) << ")".freeze
end
+
+ INSTANCE = new
end
- class Dot < Visitor # :nodoc:
+ class Dot < FunctionalVisitor # :nodoc:
def initialize
@nodes = []
@edges = []
end
- def accept(node)
+ def accept(node, seed = [[], []])
super
+ nodes, edges = seed
<<-eodot
digraph parse_tree {
size="8,5"
node [shape = none];
edge [dir = none];
- #{@nodes.join "\n"}
- #{@edges.join("\n")}
+ #{nodes.join "\n"}
+ #{edges.join("\n")}
}
eodot
end
private
- def binary(node)
- node.children.each do |c|
- @edges << "#{node.object_id} -> #{c.object_id};"
- end
+ def binary(node, seed)
+ seed.last.concat node.children.map { |c|
+ "#{node.object_id} -> #{c.object_id};"
+ }
super
end
- def nary(node)
- node.children.each do |c|
- @edges << "#{node.object_id} -> #{c.object_id};"
- end
+ def nary(node, seed)
+ seed.last.concat node.children.map { |c|
+ "#{node.object_id} -> #{c.object_id};"
+ }
super
end
- def unary(node)
- @edges << "#{node.object_id} -> #{node.left.object_id};"
+ def unary(node, seed)
+ seed.last << "#{node.object_id} -> #{node.left.object_id};"
super
end
- def visit_GROUP(node)
- @nodes << "#{node.object_id} [label=\"()\"];"
+ def visit_GROUP(node, seed)
+ seed.first << "#{node.object_id} [label=\"()\"];"
super
end
- def visit_CAT(node)
- @nodes << "#{node.object_id} [label=\"○\"];"
+ def visit_CAT(node, seed)
+ seed.first << "#{node.object_id} [label=\"○\"];"
super
end
- def visit_STAR(node)
- @nodes << "#{node.object_id} [label=\"*\"];"
+ def visit_STAR(node, seed)
+ seed.first << "#{node.object_id} [label=\"*\"];"
super
end
- def visit_OR(node)
- @nodes << "#{node.object_id} [label=\"|\"];"
+ def visit_OR(node, seed)
+ seed.first << "#{node.object_id} [label=\"|\"];"
super
end
- def terminal(node)
+ def terminal(node, seed)
value = node.left
- @nodes << "#{node.object_id} [label=\"#{value}\"];"
+ seed.first << "#{node.object_id} [label=\"#{value}\"];"
+ seed
end
+ INSTANCE = new
end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index 07d97bd6bd..cf4f654ed6 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -8,8 +8,50 @@ require 'active_support/json'
module ActionDispatch
class Request < Rack::Request
def cookie_jar
- env['action_dispatch.cookies'] ||= Cookies::CookieJar.build(env, host, ssl?, cookies)
+ env['action_dispatch.cookies'.freeze] ||= Cookies::CookieJar.build(self, cookies)
end
+
+ # :stopdoc:
+ def have_cookie_jar?
+ env.key? 'action_dispatch.cookies'.freeze
+ end
+
+ def cookie_jar=(jar)
+ env['action_dispatch.cookies'.freeze] = jar
+ end
+
+ def key_generator
+ env[Cookies::GENERATOR_KEY]
+ end
+
+ def signed_cookie_salt
+ env[Cookies::SIGNED_COOKIE_SALT]
+ end
+
+ def encrypted_cookie_salt
+ env[Cookies::ENCRYPTED_COOKIE_SALT]
+ end
+
+ def encrypted_signed_cookie_salt
+ env[Cookies::ENCRYPTED_SIGNED_COOKIE_SALT]
+ end
+
+ def secret_token
+ env[Cookies::SECRET_TOKEN]
+ end
+
+ def secret_key_base
+ env[Cookies::SECRET_KEY_BASE]
+ end
+
+ def cookies_serializer
+ env[Cookies::COOKIES_SERIALIZER]
+ end
+
+ def cookies_digest
+ env[Cookies::COOKIES_DIGEST]
+ end
+ # :startdoc:
end
# \Cookies are read and written through ActionController#cookies.
@@ -118,7 +160,7 @@ module ActionDispatch
# cookies.permanent.signed[:remember_me] = current_user.id
# # => Set-Cookie: remember_me=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
def permanent
- @permanent ||= PermanentCookieJar.new(self, @key_generator, @options)
+ @permanent ||= PermanentCookieJar.new(self)
end
# Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from
@@ -138,10 +180,10 @@ module ActionDispatch
# cookies.signed[:discount] # => 45
def signed
@signed ||=
- if @options[:upgrade_legacy_signed_cookies]
- UpgradeLegacySignedCookieJar.new(self, @key_generator, @options)
+ if upgrade_legacy_signed_cookies?
+ UpgradeLegacySignedCookieJar.new(self)
else
- SignedCookieJar.new(self, @key_generator, @options)
+ SignedCookieJar.new(self)
end
end
@@ -161,10 +203,10 @@ module ActionDispatch
# cookies.encrypted[:discount] # => 45
def encrypted
@encrypted ||=
- if @options[:upgrade_legacy_signed_cookies]
- UpgradeLegacyEncryptedCookieJar.new(self, @key_generator, @options)
+ if upgrade_legacy_signed_cookies?
+ UpgradeLegacyEncryptedCookieJar.new(self)
else
- EncryptedCookieJar.new(self, @key_generator, @options)
+ EncryptedCookieJar.new(self)
end
end
@@ -172,12 +214,26 @@ module ActionDispatch
# Used by ActionDispatch::Session::CookieStore to avoid the need to introduce new cookie stores.
def signed_or_encrypted
@signed_or_encrypted ||=
- if @options[:secret_key_base].present?
+ if request.secret_key_base.present?
encrypted
else
signed
end
end
+
+ protected
+
+ def request; @parent_jar.request; end
+
+ private
+
+ def upgrade_legacy_signed_cookies?
+ request.secret_token.present? && request.secret_key_base.present?
+ end
+
+ def key_generator
+ request.key_generator
+ end
end
# Passing the ActiveSupport::MessageEncryptor::NullSerializer downstream
@@ -187,7 +243,7 @@ module ActionDispatch
module VerifyAndUpgradeLegacySignedMessage # :nodoc:
def initialize(*args)
super
- @legacy_verifier = ActiveSupport::MessageVerifier.new(@options[:secret_token], serializer: ActiveSupport::MessageEncryptor::NullSerializer)
+ @legacy_verifier = ActiveSupport::MessageVerifier.new(request.secret_token, serializer: ActiveSupport::MessageEncryptor::NullSerializer)
end
def verify_and_upgrade_legacy_signed_message(name, signed_message)
@@ -216,34 +272,18 @@ module ActionDispatch
# $& => example.local
DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
- def self.options_for_env(env) #:nodoc:
- { signed_cookie_salt: env[SIGNED_COOKIE_SALT] || '',
- encrypted_cookie_salt: env[ENCRYPTED_COOKIE_SALT] || '',
- encrypted_signed_cookie_salt: env[ENCRYPTED_SIGNED_COOKIE_SALT] || '',
- secret_token: env[SECRET_TOKEN],
- secret_key_base: env[SECRET_KEY_BASE],
- upgrade_legacy_signed_cookies: env[SECRET_TOKEN].present? && env[SECRET_KEY_BASE].present?,
- serializer: env[COOKIES_SERIALIZER],
- digest: env[COOKIES_DIGEST]
- }
- end
-
- def self.build(env, host, secure, cookies)
- key_generator = env[GENERATOR_KEY]
- options = options_for_env env
-
- new(key_generator, host, secure, options).tap do |hash|
+ def self.build(req, cookies)
+ new(req).tap do |hash|
hash.update(cookies)
end
end
- def initialize(key_generator, host = nil, secure = false, options = {})
- @key_generator = key_generator
+ attr_reader :request
+
+ def initialize(request)
@set_cookies = {}
@delete_cookies = {}
- @host = host
- @secure = secure
- @options = options
+ @request = request
@cookies = {}
@committed = false
end
@@ -292,12 +332,12 @@ module ActionDispatch
# if host is not ip and matches domain regexp
# (ip confirms to domain regexp so we explicitly check for ip)
- options[:domain] = if (@host !~ /^[\d.]+$/) && (@host =~ domain_regexp)
+ 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
- options[:domain] = options[:domain].find {|domain| @host.include? domain.sub(/^\./, '') }
+ options[:domain] = options[:domain].find {|domain| request.host.include? domain.sub(/^\./, '') }
end
end
@@ -356,27 +396,20 @@ module ActionDispatch
@delete_cookies.each { |k, v| ::Rack::Utils.delete_cookie_header!(headers, k, v) }
end
- def recycle! #:nodoc:
- @set_cookies = {}
- @delete_cookies = {}
- end
-
mattr_accessor :always_write_cookie
self.always_write_cookie = false
private
def write_cookie?(cookie)
- @secure || !cookie[:secure] || always_write_cookie
+ request.ssl? || !cookie[:secure] || always_write_cookie
end
end
class PermanentCookieJar #:nodoc:
include ChainedCookieJars
- def initialize(parent_jar, key_generator, options = {})
+ def initialize(parent_jar)
@parent_jar = parent_jar
- @key_generator = key_generator
- @options = options
end
def [](name)
@@ -410,7 +443,7 @@ module ActionDispatch
protected
def needs_migration?(value)
- @options[:serializer] == :hybrid && value.start_with?(MARSHAL_SIGNATURE)
+ request.cookies_serializer == :hybrid && value.start_with?(MARSHAL_SIGNATURE)
end
def serialize(value)
@@ -430,7 +463,7 @@ module ActionDispatch
end
def serializer
- serializer = @options[:serializer] || :marshal
+ serializer = request.cookies_serializer || :marshal
case serializer
when :marshal
Marshal
@@ -442,7 +475,7 @@ module ActionDispatch
end
def digest
- @options[:digest] || 'SHA1'
+ request.cookies_digest || 'SHA1'
end
end
@@ -450,10 +483,9 @@ module ActionDispatch
include ChainedCookieJars
include SerializedCookieJars
- def initialize(parent_jar, key_generator, options = {})
+ def initialize(parent_jar)
@parent_jar = parent_jar
- @options = options
- secret = key_generator.generate_key(@options[:signed_cookie_salt])
+ secret = key_generator.generate_key(request.signed_cookie_salt)
@verifier = ActiveSupport::MessageVerifier.new(secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer)
end
@@ -505,16 +537,16 @@ module ActionDispatch
include ChainedCookieJars
include SerializedCookieJars
- def initialize(parent_jar, key_generator, options = {})
+ def initialize(parent_jar)
+ @parent_jar = parent_jar
+
if ActiveSupport::LegacyKeyGenerator === key_generator
raise "You didn't set secrets.secret_key_base, which is required for this cookie jar. " +
"Read the upgrade documentation to learn more about this new config option."
end
- @parent_jar = parent_jar
- @options = options
- secret = key_generator.generate_key(@options[:encrypted_cookie_salt])
- sign_secret = key_generator.generate_key(@options[:encrypted_signed_cookie_salt])
+ secret = key_generator.generate_key(request.encrypted_cookie_salt || '')
+ sign_secret = key_generator.generate_key(request.encrypted_signed_cookie_salt || '')
@encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer)
end
@@ -568,9 +600,12 @@ module ActionDispatch
end
def call(env)
+ request = ActionDispatch::Request.new env
+
status, headers, body = @app.call(env)
- if cookie_jar = env['action_dispatch.cookies']
+ if request.have_cookie_jar?
+ cookie_jar = request.cookie_jar
unless cookie_jar.committed?
cookie_jar.write(headers)
if headers[HTTP_HEADER].respond_to?(:join)
diff --git a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
index 9082aac271..226a688fe2 100644
--- a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
@@ -44,6 +44,7 @@ module ActionDispatch
end
def call(env)
+ request = ActionDispatch::Request.new env
_, headers, body = response = @app.call(env)
if headers['X-Cascade'] == 'pass'
@@ -53,14 +54,15 @@ module ActionDispatch
response
rescue Exception => exception
- raise exception if env['action_dispatch.show_exceptions'] == false
+ raise exception unless request.show_exceptions?
render_exception(env, exception)
end
private
def render_exception(env, exception)
- wrapper = ExceptionWrapper.new(env, exception)
+ backtrace_cleaner = env['action_dispatch.backtrace_cleaner']
+ wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
log_error(env, wrapper)
if env['action_dispatch.show_detailed_exceptions']
diff --git a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
index 8c3d45584d..039efc3af8 100644
--- a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
+++ b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
@@ -31,10 +31,10 @@ module ActionDispatch
'ActionView::Template::Error' => 'template_error'
)
- attr_reader :env, :exception, :line_number, :file
+ attr_reader :backtrace_cleaner, :exception, :line_number, :file
- def initialize(env, exception)
- @env = env
+ def initialize(backtrace_cleaner, exception)
+ @backtrace_cleaner = backtrace_cleaner
@exception = original_exception(exception)
expand_backtrace if exception.is_a?(SyntaxError) || exception.try(:original_exception).try(:is_a?, SyntaxError)
@@ -125,10 +125,6 @@ module ActionDispatch
end
end
- def backtrace_cleaner
- @backtrace_cleaner ||= @env['action_dispatch.backtrace_cleaner']
- end
-
def source_fragment(path, line)
return unless Rails.respond_to?(:root) && Rails.root
full_path = Rails.root.join(path)
diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb
index 59639a010e..23da169b22 100644
--- a/actionpack/lib/action_dispatch/middleware/flash.rb
+++ b/actionpack/lib/action_dispatch/middleware/flash.rb
@@ -8,6 +8,14 @@ module ActionDispatch
def flash
@env[Flash::KEY] ||= Flash::FlashHash.from_session_value(session["flash"])
end
+
+ def flash=(flash)
+ @env[Flash::KEY] = flash
+ end
+
+ def flash_hash # :nodoc:
+ @env[Flash::KEY]
+ end
end
# The flash provides a way to pass temporary primitive-types (String, Array, Hash) between actions. Anything you place in the flash will be exposed
@@ -263,14 +271,15 @@ module ActionDispatch
end
def call(env)
+ req = ActionDispatch::Request.new env
@app.call(env)
ensure
session = Request::Session.find(env) || {}
- flash_hash = env[KEY]
+ flash_hash = req.flash_hash
if flash_hash && (flash_hash.present? || session.key?('flash'))
session["flash"] = flash_hash.to_session_value
- env[KEY] = flash_hash.dup
+ req.flash = flash_hash.dup
end
if (!session.respond_to?(:loaded?) || session.loaded?) && # (reset_session uses {}, which doesn't implement #loaded?)
diff --git a/actionpack/lib/action_dispatch/middleware/params_parser.rb b/actionpack/lib/action_dispatch/middleware/params_parser.rb
index 2617956c74..402ad778fa 100644
--- a/actionpack/lib/action_dispatch/middleware/params_parser.rb
+++ b/actionpack/lib/action_dispatch/middleware/params_parser.rb
@@ -1,9 +1,14 @@
-require 'active_support/core_ext/hash/conversions'
require 'action_dispatch/http/request'
-require 'active_support/core_ext/hash/indifferent_access'
module ActionDispatch
+ # ActionDispatch::ParamsParser works for all the requests having any Content-Length
+ # (like POST). It takes raw data from the request and puts it through the parser
+ # that is picked based on Content-Type header.
+ #
+ # In case of any error while parsing data ParamsParser::ParseError is raised.
class ParamsParser
+ # Raised when raw data from the request cannot be parsed by the parser
+ # defined for request's content mime type.
class ParseError < StandardError
attr_reader :original_exception
@@ -21,35 +26,38 @@ module ActionDispatch
}
}
+ # Create a new +ParamsParser+ middleware instance.
+ #
+ # The +parsers+ argument can take Hash of parsers where key is identifying
+ # content mime type, and value is a lambda that is going to process data.
def initialize(app, parsers = {})
@app, @parsers = app, DEFAULT_PARSERS.merge(parsers)
end
def call(env)
- default = env["action_dispatch.request.request_parameters"]
- env["action_dispatch.request.request_parameters"] = parse_formatted_parameters(env, @parsers, default)
+ request = Request.new(env)
+
+ request.request_parameters = parse_formatted_parameters(request, @parsers)
@app.call(env)
end
private
- def parse_formatted_parameters(env, parsers, default)
- request = Request.new(env)
-
- return default if request.content_length.zero?
+ def parse_formatted_parameters(request, parsers)
+ return if request.content_length.zero?
- strategy = parsers.fetch(request.content_mime_type) { return default }
+ strategy = parsers.fetch(request.content_mime_type) { return nil }
strategy.call(request.raw_post)
rescue => e # JSON or Ruby code block errors
- logger(env).debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}"
+ logger(request).debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}"
raise ParseError.new(e.message, e)
end
- def logger(env)
- env['action_dispatch.logger'] || ActiveSupport::Logger.new($stderr)
+ def logger(request)
+ request.logger || ActiveSupport::Logger.new($stderr)
end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
index 9f894e2ec6..aee2334da9 100644
--- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb
+++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
@@ -74,16 +74,17 @@ module ActionDispatch
# requests. For those requests that do need to know the IP, the
# GetIp#calculate_ip method will calculate the memoized client IP address.
def call(env)
- env["action_dispatch.remote_ip"] = GetIp.new(env, check_ip, proxies)
- @app.call(env)
+ req = ActionDispatch::Request.new env
+ req.remote_ip = GetIp.new(req, check_ip, proxies)
+ @app.call(req.env)
end
# The GetIp class exists as a way to defer processing of the request data
# into an actual IP address. If the ActionDispatch::Request#remote_ip method
# is called, this class will calculate the value and then memoize it.
class GetIp
- def initialize(env, check_ip, proxies)
- @env = env
+ def initialize(req, check_ip, proxies)
+ @req = req
@check_ip = check_ip
@proxies = proxies
end
@@ -108,11 +109,11 @@ module ActionDispatch
# the last address left, which was presumably set by one of those proxies.
def calculate_ip
# Set by the Rack web server, this is a single value.
- remote_addr = ips_from('REMOTE_ADDR').last
+ remote_addr = ips_from(@req.remote_addr).last
# Could be a CSV list and/or repeated headers that were concatenated.
- client_ips = ips_from('HTTP_CLIENT_IP').reverse
- forwarded_ips = ips_from('HTTP_X_FORWARDED_FOR').reverse
+ client_ips = ips_from(@req.client_ip).reverse
+ forwarded_ips = ips_from(@req.x_forwarded_for).reverse
# +Client-Ip+ and +X-Forwarded-For+ should not, generally, both be set.
# If they are both set, it means that this request passed through two
@@ -123,8 +124,8 @@ module ActionDispatch
if should_check_ip && !forwarded_ips.include?(client_ips.last)
# We don't know which came from the proxy, and which from the user
raise IpSpoofAttackError, "IP spoofing attack?! " +
- "HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect} " +
- "HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}"
+ "HTTP_CLIENT_IP=#{@req.client_ip.inspect} " +
+ "HTTP_X_FORWARDED_FOR=#{@req.x_forwarded_for.inspect}"
end
# We assume these things about the IP headers:
@@ -147,8 +148,9 @@ module ActionDispatch
protected
def ips_from(header)
+ return [] unless header
# Split the comma-separated list into an array of strings
- ips = @env[header] ? @env[header].strip.split(/[,\s]+/) : []
+ ips = header.strip.split(/[,\s]+/)
ips.select do |ip|
begin
# Only return IPs that are valid according to the IPAddr#new method
diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
index f0779279c1..12d8dab8eb 100644
--- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
@@ -27,19 +27,21 @@ module ActionDispatch
end
def call(env)
+ request = ActionDispatch::Request.new env
@app.call(env)
rescue Exception => exception
- if env['action_dispatch.show_exceptions'] == false
- raise exception
- else
+ if request.show_exceptions?
render_exception(env, exception)
+ else
+ raise exception
end
end
private
def render_exception(env, exception)
- wrapper = ExceptionWrapper.new(env, exception)
+ backtrace_cleaner = env['action_dispatch.backtrace_cleaner']
+ wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
status = wrapper.status_code
env["action_dispatch.exception"] = wrapper.exception
env["action_dispatch.original_path"] = env["PATH_INFO"]
diff --git a/actionpack/lib/action_dispatch/middleware/stack.rb b/actionpack/lib/action_dispatch/middleware/stack.rb
index bbf734f103..0430ce3b9a 100644
--- a/actionpack/lib/action_dispatch/middleware/stack.rb
+++ b/actionpack/lib/action_dispatch/middleware/stack.rb
@@ -4,36 +4,15 @@ require "active_support/dependencies"
module ActionDispatch
class MiddlewareStack
class Middleware
- attr_reader :args, :block, :name, :classcache
+ attr_reader :args, :block, :klass
- def initialize(klass_or_name, *args, &block)
- @klass = nil
-
- if klass_or_name.respond_to?(:name)
- @klass = klass_or_name
- @name = @klass.name
- else
- @name = klass_or_name.to_s
- end
-
- @classcache = ActiveSupport::Dependencies::Reference
- @args, @block = args, block
+ def initialize(klass, args, block)
+ @klass = klass
+ @args = args
+ @block = block
end
- def klass
- @klass || classcache[@name]
- end
-
- def ==(middleware)
- case middleware
- when Middleware
- klass == middleware.klass
- when Class
- klass == middleware
- else
- normalize(@name) == normalize(middleware)
- end
- end
+ def name; klass.name; end
def inspect
klass.to_s
@@ -42,12 +21,6 @@ module ActionDispatch
def build(app)
klass.new(app, *args, &block)
end
-
- private
-
- def normalize(object)
- object.to_s.strip.sub(/^::/, '')
- end
end
include Enumerable
@@ -75,19 +48,17 @@ module ActionDispatch
middlewares[i]
end
- def unshift(*args, &block)
- middleware = self.class::Middleware.new(*args, &block)
- middlewares.unshift(middleware)
+ def unshift(klass, *args, &block)
+ middlewares.unshift(build_middleware(klass, args, block))
end
def initialize_copy(other)
self.middlewares = other.middlewares.dup
end
- def insert(index, *args, &block)
+ def insert(index, klass, *args, &block)
index = assert_index(index, :before)
- middleware = self.class::Middleware.new(*args, &block)
- middlewares.insert(index, middleware)
+ middlewares.insert(index, build_middleware(klass, args, block))
end
alias_method :insert_before, :insert
@@ -104,26 +75,48 @@ module ActionDispatch
end
def delete(target)
- middlewares.delete target
+ target = get_class target
+ middlewares.delete_if { |m| m.klass == target }
end
- def use(*args, &block)
- middleware = self.class::Middleware.new(*args, &block)
- middlewares.push(middleware)
+ def use(klass, *args, &block)
+ middlewares.push(build_middleware(klass, args, block))
end
- def build(app = nil, &block)
- app ||= block
- raise "MiddlewareStack#build requires an app" unless app
+ def build(app = Proc.new)
middlewares.freeze.reverse.inject(app) { |a, e| e.build(a) }
end
protected
def assert_index(index, where)
- i = index.is_a?(Integer) ? index : middlewares.index(index)
+ index = get_class index
+ i = index.is_a?(Integer) ? index : middlewares.index { |m| m.klass == index }
raise "No such middleware to insert #{where}: #{index.inspect}" unless i
i
end
+
+ private
+
+ def get_class(klass)
+ if klass.is_a?(String) || klass.is_a?(Symbol)
+ classcache = ActiveSupport::Dependencies::Reference
+ converted_klass = classcache[klass.to_s]
+ ActiveSupport::Deprecation.warn <<-eowarn
+Passing strings or symbols to the middleware builder is deprecated, please change
+them to actual class references. For example:
+
+ "#{klass}" => #{converted_klass}
+
+ eowarn
+ converted_klass
+ else
+ klass
+ end
+ end
+
+ def build_middleware(klass, args, block)
+ Middleware.new(get_class(klass), args, block)
+ end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb
index f38da4fdf6..9462ae4278 100644
--- a/actionpack/lib/action_dispatch/middleware/static.rb
+++ b/actionpack/lib/action_dispatch/middleware/static.rb
@@ -48,26 +48,30 @@ module ActionDispatch
end
def call(env)
- path = env['PATH_INFO']
+ serve ActionDispatch::Request.new env
+ end
+
+ def serve(request)
+ path = request.path_info
gzip_path = gzip_file_path(path)
- if gzip_path && gzip_encoding_accepted?(env)
- env['PATH_INFO'] = gzip_path
- status, headers, body = @file_server.call(env)
+ if gzip_path && gzip_encoding_accepted?(request)
+ request.path_info = gzip_path
+ status, headers, body = @file_server.call(request.env)
if status == 304
return [status, headers, body]
end
headers['Content-Encoding'] = 'gzip'
headers['Content-Type'] = content_type(path)
else
- status, headers, body = @file_server.call(env)
+ status, headers, body = @file_server.call(request.env)
end
headers['Vary'] = 'Accept-Encoding' if gzip_path
return [status, headers, body]
ensure
- env['PATH_INFO'] = path
+ request.path_info = path
end
private
@@ -79,8 +83,8 @@ module ActionDispatch
::Rack::Mime.mime_type(::File.extname(path), 'text/plain'.freeze)
end
- def gzip_encoding_accepted?(env)
- env['HTTP_ACCEPT_ENCODING'] =~ /\bgzip\b/i
+ def gzip_encoding_accepted?(request)
+ request.accept_encoding =~ /\bgzip\b/i
end
def gzip_file_path(path)
@@ -110,16 +114,17 @@ module ActionDispatch
end
def call(env)
- case env['REQUEST_METHOD']
- when 'GET', 'HEAD'
- path = env['PATH_INFO'].chomp('/'.freeze)
+ req = ActionDispatch::Request.new env
+
+ if req.get? || req.head?
+ path = req.path_info.chomp('/'.freeze)
if match = @file_handler.match?(path)
- env['PATH_INFO'] = match
- return @file_handler.call(env)
+ req.path_info = match
+ return @file_handler.serve(req)
end
end
- @app.call(env)
+ @app.call(req.env)
end
end
end
diff --git a/actionpack/lib/action_dispatch/request/session.rb b/actionpack/lib/action_dispatch/request/session.rb
index a8a3cd20b9..bae3d909fa 100644
--- a/actionpack/lib/action_dispatch/request/session.rb
+++ b/actionpack/lib/action_dispatch/request/session.rb
@@ -4,8 +4,8 @@ module ActionDispatch
class Request < Rack::Request
# Session is responsible for lazily loading the session from store.
class Session # :nodoc:
- ENV_SESSION_KEY = Rack::Session::Abstract::ENV_SESSION_KEY # :nodoc:
- ENV_SESSION_OPTIONS_KEY = Rack::Session::Abstract::ENV_SESSION_OPTIONS_KEY # :nodoc:
+ 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
Unspecified = Object.new
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 64352d5742..51bafb539f 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -5,6 +5,7 @@ require 'active_support/core_ext/enumerable'
require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/module/remove_method'
require 'active_support/inflector'
+require 'active_support/deprecation'
require 'action_dispatch/routing/redirection'
require 'action_dispatch/routing/endpoint'
@@ -16,7 +17,10 @@ module ActionDispatch
class Constraints < Endpoint #:nodoc:
attr_reader :app, :constraints
- def initialize(app, constraints, dispatcher_p)
+ SERVE = ->(app, req) { app.serve req }
+ CALL = ->(app, req) { app.call req.env }
+
+ def initialize(app, constraints, strategy)
# 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
@@ -26,12 +30,12 @@ module ActionDispatch
app = app.app
end
- @dispatcher = dispatcher_p
+ @strategy = strategy
@app, @constraints, = app, constraints
end
- def dispatcher?; @dispatcher; end
+ def dispatcher?; @strategy == SERVE; end
def matches?(req)
@constraints.all? do |constraint|
@@ -43,11 +47,7 @@ module ActionDispatch
def serve(req)
return [ 404, {'X-Cascade' => 'pass'}, [] ] unless matches?(req)
- if dispatcher?
- @app.serve req
- else
- @app.call req.env
- end
+ @strategy.call @app, req
end
private
@@ -59,101 +59,168 @@ module ActionDispatch
class Mapping #:nodoc:
ANCHOR_CHARACTERS_REGEX = %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
- attr_reader :requirements, :conditions, :defaults
- attr_reader :to, :default_controller, :default_action, :as, :anchor
+ attr_reader :requirements, :defaults
+ attr_reader :to, :default_controller, :default_action
+ attr_reader :required_defaults, :ast
- def self.build(scope, set, path, as, options)
+ def self.build(scope, set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options)
options = scope[:options].merge(options) if scope[:options]
- options.delete :only
- options.delete :except
- options.delete :shallow_path
- options.delete :shallow_prefix
- options.delete :shallow
+ defaults = (scope[:defaults] || {}).dup
+ scope_constraints = scope[:constraints] || {}
- defaults = (scope[:defaults] || {}).merge options.delete(:defaults) || {}
+ new set, ast, defaults, controller, default_action, scope[:module], to, formatted, scope_constraints, scope[:blocks] || [], via, options_constraints, anchor, options
+ end
- new scope, set, path, defaults, as, options
+ def self.check_via(via)
+ if via.empty?
+ msg = "You should not use the `match` method in your router without specifying an HTTP method.\n" \
+ "If you want to expose your action to both GET and POST, add `via: [:get, :post]` option.\n" \
+ "If you want to expose your action to GET, use `get` in the router:\n" \
+ " Instead of: match \"controller#action\"\n" \
+ " Do: get \"controller#action\""
+ raise ArgumentError, msg
+ end
+ via
end
- def initialize(scope, set, path, defaults, as, options)
- @requirements, @conditions = {}, {}
- @defaults = defaults
- @set = set
+ def self.normalize_path(path, format)
+ path = Mapper.normalize_path(path)
- @to = options.delete :to
- @default_controller = options.delete(:controller) || scope[:controller]
- @default_action = options.delete(:action) || scope[:action]
- @as = as
- @anchor = options.delete :anchor
+ if format == true
+ "#{path}.:format"
+ elsif optional_format?(path, format)
+ "#{path}(.:format)"
+ else
+ path
+ end
+ end
- formatted = options.delete :format
- via = Array(options.delete(:via) { [] })
- options_constraints = options.delete :constraints
+ def self.optional_format?(path, format)
+ format != false && !path.include?(':format') && !path.end_with?('/')
+ end
- path = normalize_path! path, formatted
- ast = path_ast path
- path_params = path_params ast
+ def initialize(set, ast, defaults, controller, default_action, modyoule, to, formatted, scope_constraints, blocks, via, options_constraints, anchor, options)
+ @defaults = defaults
+ @set = set
- options = normalize_options!(options, formatted, path_params, ast, scope[:module])
+ @to = to
+ @default_controller = controller
+ @default_action = default_action
+ @ast = ast
+ @anchor = anchor
+ @via = via
+ path_params = ast.find_all(&:symbol?).map(&:to_sym)
- split_constraints(path_params, scope[:constraints]) if scope[:constraints]
- constraints = constraints(options, path_params)
+ options = add_wildcard_options(options, formatted, ast)
- split_constraints path_params, constraints
+ options = normalize_options!(options, path_params, modyoule)
- @blocks = blocks(options_constraints, scope[:blocks])
+ split_options = constraints(options, path_params)
+
+ constraints = scope_constraints.merge Hash[split_options[:constraints] || []]
if options_constraints.is_a?(Hash)
- split_constraints path_params, options_constraints
- options_constraints.each do |key, default|
- if URL_OPTIONS.include?(key) && (String === default || Fixnum === default)
- @defaults[key] ||= default
- end
- end
+ @defaults = Hash[options_constraints.find_all { |key, default|
+ URL_OPTIONS.include?(key) && (String === default || Fixnum === default)
+ }].merge @defaults
+ @blocks = blocks
+ constraints.merge! options_constraints
+ else
+ @blocks = blocks(options_constraints)
end
- normalize_format!(formatted)
+ requirements, conditions = split_constraints path_params, constraints
+ verify_regexp_requirements requirements.map(&:last).grep(Regexp)
- @conditions[:path_info] = path
- @conditions[:parsed_path_info] = ast
+ formats = normalize_format(formatted)
- add_request_method(via, @conditions)
- normalize_defaults!(options)
+ @requirements = formats[:requirements].merge Hash[requirements]
+ @conditions = Hash[conditions]
+ @defaults = formats[:defaults].merge(@defaults).merge(normalize_defaults(options))
+
+ @required_defaults = (split_options[:required_defaults] || []).map(&:first)
end
- def to_route
- [ app(@blocks), conditions, requirements, defaults, as, anchor ]
+ def make_route(name, precedence)
+ route = Journey::Route.new(name,
+ application,
+ path,
+ conditions,
+ required_defaults,
+ defaults,
+ request_method,
+ precedence)
+
+ route
end
- private
+ def application
+ app(@blocks)
+ end
- def normalize_path!(path, format)
- path = Mapper.normalize_path(path)
+ def path
+ build_path @ast, requirements, @anchor
+ end
- if format == true
- "#{path}.:format"
- elsif optional_format?(path, format)
- "#{path}(.:format)"
- else
- path
- end
- end
+ def conditions
+ build_conditions @conditions, @set.request_class
+ end
+
+ def build_conditions(current_conditions, request_class)
+ conditions = current_conditions.dup
- def optional_format?(path, format)
- format != false && !path.include?(':format') && !path.end_with?('/')
+ conditions.keep_if do |k, _|
+ request_class.public_method_defined?(k)
end
+ end
+ private :build_conditions
+
+ def request_method
+ @via.map { |x| Journey::Route.verb_matcher(x) }
+ end
+ private :request_method
+
+ JOINED_SEPARATORS = SEPARATORS.join # :nodoc:
+
+ def build_path(ast, requirements, anchor)
+ pattern = Journey::Path::Pattern.new(ast, requirements, JOINED_SEPARATORS, anchor)
+
+ # Get all the symbol nodes followed by literals that are not the
+ # dummy node.
+ symbols = ast.find_all { |n|
+ n.cat? && n.left.symbol? && n.right.cat? && n.right.left.literal?
+ }.map(&:left)
+
+ # Get all the symbol nodes preceded by literals.
+ symbols.concat ast.find_all { |n|
+ n.cat? && n.left.literal? && n.right.cat? && n.right.left.symbol?
+ }.map { |n| n.right.left }
+
+ symbols.each { |x|
+ x.regexp = /(?:#{Regexp.union(x.regexp, '-')})+/
+ }
+
+ pattern
+ end
+ private :build_path
+
- def normalize_options!(options, formatted, path_params, path_ast, modyoule)
+ 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
if formatted != false
- path_ast.grep(Journey::Nodes::Star) do |node|
- options[node.name.to_sym] ||= /.+?/
- end
+ path_ast.grep(Journey::Nodes::Star).each_with_object({}) { |node, hash|
+ hash[node.name.to_sym] ||= /.+?/
+ }.merge options
+ else
+ options
end
+ end
+ def normalize_options!(options, path_params, modyoule)
if path_params.include?(:controller)
raise ArgumentError, ":controller segment is not allowed within a namespace block" if modyoule
@@ -178,74 +245,50 @@ module ActionDispatch
end
def split_constraints(path_params, constraints)
- constraints.each_pair do |key, requirement|
- if path_params.include?(key) || key == :controller
- verify_regexp_requirement(requirement) if requirement.is_a?(Regexp)
- @requirements[key] = requirement
- else
- @conditions[key] = requirement
- end
+ constraints.partition do |key, requirement|
+ path_params.include?(key) || key == :controller
end
end
- def normalize_format!(formatted)
- if formatted == true
- @requirements[:format] ||= /.+/
- elsif Regexp === formatted
- @requirements[:format] = formatted
- @defaults[:format] = nil
- elsif String === formatted
- @requirements[:format] = Regexp.compile(formatted)
- @defaults[:format] = formatted
+ def normalize_format(formatted)
+ case formatted
+ when true
+ { requirements: { format: /.+/ },
+ defaults: {} }
+ when Regexp
+ { requirements: { format: formatted },
+ defaults: { format: nil } }
+ when String
+ { requirements: { format: Regexp.compile(formatted) },
+ defaults: { format: formatted } }
+ else
+ { requirements: { }, defaults: { } }
end
end
- def verify_regexp_requirement(requirement)
- if requirement.source =~ ANCHOR_CHARACTERS_REGEX
- raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
- end
-
- if requirement.multiline?
- raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{requirement.inspect}"
- end
- end
+ def verify_regexp_requirements(requirements)
+ requirements.each do |requirement|
+ if requirement.source =~ ANCHOR_CHARACTERS_REGEX
+ raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
+ end
- def normalize_defaults!(options)
- options.each_pair do |key, default|
- unless Regexp === default
- @defaults[key] = default
+ if requirement.multiline?
+ raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{requirement.inspect}"
end
end
end
- def verify_callable_constraint(callable_constraint)
- unless callable_constraint.respond_to?(:call) || callable_constraint.respond_to?(:matches?)
- raise ArgumentError, "Invalid constraint: #{callable_constraint.inspect} must respond to :call or :matches?"
- end
- end
-
- def add_request_method(via, conditions)
- return if via == [:all]
-
- if via.empty?
- msg = "You should not use the `match` method in your router without specifying an HTTP method.\n" \
- "If you want to expose your action to both GET and POST, add `via: [:get, :post]` option.\n" \
- "If you want to expose your action to GET, use `get` in the router:\n" \
- " Instead of: match \"controller#action\"\n" \
- " Do: get \"controller#action\""
- raise ArgumentError, msg
- end
-
- conditions[:request_method] = via.map { |m| m.to_s.dasherize.upcase }
+ def normalize_defaults(options)
+ Hash[options.reject { |_, default| Regexp === default }]
end
def app(blocks)
if to.respond_to?(:call)
- Constraints.new(to, blocks, false)
+ Constraints.new(to, blocks, Constraints::CALL)
elsif blocks.any?
- Constraints.new(dispatcher(defaults), blocks, true)
+ Constraints.new(dispatcher(defaults.key?(:controller)), blocks, Constraints::SERVE)
else
- dispatcher(defaults)
+ dispatcher(defaults.key?(:controller))
end
end
@@ -303,40 +346,29 @@ module ActionDispatch
yield
end
- def blocks(options_constraints, scope_blocks)
- if options_constraints && !options_constraints.is_a?(Hash)
- verify_callable_constraint(options_constraints)
- [options_constraints]
- else
- scope_blocks || []
+ def blocks(callable_constraint)
+ unless callable_constraint.respond_to?(:call) || callable_constraint.respond_to?(:matches?)
+ raise ArgumentError, "Invalid constraint: #{callable_constraint.inspect} must respond to :call or :matches?"
end
+ [callable_constraint]
end
def constraints(options, path_params)
- constraints = {}
- required_defaults = []
- options.each_pair do |key, option|
+ options.group_by do |key, option|
if Regexp === option
- constraints[key] = option
+ :constraints
else
- required_defaults << key unless path_params.include?(key)
+ if path_params.include?(key)
+ :path_params
+ else
+ :required_defaults
+ end
end
end
- @conditions[:required_defaults] = required_defaults
- constraints
- end
-
- def path_params(ast)
- ast.grep(Journey::Nodes::Symbol).map { |n| n.name.to_sym }
- end
-
- def path_ast(path)
- parser = Journey::Parser.new
- parser.parse path
end
- def dispatcher(defaults)
- @set.dispatcher defaults
+ def dispatcher(raise_on_name_error)
+ @set.dispatcher raise_on_name_error
end
end
@@ -448,10 +480,10 @@ module ActionDispatch
# resources :user, param: :name
#
# You can override <tt>ActiveRecord::Base#to_param</tt> of a related
- # model to constructe an URL.
+ # model to construct an URL:
#
# class User < ActiveRecord::Base
- # def to_param # overridden
+ # def to_param
# name
# end
# end
@@ -603,7 +635,7 @@ module ActionDispatch
# Query if the following named route was already defined.
def has_named_route?(name)
- @set.named_routes.routes[name.to_sym]
+ @set.named_routes.key? name
end
private
@@ -685,7 +717,11 @@ module ActionDispatch
def map_method(method, args, &block)
options = args.extract_options!
options[:via] = method
- match(*args, options, &block)
+ if options.key?(:defaults)
+ defaults(options.delete(:defaults)) { match(*args, options, &block) }
+ else
+ match(*args, options, &block)
+ end
self
end
end
@@ -788,8 +824,8 @@ module ActionDispatch
end
if options[:constraints].is_a?(Hash)
- defaults = options[:constraints].select do
- |k, v| URL_OPTIONS.include?(k) && (v.is_a?(String) || v.is_a?(Fixnum))
+ defaults = options[:constraints].select do |k, v|
+ URL_OPTIONS.include?(k) && (v.is_a?(String) || v.is_a?(Fixnum))
end
(options[:defaults] ||= {}).reverse_merge!(defaults)
@@ -797,16 +833,25 @@ module ActionDispatch
block, options[:constraints] = options[:constraints], {}
end
+ if options.key?(:only) || options.key?(:except)
+ scope[:action_options] = { only: options.delete(:only),
+ except: options.delete(:except) }
+ end
+
+ if options.key? :anchor
+ raise ArgumentError, 'anchor is ignored unless passed to `match`'
+ end
+
@scope.options.each do |option|
if option == :blocks
value = block
elsif option == :options
value = options
else
- value = options.delete(option)
+ value = options.delete(option) { POISON }
end
- if value
+ unless POISON == value
scope[option] = send("merge_#{option}_scope", @scope[option], value)
end
end
@@ -818,14 +863,18 @@ module ActionDispatch
@scope = @scope.parent
end
+ POISON = Object.new # :nodoc:
+
# Scopes routes to a specific controller
#
# controller "food" do
# match "bacon", action: :bacon, via: :get
# end
- def controller(controller, options={})
- options[:controller] = controller
- scope(options) { yield }
+ def controller(controller)
+ @scope = @scope.new(controller: controller)
+ yield
+ ensure
+ @scope = @scope.parent
end
# Scopes routes to a specific namespace. For example:
@@ -871,13 +920,14 @@ module ActionDispatch
defaults = {
module: path,
- path: options.fetch(:path, path),
as: options.fetch(:as, path),
shallow_path: options.fetch(:path, path),
shallow_prefix: options.fetch(:as, path)
}
- scope(defaults.merge!(options)) { yield }
+ path_scope(options.delete(:path) { path }) do
+ scope(defaults.merge!(options)) { yield }
+ end
end
# === Parameter Restriction
@@ -945,7 +995,10 @@ module ActionDispatch
# end
# Using this, the +:id+ parameter here will default to 'home'.
def defaults(defaults = {})
- scope(:defaults => defaults) { yield }
+ @scope = @scope.new(defaults: merge_defaults_scope(@scope[:defaults], defaults))
+ yield
+ ensure
+ @scope = @scope.parent
end
private
@@ -977,6 +1030,14 @@ module ActionDispatch
child
end
+ def merge_via_scope(parent, child) #:nodoc:
+ child
+ end
+
+ def merge_format_scope(parent, child) #:nodoc:
+ child
+ end
+
def merge_path_names_scope(parent, child) #:nodoc:
merge_options_scope(parent, child)
end
@@ -996,16 +1057,12 @@ module ActionDispatch
end
def merge_options_scope(parent, child) #:nodoc:
- (parent || {}).except(*override_keys(child)).merge!(child)
+ (parent || {}).merge(child)
end
def merge_shallow_scope(parent, child) #:nodoc:
child ? true : false
end
-
- def override_keys(child) #:nodoc:
- child.key?(:only) || child.key?(:except) ? [:only, :except] : []
- end
end
# Resource routing allows you to quickly declare all of the common routes
@@ -1055,17 +1112,19 @@ module ActionDispatch
CANONICAL_ACTIONS = %w(index create new show update destroy)
class Resource #:nodoc:
- attr_reader :controller, :path, :options, :param
+ attr_reader :controller, :path, :param
- def initialize(entities, api_only = false, options = {})
+ def initialize(entities, api_only, shallow, options = {})
@name = entities.to_s
@path = (options[:path] || @name).to_s
@controller = (options[:controller] || @name).to_s
@as = options[:as]
@param = (options[:param] || :id).to_sym
@options = options
- @shallow = false
+ @shallow = shallow
@api_only = api_only
+ @only = options.delete :only
+ @except = options.delete :except
end
def default_actions
@@ -1077,10 +1136,10 @@ module ActionDispatch
end
def actions
- if only = @options[:only]
- Array(only).map(&:to_sym)
- elsif except = @options[:except]
- default_actions - Array(except).map(&:to_sym)
+ if @only
+ Array(@only).map(&:to_sym)
+ elsif @except
+ default_actions - Array(@except).map(&:to_sym)
else
default_actions
end
@@ -1107,7 +1166,7 @@ module ActionDispatch
end
def resource_scope
- { :controller => controller }
+ controller
end
alias :collection_scope :path
@@ -1130,17 +1189,15 @@ module ActionDispatch
"#{path}/:#{nested_param}"
end
- def shallow=(value)
- @shallow = value
- end
-
def shallow?
@shallow
end
+
+ def singleton?; false; end
end
class SingletonResource < Resource #:nodoc:
- def initialize(entities, api_only, options)
+ def initialize(entities, api_only, shallow, options)
super
@as = nil
@controller = (options[:controller] || plural).to_s
@@ -1168,6 +1225,8 @@ module ActionDispatch
alias :member_scope :path
alias :nested_scope :path
+
+ def singleton?; true; end
end
def resources_path_names(options)
@@ -1202,20 +1261,23 @@ module ActionDispatch
return self
end
- resource_scope(:resource, SingletonResource.new(resources.pop, api_only?, options)) do
- yield if block_given?
+ with_scope_level(:resource) do
+ options = apply_action_options options
+ resource_scope(SingletonResource.new(resources.pop, api_only?, @scope[:shallow], options)) do
+ yield if block_given?
- concerns(options[:concerns]) if options[:concerns]
+ concerns(options[:concerns]) if options[:concerns]
- collection do
- post :create
- end if parent_resource.actions.include?(:create)
+ collection do
+ post :create
+ end if parent_resource.actions.include?(:create)
- new do
- get :new
- end if parent_resource.actions.include?(:new)
+ new do
+ get :new
+ end if parent_resource.actions.include?(:new)
- set_member_mappings_for_resource
+ set_member_mappings_for_resource
+ end
end
self
@@ -1360,21 +1422,24 @@ module ActionDispatch
return self
end
- resource_scope(:resources, Resource.new(resources.pop, api_only?, options)) do
- yield if block_given?
+ with_scope_level(:resources) do
+ options = apply_action_options options
+ resource_scope(Resource.new(resources.pop, api_only?, @scope[:shallow], options)) do
+ yield if block_given?
- concerns(options[:concerns]) if options[:concerns]
+ concerns(options[:concerns]) if options[:concerns]
- collection do
- get :index if parent_resource.actions.include?(:index)
- post :create if parent_resource.actions.include?(:create)
- end
+ collection do
+ get :index if parent_resource.actions.include?(:index)
+ post :create if parent_resource.actions.include?(:create)
+ end
- new do
- get :new
- end if parent_resource.actions.include?(:new)
+ new do
+ get :new
+ end if parent_resource.actions.include?(:new)
- set_member_mappings_for_resource
+ set_member_mappings_for_resource
+ end
end
self
@@ -1398,7 +1463,7 @@ module ActionDispatch
end
with_scope_level(:collection) do
- scope(parent_resource.collection_scope) do
+ path_scope(parent_resource.collection_scope) do
yield
end
end
@@ -1422,9 +1487,11 @@ module ActionDispatch
with_scope_level(:member) do
if shallow?
- shallow_scope(parent_resource.member_scope) { yield }
+ shallow_scope {
+ path_scope(parent_resource.member_scope) { yield }
+ }
else
- scope(parent_resource.member_scope) { yield }
+ path_scope(parent_resource.member_scope) { yield }
end
end
end
@@ -1435,7 +1502,7 @@ module ActionDispatch
end
with_scope_level(:new) do
- scope(parent_resource.new_scope(action_path(:new))) do
+ path_scope(parent_resource.new_scope(action_path(:new))) do
yield
end
end
@@ -1448,9 +1515,15 @@ module ActionDispatch
with_scope_level(:nested) do
if shallow? && shallow_nesting_depth >= 1
- shallow_scope(parent_resource.nested_scope, nested_options) { yield }
+ shallow_scope do
+ path_scope(parent_resource.nested_scope) do
+ scope(nested_options) { yield }
+ end
+ end
else
- scope(parent_resource.nested_scope, nested_options) { yield }
+ path_scope(parent_resource.nested_scope) do
+ scope(nested_options) { yield }
+ end
end
end
end
@@ -1465,13 +1538,14 @@ module ActionDispatch
end
def shallow
- scope(:shallow => true) do
- yield
- end
+ @scope = @scope.new(shallow: true)
+ yield
+ ensure
+ @scope = @scope.parent
end
def shallow?
- parent_resource.instance_of?(Resource) && @scope[:shallow]
+ !parent_resource.singleton? && @scope[:shallow]
end
# Matches a url pattern to one or more routes.
@@ -1505,8 +1579,6 @@ module ActionDispatch
paths = [path] + rest
end
- options[:anchor] = true unless options.key?(:anchor)
-
if options[:on] && !VALID_ON_OPTIONS.include?(options[:on])
raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
end
@@ -1515,48 +1587,85 @@ module ActionDispatch
options[:to] ||= "#{@scope[:controller]}##{@scope[:action]}"
end
- paths.each do |_path|
+ controller = options.delete(:controller) || @scope[:controller]
+ option_path = options.delete :path
+ to = options.delete :to
+ via = Mapping.check_via Array(options.delete(:via) {
+ @scope[:via]
+ })
+ formatted = options.delete(:format) { @scope[:format] }
+ anchor = options.delete(:anchor) { true }
+ options_constraints = options.delete(:constraints) || {}
+
+ path_types = paths.group_by(&:class)
+ path_types.fetch(String, []).each do |_path|
route_options = options.dup
- route_options[:path] ||= _path if _path.is_a?(String)
+ if _path && option_path
+ ActiveSupport::Deprecation.warn <<-eowarn
+Specifying strings for both :path and the route path is deprecated. Change things like this:
+
+ match #{_path.inspect}, :path => #{option_path.inspect}
- path_without_format = _path.to_s.sub(/\(\.:format\)$/, '')
- if using_match_shorthand?(path_without_format, route_options)
- route_options[:to] ||= path_without_format.gsub(%r{^/}, "").sub(%r{/([^/]*)$}, '#\1')
- route_options[:to].tr!("-", "_")
+to this:
+
+ match #{option_path.inspect}, :as => #{_path.inspect}, :action => #{path.inspect}
+ eowarn
+ route_options[:action] = _path
+ route_options[:as] = _path
+ _path = option_path
end
+ to = get_to_from_path(_path, to, route_options[:action])
+ decomposed_match(_path, controller, route_options, _path, to, via, formatted, anchor, options_constraints)
+ end
- decomposed_match(_path, route_options)
+ path_types.fetch(Symbol, []).each do |action|
+ route_options = options.dup
+ decomposed_match(action, controller, route_options, option_path, to, via, formatted, anchor, options_constraints)
end
+
self
end
- def using_match_shorthand?(path, options)
- path && (options[:to] || options[:action]).nil? && path =~ %r{^/?[-\w]+/[-\w/]+$}
+ def get_to_from_path(path, to, action)
+ return to if to || action
+
+ path_without_format = path.sub(/\(\.:format\)$/, '')
+ if using_match_shorthand?(path_without_format)
+ path_without_format.gsub(%r{^/}, "").sub(%r{/([^/]*)$}, '#\1').tr("-", "_")
+ else
+ nil
+ end
+ end
+
+ def using_match_shorthand?(path)
+ path =~ %r{^/?[-\w]+/[-\w/]+$}
end
- def decomposed_match(path, options) # :nodoc:
+ def decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) # :nodoc:
if on = options.delete(:on)
- send(on) { decomposed_match(path, options) }
+ send(on) { decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) }
else
case @scope.scope_level
when :resources
- nested { decomposed_match(path, options) }
+ nested { decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) }
when :resource
- member { decomposed_match(path, options) }
+ member { decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) }
else
- add_route(path, options)
+ add_route(path, controller, options, _path, to, via, formatted, anchor, options_constraints)
end
end
end
- def add_route(action, options) # :nodoc:
- path = path_for_action(action, options.delete(:path))
+ def add_route(action, controller, options, _path, to, via, formatted, anchor, options_constraints) # :nodoc:
+ path = path_for_action(action, _path)
raise ArgumentError, "path is required" if path.blank?
action = action.to_s
+ default_action = options.delete(:action) || @scope[:action]
+
if action =~ /^[\w\-\/]+$/
- options[:action] ||= action.tr('-', '_') unless action.include?("/")
+ default_action ||= action.tr('-', '_') unless action.include?("/")
else
action = nil
end
@@ -1567,9 +1676,11 @@ module ActionDispatch
name_for_action(options.delete(:as), action)
end
- mapping = Mapping.build(@scope, @set, URI.parser.escape(path), as, options)
- app, conditions, requirements, defaults, as, anchor = mapping.to_route
- @set.add_route(app, conditions, requirements, defaults, as, anchor)
+ path = Mapping.normalize_path URI.parser.escape(path), formatted
+ 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)
end
def root(path, options={})
@@ -1583,7 +1694,7 @@ module ActionDispatch
if @scope.resources?
with_scope_level(:root) do
- scope(parent_resource.path) do
+ path_scope(parent_resource.path) do
super(options)
end
end
@@ -1628,23 +1739,20 @@ module ActionDispatch
return true
end
- unless action_options?(options)
- options.merge!(scope_action_options) if scope_action_options?
- end
-
false
end
- def action_options?(options) #:nodoc:
- options[:only] || options[:except]
+ def apply_action_options(options) # :nodoc:
+ return options if action_options? options
+ options.merge scope_action_options
end
- def scope_action_options? #:nodoc:
- @scope[:options] && (@scope[:options][:only] || @scope[:options][:except])
+ def action_options?(options) #:nodoc:
+ options[:only] || options[:except]
end
def scope_action_options #:nodoc:
- @scope[:options].slice(:only, :except)
+ @scope[:action_options] || {}
end
def resource_scope? #:nodoc:
@@ -1659,18 +1767,6 @@ module ActionDispatch
@scope.nested?
end
- def with_exclusive_scope
- begin
- @scope = @scope.new(:as => nil, :path => nil)
-
- with_scope_level(:exclusive) do
- yield
- end
- ensure
- @scope = @scope.parent
- end
- end
-
def with_scope_level(kind)
@scope = @scope.new_level(kind)
yield
@@ -1678,16 +1774,11 @@ module ActionDispatch
@scope = @scope.parent
end
- def resource_scope(kind, resource) #:nodoc:
- resource.shallow = @scope[:shallow]
+ def resource_scope(resource) #:nodoc:
@scope = @scope.new(:scope_level_resource => resource)
- @nesting.push(resource)
- with_scope_level(kind) do
- scope(parent_resource.resource_scope) { yield }
- end
+ controller(resource.resource_scope) { yield }
ensure
- @nesting.pop
@scope = @scope.parent
end
@@ -1700,12 +1791,10 @@ module ActionDispatch
options
end
- def nesting_depth #:nodoc:
- @nesting.size
- end
-
def shallow_nesting_depth #:nodoc:
- @nesting.count(&:shallow?)
+ @scope.find_all { |node|
+ node.frame[:scope_level_resource]
+ }.count { |node| node.frame[:scope_level_resource].shallow? }
end
def param_constraint? #:nodoc:
@@ -1720,27 +1809,28 @@ module ActionDispatch
resource_method_scope? && CANONICAL_ACTIONS.include?(action.to_s)
end
- def shallow_scope(path, options = {}) #:nodoc:
+ def shallow_scope #:nodoc:
scope = { :as => @scope[:shallow_prefix],
:path => @scope[:shallow_path] }
@scope = @scope.new scope
- scope(path, options) { yield }
+ yield
ensure
@scope = @scope.parent
end
def path_for_action(action, path) #:nodoc:
- if path.blank? && canonical_action?(action)
+ return "#{@scope[:path]}/#{path}" if path
+
+ if canonical_action?(action)
@scope[:path].to_s
else
- "#{@scope[:path]}/#{action_path(action, path)}"
+ "#{@scope[:path]}/#{action_path(action)}"
end
end
- def action_path(name, path = nil) #:nodoc:
- name = name.to_sym if name.is_a?(String)
- path || @scope[:path_names][name] || name.to_s
+ def action_path(name) #:nodoc:
+ @scope[:path_names][name.to_sym] || name
end
def prefix_name_for_action(as, action) #:nodoc:
@@ -1796,6 +1886,14 @@ module ActionDispatch
def api_only?
@set.api_only?
end
+ private
+
+ def path_scope(path)
+ @scope = @scope.new(path: merge_path_scope(@scope[:path], path))
+ yield
+ ensure
+ @scope = @scope.parent
+ end
end
# Routing Concerns allow you to declare common routes that can be reused
@@ -1906,14 +2004,14 @@ module ActionDispatch
class Scope # :nodoc:
OPTIONS = [:path, :shallow_path, :as, :shallow_prefix, :module,
:controller, :action, :path_names, :constraints,
- :shallow, :blocks, :defaults, :options]
+ :shallow, :blocks, :defaults, :via, :format, :options]
RESOURCE_SCOPES = [:resource, :resources]
RESOURCE_METHOD_SCOPES = [:collection, :member, :new]
attr_reader :parent, :scope_level
- def initialize(hash, parent = {}, scope_level = nil)
+ def initialize(hash, parent = NULL, scope_level = nil)
@hash = hash
@parent = parent
@scope_level = scope_level
@@ -1961,27 +2059,34 @@ module ActionDispatch
end
def new_level(level)
- self.class.new(self, self, level)
- end
-
- def fetch(key, &block)
- @hash.fetch(key, &block)
+ self.class.new(frame, self, level)
end
def [](key)
- @hash.fetch(key) { @parent[key] }
+ scope = find { |node| node.frame.key? key }
+ scope && scope.frame[key]
end
- def []=(k,v)
- @hash[k] = v
+ include Enumerable
+
+ def each
+ node = self
+ loop do
+ break if node.equal? NULL
+ yield node
+ node = node.parent
+ end
end
+
+ def frame; @hash; end
+
+ NULL = Scope.new(nil, nil)
end
def initialize(set) #:nodoc:
@set = set
@scope = Scope.new({ :path_names => @set.resources_path_names })
@concerns = {}
- @nesting = []
end
include Base
diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb
index 3c1c4fadf6..d6987f4d09 100644
--- a/actionpack/lib/action_dispatch/routing/redirection.rb
+++ b/actionpack/lib/action_dispatch/routing/redirection.rb
@@ -24,7 +24,7 @@ module ActionDispatch
def serve(req)
req.check_path_parameters!
uri = URI.parse(path(req.path_parameters, req))
-
+
unless uri.host
if relative_path?(uri.path)
uri.path = "#{req.script_name}/#{uri.path}"
@@ -32,7 +32,7 @@ module ActionDispatch
uri.path = req.script_name.empty? ? "/" : req.script_name
end
end
-
+
uri.scheme ||= req.scheme
uri.host ||= req.host
uri.port ||= req.port unless req.standard_port?
@@ -124,7 +124,7 @@ module ActionDispatch
url_options[:script_name] = request.script_name
end
end
-
+
ActionDispatch::Http::URL.url_for url_options
end
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index c968381c26..20926012b4 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -21,8 +21,8 @@ module ActionDispatch
alias inspect to_s
class Dispatcher < Routing::Endpoint
- def initialize(defaults)
- @defaults = defaults
+ def initialize(raise_on_name_error)
+ @raise_on_name_error = raise_on_name_error
@controller_class_names = ThreadSafe::Cache.new
end
@@ -34,12 +34,11 @@ module ActionDispatch
prepare_params!(params)
- # Just raise undefined constant errors if a controller was specified as default.
- unless controller = controller(params, @defaults.key?(:controller))
+ controller = controller(params, @raise_on_name_error) do
return [404, {'X-Cascade' => 'pass'}, []]
end
- dispatch(controller, params[:action], req.env)
+ dispatch(controller, params[:action], req)
end
def prepare_params!(params)
@@ -53,24 +52,26 @@ module ActionDispatch
# segment, as in :controller(/:action), we should simply return nil and
# delegate the control back to Rack cascade. Besides, if this is not a default
# controller, it means we should respect the @scope[:module] parameter.
- def controller(params, default_controller=true)
- if params && params.key?(:controller)
- controller_param = params[:controller]
- controller_reference(controller_param)
- end
+ def controller(params, raise_on_name_error=true)
+ controller_reference params.fetch(:controller) { yield }
rescue NameError => e
- raise ActionController::RoutingError, e.message, e.backtrace if default_controller
+ raise ActionController::RoutingError, e.message, e.backtrace if raise_on_name_error
+ yield
end
- private
+ protected
+
+ attr_reader :controller_class_names
def controller_reference(controller_param)
- const_name = @controller_class_names[controller_param] ||= "#{controller_param.camelize}Controller"
+ const_name = controller_class_names[controller_param] ||= "#{controller_param.camelize}Controller"
ActiveSupport::Dependencies.constantize(const_name)
end
- def dispatch(controller, action, env)
- controller.action(action).call(env)
+ private
+
+ def dispatch(controller, action, req)
+ controller.action(action).call(req.env)
end
def normalize_controller!(params)
@@ -88,6 +89,7 @@ module ActionDispatch
class NamedRouteCollection
include Enumerable
attr_reader :routes, :url_helpers_module, :path_helpers_module
+ private :routes
def initialize
@routes = {}
@@ -142,6 +144,7 @@ module ActionDispatch
end
def key?(name)
+ return unless name
routes.key? name.to_sym
end
@@ -267,7 +270,7 @@ module ActionDispatch
path_params -= controller_options.keys
path_params -= result.keys
end
- inner_options.each do |key, _|
+ inner_options.each_key do |key|
path_params.delete(key)
end
@@ -313,7 +316,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_accessor :default_url_options, :dispatcher_class
attr_reader :env_key
alias :routes :set
@@ -355,7 +358,8 @@ module ActionDispatch
@set = Journey::Routes.new
@router = Journey::Router.new @set
- @formatter = Journey::Formatter.new @set
+ @formatter = Journey::Formatter.new self
+ @dispatcher_class = Routing::RouteSet::Dispatcher
end
def relative_url_root
@@ -413,8 +417,8 @@ module ActionDispatch
@prepend.each { |blk| eval_block(blk) }
end
- def dispatcher(defaults)
- Routing::RouteSet::Dispatcher.new(defaults)
+ def dispatcher(raise_on_name_error)
+ dispatcher_class.new(raise_on_name_error)
end
module MountedHelpers
@@ -512,7 +516,7 @@ module ActionDispatch
routes.empty?
end
- def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil, anchor = true)
+ def add_route(mapping, path_ast, name, anchor)
raise ArgumentError, "Invalid route name: '#{name}'" unless name.blank? || name.to_s.match(/^[_a-z]\w*$/i)
if name && named_routes[name]
@@ -523,74 +527,17 @@ module ActionDispatch
"http://guides.rubyonrails.org/routing.html#restricting-the-routes-created"
end
- path = conditions.delete :path_info
- ast = conditions.delete :parsed_path_info
- required_defaults = conditions.delete :required_defaults
- path = build_path(path, ast, requirements, anchor)
- conditions = build_conditions(conditions)
-
- route = @set.add_route(app, path, conditions, required_defaults, defaults, name)
+ route = @set.add_route(name, mapping)
named_routes[name] = route if name
route
end
- def build_path(path, ast, requirements, anchor)
- strexp = Journey::Router::Strexp.new(
- ast,
- path,
- requirements,
- SEPARATORS,
- anchor)
-
- pattern = Journey::Path::Pattern.new(strexp)
-
- builder = Journey::GTG::Builder.new pattern.spec
-
- # Get all the symbol nodes followed by literals that are not the
- # dummy node.
- symbols = pattern.spec.grep(Journey::Nodes::Symbol).find_all { |n|
- builder.followpos(n).first.literal?
- }
-
- # Get all the symbol nodes preceded by literals.
- symbols.concat pattern.spec.find_all(&:literal?).map { |n|
- builder.followpos(n).first
- }.find_all(&:symbol?)
-
- symbols.each { |x|
- x.regexp = /(?:#{Regexp.union(x.regexp, '-')})+/
- }
-
- pattern
- end
- private :build_path
-
- def build_conditions(current_conditions)
- conditions = current_conditions.dup
-
- # Rack-Mount requires that :request_method be a regular expression.
- # :request_method represents the HTTP verb that matches this route.
- #
- # Here we munge values before they get sent on to rack-mount.
- verbs = conditions[:request_method] || []
- unless verbs.empty?
- conditions[:request_method] = %r[^#{verbs.join('|')}$]
- end
-
- conditions.keep_if do |k, _|
- request_class.public_method_defined?(k)
- end
- end
- private :build_conditions
-
class Generator
PARAMETERIZE = lambda do |name, value|
if name == :controller
value
- elsif value.is_a?(Array)
- value.map(&:to_param).join('/')
- elsif param = value.to_param
- param
+ else
+ value.to_param
end
end
@@ -815,12 +762,12 @@ module ActionDispatch
if app.matches?(req) && app.dispatcher?
dispatcher = app.app
- if dispatcher.controller(params, false)
- dispatcher.prepare_params!(params)
- return params
- else
+ dispatcher.controller(params, false) do
raise ActionController::RoutingError, "A route matches #{path.inspect}, but references missing controller: #{params[:controller].camelize}Controller"
end
+
+ dispatcher.prepare_params!(params)
+ return params
end
end
diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
index d0e3ea818e..54e24ed6bf 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
@@ -86,8 +86,8 @@ module ActionDispatch
end
# Load routes.rb if it hasn't been loaded.
- generated_path, extra_keys = @routes.generate_extras(options, defaults)
- found_extras = options.reject { |k, _| ! extra_keys.include? k }
+ generated_path, query_string_keys = @routes.generate_extras(options, defaults)
+ found_extras = options.reject { |k, _| ! query_string_keys.include? k }
msg = message || sprintf("found extras <%s>, not <%s>", found_extras, extras)
assert_equal(extras, found_extras, msg)
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 0298962409..0cdc6d4e77 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -325,7 +325,11 @@ module ActionDispatch
if path =~ %r{://}
location = URI.parse(path)
https! URI::HTTPS === location if location.scheme
- host! "#{location.host}:#{location.port}" if location.host
+ if url_host = location.host
+ default = Rack::Request::DEFAULT_PORTS[location.scheme]
+ url_host += ":#{location.port}" if default != location.port
+ host! url_host
+ end
path = location.query ? "#{location.path}?#{location.query}" : location.path
end
@@ -374,7 +378,7 @@ module ActionDispatch
@html_document = nil
@url_options = nil
- @controller = session.last_request.env['action_controller.instance']
+ @controller = @request.controller_instance
response.status
end
diff --git a/actionpack/lib/action_dispatch/testing/test_process.rb b/actionpack/lib/action_dispatch/testing/test_process.rb
index 494644cd46..c28d701b48 100644
--- a/actionpack/lib/action_dispatch/testing/test_process.rb
+++ b/actionpack/lib/action_dispatch/testing/test_process.rb
@@ -19,7 +19,7 @@ module ActionDispatch
end
def cookies
- @cookie_jar ||= Cookies::CookieJar.build(@request.env, @request.host, @request.ssl?, @request.cookies)
+ @cookie_jar ||= Cookies::CookieJar.build(@request, @request.cookies)
end
def redirect_to_url
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index cc610b6d75..60e2cea8a2 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -63,6 +63,10 @@ FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures')
SharedTestRoutes = ActionDispatch::Routing::RouteSet.new
+SharedTestRoutes.draw do
+ get ':controller(/:action)'
+end
+
module ActionDispatch
module SharedRoutes
def before_setup
@@ -70,35 +74,10 @@ module ActionDispatch
super
end
end
-
- # Hold off drawing routes until all the possible controller classes
- # have been loaded.
- module DrawOnce
- class << self
- attr_accessor :drew
- end
- self.drew = false
-
- def before_setup
- super
- return if DrawOnce.drew
-
- SharedTestRoutes.draw do
- get ':controller(/:action)'
- end
-
- ActionDispatch::IntegrationTest.app.routes.draw do
- get ':controller(/:action)'
- end
-
- DrawOnce.drew = true
- end
- end
end
module ActiveSupport
class TestCase
- include ActionDispatch::DrawOnce
if RUBY_ENGINE == "ruby" && PROCESS_COUNT > 0
parallelize_me!
end
@@ -119,23 +98,25 @@ class RoutedRackApp
end
class ActionDispatch::IntegrationTest < ActiveSupport::TestCase
- include ActionDispatch::SharedRoutes
-
def self.build_app(routes = nil)
RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new) do |middleware|
- middleware.use "ActionDispatch::ShowExceptions", ActionDispatch::PublicExceptions.new("#{FIXTURE_LOAD_PATH}/public")
- middleware.use "ActionDispatch::DebugExceptions"
- middleware.use "ActionDispatch::Callbacks"
- middleware.use "ActionDispatch::ParamsParser"
- middleware.use "ActionDispatch::Cookies"
- middleware.use "ActionDispatch::Flash"
- middleware.use "Rack::Head"
+ middleware.use ActionDispatch::ShowExceptions, ActionDispatch::PublicExceptions.new("#{FIXTURE_LOAD_PATH}/public")
+ middleware.use ActionDispatch::DebugExceptions
+ middleware.use ActionDispatch::Callbacks
+ middleware.use ActionDispatch::ParamsParser
+ middleware.use ActionDispatch::Cookies
+ middleware.use ActionDispatch::Flash
+ middleware.use Rack::Head
yield(middleware) if block_given?
end
end
self.app = build_app
+ app.routes.draw do
+ get ':controller(/:action)'
+ end
+
# Stub Rails dispatcher so it does not get controller references and
# simply return the controller#action as Rack::Body.
class StubDispatcher < ::ActionDispatch::Routing::RouteSet::Dispatcher
@@ -149,14 +130,10 @@ class ActionDispatch::IntegrationTest < ActiveSupport::TestCase
end
end
- def self.stub_controllers
- old_dispatcher = ActionDispatch::Routing::RouteSet::Dispatcher
- ActionDispatch::Routing::RouteSet.module_eval { remove_const :Dispatcher }
- ActionDispatch::Routing::RouteSet.module_eval { const_set :Dispatcher, StubDispatcher }
- yield ActionDispatch::Routing::RouteSet.new
- ensure
- ActionDispatch::Routing::RouteSet.module_eval { remove_const :Dispatcher }
- ActionDispatch::Routing::RouteSet.module_eval { const_set :Dispatcher, old_dispatcher }
+ def self.stub_controllers(config = nil)
+ route_set = ActionDispatch::Routing::RouteSet.new(*[config].compact)
+ route_set.dispatcher_class = StubDispatcher
+ yield route_set
end
def with_routing(&block)
diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb
index 64543f0659..b063d769a4 100644
--- a/actionpack/test/controller/flash_test.rb
+++ b/actionpack/test/controller/flash_test.rb
@@ -329,7 +329,7 @@ class FlashIntegrationTest < ActionDispatch::IntegrationTest
@app = self.class.build_app(set) do |middleware|
middleware.use ActionDispatch::Session::CookieStore, :key => SessionKey
middleware.use ActionDispatch::Flash
- middleware.delete "ActionDispatch::ShowExceptions"
+ middleware.delete ActionDispatch::ShowExceptions
end
yield
diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb
index ed3632007d..0a5e5402b9 100644
--- a/actionpack/test/controller/http_basic_authentication_test.rb
+++ b/actionpack/test/controller/http_basic_authentication_test.rb
@@ -100,6 +100,14 @@ class HttpBasicAuthenticationTest < ActionController::TestCase
assert_no_match(/\n/, result)
end
+ test "succesful authentication with uppercase authorization scheme" do
+ @request.env['HTTP_AUTHORIZATION'] = "BASIC #{::Base64.encode64("lifo:world")}"
+ get :index
+
+ assert_response :success
+ assert_equal 'Hello Secret', @response.body, 'Authentication failed when authorization scheme BASIC'
+ end
+
test "authentication request without credential" do
get :display
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index 0b9be8d671..dc4c32b07e 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -755,6 +755,25 @@ class MetalIntegrationTest < ActionDispatch::IntegrationTest
assert_equal "http://test.com/", @request.env["HTTP_REFERER"]
end
+ def test_ignores_common_ports_in_host
+ get "http://test.com"
+ assert_equal "test.com", @request.env["HTTP_HOST"]
+
+ get "https://test.com"
+ assert_equal "test.com", @request.env["HTTP_HOST"]
+ end
+
+ def test_keeps_uncommon_ports_in_host
+ get "http://test.com:123"
+ assert_equal "test.com:123", @request.env["HTTP_HOST"]
+
+ get "http://test.com:443"
+ assert_equal "test.com:443", @request.env["HTTP_HOST"]
+
+ get "https://test.com:80"
+ assert_equal "test.com:80", @request.env["HTTP_HOST"]
+ end
+
end
class ApplicationIntegrationTest < ActionDispatch::IntegrationTest
diff --git a/actionpack/test/controller/new_base/metal_test.rb b/actionpack/test/controller/new_base/metal_test.rb
deleted file mode 100644
index 537b93387a..0000000000
--- a/actionpack/test/controller/new_base/metal_test.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-require 'abstract_unit'
-
-module MetalTest
- class MetalMiddleware < ActionController::Middleware
- def call(env)
- if env["PATH_INFO"] =~ /authed/
- app.call(env)
- else
- [401, headers, "Not authed!"]
- end
- end
- end
-
- class Endpoint
- def call(env)
- [200, {}, "Hello World"]
- end
- end
-
- class TestMiddleware < ActiveSupport::TestCase
- def setup
- @app = Rack::Builder.new do
- use MetalTest::MetalMiddleware
- run MetalTest::Endpoint.new
- end.to_app
- end
-
- test "it can call the next app by using @app" do
- env = Rack::MockRequest.env_for("/authed")
- response = @app.call(env)
-
- assert_equal ["Hello World"], response[2]
- end
-
- test "it can return a response using the normal AC::Metal techniques" do
- env = Rack::MockRequest.env_for("/")
- response = @app.call(env)
-
- assert_equal ["Not authed!"], response[2]
- assert_equal 401, response[0]
- end
- end
-end
diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb
index 5a279639cc..dd7c128566 100644
--- a/actionpack/test/controller/resources_test.rb
+++ b/actionpack/test/controller/resources_test.rb
@@ -505,8 +505,8 @@ class ResourcesTest < ActionController::TestCase
routes = @routes.routes
routes.each do |route|
routes.each do |r|
- next if route === r # skip the comparison instance
- assert_not_equal [route.conditions, route.path.spec.to_s], [r.conditions, r.path.spec.to_s]
+ next if route == r # skip the comparison instance
+ assert_not_equal [route.conditions, route.path.spec.to_s, route.verb], [r.conditions, r.path.spec.to_s, r.verb]
end
end
end
@@ -1128,14 +1128,14 @@ class ResourcesTest < ActionController::TestCase
end
def assert_restful_routes_for(controller_name, options = {})
- options[:options] ||= {}
- options[:options][:controller] = options[:controller] || controller_name.to_s
+ route_options = (options[:options] ||= {}).dup
+ route_options[:controller] = options[:controller] || controller_name.to_s
if options[:shallow]
options[:shallow_options] ||= {}
- options[:shallow_options][:controller] = options[:options][:controller]
+ options[:shallow_options][:controller] = route_options[:controller]
else
- options[:shallow_options] = options[:options]
+ options[:shallow_options] = route_options
end
new_action = @routes.resources_path_names[:new] || "new"
@@ -1154,7 +1154,7 @@ class ResourcesTest < ActionController::TestCase
edit_member_path = "#{member_path}/#{edit_action}"
formatted_edit_member_path = "#{member_path}/#{edit_action}.xml"
- with_options(options[:options]) do |controller|
+ with_options(route_options) do |controller|
controller.assert_routing collection_path, :action => 'index'
controller.assert_routing new_path, :action => 'new'
controller.assert_routing "#{collection_path}.xml", :action => 'index', :format => 'xml'
@@ -1168,23 +1168,23 @@ class ResourcesTest < ActionController::TestCase
controller.assert_routing formatted_edit_member_path, :action => 'edit', :id => '1', :format => 'xml'
end
- assert_recognizes(options[:options].merge(:action => 'index'), :path => collection_path, :method => :get)
- assert_recognizes(options[:options].merge(:action => 'new'), :path => new_path, :method => :get)
- assert_recognizes(options[:options].merge(:action => 'create'), :path => collection_path, :method => :post)
+ assert_recognizes(route_options.merge(:action => 'index'), :path => collection_path, :method => :get)
+ assert_recognizes(route_options.merge(:action => 'new'), :path => new_path, :method => :get)
+ assert_recognizes(route_options.merge(:action => 'create'), :path => collection_path, :method => :post)
assert_recognizes(options[:shallow_options].merge(:action => 'show', :id => '1'), :path => member_path, :method => :get)
assert_recognizes(options[:shallow_options].merge(:action => 'edit', :id => '1'), :path => edit_member_path, :method => :get)
assert_recognizes(options[:shallow_options].merge(:action => 'update', :id => '1'), :path => member_path, :method => :put)
assert_recognizes(options[:shallow_options].merge(:action => 'destroy', :id => '1'), :path => member_path, :method => :delete)
- assert_recognizes(options[:options].merge(:action => 'index', :format => 'xml'), :path => "#{collection_path}.xml", :method => :get)
- assert_recognizes(options[:options].merge(:action => 'new', :format => 'xml'), :path => "#{new_path}.xml", :method => :get)
- assert_recognizes(options[:options].merge(:action => 'create', :format => 'xml'), :path => "#{collection_path}.xml", :method => :post)
+ assert_recognizes(route_options.merge(:action => 'index', :format => 'xml'), :path => "#{collection_path}.xml", :method => :get)
+ assert_recognizes(route_options.merge(:action => 'new', :format => 'xml'), :path => "#{new_path}.xml", :method => :get)
+ assert_recognizes(route_options.merge(:action => 'create', :format => 'xml'), :path => "#{collection_path}.xml", :method => :post)
assert_recognizes(options[:shallow_options].merge(:action => 'show', :id => '1', :format => 'xml'), :path => "#{member_path}.xml", :method => :get)
assert_recognizes(options[:shallow_options].merge(:action => 'edit', :id => '1', :format => 'xml'), :path => formatted_edit_member_path, :method => :get)
assert_recognizes(options[:shallow_options].merge(:action => 'update', :id => '1', :format => 'xml'), :path => "#{member_path}.xml", :method => :put)
assert_recognizes(options[:shallow_options].merge(:action => 'destroy', :id => '1', :format => 'xml'), :path => "#{member_path}.xml", :method => :delete)
- yield options[:options] if block_given?
+ yield route_options if block_given?
end
# test named routes like foo_path and foos_path map to the correct options.
@@ -1195,20 +1195,20 @@ class ResourcesTest < ActionController::TestCase
end
singular_name ||= controller_name.to_s.singularize
- options[:options] ||= {}
- options[:options][:controller] = options[:controller] || controller_name.to_s
+ route_options = (options[:options] ||= {}).dup
+ route_options[:controller] = options[:controller] || controller_name.to_s
if options[:shallow]
options[:shallow_options] ||= {}
- options[:shallow_options][:controller] = options[:options][:controller]
+ options[:shallow_options][:controller] = route_options[:controller]
else
- options[:shallow_options] = options[:options]
+ options[:shallow_options] = route_options
end
- @controller = "#{options[:options][:controller].camelize}Controller".constantize.new
+ @controller = "#{route_options[:controller].camelize}Controller".constantize.new
@controller.singleton_class.include(@routes.url_helpers)
- get :index, params: options[:options]
- options[:options].delete :action
+ get :index, params: route_options
+ route_options.delete :action
path = "#{options[:as] || controller_name}"
shallow_path = "/#{options[:shallow] ? options[:namespace] : options[:path_prefix]}#{path}"
@@ -1223,29 +1223,29 @@ class ResourcesTest < ActionController::TestCase
edit_action = options[:path_names][:edit] || "edit"
end
- assert_named_route "#{full_path}", "#{name_prefix}#{controller_name}_path", options[:options]
- assert_named_route "#{full_path}.xml", "#{name_prefix}#{controller_name}_path", options[:options].merge(:format => 'xml')
+ assert_named_route "#{full_path}", "#{name_prefix}#{controller_name}_path", route_options
+ assert_named_route "#{full_path}.xml", "#{name_prefix}#{controller_name}_path", route_options.merge(:format => 'xml')
assert_named_route "#{shallow_path}/1", "#{shallow_prefix}#{singular_name}_path", options[:shallow_options].merge(:id => '1')
assert_named_route "#{shallow_path}/1.xml", "#{shallow_prefix}#{singular_name}_path", options[:shallow_options].merge(:id => '1', :format => 'xml')
- assert_named_route "#{full_path}/#{new_action}", "new_#{name_prefix}#{singular_name}_path", options[:options]
- assert_named_route "#{full_path}/#{new_action}.xml", "new_#{name_prefix}#{singular_name}_path", options[:options].merge(:format => 'xml')
+ assert_named_route "#{full_path}/#{new_action}", "new_#{name_prefix}#{singular_name}_path", route_options
+ assert_named_route "#{full_path}/#{new_action}.xml", "new_#{name_prefix}#{singular_name}_path", route_options.merge(:format => 'xml')
assert_named_route "#{shallow_path}/1/#{edit_action}", "edit_#{shallow_prefix}#{singular_name}_path", options[:shallow_options].merge(:id => '1')
assert_named_route "#{shallow_path}/1/#{edit_action}.xml", "edit_#{shallow_prefix}#{singular_name}_path", options[:shallow_options].merge(:id => '1', :format => 'xml')
- yield options[:options] if block_given?
+ yield route_options if block_given?
end
def assert_singleton_routes_for(singleton_name, options = {})
- options[:options] ||= {}
- options[:options][:controller] = options[:controller] || singleton_name.to_s.pluralize
+ route_options = (options[:options] ||= {}).dup
+ route_options[:controller] = options[:controller] || singleton_name.to_s.pluralize
full_path = "/#{options[:path_prefix]}#{options[:as] || singleton_name}"
new_path = "#{full_path}/new"
edit_path = "#{full_path}/edit"
formatted_edit_path = "#{full_path}/edit.xml"
- with_options options[:options] do |controller|
+ with_options route_options do |controller|
controller.assert_routing full_path, :action => 'show'
controller.assert_routing new_path, :action => 'new'
controller.assert_routing edit_path, :action => 'edit'
@@ -1254,40 +1254,41 @@ class ResourcesTest < ActionController::TestCase
controller.assert_routing formatted_edit_path, :action => 'edit', :format => 'xml'
end
- assert_recognizes(options[:options].merge(:action => 'show'), :path => full_path, :method => :get)
- assert_recognizes(options[:options].merge(:action => 'new'), :path => new_path, :method => :get)
- assert_recognizes(options[:options].merge(:action => 'edit'), :path => edit_path, :method => :get)
- assert_recognizes(options[:options].merge(:action => 'create'), :path => full_path, :method => :post)
- assert_recognizes(options[:options].merge(:action => 'update'), :path => full_path, :method => :put)
- assert_recognizes(options[:options].merge(:action => 'destroy'), :path => full_path, :method => :delete)
+ assert_recognizes(route_options.merge(:action => 'show'), :path => full_path, :method => :get)
+ assert_recognizes(route_options.merge(:action => 'new'), :path => new_path, :method => :get)
+ assert_recognizes(route_options.merge(:action => 'edit'), :path => edit_path, :method => :get)
+ assert_recognizes(route_options.merge(:action => 'create'), :path => full_path, :method => :post)
+ assert_recognizes(route_options.merge(:action => 'update'), :path => full_path, :method => :put)
+ assert_recognizes(route_options.merge(:action => 'destroy'), :path => full_path, :method => :delete)
- assert_recognizes(options[:options].merge(:action => 'show', :format => 'xml'), :path => "#{full_path}.xml", :method => :get)
- assert_recognizes(options[:options].merge(:action => 'new', :format => 'xml'), :path => "#{new_path}.xml", :method => :get)
- assert_recognizes(options[:options].merge(:action => 'edit', :format => 'xml'), :path => formatted_edit_path, :method => :get)
- assert_recognizes(options[:options].merge(:action => 'create', :format => 'xml'), :path => "#{full_path}.xml", :method => :post)
- assert_recognizes(options[:options].merge(:action => 'update', :format => 'xml'), :path => "#{full_path}.xml", :method => :put)
- assert_recognizes(options[:options].merge(:action => 'destroy', :format => 'xml'), :path => "#{full_path}.xml", :method => :delete)
+ assert_recognizes(route_options.merge(:action => 'show', :format => 'xml'), :path => "#{full_path}.xml", :method => :get)
+ assert_recognizes(route_options.merge(:action => 'new', :format => 'xml'), :path => "#{new_path}.xml", :method => :get)
+ assert_recognizes(route_options.merge(:action => 'edit', :format => 'xml'), :path => formatted_edit_path, :method => :get)
+ assert_recognizes(route_options.merge(:action => 'create', :format => 'xml'), :path => "#{full_path}.xml", :method => :post)
+ assert_recognizes(route_options.merge(:action => 'update', :format => 'xml'), :path => "#{full_path}.xml", :method => :put)
+ assert_recognizes(route_options.merge(:action => 'destroy', :format => 'xml'), :path => "#{full_path}.xml", :method => :delete)
- yield options[:options] if block_given?
+ yield route_options if block_given?
end
def assert_singleton_named_routes_for(singleton_name, options = {})
- (options[:options] ||= {})[:controller] ||= singleton_name.to_s.pluralize
- @controller = "#{options[:options][:controller].camelize}Controller".constantize.new
+ route_options = (options[:options] ||= {}).dup
+ controller_name = route_options[:controller] || options[:controller] || singleton_name.to_s.pluralize
+ @controller = "#{controller_name.camelize}Controller".constantize.new
@controller.singleton_class.include(@routes.url_helpers)
- get :show, params: options[:options]
- options[:options].delete :action
+ get :show, params: route_options
+ route_options.delete :action
full_path = "/#{options[:path_prefix]}#{options[:as] || singleton_name}"
name_prefix = options[:name_prefix]
- assert_named_route "#{full_path}", "#{name_prefix}#{singleton_name}_path", options[:options]
- assert_named_route "#{full_path}.xml", "#{name_prefix}#{singleton_name}_path", options[:options].merge(:format => 'xml')
+ assert_named_route "#{full_path}", "#{name_prefix}#{singleton_name}_path", route_options
+ assert_named_route "#{full_path}.xml", "#{name_prefix}#{singleton_name}_path", route_options.merge(:format => 'xml')
- assert_named_route "#{full_path}/new", "new_#{name_prefix}#{singleton_name}_path", options[:options]
- assert_named_route "#{full_path}/new.xml", "new_#{name_prefix}#{singleton_name}_path", options[:options].merge(:format => 'xml')
- assert_named_route "#{full_path}/edit", "edit_#{name_prefix}#{singleton_name}_path", options[:options]
- assert_named_route "#{full_path}/edit.xml", "edit_#{name_prefix}#{singleton_name}_path", options[:options].merge(:format => 'xml')
+ assert_named_route "#{full_path}/new", "new_#{name_prefix}#{singleton_name}_path", route_options
+ assert_named_route "#{full_path}/new.xml", "new_#{name_prefix}#{singleton_name}_path", route_options.merge(:format => 'xml')
+ assert_named_route "#{full_path}/edit", "edit_#{name_prefix}#{singleton_name}_path", route_options
+ assert_named_route "#{full_path}/edit.xml", "edit_#{name_prefix}#{singleton_name}_path", route_options.merge(:format => 'xml')
end
def assert_named_route(expected, route, options)
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index 06ac80356b..feb3e7eab7 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -329,9 +329,11 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
def test_route_with_colon_first
rs.draw do
- get '/:controller/:action/:id', :action => 'index', :id => nil
- get ':url', :controller => 'tiny_url', :action => 'translate'
+ get '/:controller/:action/:id', action: 'index', id: nil
+ get ':url', controller: 'content', action: 'translate'
end
+
+ assert_equal({controller: 'content', action: 'translate', url: 'example'}, rs.recognize_path('/example'))
end
def test_route_with_regexp_for_controller
diff --git a/actionpack/test/dispatch/exception_wrapper_test.rb b/actionpack/test/dispatch/exception_wrapper_test.rb
index 7a29a7ff97..3dac582407 100644
--- a/actionpack/test/dispatch/exception_wrapper_test.rb
+++ b/actionpack/test/dispatch/exception_wrapper_test.rb
@@ -19,15 +19,13 @@ module ActionDispatch
setup do
Rails.stubs(:root).returns(Pathname.new('.'))
- cleaner = ActiveSupport::BacktraceCleaner.new
- cleaner.add_silencer { |line| line !~ /^lib/ }
-
- @environment = { 'action_dispatch.backtrace_cleaner' => cleaner }
+ @cleaner = ActiveSupport::BacktraceCleaner.new
+ @cleaner.add_silencer { |line| line !~ /^lib/ }
end
test '#source_extracts fetches source fragments for every backtrace entry' do
exception = TestError.new("lib/file.rb:42:in `index'")
- wrapper = ExceptionWrapper.new({}, exception)
+ wrapper = ExceptionWrapper.new(nil, exception)
wrapper.expects(:source_fragment).with('lib/file.rb', 42).returns('foo')
@@ -37,7 +35,7 @@ module ActionDispatch
test '#source_extracts works with Windows paths' do
exc = TestError.new("c:/path/to/rails/app/controller.rb:27:in 'index':")
- wrapper = ExceptionWrapper.new({}, exc)
+ wrapper = ExceptionWrapper.new(nil, exc)
wrapper.expects(:source_fragment).with('c:/path/to/rails/app/controller.rb', 27).returns('nothing')
assert_equal [ code: 'nothing', line_number: 27 ], wrapper.source_extracts
@@ -46,7 +44,7 @@ module ActionDispatch
test '#source_extracts works with non standard backtrace' do
exc = TestError.new('invalid')
- wrapper = ExceptionWrapper.new({}, exc)
+ wrapper = ExceptionWrapper.new(nil, exc)
wrapper.expects(:source_fragment).with('invalid', 0).returns('nothing')
assert_equal [ code: 'nothing', line_number: 0 ], wrapper.source_extracts
@@ -54,14 +52,14 @@ module ActionDispatch
test '#application_trace returns traces only from the application' do
exception = TestError.new(caller.prepend("lib/file.rb:42:in `index'"))
- wrapper = ExceptionWrapper.new(@environment, exception)
+ wrapper = ExceptionWrapper.new(@cleaner, exception)
assert_equal [ "lib/file.rb:42:in `index'" ], wrapper.application_trace
end
test '#application_trace cannot be nil' do
- nil_backtrace_wrapper = ExceptionWrapper.new(@environment, BadlyDefinedError.new)
- nil_cleaner_wrapper = ExceptionWrapper.new({}, BadlyDefinedError.new)
+ nil_backtrace_wrapper = ExceptionWrapper.new(@cleaner, BadlyDefinedError.new)
+ nil_cleaner_wrapper = ExceptionWrapper.new(nil, BadlyDefinedError.new)
assert_equal [], nil_backtrace_wrapper.application_trace
assert_equal [], nil_cleaner_wrapper.application_trace
@@ -69,14 +67,14 @@ module ActionDispatch
test '#framework_trace returns traces outside the application' do
exception = TestError.new(caller.prepend("lib/file.rb:42:in `index'"))
- wrapper = ExceptionWrapper.new(@environment, exception)
+ wrapper = ExceptionWrapper.new(@cleaner, exception)
assert_equal caller, wrapper.framework_trace
end
test '#framework_trace cannot be nil' do
- nil_backtrace_wrapper = ExceptionWrapper.new(@environment, BadlyDefinedError.new)
- nil_cleaner_wrapper = ExceptionWrapper.new({}, BadlyDefinedError.new)
+ nil_backtrace_wrapper = ExceptionWrapper.new(@cleaner, BadlyDefinedError.new)
+ nil_cleaner_wrapper = ExceptionWrapper.new(nil, BadlyDefinedError.new)
assert_equal [], nil_backtrace_wrapper.framework_trace
assert_equal [], nil_cleaner_wrapper.framework_trace
@@ -84,14 +82,14 @@ module ActionDispatch
test '#full_trace returns application and framework traces' do
exception = TestError.new(caller.prepend("lib/file.rb:42:in `index'"))
- wrapper = ExceptionWrapper.new(@environment, exception)
+ wrapper = ExceptionWrapper.new(@cleaner, exception)
assert_equal exception.backtrace, wrapper.full_trace
end
test '#full_trace cannot be nil' do
- nil_backtrace_wrapper = ExceptionWrapper.new(@environment, BadlyDefinedError.new)
- nil_cleaner_wrapper = ExceptionWrapper.new({}, BadlyDefinedError.new)
+ nil_backtrace_wrapper = ExceptionWrapper.new(@cleaner, BadlyDefinedError.new)
+ nil_cleaner_wrapper = ExceptionWrapper.new(nil, BadlyDefinedError.new)
assert_equal [], nil_backtrace_wrapper.full_trace
assert_equal [], nil_cleaner_wrapper.full_trace
@@ -99,7 +97,7 @@ module ActionDispatch
test '#traces returns every trace by category enumerated with an index' do
exception = TestError.new("lib/file.rb:42:in `index'", "/gems/rack.rb:43:in `index'")
- wrapper = ExceptionWrapper.new(@environment, exception)
+ wrapper = ExceptionWrapper.new(@cleaner, exception)
assert_equal({
'Application Trace' => [ id: 0, trace: "lib/file.rb:42:in `index'" ],
diff --git a/actionpack/test/dispatch/mapper_test.rb b/actionpack/test/dispatch/mapper_test.rb
index 889f9a4736..f35ffd8845 100644
--- a/actionpack/test/dispatch/mapper_test.rb
+++ b/actionpack/test/dispatch/mapper_test.rb
@@ -4,13 +4,6 @@ module ActionDispatch
module Routing
class MapperTest < ActiveSupport::TestCase
class FakeSet < ActionDispatch::Routing::RouteSet
- attr_reader :routes
- alias :set :routes
-
- def initialize
- @routes = []
- end
-
def resources_path_names
{}
end
@@ -19,16 +12,24 @@ module ActionDispatch
ActionDispatch::Request
end
- def add_route(*args)
- routes << args
+ def dispatcher_class
+ RouteSet::Dispatcher
+ end
+
+ def defaults
+ routes.map(&:defaults)
end
def conditions
- routes.map { |x| x[1] }
+ routes.map(&:constraints)
end
def requirements
- routes.map { |x| x[2] }
+ routes.map(&:path).map(&:requirements)
+ end
+
+ def asts
+ routes.map(&:path).map(&:spec)
end
end
@@ -36,18 +37,76 @@ module ActionDispatch
Mapper.new FakeSet.new
end
+ def test_scope_raises_on_anchor
+ fakeset = FakeSet.new
+ mapper = Mapper.new fakeset
+ assert_raises(ArgumentError) do
+ mapper.scope(anchor: false) do
+ end
+ end
+ end
+
+ def test_blows_up_without_via
+ fakeset = FakeSet.new
+ mapper = Mapper.new fakeset
+ assert_raises(ArgumentError) do
+ mapper.match '/', :to => 'posts#index', :as => :main
+ end
+ end
+
+ def test_unscoped_formatted
+ fakeset = FakeSet.new
+ mapper = Mapper.new fakeset
+ mapper.get '/foo', :to => 'posts#index', :as => :main, :format => true
+ assert_equal({:controller=>"posts", :action=>"index"},
+ fakeset.defaults.first)
+ assert_equal "/foo.:format", fakeset.asts.first.to_s
+ end
+
+ def test_scoped_formatted
+ fakeset = FakeSet.new
+ mapper = Mapper.new fakeset
+ mapper.scope(format: true) do
+ mapper.get '/foo', :to => 'posts#index', :as => :main
+ end
+ assert_equal({:controller=>"posts", :action=>"index"},
+ fakeset.defaults.first)
+ assert_equal "/foo.:format", fakeset.asts.first.to_s
+ end
+
+ def test_random_keys
+ fakeset = FakeSet.new
+ mapper = Mapper.new fakeset
+ mapper.scope(omg: :awesome) do
+ mapper.get '/', :to => 'posts#index', :as => :main
+ end
+ assert_equal({:omg=>:awesome, :controller=>"posts", :action=>"index"},
+ fakeset.defaults.first)
+ assert_equal(/^GET$/, fakeset.routes.first.verb)
+ end
+
def test_mapping_requirements
- options = { :controller => 'foo', :action => 'bar', :via => :get }
- m = Mapper::Mapping.build({}, FakeSet.new, '/store/:name(*rest)', nil, options)
- _, _, requirements, _ = m.to_route
- assert_equal(/.+?/, requirements[:rest])
+ options = { }
+ scope = Mapper::Scope.new({})
+ ast = Journey::Parser.parse '/store/:name(*rest)'
+ m = Mapper::Mapping.build(scope, FakeSet.new, ast, 'foo', 'bar', nil, [:get], nil, {}, true, options)
+ assert_equal(/.+?/, m.requirements[:rest])
+ end
+
+ def test_via_scope
+ fakeset = FakeSet.new
+ mapper = Mapper.new fakeset
+ mapper.scope(via: :put) do
+ mapper.match '/', :to => 'posts#index', :as => :main
+ end
+ assert_equal(/^PUT$/, fakeset.routes.first.verb)
end
def test_map_slash
fakeset = FakeSet.new
mapper = Mapper.new fakeset
mapper.get '/', :to => 'posts#index', :as => :main
- assert_equal '/', fakeset.conditions.first[:path_info]
+ assert_equal '/', fakeset.asts.first.to_s
end
def test_map_more_slashes
@@ -56,14 +115,14 @@ module ActionDispatch
# FIXME: is this a desired behavior?
mapper.get '/one/two/', :to => 'posts#index', :as => :main
- assert_equal '/one/two(.:format)', fakeset.conditions.first[:path_info]
+ assert_equal '/one/two(.:format)', fakeset.asts.first.to_s
end
def test_map_wildcard
fakeset = FakeSet.new
mapper = Mapper.new fakeset
mapper.get '/*path', :to => 'pages#show'
- assert_equal '/*path(.:format)', fakeset.conditions.first[:path_info]
+ assert_equal '/*path(.:format)', fakeset.asts.first.to_s
assert_equal(/.+?/, fakeset.requirements.first[:path])
end
@@ -71,7 +130,7 @@ module ActionDispatch
fakeset = FakeSet.new
mapper = Mapper.new fakeset
mapper.get '/*path/foo/:bar', :to => 'pages#show'
- assert_equal '/*path/foo/:bar(.:format)', fakeset.conditions.first[:path_info]
+ assert_equal '/*path/foo/:bar(.:format)', fakeset.asts.first.to_s
assert_equal(/.+?/, fakeset.requirements.first[:path])
end
@@ -79,7 +138,7 @@ module ActionDispatch
fakeset = FakeSet.new
mapper = Mapper.new fakeset
mapper.get '/*foo/*bar', :to => 'pages#show'
- assert_equal '/*foo/*bar(.:format)', fakeset.conditions.first[:path_info]
+ assert_equal '/*foo/*bar(.:format)', fakeset.asts.first.to_s
assert_equal(/.+?/, fakeset.requirements.first[:foo])
assert_equal(/.+?/, fakeset.requirements.first[:bar])
end
@@ -88,7 +147,7 @@ module ActionDispatch
fakeset = FakeSet.new
mapper = Mapper.new fakeset
mapper.get '/*path', :to => 'pages#show', :format => false
- assert_equal '/*path', fakeset.conditions.first[:path_info]
+ assert_equal '/*path', fakeset.asts.first.to_s
assert_nil fakeset.requirements.first[:path]
end
@@ -96,7 +155,7 @@ module ActionDispatch
fakeset = FakeSet.new
mapper = Mapper.new fakeset
mapper.get '/*path', :to => 'pages#show', :format => true
- assert_equal '/*path.:format', fakeset.conditions.first[:path_info]
+ assert_equal '/*path.:format', fakeset.asts.first.to_s
end
def test_raising_helpful_error_on_invalid_arguments
diff --git a/actionpack/test/dispatch/middleware_stack/middleware_test.rb b/actionpack/test/dispatch/middleware_stack/middleware_test.rb
deleted file mode 100644
index 9607f026db..0000000000
--- a/actionpack/test/dispatch/middleware_stack/middleware_test.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-require 'abstract_unit'
-require 'action_dispatch/middleware/stack'
-
-module ActionDispatch
- class MiddlewareStack
- class MiddlewareTest < ActiveSupport::TestCase
- class Omg; end
-
- {
- 'concrete' => Omg,
- 'anonymous' => Class.new
- }.each do |name, klass|
-
- define_method("test_#{name}_klass") do
- mw = Middleware.new klass
- assert_equal klass, mw.klass
- end
-
- define_method("test_#{name}_==") do
- mw1 = Middleware.new klass
- mw2 = Middleware.new klass
- assert_equal mw1, mw2
- end
-
- end
-
- def test_string_class
- mw = Middleware.new Omg.name
- assert_equal Omg, mw.klass
- end
-
- def test_double_equal_works_with_classes
- k = Class.new
- mw = Middleware.new k
- assert_operator mw, :==, k
-
- result = mw != Class.new
- assert result, 'middleware should not equal other anon class'
- end
-
- def test_double_equal_works_with_strings
- mw = Middleware.new Omg
- assert_operator mw, :==, Omg.name
- end
-
- def test_double_equal_normalizes_strings
- mw = Middleware.new Omg
- assert_operator mw, :==, "::#{Omg.name}"
- end
-
- def test_middleware_loads_classnames_from_cache
- mw = Class.new(Middleware) {
- attr_accessor :classcache
- }.new(Omg.name)
-
- fake_cache = { mw.name => Omg }
- mw.classcache = fake_cache
-
- assert_equal Omg, mw.klass
-
- fake_cache[mw.name] = Middleware
- assert_equal Middleware, mw.klass
- end
-
- def test_middleware_always_returns_class
- mw = Class.new(Middleware) {
- attr_accessor :classcache
- }.new(Omg)
-
- fake_cache = { mw.name => Middleware }
- mw.classcache = fake_cache
-
- assert_equal Omg, mw.klass
- end
- end
- end
-end
diff --git a/actionpack/test/dispatch/middleware_stack_test.rb b/actionpack/test/dispatch/middleware_stack_test.rb
index 948a690979..33aa616474 100644
--- a/actionpack/test/dispatch/middleware_stack_test.rb
+++ b/actionpack/test/dispatch/middleware_stack_test.rb
@@ -4,6 +4,7 @@ class MiddlewareStackTest < ActiveSupport::TestCase
class FooMiddleware; end
class BarMiddleware; end
class BazMiddleware; end
+ class HiyaMiddleware; end
class BlockMiddleware
attr_reader :block
def initialize(&block)
@@ -17,6 +18,20 @@ class MiddlewareStackTest < ActiveSupport::TestCase
@stack.use BarMiddleware
end
+ def test_delete_with_string_is_deprecated
+ assert_deprecated do
+ assert_difference "@stack.size", -1 do
+ @stack.delete FooMiddleware.name
+ end
+ end
+ end
+
+ def test_delete_works
+ assert_difference "@stack.size", -1 do
+ @stack.delete FooMiddleware
+ end
+ end
+
test "use should push middleware as class onto the stack" do
assert_difference "@stack.size" do
@stack.use BazMiddleware
@@ -25,17 +40,21 @@ class MiddlewareStackTest < ActiveSupport::TestCase
end
test "use should push middleware as a string onto the stack" do
- assert_difference "@stack.size" do
- @stack.use "MiddlewareStackTest::BazMiddleware"
+ assert_deprecated do
+ assert_difference "@stack.size" do
+ @stack.use "MiddlewareStackTest::BazMiddleware"
+ end
+ assert_equal BazMiddleware, @stack.last.klass
end
- assert_equal BazMiddleware, @stack.last.klass
end
test "use should push middleware as a symbol onto the stack" do
- assert_difference "@stack.size" do
- @stack.use :"MiddlewareStackTest::BazMiddleware"
+ assert_deprecated do
+ assert_difference "@stack.size" do
+ @stack.use :"MiddlewareStackTest::BazMiddleware"
+ end
+ assert_equal BazMiddleware, @stack.last.klass
end
- assert_equal BazMiddleware, @stack.last.klass
end
test "use should push middleware class with arguments onto the stack" do
@@ -88,30 +107,28 @@ class MiddlewareStackTest < ActiveSupport::TestCase
end
test "unshift adds a new middleware at the beginning of the stack" do
- @stack.unshift :"MiddlewareStackTest::BazMiddleware"
- assert_equal BazMiddleware, @stack.first.klass
+ assert_deprecated do
+ @stack.unshift :"MiddlewareStackTest::BazMiddleware"
+ assert_equal BazMiddleware, @stack.first.klass
+ end
end
test "raise an error on invalid index" do
assert_raise RuntimeError do
- @stack.insert("HiyaMiddleware", BazMiddleware)
+ @stack.insert(HiyaMiddleware, BazMiddleware)
end
assert_raise RuntimeError do
- @stack.insert_after("HiyaMiddleware", BazMiddleware)
+ @stack.insert_after(HiyaMiddleware, BazMiddleware)
end
end
test "lazy evaluates middleware class" do
- assert_difference "@stack.size" do
- @stack.use "MiddlewareStackTest::BazMiddleware"
+ assert_deprecated do
+ assert_difference "@stack.size" do
+ @stack.use "MiddlewareStackTest::BazMiddleware"
+ end
+ assert_equal BazMiddleware, @stack.last.klass
end
- assert_equal BazMiddleware, @stack.last.klass
- end
-
- test "lazy compares so unloaded constants are not loaded" do
- @stack.use "UnknownMiddleware"
- @stack.use :"MiddlewareStackTest::BazMiddleware"
- assert @stack.include?("::MiddlewareStackTest::BazMiddleware")
end
end
diff --git a/actionpack/test/dispatch/mount_test.rb b/actionpack/test/dispatch/mount_test.rb
index 6a439be2b5..d027f09762 100644
--- a/actionpack/test/dispatch/mount_test.rb
+++ b/actionpack/test/dispatch/mount_test.rb
@@ -49,7 +49,7 @@ class TestRoutingMount < ActionDispatch::IntegrationTest
def test_app_name_is_properly_generated_when_engine_is_mounted_in_resources
assert Router.mounted_helpers.method_defined?(:user_fake_mounted_at_resource),
"A mounted helper should be defined with a parent's prefix"
- assert Router.named_routes.routes[:user_fake_mounted_at_resource],
+ assert Router.named_routes.key?(:user_fake_mounted_at_resource),
"A named route should be defined with a parent's prefix"
end
diff --git a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
index 939a771c65..b36fbd3c76 100644
--- a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
@@ -63,6 +63,17 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest
assert_equal 'contents', file.read
end
+ test "parses utf8 filename with percent character" do
+ params = parse_multipart('utf8_filename')
+ assert_equal %w(file foo), params.keys.sort
+ assert_equal 'bar', params['foo']
+
+ file = params['file']
+ assert_equal 'ファイル%名.txt', file.original_filename
+ assert_equal "text/plain", file.content_type
+ assert_equal 'contents', file.read
+ end
+
test "parses boundary problem file" do
params = parse_multipart('boundary_problem_file')
assert_equal %w(file foo), params.keys.sort
diff --git a/actionpack/test/dispatch/request/session_test.rb b/actionpack/test/dispatch/request/session_test.rb
index 10fb04e230..8bcc07dd04 100644
--- a/actionpack/test/dispatch/request/session_test.rb
+++ b/actionpack/test/dispatch/request/session_test.rb
@@ -7,7 +7,7 @@ module ActionDispatch
def test_create_adds_itself_to_env
env = {}
s = Session.create(store, env, {})
- assert_equal s, env[Rack::Session::Abstract::ENV_SESSION_KEY]
+ assert_equal s, env[Rack::RACK_SESSION]
end
def test_to_hash
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index b18a9ab647..332a550de0 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -168,12 +168,10 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
def test_session_singleton_resource_for_api_app
- self.class.stub_controllers do |_|
- config = ActionDispatch::Routing::RouteSet::Config.new
- config.api_only = true
-
- routes = ActionDispatch::Routing::RouteSet.new(config)
+ config = ActionDispatch::Routing::RouteSet::Config.new
+ config.api_only = true
+ self.class.stub_controllers(config) do |routes|
routes.draw do
resource :session do
get :create
@@ -363,9 +361,12 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
def test_pagemarks
+ tc = self
draw do
scope "pagemark", :controller => "pagemarks", :as => :pagemark do
- get "new", :path => "build"
+ tc.assert_deprecated do
+ get "new", :path => "build"
+ end
post "create", :as => ""
put "update"
get "remove", :action => :destroy, :as => :remove
@@ -550,11 +551,10 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
def test_projects_for_api_app
- self.class.stub_controllers do |_|
- config = ActionDispatch::Routing::RouteSet::Config.new
- config.api_only = true
+ config = ActionDispatch::Routing::RouteSet::Config.new
+ config.api_only = true
- routes = ActionDispatch::Routing::RouteSet.new(config)
+ self.class.stub_controllers(config) do |routes|
routes.draw do
resources :projects, controller: :project
end
@@ -3621,7 +3621,7 @@ private
end
class TestAltApp < ActionDispatch::IntegrationTest
- class AltRequest
+ class AltRequest < ActionDispatch::Request
attr_accessor :path_parameters, :path_info, :script_name
attr_reader :env
@@ -3630,6 +3630,7 @@ class TestAltApp < ActionDispatch::IntegrationTest
@env = env
@path_info = "/"
@script_name = ""
+ super
end
def request_method
diff --git a/actionpack/test/dispatch/session/cache_store_test.rb b/actionpack/test/dispatch/session/cache_store_test.rb
index e6a70358c8..dbb996973d 100644
--- a/actionpack/test/dispatch/session/cache_store_test.rb
+++ b/actionpack/test/dispatch/session/cache_store_test.rb
@@ -170,7 +170,7 @@ class CacheStoreTest < ActionDispatch::IntegrationTest
@app = self.class.build_app(set) do |middleware|
@cache = ActiveSupport::Cache::MemoryStore.new
middleware.use ActionDispatch::Session::CacheStore, :key => '_session_id', :cache => @cache
- middleware.delete "ActionDispatch::ShowExceptions"
+ middleware.delete ActionDispatch::ShowExceptions
end
yield
diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb
index 715eb90566..e432c65c62 100644
--- a/actionpack/test/dispatch/session/cookie_store_test.rb
+++ b/actionpack/test/dispatch/session/cookie_store_test.rb
@@ -348,7 +348,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
@app = self.class.build_app(set) do |middleware|
middleware.use ActionDispatch::Session::CookieStore, options
- middleware.delete "ActionDispatch::ShowExceptions"
+ middleware.delete ActionDispatch::ShowExceptions
end
yield
diff --git a/actionpack/test/dispatch/session/mem_cache_store_test.rb b/actionpack/test/dispatch/session/mem_cache_store_test.rb
index 6e9107ecbf..3fed9bad4f 100644
--- a/actionpack/test/dispatch/session/mem_cache_store_test.rb
+++ b/actionpack/test/dispatch/session/mem_cache_store_test.rb
@@ -192,7 +192,7 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest
@app = self.class.build_app(set) do |middleware|
middleware.use ActionDispatch::Session::MemCacheStore, :key => '_session_id', :namespace => "mem_cache_store_test:#{SecureRandom.hex(10)}"
- middleware.delete "ActionDispatch::ShowExceptions"
+ middleware.delete ActionDispatch::ShowExceptions
end
yield
diff --git a/actionpack/test/dispatch/session/test_session_test.rb b/actionpack/test/dispatch/session/test_session_test.rb
index d30461a623..59c030176b 100644
--- a/actionpack/test/dispatch/session/test_session_test.rb
+++ b/actionpack/test/dispatch/session/test_session_test.rb
@@ -40,4 +40,14 @@ class ActionController::TestSessionTest < ActiveSupport::TestCase
assert_equal %w(one two), session.keys
assert_equal %w(1 2), session.values
end
+
+ def test_fetch_returns_default
+ session = ActionController::TestSession.new(one: '1')
+ assert_equal('2', session.fetch(:two, '2'))
+ end
+
+ def test_fetch_returns_block_value
+ session = ActionController::TestSession.new(one: '1')
+ assert_equal(2, session.fetch('2') { |key| key.to_i })
+ end
end
diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb
index 95971b3a0e..13dec8b618 100644
--- a/actionpack/test/dispatch/static_test.rb
+++ b/actionpack/test/dispatch/static_test.rb
@@ -156,7 +156,7 @@ module StaticTests
def test_does_not_modify_path_info
file_name = "/gzip/application-a71b3024f80aea3181c09774ca17e712.js"
- env = {'PATH_INFO' => file_name, 'HTTP_ACCEPT_ENCODING' => 'gzip'}
+ env = {'PATH_INFO' => file_name, 'HTTP_ACCEPT_ENCODING' => 'gzip', "REQUEST_METHOD" => 'POST'}
@app.call(env)
assert_equal file_name, env['PATH_INFO']
end
diff --git a/actionpack/test/fixtures/multipart/utf8_filename b/actionpack/test/fixtures/multipart/utf8_filename
new file mode 100644
index 0000000000..60738d53b0
--- /dev/null
+++ b/actionpack/test/fixtures/multipart/utf8_filename
@@ -0,0 +1,10 @@
+--AaB03x
+Content-Disposition: form-data; name="foo"
+
+bar
+--AaB03x
+Content-Disposition: form-data; name="file"; filename="ファイル%名.txt"
+Content-Type: text/plain
+
+contents
+--AaB03x--
diff --git a/actionpack/test/journey/nodes/symbol_test.rb b/actionpack/test/journey/nodes/symbol_test.rb
index d411a5018a..adf85b860c 100644
--- a/actionpack/test/journey/nodes/symbol_test.rb
+++ b/actionpack/test/journey/nodes/symbol_test.rb
@@ -5,7 +5,7 @@ module ActionDispatch
module Nodes
class TestSymbol < ActiveSupport::TestCase
def test_default_regexp?
- sym = Symbol.new nil
+ sym = Symbol.new "foo"
assert sym.default_regexp?
sym.regexp = nil
diff --git a/actionpack/test/journey/path/pattern_test.rb b/actionpack/test/journey/path/pattern_test.rb
index 6939b426f6..72858f5eda 100644
--- a/actionpack/test/journey/path/pattern_test.rb
+++ b/actionpack/test/journey/path/pattern_test.rb
@@ -4,6 +4,8 @@ module ActionDispatch
module Journey
module Path
class TestPattern < ActiveSupport::TestCase
+ SEPARATORS = ["/", ".", "?"].join
+
x = /.+/
{
'/:controller(/:action)' => %r{\A/(#{x})(?:/([^/.?]+))?\Z},
@@ -19,12 +21,12 @@ module ActionDispatch
'/:foo|*bar' => %r{\A/(?:([^/.?]+)|(.+))\Z},
}.each do |path, expected|
define_method(:"test_to_regexp_#{path}") do
- strexp = Router::Strexp.build(
+ path = Pattern.build(
path,
{ :controller => /.+/ },
- ["/", ".", "?"]
+ SEPARATORS,
+ true
)
- path = Pattern.new strexp
assert_equal(expected, path.to_regexp)
end
end
@@ -43,13 +45,12 @@ module ActionDispatch
'/:foo|*bar' => %r{\A/(?:([^/.?]+)|(.+))},
}.each do |path, expected|
define_method(:"test_to_non_anchored_regexp_#{path}") do
- strexp = Router::Strexp.build(
+ path = Pattern.build(
path,
{ :controller => /.+/ },
- ["/", ".", "?"],
+ SEPARATORS,
false
)
- path = Pattern.new strexp
assert_equal(expected, path.to_regexp)
end
end
@@ -67,27 +68,27 @@ module ActionDispatch
'/:controller/*foo/bar' => %w{ controller foo },
}.each do |path, expected|
define_method(:"test_names_#{path}") do
- strexp = Router::Strexp.build(
+ path = Pattern.build(
path,
{ :controller => /.+/ },
- ["/", ".", "?"]
+ SEPARATORS,
+ true
)
- path = Pattern.new strexp
assert_equal(expected, path.names)
end
end
def test_to_regexp_with_extended_group
- strexp = Router::Strexp.build(
+ path = Pattern.build(
'/page/:name',
{ :name => /
#ROFL
(tender|love
#MAO
)/x },
- ["/", ".", "?"]
+ SEPARATORS,
+ true
)
- path = Pattern.new strexp
assert_match(path, '/page/tender')
assert_match(path, '/page/love')
assert_no_match(path, '/page/loving')
@@ -105,23 +106,23 @@ module ActionDispatch
end
def test_to_regexp_match_non_optional
- strexp = Router::Strexp.build(
+ path = Pattern.build(
'/:name',
{ :name => /\d+/ },
- ["/", ".", "?"]
+ SEPARATORS,
+ true
)
- path = Pattern.new strexp
assert_match(path, '/123')
assert_no_match(path, '/')
end
def test_to_regexp_with_group
- strexp = Router::Strexp.build(
+ path = Pattern.build(
'/page/:name',
{ :name => /(tender|love)/ },
- ["/", ".", "?"]
+ SEPARATORS,
+ true
)
- path = Pattern.new strexp
assert_match(path, '/page/tender')
assert_match(path, '/page/love')
assert_no_match(path, '/page/loving')
@@ -129,15 +130,13 @@ module ActionDispatch
def test_ast_sets_regular_expressions
requirements = { :name => /(tender|love)/, :value => /./ }
- strexp = Router::Strexp.build(
+ path = Pattern.build(
'/page/:name/:value',
requirements,
- ["/", ".", "?"]
+ SEPARATORS,
+ true
)
- assert_equal requirements, strexp.requirements
-
- path = Pattern.new strexp
nodes = path.ast.grep(Nodes::Symbol)
assert_equal 2, nodes.length
nodes.each do |node|
@@ -146,24 +145,24 @@ module ActionDispatch
end
def test_match_data_with_group
- strexp = Router::Strexp.build(
+ path = Pattern.build(
'/page/:name',
{ :name => /(tender|love)/ },
- ["/", ".", "?"]
+ SEPARATORS,
+ true
)
- path = Pattern.new strexp
match = path.match '/page/tender'
assert_equal 'tender', match[1]
assert_equal 2, match.length
end
def test_match_data_with_multi_group
- strexp = Router::Strexp.build(
+ path = Pattern.build(
'/page/:name/:id',
{ :name => /t(((ender|love)))()/ },
- ["/", ".", "?"]
+ SEPARATORS,
+ true
)
- path = Pattern.new strexp
match = path.match '/page/tender/10'
assert_equal 'tender', match[1]
assert_equal '10', match[2]
@@ -173,30 +172,29 @@ module ActionDispatch
def test_star_with_custom_re
z = /\d+/
- strexp = Router::Strexp.build(
+ path = Pattern.build(
'/page/*foo',
{ :foo => z },
- ["/", ".", "?"]
+ SEPARATORS,
+ true
)
- path = Pattern.new strexp
assert_equal(%r{\A/page/(#{z})\Z}, path.to_regexp)
end
def test_insensitive_regexp_with_group
- strexp = Router::Strexp.build(
+ path = Pattern.build(
'/page/:name/aaron',
{ :name => /(tender|love)/i },
- ["/", ".", "?"]
+ SEPARATORS,
+ true
)
- path = Pattern.new strexp
assert_match(path, '/page/TENDER/aaron')
assert_match(path, '/page/loVE/aaron')
assert_no_match(path, '/page/loVE/AAron')
end
def test_to_regexp_with_strexp
- strexp = Router::Strexp.build('/:controller', { }, ["/", ".", "?"])
- path = Pattern.new strexp
+ path = Pattern.build('/:controller', { }, SEPARATORS, true)
x = %r{\A/([^/.?]+)\Z}
assert_equal(x.source, path.source)
diff --git a/actionpack/test/journey/route_test.rb b/actionpack/test/journey/route_test.rb
index eff96a0abc..22c3b8113d 100644
--- a/actionpack/test/journey/route_test.rb
+++ b/actionpack/test/journey/route_test.rb
@@ -7,7 +7,7 @@ module ActionDispatch
app = Object.new
path = Path::Pattern.from_string '/:controller(/:action(/:id(.:format)))'
defaults = {}
- route = Route.new("name", app, path, {}, [], defaults)
+ route = Route.build("name", app, path, {}, [], defaults)
assert_equal app, route.app
assert_equal path, route.path
@@ -18,7 +18,7 @@ module ActionDispatch
app = Object.new
path = Path::Pattern.from_string '/:controller(/:action(/:id(.:format)))'
defaults = {}
- route = Route.new("name", app, path, {}, [], defaults)
+ route = Route.build("name", app, path, {}, [], defaults)
route.ast.grep(Nodes::Terminal).each do |node|
assert_equal route, node.memo
@@ -26,30 +26,29 @@ module ActionDispatch
end
def test_path_requirements_override_defaults
- strexp = Router::Strexp.build(':name', { name: /love/ }, ['/'])
- path = Path::Pattern.new strexp
+ path = Path::Pattern.build(':name', { name: /love/ }, '/', true)
defaults = { name: 'tender' }
- route = Route.new('name', nil, path, nil, [], defaults)
+ route = Route.build('name', nil, path, {}, [], defaults)
assert_equal(/love/, route.requirements[:name])
end
def test_ip_address
path = Path::Pattern.from_string '/messages/:id(.:format)'
- route = Route.new("name", nil, path, {:ip => '192.168.1.1'}, [],
+ route = Route.build("name", nil, path, {:ip => '192.168.1.1'}, [],
{ :controller => 'foo', :action => 'bar' })
assert_equal '192.168.1.1', route.ip
end
def test_default_ip
path = Path::Pattern.from_string '/messages/:id(.:format)'
- route = Route.new("name", nil, path, {}, [],
+ route = Route.build("name", nil, path, {}, [],
{ :controller => 'foo', :action => 'bar' })
assert_equal(//, route.ip)
end
def test_format_with_star
path = Path::Pattern.from_string '/:controller/*extra'
- route = Route.new("name", nil, path, {}, [],
+ route = Route.build("name", nil, path, {}, [],
{ :controller => 'foo', :action => 'bar' })
assert_equal '/foo/himom', route.format({
:controller => 'foo',
@@ -59,7 +58,7 @@ module ActionDispatch
def test_connects_all_match
path = Path::Pattern.from_string '/:controller(/:action(/:id(.:format)))'
- route = Route.new("name", nil, path, {:action => 'bar'}, [], { :controller => 'foo' })
+ route = Route.build("name", nil, path, {:action => 'bar'}, [], { :controller => 'foo' })
assert_equal '/foo/bar/10', route.format({
:controller => 'foo',
@@ -70,21 +69,21 @@ module ActionDispatch
def test_extras_are_not_included_if_optional
path = Path::Pattern.from_string '/page/:id(/:action)'
- route = Route.new("name", nil, path, { }, [], { :action => 'show' })
+ route = Route.build("name", nil, path, { }, [], { :action => 'show' })
assert_equal '/page/10', route.format({ :id => 10 })
end
def test_extras_are_not_included_if_optional_with_parameter
path = Path::Pattern.from_string '(/sections/:section)/pages/:id'
- route = Route.new("name", nil, path, { }, [], { :action => 'show' })
+ route = Route.build("name", nil, path, { }, [], { :action => 'show' })
assert_equal '/pages/10', route.format({:id => 10})
end
def test_extras_are_not_included_if_optional_parameter_is_nil
path = Path::Pattern.from_string '(/sections/:section)/pages/:id'
- route = Route.new("name", nil, path, { }, [], { :action => 'show' })
+ route = Route.build("name", nil, path, { }, [], { :action => 'show' })
assert_equal '/pages/10', route.format({:id => 10, :section => nil})
end
@@ -94,10 +93,10 @@ module ActionDispatch
defaults = {:controller=>"pages", :action=>"show"}
path = Path::Pattern.from_string "/page/:id(/:action)(.:format)"
- specific = Route.new "name", nil, path, constraints, [:controller, :action], defaults
+ specific = Route.build "name", nil, path, constraints, [:controller, :action], defaults
path = Path::Pattern.from_string "/:controller(/:action(/:id))(.:format)"
- generic = Route.new "name", nil, path, constraints, [], {}
+ generic = Route.build "name", nil, path, constraints, [], {}
knowledge = {:id=>20, :controller=>"pages", :action=>"show"}
diff --git a/actionpack/test/journey/router_test.rb b/actionpack/test/journey/router_test.rb
index 802fb93c69..d512dae4e7 100644
--- a/actionpack/test/journey/router_test.rb
+++ b/actionpack/test/journey/router_test.rb
@@ -4,138 +4,43 @@ require 'abstract_unit'
module ActionDispatch
module Journey
class TestRouter < ActiveSupport::TestCase
- attr_reader :routes
+ attr_reader :routes, :mapper
def setup
@app = Routing::RouteSet::Dispatcher.new({})
- @routes = Routes.new
- @router = Router.new(@routes)
- @formatter = Formatter.new(@routes)
- end
-
- class FakeRequestFeeler < Struct.new(:env, :called)
- def new env
- self.env = env
- self
- end
-
- def hello
- self.called = true
- 'world'
- end
-
- def path_info; env['PATH_INFO']; end
- def request_method; env['REQUEST_METHOD']; end
- def ip; env['REMOTE_ADDR']; end
+ @route_set = ActionDispatch::Routing::RouteSet.new
+ @routes = @route_set.router.routes
+ @router = @route_set.router
+ @formatter = @route_set.formatter
+ @mapper = ActionDispatch::Routing::Mapper.new @route_set
end
def test_dashes
- router = Router.new(routes)
-
- exp = Router::Strexp.build '/foo-bar-baz', {}, ['/.?']
- path = Path::Pattern.new exp
-
- routes.add_route nil, path, {}, [], {:id => nil}, {}
+ mapper.get '/foo-bar-baz', to: 'foo#bar'
env = rails_env 'PATH_INFO' => '/foo-bar-baz'
called = false
- router.recognize(env) do |r, params|
+ @router.recognize(env) do |r, params|
called = true
end
assert called
end
def test_unicode
- router = Router.new(routes)
+ mapper.get '/ほげ', to: 'foo#bar'
#match the escaped version of /ほげ
- exp = Router::Strexp.build '/%E3%81%BB%E3%81%92', {}, ['/.?']
- path = Path::Pattern.new exp
-
- routes.add_route nil, path, {}, [], {:id => nil}, {}
-
env = rails_env 'PATH_INFO' => '/%E3%81%BB%E3%81%92'
called = false
- router.recognize(env) do |r, params|
+ @router.recognize(env) do |r, params|
called = true
end
assert called
end
- def test_request_class_and_requirements_success
- klass = FakeRequestFeeler.new nil
- router = Router.new(routes)
-
- requirements = { :hello => /world/ }
-
- exp = Router::Strexp.build '/foo(/:id)', {}, ['/.?']
- path = Path::Pattern.new exp
-
- routes.add_route nil, path, requirements, [], {:id => nil}, {}
-
- env = rails_env({'PATH_INFO' => '/foo/10'}, klass)
- router.recognize(env) do |r, params|
- assert_equal({:id => '10'}, params)
- end
-
- assert klass.called, 'hello should have been called'
- assert_equal env.env, klass.env
- end
-
- def test_request_class_and_requirements_fail
- klass = FakeRequestFeeler.new nil
- router = Router.new(routes)
-
- requirements = { :hello => /mom/ }
-
- exp = Router::Strexp.build '/foo(/:id)', {}, ['/.?']
- path = Path::Pattern.new exp
-
- router.routes.add_route nil, path, requirements, [], {:id => nil}, {}
-
- env = rails_env({'PATH_INFO' => '/foo/10'}, klass)
- router.recognize(env) do |r, params|
- flunk 'route should not be found'
- end
-
- assert klass.called, 'hello should have been called'
- assert_equal env.env, klass.env
- end
-
- class CustomPathRequest < ActionDispatch::Request
- def path_info
- env['custom.path_info']
- end
-
- def path_info=(x)
- env['custom.path_info'] = x
- end
- end
-
- def test_request_class_overrides_path_info
- router = Router.new(routes)
-
- exp = Router::Strexp.build '/bar', {}, ['/.?']
- path = Path::Pattern.new exp
-
- routes.add_route nil, path, {}, [], {}, {}
-
- env = rails_env({'PATH_INFO' => '/foo',
- 'custom.path_info' => '/bar'}, CustomPathRequest)
-
- recognized = false
- router.recognize(env) do |r, params|
- recognized = true
- end
-
- assert recognized, "route should have been recognized"
- end
-
def test_regexp_first_precedence
- add_routes @router, [
- Router::Strexp.build("/whois/:domain", {:domain => /\w+\.[\w\.]+/}, ['/', '.', '?']),
- Router::Strexp.build("/whois/:id(.:format)", {}, ['/', '.', '?'])
- ]
+ mapper.get "/whois/:domain", :domain => /\w+\.[\w\.]+/, to: "foo#bar"
+ mapper.get "/whois/:id(.:format)", to: "foo#baz"
env = rails_env 'PATH_INFO' => '/whois/example.com'
@@ -147,25 +52,21 @@ module ActionDispatch
r = list.first
- assert_equal '/whois/:domain', r.path.spec.to_s
+ assert_equal '/whois/:domain(.:format)', r.path.spec.to_s
end
def test_required_parts_verified_are_anchored
- add_routes @router, [
- Router::Strexp.build("/foo/:id", { :id => /\d/ }, ['/', '.', '?'], false)
- ]
+ mapper.get "/foo/:id", :id => /\d/, anchor: false, to: "foo#bar"
assert_raises(ActionController::UrlGenerationError) do
- @formatter.generate(nil, { :id => '10' }, { })
+ @formatter.generate(nil, { :controller => "foo", :action => "bar", :id => '10' }, { })
end
end
def test_required_parts_are_verified_when_building
- add_routes @router, [
- Router::Strexp.build("/foo/:id", { :id => /\d+/ }, ['/', '.', '?'], false)
- ]
+ mapper.get "/foo/:id", :id => /\d+/, anchor: false, to: "foo#bar"
- path, _ = @formatter.generate(nil, { :id => '10' }, { })
+ path, _ = @formatter.generate(nil, { :controller => "foo", :action => "bar", :id => '10' }, { })
assert_equal '/foo/10', path
assert_raises(ActionController::UrlGenerationError) do
@@ -174,25 +75,22 @@ module ActionDispatch
end
def test_only_required_parts_are_verified
- add_routes @router, [
- Router::Strexp.build("/foo(/:id)", {:id => /\d/}, ['/', '.', '?'], false)
- ]
+ mapper.get "/foo(/:id)", :id => /\d/, :to => "foo#bar"
- path, _ = @formatter.generate(nil, { :id => '10' }, { })
+ path, _ = @formatter.generate(nil, { :controller => "foo", :action => "bar", :id => '10' }, { })
assert_equal '/foo/10', path
- path, _ = @formatter.generate(nil, { }, { })
+ path, _ = @formatter.generate(nil, { :controller => "foo", :action => "bar" }, { })
assert_equal '/foo', path
- path, _ = @formatter.generate(nil, { :id => 'aa' }, { })
+ path, _ = @formatter.generate(nil, { :controller => "foo", :action => "bar", :id => 'aa' }, { })
assert_equal '/foo/aa', path
end
def test_knows_what_parts_are_missing_from_named_route
route_name = "gorby_thunderhorse"
- pattern = Router::Strexp.build("/foo/:id", { :id => /\d+/ }, ['/', '.', '?'], false)
- path = Path::Pattern.new pattern
- @router.routes.add_route nil, path, {}, [], {}, route_name
+ mapper = ActionDispatch::Routing::Mapper.new @route_set
+ mapper.get "/foo/:id", :as => route_name, :id => /\d+/, :to => "foo#bar"
error = assert_raises(ActionController::UrlGenerationError) do
@formatter.generate(route_name, { }, { })
@@ -212,7 +110,7 @@ module ActionDispatch
end
def test_X_Cascade
- add_routes @router, [ "/messages(.:format)" ]
+ mapper.get "/messages(.:format)", to: "foo#bar"
resp = @router.serve(rails_env({ 'REQUEST_METHOD' => 'GET', 'PATH_INFO' => '/lol' }))
assert_equal ['Not Found'], resp.last
assert_equal 'pass', resp[1]['X-Cascade']
@@ -233,24 +131,21 @@ module ActionDispatch
end
def test_defaults_merge_correctly
- path = Path::Pattern.from_string '/foo(/:id)'
- @router.routes.add_route nil, path, {}, [], {:id => nil}, {}
+ mapper.get '/foo(/:id)', to: "foo#bar", id: nil
env = rails_env 'PATH_INFO' => '/foo/10'
@router.recognize(env) do |r, params|
- assert_equal({:id => '10'}, params)
+ assert_equal({:id => '10', :controller => "foo", :action => "bar"}, params)
end
env = rails_env 'PATH_INFO' => '/foo'
@router.recognize(env) do |r, params|
- assert_equal({:id => nil}, params)
+ assert_equal({:id => nil, :controller => "foo", :action => "bar"}, params)
end
end
def test_recognize_with_unbound_regexp
- add_routes @router, [
- Router::Strexp.build("/foo", { }, ['/', '.', '?'], false)
- ]
+ mapper.get "/foo", anchor: false, to: "foo#bar"
env = rails_env 'PATH_INFO' => '/foo/bar'
@@ -261,9 +156,7 @@ module ActionDispatch
end
def test_bound_regexp_keeps_path_info
- add_routes @router, [
- Router::Strexp.build("/foo", { }, ['/', '.', '?'], true)
- ]
+ mapper.get "/foo", to: "foo#bar"
env = rails_env 'PATH_INFO' => '/foo'
@@ -276,12 +169,14 @@ module ActionDispatch
end
def test_path_not_found
- add_routes @router, [
+ [
"/messages(.:format)",
"/messages/new(.:format)",
"/messages/:id/edit(.:format)",
"/messages/:id(.:format)"
- ]
+ ].each do |path|
+ mapper.get path, to: "foo#bar"
+ end
env = rails_env 'PATH_INFO' => '/messages/unknown/path'
yielded = false
@@ -292,32 +187,29 @@ module ActionDispatch
end
def test_required_part_in_recall
- add_routes @router, [ "/messages/:a/:b" ]
+ mapper.get "/messages/:a/:b", to: "foo#bar"
- path, _ = @formatter.generate(nil, { :a => 'a' }, { :b => 'b' })
+ path, _ = @formatter.generate(nil, { :controller => "foo", :action => "bar", :a => 'a' }, { :b => 'b' })
assert_equal "/messages/a/b", path
end
def test_splat_in_recall
- add_routes @router, [ "/*path" ]
+ mapper.get "/*path", to: "foo#bar"
- path, _ = @formatter.generate(nil, { }, { :path => 'b' })
+ path, _ = @formatter.generate(nil, { :controller => "foo", :action => "bar" }, { :path => 'b' })
assert_equal "/b", path
end
def test_recall_should_be_used_when_scoring
- add_routes @router, [
- "/messages/:action(/:id(.:format))",
- "/messages/:id(.:format)"
- ]
+ mapper.get "/messages/:action(/:id(.:format))", to: 'foo#bar'
+ mapper.get "/messages/:id(.:format)", to: 'bar#baz'
- path, _ = @formatter.generate(nil, { :id => 10 }, { :action => 'index' })
+ path, _ = @formatter.generate(nil, { :controller => "foo", :id => 10 }, { :action => 'index' })
assert_equal "/messages/index/10", path
end
def test_nil_path_parts_are_ignored
- path = Path::Pattern.from_string "/:controller(/:action(.:format))"
- @router.routes.add_route @app, path, {}, [], {}, {}
+ mapper.get "/:controller(/:action(.:format))", to: "tasks#lol"
params = { :controller => "tasks", :format => nil }
extras = { :action => 'lol' }
@@ -329,18 +221,14 @@ module ActionDispatch
def test_generate_slash
params = [ [:controller, "tasks"],
[:action, "show"] ]
- str = Router::Strexp.build("/", Hash[params], ['/', '.', '?'], true)
- path = Path::Pattern.new str
-
- @router.routes.add_route @app, path, {}, [], {}, {}
+ mapper.get "/", Hash[params]
path, _ = @formatter.generate(nil, Hash[params], {})
assert_equal '/', path
end
def test_generate_calls_param_proc
- path = Path::Pattern.from_string '/:controller(/:action)'
- @router.routes.add_route @app, path, {}, [], {}, {}
+ mapper.get '/:controller(/:action)', to: "foo#bar"
parameterized = []
params = [ [:controller, "tasks"],
@@ -356,8 +244,7 @@ module ActionDispatch
end
def test_generate_id
- path = Path::Pattern.from_string '/:controller(/:action)'
- @router.routes.add_route @app, path, {}, [], {}, {}
+ mapper.get '/:controller(/:action)', to: 'foo#bar'
path, params = @formatter.generate(
nil, {:id=>1, :controller=>"tasks", :action=>"show"}, {})
@@ -366,8 +253,7 @@ module ActionDispatch
end
def test_generate_escapes
- path = Path::Pattern.from_string '/:controller(/:action)'
- @router.routes.add_route @app, path, {}, [], {}, {}
+ mapper.get '/:controller(/:action)', to: "foo#bar"
path, _ = @formatter.generate(nil,
{ :controller => "tasks",
@@ -377,8 +263,7 @@ module ActionDispatch
end
def test_generate_escapes_with_namespaced_controller
- path = Path::Pattern.from_string '/:controller(/:action)'
- @router.routes.add_route @app, path, {}, [], {}, {}
+ mapper.get '/:controller(/:action)', to: "foo#bar"
path, _ = @formatter.generate(
nil, { :controller => "admin/tasks",
@@ -388,8 +273,7 @@ module ActionDispatch
end
def test_generate_extra_params
- path = Path::Pattern.from_string '/:controller(/:action)'
- @router.routes.add_route @app, path, {}, [], {}, {}
+ mapper.get '/:controller(/:action)', to: "foo#bar"
path, params = @formatter.generate(
nil, { :id => 1,
@@ -402,8 +286,7 @@ module ActionDispatch
end
def test_generate_missing_keys_no_matches_different_format_keys
- path = Path::Pattern.from_string '/:controller/:action/:name'
- @router.routes.add_route @app, path, {}, [], {}, {}
+ mapper.get '/:controller/:action/:name', to: "foo#bar"
primarty_parameters = {
:id => 1,
:controller => "tasks",
@@ -429,8 +312,7 @@ module ActionDispatch
end
def test_generate_uses_recall_if_needed
- path = Path::Pattern.from_string '/:controller(/:action(/:id))'
- @router.routes.add_route @app, path, {}, [], {}, {}
+ mapper.get '/:controller(/:action(/:id))', to: "foo#bar"
path, params = @formatter.generate(
nil,
@@ -441,8 +323,7 @@ module ActionDispatch
end
def test_generate_with_name
- path = Path::Pattern.from_string '/:controller(/:action)'
- @router.routes.add_route @app, path, {}, [], {}, "tasks"
+ mapper.get '/:controller(/:action)', to: 'foo#bar', as: 'tasks'
path, params = @formatter.generate(
"tasks",
@@ -458,16 +339,15 @@ module ActionDispatch
'/content/show/10' => { :controller => 'content', :action => 'show', :id => "10" },
}.each do |request_path, expected|
define_method("test_recognize_#{expected.keys.map(&:to_s).join('_')}") do
- path = Path::Pattern.from_string "/:controller(/:action(/:id))"
- app = Object.new
- route = @router.routes.add_route(app, path, {}, [], {}, {})
+ mapper.get "/:controller(/:action(/:id))", to: 'foo#bar'
+ route = @routes.first
env = rails_env 'PATH_INFO' => request_path
called = false
@router.recognize(env) do |r, params|
assert_equal route, r
- assert_equal(expected, params)
+ assert_equal({ :action => "bar" }.merge(expected), params)
called = true
end
@@ -480,16 +360,15 @@ module ActionDispatch
:splat => ['/segment/a/b%20c+d', { :segment => 'segment', :splat => 'a/b c+d' }]
}.each do |name, (request_path, expected)|
define_method("test_recognize_#{name}") do
- path = Path::Pattern.from_string '/:segment/*splat'
- app = Object.new
- route = @router.routes.add_route(app, path, {}, [], {}, {})
+ mapper.get '/:segment/*splat', to: 'foo#bar'
env = rails_env 'PATH_INFO' => request_path
called = false
+ route = @routes.first
@router.recognize(env) do |r, params|
assert_equal route, r
- assert_equal(expected, params)
+ assert_equal(expected.merge(:controller=>"foo", :action=>"bar"), params)
called = true
end
@@ -498,14 +377,8 @@ module ActionDispatch
end
def test_namespaced_controller
- strexp = Router::Strexp.build(
- "/:controller(/:action(/:id))",
- { :controller => /.+?/ },
- ["/", ".", "?"]
- )
- path = Path::Pattern.new strexp
- app = Object.new
- route = @router.routes.add_route(app, path, {}, [], {}, {})
+ mapper.get "/:controller(/:action(/:id))", { :controller => /.+?/ }
+ route = @routes.first
env = rails_env 'PATH_INFO' => '/admin/users/show/10'
called = false
@@ -524,9 +397,8 @@ module ActionDispatch
end
def test_recognize_literal
- path = Path::Pattern.from_string "/books(/:action(.:format))"
- app = Object.new
- route = @router.routes.add_route(app, path, {}, [], {:controller => 'books'})
+ mapper.get "/books(/:action(.:format))", controller: "books"
+ route = @routes.first
env = rails_env 'PATH_INFO' => '/books/list.rss'
expected = { :controller => 'books', :action => 'list', :format => 'rss' }
@@ -541,10 +413,7 @@ module ActionDispatch
end
def test_recognize_head_route
- path = Path::Pattern.from_string "/books(/:action(.:format))"
- app = Object.new
- conditions = { request_method: 'HEAD' }
- @router.routes.add_route(app, path, conditions, [], {})
+ mapper.match "/books(/:action(.:format))", via: 'head', to: 'foo#bar'
env = rails_env(
'PATH_INFO' => '/books/list.rss',
@@ -560,12 +429,7 @@ module ActionDispatch
end
def test_recognize_head_request_as_get_route
- path = Path::Pattern.from_string "/books(/:action(.:format))"
- app = Object.new
- conditions = {
- :request_method => 'GET'
- }
- @router.routes.add_route(app, path, conditions, [], {})
+ mapper.get "/books(/:action(.:format))", to: 'foo#bar'
env = rails_env 'PATH_INFO' => '/books/list.rss',
"REQUEST_METHOD" => "HEAD"
@@ -578,11 +442,8 @@ module ActionDispatch
assert called
end
- def test_recognize_cares_about_verbs
- path = Path::Pattern.from_string "/books(/:action(.:format))"
- app = Object.new
- conditions = { request_method: 'GET' }
- @router.routes.add_route(app, path, conditions, [], {})
+ def test_recognize_cares_about_get_verbs
+ mapper.match "/books(/:action(.:format))", to: "foo#bar", via: :get
env = rails_env 'PATH_INFO' => '/books/list.rss',
"REQUEST_METHOD" => "POST"
@@ -593,31 +454,58 @@ module ActionDispatch
end
assert_not called
+ end
- conditions = conditions.dup
- conditions[:request_method] = 'POST'
+ def test_recognize_cares_about_post_verbs
+ mapper.match "/books(/:action(.:format))", to: "foo#bar", via: :post
- post = @router.routes.add_route(app, path, conditions, [], {})
+ env = rails_env 'PATH_INFO' => '/books/list.rss',
+ "REQUEST_METHOD" => "POST"
called = false
@router.recognize(env) do |r, params|
- assert_equal post, r
called = true
end
assert called
end
+ def test_multi_verb_recognition
+ mapper.match "/books(/:action(.:format))", to: "foo#bar", via: [:post, :get]
+
+ %w( POST GET ).each do |verb|
+ env = rails_env 'PATH_INFO' => '/books/list.rss',
+ "REQUEST_METHOD" => verb
+
+ called = false
+ @router.recognize(env) do |r, params|
+ called = true
+ end
+
+ assert called
+ end
+
+ env = rails_env 'PATH_INFO' => '/books/list.rss',
+ "REQUEST_METHOD" => 'PUT'
+
+ called = false
+ @router.recognize(env) do |r, params|
+ called = true
+ end
+
+ assert_not called
+ end
+
private
- def add_routes router, paths
+ def add_routes router, paths, anchor = true
paths.each do |path|
if String === path
path = Path::Pattern.from_string path
else
- path = Path::Pattern.new path
+ path
end
- router.routes.add_route @app, path, {}, [], {}, {}
+ add_route @app, path, {}, [], {}, {}
end
end
diff --git a/actionpack/test/journey/routes_test.rb b/actionpack/test/journey/routes_test.rb
index b9dac8751c..f8293dfc5f 100644
--- a/actionpack/test/journey/routes_test.rb
+++ b/actionpack/test/journey/routes_test.rb
@@ -3,18 +3,19 @@ require 'abstract_unit'
module ActionDispatch
module Journey
class TestRoutes < ActiveSupport::TestCase
- setup do
- @routes = Routes.new
+ attr_reader :routes, :mapper
+
+ def setup
+ @route_set = ActionDispatch::Routing::RouteSet.new
+ @routes = @route_set.router.routes
+ @router = @route_set.router
+ @mapper = ActionDispatch::Routing::Mapper.new @route_set
+ super
end
def test_clear
- routes = Routes.new
- exp = Router::Strexp.build '/foo(/:id)', {}, ['/.?']
- path = Path::Pattern.new exp
- requirements = { :hello => /world/ }
-
- routes.add_route nil, path, requirements, [], {:id => nil}, {}
- assert_not routes.empty?
+ mapper.get "/foo(/:id)", to: "foo#bar", as: 'aaron'
+ assert_not_predicate routes, :empty?
assert_equal 1, routes.length
routes.clear
@@ -23,52 +24,36 @@ module ActionDispatch
end
def test_ast
- routes = Routes.new
- path = Path::Pattern.from_string '/hello'
-
- routes.add_route nil, path, {}, [], {}, {}
+ mapper.get "/foo(/:id)", to: "foo#bar", as: 'aaron'
ast = routes.ast
- routes.add_route nil, path, {}, [], {}, {}
+ mapper.get "/foo(/:id)", to: "foo#bar", as: 'gorby'
assert_not_equal ast, routes.ast
end
def test_simulator_changes
- routes = Routes.new
- path = Path::Pattern.from_string '/hello'
-
- routes.add_route nil, path, {}, [], {}, {}
+ mapper.get "/foo(/:id)", to: "foo#bar", as: 'aaron'
sim = routes.simulator
- routes.add_route nil, path, {}, [], {}, {}
+ mapper.get "/foo(/:id)", to: "foo#bar", as: 'gorby'
assert_not_equal sim, routes.simulator
end
def test_partition_route
- path = Path::Pattern.from_string '/hello'
+ mapper.get "/foo(/:id)", to: "foo#bar", as: 'aaron'
- anchored_route = @routes.add_route nil, path, {}, [], {}, {}
- assert_equal [anchored_route], @routes.anchored_routes
- assert_equal [], @routes.custom_routes
+ assert_equal 1, @routes.anchored_routes.length
+ assert_predicate @routes.custom_routes, :empty?
- strexp = Router::Strexp.build(
- "/hello/:who", { who: /\d/ }, ['/', '.', '?']
- )
- path = Path::Pattern.new strexp
+ mapper.get "/hello/:who", to: "foo#bar", as: 'bar', who: /\d/
- custom_route = @routes.add_route nil, path, {}, [], {}, {}
- assert_equal [custom_route], @routes.custom_routes
- assert_equal [anchored_route], @routes.anchored_routes
+ assert_equal 1, @routes.custom_routes.length
+ assert_equal 1, @routes.anchored_routes.length
end
def test_first_name_wins
- routes = Routes.new
-
- one = Path::Pattern.from_string '/hello'
- two = Path::Pattern.from_string '/aaron'
-
- routes.add_route nil, one, {}, [], {}, 'aaron'
- routes.add_route nil, two, {}, [], {}, 'aaron'
-
- assert_equal '/hello', routes.named_routes['aaron'].path.spec.to_s
+ mapper.get "/hello", to: "foo#bar", as: 'aaron'
+ assert_raise(ArgumentError) do
+ mapper.get "/aaron", to: "foo#bar", as: 'aaron'
+ end
end
end
end