aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_dispatch
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_dispatch')
-rw-r--r--actionpack/lib/action_dispatch/http/mime_type.rb4
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb16
-rw-r--r--actionpack/lib/action_dispatch/http/upload.rb17
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb12
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb7
-rw-r--r--actionpack/lib/action_dispatch/middleware/flash.rb4
-rw-r--r--actionpack/lib/action_dispatch/middleware/request_id.rb39
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/abstract_store.rb5
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/cache_store.rb50
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb4
-rw-r--r--actionpack/lib/action_dispatch/railtie.rb3
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb46
-rw-r--r--actionpack/lib/action_dispatch/routing/route.rb60
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb112
-rw-r--r--actionpack/lib/action_dispatch/routing/url_for.rb19
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/selector.rb4
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb8
-rw-r--r--actionpack/lib/action_dispatch/testing/test_process.rb7
18 files changed, 252 insertions, 165 deletions
diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb
index fa2948c8db..8a9f9c4315 100644
--- a/actionpack/lib/action_dispatch/http/mime_type.rb
+++ b/actionpack/lib/action_dispatch/http/mime_type.rb
@@ -256,6 +256,10 @@ module Mime
@@html_types.include?(to_sym) || @string =~ /html/
end
+ def respond_to?(method, include_private = false) #:nodoc:
+ super || method.to_s =~ /(\w+)\?$/
+ end
+
private
def method_missing(method, *args)
if method.to_s =~ /(\w+)\?$/
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index b80574f497..7a5237dcf3 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -29,9 +29,9 @@ module ActionDispatch
ENV_METHODS.each do |env|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
- def #{env.sub(/^HTTP_/n, '').downcase}
- @env["#{env}"]
- end
+ def #{env.sub(/^HTTP_/n, '').downcase} # def accept_charset
+ @env["#{env}"] # @env["HTTP_ACCEPT_CHARSET"]
+ end # end
METHOD
end
@@ -177,6 +177,16 @@ module ActionDispatch
@remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s
end
+ # Returns the unique request id, which is based off either the X-Request-Id header that can
+ # be generated by a firewall, load balancer, or web server or by the RequestId middleware
+ # (which sets the action_dispatch.request_id environment variable).
+ #
+ # This unique ID is useful for tracing a request from end-to-end as part of logging or debugging.
+ # This relies on the rack variable set by the ActionDispatch::RequestId middleware.
+ def uuid
+ @uuid ||= env["action_dispatch.request_id"]
+ end
+
# Returns the lowercase name of the HTTP server software.
def server_software
(@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil
diff --git a/actionpack/lib/action_dispatch/http/upload.rb b/actionpack/lib/action_dispatch/http/upload.rb
index a15ad28f16..94fa747a79 100644
--- a/actionpack/lib/action_dispatch/http/upload.rb
+++ b/actionpack/lib/action_dispatch/http/upload.rb
@@ -11,24 +11,13 @@ module ActionDispatch
raise(ArgumentError, ':tempfile is required') unless @tempfile
end
- def open
- @tempfile.open
- end
-
- def path
- @tempfile.path
- end
-
def read(*args)
@tempfile.read(*args)
end
- def rewind
- @tempfile.rewind
- end
-
- def size
- @tempfile.size
+ # Delegate these methods to the tempfile.
+ [:open, :path, :rewind, :size].each do |method|
+ class_eval "def #{method}; @tempfile.#{method}; end"
end
private
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index 8487b0fc8c..c8ddd07bfa 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -45,7 +45,7 @@ module ActionDispatch
rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
rewritten_url << "?#{params.to_query}" unless params.empty?
- rewritten_url << "##{Rack::Mount::Utils.escape_uri(options[:anchor].to_param.to_s)}" if options[:anchor]
+ rewritten_url << "##{Journey::Router::Utils.escape_fragment(options[:anchor].to_param.to_s)}" if options[:anchor]
rewritten_url
end
@@ -64,14 +64,16 @@ module ActionDispatch
end
def host_or_subdomain_and_domain(options)
- return options[:host] unless options[:subdomain] || options[:domain]
+ return options[:host] if options[:subdomain].nil? && options[:domain].nil?
tld_length = options[:tld_length] || @@tld_length
host = ""
- host << (options[:subdomain] || extract_subdomain(options[:host], tld_length))
- host << "."
- host << (options[:domain] || extract_domain(options[:host], tld_length))
+ unless options[:subdomain] == false
+ host << (options[:subdomain] || extract_subdomain(options[:host], tld_length))
+ host << "."
+ end
+ host << (options[:domain] || extract_domain(options[:host], tld_length))
host
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index 1c312f2587..a4ffd40a66 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -85,6 +85,7 @@ module ActionDispatch
class CookieOverflow < StandardError; end
class CookieJar #:nodoc:
+ include Enumerable
# This regular expression is used to split the levels of a domain.
# The top level domain can be any string without a period or
@@ -124,6 +125,10 @@ module ActionDispatch
alias :closed? :closed
def close!; @closed = true end
+ def each(&block)
+ @cookies.each(&block)
+ end
+
# Returns the value of the cookie by +name+, or +nil+ if no such cookie exists.
def [](name)
@cookies[name.to_s]
@@ -169,7 +174,7 @@ module ActionDispatch
options = { :value => value }
end
- value = @cookies[key.to_s] = value
+ @cookies[key.to_s] = value
handle_options(options)
diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb
index 2adbce031b..e59404ef68 100644
--- a/actionpack/lib/action_dispatch/middleware/flash.rb
+++ b/actionpack/lib/action_dispatch/middleware/flash.rb
@@ -70,6 +70,10 @@ module ActionDispatch
end
end
+ # Implementation detail: please do not change the signature of the
+ # FlashHash class. Doing that will likely affect all Rails apps in
+ # production as the FlashHash currently stored in their sessions will
+ # become invalid.
class FlashHash
include Enumerable
diff --git a/actionpack/lib/action_dispatch/middleware/request_id.rb b/actionpack/lib/action_dispatch/middleware/request_id.rb
new file mode 100644
index 0000000000..bee446c8a5
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/request_id.rb
@@ -0,0 +1,39 @@
+require 'securerandom'
+require 'active_support/core_ext/string/access'
+require 'active_support/core_ext/object/blank'
+
+module ActionDispatch
+ # Makes a unique request id available to the action_dispatch.request_id env variable (which is then accessible through
+ # ActionDispatch::Request#uuid) and sends the same id to the client via the X-Request-Id header.
+ #
+ # The unique request id is either based off the X-Request-Id header in the request, which would typically be generated
+ # by a firewall, load balancer, or the web server, or, if this header is not available, a random uuid. If the
+ # header is accepted from the outside world, we sanitize it to a max of 255 chars and alphanumeric and dashes only.
+ #
+ # The unique request id can be used to trace a request end-to-end and would typically end up being part of log files
+ # from multiple pieces of the stack.
+ class RequestId
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ env["action_dispatch.request_id"] = external_request_id(env) || internal_request_id
+ status, headers, body = @app.call(env)
+
+ headers["X-Request-Id"] = env["action_dispatch.request_id"]
+ [ status, headers, body ]
+ end
+
+ private
+ def external_request_id(env)
+ if request_id = env["HTTP_X_REQUEST_ID"].presence
+ request_id.gsub(/[^\w\-]/, "").first(255)
+ end
+ end
+
+ def internal_request_id
+ SecureRandom.hex(16)
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
index a70d814749..6bcf099d2c 100644
--- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
@@ -59,7 +59,10 @@ module ActionDispatch
# Note that the regexp does not allow $1 to end with a ':'
$1.constantize
rescue LoadError, NameError => const_error
- raise ActionDispatch::Session::SessionRestoreError, "Session contains objects whose class definition isn't available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: #{const_error.message} [#{const_error.class}])\n"
+ raise ActionDispatch::Session::SessionRestoreError,
+ "Session contains objects whose class definition isn't available.\n" +
+ "Remember to require the classes for all objects kept in the session.\n" +
+ "(Original exception: #{const_error.message} [#{const_error.class}])\n"
end
retry
else
diff --git a/actionpack/lib/action_dispatch/middleware/session/cache_store.rb b/actionpack/lib/action_dispatch/middleware/session/cache_store.rb
new file mode 100644
index 0000000000..d3b6fd12fa
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/session/cache_store.rb
@@ -0,0 +1,50 @@
+require 'action_dispatch/middleware/session/abstract_store'
+require 'rack/session/memcache'
+
+module ActionDispatch
+ module Session
+ # Session store that uses an ActiveSupport::Cache::Store to store the sessions. This store is most useful
+ # if you don't store critical data in your sessions and you don't need them to live for extended periods
+ # of time.
+ class CacheStore < AbstractStore
+ # Create a new store. The cache to use can be passed in the <tt>:cache</tt> option. If it is
+ # not specified, <tt>Rails.cache</tt> will be used.
+ def initialize(app, options = {})
+ @cache = options[:cache] || Rails.cache
+ options[:expire_after] ||= @cache.options[:expires_in]
+ super
+ end
+
+ # Get a session from the cache.
+ def get_session(env, sid)
+ sid ||= generate_sid
+ session = @cache.read(cache_key(sid))
+ session ||= {}
+ [sid, session]
+ end
+
+ # Set a session in the cache.
+ def set_session(env, sid, session, options)
+ key = cache_key(sid)
+ if session
+ @cache.write(key, session, :expires_in => options[:expire_after])
+ else
+ @cache.delete(key)
+ end
+ sid
+ end
+
+ # Remove a session from the cache.
+ def destroy_session(env, sid, options)
+ @cache.delete(cache_key(sid))
+ generate_sid
+ end
+
+ private
+ # Turn the session id into a cache key.
+ def cache_key(sid)
+ "_session_id:#{sid}"
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
index a765c23dae..2fa68c64c5 100644
--- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
@@ -86,8 +86,8 @@ module ActionDispatch
:framework_trace => framework_trace(exception),
:full_trace => full_trace(exception)
)
- file = "rescues/#{@@rescue_templates[exception.class.name]}.erb"
- body = template.render(:file => file, :layout => 'rescues/layout.erb')
+ file = "rescues/#{@@rescue_templates[exception.class.name]}"
+ body = template.render(:template => file, :layout => 'rescues/layout')
render(status_code(exception), body)
end
diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb
index f51cc3711b..1af89858d1 100644
--- a/actionpack/lib/action_dispatch/railtie.rb
+++ b/actionpack/lib/action_dispatch/railtie.rb
@@ -1,10 +1,9 @@
require "action_dispatch"
-require "rails"
module ActionDispatch
class Railtie < Rails::Railtie
config.action_dispatch = ActiveSupport::OrderedOptions.new
- config.action_dispatch.x_sendfile_header = ""
+ config.action_dispatch.x_sendfile_header = nil
config.action_dispatch.ip_spoofing_check = true
config.action_dispatch.show_exceptions = true
config.action_dispatch.best_standards_support = true
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index a5c1501f61..970236a05a 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -49,7 +49,7 @@ module ActionDispatch
class Mapping #:nodoc:
IGNORE_OPTIONS = [:to, :as, :via, :on, :constraints, :defaults, :only, :except, :anchor, :shallow, :shallow_path, :shallow_prefix]
ANCHOR_CHARACTERS_REGEX = %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
- SHORTHAND_REGEX = %r{^/[\w/]+$}
+ SHORTHAND_REGEX = %r{/[\w/]+$}
WILDCARD_PATH = %r{\*([^/\)]+)\)?$}
def initialize(set, scope, path, options)
@@ -70,7 +70,7 @@ module ActionDispatch
if using_match_shorthand?(path_without_format, @options)
to_shorthand = @options[:to].blank?
- @options[:to] ||= path_without_format[1..-1].sub(%r{/([^/]*)$}, '#\1')
+ @options[:to] ||= path_without_format.gsub(/\(.*\)/, "")[1..-1].sub(%r{/([^/]*)$}, '#\1')
end
@options.merge!(default_controller_and_action(to_shorthand))
@@ -90,7 +90,7 @@ module ActionDispatch
# match "account/overview"
def using_match_shorthand?(path, options)
- path && options.except(:via, :anchor, :to, :as).empty? && path =~ SHORTHAND_REGEX
+ path && (options[:to] || options[:action]).nil? && path =~ SHORTHAND_REGEX
end
def normalize_path(path)
@@ -213,8 +213,8 @@ module ActionDispatch
end
def segment_keys
- @segment_keys ||= Rack::Mount::RegexpWithNamedGroups.new(
- Rack::Mount::Strexp.compile(@path, requirements, SEPARATORS)
+ @segment_keys ||= Journey::Path::Pattern.new(
+ Journey::Router::Strexp.compile(@path, requirements, SEPARATORS)
).names
end
@@ -235,7 +235,7 @@ module ActionDispatch
# (:locale) becomes (/:locale) instead of /(:locale). Except
# for root cases, where the latter is the correct one.
def self.normalize_path(path)
- path = Rack::Mount::Utils.normalize_path(path)
+ path = Journey::Router::Utils.normalize_path(path)
path.gsub!(%r{/(\(+)/?}, '\1/') unless path =~ %r{^/\(+[^/]+\)$}
path
end
@@ -285,7 +285,7 @@ module ActionDispatch
# A pattern can also point to a +Rack+ endpoint i.e. anything that
# responds to +call+:
#
- # match 'photos/:id' => lambda {|hash| [200, {}, "Coming soon" }
+ # match 'photos/:id' => lambda {|hash| [200, {}, "Coming soon"] }
# match 'photos/:id' => PhotoRackApp
# # Yes, controller actions are just rack endpoints
# match 'photos/:id' => PhotosController.action(:show)
@@ -452,7 +452,9 @@ module ActionDispatch
prefix_options = options.slice(*_route.segment_keys)
# we must actually delete prefix segment keys to avoid passing them to next url_for
_route.segment_keys.each { |k| options.delete(k) }
- _routes.url_helpers.send("#{name}_path", prefix_options)
+ prefix = _routes.url_helpers.send("#{name}_path", prefix_options)
+ prefix = '' if prefix == '/'
+ prefix
end
end
end
@@ -694,7 +696,7 @@ module ActionDispatch
# Allows you to constrain the nested routes based on a set of rules.
# For instance, in order to change the routes to allow for a dot character in the +id+ parameter:
#
- # constraints(:id => /\d+\.\d+) do
+ # constraints(:id => /\d+\.\d+/) do
# resources :posts
# end
#
@@ -704,7 +706,7 @@ module ActionDispatch
# You may use this to also restrict other parameters:
#
# resources :posts do
- # constraints(:post_id => /\d+\.\d+) do
+ # constraints(:post_id => /\d+\.\d+/) do
# resources :comments
# end
# end
@@ -733,7 +735,7 @@ module ActionDispatch
# if the user should be given access to that route, or +false+ if the user should not.
#
# class Iphone
- # def self.matches(request)
+ # def self.matches?(request)
# request.env["HTTP_USER_AGENT"] =~ /iPhone/
# end
# end
@@ -1021,6 +1023,7 @@ module ActionDispatch
# creates seven different routes in your application, all mapping to
# the +Photos+ controller:
#
+ # GET /photos
# GET /photos/new
# POST /photos
# GET /photos/:id
@@ -1036,12 +1039,13 @@ module ActionDispatch
#
# This generates the following comments routes:
#
- # GET /photos/:id/comments/new
- # POST /photos/:id/comments
- # GET /photos/:id/comments/:id
- # GET /photos/:id/comments/:id/edit
- # PUT /photos/:id/comments/:id
- # DELETE /photos/:id/comments/:id
+ # GET /photos/:photo_id/comments
+ # GET /photos/:photo_id/comments/new
+ # POST /photos/:photo_id/comments
+ # GET /photos/:photo_id/comments/:id
+ # GET /photos/:photo_id/comments/:id/edit
+ # PUT /photos/:photo_id/comments/:id
+ # DELETE /photos/:photo_id/comments/:id
#
# === Options
# Takes same options as <tt>Base#match</tt> as well as:
@@ -1436,7 +1440,7 @@ module ActionDispatch
name_prefix = @scope[:as]
if parent_resource
- return nil if as.nil? && action.nil?
+ return nil unless as || action
collection_name = parent_resource.collection_name
member_name = parent_resource.member_name
@@ -1463,9 +1467,9 @@ module ActionDispatch
end
module Shorthand #:nodoc:
- def match(*args)
- if args.size == 1 && args.last.is_a?(Hash)
- options = args.pop
+ def match(path, *rest)
+ if rest.empty? && Hash === path
+ options = path
path, to = options.find { |name, value| name.is_a?(String) }
options.merge!(:to => to).delete(path)
super(path, options)
diff --git a/actionpack/lib/action_dispatch/routing/route.rb b/actionpack/lib/action_dispatch/routing/route.rb
deleted file mode 100644
index 10b3d38346..0000000000
--- a/actionpack/lib/action_dispatch/routing/route.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-module ActionDispatch
- module Routing
- class Route #:nodoc:
- attr_reader :app, :conditions, :defaults, :name
- attr_reader :path, :requirements, :set
-
- def initialize(set, app, conditions, requirements, defaults, name, anchor)
- @set = set
- @app = app
- @defaults = defaults
- @name = name
-
- # FIXME: we should not be doing this much work in a constructor.
-
- @requirements = requirements.merge(defaults)
- @requirements.delete(:controller) if @requirements[:controller].is_a?(Regexp)
- @requirements.delete_if { |k, v|
- v == Regexp.compile("[^#{SEPARATORS.join}]+")
- }
-
- if path = conditions[:path_info]
- @path = path
- conditions[:path_info] = ::Rack::Mount::Strexp.compile(path, requirements, SEPARATORS, anchor)
- end
-
- @verbs = conditions[:request_method] || []
-
- @conditions = 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.
- @conditions[:request_method] = %r[^#{verb}$] unless @verbs.empty?
- @conditions[:path_info] = Rack::Mount::RegexpWithNamedGroups.new(@conditions[:path_info]) if @conditions[:path_info]
- @conditions.delete_if{ |k,v| k != :path_info && !valid_condition?(k) }
- @requirements.delete_if{ |k,v| !valid_condition?(k) }
- end
-
- def verb
- @verbs.join '|'
- end
-
- def segment_keys
- @segment_keys ||= conditions[:path_info].names.compact.map { |key| key.to_sym }
- end
-
- def to_s
- @to_s ||= begin
- "%-6s %-40s %s" % [(verb || :any).to_s.upcase, path, requirements.inspect]
- end
- end
-
- private
- def valid_condition?(method)
- segment_keys.include?(method) || set.valid_conditions.include?(method)
- end
- end
- end
-end
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 11228c597d..2bcde16110 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -1,4 +1,4 @@
-require 'rack/mount'
+require 'journey'
require 'forwardable'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/object/to_query'
@@ -37,7 +37,7 @@ module ActionDispatch
# If this is a default_controller (i.e. a controller specified by the user)
# we should raise an error in case it's not found, because it usually means
- # an user error. However, if the controller was retrieved through a dynamic
+ # a user error. However, if the controller was retrieved through a dynamic
# 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.
@@ -165,13 +165,14 @@ module ActionDispatch
remove_possible_method :#{selector}
def #{selector}(*args)
options = args.extract_options!
+ result = #{options.inspect}
if args.any?
- options[:_positional_args] = args
- options[:_positional_keys] = #{route.segment_keys.inspect}
+ result[:_positional_args] = args
+ result[:_positional_keys] = #{route.segment_keys.inspect}
end
- options ? #{options.inspect}.merge(options) : #{options.inspect}
+ result.merge(options)
end
protected :#{selector}
END_EVAL
@@ -205,29 +206,42 @@ module ActionDispatch
end
end
- attr_accessor :set, :routes, :named_routes, :default_scope
+ attr_accessor :formatter, :set, :named_routes, :default_scope, :router
attr_accessor :disable_clear_and_finalize, :resources_path_names
attr_accessor :default_url_options, :request_class, :valid_conditions
+ alias :routes :set
+
def self.default_resources_path_names
{ :new => 'new', :edit => 'edit' }
end
def initialize(request_class = ActionDispatch::Request)
- self.routes = []
self.named_routes = NamedRouteCollection.new
self.resources_path_names = self.class.default_resources_path_names.dup
self.default_url_options = {}
self.request_class = request_class
- self.valid_conditions = request_class.public_instance_methods.map { |m| m.to_sym }
+ @valid_conditions = {}
+
+ request_class.public_instance_methods.each { |m|
+ @valid_conditions[m.to_sym] = true
+ }
+ @valid_conditions[:controller] = true
+ @valid_conditions[:action] = true
+
self.valid_conditions.delete(:id)
- self.valid_conditions.push(:controller, :action)
- @append = []
- @prepend = []
+ @append = []
+ @prepend = []
@disable_clear_and_finalize = false
- clear!
+ @finalized = false
+
+ @set = Journey::Routes.new
+ @router = Journey::Router.new(@set, {
+ :parameters_key => PARAMETERS_KEY,
+ :request_class => request_class})
+ @formatter = Journey::Formatter.new @set
end
def draw(&block)
@@ -263,17 +277,13 @@ module ActionDispatch
return if @finalized
@append.each { |blk| eval_block(blk) }
@finalized = true
- @set.freeze
end
def clear!
@finalized = false
- routes.clear
named_routes.clear
- @set = ::Rack::Mount::RouteSet.new(
- :parameters_key => PARAMETERS_KEY,
- :request_class => request_class
- )
+ set.clear
+ formatter.clear
@prepend.each { |blk| eval_block(blk) }
end
@@ -341,26 +351,54 @@ module ActionDispatch
def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil, anchor = true)
raise ArgumentError, "Invalid route name: '#{name}'" unless name.blank? || name.to_s.match(/^[_a-z]\w*$/i)
- route = Route.new(self, app, conditions, requirements, defaults, name, anchor)
- @set.add_route(route.app, route.conditions, route.defaults, route.name)
+
+ path = build_path(conditions.delete(:path_info), requirements, SEPARATORS, anchor)
+ conditions = build_conditions(conditions, valid_conditions, path.names.map { |x| x.to_sym })
+
+ route = @set.add_route(app, path, conditions, defaults, name)
named_routes[name] = route if name
- routes << route
route
end
+ def build_path(path, requirements, separators, anchor)
+ strexp = Journey::Router::Strexp.new(
+ path,
+ requirements,
+ SEPARATORS,
+ anchor)
+
+ Journey::Path::Pattern.new(strexp)
+ end
+ private :build_path
+
+ def build_conditions(current_conditions, req_predicates, path_values)
+ conditions = current_conditions.dup
+
+ verbs = conditions[:request_method] || []
+
+ # 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.
+ unless verbs.empty?
+ conditions[:request_method] = %r[^#{verbs.join('|')}$]
+ end
+ conditions.delete_if { |k,v| !(req_predicates.include?(k) || path_values.include?(k)) }
+
+ conditions
+ end
+ private :build_conditions
+
class Generator #:nodoc:
- PARAMETERIZE = {
- :parameterize => lambda do |name, value|
- if name == :controller
- value
- elsif value.is_a?(Array)
- value.map { |v| Rack::Mount::Utils.escape_uri(v.to_param) }.join('/')
- else
- return nil unless param = value.to_param
- param.split('/').map { |v| Rack::Mount::Utils.escape_uri(v) }.join("/")
- end
+ PARAMETERIZE = lambda do |name, value|
+ if name == :controller
+ value
+ elsif value.is_a?(Array)
+ value.map { |v| v.to_param }.join('/')
+ elsif param = value.to_param
+ param
end
- }
+ end
attr_reader :options, :recall, :set, :named_route
@@ -450,14 +488,14 @@ module ActionDispatch
end
def generate
- path, params = @set.set.generate(:path_info, named_route, options, recall, PARAMETERIZE)
+ path, params = @set.formatter.generate(:path_info, named_route, options, recall, PARAMETERIZE)
raise_routing_error unless path
return [path, params.keys] if @extras
[path, params]
- rescue Rack::Mount::RoutingError
+ rescue Journey::Router::RoutingError
raise_routing_error
end
@@ -529,12 +567,12 @@ module ActionDispatch
def call(env)
finalize!
- @set.call(env)
+ @router.call(env)
end
def recognize_path(path, environment = {})
method = (environment[:method] || "GET").to_s.upcase
- path = Rack::Mount::Utils.normalize_path(path) unless path =~ %r{://}
+ path = Journey::Router::Utils.normalize_path(path) unless path =~ %r{://}
begin
env = Rack::MockRequest.env_for(path, {:method => method})
@@ -543,7 +581,7 @@ module ActionDispatch
end
req = @request_class.new(env)
- @set.recognize(req) do |route, matches, params|
+ @router.recognize(req) do |route, matches, params|
params.each do |key, value|
if value.is_a?(String)
value = value.dup.force_encoding(Encoding::BINARY) if value.encoding_aware?
diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb
index 480144fe9d..8fc8dc191b 100644
--- a/actionpack/lib/action_dispatch/routing/url_for.rb
+++ b/actionpack/lib/action_dispatch/routing/url_for.rb
@@ -116,9 +116,10 @@ module ActionDispatch
# If <tt>:only_path</tt> is false, this option must be
# provided either explicitly, or via +default_url_options+.
# * <tt>:subdomain</tt> - Specifies the subdomain of the link, using the +tld_length+
- # to split the domain from the host.
- # * <tt>:domain</tt> - Specifies the domain of the link, using the +tld_length+
# to split the subdomain from the host.
+ # If false, removes all subdomains from the host part of the link.
+ # * <tt>:domain</tt> - Specifies the domain of the link, using the +tld_length+
+ # to split the domain from the host.
# * <tt>:tld_length</tt> - Number of labels the TLD id composed of, only used if
# <tt>:subdomain</tt> or <tt>:domain</tt> are supplied. Defaults to
# <tt>ActionDispatch::Http::URL.tld_length</tt>, which in turn defaults to 1.
@@ -131,16 +132,20 @@ module ActionDispatch
#
# Examples:
#
- # url_for :controller => 'tasks', :action => 'testing', :host => 'somehost.org', :port => '8080' # => 'http://somehost.org:8080/tasks/testing'
- # url_for :controller => 'tasks', :action => 'testing', :host => 'somehost.org', :anchor => 'ok', :only_path => true # => '/tasks/testing#ok'
- # url_for :controller => 'tasks', :action => 'testing', :trailing_slash => true # => 'http://somehost.org/tasks/testing/'
- # url_for :controller => 'tasks', :action => 'testing', :host => 'somehost.org', :number => '33' # => 'http://somehost.org/tasks/testing?number=33'
+ # url_for :controller => 'tasks', :action => 'testing', :host => 'somehost.org', :port => '8080'
+ # # => 'http://somehost.org:8080/tasks/testing'
+ # url_for :controller => 'tasks', :action => 'testing', :host => 'somehost.org', :anchor => 'ok', :only_path => true
+ # # => '/tasks/testing#ok'
+ # url_for :controller => 'tasks', :action => 'testing', :trailing_slash => true
+ # # => 'http://somehost.org/tasks/testing/'
+ # url_for :controller => 'tasks', :action => 'testing', :host => 'somehost.org', :number => '33'
+ # # => 'http://somehost.org/tasks/testing?number=33'
def url_for(options = nil)
case options
when String
options
when nil, Hash
- _routes.url_for((options || {}).reverse_merge!(url_options).symbolize_keys)
+ _routes.url_for((options || {}).reverse_merge(url_options).symbolize_keys)
else
polymorphic_url(options)
end
diff --git a/actionpack/lib/action_dispatch/testing/assertions/selector.rb b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
index 5fa91d1a76..b4555f4f59 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/selector.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
@@ -415,9 +415,9 @@ module ActionDispatch
assert !deliveries.empty?, "No e-mail in delivery list"
for delivery in deliveries
- for part in delivery.parts
+ for part in (delivery.parts.empty? ? [delivery] : delivery.parts)
if part["Content-Type"].to_s =~ /^text\/html\W/
- root = HTML::Document.new(part.body).root
+ root = HTML::Document.new(part.body.to_s).root
assert_select root, ":root", &block
end
end
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index aae5752c93..0f1bb9f260 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -241,8 +241,8 @@ module ActionDispatch
end
# Performs the actual request.
- def process(method, path, parameters = nil, env = nil)
- env ||= {}
+ def process(method, path, parameters = nil, rack_env = nil)
+ rack_env ||= {}
if path =~ %r{://}
location = URI.parse(path)
https! URI::HTTPS === location if location.scheme
@@ -258,7 +258,7 @@ module ActionDispatch
hostname, port = host.split(':')
- default_env = {
+ env = {
:method => method,
:params => parameters,
@@ -276,7 +276,7 @@ module ActionDispatch
session = Rack::Test::Session.new(_mock_session)
- env.reverse_merge!(default_env)
+ env.merge!(rack_env)
# NOTE: rack-test v0.5 doesn't build a default uri correctly
# Make sure requested path is always a full uri
diff --git a/actionpack/lib/action_dispatch/testing/test_process.rb b/actionpack/lib/action_dispatch/testing/test_process.rb
index f668b81b45..b08ff41950 100644
--- a/actionpack/lib/action_dispatch/testing/test_process.rb
+++ b/actionpack/lib/action_dispatch/testing/test_process.rb
@@ -5,12 +5,7 @@ require 'active_support/core_ext/hash/indifferent_access'
module ActionDispatch
module TestProcess
def assigns(key = nil)
- assigns = {}.with_indifferent_access
- @controller.instance_variable_names.each do |ivar|
- next if ActionController::Base.protected_instance_variables.include?(ivar)
- assigns[ivar[1..-1]] = @controller.instance_variable_get(ivar)
- end
-
+ assigns = @controller.view_assigns.with_indifferent_access
key.nil? ? assigns : assigns[key]
end