diff options
author | Rafael Mendonça França <rafaelmfranca@gmail.com> | 2012-08-13 21:44:13 -0300 |
---|---|---|
committer | Rafael Mendonça França <rafaelmfranca@gmail.com> | 2012-08-13 21:44:13 -0300 |
commit | 2d9dbf416b14610cc21fc10e68700c3a7ffc32a3 (patch) | |
tree | a758df96d03e3756d22b7d588d9fabf976eb31d4 | |
parent | fa736e69a197522ae3af3d3e6394cdc1eb1da228 (diff) | |
parent | 0bd7b07dff253e0fc2d01644371680bafa0df372 (diff) | |
download | rails-2d9dbf416b14610cc21fc10e68700c3a7ffc32a3.tar.gz rails-2d9dbf416b14610cc21fc10e68700c3a7ffc32a3.tar.bz2 rails-2d9dbf416b14610cc21fc10e68700c3a7ffc32a3.zip |
Merge branch 'concerns'
-rw-r--r-- | actionpack/CHANGELOG.md | 30 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/routing/mapper.rb | 62 | ||||
-rw-r--r-- | actionpack/test/abstract_unit.rb | 29 | ||||
-rw-r--r-- | actionpack/test/controller/resources_test.rb | 29 | ||||
-rw-r--r-- | actionpack/test/dispatch/routing/concerns_test.rb | 82 | ||||
-rw-r--r-- | guides/source/routing.textile | 30 |
6 files changed, 232 insertions, 30 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 2e683e7c47..d674fae9d4 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,5 +1,35 @@ ## Rails 4.0.0 (unreleased) ## +* Add Routing Concerns to declare common routes that can be reused inside + others resources and routes. + + Code before: + + resources :messages do + resources :comments + end + + resources :posts do + resources :comments + resources :images, only: :index + end + + Code after: + + concern :commentable do + resources :comments + end + + concern :image_attachable do + resources :images, only: :index + end + + resources :messages, concerns: :commentable + + resources :posts, concerns: [:commentable, :image_attachable] + + *David Heinemeier Hansson + Rafael Mendonça França* + * Add start_hour and end_hour options to the select_hour helper. *Evan Tann* * Raises an ArgumentError when the first argument in `form_for` contain `nil` diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 5e2f1ff1e0..ea5028a7c0 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,71 @@ module ActionDispatch end end + # Routing Concerns allows you to declare common routes that can be reused + # inside others resources and routes. + # + # concern :commentable do + # resources :comments + # end + # + # concern :image_attachable do + # resources :images, only: :index + # end + # + # These concerns are used in Resources routing: + # + # resources :messages, concerns: [:commentable, :image_attachable] + # + # or in a scope or namespace: + # + # namespace :posts do + # concerns :commentable + # end + module Concerns + # Define a routing concern using a name. + # + # concern :commentable do + # resources :comments + # end + # + # Any routing helpers can be used inside a concern. + def concern(name, &block) + @concerns[name] = block + end + + # Use the named concerns + # + # resources :posts do + # concerns :commentable + # end + # + # concerns also work in any routes helper that you want to use: + # + # namespace :posts do + # concerns :commentable + # 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/abstract_unit.rb b/actionpack/test/abstract_unit.rb index b914bbce4d..e5054a9eb8 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -349,3 +349,32 @@ module RoutingTestHelpers set.send(:url_for, options.merge(:only_path => true, :_recall => recall)) end end + +class ResourcesController < ActionController::Base + def index() render :nothing => true end + alias_method :show, :index +end + +class ThreadsController < ResourcesController; end +class MessagesController < ResourcesController; end +class CommentsController < ResourcesController; end +class AuthorsController < ResourcesController; end +class LogosController < ResourcesController; end + +class AccountsController < ResourcesController; end +class AdminController < ResourcesController; end +class ProductsController < ResourcesController; end +class ImagesController < ResourcesController; end +class PreferencesController < ResourcesController; end + +module Backoffice + class ProductsController < ResourcesController; end + class TagsController < ResourcesController; end + class ManufacturersController < ResourcesController; end + class ImagesController < ResourcesController; end + + module Admin + class ProductsController < ResourcesController; end + class ImagesController < ResourcesController; end + end +end diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb index 236e16c68e..305659b219 100644 --- a/actionpack/test/controller/resources_test.rb +++ b/actionpack/test/controller/resources_test.rb @@ -2,35 +2,6 @@ require 'abstract_unit' require 'active_support/core_ext/object/try' require 'active_support/core_ext/object/with_options' -class ResourcesController < ActionController::Base - def index() render :nothing => true end - alias_method :show, :index -end - -class ThreadsController < ResourcesController; end -class MessagesController < ResourcesController; end -class CommentsController < ResourcesController; end -class AuthorsController < ResourcesController; end -class LogosController < ResourcesController; end - -class AccountsController < ResourcesController; end -class AdminController < ResourcesController; end -class ProductsController < ResourcesController; end -class ImagesController < ResourcesController; end -class PreferencesController < ResourcesController; end - -module Backoffice - class ProductsController < ResourcesController; end - class TagsController < ResourcesController; end - class ManufacturersController < ResourcesController; end - class ImagesController < ResourcesController; end - - module Admin - class ProductsController < ResourcesController; end - class ImagesController < ResourcesController; end - end -end - class ResourcesTest < ActionController::TestCase def test_default_restful_routes with_restful_routing :messages do diff --git a/actionpack/test/dispatch/routing/concerns_test.rb b/actionpack/test/dispatch/routing/concerns_test.rb new file mode 100644 index 0000000000..21da3bd77a --- /dev/null +++ b/actionpack/test/dispatch/routing/concerns_test.rb @@ -0,0 +1,82 @@ +require 'abstract_unit' + +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 :images, 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/images" + assert_equal "200", @response.code + assert_equal "/posts/1/images", post_images_path(post_id: 1) + end + + def test_accessing_concern_from_resources_using_only_option + get "/posts/1/image/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 diff --git a/guides/source/routing.textile b/guides/source/routing.textile index cffbf9bec4..bed7d03e06 100644 --- a/guides/source/routing.textile +++ b/guides/source/routing.textile @@ -273,6 +273,36 @@ The corresponding route helper would be +publisher_magazine_photo_url+, requirin TIP: _Resources should never be nested more than 1 level deep._ +h4. Routing concerns + +Routing Concerns allows you to declare common routes that can be reused inside others resources and routes. + +<ruby> +concern :commentable do + resources :comments +end + +concern :image_attachable do + resources :images, only: :index +end +</ruby> + +These concerns can be used in resources to avoid code duplication and share behavior across routes. + +<ruby> +resources :messages, concerns: :commentable + +resources :posts, concerns: [:commentable, :image_attachable] +</ruby> + +Also you can use them in any place that you want inside the routes, for example in a scope or namespace call: + +<ruby> +namespace :posts do + concerns :commentable +end +</ruby> + h4. Creating Paths and URLs From Objects In addition to using the routing helpers, Rails can also create paths and URLs from an array of parameters. For example, suppose you have this set of routes: |