actionpack 3.0.1 → 3.0.2

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.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (42) hide show
  1. data/CHANGELOG +7 -1
  2. data/lib/abstract_controller/base.rb +1 -0
  3. data/lib/abstract_controller/callbacks.rb +2 -0
  4. data/lib/abstract_controller/rendering.rb +4 -4
  5. data/lib/action_controller/base.rb +2 -0
  6. data/lib/action_controller/metal.rb +8 -3
  7. data/lib/action_controller/metal/head.rb +2 -4
  8. data/lib/action_controller/metal/http_authentication.rb +8 -10
  9. data/lib/action_controller/metal/mime_responds.rb +1 -1
  10. data/lib/action_controller/metal/redirecting.rb +4 -2
  11. data/lib/action_controller/metal/renderers.rb +1 -1
  12. data/lib/action_controller/metal/responder.rb +20 -0
  13. data/lib/action_controller/test_case.rb +7 -0
  14. data/lib/action_controller/vendor/html-scanner/html/node.rb +5 -11
  15. data/lib/action_dispatch/http/request.rb +19 -2
  16. data/lib/action_dispatch/http/response.rb +9 -10
  17. data/lib/action_dispatch/http/upload.rb +21 -29
  18. data/lib/action_dispatch/middleware/cookies.rb +11 -3
  19. data/lib/action_dispatch/railtie.rb +0 -5
  20. data/lib/action_dispatch/routing.rb +75 -22
  21. data/lib/action_dispatch/routing/mapper.rb +429 -60
  22. data/lib/action_dispatch/routing/polymorphic_routes.rb +1 -1
  23. data/lib/action_dispatch/routing/route.rb +2 -1
  24. data/lib/action_dispatch/routing/url_for.rb +4 -5
  25. data/lib/action_dispatch/testing/integration.rb +9 -7
  26. data/lib/action_pack/version.rb +2 -2
  27. data/lib/action_view/base.rb +1 -1
  28. data/lib/action_view/helpers/asset_tag_helper.rb +1 -1
  29. data/lib/action_view/helpers/capture_helper.rb +2 -1
  30. data/lib/action_view/helpers/date_helper.rb +2 -0
  31. data/lib/action_view/helpers/form_helper.rb +15 -12
  32. data/lib/action_view/helpers/form_options_helper.rb +5 -5
  33. data/lib/action_view/helpers/javascript_helper.rb +2 -1
  34. data/lib/action_view/helpers/number_helper.rb +23 -12
  35. data/lib/action_view/helpers/prototype_helper.rb +4 -4
  36. data/lib/action_view/helpers/text_helper.rb +18 -0
  37. data/lib/action_view/helpers/url_helper.rb +12 -10
  38. data/lib/action_view/render/partials.rb +52 -8
  39. data/lib/action_view/render/rendering.rb +1 -1
  40. data/lib/action_view/template/handlers/erb.rb +6 -1
  41. data/lib/action_view/test_case.rb +26 -10
  42. metadata +35 -12
@@ -8,10 +8,5 @@ module ActionDispatch
8
8
  config.action_dispatch.ip_spoofing_check = true
9
9
  config.action_dispatch.show_exceptions = true
10
10
  config.action_dispatch.best_standards_support = true
11
-
12
- # Prepare dispatcher callbacks and run 'prepare' callbacks
13
- initializer "action_dispatch.prepare_dispatcher" do |app|
14
- ActionDispatch::Callbacks.to_prepare { app.routes_reloader.execute_if_updated }
15
- end
16
11
  end
17
12
  end
@@ -2,31 +2,11 @@ require 'active_support/core_ext/object/to_param'
2
2
  require 'active_support/core_ext/regexp'
3
3
 
4
4
  module ActionDispatch
5
- # = Routing
6
- #
7
5
  # The routing module provides URL rewriting in native Ruby. It's a way to
8
6
  # redirect incoming requests to controllers and actions. This replaces
9
- # mod_rewrite rules. Best of all, Rails' Routing works with any web server.
7
+ # mod_rewrite rules. Best of all, Rails' \Routing works with any web server.
10
8
  # Routes are defined in <tt>config/routes.rb</tt>.
11
9
  #
12
- # Consider the following route, which you will find commented out at the
13
- # bottom of your generated <tt>config/routes.rb</tt>:
14
- #
15
- # match ':controller(/:action(/:id(.:format)))'
16
- #
17
- # This route states that it expects requests to consist of a
18
- # <tt>:controller</tt> followed optionally by an <tt>:action</tt> that in
19
- # turn is followed optionally by an <tt>:id</tt>, which in turn is followed
20
- # optionally by a <tt>:format</tt>
21
- #
22
- # Suppose you get an incoming request for <tt>/blog/edit/22</tt>, you'll end
23
- # up with:
24
- #
25
- # params = { :controller => 'blog',
26
- # :action => 'edit',
27
- # :id => '22'
28
- # }
29
- #
30
10
  # Think of creating routes as drawing a map for your requests. The map tells
31
11
  # them where to go based on some predefined pattern:
32
12
  #
@@ -43,6 +23,39 @@ module ActionDispatch
43
23
  #
44
24
  # Other names simply map to a parameter as in the case of <tt>:id</tt>.
45
25
  #
26
+ # == Resources
27
+ #
28
+ # Resource routing allows you to quickly declare all of the common routes
29
+ # for a given resourceful controller. Instead of declaring separate routes
30
+ # for your +index+, +show+, +new+, +edit+, +create+, +update+ and +destroy+
31
+ # actions, a resourceful route declares them in a single line of code:
32
+ #
33
+ # resources :photos
34
+ #
35
+ # Sometimes, you have a resource that clients always look up without
36
+ # referencing an ID. A common example, /profile always shows the profile of
37
+ # the currently logged in user. In this case, you can use a singular resource
38
+ # to map /profile (rather than /profile/:id) to the show action.
39
+ #
40
+ # resource :profile
41
+ #
42
+ # It's common to have resources that are logically children of other
43
+ # resources:
44
+ #
45
+ # resources :magazines do
46
+ # resources :ads
47
+ # end
48
+ #
49
+ # You may wish to organize groups of controllers under a namespace. Most
50
+ # commonly, you might group a number of administrative controllers under
51
+ # an +admin+ namespace. You would place these controllers under the
52
+ # app/controllers/admin directory, and you can group them together in your
53
+ # router:
54
+ #
55
+ # namespace "admin" do
56
+ # resources :posts, :comments
57
+ # end
58
+ #
46
59
  # == Named routes
47
60
  #
48
61
  # Routes can be named by passing an <tt>:as</tt> option,
@@ -131,6 +144,30 @@ module ActionDispatch
131
144
  # Encoding regular expression modifiers are silently ignored. The
132
145
  # match will always use the default encoding or ASCII.
133
146
  #
147
+ # == Default route
148
+ #
149
+ # Consider the following route, which you will find commented out at the
150
+ # bottom of your generated <tt>config/routes.rb</tt>:
151
+ #
152
+ # match ':controller(/:action(/:id(.:format)))'
153
+ #
154
+ # This route states that it expects requests to consist of a
155
+ # <tt>:controller</tt> followed optionally by an <tt>:action</tt> that in
156
+ # turn is followed optionally by an <tt>:id</tt>, which in turn is followed
157
+ # optionally by a <tt>:format</tt>.
158
+ #
159
+ # Suppose you get an incoming request for <tt>/blog/edit/22</tt>, you'll end
160
+ # up with:
161
+ #
162
+ # params = { :controller => 'blog',
163
+ # :action => 'edit',
164
+ # :id => '22'
165
+ # }
166
+ #
167
+ # By not relying on default routes, you improve the security of your
168
+ # application since not all controller actions, which includes actions you
169
+ # might add at a later time, are exposed by default.
170
+ #
134
171
  # == HTTP Methods
135
172
  #
136
173
  # Using the <tt>:via</tt> option when specifying a route allows you to restrict it to a specific HTTP method.
@@ -160,6 +197,20 @@ module ActionDispatch
160
197
  # however if your route needs to respond to more than one HTTP method (or all methods) then using the
161
198
  # <tt>:via</tt> option on <tt>match</tt> is preferable.
162
199
  #
200
+ # == External redirects
201
+ #
202
+ # You can redirect any path to another path using the redirect helper in your router:
203
+ #
204
+ # match "/stories" => redirect("/posts")
205
+ #
206
+ # == Routing to Rack Applications
207
+ #
208
+ # Instead of a String, like <tt>posts#index</tt>, which corresponds to the
209
+ # index action in the PostsController, you can specify any Rack application
210
+ # as the endpoint for a matcher:
211
+ #
212
+ # match "/application.js" => Sprockets
213
+ #
163
214
  # == Reloading routes
164
215
  #
165
216
  # You can reload routes if you feel you must:
@@ -208,7 +259,9 @@ module ActionDispatch
208
259
  #
209
260
  # == View a list of all your routes
210
261
  #
211
- # Run <tt>rake routes</tt>.
262
+ # rake routes
263
+ #
264
+ # Target specific controllers by prefixing the command with <tt>CONTROLLER=x</tt>.
212
265
  #
213
266
  module Routing
214
267
  autoload :DeprecatedMapper, 'action_dispatch/routing/deprecated_mapper'
@@ -1,6 +1,7 @@
1
1
  require 'erb'
2
2
  require 'active_support/core_ext/hash/except'
3
3
  require 'active_support/core_ext/object/blank'
4
+ require 'active_support/inflector'
4
5
 
5
6
  module ActionDispatch
6
7
  module Routing
@@ -62,7 +63,6 @@ module ActionDispatch
62
63
  if using_match_shorthand?(path_without_format, @options)
63
64
  to_shorthand = @options[:to].blank?
64
65
  @options[:to] ||= path_without_format[1..-1].sub(%r{/([^/]*)$}, '#\1')
65
- @options[:as] ||= Mapper.normalize_name(path_without_format)
66
66
  end
67
67
 
68
68
  @options.merge!(default_controller_and_action(to_shorthand))
@@ -113,6 +113,15 @@ module ActionDispatch
113
113
  @requirements ||= (@options[:constraints].is_a?(Hash) ? @options[:constraints] : {}).tap do |requirements|
114
114
  requirements.reverse_merge!(@scope[:constraints]) if @scope[:constraints]
115
115
  @options.each { |k, v| requirements[k] = v if v.is_a?(Regexp) }
116
+
117
+ requirements.each do |_, requirement|
118
+ if requirement.source =~ %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
119
+ raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
120
+ end
121
+ if requirement.multiline?
122
+ raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}"
123
+ end
124
+ end
116
125
  end
117
126
  end
118
127
 
@@ -140,6 +149,10 @@ module ActionDispatch
140
149
  controller = [@scope[:module], controller].compact.join("/").presence
141
150
  end
142
151
 
152
+ if controller.is_a?(String) && controller =~ %r{\A/}
153
+ raise ArgumentError, "controller name should not start with a slash"
154
+ end
155
+
143
156
  controller = controller.to_s unless controller.is_a?(Regexp)
144
157
  action = action.to_s unless action.is_a?(Regexp)
145
158
 
@@ -174,8 +187,8 @@ module ActionDispatch
174
187
 
175
188
  def request_method_condition
176
189
  if via = @options[:via]
177
- via = Array(via).map { |m| m.to_s.upcase }
178
- { :request_method => Regexp.union(*via) }
190
+ via = Array(via).map { |m| m.to_s.dasherize.upcase }
191
+ { :request_method => %r[^#{via.join('|')}$] }
179
192
  else
180
193
  { }
181
194
  end
@@ -226,16 +239,47 @@ module ActionDispatch
226
239
  @set = set
227
240
  end
228
241
 
242
+ # You can specify what Rails should route "/" to with the root method:
243
+ #
244
+ # root :to => 'pages#main'
245
+ #
246
+ # You should put the root route at the end of <tt>config/routes.rb</tt>.
229
247
  def root(options = {})
230
248
  match '/', options.reverse_merge(:as => :root)
231
249
  end
232
250
 
251
+ # When you set up a regular route, you supply a series of symbols that
252
+ # Rails maps to parts of an incoming HTTP request.
253
+ #
254
+ # match ':controller/:action/:id/:user_id'
255
+ #
256
+ # Two of these symbols are special: :controller maps to the name of a
257
+ # controller in your application, and :action maps to the name of an
258
+ # action within that controller. Anything other than :controller or
259
+ # :action will be available to the action as part of params.
233
260
  def match(path, options=nil)
234
261
  mapping = Mapping.new(@set, @scope, path, options || {}).to_route
235
262
  @set.add_route(*mapping)
236
263
  self
237
264
  end
238
265
 
266
+ # Mount a Rack-based application to be used within the application.
267
+ #
268
+ # mount SomeRackApp, :at => "some_route"
269
+ #
270
+ # Alternatively:
271
+ #
272
+ # mount(SomeRackApp => "some_route")
273
+ #
274
+ # All mounted applications come with routing helpers to access them.
275
+ # These are named after the class specified, so for the above example
276
+ # the helper is either +some_rack_app_path+ or +some_rack_app_url+.
277
+ # To customize this helper's name, use the +:as+ option:
278
+ #
279
+ # mount(SomeRackApp => "some_route", :as => "exciting")
280
+ #
281
+ # This will generate the +exciting_path+ and +exciting_url+ helpers
282
+ # which can be used to navigate to this mounted app.
239
283
  def mount(app, options = nil)
240
284
  if options
241
285
  path = options.delete(:at)
@@ -258,22 +302,49 @@ module ActionDispatch
258
302
  end
259
303
 
260
304
  module HttpHelpers
305
+ # Define a route that only recognizes HTTP GET.
306
+ # For supported arguments, see +match+.
307
+ #
308
+ # Example:
309
+ #
310
+ # get 'bacon', :to => 'food#bacon'
261
311
  def get(*args, &block)
262
312
  map_method(:get, *args, &block)
263
313
  end
264
314
 
315
+ # Define a route that only recognizes HTTP POST.
316
+ # For supported arguments, see +match+.
317
+ #
318
+ # Example:
319
+ #
320
+ # post 'bacon', :to => 'food#bacon'
265
321
  def post(*args, &block)
266
322
  map_method(:post, *args, &block)
267
323
  end
268
324
 
325
+ # Define a route that only recognizes HTTP PUT.
326
+ # For supported arguments, see +match+.
327
+ #
328
+ # Example:
329
+ #
330
+ # put 'bacon', :to => 'food#bacon'
269
331
  def put(*args, &block)
270
332
  map_method(:put, *args, &block)
271
333
  end
272
334
 
335
+ # Define a route that only recognizes HTTP PUT.
336
+ # For supported arguments, see +match+.
337
+ #
338
+ # Example:
339
+ #
340
+ # delete 'broccoli', :to => 'food#broccoli'
273
341
  def delete(*args, &block)
274
342
  map_method(:delete, *args, &block)
275
343
  end
276
344
 
345
+ # Redirect any path to another path:
346
+ #
347
+ # match "/stories" => redirect("/posts")
277
348
  def redirect(*args, &block)
278
349
  options = args.last.is_a?(Hash) ? args.pop : {}
279
350
 
@@ -314,12 +385,113 @@ module ActionDispatch
314
385
  end
315
386
  end
316
387
 
388
+ # You may wish to organize groups of controllers under a namespace.
389
+ # Most commonly, you might group a number of administrative controllers
390
+ # under an +admin+ namespace. You would place these controllers under
391
+ # the app/controllers/admin directory, and you can group them together
392
+ # in your router:
393
+ #
394
+ # namespace "admin" do
395
+ # resources :posts, :comments
396
+ # end
397
+ #
398
+ # This will create a number of routes for each of the posts and comments
399
+ # controller. For Admin::PostsController, Rails will create:
400
+ #
401
+ # GET /admin/photos
402
+ # GET /admin/photos/new
403
+ # POST /admin/photos
404
+ # GET /admin/photos/1
405
+ # GET /admin/photos/1/edit
406
+ # PUT /admin/photos/1
407
+ # DELETE /admin/photos/1
408
+ #
409
+ # If you want to route /photos (without the prefix /admin) to
410
+ # Admin::PostsController, you could use
411
+ #
412
+ # scope :module => "admin" do
413
+ # resources :posts, :comments
414
+ # end
415
+ #
416
+ # or, for a single case
417
+ #
418
+ # resources :posts, :module => "admin"
419
+ #
420
+ # If you want to route /admin/photos to PostsController
421
+ # (without the Admin:: module prefix), you could use
422
+ #
423
+ # scope "/admin" do
424
+ # resources :posts, :comments
425
+ # end
426
+ #
427
+ # or, for a single case
428
+ #
429
+ # resources :posts, :path => "/admin"
430
+ #
431
+ # In each of these cases, the named routes remain the same as if you did
432
+ # not use scope. In the last case, the following paths map to
433
+ # PostsController:
434
+ #
435
+ # GET /admin/photos
436
+ # GET /admin/photos/new
437
+ # POST /admin/photos
438
+ # GET /admin/photos/1
439
+ # GET /admin/photos/1/edit
440
+ # PUT /admin/photos/1
441
+ # DELETE /admin/photos/1
317
442
  module Scoping
318
443
  def initialize(*args) #:nodoc:
319
444
  @scope = {}
320
445
  super
321
446
  end
322
447
 
448
+ # Used to route <tt>/photos</tt> (without the prefix <tt>/admin</tt>)
449
+ # to Admin::PostsController:
450
+ # === Supported options
451
+ # [:module]
452
+ # If you want to route /posts (without the prefix /admin) to
453
+ # Admin::PostsController, you could use
454
+ #
455
+ # scope :module => "admin" do
456
+ # resources :posts
457
+ # end
458
+ #
459
+ # [:path]
460
+ # If you want to prefix the route, you could use
461
+ #
462
+ # scope :path => "/admin" do
463
+ # resources :posts
464
+ # end
465
+ #
466
+ # This will prefix all of the +posts+ resource's requests with '/admin'
467
+ #
468
+ # [:as]
469
+ # Prefixes the routing helpers in this scope with the specified label.
470
+ #
471
+ # scope :as => "sekret" do
472
+ # resources :posts
473
+ # end
474
+ #
475
+ # Helpers such as +posts_path+ will now be +sekret_posts_path+
476
+ #
477
+ # [:shallow_path]
478
+ #
479
+ # Prefixes nested shallow routes with the specified path.
480
+ #
481
+ # scope :shallow_path => "sekret" do
482
+ # resources :posts do
483
+ # resources :comments, :shallow => true
484
+ # end
485
+ #
486
+ # The +comments+ resource here will have the following routes generated for it:
487
+ #
488
+ # post_comments GET /sekret/posts/:post_id/comments(.:format)
489
+ # post_comments POST /sekret/posts/:post_id/comments(.:format)
490
+ # new_post_comment GET /sekret/posts/:post_id/comments/new(.:format)
491
+ # edit_comment GET /sekret/comments/:id/edit(.:format)
492
+ # comment GET /sekret/comments/:id(.:format)
493
+ # comment PUT /sekret/comments/:id(.:format)
494
+ # comment DELETE /sekret/comments/:id(.:format)
323
495
  def scope(*args)
324
496
  options = args.extract_options!
325
497
  options = options.dup
@@ -361,22 +533,139 @@ module ActionDispatch
361
533
  @scope[:blocks] = recover[:block]
362
534
  end
363
535
 
536
+ # Scopes routes to a specific controller
537
+ #
538
+ # Example:
539
+ # controller "food" do
540
+ # match "bacon", :action => "bacon"
541
+ # end
364
542
  def controller(controller, options={})
365
543
  options[:controller] = controller
366
544
  scope(options) { yield }
367
545
  end
368
546
 
547
+ # Scopes routes to a specific namespace. For example:
548
+ #
549
+ # namespace :admin do
550
+ # resources :posts
551
+ # end
552
+ #
553
+ # This generates the following routes:
554
+ #
555
+ # admin_posts GET /admin/posts(.:format) {:action=>"index", :controller=>"admin/posts"}
556
+ # admin_posts POST /admin/posts(.:format) {:action=>"create", :controller=>"admin/posts"}
557
+ # new_admin_post GET /admin/posts/new(.:format) {:action=>"new", :controller=>"admin/posts"}
558
+ # edit_admin_post GET /admin/posts/:id/edit(.:format) {:action=>"edit", :controller=>"admin/posts"}
559
+ # admin_post GET /admin/posts/:id(.:format) {:action=>"show", :controller=>"admin/posts"}
560
+ # admin_post PUT /admin/posts/:id(.:format) {:action=>"update", :controller=>"admin/posts"}
561
+ # admin_post DELETE /admin/posts/:id(.:format) {:action=>"destroy", :controller=>"admin/posts"}
562
+ # === Supported options
563
+ #
564
+ # The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+ all default to the name of the namespace.
565
+ #
566
+ # [:path]
567
+ # The path prefix for the routes.
568
+ #
569
+ # namespace :admin, :path => "sekret" do
570
+ # resources :posts
571
+ # end
572
+ #
573
+ # All routes for the above +resources+ will be accessible through +/sekret/posts+, rather than +/admin/posts+
574
+ #
575
+ # [:module]
576
+ # The namespace for the controllers.
577
+ #
578
+ # namespace :admin, :module => "sekret" do
579
+ # resources :posts
580
+ # end
581
+ #
582
+ # The +PostsController+ here should go in the +Sekret+ namespace and so it should be defined like this:
583
+ #
584
+ # class Sekret::PostsController < ApplicationController
585
+ # # code go here
586
+ # end
587
+ #
588
+ # [:as]
589
+ # Changes the name used in routing helpers for this namespace.
590
+ #
591
+ # namespace :admin, :as => "sekret" do
592
+ # resources :posts
593
+ # end
594
+ #
595
+ # Routing helpers such as +admin_posts_path+ will now be +sekret_posts_path+.
596
+ #
597
+ # [:shallow_path]
598
+ # See the +scope+ method.
369
599
  def namespace(path, options = {})
370
600
  path = path.to_s
371
601
  options = { :path => path, :as => path, :module => path,
372
602
  :shallow_path => path, :shallow_prefix => path }.merge!(options)
373
603
  scope(options) { yield }
374
604
  end
375
-
605
+
606
+ # === Parameter Restriction
607
+ # Allows you to constrain the nested routes based on a set of rules.
608
+ # For instance, in order to change the routes to allow for a dot character in the +id+ parameter:
609
+ #
610
+ # constraints(:id => /\d+\.\d+) do
611
+ # resources :posts
612
+ # end
613
+ #
614
+ # Now routes such as +/posts/1+ will no longer be valid, but +/posts/1.1+ will be.
615
+ # The +id+ parameter must match the constraint passed in for this example.
616
+ #
617
+ # You may use this to also resrict other parameters:
618
+ #
619
+ # resources :posts do
620
+ # constraints(:post_id => /\d+\.\d+) do
621
+ # resources :comments
622
+ # end
623
+ #
624
+ # === Restricting based on IP
625
+ #
626
+ # Routes can also be constrained to an IP or a certain range of IP addresses:
627
+ #
628
+ # constraints(:ip => /192.168.\d+.\d+/) do
629
+ # resources :posts
630
+ # end
631
+ #
632
+ # Any user connecting from the 192.168.* range will be able to see this resource,
633
+ # where as any user connecting outside of this range will be told there is no such route.
634
+ #
635
+ # === Dynamic request matching
636
+ #
637
+ # Requests to routes can be constrained based on specific critera:
638
+ #
639
+ # constraints(lambda { |req| req.env["HTTP_USER_AGENT"] =~ /iPhone/ }) do
640
+ # resources :iphones
641
+ # end
642
+ #
643
+ # You are able to move this logic out into a class if it is too complex for routes.
644
+ # This class must have a +matches?+ method defined on it which either returns +true+
645
+ # if the user should be given access to that route, or +false+ if the user should not.
646
+ #
647
+ # class Iphone
648
+ # def self.matches(request)
649
+ # request.env["HTTP_USER_AGENT"] =~ /iPhone/
650
+ # end
651
+ # end
652
+ #
653
+ # An expected place for this code would be +lib/constraints+.
654
+ #
655
+ # This class is then used like this:
656
+ #
657
+ # constraints(Iphone) do
658
+ # resources :iphones
659
+ # end
376
660
  def constraints(constraints = {})
377
661
  scope(:constraints => constraints) { yield }
378
662
  end
379
663
 
664
+ # Allows you to set default parameters for a route, such as this:
665
+ # defaults :id => 'home' do
666
+ # match 'scoped_pages/(:id)', :to => 'pages#show'
667
+ # end
668
+ # Using this, the +:id+ parameter here will default to 'home'.
380
669
  def defaults(defaults = {})
381
670
  scope(:defaults => defaults) { yield }
382
671
  end
@@ -441,6 +730,37 @@ module ActionDispatch
441
730
  end
442
731
  end
443
732
 
733
+ # Resource routing allows you to quickly declare all of the common routes
734
+ # for a given resourceful controller. Instead of declaring separate routes
735
+ # for your +index+, +show+, +new+, +edit+, +create+, +update+ and +destroy+
736
+ # actions, a resourceful route declares them in a single line of code:
737
+ #
738
+ # resources :photos
739
+ #
740
+ # Sometimes, you have a resource that clients always look up without
741
+ # referencing an ID. A common example, /profile always shows the profile of
742
+ # the currently logged in user. In this case, you can use a singular resource
743
+ # to map /profile (rather than /profile/:id) to the show action.
744
+ #
745
+ # resource :profile
746
+ #
747
+ # It's common to have resources that are logically children of other
748
+ # resources:
749
+ #
750
+ # resources :magazines do
751
+ # resources :ads
752
+ # end
753
+ #
754
+ # You may wish to organize groups of controllers under a namespace. Most
755
+ # commonly, you might group a number of administrative controllers under
756
+ # an +admin+ namespace. You would place these controllers under the
757
+ # app/controllers/admin directory, and you can group them together in your
758
+ # router:
759
+ #
760
+ # namespace "admin" do
761
+ # resources :posts, :comments
762
+ # end
763
+ #
444
764
  module Resources
445
765
  # CANONICAL_ACTIONS holds all actions that does not need a prefix or
446
766
  # a path appended since they fit properly in their scope level.
@@ -549,6 +869,24 @@ module ActionDispatch
549
869
  @scope[:path_names].merge!(options)
550
870
  end
551
871
 
872
+ # Sometimes, you have a resource that clients always look up without
873
+ # referencing an ID. A common example, /profile always shows the
874
+ # profile of the currently logged in user. In this case, you can use
875
+ # a singular resource to map /profile (rather than /profile/:id) to
876
+ # the show action:
877
+ #
878
+ # resource :geocoder
879
+ #
880
+ # creates six different routes in your application, all mapping to
881
+ # the GeoCoders controller (note that the controller is named after
882
+ # the plural):
883
+ #
884
+ # GET /geocoder/new
885
+ # POST /geocoder
886
+ # GET /geocoder
887
+ # GET /geocoder/edit
888
+ # PUT /geocoder
889
+ # DELETE /geocoder
552
890
  def resource(*resources, &block)
553
891
  options = resources.extract_options!
554
892
 
@@ -559,15 +897,15 @@ module ActionDispatch
559
897
  resource_scope(SingletonResource.new(resources.pop, options)) do
560
898
  yield if block_given?
561
899
 
562
- collection_scope do
900
+ collection do
563
901
  post :create
564
902
  end if parent_resource.actions.include?(:create)
565
903
 
566
- new_scope do
904
+ new do
567
905
  get :new
568
906
  end if parent_resource.actions.include?(:new)
569
907
 
570
- member_scope do
908
+ member do
571
909
  get :edit if parent_resource.actions.include?(:edit)
572
910
  get :show if parent_resource.actions.include?(:show)
573
911
  put :update if parent_resource.actions.include?(:update)
@@ -578,6 +916,30 @@ module ActionDispatch
578
916
  self
579
917
  end
580
918
 
919
+ # In Rails, a resourceful route provides a mapping between HTTP verbs
920
+ # and URLs and controller actions. By convention, each action also maps
921
+ # to particular CRUD operations in a database. A single entry in the
922
+ # routing file, such as
923
+ #
924
+ # resources :photos
925
+ #
926
+ # creates seven different routes in your application, all mapping to
927
+ # the Photos controller:
928
+ #
929
+ # GET /photos/new
930
+ # POST /photos
931
+ # GET /photos/:id
932
+ # GET /photos/:id/edit
933
+ # PUT /photos/:id
934
+ # DELETE /photos/:id
935
+ # === Supported options
936
+ # [:path_names]
937
+ # Allows you to change the paths of the seven default actions.
938
+ # Paths not specified are not changed.
939
+ #
940
+ # resources :posts, :path_names => { :new => "brand_new" }
941
+ #
942
+ # The above example will now change /posts/new to /posts/brand_new
581
943
  def resources(*resources, &block)
582
944
  options = resources.extract_options!
583
945
 
@@ -588,16 +950,16 @@ module ActionDispatch
588
950
  resource_scope(Resource.new(resources.pop, options)) do
589
951
  yield if block_given?
590
952
 
591
- collection_scope do
953
+ collection do
592
954
  get :index if parent_resource.actions.include?(:index)
593
955
  post :create if parent_resource.actions.include?(:create)
594
956
  end
595
957
 
596
- new_scope do
958
+ new do
597
959
  get :new
598
960
  end if parent_resource.actions.include?(:new)
599
961
 
600
- member_scope do
962
+ member do
601
963
  get :edit if parent_resource.actions.include?(:edit)
602
964
  get :show if parent_resource.actions.include?(:show)
603
965
  put :update if parent_resource.actions.include?(:update)
@@ -608,23 +970,50 @@ module ActionDispatch
608
970
  self
609
971
  end
610
972
 
973
+ # To add a route to the collection:
974
+ #
975
+ # resources :photos do
976
+ # collection do
977
+ # get 'search'
978
+ # end
979
+ # end
980
+ #
981
+ # This will enable Rails to recognize paths such as <tt>/photos/search</tt>
982
+ # with GET, and route to the search action of PhotosController. It will also
983
+ # create the <tt>search_photos_url</tt> and <tt>search_photos_path</tt>
984
+ # route helpers.
611
985
  def collection
612
- unless @scope[:scope_level] == :resources
613
- raise ArgumentError, "can't use collection outside resources scope"
986
+ unless resource_scope?
987
+ raise ArgumentError, "can't use collection outside resource(s) scope"
614
988
  end
615
989
 
616
- collection_scope do
617
- yield
990
+ with_scope_level(:collection) do
991
+ scope(parent_resource.collection_scope) do
992
+ yield
993
+ end
618
994
  end
619
995
  end
620
996
 
997
+ # To add a member route, add a member block into the resource block:
998
+ #
999
+ # resources :photos do
1000
+ # member do
1001
+ # get 'preview'
1002
+ # end
1003
+ # end
1004
+ #
1005
+ # This will recognize <tt>/photos/1/preview</tt> with GET, and route to the
1006
+ # preview action of PhotosController. It will also create the
1007
+ # <tt>preview_photo_url</tt> and <tt>preview_photo_path</tt> helpers.
621
1008
  def member
622
1009
  unless resource_scope?
623
1010
  raise ArgumentError, "can't use member outside resource(s) scope"
624
1011
  end
625
1012
 
626
- member_scope do
627
- yield
1013
+ with_scope_level(:member) do
1014
+ scope(parent_resource.member_scope) do
1015
+ yield
1016
+ end
628
1017
  end
629
1018
  end
630
1019
 
@@ -633,8 +1022,10 @@ module ActionDispatch
633
1022
  raise ArgumentError, "can't use new outside resource(s) scope"
634
1023
  end
635
1024
 
636
- new_scope do
637
- yield
1025
+ with_scope_level(:new) do
1026
+ scope(parent_resource.new_scope(action_path(:new))) do
1027
+ yield
1028
+ end
638
1029
  end
639
1030
  end
640
1031
 
@@ -708,9 +1099,14 @@ module ActionDispatch
708
1099
 
709
1100
  if action.to_s =~ /^[\w\/]+$/
710
1101
  options[:action] ||= action unless action.to_s.include?("/")
711
- options[:as] = name_for_action(action, options[:as])
712
1102
  else
713
- options[:as] = name_for_action(options[:as])
1103
+ action = nil
1104
+ end
1105
+
1106
+ if options.key?(:as) && !options[:as]
1107
+ options.delete(:as)
1108
+ else
1109
+ options[:as] = name_for_action(options[:as], action)
714
1110
  end
715
1111
 
716
1112
  super(path, options)
@@ -740,6 +1136,11 @@ module ActionDispatch
740
1136
  return true
741
1137
  end
742
1138
 
1139
+ if resource_scope?
1140
+ nested { send(method, resources.pop, options, &block) }
1141
+ return true
1142
+ end
1143
+
743
1144
  options.keys.each do |k|
744
1145
  (options[:constraints] ||= {})[k] = options.delete(k) if options[k].is_a?(Regexp)
745
1146
  end
@@ -756,13 +1157,6 @@ module ActionDispatch
756
1157
  options.merge!(scope_action_options) if scope_action_options?
757
1158
  end
758
1159
 
759
- if resource_scope?
760
- nested do
761
- send(method, resources.pop, options, &block)
762
- end
763
- return true
764
- end
765
-
766
1160
  false
767
1161
  end
768
1162
 
@@ -816,30 +1210,6 @@ module ActionDispatch
816
1210
  end
817
1211
  end
818
1212
 
819
- def new_scope
820
- with_scope_level(:new) do
821
- scope(parent_resource.new_scope(action_path(:new))) do
822
- yield
823
- end
824
- end
825
- end
826
-
827
- def collection_scope
828
- with_scope_level(:collection) do
829
- scope(parent_resource.collection_scope) do
830
- yield
831
- end
832
- end
833
- end
834
-
835
- def member_scope
836
- with_scope_level(:member) do
837
- scope(parent_resource.member_scope) do
838
- yield
839
- end
840
- end
841
- end
842
-
843
1213
  def nested_options
844
1214
  {}.tap do |options|
845
1215
  options[:as] = parent_resource.member_name
@@ -848,11 +1218,11 @@ module ActionDispatch
848
1218
  end
849
1219
 
850
1220
  def id_constraint?
851
- @scope[:id].is_a?(Regexp) || (@scope[:constraints] && @scope[:constraints][:id].is_a?(Regexp))
1221
+ @scope[:constraints] && @scope[:constraints][:id].is_a?(Regexp)
852
1222
  end
853
1223
 
854
1224
  def id_constraint
855
- @scope[:id] || @scope[:constraints][:id]
1225
+ @scope[:constraints][:id]
856
1226
  end
857
1227
 
858
1228
  def canonical_action?(action, flag)
@@ -878,18 +1248,16 @@ module ActionDispatch
878
1248
  path || @scope[:path_names][name.to_sym] || name.to_s
879
1249
  end
880
1250
 
881
- def prefix_name_for_action(action, as)
882
- if as.present?
1251
+ def prefix_name_for_action(as, action)
1252
+ if as
883
1253
  as.to_s
884
- elsif as
885
- nil
886
1254
  elsif !canonical_action?(action, @scope[:scope_level])
887
1255
  action.to_s
888
1256
  end
889
1257
  end
890
1258
 
891
- def name_for_action(action, as=nil)
892
- prefix = prefix_name_for_action(action, as)
1259
+ def name_for_action(as, action)
1260
+ prefix = prefix_name_for_action(as, action)
893
1261
  prefix = Mapper.normalize_name(prefix) if prefix
894
1262
  name_prefix = @scope[:as]
895
1263
 
@@ -913,7 +1281,8 @@ module ActionDispatch
913
1281
  [name_prefix, member_name, prefix]
914
1282
  end
915
1283
 
916
- name.select(&:present?).join("_").presence
1284
+ candidate = name.select(&:present?).join("_").presence
1285
+ candidate unless as.nil? && @set.routes.find { |r| r.name == candidate }
917
1286
  end
918
1287
  end
919
1288