mholling-subdomain_routes 0.1.0 → 0.2.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 +65 -100
- data/VERSION.yml +1 -1
- data/lib/subdomain_routes/config.rb +0 -1
- data/lib/subdomain_routes/mapper.rb +10 -8
- data/lib/subdomain_routes/request.rb +0 -1
- data/lib/subdomain_routes/routes.rb +24 -24
- data/lib/subdomain_routes/split_host.rb +0 -3
- data/lib/subdomain_routes/url_writer.rb +5 -6
- data/lib/subdomain_routes/validations.rb +17 -3
- data/lib/subdomain_routes.rb +0 -2
- data/spec/recognition_spec.rb +12 -21
- data/spec/resources_spec.rb +1 -1
- data/spec/routes_spec.rb +23 -100
- data/spec/url_writing_spec.rb +31 -41
- data/spec/validations_spec.rb +21 -4
- metadata +2 -8
- data/lib/subdomain_routes/mailer.rb +0 -17
- data/lib/subdomain_routes/proc_set.rb +0 -33
- data/spec/mailer_spec.rb +0 -15
- data/spec/proc_set_spec.rb +0 -61
data/README.textile
CHANGED
@@ -1,12 +1,10 @@
|
|
1
|
-
h1. SubdomainRoutes - Adding Subdomains to Rails Routing
|
2
|
-
|
3
1
|
The Rails "routing system":http://api.rubyonrails.org/classes/ActionController/Routing.html is a pretty impressive piece of code. There's a fair bit of magic going on to make your routes so easy to define and use in the rest of your Rails application. One area in which the routing system is limited however is its use of subdomains: it's pretty much assumed that your site will be using a single, fixed domain.
|
4
2
|
|
5
3
|
There are times where it is preferable to spread a website over multiple subdomains. One common idiom in URL schemes is to separate aspects of the site under different subdomains, representative of those aspect. It many cases a simple, fixed subdomain scheme is desirable: _support.whatever.com_, _forums.whatever.com_, _gallery.whatever.com_ and so on. On some international sites, the subdomain is used to select the language and localization: _en.wikipedia.org_, _fr.wikipedia.org_, _ja.wikipedia.org_. Other schemes allocate each user of the site their own subdomain, so as to personalise the experience (_blogspot.com_ is a good example of this).
|
6
4
|
|
7
5
|
A couple of plugins currently exists for Rails developers wishing to incorporate subdomains into their routes. The de facto standard is "SubdomainFu":http://www.intridea.com/2008/6/23/subdomainfu-a-new-way-to-tame-the-subdomain. (I'll admit - I haven't actually used this plugin myself.) There's also "SubdomainAccount":http://github.com/shuber/subdomain_account/tree/master.
|
8
6
|
|
9
|
-
I've recently completed work on a subdomain library which fully incorporates subdomains into the rails routing environment - in URL generation, route recognition *and* in route definition, something I don't believe is currently available. As an added bonus, if offers the ability to define routes
|
7
|
+
I've recently completed work on a subdomain library which fully incorporates subdomains into the rails routing environment - in URL generation, route recognition *and* in route definition, something I don't believe is currently available. As an added bonus, if offers the ability to define subdomain routes which are keyed to a model (user, category, etc.) stored in your database.
|
10
8
|
|
11
9
|
h2. Installation
|
12
10
|
|
@@ -76,7 +74,7 @@ support_tickets_path(:subdomain => :www)
|
|
76
74
|
|
77
75
|
Notice that, by necessity, requesting a path still results in an URL if the subdomain of the route is different. If you try and override the subdomain manually, you'll get an error, because the resulting URL would be invalid and would not be recognized. This is a good thing - you don't want to be linking to invalid URLs by mistake!
|
78
76
|
|
79
|
-
In other words, <code>url_for</code> and your named routes will *never* generate an invalid URL. This is one major benefit of the SubdomainRoutes gem: it offers a smart way of switching subdomains, requiring them to be specified manually only when absolutely necessary.
|
77
|
+
In other words, <code>url_for</code> and your named routes will *never* generate an invalid URL. This is one major benefit of the SubdomainRoutes gem: it offers a smart way of switching subdomains, requiring them to be specified manually only when absolutely necessary.
|
80
78
|
|
81
79
|
h2. Mapping Multiple Subdomains
|
82
80
|
|
@@ -163,125 +161,74 @@ end
|
|
163
161
|
|
164
162
|
Your <code>admin_role_users_path(@role)</code> will automatically generate with the correct _admin_ subdomain if required, and paths such as _/roles/1/users_ will only be recognised when under the _admin_ subdomain. Note that both the block form and the <code>:has_many</code> form of nested resources will work in this manner. (In fact, under the hood, the latter just falls through to the former.) Any other (non-resource) routes you nest under a resource will also inherit the subdomain conditions.
|
165
163
|
|
166
|
-
h2.
|
167
|
-
|
168
|
-
To develop your app using SudomainRoutes, you'll need to set up your machine to point some test domains to the server on your machine (i.e. to the local loopback address, 127.0.0.1). On a Mac, you can do this by editing <code>/etc/hosts</code>. Let's say you want to use the subdomains _www_, _dvd_, _game_, _book_ and _cd_, with a domain of _reviews.local_. Adding these lines to <code>/etc/hosts</code> will do the trick:
|
169
|
-
|
170
|
-
<pre>
|
171
|
-
127.0.0.1 reviews.local
|
172
|
-
127.0.0.1 www.reviews.local
|
173
|
-
127.0.0.1 dvd.reviews.local
|
174
|
-
127.0.0.1 game.reviews.local
|
175
|
-
127.0.0.1 book.reviews.local
|
176
|
-
127.0.0.1 cd.reviews.local
|
177
|
-
</pre>
|
178
|
-
|
179
|
-
You'll need to flush your DNS cache for these changes to take effect:
|
180
|
-
|
181
|
-
<pre>
|
182
|
-
dscacheutil -flushcache
|
183
|
-
</pre>
|
184
|
-
|
185
|
-
Then fire up your <code>script/server</code>, point your browser to _www.reviews.local:3000_ and your app should be up and running. If you're using "Passenger":http://www.modrails.com to "serve your apps in development":http://www.google.com/search?q=rails+passenger+development (and I highly recommend that you do), you'll need to add a Virtual Host to your Apache .conf file. (Don't forget to alias all the subdomains and restart the server.)
|
186
|
-
|
187
|
-
If you're using dynamic subdomain routes (covered in my next article), you may want to use a catch-all (wildcard) subdomain. Setting this up is not so easy, since wildcards (like _*.reviews.local_) won't work in your <code>/etc/hosts</code> file. There are a couple of work-around for this:
|
188
|
-
|
189
|
-
# 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.
|
190
|
-
# Set up a local DNS server with an A record for the domain. This may be a bit involved.
|
191
|
-
|
192
|
-
h2. Dynamic Subdomain Recognition
|
193
|
-
|
194
|
-
SubdomainRoutes also offers a full complement of features for recognizing subdomain URLS which are not known in advance. (I'll refer to these cases as _dynamic subdomains_.) For example, if you are keying each user of your website to their own subdomain, your routes will need to recognise new subdomains as new users sign up. Another example is if you want to key your URL subdomains to a set of categories (language, city, product type, etc.) which may expand as your site grows. In each case, your subdomains will depend on records in the database.
|
164
|
+
h2. Defining Model-Based Subdomain Routes
|
195
165
|
|
196
|
-
Let's take a hypothetical example of a
|
166
|
+
The idea here is to have the subdomain of the URL keyed to an ActiveRecord model. Let's take a hypothetical example of a site which lists items under different categories, each category being represented under its own subdomain. Assume our <code>Category</code> model has a <code>subdomain</code> attribute which contains the category's custom subdomain. In our routes we'll still use the <code>subdomain</code> mapper, but instead of specifying one or more subdomains, we just specify a <code>:model</code> option:
|
197
167
|
|
198
168
|
<pre>
|
199
169
|
ActionController::Routing::Routes.draw do |map|
|
200
|
-
map.subdomain :
|
201
|
-
|
170
|
+
map.subdomain :model => :category do |category|
|
171
|
+
category.resources :items
|
202
172
|
...
|
203
173
|
end
|
204
174
|
end
|
205
|
-
|
206
|
-
ActionController::Routing::Routes.recognize_subdomain :blog do |subdomain|
|
207
|
-
User.exists?(:username => subdomain)
|
208
|
-
end
|
209
175
|
</pre>
|
210
176
|
|
211
|
-
|
212
|
-
|
213
|
-
As arguments, the <code>recognize_subdomain</code> method takes a subdomain label, as used in the route (in this case, <code>:blog</code>), and a block. The block should take a single argument (the subdomain to be recognised), and should return true or false depending on whether the subdomain should be recognised for that route. In this case, we simply look up the subdomain in the <code>User</code> model to see if it's in use.
|
177
|
+
As before, the namespace and name prefix for all the nested routes will default to the name of the model (you can override these in the options). The routes will match under any subdomain, and that subdomain will be passed to the controller in the <code>params</code> hash as <code>params[:category_id]</code>. For example, a GET request to _dvds.example.com/items_ will go to the <code>Category::ItemsController#index</code> action with <code>params[:category_id]</code> set to <code>"dvds"</code>.
|
214
178
|
|
215
|
-
|
179
|
+
h2. Generating Model-Based Subdomain URLs
|
216
180
|
|
217
|
-
|
181
|
+
So how does URL _generation_ work with these routes? That's the best bit: just the same way as you're used to! The routes are fully integrated with your named routes, as well as the <code>form_for</code>, <code>redirect_to</code> and <code>polymorphic_path</code> helpers. The only thing you have to do is make sure your model's <code>to_param</code> returns the subdomain field for the user:
|
218
182
|
|
219
183
|
<pre>
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
=> "http://rails.awesomblogs.com/articles"
|
226
|
-
|
227
|
-
blog_articles_path(:subdomain => "www")
|
228
|
-
# ActionController::RoutingError: Route failed to generate
|
229
|
-
# (subdomain "www" is invalid)
|
230
|
-
|
231
|
-
# when the host is www.awesomeblogs.com:
|
232
|
-
blog_articles_path(:subdomain => "rails")
|
233
|
-
=> "http://rails.awesomblogs.com/articles"
|
234
|
-
|
235
|
-
blog_articles_path
|
236
|
-
# ActionController::RoutingError: Route failed to generate
|
237
|
-
# (subdomain "www" is invalid)
|
184
|
+
class Category < ActiveRecord::Base
|
185
|
+
...
|
186
|
+
alias_method :to_param, :subdomain
|
187
|
+
...
|
188
|
+
end
|
238
189
|
</pre>
|
239
190
|
|
240
|
-
|
191
|
+
Now, in the above example, let's say our site has _dvd_ and _cd_ su categories, with subdomains _dvds_ and _cds_. In our controller:
|
241
192
|
|
242
|
-
|
193
|
+
<pre>
|
194
|
+
@dvd
|
195
|
+
=> #<Category id: 1, subdomain: "dvds", ... >
|
243
196
|
|
244
|
-
|
197
|
+
@cd
|
198
|
+
=> #<Category id: 2, subdomain: "cds", ... >
|
245
199
|
|
246
|
-
|
200
|
+
# when the current host is dvds.example.com:
|
201
|
+
category_items_path(@dvd)
|
202
|
+
=> "/items"
|
247
203
|
|
248
|
-
|
204
|
+
polymorphic_path [ @dvd, @dvd.items.first ]
|
205
|
+
=> "/items/2"
|
249
206
|
|
250
|
-
|
251
|
-
|
252
|
-
</pre>
|
253
|
-
|
254
|
-
In the above example we might want to choose this route, so that we can flush the cache only when a user is created or destroyed (or changes the subdomain). We could do this easily using an observer:
|
207
|
+
category_items_path(@cd)
|
208
|
+
=> "http://cds.example.com/items"
|
255
209
|
|
256
|
-
|
257
|
-
|
258
|
-
|
210
|
+
polymorphic_path [ @cd, @cd.items.first ]
|
211
|
+
=> "http://cds.example.com/items/10"
|
212
|
+
</pre>
|
259
213
|
|
260
|
-
|
261
|
-
if user.subdomain_changed?
|
262
|
-
ActionController::Routing::Routes.subdomain_procs.flush!
|
263
|
-
end
|
264
|
-
end
|
214
|
+
As you can see, the first argument for the named routes (and polymorphic paths) feeds directly into the subdomain for the URL. No more passing <code>:subdomain</code> options. Nice!
|
265
215
|
|
266
|
-
|
267
|
-
ActionController::Routing::Routes.subdomain_procs.flush!
|
268
|
-
end
|
269
|
-
end
|
216
|
+
h2. ActiveRecord Validations
|
270
217
|
|
271
|
-
|
272
|
-
config.active_record.observers = [ :user_observer ]
|
273
|
-
</pre>
|
218
|
+
SubdomainRoutes also gives you a couple of utility validations for your ActiveRecord models:
|
274
219
|
|
275
|
-
|
220
|
+
* <code>validates_subdomain_format_of</code> ensures a subdomain field uses only legal characters in an allowed format; and
|
221
|
+
* <code>validates_subdomain_not_reserved</code> ensures the field does not take a value already in use by your fixed-subdomain routes.
|
276
222
|
|
277
|
-
|
223
|
+
(Undoubtedly, you'll want to throw in a <code>validates_uniqueness_of</code> as well.)
|
278
224
|
|
279
|
-
|
225
|
+
Let's take an example of a site where each user gets a dedicated subdomain. Validations for the <code>subdomain</code> attribute of the <code>User</code> model would be:
|
280
226
|
|
281
227
|
<pre>
|
282
228
|
class User < ActiveRecord::Base
|
283
229
|
...
|
284
230
|
validates_subdomain_format_of :subdomain
|
231
|
+
validates_subdomain_not_reserved :subdomain
|
285
232
|
validates_uniqueness_of :subdomain
|
286
233
|
...
|
287
234
|
end
|
@@ -289,35 +236,53 @@ end
|
|
289
236
|
|
290
237
|
The library currently uses a simple regexp to limit subdomains to lowercase alphanumeric characters and dashes (except on either end). If you want to conform more precisely to the URI specs, you can override the <code>SubdomainRoutes.valid_subdomain?</code> method and implement your own.
|
291
238
|
|
292
|
-
h2. Using
|
239
|
+
h2. Using Fixed and Model-Based Subdomain Routes Together
|
293
240
|
|
294
|
-
Let's try using
|
241
|
+
Let's try using fixed and model-based subdomain routes together. Say we want to reserve some subdomains (say _support_ and _admin_) for administrative functions, with the remainder keyed to user accounts. Our routes:
|
295
242
|
|
296
243
|
<pre>
|
297
244
|
ActionController::Routing::Routes.draw do |map|
|
298
245
|
map.subdomain :support do |support|
|
299
246
|
...
|
300
247
|
end
|
248
|
+
|
301
249
|
map.subdomain :admin do |admin|
|
302
250
|
...
|
303
251
|
end
|
304
|
-
|
252
|
+
|
253
|
+
map.subdomain :model => :user do |user|
|
305
254
|
...
|
306
255
|
end
|
307
256
|
end
|
257
|
+
</pre>
|
308
258
|
|
309
|
-
|
310
|
-
|
311
|
-
|
259
|
+
These routes will co-exist quite happily together. We've made sure our static subdomain routes are listed first though, so that they get matched first. In the <code>User</code> model we'd add the validations above, which in this case would prevent users from taking _www_ or _support_ as a subdomain. (We could also validate for a minimum and maximum length using <code>validates_length_of</code>.)
|
260
|
+
|
261
|
+
h2. Setting Up Your Development Environment
|
262
|
+
|
263
|
+
To develop your app using SudomainRoutes, you'll need to set up your machine to point some test domains to the server on your machine (i.e. to the local loopback address, 127.0.0.1). On a Mac, you can do this by editing <code>/etc/hosts</code>. Let's say you want to use the subdomains _www_, _dvd_, _game_, _book_ and _cd_, with a domain of _reviews.local_. Adding these lines to <code>/etc/hosts</code> will do the trick:
|
264
|
+
|
265
|
+
<pre>
|
266
|
+
127.0.0.1 reviews.local
|
267
|
+
127.0.0.1 www.reviews.local
|
268
|
+
127.0.0.1 dvd.reviews.local
|
269
|
+
127.0.0.1 game.reviews.local
|
270
|
+
127.0.0.1 book.reviews.local
|
271
|
+
127.0.0.1 cd.reviews.local
|
312
272
|
</pre>
|
313
273
|
|
314
|
-
|
274
|
+
You'll need to flush your DNS cache for these changes to take effect:
|
315
275
|
|
316
276
|
<pre>
|
317
|
-
|
277
|
+
dscacheutil -flushcache
|
318
278
|
</pre>
|
319
279
|
|
320
|
-
(
|
280
|
+
Then fire up your <code>script/server</code>, point your browser to _www.reviews.local:3000_ and your app should be up and running. If you're using "Passenger":http://www.modrails.com to "serve your apps in development":http://www.google.com/search?q=rails+passenger+development (and I highly recommend that you do), you'll need to add a Virtual Host to your Apache .conf file. (Don't forget to alias all the subdomains and restart the server.)
|
281
|
+
|
282
|
+
If you're using model-based subdomain routes (covered next), you may want to use a catch-all (wildcard) subdomain. Setting this up is not so easy, since wildcards (like _*.reviews.local_) won't work in your <code>/etc/hosts</code> file. There are a couple of work-around for this:
|
283
|
+
|
284
|
+
# 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
|
+
# Set up a local DNS server with an A record for the domain. This may be a bit involved.
|
321
286
|
|
322
287
|
|
323
288
|
Copyright (c) 2009 Matthew Hollingworth. See LICENSE for details.
|
data/VERSION.yml
CHANGED
@@ -4,11 +4,13 @@ module SubdomainRoutes
|
|
4
4
|
module Mapper
|
5
5
|
def subdomain(*subdomains, &block)
|
6
6
|
options = subdomains.extract_options!
|
7
|
-
name = nil
|
8
7
|
if subdomains.empty?
|
9
|
-
if
|
10
|
-
|
11
|
-
|
8
|
+
if model = options.delete(:model)
|
9
|
+
raise ArgumentError, "Invalid model name" if model.blank?
|
10
|
+
models = model.to_s.downcase.pluralize
|
11
|
+
model = models.singularize
|
12
|
+
model_id = model.foreign_key.to_sym
|
13
|
+
subdomain_options = { :subdomains => model_id, :name_prefix => "#{model}_", :namespace => "#{model}/" }
|
12
14
|
else
|
13
15
|
raise ArgumentError, "Please specify at least one subdomain!"
|
14
16
|
end
|
@@ -22,12 +24,12 @@ module SubdomainRoutes
|
|
22
24
|
if subdomains.include? ""
|
23
25
|
raise ArgumentError, "Can't specify a nil subdomain unless you set Config.domain_length!" unless Config.domain_length
|
24
26
|
end
|
25
|
-
name = subdomains.reject(&:blank?).first
|
26
27
|
subdomain_options = { :subdomains => subdomains }
|
28
|
+
name = subdomains.reject(&:blank?).first
|
29
|
+
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?
|
27
32
|
end
|
28
|
-
name = options.delete(:name) if options.has_key?(:name)
|
29
|
-
name = name.to_s.downcase.gsub(/[^(a-z0-9)]/, ' ').squeeze(' ').strip.gsub(' ', '_') unless name.blank?
|
30
|
-
subdomain_options.merge! :name_prefix => "#{name}_", :namespace => "#{name}/" unless name.blank?
|
31
33
|
with_options(subdomain_options.merge(options), &block)
|
32
34
|
end
|
33
35
|
alias_method :subdomains, :subdomain
|
@@ -4,46 +4,32 @@ module SubdomainRoutes
|
|
4
4
|
include SplitHost
|
5
5
|
|
6
6
|
def self.included(base)
|
7
|
-
[ :extract_request_environment, :add_route
|
7
|
+
[ :extract_request_environment, :add_route ].each { |method| base.alias_method_chain method, :subdomains }
|
8
8
|
end
|
9
9
|
|
10
10
|
def extract_request_environment_with_subdomains(request)
|
11
11
|
extract_request_environment_without_subdomains(request).merge(:subdomain => subdomain_for_host(request.host))
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def add_route_with_subdomains(*args)
|
15
15
|
options = args.extract_options!
|
16
16
|
if subdomains = options.delete(:subdomains)
|
17
17
|
options[:conditions] ||= {}
|
18
|
-
options[:conditions][:subdomains] = subdomains
|
19
18
|
options[:requirements] ||= {}
|
19
|
+
options[:conditions][:subdomains] = subdomains
|
20
20
|
options[:requirements][:subdomains] = subdomains
|
21
21
|
end
|
22
22
|
with_options(options) { |routes| routes.add_route_without_subdomains(*args) }
|
23
23
|
end
|
24
24
|
|
25
|
-
def
|
26
|
-
|
27
|
-
end
|
28
|
-
|
29
|
-
def recognize_subdomain(name, &block)
|
30
|
-
subdomain_procs.add_recognizer(name, &block)
|
31
|
-
end
|
32
|
-
|
33
|
-
def clear_with_subdomains!
|
34
|
-
subdomain_procs.clear!
|
35
|
-
clear_without_subdomains!
|
36
|
-
end
|
37
|
-
|
38
|
-
def call_with_subdomains(*args)
|
39
|
-
subdomain_procs.flush! unless SubdomainRoutes::Config.manual_flush
|
40
|
-
call_without_subdomains(*args)
|
25
|
+
def reserved_subdomains
|
26
|
+
routes.map(&:reserved_subdomains).flatten.uniq
|
41
27
|
end
|
42
28
|
end
|
43
29
|
|
44
30
|
module Route
|
45
31
|
def self.included(base)
|
46
|
-
base.alias_method_chain
|
32
|
+
[ :recognition_conditions, :segment_keys, :recognition_extraction ].each { |method| base.alias_method_chain method, :subdomains }
|
47
33
|
end
|
48
34
|
|
49
35
|
def recognition_conditions_with_subdomains
|
@@ -51,13 +37,27 @@ module SubdomainRoutes
|
|
51
37
|
case conditions[:subdomains]
|
52
38
|
when Array
|
53
39
|
result << "conditions[:subdomains].include?(env[:subdomain])"
|
54
|
-
when
|
55
|
-
|
56
|
-
result << %Q{ActionController::Routing::Routes.subdomain_procs.recognize(#{subdomain.inspect}, env[:subdomain])}
|
57
|
-
end
|
40
|
+
when Symbol
|
41
|
+
result << "(subdomain = env[:subdomain] unless env[:subdomain].blank?)"
|
58
42
|
end
|
59
43
|
result
|
60
44
|
end
|
45
|
+
|
46
|
+
def segment_keys_with_subdomains
|
47
|
+
result = segment_keys_without_subdomains
|
48
|
+
result.unshift(:subdomain) if conditions[:subdomains].is_a? Symbol
|
49
|
+
result
|
50
|
+
end
|
51
|
+
|
52
|
+
def recognition_extraction_with_subdomains
|
53
|
+
result = recognition_extraction_without_subdomains
|
54
|
+
result.unshift "\nparams[#{conditions[:subdomains].inspect}] = subdomain\n" if conditions[:subdomains].is_a? Symbol
|
55
|
+
result
|
56
|
+
end
|
57
|
+
|
58
|
+
def reserved_subdomains
|
59
|
+
conditions[:subdomains].is_a?(Array) ? conditions[:subdomains] : []
|
60
|
+
end
|
61
61
|
end
|
62
62
|
end
|
63
63
|
end
|
@@ -1,8 +1,5 @@
|
|
1
1
|
module SubdomainRoutes
|
2
2
|
class TooManySubdomains < StandardError
|
3
|
-
# TODO: should this just be an ActionController::RoutingError instead? Any benefit to having a separate error type?
|
4
|
-
# OK, keep the special codes, but catch and re-case them in UrlWriter.
|
5
|
-
# (The errors are also raised in extract_request_environment...)
|
6
3
|
end
|
7
4
|
|
8
5
|
module SplitHost
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module SubdomainRoutes
|
2
2
|
class HostNotSupplied < StandardError
|
3
|
-
# TODO: should this just be an ActionController::RoutingError instead? Any benefit to having a separate error type?
|
4
3
|
end
|
5
4
|
|
6
5
|
module RewriteSubdomainOptions
|
@@ -13,7 +12,7 @@ module SubdomainRoutes
|
|
13
12
|
def rewrite_subdomain_options(options, host)
|
14
13
|
if subdomains = options[:subdomains]
|
15
14
|
old_subdomain, domain = split_host(host)
|
16
|
-
new_subdomain = options.has_key?(:subdomain) ? options[:subdomain].to_s.downcase : old_subdomain
|
15
|
+
new_subdomain = options.has_key?(:subdomain) ? options[:subdomain].to_param.to_s.downcase : old_subdomain
|
17
16
|
begin
|
18
17
|
case subdomains
|
19
18
|
when Array
|
@@ -24,11 +23,11 @@ module SubdomainRoutes
|
|
24
23
|
new_subdomain = subdomains.first
|
25
24
|
end
|
26
25
|
end
|
27
|
-
when
|
28
|
-
unless
|
26
|
+
when Symbol
|
27
|
+
unless new_subdomain.blank? || SubdomainRoutes.valid_subdomain?(new_subdomain)
|
29
28
|
raise ActionController::RoutingError, "subdomain #{new_subdomain.inspect} is invalid"
|
30
|
-
end
|
31
|
-
end
|
29
|
+
end
|
30
|
+
end
|
32
31
|
rescue ActionController::RoutingError => e
|
33
32
|
raise ActionController::RoutingError, "Route for #{options.inspect} failed to generate (#{e.message})"
|
34
33
|
end
|
@@ -1,12 +1,15 @@
|
|
1
1
|
module SubdomainRoutes
|
2
2
|
def self.valid_subdomain?(subdomain)
|
3
3
|
subdomain.to_s =~ /^([a-z]|[a-z][a-z0-9]|[a-z]([a-z0-9]|\-[a-z0-9])*)$/
|
4
|
-
|
4
|
+
end
|
5
|
+
|
6
|
+
# # Alternatively, we use URI::parse instead. This gives more lenient subdomains however:
|
7
|
+
# def self.valid_subdomain?(subdomain)
|
5
8
|
# URI.parse "http://#{subdomain}.example.com"
|
6
9
|
# rescue URI::InvalidURIError
|
7
10
|
# false
|
8
|
-
end
|
9
|
-
|
11
|
+
# end
|
12
|
+
|
10
13
|
module Validations
|
11
14
|
module ClassMethods
|
12
15
|
def validates_subdomain_format_of(*attr_names)
|
@@ -19,6 +22,17 @@ module SubdomainRoutes
|
|
19
22
|
end
|
20
23
|
end
|
21
24
|
end
|
25
|
+
|
26
|
+
def validates_subdomain_not_reserved(*attr_names)
|
27
|
+
configuration = { :on => :save }
|
28
|
+
configuration.update(attr_names.extract_options!)
|
29
|
+
|
30
|
+
validates_each(attr_names, configuration) do |record, attr_name, value|
|
31
|
+
if ActionController::Routing::Routes.reserved_subdomains.include? value
|
32
|
+
record.errors.add(attr_name, :is_a_reserved_subdomain, :default => configuration[:message], :value => value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
22
36
|
end
|
23
37
|
end
|
24
38
|
end
|
data/lib/subdomain_routes.rb
CHANGED
@@ -5,10 +5,8 @@ require 'action_controller'
|
|
5
5
|
require 'subdomain_routes/config'
|
6
6
|
require 'subdomain_routes/split_host'
|
7
7
|
require 'subdomain_routes/mapper'
|
8
|
-
require 'subdomain_routes/proc_set'
|
9
8
|
require 'subdomain_routes/routes'
|
10
9
|
require 'subdomain_routes/resources'
|
11
10
|
require 'subdomain_routes/url_writer'
|
12
11
|
require 'subdomain_routes/request'
|
13
12
|
require 'subdomain_routes/validations'
|
14
|
-
require 'subdomain_routes/mailer'
|
data/spec/recognition_spec.rb
CHANGED
@@ -57,33 +57,24 @@ describe "subdomain route recognition" do
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
-
context "for a :
|
60
|
+
context "for a :model subdomain" do
|
61
61
|
before(:each) do
|
62
|
-
|
63
|
-
ActionController::Routing::Routes.recognize_subdomain(:user, &@user_block)
|
64
|
-
map_subdomain(:proc => :user) { |user| user.resources :articles }
|
65
|
-
@request.request_uri = "/articles"
|
66
|
-
@request.host = "mholling.example.com"
|
62
|
+
map_subdomain(:model => :user) { |user| user.resources :articles }
|
67
63
|
end
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
64
|
+
|
65
|
+
context "when a nested route is requested" do
|
66
|
+
before(:each) do
|
67
|
+
@request.host = "mholling.example.com"
|
68
|
+
@request.request_uri = "/articles"
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should match the route if there is a subdomain" do
|
72
72
|
lambda { recognize_path(@request) }.should_not raise_error
|
73
73
|
end
|
74
|
-
end
|
75
74
|
|
76
|
-
|
77
|
-
|
78
|
-
ActionController::Routing::Routes.subdomain_procs.should_receive(:recognize).any_number_of_times.with(:user, "mholling").and_return(value)
|
79
|
-
lambda { recognize_path(@request) }.should raise_error(ActionController::RoutingError)
|
75
|
+
it "should put the subdomain into the params as :model_id" do
|
76
|
+
recognize_path(@request)[:user_id].should == "mholling"
|
80
77
|
end
|
81
78
|
end
|
82
|
-
|
83
|
-
it "should raise any error that the recognize proc raises" do
|
84
|
-
error = StandardError.new
|
85
|
-
ActionController::Routing::Routes.subdomain_procs.should_receive(:recognize).any_number_of_times.with(:user, "mholling").and_raise(error)
|
86
|
-
lambda { recognize_path(@request) }.should raise_error { |e| e.should == error }
|
87
|
-
end
|
88
79
|
end
|
89
80
|
end
|
data/spec/resources_spec.rb
CHANGED
@@ -29,7 +29,7 @@ describe "resource" do
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
it "should include the subdomains in the routing
|
32
|
+
it "should include the subdomains in the routing requirements" do
|
33
33
|
ActionController::Routing::Routes.routes.each do |route|
|
34
34
|
route.requirements[:subdomains].should == [ "admin" ]
|
35
35
|
end
|
data/spec/routes_spec.rb
CHANGED
@@ -45,9 +45,9 @@ describe "subdomain routes" do
|
|
45
45
|
it "should downcase the subdomains" do
|
46
46
|
map_subdomain(:Admin, "SUPPORT") { |map| map.options[:subdomains].should == [ "admin", "support" ] }
|
47
47
|
end
|
48
|
-
|
49
|
-
it "should
|
50
|
-
map_subdomain(:
|
48
|
+
|
49
|
+
it "should raise ArgumentError if a nil :model option is specified as the subdomain" do
|
50
|
+
lambda { map_subdomain(:model => "") { } }.should raise_error(ArgumentError)
|
51
51
|
end
|
52
52
|
|
53
53
|
it "should raise ArgumentError if no subdomain is specified" do
|
@@ -132,117 +132,40 @@ describe "subdomain routes" do
|
|
132
132
|
end
|
133
133
|
end
|
134
134
|
end
|
135
|
-
|
136
|
-
context "for a
|
137
|
-
|
138
|
-
map_subdomain(:
|
139
|
-
map.resources :articles, :has_many => :comments
|
140
|
-
map.foobar "foobar", :controller => "foo", :action => "bar"
|
141
|
-
map.named_route "foobaz", "foobaz", :controller => "foo", :action => "baz"
|
142
|
-
map.connect "/:controller/:action/:id"
|
143
|
-
end
|
135
|
+
|
136
|
+
context "for a :model subdomain" do
|
137
|
+
it "should accept a :model option as the subdomain and turn it into a foreign key symbol" do
|
138
|
+
map_subdomain(:model => :city) { |city| city.options[:subdomains].should == :city_id }
|
144
139
|
end
|
145
140
|
|
146
|
-
it "should
|
147
|
-
|
148
|
-
route.conditions[:subdomains].should == [ "admin" ]
|
149
|
-
end
|
141
|
+
it "should singularize a plural model name" do
|
142
|
+
map_subdomain(:model => :cities) { |city| city.options[:subdomains].should == :city_id }
|
150
143
|
end
|
151
|
-
|
152
|
-
it "should
|
153
|
-
|
154
|
-
route.requirements[:subdomains].should == [ "admin" ]
|
155
|
-
end
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
context "for multiple specified subdomains" do
|
160
|
-
before(:each) do
|
161
|
-
map_subdomain(:support, :admin) do |map|
|
162
|
-
map.resources :articles, :has_many => :comments
|
163
|
-
map.foobar "foobar", :controller => "foo", :action => "bar"
|
164
|
-
map.named_route "foobaz", "foobaz", :controller => "foo", :action => "baz"
|
165
|
-
map.connect "/:controller/:action/:id"
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
it "should add the specified subdomain to the route recognition conditions" do
|
170
|
-
ActionController::Routing::Routes.routes.each do |route|
|
171
|
-
route.conditions[:subdomains].should == [ "support", "admin" ]
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
it "should not add a subdomain to the route generation requirements" do
|
176
|
-
ActionController::Routing::Routes.routes.each do |route|
|
177
|
-
route.requirements[:subdomains].should == [ "support", "admin" ]
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
context "for a :proc subdomain" do
|
183
|
-
it "should set the value a namespace" do
|
184
|
-
map_subdomain(:proc => :city) { |city| city.options[:namespace].should == "city/" }
|
144
|
+
|
145
|
+
it "should accept a string model name" do
|
146
|
+
map_subdomain(:model => "city") { |city| city.options[:subdomains].should == :city_id }
|
185
147
|
end
|
186
|
-
|
187
|
-
it "should
|
188
|
-
map_subdomain(:
|
148
|
+
|
149
|
+
it "should set the model name as a namespace" do
|
150
|
+
map_subdomain(:model => :city) { |city| city.options[:namespace].should == "city/" }
|
189
151
|
end
|
190
152
|
|
191
|
-
it "should
|
192
|
-
map_subdomain(:
|
193
|
-
end
|
194
|
-
|
195
|
-
it "should prefix the name to named routes if specified" do
|
196
|
-
map_subdomain(:proc => :city, :name => :something) { |city| city.options[:name_prefix].should == "something_" }
|
197
|
-
end
|
198
|
-
|
199
|
-
it "should add the specified proc to the route recognition conditions" do
|
200
|
-
map_subdomain(:proc => :city) { |city| city.resources :events }
|
201
|
-
ActionController::Routing::Routes.routes.each do |route|
|
202
|
-
route.conditions[:subdomains].should == { :proc => :city }
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
it "should add the specified proc to the route generation requirements" do
|
207
|
-
map_subdomain(:proc => :city) { |city| city.resources :events }
|
208
|
-
ActionController::Routing::Routes.routes.each do |route|
|
209
|
-
route.requirements[:subdomains].should == { :proc => :city }
|
210
|
-
end
|
153
|
+
it "should prefix the model name to named routes" do
|
154
|
+
map_subdomain(:model => :city) { |city| city.options[:name_prefix].should == "city_" }
|
211
155
|
end
|
212
156
|
end
|
213
157
|
end
|
214
158
|
|
215
159
|
describe "ActionController::Routing::Routes" do
|
216
160
|
before(:each) do
|
161
|
+
SubdomainRoutes::Config.stub!(:domain_length).and_return(2)
|
217
162
|
ActionController::Routing::Routes.clear!
|
218
|
-
|
219
|
-
|
220
|
-
it "should allow a subdomain recognition method to be added" do
|
221
|
-
ActionController::Routing::Routes.subdomain_procs.recognizes?(:city).should be_false
|
222
|
-
ActionController::Routing::Routes.recognize_subdomain(:city) { |city| city == "perth" }
|
223
|
-
ActionController::Routing::Routes.subdomain_procs.recognizes?(:city).should be_true
|
224
|
-
end
|
225
|
-
|
226
|
-
it "should allow subdomain recognition methods to be cleared" do
|
227
|
-
ActionController::Routing::Routes.recognize_subdomain(:city) { |city| city == "perth" }
|
228
|
-
ActionController::Routing::Routes.clear!
|
229
|
-
ActionController::Routing::Routes.subdomain_procs.recognizes?(:city).should be_false
|
163
|
+
map_subdomain(:www, nil) { |www| www.root :controller => "homes", :action => "show" }
|
230
164
|
end
|
231
165
|
|
232
|
-
it "should
|
233
|
-
ActionController::Routing::Routes.
|
234
|
-
|
235
|
-
|
236
|
-
rescue ActionController::RoutingError # no routes defined so the call will raise a routing error
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
it "should not flush the cache when called if SubdomainRoutes::Config.manual_flush is set" do
|
241
|
-
SubdomainRoutes::Config.stub!(:manual_flush).and_return(true)
|
242
|
-
ActionController::Routing::Routes.subdomain_procs.should_not_receive(:flush!)
|
243
|
-
begin
|
244
|
-
ActionController::Routing::Routes.call(ActionController::TestRequest.new.env)
|
245
|
-
rescue ActionController::RoutingError # no routes defined so the call will raise a routing error
|
246
|
-
end
|
166
|
+
it "should know a list of its reserved subdomains" do
|
167
|
+
ActionController::Routing::Routes.reserved_subdomains.should == [ "www", "" ]
|
168
|
+
ActionController::Routing::Routes.clear!
|
169
|
+
ActionController::Routing::Routes.reserved_subdomains.should be_empty
|
247
170
|
end
|
248
171
|
end
|
data/spec/url_writing_spec.rb
CHANGED
@@ -123,54 +123,44 @@ describe "URL writing" do
|
|
123
123
|
with_host("www.example.com") { users_url(:subdomain => mixedcase).should == "http://#{lowercase}.example.com/users" }
|
124
124
|
end
|
125
125
|
end
|
126
|
-
|
127
|
-
context "when a :
|
126
|
+
|
127
|
+
context "when a :model subdomain is specified" do
|
128
128
|
before(:each) do
|
129
|
-
map_subdomain(:
|
129
|
+
map_subdomain(:model => :city) { |city| city.resources :events }
|
130
|
+
class City < ActiveRecord::Base; end
|
131
|
+
@boston = City.new
|
132
|
+
@boston.stub!(:new_record?).and_return(false)
|
133
|
+
@boston.stub!(:to_param).and_return("boston")
|
130
134
|
end
|
131
|
-
|
132
|
-
it "should
|
135
|
+
|
136
|
+
it "should not change the host if the object has the same to_param as the current subdomain" do
|
133
137
|
with_host "boston.example.com" do
|
134
|
-
|
135
|
-
|
138
|
+
city_events_url(@boston).should == "http://boston.example.com/events"
|
139
|
+
city_events_path(@boston).should == "/events"
|
136
140
|
end
|
137
141
|
end
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
it "should not change the host if the recognize proc returns true" do
|
145
|
-
with_host "boston.example.com" do
|
146
|
-
ActionController::Routing::Routes.subdomain_procs.should_receive(:recognize).twice.with(:city, "boston").and_return(true)
|
147
|
-
city_events_url.should == "http://boston.example.com/events"
|
148
|
-
city_events_path.should == "/events"
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
it "should raise a routing error if the recognize proc returns false" do
|
153
|
-
with_host "www.example.com" do
|
154
|
-
ActionController::Routing::Routes.subdomain_procs.should_receive(:recognize).twice.with(:city, "www").and_return(false)
|
155
|
-
lambda { city_events_url }.should raise_error(ActionController::RoutingError)
|
156
|
-
lambda { city_events_path }.should raise_error(ActionController::RoutingError)
|
157
|
-
end
|
142
|
+
|
143
|
+
it "should force the host if the object has a different to_param from the current subdomain" do
|
144
|
+
with_host "example.com" do
|
145
|
+
city_events_url(@boston).should == "http://boston.example.com/events"
|
146
|
+
city_events_path(@boston).should == "http://boston.example.com/events"
|
158
147
|
end
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should raise an error if the object to_param is an invalid subdomain" do
|
151
|
+
@newyork = City.new
|
152
|
+
@newyork.stub!(:new_record?).and_return(false)
|
153
|
+
@newyork.stub!(:to_param).and_return("new york")
|
154
|
+
with_host "www.example.com" do
|
155
|
+
lambda { city_events_url(@newyork) }.should raise_error(ActionController::RoutingError)
|
156
|
+
lambda { city_events_path(@newyork) }.should raise_error(ActionController::RoutingError)
|
166
157
|
end
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
end
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should not allow the subdomain to be manually overridden" do
|
161
|
+
with_host "www.example.com" do
|
162
|
+
city_events_url(@boston, :subdomain => :canberra).should == "http://boston.example.com/events"
|
163
|
+
city_events_path(@boston, :subdomain => :canberra).should == "http://boston.example.com/events"
|
174
164
|
end
|
175
165
|
end
|
176
166
|
end
|
data/spec/validations_spec.rb
CHANGED
@@ -1,12 +1,29 @@
|
|
1
1
|
describe "ActiveRecord::Base" do
|
2
|
-
|
2
|
+
before(:each) do
|
3
|
+
ActionController::Routing::Routes.clear!
|
4
|
+
SubdomainRoutes::Config.stub!(:domain_length).and_return(2)
|
3
5
|
class User < ActiveRecord::Base
|
4
6
|
attr_accessor :subdomain
|
5
|
-
User.validates_subdomain_format_of :subdomain
|
6
7
|
end
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should have validates_subdomain_format_of which runs SubdomainRoutes.valid_subdomain? against the attributes" do
|
11
|
+
User.validates_subdomain_format_of :subdomain
|
7
12
|
SubdomainRoutes.should_receive(:valid_subdomain?).with("mholling").and_return(true)
|
8
|
-
User.new(:subdomain => "mholling").
|
13
|
+
User.new(:subdomain => "mholling").should be_valid
|
9
14
|
SubdomainRoutes.should_receive(:valid_subdomain?).with("mholling").and_return(nil)
|
10
|
-
User.new(:subdomain => "mholling").
|
15
|
+
User.new(:subdomain => "mholling").should_not be_valid
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should have validates_subdomain_not_reserved which checks the attributes against the fixed-subdomain routes" do
|
19
|
+
User.validates_subdomain_not_reserved :subdomain
|
20
|
+
reserved = [ "", "www", "support", "admin" ]
|
21
|
+
map_subdomain(*reserved) { |map| map.resource :home }
|
22
|
+
reserved.each do |subdomain|
|
23
|
+
User.new(:subdomain => subdomain).should_not be_valid
|
24
|
+
end
|
25
|
+
[ "mholling", "edmondst" ].each do |subdomain|
|
26
|
+
User.new(:subdomain => subdomain).should be_valid
|
27
|
+
end
|
11
28
|
end
|
12
29
|
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.
|
4
|
+
version: 0.2.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-
|
12
|
+
date: 2009-06-02 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -38,9 +38,7 @@ files:
|
|
38
38
|
- VERSION.yml
|
39
39
|
- lib/subdomain_routes.rb
|
40
40
|
- lib/subdomain_routes/config.rb
|
41
|
-
- lib/subdomain_routes/mailer.rb
|
42
41
|
- lib/subdomain_routes/mapper.rb
|
43
|
-
- lib/subdomain_routes/proc_set.rb
|
44
42
|
- lib/subdomain_routes/request.rb
|
45
43
|
- lib/subdomain_routes/resources.rb
|
46
44
|
- lib/subdomain_routes/routes.rb
|
@@ -48,8 +46,6 @@ files:
|
|
48
46
|
- lib/subdomain_routes/url_writer.rb
|
49
47
|
- lib/subdomain_routes/validations.rb
|
50
48
|
- spec/extraction_spec.rb
|
51
|
-
- spec/mailer_spec.rb
|
52
|
-
- spec/proc_set_spec.rb
|
53
49
|
- spec/recognition_spec.rb
|
54
50
|
- spec/resources_spec.rb
|
55
51
|
- spec/routes_spec.rb
|
@@ -84,8 +80,6 @@ specification_version: 2
|
|
84
80
|
summary: A Rails library for incorporating subdomains into route generation and recognition.
|
85
81
|
test_files:
|
86
82
|
- spec/extraction_spec.rb
|
87
|
-
- spec/mailer_spec.rb
|
88
|
-
- spec/proc_set_spec.rb
|
89
83
|
- spec/recognition_spec.rb
|
90
84
|
- spec/resources_spec.rb
|
91
85
|
- spec/routes_spec.rb
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module SubdomainRoutes
|
2
|
-
module MailerMethods
|
3
|
-
def self.included(base)
|
4
|
-
base.alias_method_chain :create!, :subdomains
|
5
|
-
end
|
6
|
-
|
7
|
-
def create_with_subdomains!(*args)
|
8
|
-
ActionController::Routing::Routes.subdomain_procs.flush! unless SubdomainRoutes::Config.manual_flush
|
9
|
-
create_without_subdomains!(*args)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
if defined? ActionMailer::Base
|
15
|
-
ActionMailer::Base.send :include, SubdomainRoutes::MailerMethods
|
16
|
-
end
|
17
|
-
|
@@ -1,33 +0,0 @@
|
|
1
|
-
module SubdomainRoutes
|
2
|
-
class ProcSet
|
3
|
-
extend ActiveSupport::Memoizable
|
4
|
-
|
5
|
-
def initialize
|
6
|
-
clear!
|
7
|
-
end
|
8
|
-
|
9
|
-
def add_recognizer(name, &block)
|
10
|
-
@recognizers[name] = block
|
11
|
-
end
|
12
|
-
|
13
|
-
def recognizes?(name)
|
14
|
-
@recognizers.has_key?(name)
|
15
|
-
end
|
16
|
-
|
17
|
-
def recognize(name, subdomain)
|
18
|
-
@recognizers[name].call(subdomain) if recognizes?(name)
|
19
|
-
end
|
20
|
-
|
21
|
-
memoize :recognize
|
22
|
-
private :flush_cache
|
23
|
-
|
24
|
-
def flush!
|
25
|
-
flush_cache :recognize
|
26
|
-
end
|
27
|
-
|
28
|
-
def clear!
|
29
|
-
@recognizers = {}
|
30
|
-
flush!
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
data/spec/mailer_spec.rb
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
describe "ActionMailer::Base" do
|
2
|
-
before(:each) do
|
3
|
-
@mailer_class = Class.new(ActionMailer::Base) { def test; body "test"; end }
|
4
|
-
end
|
5
|
-
it "should flush the subdomain procs cache each time a mailer is created" do
|
6
|
-
ActionController::Routing::Routes.subdomain_procs.should_receive(:flush!)
|
7
|
-
@mailer_class.create_test
|
8
|
-
end
|
9
|
-
|
10
|
-
it "should not flush the subdomain procs cache if SubdomainRoutes::Config.manual_flush is set" do
|
11
|
-
SubdomainRoutes::Config.stub!(:manual_flush).and_return(true)
|
12
|
-
ActionController::Routing::Routes.subdomain_procs.should_not_receive(:flush!)
|
13
|
-
@mailer_class.create_test
|
14
|
-
end
|
15
|
-
end
|
data/spec/proc_set_spec.rb
DELETED
@@ -1,61 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe "subdomain proc set" do
|
4
|
-
before(:each) do
|
5
|
-
@proc_set = SubdomainRoutes::ProcSet.new
|
6
|
-
end
|
7
|
-
|
8
|
-
context "with a subdomain recognizer" do
|
9
|
-
before(:each) do
|
10
|
-
@city_block = lambda { |city| } # this recognizer block will be stubbed out
|
11
|
-
@proc_set.add_recognizer(:city, &@city_block)
|
12
|
-
end
|
13
|
-
|
14
|
-
it "should indicate what subdomain it recognizes" do
|
15
|
-
@proc_set.recognizes?(:city).should be_true
|
16
|
-
@proc_set.recognizes?(:user).should be_false
|
17
|
-
end
|
18
|
-
|
19
|
-
it "should run the recognizer" do
|
20
|
-
@city_block.should_receive(:call).with("boston").and_return(true)
|
21
|
-
@proc_set.recognize(:city, "boston").should == true
|
22
|
-
end
|
23
|
-
|
24
|
-
it "should raise any error that the recognizer raises" do
|
25
|
-
error = StandardError.new
|
26
|
-
@city_block.stub!(:call).and_raise(error)
|
27
|
-
lambda { @proc_set.recognize(:city, "hobart") }.should raise_error { |e| e.should == error }
|
28
|
-
end
|
29
|
-
|
30
|
-
it "should return nil if it can't recognize the name" do
|
31
|
-
@proc_set.recognize(:user, "mholling").should be_nil
|
32
|
-
end
|
33
|
-
|
34
|
-
it "should call the recognize proc only once for multiple recognitions" do
|
35
|
-
@city_block.should_receive(:call).with("boston").once
|
36
|
-
2.times { @proc_set.recognize(:city, "boston") }
|
37
|
-
end
|
38
|
-
|
39
|
-
it "should return the cached value according to the arguments" do
|
40
|
-
@city_block.should_receive(:call).with("boston").once.and_return(true)
|
41
|
-
@city_block.should_receive(:call).with("hobart").once.and_return(false)
|
42
|
-
2.times do
|
43
|
-
@proc_set.recognize(:city, "boston").should == true
|
44
|
-
@proc_set.recognize(:city, "hobart").should == false
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
it "should call the recognize proc again once the cache is flushed" do
|
49
|
-
@city_block.should_receive(:call).with("boston").twice
|
50
|
-
5.times { @proc_set.recognize(:city, "boston") }
|
51
|
-
@proc_set.flush!
|
52
|
-
5.times { @proc_set.recognize(:city, "boston") }
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
it "can be cleared of its procs" do
|
57
|
-
@proc_set.add_recognizer(:city) { |city| }
|
58
|
-
@proc_set.clear!
|
59
|
-
@proc_set.recognizes?(:city).should be_false
|
60
|
-
end
|
61
|
-
end
|