aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack')
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb61
-rw-r--r--actionpack/test/dispatch/routing_test.rb26
2 files changed, 73 insertions, 14 deletions
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 26f0e14e72..2546bdd43e 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -259,6 +259,7 @@ module ActionDispatch
def scope(*args)
options = args.extract_options!
+ options = options.dup
case args.first
when String
@@ -440,6 +441,32 @@ module ActionDispatch
def id_segment
":#{singular}_id"
end
+
+ def constraints
+ options[:constraints] || {}
+ end
+
+ def id_constraint?
+ options[:id] && options[:id].is_a?(Regexp) || constraints[:id] && constraints[:id].is_a?(Regexp)
+ end
+
+ def id_constraint
+ options[:id] || constraints[:id]
+ end
+
+ def collection_options
+ (options || {}).dup.tap do |options|
+ options.delete(:id)
+ options[:constraints] = options[:constraints].dup if options[:constraints]
+ options[:constraints].delete(:id) if options[:constraints].is_a?(Hash)
+ end
+ end
+
+ def nested_options
+ options = { :name_prefix => member_name }
+ options["#{singular}_id".to_sym] = id_constraint if id_constraint?
+ options
+ end
end
class SingletonResource < Resource #:nodoc:
@@ -484,12 +511,14 @@ module ActionDispatch
yield if block_given?
end
- get :show if resource.actions.include?(:show)
- post :create if resource.actions.include?(:create)
- put :update if resource.actions.include?(:update)
- delete :destroy if resource.actions.include?(:destroy)
- get :new, :as => resource.name if resource.actions.include?(:new)
- get :edit, :as => resource.name if resource.actions.include?(:edit)
+ scope(resource.options) do
+ get :show if resource.actions.include?(:show)
+ post :create if resource.actions.include?(:create)
+ put :update if resource.actions.include?(:update)
+ delete :destroy if resource.actions.include?(:destroy)
+ get :new, :as => resource.name if resource.actions.include?(:new)
+ get :edit, :as => resource.name if resource.actions.include?(:edit)
+ end
end
end
@@ -510,17 +539,21 @@ module ActionDispatch
yield if block_given?
with_scope_level(:collection) do
- get :index if resource.actions.include?(:index)
- post :create if resource.actions.include?(:create)
- get :new, :as => resource.singular if resource.actions.include?(:new)
+ scope(resource.collection_options) do
+ get :index if resource.actions.include?(:index)
+ post :create if resource.actions.include?(:create)
+ get :new, :as => resource.singular if resource.actions.include?(:new)
+ end
end
with_scope_level(:member) do
scope(':id') do
- get :show if resource.actions.include?(:show)
- put :update if resource.actions.include?(:update)
- delete :destroy if resource.actions.include?(:destroy)
- get :edit, :as => resource.singular if resource.actions.include?(:edit)
+ scope(resource.options) do
+ get :show if resource.actions.include?(:show)
+ put :update if resource.actions.include?(:update)
+ delete :destroy if resource.actions.include?(:destroy)
+ get :edit, :as => resource.singular if resource.actions.include?(:edit)
+ end
end
end
end
@@ -559,7 +592,7 @@ module ActionDispatch
end
with_scope_level(:nested) do
- scope(parent_resource.id_segment, :name_prefix => parent_resource.member_name) do
+ scope(parent_resource.id_segment, parent_resource.nested_options) do
yield
end
end
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index c4e71a8689..b1de96cee5 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -171,6 +171,12 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
resources :descriptions
root :to => 'projects#index'
end
+
+ resources :products, :constraints => { :id => /\d{4}/ } do
+ resources :images
+ end
+
+ resource :dashboard, :constraints => { :ip => /192\.168\.1\.\d{1,3}/ }
end
end
@@ -794,6 +800,26 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
+ def test_resource_constraints
+ with_test_routes do
+ assert_raise(ActionController::RoutingError) { get '/products/1' }
+ get '/products'
+ assert_equal 'products#index', @response.body
+ get '/products/0001'
+ assert_equal 'products#show', @response.body
+
+ assert_raise(ActionController::RoutingError) { get '/products/1/images' }
+ get '/products/0001/images'
+ assert_equal 'images#index', @response.body
+ get '/products/0001/images/1'
+ assert_equal 'images#show', @response.body
+
+ assert_raise(ActionController::RoutingError) { get '/dashboard', {}, {'REMOTE_ADDR' => '10.0.0.100'} }
+ get '/dashboard', {}, {'REMOTE_ADDR' => '192.168.1.100'}
+ assert_equal 'dashboards#show', @response.body
+ end
+ end
+
private
def with_test_routes
yield