diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 388f695187068..62fb7977cc5c9 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -433,6 +433,8 @@ def merge_shallow_scope(parent, child) end module Resources + MERGE_FROM_SCOPE_OPTIONS = [:shallow, :constraints] + class Resource #:nodoc: def self.default_actions [:index, :create, :new, :show, :update, :destroy, :edit] @@ -591,8 +593,6 @@ def resources_path_names(options) def resource(*resources, &block) options = resources.extract_options! - options = (@scope[:options] || {}).merge(options) - options[:shallow] = true if @scope[:shallow] && !options.has_key?(:shallow) if apply_common_behavior_for(:resource, resources, options, &block) return self @@ -619,8 +619,6 @@ def resource(*resources, &block) def resources(*resources, &block) options = resources.extract_options! - options = (@scope[:options] || {}).merge(options) - options[:shallow] = true if @scope[:shallow] && !options.has_key?(:shallow) if apply_common_behavior_for(:resources, resources, options, &block) return self @@ -804,6 +802,10 @@ def apply_common_behavior_for(method, resources, options, &block) return true end + scope_options = @scope.slice(*MERGE_FROM_SCOPE_OPTIONS).delete_if{ |k,v| v.blank? } + options.reverse_merge!(scope_options) unless scope_options.empty? + options.reverse_merge!(@scope[:options]) unless @scope[:options].blank? + if resource_scope? nested do send(method, resources.pop, options, &block) diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 0548456b639d8..b5653a391a638 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -334,6 +334,13 @@ def self.matches?(request) get '/tickets', :to => 'tickets#index', :as => :tickets end + scope :constraints => { :id => /\d{4}/ } do + resources :movies do + resources :reviews + resource :trailer + end + end + match '/:locale/*file.:format', :to => 'files#show', :file => /path\/to\/existing\/file/ end end @@ -1558,6 +1565,42 @@ def test_router_removes_invalid_conditions end end + def test_constraints_are_merged_from_scope + with_test_routes do + get '/movies/0001' + assert_equal 'movies#show', @response.body + assert_equal '/movies/0001', movie_path(:id => '0001') + + get '/movies/00001' + assert_equal 'Not Found', @response.body + assert_raises(ActionController::RoutingError){ movie_path(:id => '00001') } + + get '/movies/0001/reviews' + assert_equal 'reviews#index', @response.body + assert_equal '/movies/0001/reviews', movie_reviews_path(:movie_id => '0001') + + get '/movies/00001/reviews' + assert_equal 'Not Found', @response.body + assert_raises(ActionController::RoutingError){ movie_reviews_path(:movie_id => '00001') } + + get '/movies/0001/reviews/0001' + assert_equal 'reviews#show', @response.body + assert_equal '/movies/0001/reviews/0001', movie_review_path(:movie_id => '0001', :id => '0001') + + get '/movies/00001/reviews/0001' + assert_equal 'Not Found', @response.body + assert_raises(ActionController::RoutingError){ movie_path(:movie_id => '00001', :id => '00001') } + + get '/movies/0001/trailer' + assert_equal 'trailers#show', @response.body + assert_equal '/movies/0001/trailer', movie_trailer_path(:movie_id => '0001') + + get '/movies/00001/trailer' + assert_equal 'Not Found', @response.body + assert_raises(ActionController::RoutingError){ movie_trailer_path(:movie_id => '00001') } + end + end + private def with_test_routes yield