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.
- data/CHANGELOG +7 -1
- data/lib/abstract_controller/base.rb +1 -0
- data/lib/abstract_controller/callbacks.rb +2 -0
- data/lib/abstract_controller/rendering.rb +4 -4
- data/lib/action_controller/base.rb +2 -0
- data/lib/action_controller/metal.rb +8 -3
- data/lib/action_controller/metal/head.rb +2 -4
- data/lib/action_controller/metal/http_authentication.rb +8 -10
- data/lib/action_controller/metal/mime_responds.rb +1 -1
- data/lib/action_controller/metal/redirecting.rb +4 -2
- data/lib/action_controller/metal/renderers.rb +1 -1
- data/lib/action_controller/metal/responder.rb +20 -0
- data/lib/action_controller/test_case.rb +7 -0
- data/lib/action_controller/vendor/html-scanner/html/node.rb +5 -11
- data/lib/action_dispatch/http/request.rb +19 -2
- data/lib/action_dispatch/http/response.rb +9 -10
- data/lib/action_dispatch/http/upload.rb +21 -29
- data/lib/action_dispatch/middleware/cookies.rb +11 -3
- data/lib/action_dispatch/railtie.rb +0 -5
- data/lib/action_dispatch/routing.rb +75 -22
- data/lib/action_dispatch/routing/mapper.rb +429 -60
- data/lib/action_dispatch/routing/polymorphic_routes.rb +1 -1
- data/lib/action_dispatch/routing/route.rb +2 -1
- data/lib/action_dispatch/routing/url_for.rb +4 -5
- data/lib/action_dispatch/testing/integration.rb +9 -7
- data/lib/action_pack/version.rb +2 -2
- data/lib/action_view/base.rb +1 -1
- data/lib/action_view/helpers/asset_tag_helper.rb +1 -1
- data/lib/action_view/helpers/capture_helper.rb +2 -1
- data/lib/action_view/helpers/date_helper.rb +2 -0
- data/lib/action_view/helpers/form_helper.rb +15 -12
- data/lib/action_view/helpers/form_options_helper.rb +5 -5
- data/lib/action_view/helpers/javascript_helper.rb +2 -1
- data/lib/action_view/helpers/number_helper.rb +23 -12
- data/lib/action_view/helpers/prototype_helper.rb +4 -4
- data/lib/action_view/helpers/text_helper.rb +18 -0
- data/lib/action_view/helpers/url_helper.rb +12 -10
- data/lib/action_view/render/partials.rb +52 -8
- data/lib/action_view/render/rendering.rb +1 -1
- data/lib/action_view/template/handlers/erb.rb +6 -1
- data/lib/action_view/test_case.rb +26 -10
- 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
|
-
#
|
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 =>
|
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
|
-
|
900
|
+
collection do
|
563
901
|
post :create
|
564
902
|
end if parent_resource.actions.include?(:create)
|
565
903
|
|
566
|
-
|
904
|
+
new do
|
567
905
|
get :new
|
568
906
|
end if parent_resource.actions.include?(:new)
|
569
907
|
|
570
|
-
|
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
|
-
|
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
|
-
|
958
|
+
new do
|
597
959
|
get :new
|
598
960
|
end if parent_resource.actions.include?(:new)
|
599
961
|
|
600
|
-
|
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
|
613
|
-
raise ArgumentError, "can't use collection outside
|
986
|
+
unless resource_scope?
|
987
|
+
raise ArgumentError, "can't use collection outside resource(s) scope"
|
614
988
|
end
|
615
989
|
|
616
|
-
|
617
|
-
|
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
|
-
|
627
|
-
|
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
|
-
|
637
|
-
|
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
|
-
|
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[:
|
1221
|
+
@scope[:constraints] && @scope[:constraints][:id].is_a?(Regexp)
|
852
1222
|
end
|
853
1223
|
|
854
1224
|
def id_constraint
|
855
|
-
@scope[:
|
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(
|
882
|
-
if as
|
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(
|
892
|
-
prefix = prefix_name_for_action(
|
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
|
|