diff --git a/lib/subdomain_routes.rb b/lib/subdomain_routes.rb index 47bb845..6833ad7 100644 --- a/lib/subdomain_routes.rb +++ b/lib/subdomain_routes.rb @@ -4,4 +4,5 @@ require 'subdomain_routes/routing' require 'subdomain_routes/resources' -require 'subdomain_routes/url_for' +require 'subdomain_routes/url_writer' +require 'subdomain_routes/url_rewriter' diff --git a/lib/subdomain_routes/url_for.rb b/lib/subdomain_routes/url_rewriter.rb similarity index 53% rename from lib/subdomain_routes/url_for.rb rename to lib/subdomain_routes/url_rewriter.rb index 8c82357..43e0708 100644 --- a/lib/subdomain_routes/url_for.rb +++ b/lib/subdomain_routes/url_rewriter.rb @@ -1,21 +1,4 @@ module SubdomainRoutes - module UrlWriter - def self.included(base) - base.alias_method_chain :url_for, :subdomains - end - - def url_for_with_subdomains(options) - first_part, *other_parts = options[:host].split(".") - if subdomain = options.delete(:subdomain) - if subdomain.to_s != first_part - options[:only_path] = false - options[:host] = other_parts.unshift(subdomain).join('.') - end - end - url_for_without_subdomains(options) - end - end - module UrlRewriter def self.included(base) base.alias_method_chain :rewrite, :subdomains @@ -35,6 +18,4 @@ def rewrite_with_subdomains(options) end end -ActionController::UrlWriter.send :include, SubdomainRoutes::UrlWriter ActionController::UrlRewriter.send :include, SubdomainRoutes::UrlRewriter - diff --git a/lib/subdomain_routes/url_writer.rb b/lib/subdomain_routes/url_writer.rb new file mode 100644 index 0000000..c125481 --- /dev/null +++ b/lib/subdomain_routes/url_writer.rb @@ -0,0 +1,22 @@ +module SubdomainRoutes + module UrlWriter + def self.included(base) + base.alias_method_chain :url_for, :subdomains + end + + def url_for_with_subdomains(options) + if subdomain = options.delete(:subdomain) + host = options[:host] || default_url_options[:host] + raise "Missing host to link to! Please provide :host parameter or set default_url_options[:host]" unless host + first_part, *other_parts = host.split(".") + if subdomain.to_s != first_part + options[:only_path] = false + options[:host] = other_parts.unshift(subdomain).join('.') + end + end + url_for_without_subdomains(options) + end + end +end + +ActionController::UrlWriter.send :include, SubdomainRoutes::UrlWriter diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 7e24f66..5d646db 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -5,5 +5,51 @@ require 'subdomain_routes' Spec::Runner.configure do |config| - +end + + +require 'action_controller/test_process' +require 'action_view/test_case' + +ActiveSupport::OptionMerger.send(:define_method, :options) { @options } + +def map_subdomain(*subdomains, &block) + ActionController::Routing::Routes.draw do |map| + map.subdomain(*subdomains, &block) + end +end + +def recognize_path(request) + ActionController::Routing::Routes.recognize_path(request.path, ActionController::Routing::Routes.extract_request_environment(request)) +end + +def with_host(host, &block) + eval %Q{ + Class.new(ActionView::TestCase::TestController) do + def initialize + super + request.host = "#{host}" + end + end.new.instance_eval(&block) + + Class.new do + include ActionController::UrlWriter + self.default_url_options = { :host => "#{host}" } + end.new.instance_eval(&block) + } +end + +def new_class(*names) + names.map do |name| + class_name = name.to_s.capitalize + unless Object.const_defined?(class_name) + klass = Class.new do + attr_reader :id + def save; @id = object_id; end + def to_param; id.to_s; end + def self.create; object = new; object.save; object; end + end + Object.const_set(class_name, klass) + end + end end diff --git a/spec/subdomain_routes_spec.rb b/spec/subdomain_routes_spec.rb index ca5cb5d..c55a989 100644 --- a/spec/subdomain_routes_spec.rb +++ b/spec/subdomain_routes_spec.rb @@ -1,26 +1,5 @@ require 'spec_helper' -def map_subdomain(*subdomains, &block) - ActiveSupport::OptionMerger.send(:define_method, :options) { @options } - ActionController::Routing::Routes.draw do |map| - map.subdomain(*subdomains, &block) - end -end - -def environment(headers = {}) - { 'REQUEST_METHOD' => "GET", - 'QUERY_STRING' => "", - "REQUEST_URI" => "/", - "HTTP_HOST" => "www.example.com", - "SERVER_PORT" => "80", - "HTTPS" => "off" }.merge(headers) -end - -def recognize_path(request) - ActionController::Routing::Routes.recognize_path(request.path, ActionController::Routing::Routes.extract_request_environment(request)) -end - - describe SubdomainRoutes do before(:each) do ActionController::Routing::Routes.clear! @@ -119,7 +98,7 @@ def recognize_path(request) end describe "resources route" do - it "should transfer specified subdomains to any nested routes" do + it "should pass the specified subdomains to any nested routes" do map_subdomain(:admin) do |admin| admin.resources(:items) { |item| item.options[:subdomains].should == [ :admin ] } admin.resource(:config) { |config| config.options[:subdomains].should == [ :admin ] } @@ -129,35 +108,36 @@ def recognize_path(request) describe "route recognition" do before(:each) do - @environment = environment("HTTP_HOST" => "www.example.com", "REQUEST_URI" => "/items/2") - @request = ActionController::Request.new(@environment) - class ItemsController < ActionController::Base; end + @request = ActionController::TestRequest.new + @request.host, @request.request_uri = "www.example.com", "/items/2" + @subdomain = @request.host.downcase.split(".").first end it "should add the host's subdomain to the request environment" do request_environment = ActionController::Routing::Routes.extract_request_environment(@request) - request_environment[:subdomain].should == @request.host.downcase.split(".").first - request_environment[:subdomain].should == "www" + request_environment[:subdomain].should == @subdomain end context "for a single specified subdomain" do it "should recognise a route if the subdomain matches" do - map_subdomain(:www) { |www| www.resources :items } + map_subdomain(@subdomain) { |subdomain| subdomain.resources :items } params = recognize_path(@request) - params[:controller].should == "www/items" + params[:controller].should == "#{@subdomain}/items" params[:action].should == "show" params[:id].should == "2" end it "should not recognise a route if the subdomain doesn't match" do - map_subdomain(:admin) { |admin| admin.resources :items } + "admin".should_not == @subdomain + map_subdomain("admin") { |admin| admin.resources :items } lambda { recognize_path(@request) }.should raise_error(ActionController::RoutingError) end end context "for multiple specified subdomains" do it "should recognise a route if the subdomain matches" do - map_subdomain(:www, :admin, :name => nil) { |map| map.resources :items } + "admin".should_not == @subdomain + map_subdomain(@subdomain, "admin", :name => nil) { |map| map.resources :items } params = recognize_path(@request) params[:controller].should == "items" params[:action].should == "show" @@ -165,47 +145,96 @@ class ItemsController < ActionController::Base; end end it "should not recognise a route if the subdomain doesn't match" do - map_subdomain(:support, :admin, :name => nil) { |map| map.resources :items } + [ "support", "admin" ].each { |subdomain| subdomain.should_not == @subdomain } + map_subdomain("support", "admin", :name => nil) { |map| map.resources :items } lambda { recognize_path(@request) }.should raise_error(ActionController::RoutingError) end end end - # # TODO: url writing and rewriting is all we have left to test! - - # describe "UrlWriter" do - # include ActionController::UrlWriter - # - # context "when a single subdomain is specified in the route" do - # it "should force the host for a path if the host subdomain differs" do - # map_subdomain(:admin) { |admin| admin.resources :users } - # admin_users_path(:host => "www.example.com").should == "http://admin.example.com/users" - # end - # - # it "should not force the host for a path if the host subdomain is the same" do - # map_subdomain(:www) { |www| www.resources :users } - # www_users_path(:host => "www.example.com").should == "/users" - # end - # end - # end - - # describe "UrlRewriter" do - # - # context "when multiple subdomains are specified in the route" do - # it "should not force the host for a path if the request subdomain differs" do - # map_subdomain(:www, :name => nil) { |map| map.resources :users } - # @rewriter.rewrite(:controller => :users, :action => :index).should == "/users" # i.e. with the host being www.example.com, this route won't be recognised! - # # - # # TODO: - # # - # # This is a currently a limitation of the library. - # # - # # Ideally, in this case the host should not be changed. Instead - # # an error should be raised if the request subdomain is not one - # # of the subdomains specified in the route, or a path generated - # # otherwise. Can't figure out how to do this!! - # # - # end - # end - # end + describe "URL writing" do + before(:all) do + new_class :user, :article, :item + end + + context "when a single subdomain is specified" do + before(:each) do + map_subdomain(:admin) { |admin| admin.resources :users } + end + + it "should not change the host for an URL if the subdomains are the same" do + with_host "admin.example.com" do + admin_users_url.should == "http://admin.example.com/users" + @user = User.create + polymorphic_url([ :admin, @user ]).should == "http://admin.example.com/users/#{@user.to_param}" + end + end + + it "should change the host for an URL if the subdomains differ" do + with_host "www.example.com" do + admin_users_url.should == "http://admin.example.com/users" + @user = User.create + polymorphic_url([ :admin, @user ]).should == "http://admin.example.com/users/#{@user.to_param}" + end + end + + it "should not force the host for a path if the subdomains are the same" do + with_host "admin.example.com" do + admin_users_path.should == "/users" + @user = User.create + polymorphic_path([ :admin, @user ]).should == "/users/#{@user.to_param}" + end + end + + it "should force the host for a path if the subdomains differ" do + with_host "www.example.com" do + admin_users_path.should == "http://admin.example.com/users" + @user = User.create + polymorphic_path([ :admin, @user ]).should == "http://admin.example.com/users/#{@user.to_param}" + end + end + end + + context "when multiple subdomains are specified" do + before(:each) do + map_subdomain(:books, :dvds, :cds, :name => nil) { |map| map.resources :items } + end + + # + # TODO: + # + # This is a currently a limitation of the library. + # + # Ideally, if the current subdomain does not match any of those specified in the requested route, + # one of two things should happen: + # 1. the route should generate if a :subdomain option is specified with a matching subdomain, or + # 2. an error should be raised if no (or a non-matching) :subdomain option is specified. + # At present I can't figure out how to do this!! + # + # Thes tests below would be the ones to change to spec the desired behaviour, along with those + # describing "for multiple specified subdomains" in the routing tests above (which is where the + # implementation would likely go). + # + + it "should not change the host for an URL, irrespective of the host subdomain" do + [ "books.example.com", "dvds.example.com", "www.example.com" ].each do |host| + with_host(host) do + items_url.should == "http://#{host}/items" + @item = Item.create + polymorphic_url(@item).should == "http://#{host}/items/#{@item.to_param}" + end + end + end + + it "should not force the host for a path, irrespective of the host subdomain" do + [ "books.example.com", "dvds.example.com", "www.example.com" ].each do |host| + with_host(host) do + items_path.should == "/items" + @item = Item.create + polymorphic_path(@item).should == "/items/#{@item.to_param}" + end + end + end + end + end end