diff options
-rw-r--r-- | actionpack/lib/action_dispatch/routing/mapper.rb | 24 | ||||
-rw-r--r-- | actionpack/test/dispatch/routing/concerns_test.rb | 94 |
2 files changed, 117 insertions, 1 deletions
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 5e2f1ff1e0..5ff2420921 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -909,7 +909,7 @@ module ActionDispatch # CANONICAL_ACTIONS holds all actions that does not need a prefix or # a path appended since they fit properly in their scope level. VALID_ON_OPTIONS = [:new, :collection, :member] - RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except, :param] + RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except, :param, :concerns] CANONICAL_ACTIONS = %w(index create new show update destroy) class Resource #:nodoc: @@ -1046,6 +1046,8 @@ module ActionDispatch resource_scope(:resource, SingletonResource.new(resources.pop, options)) do yield if block_given? + concerns(options[:concerns]) if options[:concerns] + collection do post :create end if parent_resource.actions.include?(:create) @@ -1210,6 +1212,8 @@ module ActionDispatch resource_scope(:resources, Resource.new(resources.pop, options)) do yield if block_given? + concerns(options[:concerns]) if options[:concerns] + collection do get :index if parent_resource.actions.include?(:index) post :create if parent_resource.actions.include?(:create) @@ -1580,15 +1584,33 @@ module ActionDispatch end end + module Concerns + def concern(name, &block) + @concerns[name] = block + end + + def concerns(*names) + names.flatten.each do |name| + if concern = @concerns[name] + instance_eval(&concern) + else + raise ArgumentError, "No concern named #{name} was found!" + end + end + end + end + def initialize(set) #:nodoc: @set = set @scope = { :path_names => @set.resources_path_names } + @concerns = {} end include Base include HttpHelpers include Redirection include Scoping + include Concerns include Resources end end diff --git a/actionpack/test/dispatch/routing/concerns_test.rb b/actionpack/test/dispatch/routing/concerns_test.rb new file mode 100644 index 0000000000..ab4ad6b72e --- /dev/null +++ b/actionpack/test/dispatch/routing/concerns_test.rb @@ -0,0 +1,94 @@ +require 'abstract_unit' + +class CommentsController < ActionController::Base + def index + head :ok + end +end + +class ImageAttachmentsController < ActionController::Base + def index + head :ok + end +end + +class RoutingConcernsTest < ActionDispatch::IntegrationTest + Routes = ActionDispatch::Routing::RouteSet.new.tap do |app| + app.draw do + concern :commentable do + resources :comments + end + + concern :image_attachable do + resources :image_attachments, only: :index + end + + resources :posts, concerns: [:commentable, :image_attachable] do + resource :video, concerns: :commentable + end + + resource :picture, concerns: :commentable do + resources :posts, concerns: :commentable + end + + scope "/videos" do + concerns :commentable + end + end + end + + include Routes.url_helpers + def app; Routes end + + def test_accessing_concern_from_resources + get "/posts/1/comments" + assert_equal "200", @response.code + assert_equal "/posts/1/comments", post_comments_path(post_id: 1) + end + + def test_accessing_concern_from_resource + get "/picture/comments" + assert_equal "200", @response.code + assert_equal "/picture/comments", picture_comments_path + end + + def test_accessing_concern_from_nested_resource + get "/posts/1/video/comments" + assert_equal "200", @response.code + assert_equal "/posts/1/video/comments", post_video_comments_path(post_id: 1) + end + + def test_accessing_concern_from_nested_resources + get "/picture/posts/1/comments" + assert_equal "200", @response.code + assert_equal "/picture/posts/1/comments", picture_post_comments_path(post_id: 1) + end + + def test_accessing_concern_from_resources_with_more_than_one_concern + get "/posts/1/image_attachments" + assert_equal "200", @response.code + assert_equal "/posts/1/image_attachments", post_image_attachments_path(post_id: 1) + end + + def test_accessing_concern_from_resources_using_only_option + get "/posts/1/image_attachment/1" + assert_equal "404", @response.code + end + + def test_accessing_concern_from_a_scope + get "/videos/comments" + assert_equal "200", @response.code + end + + def test_with_an_invalid_concern_name + e = assert_raise ArgumentError do + ActionDispatch::Routing::RouteSet.new.tap do |app| + app.draw do + resources :posts, concerns: :foo + end + end + end + + assert_equal "No concern named foo was found!", e.message + end +end |