Skip to content

Commit 0dd2472

Browse files
committed
Implementing Routing Concerns
This pattern was introduced as a plugin by @dhh. The original implementation can be found in https://github.com/rails/routing_concerns
1 parent fa736e6 commit 0dd2472

File tree

2 files changed

+117
-1
lines changed

2 files changed

+117
-1
lines changed

Diff for: actionpack/lib/action_dispatch/routing/mapper.rb

+23-1
Original file line numberDiff line numberDiff line change
@@ -909,7 +909,7 @@ module Resources
909909
# CANONICAL_ACTIONS holds all actions that does not need a prefix or
910910
# a path appended since they fit properly in their scope level.
911911
VALID_ON_OPTIONS = [:new, :collection, :member]
912-
RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except, :param]
912+
RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except, :param, :concerns]
913913
CANONICAL_ACTIONS = %w(index create new show update destroy)
914914

915915
class Resource #:nodoc:
@@ -1046,6 +1046,8 @@ def resource(*resources, &block)
10461046
resource_scope(:resource, SingletonResource.new(resources.pop, options)) do
10471047
yield if block_given?
10481048

1049+
concerns(options[:concerns]) if options[:concerns]
1050+
10491051
collection do
10501052
post :create
10511053
end if parent_resource.actions.include?(:create)
@@ -1210,6 +1212,8 @@ def resources(*resources, &block)
12101212
resource_scope(:resources, Resource.new(resources.pop, options)) do
12111213
yield if block_given?
12121214

1215+
concerns(options[:concerns]) if options[:concerns]
1216+
12131217
collection do
12141218
get :index if parent_resource.actions.include?(:index)
12151219
post :create if parent_resource.actions.include?(:create)
@@ -1580,15 +1584,33 @@ def name_for_action(as, action) #:nodoc:
15801584
end
15811585
end
15821586

1587+
module Concerns
1588+
def concern(name, &block)
1589+
@concerns[name] = block
1590+
end
1591+
1592+
def concerns(*names)
1593+
names.flatten.each do |name|
1594+
if concern = @concerns[name]
1595+
instance_eval(&concern)
1596+
else
1597+
raise ArgumentError, "No concern named #{name} was found!"
1598+
end
1599+
end
1600+
end
1601+
end
1602+
15831603
def initialize(set) #:nodoc:
15841604
@set = set
15851605
@scope = { :path_names => @set.resources_path_names }
1606+
@concerns = {}
15861607
end
15871608

15881609
include Base
15891610
include HttpHelpers
15901611
include Redirection
15911612
include Scoping
1613+
include Concerns
15921614
include Resources
15931615
end
15941616
end

Diff for: actionpack/test/dispatch/routing/concerns_test.rb

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
require 'abstract_unit'
2+
3+
class CommentsController < ActionController::Base
4+
def index
5+
head :ok
6+
end
7+
end
8+
9+
class ImageAttachmentsController < ActionController::Base
10+
def index
11+
head :ok
12+
end
13+
end
14+
15+
class RoutingConcernsTest < ActionDispatch::IntegrationTest
16+
Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
17+
app.draw do
18+
concern :commentable do
19+
resources :comments
20+
end
21+
22+
concern :image_attachable do
23+
resources :image_attachments, only: :index
24+
end
25+
26+
resources :posts, concerns: [:commentable, :image_attachable] do
27+
resource :video, concerns: :commentable
28+
end
29+
30+
resource :picture, concerns: :commentable do
31+
resources :posts, concerns: :commentable
32+
end
33+
34+
scope "/videos" do
35+
concerns :commentable
36+
end
37+
end
38+
end
39+
40+
include Routes.url_helpers
41+
def app; Routes end
42+
43+
def test_accessing_concern_from_resources
44+
get "/posts/1/comments"
45+
assert_equal "200", @response.code
46+
assert_equal "/posts/1/comments", post_comments_path(post_id: 1)
47+
end
48+
49+
def test_accessing_concern_from_resource
50+
get "/picture/comments"
51+
assert_equal "200", @response.code
52+
assert_equal "/picture/comments", picture_comments_path
53+
end
54+
55+
def test_accessing_concern_from_nested_resource
56+
get "/posts/1/video/comments"
57+
assert_equal "200", @response.code
58+
assert_equal "/posts/1/video/comments", post_video_comments_path(post_id: 1)
59+
end
60+
61+
def test_accessing_concern_from_nested_resources
62+
get "/picture/posts/1/comments"
63+
assert_equal "200", @response.code
64+
assert_equal "/picture/posts/1/comments", picture_post_comments_path(post_id: 1)
65+
end
66+
67+
def test_accessing_concern_from_resources_with_more_than_one_concern
68+
get "/posts/1/image_attachments"
69+
assert_equal "200", @response.code
70+
assert_equal "/posts/1/image_attachments", post_image_attachments_path(post_id: 1)
71+
end
72+
73+
def test_accessing_concern_from_resources_using_only_option
74+
get "/posts/1/image_attachment/1"
75+
assert_equal "404", @response.code
76+
end
77+
78+
def test_accessing_concern_from_a_scope
79+
get "/videos/comments"
80+
assert_equal "200", @response.code
81+
end
82+
83+
def test_with_an_invalid_concern_name
84+
e = assert_raise ArgumentError do
85+
ActionDispatch::Routing::RouteSet.new.tap do |app|
86+
app.draw do
87+
resources :posts, concerns: :foo
88+
end
89+
end
90+
end
91+
92+
assert_equal "No concern named foo was found!", e.message
93+
end
94+
end

0 commit comments

Comments
 (0)