actionpack 3.0.0.beta4 → 3.0.0.rc
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- data/CHANGELOG +36 -0
- data/{README → README.rdoc} +79 -137
- data/lib/abstract_controller.rb +1 -0
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +3 -12
- data/lib/abstract_controller/rendering.rb +2 -2
- data/lib/abstract_controller/view_paths.rb +2 -1
- data/lib/action_controller.rb +1 -2
- data/lib/action_controller/base.rb +3 -9
- data/lib/action_controller/log_subscriber.rb +56 -0
- data/lib/action_controller/metal.rb +10 -3
- data/lib/action_controller/metal/helpers.rb +5 -4
- data/lib/action_controller/metal/hide_actions.rb +3 -3
- data/lib/action_controller/metal/instrumentation.rb +2 -1
- data/lib/action_controller/metal/mime_responds.rb +13 -10
- data/lib/action_controller/metal/rack_delegation.rb +0 -4
- data/lib/action_controller/metal/request_forgery_protection.rb +1 -1
- data/lib/action_controller/metal/rescue.rb +9 -0
- data/lib/action_controller/metal/responder.rb +13 -5
- data/lib/action_controller/metal/streaming.rb +2 -0
- data/lib/action_controller/metal/url_for.rb +5 -5
- data/lib/action_controller/railtie.rb +14 -23
- data/lib/action_controller/record_identifier.rb +6 -25
- data/lib/action_controller/test_case.rb +18 -6
- data/lib/action_controller/vendor/html-scanner/html/node.rb +1 -0
- data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +1 -0
- data/lib/action_dispatch.rb +6 -0
- data/lib/action_dispatch/http/cache.rb +2 -2
- data/lib/action_dispatch/http/filter_parameters.rb +10 -66
- data/lib/action_dispatch/http/mime_type.rb +1 -1
- data/lib/action_dispatch/http/parameter_filter.rb +72 -0
- data/lib/action_dispatch/http/parameters.rb +31 -2
- data/lib/action_dispatch/http/request.rb +4 -1
- data/lib/action_dispatch/http/upload.rb +2 -2
- data/lib/action_dispatch/middleware/callbacks.rb +4 -4
- data/lib/action_dispatch/middleware/cookies.rb +39 -6
- data/lib/action_dispatch/middleware/flash.rb +9 -2
- data/lib/action_dispatch/middleware/session/abstract_store.rb +121 -36
- data/lib/action_dispatch/middleware/session/cookie_store.rb +26 -19
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +9 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +2 -2
- data/lib/action_dispatch/middleware/stack.rb +12 -5
- data/lib/action_dispatch/railtie.rb +1 -1
- data/lib/action_dispatch/routing.rb +11 -13
- data/lib/action_dispatch/routing/deprecated_mapper.rb +6 -388
- data/lib/action_dispatch/routing/mapper.rb +364 -234
- data/lib/action_dispatch/routing/polymorphic_routes.rb +186 -0
- data/lib/action_dispatch/routing/route.rb +11 -2
- data/lib/action_dispatch/routing/route_set.rb +62 -28
- data/lib/action_dispatch/routing/url_for.rb +2 -1
- data/lib/action_dispatch/testing/assertions.rb +0 -2
- data/lib/action_dispatch/testing/assertions/routing.rb +0 -1
- data/lib/action_dispatch/testing/assertions/selector.rb +20 -24
- data/lib/action_dispatch/testing/integration.rb +2 -2
- data/lib/action_dispatch/testing/test_response.rb +2 -2
- data/lib/action_pack/version.rb +1 -1
- data/lib/action_view.rb +1 -0
- data/lib/action_view/base.rb +20 -21
- data/lib/action_view/context.rb +9 -12
- data/lib/action_view/helpers.rb +0 -2
- data/lib/action_view/helpers/active_model_helper.rb +17 -2
- data/lib/action_view/helpers/asset_tag_helper.rb +15 -33
- data/lib/action_view/helpers/atom_feed_helper.rb +5 -3
- data/lib/action_view/helpers/cache_helper.rb +4 -2
- data/lib/action_view/helpers/capture_helper.rb +4 -4
- data/lib/action_view/helpers/csrf_helper.rb +3 -1
- data/lib/action_view/helpers/date_helper.rb +10 -5
- data/lib/action_view/helpers/debug_helper.rb +3 -1
- data/lib/action_view/helpers/form_helper.rb +36 -30
- data/lib/action_view/helpers/form_options_helper.rb +7 -6
- data/lib/action_view/helpers/form_tag_helper.rb +17 -6
- data/lib/action_view/helpers/javascript_helper.rb +1 -0
- data/lib/action_view/helpers/number_helper.rb +16 -45
- data/lib/action_view/helpers/prototype_helper.rb +14 -16
- data/lib/action_view/helpers/raw_output_helper.rb +9 -0
- data/lib/action_view/helpers/record_tag_helper.rb +5 -0
- data/lib/action_view/helpers/sanitize_helper.rb +26 -20
- data/lib/action_view/helpers/scriptaculous_helper.rb +6 -5
- data/lib/action_view/helpers/tag_helper.rb +2 -1
- data/lib/action_view/helpers/text_helper.rb +24 -111
- data/lib/action_view/helpers/translation_helper.rb +17 -10
- data/lib/action_view/helpers/url_helper.rb +26 -33
- data/lib/action_view/log_subscriber.rb +28 -0
- data/lib/action_view/lookup_context.rb +2 -0
- data/lib/action_view/paths.rb +1 -0
- data/lib/action_view/railtie.rb +15 -3
- data/lib/action_view/render/layouts.rb +2 -1
- data/lib/action_view/render/partials.rb +3 -1
- data/lib/action_view/render/rendering.rb +2 -1
- data/lib/action_view/template.rb +12 -8
- data/lib/action_view/template/error.rb +1 -0
- data/lib/action_view/template/handlers.rb +1 -0
- data/lib/action_view/template/resolver.rb +2 -1
- data/lib/action_view/template/text.rb +1 -0
- data/lib/action_view/test_case.rb +42 -20
- metadata +44 -23
- data/lib/action_controller/polymorphic_routes.rb +0 -182
- data/lib/action_controller/railties/log_subscriber.rb +0 -56
- data/lib/action_controller/railties/url_helpers.rb +0 -14
- data/lib/action_dispatch/testing/assertions/model.rb +0 -19
- data/lib/action_view/helpers/record_identification_helper.rb +0 -20
- data/lib/action_view/railties/log_subscriber.rb +0 -24
@@ -19,70 +19,22 @@ module ActionDispatch
|
|
19
19
|
|
20
20
|
def in_memory_controller_namespaces
|
21
21
|
namespaces = Set.new
|
22
|
-
ActionController::Base.
|
23
|
-
|
24
|
-
namespaces <<
|
22
|
+
ActionController::Base.descendants.each do |klass|
|
23
|
+
next if klass.anonymous?
|
24
|
+
namespaces << klass.name.underscore.split('/')[0...-1].join('/')
|
25
25
|
end
|
26
26
|
namespaces.delete('')
|
27
27
|
namespaces
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
-
# Mapper instances are used to build routes. The object passed to the draw
|
32
|
-
# block in config/routes.rb is a Mapper instance.
|
33
|
-
#
|
34
|
-
# Mapper instances have relatively few instance methods, in order to avoid
|
35
|
-
# clashes with named routes.
|
36
|
-
#
|
37
|
-
# == Overview
|
38
|
-
#
|
39
|
-
# ActionController::Resources are a way of defining RESTful \resources. A RESTful \resource, in basic terms,
|
40
|
-
# is something that can be pointed at and it will respond with a representation of the data requested.
|
41
|
-
# In real terms this could mean a user with a browser requests an HTML page, or that a desktop application
|
42
|
-
# requests XML data.
|
43
|
-
#
|
44
|
-
# RESTful design is based on the assumption that there are four generic verbs that a user of an
|
45
|
-
# application can request from a \resource (the noun).
|
46
|
-
#
|
47
|
-
# \Resources can be requested using four basic HTTP verbs (GET, POST, PUT, DELETE), the method used
|
48
|
-
# denotes the type of action that should take place.
|
49
|
-
#
|
50
|
-
# === The Different Methods and their Usage
|
51
|
-
#
|
52
|
-
# * GET - Requests for a \resource, no saving or editing of a \resource should occur in a GET request.
|
53
|
-
# * POST - Creation of \resources.
|
54
|
-
# * PUT - Editing of attributes on a \resource.
|
55
|
-
# * DELETE - Deletion of a \resource.
|
56
|
-
#
|
57
|
-
# === Examples
|
58
|
-
#
|
59
|
-
# # A GET request on the Posts resource is asking for all Posts
|
60
|
-
# GET /posts
|
61
|
-
#
|
62
|
-
# # A GET request on a single Post resource is asking for that particular Post
|
63
|
-
# GET /posts/1
|
64
|
-
#
|
65
|
-
# # A POST request on the Posts resource is asking for a Post to be created with the supplied details
|
66
|
-
# POST /posts # with => { :post => { :title => "My Whizzy New Post", :body => "I've got a brand new combine harvester" } }
|
67
|
-
#
|
68
|
-
# # A PUT request on a single Post resource is asking for a Post to be updated
|
69
|
-
# PUT /posts # with => { :id => 1, :post => { :title => "Changed Whizzy Title" } }
|
70
|
-
#
|
71
|
-
# # A DELETE request on a single Post resource is asking for it to be deleted
|
72
|
-
# DELETE /posts # with => { :id => 1 }
|
73
|
-
#
|
74
|
-
# By using the REST convention, users of our application can assume certain things about how the data
|
75
|
-
# is requested and how it is returned. Rails simplifies the routing part of RESTful design by
|
76
|
-
# supplying you with methods to create them in your routes.rb file.
|
77
|
-
#
|
78
|
-
# Read more about REST at http://en.wikipedia.org/wiki/Representational_State_Transfer
|
79
31
|
class DeprecatedMapper #:nodoc:
|
80
32
|
def initialize(set) #:nodoc:
|
33
|
+
ActiveSupport::Deprecation.warn "You are using the old router DSL which will be removed in Rails 3.1. " <<
|
34
|
+
"Please check how to update your routes file at: http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/"
|
81
35
|
@set = set
|
82
36
|
end
|
83
37
|
|
84
|
-
# Create an unnamed route with the provided +path+ and +options+. See
|
85
|
-
# ActionDispatch::Routing for an introduction to routes.
|
86
38
|
def connect(path, options = {})
|
87
39
|
options = options.dup
|
88
40
|
|
@@ -240,17 +192,6 @@ module ActionDispatch
|
|
240
192
|
connect(path, options)
|
241
193
|
end
|
242
194
|
|
243
|
-
# Enables the use of resources in a module by setting the name_prefix, path_prefix, and namespace for the model.
|
244
|
-
# Example:
|
245
|
-
#
|
246
|
-
# map.namespace(:admin) do |admin|
|
247
|
-
# admin.resources :products,
|
248
|
-
# :has_many => [ :tags, :images, :variants ]
|
249
|
-
# end
|
250
|
-
#
|
251
|
-
# This will create +admin_products_url+ pointing to "admin/products", which will look for an Admin::ProductsController.
|
252
|
-
# It'll also create +admin_product_tags_url+ pointing to "admin/products/#{product_id}/tags", which will look for
|
253
|
-
# Admin::TagsController.
|
254
195
|
def namespace(name, options = {}, &block)
|
255
196
|
if options[:namespace]
|
256
197
|
with_options({:path_prefix => "#{options.delete(:path_prefix)}/#{name}", :name_prefix => "#{options.delete(:name_prefix)}#{name}_", :namespace => "#{options.delete(:namespace)}#{name}/" }.merge(options), &block)
|
@@ -411,334 +352,11 @@ module ActionDispatch
|
|
411
352
|
alias_method :nesting_path_prefix, :path
|
412
353
|
end
|
413
354
|
|
414
|
-
# Creates named routes for implementing verb-oriented controllers
|
415
|
-
# for a collection \resource.
|
416
|
-
#
|
417
|
-
# For example:
|
418
|
-
#
|
419
|
-
# map.resources :messages
|
420
|
-
#
|
421
|
-
# will map the following actions in the corresponding controller:
|
422
|
-
#
|
423
|
-
# class MessagesController < ActionController::Base
|
424
|
-
# # GET messages_url
|
425
|
-
# def index
|
426
|
-
# # return all messages
|
427
|
-
# end
|
428
|
-
#
|
429
|
-
# # GET new_message_url
|
430
|
-
# def new
|
431
|
-
# # return an HTML form for describing a new message
|
432
|
-
# end
|
433
|
-
#
|
434
|
-
# # POST messages_url
|
435
|
-
# def create
|
436
|
-
# # create a new message
|
437
|
-
# end
|
438
|
-
#
|
439
|
-
# # GET message_url(:id => 1)
|
440
|
-
# def show
|
441
|
-
# # find and return a specific message
|
442
|
-
# end
|
443
|
-
#
|
444
|
-
# # GET edit_message_url(:id => 1)
|
445
|
-
# def edit
|
446
|
-
# # return an HTML form for editing a specific message
|
447
|
-
# end
|
448
|
-
#
|
449
|
-
# # PUT message_url(:id => 1)
|
450
|
-
# def update
|
451
|
-
# # find and update a specific message
|
452
|
-
# end
|
453
|
-
#
|
454
|
-
# # DELETE message_url(:id => 1)
|
455
|
-
# def destroy
|
456
|
-
# # delete a specific message
|
457
|
-
# end
|
458
|
-
# end
|
459
|
-
#
|
460
|
-
# Along with the routes themselves, +resources+ generates named routes for use in
|
461
|
-
# controllers and views. <tt>map.resources :messages</tt> produces the following named routes and helpers:
|
462
|
-
#
|
463
|
-
# Named Route Helpers
|
464
|
-
# ============ =====================================================
|
465
|
-
# messages messages_url, hash_for_messages_url,
|
466
|
-
# messages_path, hash_for_messages_path
|
467
|
-
#
|
468
|
-
# message message_url(id), hash_for_message_url(id),
|
469
|
-
# message_path(id), hash_for_message_path(id)
|
470
|
-
#
|
471
|
-
# new_message new_message_url, hash_for_new_message_url,
|
472
|
-
# new_message_path, hash_for_new_message_path
|
473
|
-
#
|
474
|
-
# edit_message edit_message_url(id), hash_for_edit_message_url(id),
|
475
|
-
# edit_message_path(id), hash_for_edit_message_path(id)
|
476
|
-
#
|
477
|
-
# You can use these helpers instead of +url_for+ or methods that take +url_for+ parameters. For example:
|
478
|
-
#
|
479
|
-
# redirect_to :controller => 'messages', :action => 'index'
|
480
|
-
# # and
|
481
|
-
# <%= link_to "edit this message", :controller => 'messages', :action => 'edit', :id => @message.id %>
|
482
|
-
#
|
483
|
-
# now become:
|
484
|
-
#
|
485
|
-
# redirect_to messages_url
|
486
|
-
# # and
|
487
|
-
# <%= link_to "edit this message", edit_message_url(@message) # calls @message.id automatically
|
488
|
-
#
|
489
|
-
# Since web browsers don't support the PUT and DELETE verbs, you will need to add a parameter '_method' to your
|
490
|
-
# form tags. The form helpers make this a little easier. For an update form with a <tt>@message</tt> object:
|
491
|
-
#
|
492
|
-
# <%= form_tag message_path(@message), :method => :put %>
|
493
|
-
#
|
494
|
-
# or
|
495
|
-
#
|
496
|
-
# <% form_for :message, @message, :url => message_path(@message), :html => {:method => :put} do |f| %>
|
497
|
-
#
|
498
|
-
# or
|
499
|
-
#
|
500
|
-
# <% form_for @message do |f| %>
|
501
|
-
#
|
502
|
-
# which takes into account whether <tt>@message</tt> is a new record or not and generates the
|
503
|
-
# path and method accordingly.
|
504
|
-
#
|
505
|
-
# The +resources+ method accepts the following options to customize the resulting routes:
|
506
|
-
# * <tt>:collection</tt> - Add named routes for other actions that operate on the collection.
|
507
|
-
# Takes a hash of <tt>#{action} => #{method}</tt>, where method is <tt>:get</tt>/<tt>:post</tt>/<tt>:put</tt>/<tt>:delete</tt>,
|
508
|
-
# an array of any of the previous, or <tt>:any</tt> if the method does not matter.
|
509
|
-
# These routes map to a URL like /messages/rss, with a route of +rss_messages_url+.
|
510
|
-
# * <tt>:member</tt> - Same as <tt>:collection</tt>, but for actions that operate on a specific member.
|
511
|
-
# * <tt>:new</tt> - Same as <tt>:collection</tt>, but for actions that operate on the new \resource action.
|
512
|
-
# * <tt>:controller</tt> - Specify the controller name for the routes.
|
513
|
-
# * <tt>:singular</tt> - Specify the singular name used in the member routes.
|
514
|
-
# * <tt>:requirements</tt> - Set custom routing parameter requirements; this is a hash of either
|
515
|
-
# regular expressions (which must match for the route to match) or extra parameters. For example:
|
516
|
-
#
|
517
|
-
# map.resource :profile, :path_prefix => ':name', :requirements => { :name => /[a-zA-Z]+/, :extra => 'value' }
|
518
|
-
#
|
519
|
-
# will only match if the first part is alphabetic, and will pass the parameter :extra to the controller.
|
520
|
-
# * <tt>:conditions</tt> - Specify custom routing recognition conditions. \Resources sets the <tt>:method</tt> value for the method-specific routes.
|
521
|
-
# * <tt>:as</tt> - Specify a different \resource name to use in the URL path. For example:
|
522
|
-
# # products_path == '/productos'
|
523
|
-
# map.resources :products, :as => 'productos' do |product|
|
524
|
-
# # product_reviews_path(product) == '/productos/1234/comentarios'
|
525
|
-
# product.resources :product_reviews, :as => 'comentarios'
|
526
|
-
# end
|
527
|
-
#
|
528
|
-
# * <tt>:has_one</tt> - Specify nested \resources, this is a shorthand for mapping singleton \resources beneath the current.
|
529
|
-
# * <tt>:has_many</tt> - Same has <tt>:has_one</tt>, but for plural \resources.
|
530
|
-
#
|
531
|
-
# You may directly specify the routing association with +has_one+ and +has_many+ like:
|
532
|
-
#
|
533
|
-
# map.resources :notes, :has_one => :author, :has_many => [:comments, :attachments]
|
534
|
-
#
|
535
|
-
# This is the same as:
|
536
|
-
#
|
537
|
-
# map.resources :notes do |notes|
|
538
|
-
# notes.resource :author
|
539
|
-
# notes.resources :comments
|
540
|
-
# notes.resources :attachments
|
541
|
-
# end
|
542
|
-
#
|
543
|
-
# * <tt>:path_names</tt> - Specify different path names for the actions. For example:
|
544
|
-
# # new_products_path == '/productos/nuevo'
|
545
|
-
# # bids_product_path(1) == '/productos/1/licitacoes'
|
546
|
-
# map.resources :products, :as => 'productos', :member => { :bids => :get }, :path_names => { :new => 'nuevo', :bids => 'licitacoes' }
|
547
|
-
#
|
548
|
-
# You can also set default action names from an environment, like this:
|
549
|
-
# config.action_controller.resources_path_names = { :new => 'nuevo', :edit => 'editar' }
|
550
|
-
#
|
551
|
-
# * <tt>:path_prefix</tt> - Set a prefix to the routes with required route variables.
|
552
|
-
#
|
553
|
-
# Weblog comments usually belong to a post, so you might use +resources+ like:
|
554
|
-
#
|
555
|
-
# map.resources :articles
|
556
|
-
# map.resources :comments, :path_prefix => '/articles/:article_id'
|
557
|
-
#
|
558
|
-
# You can nest +resources+ calls to set this automatically:
|
559
|
-
#
|
560
|
-
# map.resources :articles do |article|
|
561
|
-
# article.resources :comments
|
562
|
-
# end
|
563
|
-
#
|
564
|
-
# The comment \resources work the same, but must now include a value for <tt>:article_id</tt>.
|
565
|
-
#
|
566
|
-
# article_comments_url(@article)
|
567
|
-
# article_comment_url(@article, @comment)
|
568
|
-
#
|
569
|
-
# article_comments_url(:article_id => @article)
|
570
|
-
# article_comment_url(:article_id => @article, :id => @comment)
|
571
|
-
#
|
572
|
-
# If you don't want to load all objects from the database you might want to use the <tt>article_id</tt> directly:
|
573
|
-
#
|
574
|
-
# articles_comments_url(@comment.article_id, @comment)
|
575
|
-
#
|
576
|
-
# * <tt>:name_prefix</tt> - Define a prefix for all generated routes, usually ending in an underscore.
|
577
|
-
# Use this if you have named routes that may clash.
|
578
|
-
#
|
579
|
-
# map.resources :tags, :path_prefix => '/books/:book_id', :name_prefix => 'book_'
|
580
|
-
# map.resources :tags, :path_prefix => '/toys/:toy_id', :name_prefix => 'toy_'
|
581
|
-
#
|
582
|
-
# You may also use <tt>:name_prefix</tt> to override the generic named routes in a nested \resource:
|
583
|
-
#
|
584
|
-
# map.resources :articles do |article|
|
585
|
-
# article.resources :comments, :name_prefix => nil
|
586
|
-
# end
|
587
|
-
#
|
588
|
-
# This will yield named \resources like so:
|
589
|
-
#
|
590
|
-
# comments_url(@article)
|
591
|
-
# comment_url(@article, @comment)
|
592
|
-
#
|
593
|
-
# * <tt>:shallow</tt> - If true, paths for nested resources which reference a specific member
|
594
|
-
# (ie. those with an :id parameter) will not use the parent path prefix or name prefix.
|
595
|
-
#
|
596
|
-
# The <tt>:shallow</tt> option is inherited by any nested resource(s).
|
597
|
-
#
|
598
|
-
# For example, 'users', 'posts' and 'comments' all use shallow paths with the following nested resources:
|
599
|
-
#
|
600
|
-
# map.resources :users, :shallow => true do |user|
|
601
|
-
# user.resources :posts do |post|
|
602
|
-
# post.resources :comments
|
603
|
-
# end
|
604
|
-
# end
|
605
|
-
# # --> GET /users/1/posts (maps to the PostsController#index action as usual)
|
606
|
-
# # also adds the usual named route called "user_posts"
|
607
|
-
# # --> GET /posts/2 (maps to the PostsController#show action as if it were not nested)
|
608
|
-
# # also adds the named route called "post"
|
609
|
-
# # --> GET /posts/2/comments (maps to the CommentsController#index action)
|
610
|
-
# # also adds the named route called "post_comments"
|
611
|
-
# # --> GET /comments/2 (maps to the CommentsController#show action as if it were not nested)
|
612
|
-
# # also adds the named route called "comment"
|
613
|
-
#
|
614
|
-
# You may also use <tt>:shallow</tt> in combination with the +has_one+ and +has_many+ shorthand notations like:
|
615
|
-
#
|
616
|
-
# map.resources :users, :has_many => { :posts => :comments }, :shallow => true
|
617
|
-
#
|
618
|
-
# * <tt>:only</tt> and <tt>:except</tt> - Specify which of the seven default actions should be routed to.
|
619
|
-
#
|
620
|
-
# <tt>:only</tt> and <tt>:except</tt> may be set to <tt>:all</tt>, <tt>:none</tt>, an action name or a
|
621
|
-
# list of action names. By default, routes are generated for all seven actions.
|
622
|
-
#
|
623
|
-
# For example:
|
624
|
-
#
|
625
|
-
# map.resources :posts, :only => [:index, :show] do |post|
|
626
|
-
# post.resources :comments, :except => [:update, :destroy]
|
627
|
-
# end
|
628
|
-
# # --> GET /posts (maps to the PostsController#index action)
|
629
|
-
# # --> POST /posts (fails)
|
630
|
-
# # --> GET /posts/1 (maps to the PostsController#show action)
|
631
|
-
# # --> DELETE /posts/1 (fails)
|
632
|
-
# # --> POST /posts/1/comments (maps to the CommentsController#create action)
|
633
|
-
# # --> PUT /posts/1/comments/1 (fails)
|
634
|
-
#
|
635
|
-
# If <tt>map.resources</tt> is called with multiple resources, they all get the same options applied.
|
636
|
-
#
|
637
|
-
# Examples:
|
638
|
-
#
|
639
|
-
# map.resources :messages, :path_prefix => "/thread/:thread_id"
|
640
|
-
# # --> GET /thread/7/messages/1
|
641
|
-
#
|
642
|
-
# map.resources :messages, :collection => { :rss => :get }
|
643
|
-
# # --> GET /messages/rss (maps to the #rss action)
|
644
|
-
# # also adds a named route called "rss_messages"
|
645
|
-
#
|
646
|
-
# map.resources :messages, :member => { :mark => :post }
|
647
|
-
# # --> POST /messages/1/mark (maps to the #mark action)
|
648
|
-
# # also adds a named route called "mark_message"
|
649
|
-
#
|
650
|
-
# map.resources :messages, :new => { :preview => :post }
|
651
|
-
# # --> POST /messages/new/preview (maps to the #preview action)
|
652
|
-
# # also adds a named route called "preview_new_message"
|
653
|
-
#
|
654
|
-
# map.resources :messages, :new => { :new => :any, :preview => :post }
|
655
|
-
# # --> POST /messages/new/preview (maps to the #preview action)
|
656
|
-
# # also adds a named route called "preview_new_message"
|
657
|
-
# # --> /messages/new can be invoked via any request method
|
658
|
-
#
|
659
|
-
# map.resources :messages, :controller => "categories",
|
660
|
-
# :path_prefix => "/category/:category_id",
|
661
|
-
# :name_prefix => "category_"
|
662
|
-
# # --> GET /categories/7/messages/1
|
663
|
-
# # has named route "category_message"
|
664
|
-
#
|
665
|
-
# The +resources+ method sets HTTP method restrictions on the routes it generates. For example, making an
|
666
|
-
# HTTP POST on <tt>new_message_url</tt> will raise a RoutingError exception. The default route in
|
667
|
-
# <tt>config/routes.rb</tt> overrides this and allows invalid HTTP methods for \resource routes.
|
668
355
|
def resources(*entities, &block)
|
669
356
|
options = entities.extract_options!
|
670
357
|
entities.each { |entity| map_resource(entity, options.dup, &block) }
|
671
358
|
end
|
672
359
|
|
673
|
-
# Creates named routes for implementing verb-oriented controllers for a singleton \resource.
|
674
|
-
# A singleton \resource is global to its current context. For unnested singleton \resources,
|
675
|
-
# the \resource is global to the current user visiting the application, such as a user's
|
676
|
-
# <tt>/account</tt> profile. For nested singleton \resources, the \resource is global to its parent
|
677
|
-
# \resource, such as a <tt>projects</tt> \resource that <tt>has_one :project_manager</tt>.
|
678
|
-
# The <tt>project_manager</tt> should be mapped as a singleton \resource under <tt>projects</tt>:
|
679
|
-
#
|
680
|
-
# map.resources :projects do |project|
|
681
|
-
# project.resource :project_manager
|
682
|
-
# end
|
683
|
-
#
|
684
|
-
# See +resources+ for general conventions. These are the main differences:
|
685
|
-
# * A singular name is given to <tt>map.resource</tt>. The default controller name is still taken from the plural name.
|
686
|
-
# * To specify a custom plural name, use the <tt>:plural</tt> option. There is no <tt>:singular</tt> option.
|
687
|
-
# * No default index route is created for the singleton \resource controller.
|
688
|
-
# * When nesting singleton \resources, only the singular name is used as the path prefix (example: 'account/messages/1')
|
689
|
-
#
|
690
|
-
# For example:
|
691
|
-
#
|
692
|
-
# map.resource :account
|
693
|
-
#
|
694
|
-
# maps these actions in the Accounts controller:
|
695
|
-
#
|
696
|
-
# class AccountsController < ActionController::Base
|
697
|
-
# # GET new_account_url
|
698
|
-
# def new
|
699
|
-
# # return an HTML form for describing the new account
|
700
|
-
# end
|
701
|
-
#
|
702
|
-
# # POST account_url
|
703
|
-
# def create
|
704
|
-
# # create an account
|
705
|
-
# end
|
706
|
-
#
|
707
|
-
# # GET account_url
|
708
|
-
# def show
|
709
|
-
# # find and return the account
|
710
|
-
# end
|
711
|
-
#
|
712
|
-
# # GET edit_account_url
|
713
|
-
# def edit
|
714
|
-
# # return an HTML form for editing the account
|
715
|
-
# end
|
716
|
-
#
|
717
|
-
# # PUT account_url
|
718
|
-
# def update
|
719
|
-
# # find and update the account
|
720
|
-
# end
|
721
|
-
#
|
722
|
-
# # DELETE account_url
|
723
|
-
# def destroy
|
724
|
-
# # delete the account
|
725
|
-
# end
|
726
|
-
# end
|
727
|
-
#
|
728
|
-
# Along with the routes themselves, +resource+ generates named routes for
|
729
|
-
# use in controllers and views. <tt>map.resource :account</tt> produces
|
730
|
-
# these named routes and helpers:
|
731
|
-
#
|
732
|
-
# Named Route Helpers
|
733
|
-
# ============ =============================================
|
734
|
-
# account account_url, hash_for_account_url,
|
735
|
-
# account_path, hash_for_account_path
|
736
|
-
#
|
737
|
-
# new_account new_account_url, hash_for_new_account_url,
|
738
|
-
# new_account_path, hash_for_new_account_path
|
739
|
-
#
|
740
|
-
# edit_account edit_account_url, hash_for_edit_account_url,
|
741
|
-
# edit_account_path, hash_for_edit_account_path
|
742
360
|
def resource(*entities, &block)
|
743
361
|
options = entities.extract_options!
|
744
362
|
entities.each { |entity| map_singleton_resource(entity, options.dup, &block) }
|
@@ -882,7 +500,7 @@ module ActionDispatch
|
|
882
500
|
end
|
883
501
|
|
884
502
|
def add_conditions_for(conditions, method)
|
885
|
-
|
503
|
+
{:conditions => conditions.dup}.tap do |options|
|
886
504
|
options[:conditions][:method] = method unless method == :any
|
887
505
|
end
|
888
506
|
end
|
@@ -13,6 +13,8 @@ module ActionDispatch
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
+
attr_reader :app
|
17
|
+
|
16
18
|
def initialize(app, constraints, request)
|
17
19
|
@app, @constraints, @request = app, constraints, request
|
18
20
|
end
|
@@ -33,11 +35,12 @@ module ActionDispatch
|
|
33
35
|
end
|
34
36
|
|
35
37
|
class Mapping #:nodoc:
|
36
|
-
IGNORE_OPTIONS = [:to, :as, :
|
38
|
+
IGNORE_OPTIONS = [:to, :as, :via, :on, :constraints, :defaults, :only, :except, :anchor, :shallow, :shallow_path, :shallow_prefix]
|
37
39
|
|
38
40
|
def initialize(set, scope, args)
|
39
41
|
@set, @scope = set, scope
|
40
42
|
@path, @options = extract_path_and_options(args)
|
43
|
+
normalize_options!
|
41
44
|
end
|
42
45
|
|
43
46
|
def to_route
|
@@ -55,23 +58,29 @@ module ActionDispatch
|
|
55
58
|
path = args.first
|
56
59
|
end
|
57
60
|
|
58
|
-
if
|
59
|
-
if
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
61
|
+
if path.match(':controller')
|
62
|
+
raise ArgumentError, ":controller segment is not allowed within a namespace block" if @scope[:module]
|
63
|
+
|
64
|
+
# Add a default constraint for :controller path segments that matches namespaced
|
65
|
+
# controllers with default routes like :controller/:action/:id(.:format), e.g:
|
66
|
+
# GET /admin/products/show/1
|
67
|
+
# => { :controller => 'admin/products', :action => 'show', :id => '1' }
|
68
|
+
options.reverse_merge!(:controller => /.+?/)
|
64
69
|
end
|
65
70
|
|
66
|
-
|
67
|
-
|
71
|
+
[ normalize_path(path), options ]
|
72
|
+
end
|
73
|
+
|
74
|
+
def normalize_options!
|
75
|
+
path_without_format = @path.sub(/\(\.:format\)$/, '')
|
68
76
|
|
69
|
-
if using_match_shorthand?(path_without_format, options)
|
70
|
-
options[:to]
|
71
|
-
options[:
|
77
|
+
if using_match_shorthand?(path_without_format, @options)
|
78
|
+
to_shorthand = @options[:to].blank?
|
79
|
+
@options[:to] ||= path_without_format[1..-1].sub(%r{/([^/]*)$}, '#\1')
|
80
|
+
@options[:as] ||= path_without_format[1..-1].gsub("/", "_")
|
72
81
|
end
|
73
82
|
|
74
|
-
|
83
|
+
@options.merge!(default_controller_and_action(to_shorthand))
|
75
84
|
end
|
76
85
|
|
77
86
|
# match "account" => "account#index"
|
@@ -102,7 +111,7 @@ module ActionDispatch
|
|
102
111
|
end
|
103
112
|
|
104
113
|
def requirements
|
105
|
-
@requirements ||= (@options[:constraints]
|
114
|
+
@requirements ||= (@options[:constraints].is_a?(Hash) ? @options[:constraints] : {}).tap do |requirements|
|
106
115
|
requirements.reverse_merge!(@scope[:constraints]) if @scope[:constraints]
|
107
116
|
@options.each { |k, v| requirements[k] = v if v.is_a?(Regexp) }
|
108
117
|
end
|
@@ -110,40 +119,43 @@ module ActionDispatch
|
|
110
119
|
|
111
120
|
def defaults
|
112
121
|
@defaults ||= (@options[:defaults] || {}).tap do |defaults|
|
113
|
-
defaults.merge!(default_controller_and_action)
|
114
122
|
defaults.reverse_merge!(@scope[:defaults]) if @scope[:defaults]
|
115
123
|
@options.each { |k, v| defaults[k] = v unless v.is_a?(Regexp) || IGNORE_OPTIONS.include?(k.to_sym) }
|
116
124
|
end
|
117
125
|
end
|
118
126
|
|
119
|
-
def default_controller_and_action
|
127
|
+
def default_controller_and_action(to_shorthand=nil)
|
120
128
|
if to.respond_to?(:call)
|
121
129
|
{ }
|
122
130
|
else
|
123
|
-
|
124
|
-
when String
|
131
|
+
if to.is_a?(String)
|
125
132
|
controller, action = to.split('#')
|
126
|
-
|
127
|
-
|
128
|
-
{ :action => to.to_s }
|
129
|
-
else
|
130
|
-
{}
|
133
|
+
elsif to.is_a?(Symbol)
|
134
|
+
action = to.to_s
|
131
135
|
end
|
132
136
|
|
133
|
-
|
137
|
+
controller ||= default_controller
|
138
|
+
action ||= default_action
|
134
139
|
|
135
|
-
|
136
|
-
|
140
|
+
unless controller.is_a?(Regexp) || to_shorthand
|
141
|
+
controller = [@scope[:module], controller].compact.join("/").presence
|
142
|
+
end
|
143
|
+
|
144
|
+
controller = controller.to_s unless controller.is_a?(Regexp)
|
145
|
+
action = action.to_s unless action.is_a?(Regexp)
|
137
146
|
|
138
|
-
if
|
147
|
+
if controller.blank? && segment_keys.exclude?("controller")
|
139
148
|
raise ArgumentError, "missing :controller"
|
140
149
|
end
|
141
150
|
|
142
|
-
if
|
151
|
+
if action.blank? && segment_keys.exclude?("action")
|
143
152
|
raise ArgumentError, "missing :action"
|
144
153
|
end
|
145
154
|
|
146
|
-
|
155
|
+
{ :controller => controller, :action => action }.tap do |hash|
|
156
|
+
hash.delete(:controller) if hash[:controller].blank?
|
157
|
+
hash.delete(:action) if hash[:action].blank?
|
158
|
+
end
|
147
159
|
end
|
148
160
|
end
|
149
161
|
|
@@ -182,9 +194,17 @@ module ActionDispatch
|
|
182
194
|
|
183
195
|
def default_controller
|
184
196
|
if @options[:controller]
|
185
|
-
@options[:controller]
|
197
|
+
@options[:controller]
|
186
198
|
elsif @scope[:controller]
|
187
|
-
@scope[:controller]
|
199
|
+
@scope[:controller]
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def default_action
|
204
|
+
if @options[:action]
|
205
|
+
@options[:action]
|
206
|
+
elsif @scope[:action]
|
207
|
+
@scope[:action]
|
188
208
|
end
|
189
209
|
end
|
190
210
|
end
|
@@ -194,7 +214,7 @@ module ActionDispatch
|
|
194
214
|
# for root cases, where the latter is the correct one.
|
195
215
|
def self.normalize_path(path)
|
196
216
|
path = Rack::Mount::Utils.normalize_path(path)
|
197
|
-
path.
|
217
|
+
path.gsub!(%r{/(\(+)/?}, '\1/') unless path =~ %r{^/\(+[^/]+\)$}
|
198
218
|
path
|
199
219
|
end
|
200
220
|
|
@@ -299,6 +319,11 @@ module ActionDispatch
|
|
299
319
|
options = args.extract_options!
|
300
320
|
options = options.dup
|
301
321
|
|
322
|
+
if name_prefix = options.delete(:name_prefix)
|
323
|
+
options[:as] ||= name_prefix
|
324
|
+
ActiveSupport::Deprecation.warn ":name_prefix was deprecated in the new router syntax. Use :as instead.", caller
|
325
|
+
end
|
326
|
+
|
302
327
|
case args.first
|
303
328
|
when String
|
304
329
|
options[:path] = args.first
|
@@ -341,9 +366,11 @@ module ActionDispatch
|
|
341
366
|
scope(controller.to_sym) { yield }
|
342
367
|
end
|
343
368
|
|
344
|
-
def namespace(path)
|
369
|
+
def namespace(path, options = {})
|
345
370
|
path = path.to_s
|
346
|
-
|
371
|
+
options = { :path => path, :as => path, :module => path,
|
372
|
+
:shallow_path => path, :shallow_prefix => path }.merge!(options)
|
373
|
+
scope(options) { yield }
|
347
374
|
end
|
348
375
|
|
349
376
|
def constraints(constraints = {})
|
@@ -359,10 +386,10 @@ module ActionDispatch
|
|
359
386
|
|
360
387
|
options = (@scope[:options] || {}).merge(options)
|
361
388
|
|
362
|
-
if @scope[:
|
363
|
-
options[:as] = "#{@scope[:
|
364
|
-
elsif @scope[:
|
365
|
-
options[:as] = @scope[:
|
389
|
+
if @scope[:as] && !options[:as].blank?
|
390
|
+
options[:as] = "#{@scope[:as]}_#{options[:as]}"
|
391
|
+
elsif @scope[:as] && options[:as] == ""
|
392
|
+
options[:as] = @scope[:as].to_s
|
366
393
|
end
|
367
394
|
|
368
395
|
args.push(options)
|
@@ -378,7 +405,15 @@ module ActionDispatch
|
|
378
405
|
Mapper.normalize_path("#{parent}/#{child}")
|
379
406
|
end
|
380
407
|
|
381
|
-
def
|
408
|
+
def merge_shallow_path_scope(parent, child)
|
409
|
+
Mapper.normalize_path("#{parent}/#{child}")
|
410
|
+
end
|
411
|
+
|
412
|
+
def merge_as_scope(parent, child)
|
413
|
+
parent ? "#{parent}_#{child}" : child
|
414
|
+
end
|
415
|
+
|
416
|
+
def merge_shallow_prefix_scope(parent, child)
|
382
417
|
parent ? "#{parent}_#{child}" : child
|
383
418
|
end
|
384
419
|
|
@@ -387,7 +422,7 @@ module ActionDispatch
|
|
387
422
|
end
|
388
423
|
|
389
424
|
def merge_controller_scope(parent, child)
|
390
|
-
|
425
|
+
child
|
391
426
|
end
|
392
427
|
|
393
428
|
def merge_path_names_scope(parent, child)
|
@@ -403,56 +438,60 @@ module ActionDispatch
|
|
403
438
|
end
|
404
439
|
|
405
440
|
def merge_blocks_scope(parent, child)
|
406
|
-
|
441
|
+
merged = parent ? parent.dup : []
|
442
|
+
merged << child if child
|
443
|
+
merged
|
407
444
|
end
|
408
445
|
|
409
446
|
def merge_options_scope(parent, child)
|
410
|
-
(parent || {}).merge(child)
|
447
|
+
(parent || {}).except(*override_keys(child)).merge(child)
|
448
|
+
end
|
449
|
+
|
450
|
+
def merge_shallow_scope(parent, child)
|
451
|
+
child ? true : false
|
452
|
+
end
|
453
|
+
|
454
|
+
def override_keys(child)
|
455
|
+
child.key?(:only) || child.key?(:except) ? [:only, :except] : []
|
411
456
|
end
|
412
457
|
end
|
413
458
|
|
414
459
|
module Resources
|
415
|
-
|
460
|
+
# CANONICAL_ACTIONS holds all actions that does not need a prefix or
|
461
|
+
# a path appended since they fit properly in their scope level.
|
462
|
+
VALID_ON_OPTIONS = [:new, :collection, :member]
|
463
|
+
CANONICAL_ACTIONS = [:index, :create, :new, :show, :update, :destroy]
|
464
|
+
RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except]
|
416
465
|
|
417
466
|
class Resource #:nodoc:
|
418
|
-
|
419
|
-
[:index, :create, :new, :show, :update, :destroy, :edit]
|
420
|
-
end
|
467
|
+
DEFAULT_ACTIONS = [:index, :create, :new, :show, :update, :destroy, :edit]
|
421
468
|
|
422
469
|
attr_reader :controller, :path, :options
|
423
470
|
|
424
471
|
def initialize(entities, options = {})
|
425
472
|
@name = entities.to_s
|
426
473
|
@path = options.delete(:path) || @name
|
427
|
-
@controller = options.delete(:controller) || @name.to_s
|
474
|
+
@controller = (options.delete(:controller) || @name).to_s
|
475
|
+
@as = options.delete(:as)
|
428
476
|
@options = options
|
429
477
|
end
|
430
478
|
|
431
479
|
def default_actions
|
432
|
-
self.class
|
480
|
+
self.class::DEFAULT_ACTIONS
|
433
481
|
end
|
434
482
|
|
435
483
|
def actions
|
436
|
-
if only = options[:only]
|
484
|
+
if only = @options[:only]
|
437
485
|
Array(only).map(&:to_sym)
|
438
|
-
elsif except = options[:except]
|
486
|
+
elsif except = @options[:except]
|
439
487
|
default_actions - Array(except).map(&:to_sym)
|
440
488
|
else
|
441
489
|
default_actions
|
442
490
|
end
|
443
491
|
end
|
444
492
|
|
445
|
-
def action_type(action)
|
446
|
-
case action
|
447
|
-
when :index, :create
|
448
|
-
:collection
|
449
|
-
when :show, :update, :destroy
|
450
|
-
:member
|
451
|
-
end
|
452
|
-
end
|
453
|
-
|
454
493
|
def name
|
455
|
-
|
494
|
+
@as || @name
|
456
495
|
end
|
457
496
|
|
458
497
|
def plural
|
@@ -463,98 +502,57 @@ module ActionDispatch
|
|
463
502
|
name.to_s.singularize
|
464
503
|
end
|
465
504
|
|
466
|
-
def member_prefix
|
467
|
-
':id'
|
468
|
-
end
|
469
|
-
|
470
505
|
def member_name
|
471
506
|
singular
|
472
507
|
end
|
473
508
|
|
474
509
|
# Checks for uncountable plurals, and appends "_index" if they're.
|
475
510
|
def collection_name
|
476
|
-
|
511
|
+
singular == plural ? "#{plural}_index" : plural
|
477
512
|
end
|
478
513
|
|
479
|
-
def
|
480
|
-
|
514
|
+
def resource_scope
|
515
|
+
{ :controller => controller }
|
481
516
|
end
|
482
517
|
|
483
|
-
def
|
484
|
-
|
485
|
-
when :collection
|
486
|
-
collection_name
|
487
|
-
when :member
|
488
|
-
member_name
|
489
|
-
end
|
518
|
+
def collection_scope
|
519
|
+
path
|
490
520
|
end
|
491
521
|
|
492
|
-
def
|
493
|
-
"
|
522
|
+
def member_scope
|
523
|
+
"#{path}/:id"
|
494
524
|
end
|
495
525
|
|
496
|
-
def
|
497
|
-
|
526
|
+
def new_scope(new_path)
|
527
|
+
"#{path}/#{new_path}"
|
498
528
|
end
|
499
529
|
|
500
|
-
def
|
501
|
-
|
530
|
+
def nested_scope
|
531
|
+
"#{path}/:#{singular}_id"
|
502
532
|
end
|
503
533
|
|
504
|
-
def id_constraint
|
505
|
-
options[:id] || constraints[:id]
|
506
|
-
end
|
507
|
-
|
508
|
-
def collection_options
|
509
|
-
(options || {}).dup.tap do |options|
|
510
|
-
options.delete(:id)
|
511
|
-
options[:constraints] = options[:constraints].dup if options[:constraints]
|
512
|
-
options[:constraints].delete(:id) if options[:constraints].is_a?(Hash)
|
513
|
-
end
|
514
|
-
end
|
515
|
-
|
516
|
-
def nested_prefix
|
517
|
-
id_segment
|
518
|
-
end
|
519
|
-
|
520
|
-
def nested_options
|
521
|
-
options = { :name_prefix => member_name }
|
522
|
-
options["#{singular}_id".to_sym] = id_constraint if id_constraint?
|
523
|
-
options
|
524
|
-
end
|
525
534
|
end
|
526
535
|
|
527
536
|
class SingletonResource < Resource #:nodoc:
|
528
|
-
|
529
|
-
[:show, :create, :update, :destroy, :new, :edit]
|
530
|
-
end
|
537
|
+
DEFAULT_ACTIONS = [:show, :create, :update, :destroy, :new, :edit]
|
531
538
|
|
532
|
-
def initialize(
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
when :show, :create, :update, :destroy
|
539
|
-
:member
|
540
|
-
end
|
541
|
-
end
|
542
|
-
|
543
|
-
def member_prefix
|
544
|
-
''
|
539
|
+
def initialize(entities, options)
|
540
|
+
@name = entities.to_s
|
541
|
+
@path = options.delete(:path) || @name
|
542
|
+
@controller = (options.delete(:controller) || plural).to_s
|
543
|
+
@as = options.delete(:as)
|
544
|
+
@options = options
|
545
545
|
end
|
546
546
|
|
547
547
|
def member_name
|
548
548
|
name
|
549
549
|
end
|
550
|
+
alias :collection_name :member_name
|
550
551
|
|
551
|
-
def
|
552
|
-
|
553
|
-
end
|
554
|
-
|
555
|
-
def nested_options
|
556
|
-
{ :name_prefix => member_name }
|
552
|
+
def member_scope
|
553
|
+
path
|
557
554
|
end
|
555
|
+
alias :nested_scope :member_scope
|
558
556
|
end
|
559
557
|
|
560
558
|
def initialize(*args) #:nodoc:
|
@@ -562,31 +560,33 @@ module ActionDispatch
|
|
562
560
|
@scope[:path_names] = @set.resources_path_names
|
563
561
|
end
|
564
562
|
|
563
|
+
def resources_path_names(options)
|
564
|
+
@scope[:path_names].merge!(options)
|
565
|
+
end
|
566
|
+
|
565
567
|
def resource(*resources, &block)
|
566
568
|
options = resources.extract_options!
|
567
|
-
options = (@scope[:options] || {}).merge(options)
|
568
569
|
|
569
570
|
if apply_common_behavior_for(:resource, resources, options, &block)
|
570
571
|
return self
|
571
572
|
end
|
572
573
|
|
573
|
-
|
574
|
+
resource_scope(SingletonResource.new(resources.pop, options)) do
|
575
|
+
yield if block_given?
|
574
576
|
|
575
|
-
|
576
|
-
|
577
|
+
collection_scope do
|
578
|
+
post :create
|
579
|
+
end if parent_resource.actions.include?(:create)
|
577
580
|
|
578
|
-
|
581
|
+
new_scope do
|
582
|
+
get :new
|
583
|
+
end if parent_resource.actions.include?(:new)
|
579
584
|
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
delete :destroy if resource.actions.include?(:destroy)
|
586
|
-
get :new, :as => resource.name if resource.actions.include?(:new)
|
587
|
-
get :edit, :as => resource.name if resource.actions.include?(:edit)
|
588
|
-
end
|
589
|
-
end
|
585
|
+
member_scope do
|
586
|
+
get :show if parent_resource.actions.include?(:show)
|
587
|
+
put :update if parent_resource.actions.include?(:update)
|
588
|
+
delete :destroy if parent_resource.actions.include?(:destroy)
|
589
|
+
get :edit if parent_resource.actions.include?(:edit)
|
590
590
|
end
|
591
591
|
end
|
592
592
|
|
@@ -595,36 +595,28 @@ module ActionDispatch
|
|
595
595
|
|
596
596
|
def resources(*resources, &block)
|
597
597
|
options = resources.extract_options!
|
598
|
-
options = (@scope[:options] || {}).merge(options)
|
599
598
|
|
600
599
|
if apply_common_behavior_for(:resources, resources, options, &block)
|
601
600
|
return self
|
602
601
|
end
|
603
602
|
|
604
|
-
|
603
|
+
resource_scope(Resource.new(resources.pop, options)) do
|
604
|
+
yield if block_given?
|
605
605
|
|
606
|
-
|
607
|
-
|
608
|
-
|
606
|
+
collection_scope do
|
607
|
+
get :index if parent_resource.actions.include?(:index)
|
608
|
+
post :create if parent_resource.actions.include?(:create)
|
609
|
+
end
|
609
610
|
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
post :create if resource.actions.include?(:create)
|
614
|
-
get :new, :as => resource.singular if resource.actions.include?(:new)
|
615
|
-
end
|
616
|
-
end
|
611
|
+
new_scope do
|
612
|
+
get :new
|
613
|
+
end if parent_resource.actions.include?(:new)
|
617
614
|
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
delete :destroy if resource.actions.include?(:destroy)
|
624
|
-
get :edit, :as => resource.singular if resource.actions.include?(:edit)
|
625
|
-
end
|
626
|
-
end
|
627
|
-
end
|
615
|
+
member_scope do
|
616
|
+
get :show if parent_resource.actions.include?(:show)
|
617
|
+
put :update if parent_resource.actions.include?(:update)
|
618
|
+
delete :destroy if parent_resource.actions.include?(:destroy)
|
619
|
+
get :edit if parent_resource.actions.include?(:edit)
|
628
620
|
end
|
629
621
|
end
|
630
622
|
|
@@ -636,10 +628,8 @@ module ActionDispatch
|
|
636
628
|
raise ArgumentError, "can't use collection outside resources scope"
|
637
629
|
end
|
638
630
|
|
639
|
-
|
640
|
-
|
641
|
-
yield
|
642
|
-
end
|
631
|
+
collection_scope do
|
632
|
+
yield
|
643
633
|
end
|
644
634
|
end
|
645
635
|
|
@@ -648,10 +638,8 @@ module ActionDispatch
|
|
648
638
|
raise ArgumentError, "can't use member outside resource(s) scope"
|
649
639
|
end
|
650
640
|
|
651
|
-
|
652
|
-
|
653
|
-
yield
|
654
|
-
end
|
641
|
+
member_scope do
|
642
|
+
yield
|
655
643
|
end
|
656
644
|
end
|
657
645
|
|
@@ -659,11 +647,9 @@ module ActionDispatch
|
|
659
647
|
unless resource_scope?
|
660
648
|
raise ArgumentError, "can't use new outside resource(s) scope"
|
661
649
|
end
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
yield
|
666
|
-
end
|
650
|
+
|
651
|
+
new_scope do
|
652
|
+
yield
|
667
653
|
end
|
668
654
|
end
|
669
655
|
|
@@ -673,13 +659,23 @@ module ActionDispatch
|
|
673
659
|
end
|
674
660
|
|
675
661
|
with_scope_level(:nested) do
|
676
|
-
|
677
|
-
|
662
|
+
if shallow?
|
663
|
+
with_exclusive_scope do
|
664
|
+
if @scope[:shallow_path].blank?
|
665
|
+
scope(parent_resource.nested_scope, nested_options) { yield }
|
666
|
+
else
|
667
|
+
scope(@scope[:shallow_path], :as => @scope[:shallow_prefix]) do
|
668
|
+
scope(parent_resource.nested_scope, nested_options) { yield }
|
669
|
+
end
|
670
|
+
end
|
671
|
+
end
|
672
|
+
else
|
673
|
+
scope(parent_resource.nested_scope, nested_options) { yield }
|
678
674
|
end
|
679
675
|
end
|
680
676
|
end
|
681
677
|
|
682
|
-
def namespace(path)
|
678
|
+
def namespace(path, options = {})
|
683
679
|
if resource_scope?
|
684
680
|
nested { super }
|
685
681
|
else
|
@@ -687,93 +683,105 @@ module ActionDispatch
|
|
687
683
|
end
|
688
684
|
end
|
689
685
|
|
690
|
-
def
|
691
|
-
|
686
|
+
def shallow
|
687
|
+
scope(:shallow => true) do
|
688
|
+
yield
|
689
|
+
end
|
690
|
+
end
|
692
691
|
|
692
|
+
def shallow?
|
693
|
+
parent_resource.instance_of?(Resource) && @scope[:shallow]
|
694
|
+
end
|
695
|
+
|
696
|
+
def match(*args)
|
697
|
+
options = args.extract_options!.dup
|
693
698
|
options[:anchor] = true unless options.key?(:anchor)
|
694
699
|
|
695
700
|
if args.length > 1
|
696
|
-
args.each { |path| match(path, options) }
|
701
|
+
args.each { |path| match(path, options.dup) }
|
697
702
|
return self
|
698
703
|
end
|
699
704
|
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
old_path = @scope[:path]
|
707
|
-
@scope[:path] = "#{@scope[:path]}(.:format)"
|
708
|
-
return match(options.reverse_merge(
|
709
|
-
:to => action,
|
710
|
-
:as => parent_resource.name_for_action(action)
|
711
|
-
))
|
712
|
-
ensure
|
713
|
-
@scope[:path] = old_path
|
714
|
-
end
|
715
|
-
else
|
716
|
-
with_exclusive_name_prefix(action_name_prefix(action, options)) do
|
717
|
-
return match("#{action_path(action, path_names)}(.:format)", options.reverse_merge(:to => action))
|
718
|
-
end
|
719
|
-
end
|
705
|
+
on = options.delete(:on)
|
706
|
+
if VALID_ON_OPTIONS.include?(on)
|
707
|
+
args.push(options)
|
708
|
+
return send(on){ match(*args) }
|
709
|
+
elsif on
|
710
|
+
raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
|
720
711
|
end
|
721
712
|
|
722
|
-
|
723
|
-
|
724
|
-
case options.delete(:on)
|
725
|
-
when :collection
|
726
|
-
return collection { match(*args) }
|
727
|
-
when :member
|
713
|
+
if @scope[:scope_level] == :resource
|
714
|
+
args.push(options)
|
728
715
|
return member { match(*args) }
|
729
|
-
when :new
|
730
|
-
return new { match(*args) }
|
731
716
|
end
|
732
717
|
|
733
|
-
|
734
|
-
|
718
|
+
path = options.delete(:path)
|
719
|
+
action = args.first
|
720
|
+
|
721
|
+
if action.is_a?(Symbol)
|
722
|
+
path = path_for_action(action, path)
|
723
|
+
options[:to] ||= action
|
724
|
+
options[:as] = name_for_action(action, options[:as])
|
725
|
+
|
726
|
+
with_exclusive_scope do
|
727
|
+
return super(path, options)
|
728
|
+
end
|
729
|
+
elsif resource_method_scope?
|
730
|
+
path = path_for_custom_action
|
731
|
+
options[:as] = name_for_action(options[:as]) if options[:as]
|
732
|
+
args.push(options)
|
733
|
+
|
734
|
+
with_exclusive_scope do
|
735
|
+
scope(path) do
|
736
|
+
return super
|
737
|
+
end
|
738
|
+
end
|
735
739
|
end
|
736
740
|
|
737
741
|
if resource_scope?
|
738
742
|
raise ArgumentError, "can't define route directly in resource(s) scope"
|
739
743
|
end
|
740
744
|
|
745
|
+
args.push(options)
|
741
746
|
super
|
742
747
|
end
|
743
748
|
|
744
749
|
def root(options={})
|
745
|
-
|
746
|
-
|
750
|
+
if @scope[:scope_level] == :resources
|
751
|
+
with_scope_level(:nested) do
|
752
|
+
scope(parent_resource.path, :as => parent_resource.collection_name) do
|
753
|
+
super(options)
|
754
|
+
end
|
755
|
+
end
|
756
|
+
else
|
757
|
+
super(options)
|
758
|
+
end
|
747
759
|
end
|
748
760
|
|
749
761
|
protected
|
762
|
+
|
750
763
|
def parent_resource #:nodoc:
|
751
764
|
@scope[:scope_level_resource]
|
752
765
|
end
|
753
766
|
|
754
|
-
private
|
755
|
-
def action_path(name, path_names = nil)
|
756
|
-
path_names ||= @scope[:path_names]
|
757
|
-
path_names[name.to_sym] || name.to_s
|
758
|
-
end
|
759
|
-
|
760
|
-
def action_name_prefix(action, options = {})
|
761
|
-
(options[:on] == :new || @scope[:scope_level] == :new) ? "#{action}_new" : action
|
762
|
-
end
|
763
|
-
|
764
767
|
def apply_common_behavior_for(method, resources, options, &block)
|
765
768
|
if resources.length > 1
|
766
769
|
resources.each { |r| send(method, r, options, &block) }
|
767
770
|
return true
|
768
771
|
end
|
769
772
|
|
770
|
-
|
771
|
-
|
773
|
+
scope_options = options.slice!(*RESOURCE_OPTIONS)
|
774
|
+
unless scope_options.empty?
|
775
|
+
scope(scope_options) do
|
772
776
|
send(method, resources.pop, options, &block)
|
773
777
|
end
|
774
778
|
return true
|
775
779
|
end
|
776
780
|
|
781
|
+
unless action_options?(options)
|
782
|
+
options.merge!(scope_action_options) if scope_action_options?
|
783
|
+
end
|
784
|
+
|
777
785
|
if resource_scope?
|
778
786
|
nested do
|
779
787
|
send(method, resources.pop, options, &block)
|
@@ -784,27 +792,36 @@ module ActionDispatch
|
|
784
792
|
false
|
785
793
|
end
|
786
794
|
|
787
|
-
def
|
788
|
-
|
795
|
+
def action_options?(options)
|
796
|
+
options[:only] || options[:except]
|
797
|
+
end
|
798
|
+
|
799
|
+
def scope_action_options?
|
800
|
+
@scope[:options].is_a?(Hash) && (@scope[:options][:only] || @scope[:options][:except])
|
801
|
+
end
|
802
|
+
|
803
|
+
def scope_action_options
|
804
|
+
@scope[:options].slice(:only, :except)
|
789
805
|
end
|
790
806
|
|
791
807
|
def resource_scope?
|
792
808
|
[:resource, :resources].include?(@scope[:scope_level])
|
793
809
|
end
|
794
810
|
|
795
|
-
def
|
811
|
+
def resource_method_scope?
|
812
|
+
[:collection, :member, :new].include?(@scope[:scope_level])
|
813
|
+
end
|
814
|
+
|
815
|
+
def with_exclusive_scope
|
796
816
|
begin
|
797
|
-
old_name_prefix = @scope[:
|
817
|
+
old_name_prefix, old_path = @scope[:as], @scope[:path]
|
818
|
+
@scope[:as], @scope[:path] = nil, nil
|
798
819
|
|
799
|
-
|
800
|
-
|
801
|
-
else
|
802
|
-
@scope[:name_prefix] = prefix.to_s
|
820
|
+
with_scope_level(:exclusive) do
|
821
|
+
yield
|
803
822
|
end
|
804
|
-
|
805
|
-
yield
|
806
823
|
ensure
|
807
|
-
@scope[:
|
824
|
+
@scope[:as], @scope[:path] = old_name_prefix, old_path
|
808
825
|
end
|
809
826
|
end
|
810
827
|
|
@@ -816,6 +833,119 @@ module ActionDispatch
|
|
816
833
|
@scope[:scope_level] = old
|
817
834
|
@scope[:scope_level_resource] = old_resource
|
818
835
|
end
|
836
|
+
|
837
|
+
def resource_scope(resource)
|
838
|
+
with_scope_level(resource.is_a?(SingletonResource) ? :resource : :resources, resource) do
|
839
|
+
scope(parent_resource.resource_scope) do
|
840
|
+
yield
|
841
|
+
end
|
842
|
+
end
|
843
|
+
end
|
844
|
+
|
845
|
+
def new_scope
|
846
|
+
with_scope_level(:new) do
|
847
|
+
scope(parent_resource.new_scope(action_path(:new))) do
|
848
|
+
yield
|
849
|
+
end
|
850
|
+
end
|
851
|
+
end
|
852
|
+
|
853
|
+
def collection_scope
|
854
|
+
with_scope_level(:collection) do
|
855
|
+
scope(parent_resource.collection_scope) do
|
856
|
+
yield
|
857
|
+
end
|
858
|
+
end
|
859
|
+
end
|
860
|
+
|
861
|
+
def member_scope
|
862
|
+
with_scope_level(:member) do
|
863
|
+
scope(parent_resource.member_scope) do
|
864
|
+
yield
|
865
|
+
end
|
866
|
+
end
|
867
|
+
end
|
868
|
+
|
869
|
+
def nested_options
|
870
|
+
{}.tap do |options|
|
871
|
+
options[:as] = parent_resource.member_name
|
872
|
+
options[:constraints] = { "#{parent_resource.singular}_id".to_sym => id_constraint } if id_constraint?
|
873
|
+
end
|
874
|
+
end
|
875
|
+
|
876
|
+
def id_constraint?
|
877
|
+
@scope[:id].is_a?(Regexp) || (@scope[:constraints] && @scope[:constraints][:id].is_a?(Regexp))
|
878
|
+
end
|
879
|
+
|
880
|
+
def id_constraint
|
881
|
+
@scope[:id] || @scope[:constraints][:id]
|
882
|
+
end
|
883
|
+
|
884
|
+
def canonical_action?(action, flag)
|
885
|
+
flag && CANONICAL_ACTIONS.include?(action)
|
886
|
+
end
|
887
|
+
|
888
|
+
def shallow_scoping?
|
889
|
+
shallow? && @scope[:scope_level] == :member
|
890
|
+
end
|
891
|
+
|
892
|
+
def path_for_action(action, path)
|
893
|
+
prefix = shallow_scoping? ?
|
894
|
+
"#{@scope[:shallow_path]}/#{parent_resource.path}/:id" : @scope[:path]
|
895
|
+
|
896
|
+
if canonical_action?(action, path.blank?)
|
897
|
+
"#{prefix}(.:format)"
|
898
|
+
else
|
899
|
+
"#{prefix}/#{action_path(action, path)}(.:format)"
|
900
|
+
end
|
901
|
+
end
|
902
|
+
|
903
|
+
def path_for_custom_action
|
904
|
+
if shallow_scoping?
|
905
|
+
"#{@scope[:shallow_path]}/#{parent_resource.path}/:id"
|
906
|
+
else
|
907
|
+
@scope[:path]
|
908
|
+
end
|
909
|
+
end
|
910
|
+
|
911
|
+
def action_path(name, path = nil)
|
912
|
+
path || @scope[:path_names][name.to_sym] || name.to_s
|
913
|
+
end
|
914
|
+
|
915
|
+
def prefix_name_for_action(action, as)
|
916
|
+
if as.present?
|
917
|
+
"#{as}_"
|
918
|
+
elsif as
|
919
|
+
""
|
920
|
+
elsif !canonical_action?(action, @scope[:scope_level])
|
921
|
+
"#{action}_"
|
922
|
+
end
|
923
|
+
end
|
924
|
+
|
925
|
+
def name_for_action(action, as=nil)
|
926
|
+
prefix = prefix_name_for_action(action, as)
|
927
|
+
name_prefix = @scope[:as]
|
928
|
+
|
929
|
+
if parent_resource
|
930
|
+
collection_name = parent_resource.collection_name
|
931
|
+
member_name = parent_resource.member_name
|
932
|
+
name_prefix = "#{name_prefix}_" if name_prefix.present?
|
933
|
+
end
|
934
|
+
|
935
|
+
case @scope[:scope_level]
|
936
|
+
when :collection
|
937
|
+
"#{prefix}#{name_prefix}#{collection_name}"
|
938
|
+
when :new
|
939
|
+
"#{prefix}new_#{name_prefix}#{member_name}"
|
940
|
+
else
|
941
|
+
if shallow_scoping?
|
942
|
+
shallow_prefix = "#{@scope[:shallow_prefix]}_" if @scope[:shallow_prefix].present?
|
943
|
+
"#{prefix}#{shallow_prefix}#{member_name}"
|
944
|
+
else
|
945
|
+
"#{prefix}#{name_prefix}#{member_name}"
|
946
|
+
end
|
947
|
+
end
|
948
|
+
end
|
819
949
|
end
|
820
950
|
|
821
951
|
include Base
|