mholling-subdomain_routes 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.textile CHANGED
@@ -21,6 +21,8 @@ In your Rails app, make sure to specify the gem dependency in environment.rb:
21
21
  config.gem "mholling-subdomain_routes", :lib => "subdomain_routes", :source => "http://gems.github.com"
22
22
  </pre>
23
23
 
24
+ [ *UPDATE:* You can also install the gem as a plugin: <code>script/plugin install git://github.com/mholling/subdomain_routes.git</code> ]
25
+
24
26
  (Note that the SubdomainRoutes gem requires Rails 2.2 or later to run since it changes <code>ActionController::Resources::INHERITABLE_OPTIONS</code>. If you're on an older version of Rails, you need to get with the program. ;)
25
27
 
26
28
  Finally, you'll probably want to configure your session to work across all your subdomains. You can do this in your environment files:
@@ -33,6 +35,8 @@ config.action_controller.session[:session_domain] = "yourdomain.local" # or what
33
35
  config.action_controller.session[:session_domain] = "yourdomain.com" # or whatever
34
36
  </pre>
35
37
 
38
+ [ *UPDATE*: If you're using your domain without any subdomain, you may need to set the domain to ".yourdomain.com" (with leading period). Also, you may first need to set <code>config.action_controller.session ||= {}</code> in your environment file, in case the session configuration variable has not already been set. ]
39
+
36
40
  h2. Mapping a Single Subdomain
37
41
 
38
42
  Let's start with a simple example. Say we have a site which offers a support section, where users submit and view support tickets for problems they're having. It'd be nice to have that under a separate subdomain, say _support.mysite.com_. With subdomain routes we'd map that as follows:
@@ -284,5 +288,134 @@ If you're using model-based subdomain routes (covered next), you may want to use
284
288
  # Use a "proxy.pac":http://en.wikipedia.org/wiki/Proxy.pac file in your browser so that it proxies _*.reviews.local_ to localhost. How to do this will depend on the browser you're using.
285
289
  # Set up a local DNS server with an A record for the domain. This may be a bit involved.
286
290
 
291
+ h2. Testing with Subdomain Routes
292
+
293
+ Testing routes and controllers with the SubdomainRoutes gem may require a little extra work. Here's a simple <code>routes.rb</code> as an example:
294
+
295
+ <pre>
296
+ map.subdomain :admin do |admin|
297
+ admin.resources :users
298
+ end
299
+
300
+ map.subdomain :model => :city do |city|
301
+ city.resources :reviews, :only => [ :index, :show ]
302
+ end
303
+ </pre>
304
+
305
+ (This connects a fixed _admin_ subdomain to a <code>Admin::UsersController</code>, and a model-based _city_ subdomain to a <code>City::ReviewsController</code>.)
306
+
307
+ h2. Testing Controllers
308
+
309
+ A simple test for the <code>Admin::UsersController#show</code> action would go along these lines:
310
+
311
+ <pre>
312
+ class Admin::UsersControllerTest < ActionController::TestCase
313
+ test "show action" do
314
+ get :show, :id => "1", :subdomains => [ "admin" ]
315
+ assert_response :success # or whatever
316
+ end
317
+ end
318
+ </pre>
319
+
320
+ Notice the <code>:subdomains => [ "admin" ]</code> in the hash passed to the <code>get</code> method. This is the additional requirement for testing controller actions which lie under a subdomain route. Your tests won't work without it. The same applies for <code>post</code>, <code>put</code> and <code>delete</code>.
321
+
322
+ (For testing the model-based subdomain routes, <code>:subdomains => :city_id</code> and <code>:city_id => "..."</code> would be added to the route's options hash. Check the specs for more examples, if you need them.)
323
+
324
+ It's a little bit ugly (and not too DRY) to have to list the subdomains for the route in the test. Want to change the actual subdomains you are using? You'll have to change your tests as well. But that's the way it goes. (One way to avoid this brittleness, at least, would be to assign the subdomains to a constant and use the constant in your routes and tests.)
325
+
326
+ It's easy to figure out what <code>:subdomain</code> option you should pass. Just look it up in your routes by typing <code>rake routes</code> at the console:
327
+
328
+ <pre>
329
+ admin_users GET /users(.:format) {:action=>"index", :subdomains=>["admin"], :controller=>"admin/users"}
330
+ POST /users(.:format) {:action=>"create", :subdomains=>["admin"], :controller=>"admin/users"}
331
+ new_admin_user GET /users/new(.:format) {:action=>"new", :subdomains=>["admin"], :controller=>"admin/users"}
332
+ edit_admin_user GET /users/:id/edit(.:format) {:action=>"edit", :subdomains=>["admin"], :controller=>"admin/users"}
333
+ admin_user GET /users/:id(.:format) {:action=>"show", :subdomains=>["admin"], :controller=>"admin/users"}
334
+ PUT /users/:id(.:format) {:action=>"update", :subdomains=>["admin"], :controller=>"admin/users"}
335
+ DELETE /users/:id(.:format) {:action=>"destroy", :subdomains=>["admin"], :controller=>"admin/users"}
336
+ city_reviews GET /reviews(.:format) {:action=>"index", :subdomains=>:city_id, :controller=>"city/reviews"}
337
+ city_review GET /reviews/:id(.:format) {:action=>"show", :subdomains=>:city_id, :controller=>"city/reviews"}
338
+ </pre>
339
+
340
+ The <code>:subdomain</code> option you need to use is listed right there in the right-most column of each route.
341
+
342
+ h2. Testing Subdomain Routes
343
+
344
+ An underlying assumption in the Rails routing code is that the _path_ is all that's needed to specify an URL, since the host is assumed to be fixed and irrelevant. In some parts of the routing assertions code, this assumption is fairly tightly entangled in the code. Obviously, for subdomain routes, it's an invalid assumption.
345
+
346
+ Augmenting Rails' <code>assert_generates</code> and <code>assert_recognizes</code> methods to allow for a changeable host is not really a practical or sensible option. Instead, SubdomainRoutes adds some new assertions specifically for testing subdomain routes.
347
+
348
+ h3. Testing Recognition
349
+
350
+ The signature for Rails' traditional <code>assert_recognizes</code> method looks like this:
351
+
352
+ <pre>
353
+ def assert_recognizes(expected_options, path, extras={}, message=nil)
354
+ </pre>
355
+
356
+ The <code>expected_options</code> path is options hash describing the route that should be recognised (always including <code>:controller</code> and <code>:action</code>, as well as any other parameters that the route might produce). The <code>path</code> can either be a string representing the path, or a hash with <code>:path</code> and <code>:method</code> values (if you need to specify an HTTP method other than GET).
357
+
358
+ For <code>assert_recognizes_with_host</code> the same arguments are kept, since the <code>:host</code> can be passed as another option in the <code>path</code> hash. The <code>:host</code> option represents what host should be set in the <code>TestRequest</code> that's used to recognise the path. (Unlike traditional routes, the subdomain, and hence the host, is required for recognition of the route.)
359
+
360
+ So a typical passing recognition test for the user index route would be:
361
+
362
+ <pre>
363
+ test "admin_users route recognition" do
364
+ assert_recognizes_with_host(
365
+ { :controller => "admin/users", :action => "index", :subdomains => [ "admin" ] },
366
+ { :path => "/users", :host => "admin.example.com" })
367
+ end
368
+ </pre>
369
+
370
+ Notice the correct subdomain for this route is specified in the host. Note also the annoying <code>:subdomains</code> value in the first options hash. It needs to be there as well, to specify the route.
371
+
372
+ h3. Testing Generation
373
+
374
+ Testing route generation is a little more involved. The Rails assertion is as follows:
375
+
376
+ <pre>
377
+ def assert_generates(expected_path, options, defaults={}, extras={}, message=nil)
378
+ </pre>
379
+
380
+ This method asserts that <code>expected_path</code> (a string) is the path generated by the route <code>options</code>. But with subdomain routes, the generated route may also depend on the current host - if the subdomain for the route is different than the current host, the host will be forced to the new subdomain.
381
+
382
+ To allow testing of this behaviour, the <code>assert_generates_with_host</code> method is introduced. This assertion allows you to specify the current host, as well as the host that the new route should have (if different):
383
+
384
+ <pre>
385
+ def assert_generates_with_host(expected_path, options, host, defaults={}, extras={}, message=nil)
386
+ </pre>
387
+
388
+ Notice the additional third argument, <code>host</code>, which you should set to the current host (i.e. the host under which the route is being generated).
389
+
390
+ Now to test an example route. First, test the case where the host doesn't change:
391
+
392
+ <pre>
393
+ test "admin_users route generation for the same subdomain" do
394
+ assert_generates_with_host(
395
+ "/users",
396
+ { :controller => "admin/users", :action => "index", :subdomains => [ "admin" ] },
397
+ "admin.example.com")
398
+ end
399
+ </pre>
400
+
401
+ The assertion in this test is saying that, for _admin.example.com_, the index route should generate <code>"/users"</code> as the path, and not change the host. The test passes as this is expected behaviour.
402
+
403
+ The second test case covers the case of generating the route from a host with a different subdomain:
404
+
405
+ <pre>
406
+ test "admin_users route generation for a different subdomain" do
407
+ assert_generates_with_host(
408
+ { :path => "/users", :host => "admin.example.com" },
409
+ { :controller => "admin/users", :action => "index", :subdomains => [ "admin" ] },
410
+ "www.example.com")
411
+ end
412
+ </pre>
413
+
414
+ Here the usage diverges from <code>assert_generates</code>: instead of passing a string as the expected path, a hash is passed. As with <code>assert_recognizes</code>, the hash is used to specify both the <code>:path</code> and the <code>:host</code> that the route should generate. The above test passes because the route changes the subdomain from _www_ to _admin_. (This only occurs in a single-subdomain route, of course.)
415
+
416
+ h3. Use with RSpec
417
+
418
+ The subdomain routing assertions won't help you much if you're using RSpec or some other testing framework. Your best bet is to wrap each assertion up in its own class, just as RSpec does with <code>assert_recognizes</code> in its <code>route_for</code> method (check the rspec-rails source code). This shouldn't be too hard to do.
419
+
287
420
 
288
421
  Copyright (c) 2009 Matthew Hollingworth. See LICENSE for details.
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
- :patch: 3
2
+ :patch: 0
3
3
  :major: 0
4
- :minor: 2
4
+ :minor: 3
@@ -10,3 +10,4 @@ require 'subdomain_routes/resources'
10
10
  require 'subdomain_routes/url_writer'
11
11
  require 'subdomain_routes/request'
12
12
  require 'subdomain_routes/validations'
13
+ require 'subdomain_routes/assertions'
@@ -0,0 +1,68 @@
1
+ module SubdomainRoutes
2
+ module RoutingAssertions
3
+ include SubdomainRoutes::RewriteSubdomainOptions
4
+
5
+ def assert_recognizes_with_host(expected_options, path, extras={}, message=nil)
6
+ # copied from Rails source, with modification to set the the supplied host on the request
7
+ if path.is_a? Hash
8
+ request_method = path[:method]
9
+ host = path[:host]
10
+ path = path[:path]
11
+ else
12
+ request_method = nil
13
+ host = nil
14
+ end
15
+ clean_backtrace do
16
+ ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
17
+ request = recognized_request_for_with_host(path, host, request_method)
18
+
19
+ expected_options = expected_options.clone
20
+ extras.each_key { |key| expected_options.delete key } unless extras.nil?
21
+
22
+ expected_options.stringify_keys!
23
+ routing_diff = expected_options.diff(request.path_parameters)
24
+
25
+ msg = build_message(message, "The recognized options <?> did not match <?>, difference: <?>",
26
+ request.path_parameters, expected_options, expected_options.diff(request.path_parameters))
27
+ assert_block(msg) { request.path_parameters == expected_options }
28
+ end
29
+ end
30
+
31
+ def assert_generates_with_host(expected_path, options, host, defaults={}, extras = {}, message=nil)
32
+ if expected_path.is_a? Hash
33
+ expected_host = expected_path[:host]
34
+ expected_path = expected_path[:path]
35
+ else
36
+ expected_host = nil
37
+ end
38
+ rewrite_subdomain_options(options, host)
39
+ if options.delete(:only_path) == false
40
+ generated_host = options.delete(:host)
41
+ msg = expected_host ?
42
+ build_message(message, "The route for <?> changed the host to <?> but this did not match expected host <?>", options, generated_host, expected_host) :
43
+ build_message(message, "The route for <?> changed the host to <?> but the host was not expected to change", options, generated_host)
44
+ assert_block(msg) { expected_host == generated_host }
45
+ else
46
+ msg = build_message(message, "The route for <?> was expected to change the host to <?> but did not change the host", options, expected_host)
47
+ assert_block(msg) { expected_host == nil }
48
+ end
49
+ assert_generates(expected_path, options, defaults, extras, message)
50
+ end
51
+
52
+ private
53
+
54
+ def recognized_request_for_with_host(path, host, request_method = nil)
55
+ path = "/#{path}" unless path.first == '/'
56
+
57
+ # Assume given controller
58
+ request = ActionController::TestRequest.new
59
+ request.env["REQUEST_METHOD"] = request_method.to_s.upcase if request_method
60
+ request.path = path
61
+ request.host = host if host
62
+ ActionController::Routing::Routes.recognize(request)
63
+ request
64
+ end
65
+ end
66
+ end
67
+
68
+ ActionController::Assertions::RoutingAssertions.send :include, SubdomainRoutes::RoutingAssertions
@@ -3,14 +3,15 @@ module SubdomainRoutes
3
3
  module RouteSet
4
4
  module Mapper
5
5
  def subdomain(*subdomains, &block)
6
- options = subdomains.extract_options!
6
+ options = subdomains.extract_options!.dup
7
7
  if subdomains.empty?
8
8
  if model = options.delete(:model)
9
9
  raise ArgumentError, "Invalid model name" if model.blank?
10
10
  models = model.to_s.downcase.pluralize
11
11
  model = models.singularize
12
12
  model_id = model.foreign_key.to_sym
13
- subdomain_options = { :subdomains => model_id, :name_prefix => "#{model}_", :namespace => "#{model}/" }
13
+ subdomain_options = { :subdomains => model_id }
14
+ name = options.has_key?(:name) ? options.delete(:name) : model
14
15
  else
15
16
  raise ArgumentError, "Please specify at least one subdomain!"
16
17
  end
@@ -27,9 +28,9 @@ module SubdomainRoutes
27
28
  subdomain_options = { :subdomains => subdomains }
28
29
  name = subdomains.reject(&:blank?).first
29
30
  name = options.delete(:name) if options.has_key?(:name)
30
- name = name.to_s.downcase.gsub(/[^(a-z0-9)]/, ' ').squeeze(' ').strip.gsub(' ', '_') unless name.blank?
31
- subdomain_options.merge! :name_prefix => "#{name}_", :namespace => "#{name}/" unless name.blank?
32
31
  end
32
+ name = name.to_s.downcase.gsub(/[^(a-z0-9)]/, ' ').squeeze(' ').strip.gsub(' ', '_') unless name.blank?
33
+ subdomain_options.merge! :name_prefix => "#{name}_", :namespace => "#{name}/" unless name.blank?
33
34
  with_options(subdomain_options.merge(options), &block)
34
35
  end
35
36
  alias_method :subdomains, :subdomain
@@ -43,18 +43,18 @@ module SubdomainRoutes
43
43
 
44
44
  module Route
45
45
  def self.included(base)
46
- [ :recognition_conditions, :generation_extraction, :segment_keys, :recognition_extraction ].each { |method| base.alias_method_chain method, :subdomains }
46
+ [ :recognition_conditions, :generation_extraction, :segment_keys, :significant_keys, :recognition_extraction ].each { |method| base.alias_method_chain method, :subdomains }
47
47
  end
48
48
 
49
49
  def recognition_conditions_with_subdomains
50
- result = recognition_conditions_without_subdomains
51
- case conditions[:subdomains]
52
- when Array
53
- result << "conditions[:subdomains].include?(env[:subdomain])"
54
- when Symbol
55
- result << "(subdomain = env[:subdomain] unless env[:subdomain].blank?)"
50
+ returning recognition_conditions_without_subdomains do |result|
51
+ case conditions[:subdomains]
52
+ when Array
53
+ result << "conditions[:subdomains].include?(env[:subdomain])"
54
+ when Symbol
55
+ result << "(subdomain = env[:subdomain] unless env[:subdomain].blank?)"
56
+ end
56
57
  end
57
- result
58
58
  end
59
59
 
60
60
  def generation_extraction_with_subdomains
@@ -66,15 +66,24 @@ module SubdomainRoutes
66
66
  end
67
67
 
68
68
  def segment_keys_with_subdomains
69
- result = segment_keys_without_subdomains
70
- result.unshift(conditions[:subdomains]) if conditions[:subdomains].is_a? Symbol
71
- result
69
+ returning segment_keys_without_subdomains do |result|
70
+ result.unshift(conditions[:subdomains]) if conditions[:subdomains].is_a? Symbol
71
+ end
72
+ end
73
+
74
+ def significant_keys_with_subdomains
75
+ returning significant_keys_without_subdomains do |result|
76
+ if conditions[:subdomains].is_a? Symbol
77
+ result << conditions[:subdomains]
78
+ result.uniq!
79
+ end
80
+ end
72
81
  end
73
82
 
74
83
  def recognition_extraction_with_subdomains
75
- result = recognition_extraction_without_subdomains
76
- result.unshift "\nparams[#{conditions[:subdomains].inspect}] = subdomain\n" if conditions[:subdomains].is_a? Symbol
77
- result
84
+ returning recognition_extraction_without_subdomains do |result|
85
+ result.unshift "\nparams[#{conditions[:subdomains].inspect}] = subdomain\n" if conditions[:subdomains].is_a? Symbol
86
+ end
78
87
  end
79
88
 
80
89
  def reserved_subdomains
@@ -0,0 +1,193 @@
1
+ require 'test/unit/testresult'
2
+ require 'spec_helper'
3
+
4
+ class HomesController < ActionController::Base
5
+ end
6
+
7
+ describe "routing assertions" do
8
+ context "for single-subdomain route" do
9
+ before(:each) do
10
+ map_subdomain :admin, :name => nil do |admin|
11
+ admin.resource :home
12
+ end
13
+ @options = { :controller => "homes", :action => "show", :subdomains => [ "admin" ] }
14
+ end
15
+
16
+ context "recognition" do
17
+ it "should correctly succeed with a :host option and a subdomain route" do
18
+ test = lambda { assert_recognizes_with_host(@options, { :path => "/home", :host => "admin.example.com" }) }
19
+ test.should_not have_errors
20
+ test.should_not fail
21
+ end
22
+
23
+ it "should correctly fail with a :host option and a subdomain route" do
24
+ test = lambda { assert_recognizes_with_host(@options, { :path => "/home", :host => "www.example.com" } ) }
25
+ test.should have_errors
26
+ end
27
+ end
28
+
29
+ context "generation" do
30
+ it "should correctly succeed with :host and :path options and a subdomain route which changes the subdomain and no subdomain is specified" do
31
+ test = lambda { assert_generates_with_host({ :path => "/home", :host => "admin.example.com" }, @options, "www.example.com") }
32
+ test.should_not have_errors
33
+ test.should_not fail
34
+ end
35
+
36
+ it "should correctly fail with :host and :path options and a subdomain route which changes the subdomain and the wrong subdomain is specified" do
37
+ test = lambda { assert_generates_with_host({ :path => "/home", :host => "admin.example.com" }, @options.merge(:subdomain => "other"), "www.example.com") }
38
+ test.should have_errors
39
+ end
40
+
41
+ it "should correctly fail with :host and :path options and a subdomain route which changes the subdomain and the correct subdomain is specified" do
42
+ test = lambda { assert_generates_with_host({ :path => "/home", :host => "admin.example.com" }, @options.merge(:subdomain => "admin"), "www.example.com") }
43
+ test.should_not have_errors
44
+ test.should_not fail
45
+ end
46
+
47
+ it "should correctly fail with :host and :path options and a subdomain route which doesn't change the subdomain" do
48
+ test = lambda { assert_generates_with_host({ :path => "/home", :host => "admin.example.com" }, @options, "admin.example.com") }
49
+ test.should_not have_errors
50
+ test.should fail
51
+ end
52
+
53
+ it "should correctly succeed with a path and a subdomain route which doesn't change the subdomain" do
54
+ test = lambda { assert_generates_with_host("/home", @options, "admin.example.com") }
55
+ test.should_not have_errors
56
+ test.should_not fail
57
+ end
58
+
59
+ it "should correctly fail with a path and a subdomain route which changes the subdomain" do
60
+ test = lambda { assert_generates_with_host("/home", @options, "www.example.com") }
61
+ test.should_not have_errors
62
+ test.should fail
63
+ end
64
+ end
65
+ end
66
+
67
+ context "for multiple-subdomain route" do
68
+ before(:each) do
69
+ @subdomains = [ "admin", "support" ]
70
+ map_subdomain *(@subdomains + [ { :name => nil } ]) do |media|
71
+ media.resource :home
72
+ end
73
+ @options = { :controller => "homes", :action => "show", :subdomains => @subdomains }
74
+ end
75
+
76
+ context "recognition" do
77
+ it "should correctly succeed with a :host option and a subdomain route" do
78
+ @subdomains.each do |subdomain|
79
+ test = lambda { assert_recognizes_with_host(@options, { :path => "/home", :host => "#{subdomain}.example.com" }) }
80
+ test.should_not have_errors
81
+ test.should_not fail
82
+ end
83
+ end
84
+
85
+ it "should correctly fail with a :host option and a subdomain route" do
86
+ test = lambda { assert_recognizes_with_host(@options, { :path => "/home", :host => "www.example.com" } ) }
87
+ test.should have_errors
88
+ end
89
+ end
90
+
91
+ context "generation" do
92
+ it "should correctly fail with :host and :path options and a subdomain route which changes the subdomain and no subdomain is specified" do
93
+ @subdomains.each do |subdomain|
94
+ test = lambda { assert_generates_with_host({ :path => "/home", :host => "#{subdomain}.example.com" }, @options, "www.example.com") }
95
+ test.should have_errors
96
+ end
97
+ end
98
+
99
+ it "should correctly fail with :host and :path options and a subdomain route which changes the subdomain and the wrong subdomain is specified" do
100
+ @subdomains.each do |subdomain|
101
+ test = lambda { assert_generates_with_host({ :path => "/home", :host => "#{subdomain}.example.com" }, @options.merge(:subdomain => "other"), "www.example.com") }
102
+ test.should have_errors
103
+ end
104
+ end
105
+
106
+ it "should correctly succeed with :host and :path options and a subdomain route which changes the subdomain and a correct subdomain is specified" do
107
+ @subdomains.each do |subdomain|
108
+ test = lambda { assert_generates_with_host({ :path => "/home", :host => "#{subdomain}.example.com" }, @options.merge(:subdomain => subdomain), "www.example.com") }
109
+ test.should_not have_errors
110
+ test.should_not fail
111
+ end
112
+ end
113
+
114
+ it "should correctly fail with :host and :path options and a subdomain route which doesn't change the subdomain" do
115
+ @subdomains.each do |subdomain|
116
+ test = lambda { assert_generates_with_host({ :path => "/home", :host => "#{subdomain}.example.com" }, @options, "#{subdomain}.example.com") }
117
+ test.should_not have_errors
118
+ test.should fail
119
+ end
120
+ end
121
+
122
+ it "should correctly succeed with a path and a subdomain route which doesn't change the subdomain" do
123
+ @subdomains.each do |subdomain|
124
+ test = lambda { assert_generates_with_host("/home", @options, "#{subdomain}.example.com") }
125
+ test.should_not have_errors
126
+ test.should_not fail
127
+ end
128
+ end
129
+
130
+ it "should correctly fail with a path and a subdomain route which changes the subdomain" do
131
+ @subdomains.each do |subdomain|
132
+ test = lambda { assert_generates_with_host("/home", @options.merge(:subdomain => subdomain), "www.example.com") }
133
+ test.should_not have_errors
134
+ test.should fail
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ context "for model-based subdomain route" do
141
+ before(:each) do
142
+ map_subdomain :model => :city, :namespace => nil do |city|
143
+ city.resource :home
144
+ end
145
+ @options = { :controller => "homes", :action => "show", :subdomains => :city_id, :city_id => "canberra" }
146
+ end
147
+
148
+ context "recognition" do
149
+ it "should correctly succeed with a :host option and a subdomain route" do
150
+ test = lambda { assert_recognizes_with_host(@options, { :path => "/home", :host => "canberra.example.com" }) }
151
+ test.should_not have_errors
152
+ test.should_not fail
153
+ end
154
+
155
+ it "should correctly fail with a :host option and a subdomain route for the wrong subdomain" do
156
+ test = lambda { assert_recognizes_with_host(@options, { :path => "/home", :host => "boston.example.com" }) }
157
+ test.should_not have_errors
158
+ test.should fail
159
+ end
160
+
161
+ it "should correctly fail with a :host option and a subdomain route for no subdomain" do
162
+ test = lambda { assert_recognizes_with_host(@options, { :path => "/home", :host => "example.com" }) }
163
+ test.should have_errors
164
+ end
165
+ end
166
+
167
+ context "generation" do
168
+ it "should correctly succeed with :host and :path options and a subdomain route which changes the subdomain" do
169
+ test = lambda { assert_generates_with_host({ :path => "/home", :host => "canberra.example.com" }, @options, "boston.example.com") }
170
+ test.should_not have_errors
171
+ test.should_not fail
172
+ end
173
+
174
+ it "should correctly fail with :host and :path options and a subdomain route which doesn't change the subdomain" do
175
+ test = lambda { assert_generates_with_host({ :path => "/home", :host => "canberra.example.com" }, @options, "canberra.example.com") }
176
+ test.should_not have_errors
177
+ test.should fail
178
+ end
179
+
180
+ it "should correctly succeed with :host and :path options and a subdomain route which doesn't change the subdomain" do
181
+ test = lambda { assert_generates_with_host("/home", @options, "canberra.example.com") }
182
+ test.should_not have_errors
183
+ test.should_not fail
184
+ end
185
+
186
+ it "should correctly fail with :host and :path options and a subdomain route which changes the subdomain" do
187
+ test = lambda { assert_generates_with_host("/home", @options, "www.example.com") }
188
+ test.should_not have_errors
189
+ test.should fail
190
+ end
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,168 @@
1
+ require 'spec_helper'
2
+
3
+ describe "subdomain route mapping" do
4
+ it "should check the validity of each subdomain" do
5
+ SubdomainRoutes.should_receive(:valid_subdomain?).twice.and_return(true, true)
6
+ lambda { map_subdomain(:www, :www1) { } }.should_not raise_error
7
+ end
8
+
9
+ it "should check the validity of each subdomain and raise an error if any are invalid" do
10
+ SubdomainRoutes.should_receive(:valid_subdomain?).twice.and_return(true, false)
11
+ lambda { map_subdomain(:www, :www!) { } }.should raise_error(ArgumentError)
12
+ end
13
+
14
+ it "should check not the validity of a nil subdomain" do
15
+ SubdomainRoutes.should_not_receive(:valid_subdomain?)
16
+ map_subdomain(nil) { }
17
+ end
18
+
19
+ it "should accept a nil subdomain" do
20
+ map_subdomain(nil) { |map| map.options[:subdomains].should == [ "" ] }
21
+ end
22
+
23
+ it "should accept a blank subdomain" do
24
+ map_subdomain("") { |map| map.options[:subdomains].should == [ "" ] }
25
+ end
26
+
27
+ it "should accept a single specified subdomain" do
28
+ map_subdomain(:admin) { |admin| admin.options[:subdomains].should == [ "admin" ] }
29
+ end
30
+
31
+ it "should accept strings or symbols as subdomains" do
32
+ map_subdomain(:admin) { |admin| admin.options[:subdomains].should == [ "admin" ] }
33
+ map_subdomain("admin") { |admin| admin.options[:subdomains].should == [ "admin" ] }
34
+ end
35
+
36
+ it "should accept multiple subdomains" do
37
+ map_subdomain(:admin, :support) { |map| map.options[:subdomains].should == [ "admin", "support" ] }
38
+ end
39
+
40
+ it "should downcase the subdomains" do
41
+ map_subdomain(:Admin, "SUPPORT") { |map| map.options[:subdomains].should == [ "admin", "support" ] }
42
+ end
43
+
44
+ it "should raise ArgumentError if a nil :model option is specified as the subdomain" do
45
+ lambda { map_subdomain(:model => "") { } }.should raise_error(ArgumentError)
46
+ end
47
+
48
+ it "should raise ArgumentError if no subdomain is specified" do
49
+ lambda { map_subdomain }.should raise_error(ArgumentError)
50
+ end
51
+
52
+ it "should not include repeated subdomains in the options" do
53
+ map_subdomain(:admin, :support, :admin) { |map| map.options[:subdomains].should == [ "admin", "support" ] }
54
+ end
55
+
56
+ it "should be invoked by map.subdomains as well as map.subdomain" do
57
+ ActionController::Routing::Routes.draw do |map|
58
+ map.subdomains(:admin, :support) { |sub| sub.options[:subdomains].should == [ "admin", "support" ] }
59
+ end
60
+ end
61
+
62
+ [ [ :admin ], [ :support, :admin ] ].each do |subdomains|
63
+ context "mapping #{subdomains.size} subdomains" do
64
+ it "should set the first subdomain as a namespace" do
65
+ map_subdomain(*subdomains) { |map| map.options[:namespace].should == "#{subdomains.first}/" }
66
+ end
67
+
68
+ it "should prefix the first subdomain to named routes" do
69
+ map_subdomain(*subdomains) { |map| map.options[:name_prefix].should == "#{subdomains.first}_" }
70
+ end
71
+
72
+ it "should instead set a namespace to the name if specified" do
73
+ args = subdomains + [ :name => :something ]
74
+ map_subdomain(*args) { |map| map.options[:namespace].should == "something/" }
75
+ end
76
+
77
+ it "should instead prefix the name to named routes if specified" do
78
+ args = subdomains + [ :name => :something ]
79
+ map_subdomain(*args) { |map| map.options[:name_prefix].should == "something_" }
80
+ end
81
+
82
+ it "should not set a namespace if name is specified as nil" do
83
+ args = subdomains + [ :name => nil ]
84
+ map_subdomain(*args) { |map| map.options[:namespace].should be_nil }
85
+ end
86
+
87
+ it "should not set a named route prefix if name is specified as nil" do
88
+ args = subdomains + [ :name => nil ]
89
+ map_subdomain(*args) { |map| map.options[:name_prefix].should be_nil }
90
+ end
91
+ end
92
+ end
93
+
94
+ it "should strip bad characters from the namespace and name prefix" do
95
+ map_subdomain("just-do-it") { |map| map.options[:namespace].should == "just_do_it/" }
96
+ map_subdomain("just-do-it") { |map| map.options[:name_prefix].should == "just_do_it_" }
97
+ map_subdomain(nil, :name => "just-do-it") { |map| map.options[:namespace].should == "just_do_it/" }
98
+ map_subdomain(nil, :name => "just-do-it") { |map| map.options[:name_prefix].should == "just_do_it_" }
99
+ map_subdomain(nil, :name => "Just do it!") { |map| map.options[:namespace].should == "just_do_it/" }
100
+ map_subdomain(nil, :name => "Just do it!") { |map| map.options[:name_prefix].should == "just_do_it_" }
101
+ end
102
+
103
+ context "mapping the nil subdomain" do
104
+ it "should not set a namespace" do
105
+ [ nil, "" ].each do |none|
106
+ map_subdomain(none) { |map| map.options[:namespace].should be_nil }
107
+ end
108
+ end
109
+
110
+ it "should not set a named route prefix" do
111
+ [ nil, "" ].each do |none|
112
+ map_subdomain(none) { |map| map.options[:name_prefix].should be_nil }
113
+ end
114
+ end
115
+ end
116
+
117
+ context "mapping nil and other subdomains" do
118
+ it "should set the first non-nil subdomain as a namespace" do
119
+ [ nil, "" ].each do |none|
120
+ map_subdomain(none, :www) { |map| map.options[:namespace].should == "www/" }
121
+ end
122
+ end
123
+
124
+ it "should prefix the first non-nil subdomain to named routes" do
125
+ [ nil, "" ].each do |none|
126
+ map_subdomain(none, :www) { |map| map.options[:name_prefix].should == "www_" }
127
+ end
128
+ end
129
+ end
130
+
131
+ context "for a :model subdomain" do
132
+ it "should accept a :model option as the subdomain and turn it into a foreign key symbol" do
133
+ map_subdomain(:model => :city) { |city| city.options[:subdomains].should == :city_id }
134
+ end
135
+
136
+ it "should singularize a plural model name" do
137
+ map_subdomain(:model => :cities) { |city| city.options[:subdomains].should == :city_id }
138
+ end
139
+
140
+ it "should accept a string model name" do
141
+ map_subdomain(:model => "city") { |city| city.options[:subdomains].should == :city_id }
142
+ end
143
+
144
+ it "should set the model name as a namespace" do
145
+ map_subdomain(:model => :city) { |city| city.options[:namespace].should == "city/" }
146
+ end
147
+
148
+ it "should prefix the model name to named routes" do
149
+ map_subdomain(:model => :city) { |city| city.options[:name_prefix].should == "city_" }
150
+ end
151
+
152
+ it "should instead set a namespace to the name if specified" do
153
+ map_subdomain(:model => :city, :name => :something) { |map| map.options[:namespace].should == "something/" }
154
+ end
155
+
156
+ it "should instead prefix the name to named routes if specified" do
157
+ map_subdomain(:model => :city, :name => :something) { |map| map.options[:name_prefix].should == "something_" }
158
+ end
159
+
160
+ it "should not set a namespace if name is specified as nil" do
161
+ map_subdomain(:model => :city, :name => nil) { |map| map.options[:namespace].should be_nil }
162
+ end
163
+
164
+ it "should not set a named route prefix if name is specified as nil" do
165
+ map_subdomain(:model => :city, :name => nil) { |map| map.options[:name_prefix].should be_nil }
166
+ end
167
+ end
168
+ end
data/spec/routes_spec.rb CHANGED
@@ -1,156 +1,5 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe "subdomain routes" do
4
- it "should check the validity of each subdomain" do
5
- SubdomainRoutes.should_receive(:valid_subdomain?).twice.and_return(true, true)
6
- lambda { map_subdomain(:www, :www1) { } }.should_not raise_error
7
- end
8
-
9
- it "should check the validity of each subdomain and raise an error if any are invalid" do
10
- SubdomainRoutes.should_receive(:valid_subdomain?).twice.and_return(true, false)
11
- lambda { map_subdomain(:www, :www!) { } }.should raise_error(ArgumentError)
12
- end
13
-
14
- it "should check not the validity of a nil subdomain" do
15
- SubdomainRoutes.should_not_receive(:valid_subdomain?)
16
- map_subdomain(nil) { }
17
- end
18
-
19
- it "should accept a nil subdomain" do
20
- map_subdomain(nil) { |map| map.options[:subdomains].should == [ "" ] }
21
- end
22
-
23
- it "should accept a blank subdomain" do
24
- map_subdomain("") { |map| map.options[:subdomains].should == [ "" ] }
25
- end
26
-
27
- it "should accept a single specified subdomain" do
28
- map_subdomain(:admin) { |admin| admin.options[:subdomains].should == [ "admin" ] }
29
- end
30
-
31
- it "should accept strings or symbols as subdomains" do
32
- map_subdomain(:admin) { |admin| admin.options[:subdomains].should == [ "admin" ] }
33
- map_subdomain("admin") { |admin| admin.options[:subdomains].should == [ "admin" ] }
34
- end
35
-
36
- it "should accept multiple subdomains" do
37
- map_subdomain(:admin, :support) { |map| map.options[:subdomains].should == [ "admin", "support" ] }
38
- end
39
-
40
- it "should downcase the subdomains" do
41
- map_subdomain(:Admin, "SUPPORT") { |map| map.options[:subdomains].should == [ "admin", "support" ] }
42
- end
43
-
44
- it "should raise ArgumentError if a nil :model option is specified as the subdomain" do
45
- lambda { map_subdomain(:model => "") { } }.should raise_error(ArgumentError)
46
- end
47
-
48
- it "should raise ArgumentError if no subdomain is specified" do
49
- lambda { map_subdomain }.should raise_error(ArgumentError)
50
- end
51
-
52
- it "should not include repeated subdomains in the options" do
53
- map_subdomain(:admin, :support, :admin) { |map| map.options[:subdomains].should == [ "admin", "support" ] }
54
- end
55
-
56
- it "should be invoked by map.subdomains as well as map.subdomain" do
57
- ActionController::Routing::Routes.draw do |map|
58
- map.subdomains(:admin, :support) { |sub| sub.options[:subdomains].should == [ "admin", "support" ] }
59
- end
60
- end
61
-
62
- [ [ :admin ], [ :support, :admin ] ].each do |subdomains|
63
- context "mapping #{subdomains.size} subdomains" do
64
- it "should set the first subdomain as a namespace" do
65
- map_subdomain(*subdomains) { |map| map.options[:namespace].should == "#{subdomains.first}/" }
66
- end
67
-
68
- it "should prefix the first subdomain to named routes" do
69
- map_subdomain(*subdomains) { |map| map.options[:name_prefix].should == "#{subdomains.first}_" }
70
- end
71
-
72
- it "should instead set a namespace to the name if specified" do
73
- args = subdomains + [ :name => :something ]
74
- map_subdomain(*args) { |map| map.options[:namespace].should == "something/" }
75
- end
76
-
77
- it "should instead prefix the name to named routes if specified" do
78
- args = subdomains + [ :name => :something ]
79
- map_subdomain(*args) { |map| map.options[:name_prefix].should == "something_" }
80
- end
81
-
82
- it "should not set a namespace if name is specified as nil" do
83
- args = subdomains + [ :name => nil ]
84
- map_subdomain(*args) { |map| map.options[:namespace].should be_nil }
85
- end
86
-
87
- it "should not set a named route prefix if name is specified as nil" do
88
- args = subdomains + [ :name => nil ]
89
- map_subdomain(*args) { |map| map.options[:name_prefix].should be_nil }
90
- end
91
- end
92
- end
93
-
94
- it "should strip bad characters from the namespace and name prefix" do
95
- map_subdomain("just-do-it") { |map| map.options[:namespace].should == "just_do_it/" }
96
- map_subdomain("just-do-it") { |map| map.options[:name_prefix].should == "just_do_it_" }
97
- map_subdomain(nil, :name => "just-do-it") { |map| map.options[:namespace].should == "just_do_it/" }
98
- map_subdomain(nil, :name => "just-do-it") { |map| map.options[:name_prefix].should == "just_do_it_" }
99
- map_subdomain(nil, :name => "Just do it!") { |map| map.options[:namespace].should == "just_do_it/" }
100
- map_subdomain(nil, :name => "Just do it!") { |map| map.options[:name_prefix].should == "just_do_it_" }
101
- end
102
-
103
- context "mapping the nil subdomain" do
104
- it "should not set a namespace" do
105
- [ nil, "" ].each do |none|
106
- map_subdomain(none) { |map| map.options[:namespace].should be_nil }
107
- end
108
- end
109
-
110
- it "should not set a named route prefix" do
111
- [ nil, "" ].each do |none|
112
- map_subdomain(none) { |map| map.options[:name_prefix].should be_nil }
113
- end
114
- end
115
- end
116
-
117
- context "mapping nil and other subdomains" do
118
- it "should set the first non-nil subdomain as a namespace" do
119
- [ nil, "" ].each do |none|
120
- map_subdomain(none, :www) { |map| map.options[:namespace].should == "www/" }
121
- end
122
- end
123
-
124
- it "should prefix the first non-nil subdomain to named routes" do
125
- [ nil, "" ].each do |none|
126
- map_subdomain(none, :www) { |map| map.options[:name_prefix].should == "www_" }
127
- end
128
- end
129
- end
130
-
131
- context "for a :model subdomain" do
132
- it "should accept a :model option as the subdomain and turn it into a foreign key symbol" do
133
- map_subdomain(:model => :city) { |city| city.options[:subdomains].should == :city_id }
134
- end
135
-
136
- it "should singularize a plural model name" do
137
- map_subdomain(:model => :cities) { |city| city.options[:subdomains].should == :city_id }
138
- end
139
-
140
- it "should accept a string model name" do
141
- map_subdomain(:model => "city") { |city| city.options[:subdomains].should == :city_id }
142
- end
143
-
144
- it "should set the model name as a namespace" do
145
- map_subdomain(:model => :city) { |city| city.options[:namespace].should == "city/" }
146
- end
147
-
148
- it "should prefix the model name to named routes" do
149
- map_subdomain(:model => :city) { |city| city.options[:name_prefix].should == "city_" }
150
- end
151
- end
152
- end
153
-
154
3
  describe "ActionController::Routing::Routes" do
155
4
  before(:each) do
156
5
  map_subdomain(:www, nil) { |www| www.root :controller => "homes", :action => "show" }
data/spec/spec_helper.rb CHANGED
@@ -9,18 +9,21 @@ require 'action_mailer' # only required for testing optional features, not requi
9
9
 
10
10
  require 'subdomain_routes'
11
11
 
12
+ require 'action_controller/test_process'
13
+ require 'action_view/test_case'
14
+ require 'test_unit_matcher'
15
+
16
+ ActiveSupport::OptionMerger.send(:define_method, :options) { @options }
17
+
18
+
12
19
  Spec::Runner.configure do |config|
13
20
  config.before(:each) do
14
21
  ActionController::Routing::Routes.clear!
15
22
  SubdomainRoutes::Config.stub!(:domain_length).and_return(2)
16
23
  end
24
+ config.include(SubdomainRoutes::TestUnitMatcher)
17
25
  end
18
26
 
19
- require 'action_controller/test_process'
20
- require 'action_view/test_case'
21
-
22
- ActiveSupport::OptionMerger.send(:define_method, :options) { @options }
23
-
24
27
  def map_subdomain(*subdomains, &block)
25
28
  ActionController::Routing::Routes.draw do |map|
26
29
  map.subdomain(*subdomains, &block)
@@ -32,28 +35,24 @@ def recognize_path(request)
32
35
  end
33
36
 
34
37
  def in_controller_with_host(host, &block)
35
- variables = instance_variables.inject([]) do |array, name|
36
- array << [ name, instance_variable_get(name) ]
37
- end
38
+ spec = self
38
39
  Class.new(ActionView::TestCase::TestController) do
39
40
  include Spec::Matchers
40
41
  end.new.instance_eval do
41
42
  request.host = host
42
- variables.each { |name, value| instance_variable_set(name, value) }
43
+ copy_instance_variables_from(spec)
43
44
  instance_eval(&block)
44
45
  end
45
46
  end
46
47
 
47
48
  def in_object_with_host(host, &block)
48
- variables = instance_variables.inject([]) do |array, name|
49
- array << [ name, instance_variable_get(name) ]
50
- end
49
+ spec = self
51
50
  Class.new do
52
51
  include Spec::Matchers
53
52
  include ActionController::UrlWriter
54
53
  end.new.instance_eval do
55
54
  self.class.default_url_options = { :host => host }
56
- variables.each { |name, value| instance_variable_set(name, value) }
55
+ copy_instance_variables_from(spec)
57
56
  instance_eval(&block)
58
57
  end
59
58
  end
@@ -0,0 +1,46 @@
1
+ module SubdomainRoutes
2
+ module TestUnitMatcher
3
+ class Base
4
+ class TestCase < ActionController::TestCase
5
+ end
6
+
7
+ def initialize(count_method, messages, spec)
8
+ @count_method, @messages, @spec = count_method, messages, spec
9
+ end
10
+
11
+ def run_test(target)
12
+ result = Test::Unit::TestResult.new
13
+ spec = @spec
14
+ TestCase.send :define_method, :test do
15
+ copy_instance_variables_from(spec)
16
+ instance_eval(&target)
17
+ end
18
+ TestCase.new(:test).run(result) {}
19
+ result
20
+ end
21
+
22
+ def matches?(target)
23
+ @result = run_test(target)
24
+ @result_count = @result.send(@count_method)
25
+ @result_count > 0
26
+ end
27
+
28
+ def failure_message
29
+ "Expected #{@count_method.to_s.humanize.downcase} to be more than zero, got zero.\n"
30
+ end
31
+
32
+ def negative_failure_message
33
+ "Expected #{@count_method.to_s.humanize.downcase} to be zero, got #{@result_count}:\n" +
34
+ @result.instance_variable_get(@messages).map(&:inspect).join("\n")
35
+ end
36
+ end
37
+
38
+ def have_errors
39
+ Base.new(:error_count, :@errors, self)
40
+ end
41
+
42
+ def fail
43
+ Base.new(:failure_count, :@failures, self)
44
+ end
45
+ end
46
+ end
@@ -212,7 +212,7 @@ describe "URL writing" do
212
212
  it "should raise a routing error if no subdomain object is supplied to the named route" do
213
213
  with_host "www.example.com" do
214
214
  [ lambda { city_events_url }, lambda { city_event_url("id") } ].each do |lamb|
215
- lamb.should raise_error(ActionController::RoutingError) { |e| e.message.should include ":city_id" }
215
+ lamb.should raise_error(ActionController::RoutingError) { |e| e.message.should include(":city_id") }
216
216
  end
217
217
  end
218
218
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mholling-subdomain_routes
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Hollingworth
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-07-28 00:00:00 -07:00
12
+ date: 2009-08-25 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -37,6 +37,7 @@ files:
37
37
  - Rakefile
38
38
  - VERSION.yml
39
39
  - lib/subdomain_routes.rb
40
+ - lib/subdomain_routes/assertions.rb
40
41
  - lib/subdomain_routes/config.rb
41
42
  - lib/subdomain_routes/mapper.rb
42
43
  - lib/subdomain_routes/request.rb
@@ -46,11 +47,14 @@ files:
46
47
  - lib/subdomain_routes/url_writer.rb
47
48
  - lib/subdomain_routes/validations.rb
48
49
  - rails/init.rb
50
+ - spec/assertions_spec.rb
49
51
  - spec/extraction_spec.rb
52
+ - spec/mapping_spec.rb
50
53
  - spec/recognition_spec.rb
51
54
  - spec/resources_spec.rb
52
55
  - spec/routes_spec.rb
53
56
  - spec/spec_helper.rb
57
+ - spec/test_unit_matcher.rb
54
58
  - spec/url_writing_spec.rb
55
59
  - spec/validations_spec.rb
56
60
  has_rdoc: false
@@ -81,10 +85,13 @@ signing_key:
81
85
  specification_version: 2
82
86
  summary: A Rails library for incorporating subdomains into route generation and recognition.
83
87
  test_files:
88
+ - spec/assertions_spec.rb
84
89
  - spec/extraction_spec.rb
90
+ - spec/mapping_spec.rb
85
91
  - spec/recognition_spec.rb
86
92
  - spec/resources_spec.rb
87
93
  - spec/routes_spec.rb
88
94
  - spec/spec_helper.rb
95
+ - spec/test_unit_matcher.rb
89
96
  - spec/url_writing_spec.rb
90
97
  - spec/validations_spec.rb