From 2fe43b694f36ddb2062a91eebe61a035147265b1 Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Tue, 23 Nov 2010 00:31:03 +0100 Subject: :subdomain, :domain and :tld_length options can now be used in url_for, allowing for easy manipulation of the host during link generation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_dispatch/http/url.rb | 59 ++++++++++++++-------- .../lib/action_dispatch/routing/route_set.rb | 38 +++++++++++--- actionpack/lib/action_dispatch/routing/url_for.rb | 6 +++ 3 files changed, 77 insertions(+), 26 deletions(-) (limited to 'actionpack/lib/action_dispatch') diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb index 9c9eed2c6d..1f7633cbea 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 @@ -31,15 +52,18 @@ module ActionDispatch # Returns a \host:\port string for this request, such as "example.com" or # "example.com:8080". def host_with_port - "#{host}#{port_string}" + opt_port = optional_port ? ":#{optional_port}" : nil + "#{host}#{opt_port}" end # 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 +80,10 @@ module ActionDispatch port == standard_port end - # Returns a \port suffix like ":8080" if the \port number of this request + # Returns a \port suffix 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}" + def optional_port + standard_port? ? nil : port end def server_port @@ -69,9 +93,7 @@ module ActionDispatch # Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify # a different tld_length, 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 ["dev", "www"] would be @@ -79,20 +101,17 @@ module ActionDispatch # such as 2 to catch ["www"] instead of ["www", "rubyonrails"] # 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 "dev.www" would be + # returned for "dev.www.rubyonrails.org". You can specify a different tld_length, + # such as 2 to catch ["www"] instead of "www.rubyonrails" + # 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/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 32f41934f1..d823fd710e 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,34 @@ 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) + tld_length = options[:tld_length] || ActionDispatch::Http::URL.tld_length + + current_domain = ActionDispatch::Http::URL.extract_domain(options[:host], tld_length) + current_subdomain = ActionDispatch::Http::URL.extract_subdomain(options[:host], tld_length) + + domain_parts = if options[:subdomain] && options[:domain] + [options[:subdomain], options[:domain]] + elsif options[:subdomain] + [options[:subdomain], current_domain] + elsif options[:domain] + [current_subdomain, options[:domain]] + else + nil + end + + domain_parts ? domain_parts.join('.') : nil + 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..6c3fc5126a 100644 --- a/actionpack/lib/action_dispatch/routing/url_for.rb +++ b/actionpack/lib/action_dispatch/routing/url_for.rb @@ -115,6 +115,12 @@ module ActionDispatch # * :host - Specifies the host the link should be targeted at. # If :only_path is false, this option must be # provided either explicitly, or via +default_url_options+. + # * :subdomain - Specifies the subdomain of the link, using the +tld_length+ + # to split the domain from the host. + # * :domain - Specifies the domain of the link, using the +tld_length+ + # to split the subdomain from the host. + # * :tld_length - Optionally specify the tld length (only used if :subdomain + # or :domain are supplied). # * :port - Optionally specify the port to connect to. # * :anchor - An anchor name to be appended to the path. # * :trailing_slash - If true, adds a trailing slash, as in "/archive/2009/" -- cgit v1.2.3