aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb24
-rw-r--r--actionpack/test/dispatch/routing/concerns_test.rb94
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