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_negotiation.rb6
-rw-r--r--actionpack/lib/action_dispatch/http/mime_type.rb37
-rw-r--r--actionpack/lib/action_dispatch/http/upload.rb10
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb60
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb13
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb139
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb30
-rw-r--r--actionpack/lib/action_dispatch/routing/url_for.rb7
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb4
9 files changed, 219 insertions, 87 deletions
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
index b959aa258e..68ba1a81b5 100644
--- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb
+++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
@@ -36,19 +36,21 @@ module ActionDispatch
#
# GET /posts/5.xml | request.format => Mime::XML
# GET /posts/5.xhtml | request.format => Mime::HTML
- # GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first depending on the value of <tt>ActionController::Base.use_accept_header</tt>
+ # GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first
#
def format(view_path = [])
formats.first
end
+ BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/
+
def formats
accept = @env['HTTP_ACCEPT']
@env["action_dispatch.request.formats"] ||=
if parameters[:format]
Array(Mime[parameters[:format]])
- elsif xhr? || (accept && accept !~ /,\s*\*\/\*/)
+ elsif xhr? || (accept && accept !~ BROWSER_LIKE_ACCEPTS)
accepts
else
[Mime::HTML]
diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb
index 8f1c9b6691..08eab5634a 100644
--- a/actionpack/lib/action_dispatch/http/mime_type.rb
+++ b/actionpack/lib/action_dispatch/http/mime_type.rb
@@ -80,6 +80,9 @@ module Mime
end
class << self
+
+ TRAILING_STAR_REGEXP = /(text|application)\/\*/
+
def lookup(string)
LOOKUP[string]
end
@@ -105,7 +108,11 @@ module Mime
def parse(accept_header)
if accept_header !~ /,/
- [Mime::Type.lookup(accept_header)]
+ if accept_header =~ TRAILING_STAR_REGEXP
+ parse_data_with_trailing_star($1)
+ else
+ [Mime::Type.lookup(accept_header)]
+ end
else
# keep track of creation order to keep the subsequent sort stable
list = []
@@ -113,7 +120,11 @@ module Mime
params, q = header.split(/;\s*q=/)
if params
params.strip!
- list << AcceptItem.new(index, params, q) unless params.empty?
+ if params =~ TRAILING_STAR_REGEXP
+ parse_data_with_trailing_star($1).each { |m| list << AcceptItem.new(index, m.to_s, q) }
+ else
+ list << AcceptItem.new(index, params, q) unless params.empty?
+ end
end
end
list.sort!
@@ -160,6 +171,28 @@ module Mime
list
end
end
+
+ # input: 'text'
+ # returend value: [Mime::JSON, Mime::XML, Mime::ICS, Mime::HTML, Mime::CSS, Mime::CSV, Mime::JS, Mime::YAML, Mime::TEXT]
+ #
+ # input: 'application'
+ # returend value: [Mime::HTML, Mime::JS, Mime::XML, Mime::YAML, Mime::ATOM, Mime::JSON, Mime::RSS, Mime::URL_ENCODED_FORM
+ def parse_data_with_trailing_star(input)
+ keys = Mime::LOOKUP.keys.select{|k| k.include?(input)}
+ Mime::LOOKUP.values_at(*keys).uniq
+ end
+
+ # This method is opposite of register method.
+ #
+ # Usage:
+ #
+ # Mime::Type.unregister("text/x-mobile", :mobile)
+ def unregister(string, symbol)
+ EXTENSION_LOOKUP.delete(symbol.to_s)
+ LOOKUP.delete(string)
+ symbol = symbol.to_s.upcase.intern
+ Mime.module_eval { remove_const(symbol) if const_defined?(symbol) }
+ end
end
def initialize(string, symbol = nil, synonyms = [])
diff --git a/actionpack/lib/action_dispatch/http/upload.rb b/actionpack/lib/action_dispatch/http/upload.rb
index 84e58d7d6a..37effade4f 100644
--- a/actionpack/lib/action_dispatch/http/upload.rb
+++ b/actionpack/lib/action_dispatch/http/upload.rb
@@ -1,5 +1,3 @@
-require 'active_support/core_ext/object/blank'
-
module ActionDispatch
module Http
class UploadedFile
@@ -13,6 +11,14 @@ 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
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index 9c9eed2c6d..1e7054f381 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -4,6 +4,27 @@ module ActionDispatch
mattr_accessor :tld_length
self.tld_length = 1
+ def self.extract_domain(host, tld_length = @@tld_length)
+ return nil unless named_host?(host)
+
+ host.split('.').last(1 + tld_length).join('.')
+ end
+
+ def self.extract_subdomains(host, tld_length = @@tld_length)
+ return [] unless named_host?(host)
+ parts = host.split('.')
+ parts[0..-(tld_length+2)]
+ end
+
+ def self.extract_subdomain(host, tld_length = @@tld_length)
+ extract_subdomains(host, tld_length).join('.')
+ end
+
+ def self.named_host?(host)
+ !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
+ end
+
+
# Returns the complete URL used for this request.
def url
protocol + host_with_port + fullpath
@@ -36,10 +57,12 @@ module ActionDispatch
# Returns the port number of this request as an integer.
def port
- if raw_host_with_port =~ /:(\d+)$/
- $1.to_i
- else
- standard_port
+ @port ||= begin
+ if raw_host_with_port =~ /:(\d+)$/
+ $1.to_i
+ else
+ standard_port
+ end
end
end
@@ -56,10 +79,16 @@ module ActionDispatch
port == standard_port
end
- # Returns a \port suffix like ":8080" if the \port number of this request
+ # Returns a number \port suffix like 8080 if the \port number of this request
# is not the default HTTP \port 80 or HTTPS \port 443.
+ def optional_port
+ standard_port? ? nil : port
+ end
+
+ # Returns a string \port suffix, including colon, like ":8080" if the \port
+ # number of this request is not the default HTTP \port 80 or HTTPS \port 443.
def port_string
- port == standard_port ? '' : ":#{port}"
+ standard_port? ? '' : ":#{port}"
end
def server_port
@@ -69,9 +98,7 @@ module ActionDispatch
# Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify
# a different <tt>tld_length</tt>, such as 2 to catch rubyonrails.co.uk in "www.rubyonrails.co.uk".
def domain(tld_length = @@tld_length)
- return nil unless named_host?(host)
-
- host.split('.').last(1 + tld_length).join('.')
+ ActionDispatch::Http::URL.extract_domain(host, tld_length)
end
# Returns all the \subdomains as an array, so <tt>["dev", "www"]</tt> would be
@@ -79,20 +106,17 @@ module ActionDispatch
# such as 2 to catch <tt>["www"]</tt> instead of <tt>["www", "rubyonrails"]</tt>
# in "www.rubyonrails.co.uk".
def subdomains(tld_length = @@tld_length)
- return [] unless named_host?(host)
- parts = host.split('.')
- parts[0..-(tld_length+2)]
+ ActionDispatch::Http::URL.extract_subdomains(host, tld_length)
end
+ # Returns all the \subdomains as a string, so <tt>"dev.www"</tt> would be
+ # returned for "dev.www.rubyonrails.org". You can specify a different <tt>tld_length</tt>,
+ # such as 2 to catch <tt>["www"]</tt> instead of <tt>"www.rubyonrails"</tt>
+ # in "www.rubyonrails.co.uk".
def subdomain(tld_length = @@tld_length)
- subdomains(tld_length).join('.')
+ subdomains(tld_length)
end
- private
-
- def named_host?(host)
- !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
- end
end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index c4c2e1acd7..b0a4e3d949 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -16,17 +16,23 @@ module ActionDispatch
# Examples for writing:
#
# # Sets a simple session cookie.
+ # # This cookie will be deleted when the user's browser is closed.
# cookies[:user_name] = "david"
#
+ # # Assign an array of values to a cookie.
+ # cookies[:lat_lon] = [47.68, -122.37]
+ #
# # Sets a cookie that expires in 1 hour.
# cookies[:login] = { :value => "XJ-122", :expires => 1.hour.from_now }
#
# # Sets a signed cookie, which prevents a user from tampering with its value.
- # # You must specify a value in ActionController::Base.cookie_verifier_secret.
- # cookies.signed[:remember_me] = [current_user.id, current_user.salt]
+ # # The cookie is signed by your app's <tt>config.secret_token</tt> value.
+ # # Rails generates this value by default when you create a new Rails app.
+ # cookies.signed[:user_id] = current_user.id
#
# # Sets a "permanent" cookie (which expires in 20 years from now).
# cookies.permanent[:login] = "XJ-122"
+ #
# # You can also chain these methods:
# cookies.permanent.signed[:login] = "XJ-122"
#
@@ -34,6 +40,7 @@ module ActionDispatch
#
# cookies[:user_name] # => "david"
# cookies.size # => 2
+ # cookies[:lat_lon] # => [47.68, -122.37]
#
# Example for deleting:
#
@@ -275,7 +282,7 @@ module ActionDispatch
"integrity hash for cookie session data. Use " +
"config.secret_token = \"some secret phrase of at " +
"least #{SECRET_MIN_LENGTH} characters\"" +
- "in config/application.rb"
+ "in config/initializers/secret_token.rb"
end
if secret.length < SECRET_MIN_LENGTH
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 879ccc5791..01826fcede 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -439,13 +439,13 @@ module ActionDispatch
# This will create a number of routes for each of the posts and comments
# controller. For Admin::PostsController, Rails will create:
#
- # GET /admin/photos
- # GET /admin/photos/new
- # POST /admin/photos
- # GET /admin/photos/1
- # GET /admin/photos/1/edit
- # PUT /admin/photos/1
- # DELETE /admin/photos/1
+ # GET /admin/posts
+ # GET /admin/posts/new
+ # POST /admin/posts
+ # GET /admin/posts/1
+ # GET /admin/posts/1/edit
+ # PUT /admin/posts/1
+ # DELETE /admin/posts/1
#
# If you want to route /posts (without the prefix /admin) to
# Admin::PostsController, you could use
@@ -473,21 +473,19 @@ module ActionDispatch
# not use scope. In the last case, the following paths map to
# PostsController:
#
- # GET /admin/photos
- # GET /admin/photos/new
- # POST /admin/photos
- # GET /admin/photos/1
- # GET /admin/photos/1/edit
- # PUT /admin/photos/1
- # DELETE /admin/photos/1
+ # GET /admin/posts
+ # GET /admin/posts/new
+ # POST /admin/posts
+ # GET /admin/posts/1
+ # GET /admin/posts/1/edit
+ # PUT /admin/posts/1
+ # DELETE /admin/posts/1
module Scoping
def initialize(*args) #:nodoc:
@scope = {}
super
end
- # Used to route <tt>/photos</tt> (without the prefix <tt>/admin</tt>)
- # to Admin::PostsController:
# === Supported options
# [:module]
# If you want to route /posts (without the prefix /admin) to
@@ -588,13 +586,13 @@ module ActionDispatch
#
# This generates the following routes:
#
- # admin_posts GET /admin/posts(.:format) {:action=>"index", :controller=>"admin/posts"}
- # admin_posts POST /admin/posts(.:format) {:action=>"create", :controller=>"admin/posts"}
- # new_admin_post GET /admin/posts/new(.:format) {:action=>"new", :controller=>"admin/posts"}
- # edit_admin_post GET /admin/posts/:id/edit(.:format) {:action=>"edit", :controller=>"admin/posts"}
- # admin_post GET /admin/posts/:id(.:format) {:action=>"show", :controller=>"admin/posts"}
- # admin_post PUT /admin/posts/:id(.:format) {:action=>"update", :controller=>"admin/posts"}
- # admin_post DELETE /admin/posts/:id(.:format) {:action=>"destroy", :controller=>"admin/posts"}
+ # admin_posts GET /admin/posts(.:format) {:action=>"index", :controller=>"admin/posts"}
+ # admin_posts POST /admin/posts(.:format) {:action=>"create", :controller=>"admin/posts"}
+ # new_admin_post GET /admin/posts/new(.:format) {:action=>"new", :controller=>"admin/posts"}
+ # edit_admin_post GET /admin/posts/:id/edit(.:format) {:action=>"edit", :controller=>"admin/posts"}
+ # admin_post GET /admin/posts/:id(.:format) {:action=>"show", :controller=>"admin/posts"}
+ # admin_post PUT /admin/posts/:id(.:format) {:action=>"update", :controller=>"admin/posts"}
+ # admin_post DELETE /admin/posts/:id(.:format) {:action=>"destroy", :controller=>"admin/posts"}
# === Supported options
#
# The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+ all default to the name of the namespace.
@@ -707,61 +705,61 @@ module ActionDispatch
end
private
- def scope_options
+ def scope_options #:nodoc:
@scope_options ||= private_methods.grep(/^merge_(.+)_scope$/) { $1.to_sym }
end
- def merge_path_scope(parent, child)
+ def merge_path_scope(parent, child) #:nodoc:
Mapper.normalize_path("#{parent}/#{child}")
end
- def merge_shallow_path_scope(parent, child)
+ def merge_shallow_path_scope(parent, child) #:nodoc:
Mapper.normalize_path("#{parent}/#{child}")
end
- def merge_as_scope(parent, child)
+ def merge_as_scope(parent, child) #:nodoc:
parent ? "#{parent}_#{child}" : child
end
- def merge_shallow_prefix_scope(parent, child)
+ def merge_shallow_prefix_scope(parent, child) #:nodoc:
parent ? "#{parent}_#{child}" : child
end
- def merge_module_scope(parent, child)
+ def merge_module_scope(parent, child) #:nodoc:
parent ? "#{parent}/#{child}" : child
end
- def merge_controller_scope(parent, child)
+ def merge_controller_scope(parent, child) #:nodoc:
child
end
- def merge_path_names_scope(parent, child)
+ def merge_path_names_scope(parent, child) #:nodoc:
merge_options_scope(parent, child)
end
- def merge_constraints_scope(parent, child)
+ def merge_constraints_scope(parent, child) #:nodoc:
merge_options_scope(parent, child)
end
- def merge_defaults_scope(parent, child)
+ def merge_defaults_scope(parent, child) #:nodoc:
merge_options_scope(parent, child)
end
- def merge_blocks_scope(parent, child)
+ def merge_blocks_scope(parent, child) #:nodoc:
merged = parent ? parent.dup : []
merged << child if child
merged
end
- def merge_options_scope(parent, child)
+ def merge_options_scope(parent, child) #:nodoc:
(parent || {}).except(*override_keys(child)).merge(child)
end
- def merge_shallow_scope(parent, child)
+ def merge_shallow_scope(parent, child) #:nodoc:
child ? true : false
end
- def override_keys(child)
+ def override_keys(child) #:nodoc:
child.key?(:only) || child.key?(:except) ? [:only, :except] : []
end
end
@@ -969,6 +967,22 @@ module ActionDispatch
# GET /photos/:id/edit
# PUT /photos/:id
# DELETE /photos/:id
+ #
+ # Resources can also be nested infinitely by using this block syntax:
+ #
+ # resources :photos do
+ # resources :comments
+ # end
+ #
+ # 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
+ #
# === Supported options
# [:path_names]
# Allows you to change the paths of the seven default actions.
@@ -977,6 +991,21 @@ module ActionDispatch
# resources :posts, :path_names => { :new => "brand_new" }
#
# The above example will now change /posts/new to /posts/brand_new
+ #
+ # [:module]
+ # Set the module where the controller can be found. Defaults to nothing.
+ #
+ # resources :posts, :module => "admin"
+ #
+ # All requests to the posts resources will now go to +Admin::PostsController+.
+ #
+ # [:path]
+ #
+ # Set a path prefix for this resource.
+ #
+ # resources :posts, :path => "admin"
+ #
+ # All actions for this resource will now be at +/admin/posts+.
def resources(*resources, &block)
options = resources.extract_options!
@@ -1167,7 +1196,7 @@ module ActionDispatch
@scope[:scope_level_resource]
end
- def apply_common_behavior_for(method, resources, options, &block)
+ def apply_common_behavior_for(method, resources, options, &block) #:nodoc:
if resources.length > 1
resources.each { |r| send(method, r, options, &block) }
return true
@@ -1197,23 +1226,23 @@ module ActionDispatch
false
end
- def action_options?(options)
+ def action_options?(options) #:nodoc:
options[:only] || options[:except]
end
- def scope_action_options?
+ def scope_action_options? #:nodoc:
@scope[:options].is_a?(Hash) && (@scope[:options][:only] || @scope[:options][:except])
end
- def scope_action_options
+ def scope_action_options #:nodoc:
@scope[:options].slice(:only, :except)
end
- def resource_scope?
+ def resource_scope? #:nodoc:
[:resource, :resources].include?(@scope[:scope_level])
end
- def resource_method_scope?
+ def resource_method_scope? #:nodoc:
[:collection, :member, :new].include?(@scope[:scope_level])
end
@@ -1239,7 +1268,7 @@ module ActionDispatch
@scope[:scope_level_resource] = old_resource
end
- def resource_scope(resource)
+ def resource_scope(resource) #:nodoc:
with_scope_level(resource.is_a?(SingletonResource) ? :resource : :resources, resource) do
scope(parent_resource.resource_scope) do
yield
@@ -1247,30 +1276,30 @@ module ActionDispatch
end
end
- def nested_options
+ def nested_options #:nodoc:
{}.tap do |options|
options[:as] = parent_resource.member_name
options[:constraints] = { "#{parent_resource.singular}_id".to_sym => id_constraint } if id_constraint?
end
end
- def id_constraint?
+ def id_constraint? #:nodoc:
@scope[:constraints] && @scope[:constraints][:id].is_a?(Regexp)
end
- def id_constraint
+ def id_constraint #:nodoc:
@scope[:constraints][:id]
end
- def canonical_action?(action, flag)
+ def canonical_action?(action, flag) #:nodoc:
flag && resource_method_scope? && CANONICAL_ACTIONS.include?(action.to_s)
end
- def shallow_scoping?
+ def shallow_scoping? #:nodoc:
shallow? && @scope[:scope_level] == :member
end
- def path_for_action(action, path)
+ def path_for_action(action, path) #:nodoc:
prefix = shallow_scoping? ?
"#{@scope[:shallow_path]}/#{parent_resource.path}/:id" : @scope[:path]
@@ -1281,11 +1310,11 @@ module ActionDispatch
end
end
- def action_path(name, path = nil)
+ def action_path(name, path = nil) #:nodoc:
path || @scope[:path_names][name.to_sym] || name.to_s
end
- def prefix_name_for_action(as, action)
+ def prefix_name_for_action(as, action) #:nodoc:
if as
as.to_s
elsif !canonical_action?(action, @scope[:scope_level])
@@ -1293,12 +1322,14 @@ module ActionDispatch
end
end
- def name_for_action(as, action)
+ def name_for_action(as, action) #:nodoc:
prefix = prefix_name_for_action(as, action)
prefix = Mapper.normalize_name(prefix) if prefix
name_prefix = @scope[:as]
if parent_resource
+ return nil if as.nil? && action.nil?
+
collection_name = parent_resource.collection_name
member_name = parent_resource.member_name
end
@@ -1323,7 +1354,7 @@ module ActionDispatch
end
end
- module Shorthand
+ module Shorthand #:nodoc:
def match(*args)
if args.size == 1 && args.last.is_a?(Hash)
options = args.pop
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 32f41934f1..ebced9cabe 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -485,7 +485,8 @@ module ActionDispatch
Generator.new(options, recall, self, extras).generate
end
- RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :port, :trailing_slash, :script_name]
+ RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length,
+ :trailing_slash, :script_name, :anchor, :params, :only_path ]
def _generate_prefix(options = {})
nil
@@ -504,11 +505,8 @@ module ActionDispatch
rewritten_url << (options[:protocol] || "http")
rewritten_url << "://" unless rewritten_url.match("://")
rewritten_url << rewrite_authentication(options)
-
- raise "Missing host to link to! Please provide :host parameter or set default_url_options[:host]" unless options[:host]
-
- rewritten_url << options[:host]
- rewritten_url << ":#{options.delete(:port)}" if options.key?(:port)
+ rewritten_url << host_from_options(options)
+ rewritten_url << ":#{options.delete(:port)}" if options[:port]
end
script_name = options.delete(:script_name)
@@ -562,6 +560,26 @@ module ActionDispatch
end
private
+
+ def host_from_options(options)
+ computed_host = subdomain_and_domain(options) || options[:host]
+ unless computed_host
+ raise ArgumentError, "Missing host to link to! Please provide :host parameter or set default_url_options[:host]"
+ end
+ computed_host
+ end
+
+ def subdomain_and_domain(options)
+ return nil unless options[:subdomain] || options[:domain]
+ tld_length = options[:tld_length] || ActionDispatch::Http::URL.tld_length
+
+ host = ""
+ host << (options[:subdomain] || ActionDispatch::Http::URL.extract_subdomain(options[:host], tld_length))
+ host << "."
+ host << (options[:domain] || ActionDispatch::Http::URL.extract_domain(options[:host], tld_length))
+ host
+ end
+
def handle_positional_args(options)
return unless args = options.delete(:_positional_args)
diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb
index bfdea41f60..d4db78a25a 100644
--- a/actionpack/lib/action_dispatch/routing/url_for.rb
+++ b/actionpack/lib/action_dispatch/routing/url_for.rb
@@ -115,6 +115,13 @@ module ActionDispatch
# * <tt>:host</tt> - Specifies the host the link should be targeted at.
# 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.
+ # * <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.
# * <tt>:port</tt> - Optionally specify the port to connect to.
# * <tt>:anchor</tt> - An anchor name to be appended to the path.
# * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index fee8cad9f5..8fe74c3c80 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -363,6 +363,10 @@ module ActionDispatch
integration_session.url_options
end
+ def respond_to?(method, include_private = false)
+ integration_session.respond_to?(method, include_private) || super
+ end
+
# Delegate unhandled messages to the current session instance.
def method_missing(sym, *args, &block)
reset! unless integration_session