Module: ActionDispatch::Routing::Mapper::Resources

Included in:
ActionDispatch::Routing::Mapper
Defined in:
lib/action_dispatch/routing/mapper.rb

Overview

Resource routing allows you to quickly declare all of the common routes for a given resourceful controller. Instead of declaring separate routes for your index, show, new, edit, create, update and destroy actions, a resourceful route declares them in a single line of code:

resources :photos

Sometimes, you have a resource that clients always look up without referencing an ID. A common example, /profile always shows the profile of the currently logged in user. In this case, you can use a singular resource to map /profile (rather than /profile/:id) to the show action.

resource :profile

It’s common to have resources that are logically children of other resources:

resources :magazines do
  resources :ads
end

You may wish to organize groups of controllers under a namespace. Most commonly, you might group a number of administrative controllers under an admin namespace. You would place these controllers under the app/controllers/admin directory, and you can group them together in your router:

namespace "admin" do
  resources :posts, :comments
end

Defined Under Namespace

Classes: Resource, SingletonResource

Constant Summary collapse

VALID_ON_OPTIONS =

CANONICAL_ACTIONS holds all actions that does not need a prefix or a path appended since they fit properly in their scope level.

[:new, :collection, :member]
RESOURCE_OPTIONS =
[:as, :controller, :path, :only, :except]
CANONICAL_ACTIONS =
%w(index create new show update destroy)

Instance Method Summary collapse

Instance Method Details

#collectionObject

To add a route to the collection:

resources :photos do
  collection do
    get 'search'
  end
end

This will enable Rails to recognize paths such as /photos/search with GET, and route to the search action of PhotosController. It will also create the search_photos_url and search_photos_path route helpers.



985
986
987
988
989
990
991
992
993
994
995
# File 'lib/action_dispatch/routing/mapper.rb', line 985

def collection
  unless resource_scope?
    raise ArgumentError, "can't use collection outside resource(s) scope"
  end

  with_scope_level(:collection) do
    scope(parent_resource.collection_scope) do
      yield
    end
  end
end

#initialize(*args) ⇒ Object

:nodoc:



863
864
865
866
# File 'lib/action_dispatch/routing/mapper.rb', line 863

def initialize(*args) #:nodoc:
  super
  @scope[:path_names] = @set.resources_path_names
end

#match(*args) ⇒ Object



1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
# File 'lib/action_dispatch/routing/mapper.rb', line 1072

def match(*args)
  options = args.extract_options!.dup
  options[:anchor] = true unless options.key?(:anchor)

  if args.length > 1
    args.each { |path| match(path, options.dup) }
    return self
  end

  on = options.delete(:on)
  if VALID_ON_OPTIONS.include?(on)
    args.push(options)
    return send(on){ match(*args) }
  elsif on
    raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
  end

  if @scope[:scope_level] == :resources
    args.push(options)
    return nested { match(*args) }
  elsif @scope[:scope_level] == :resource
    args.push(options)
    return member { match(*args) }
  end

  action = args.first
  path = path_for_action(action, options.delete(:path))

  if action.to_s =~ /^[\w\/]+$/
    options[:action] ||= action unless action.to_s.include?("/")
  else
    action = nil
  end

  if options.key?(:as) && !options[:as]
    options.delete(:as)
  else
    options[:as] = name_for_action(options[:as], action)
  end

  super(path, options)
end

#memberObject

To add a member route, add a member block into the resource block:

resources :photos do
  member do
    get 'preview'
  end
end

This will recognize /photos/1/preview with GET, and route to the preview action of PhotosController. It will also create the preview_photo_url and preview_photo_path helpers.



1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
# File 'lib/action_dispatch/routing/mapper.rb', line 1008

def member
  unless resource_scope?
    raise ArgumentError, "can't use member outside resource(s) scope"
  end

  with_scope_level(:member) do
    scope(parent_resource.member_scope) do
      yield
    end
  end
end

#namespace(path, options = {}) ⇒ Object



1054
1055
1056
1057
1058
1059
1060
# File 'lib/action_dispatch/routing/mapper.rb', line 1054

def namespace(path, options = {})
  if resource_scope?
    nested { super }
  else
    super
  end
end

#nestedObject



1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
# File 'lib/action_dispatch/routing/mapper.rb', line 1032

def nested
  unless resource_scope?
    raise ArgumentError, "can't use nested outside resource(s) scope"
  end

  with_scope_level(:nested) do
    if shallow?
      with_exclusive_scope do
        if @scope[:shallow_path].blank?
          scope(parent_resource.nested_scope, nested_options) { yield }
        else
          scope(@scope[:shallow_path], :as => @scope[:shallow_prefix]) do
            scope(parent_resource.nested_scope, nested_options) { yield }
          end
        end
      end
    else
      scope(parent_resource.nested_scope, nested_options) { yield }
    end
  end
end

#newObject



1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
# File 'lib/action_dispatch/routing/mapper.rb', line 1020

def new
  unless resource_scope?
    raise ArgumentError, "can't use new outside resource(s) scope"
  end

  with_scope_level(:new) do
    scope(parent_resource.new_scope(action_path(:new))) do
      yield
    end
  end
end

#resource(*resources, &block) ⇒ Object

Sometimes, you have a resource that clients always look up without referencing an ID. A common example, /profile always shows the profile of the currently logged in user. In this case, you can use a singular resource to map /profile (rather than /profile/:id) to the show action:

resource :geocoder

creates six different routes in your application, all mapping to the GeoCoders controller (note that the controller is named after the plural):

GET     /geocoder/new
POST    /geocoder
GET     /geocoder
GET     /geocoder/edit
PUT     /geocoder
DELETE  /geocoder


890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
# File 'lib/action_dispatch/routing/mapper.rb', line 890

def resource(*resources, &block)
  options = resources.extract_options!

  if apply_common_behavior_for(:resource, resources, options, &block)
    return self
  end

  resource_scope(SingletonResource.new(resources.pop, options)) do
    yield if block_given?

    collection do
      post :create
    end if parent_resource.actions.include?(:create)

    new do
      get :new
    end if parent_resource.actions.include?(:new)

    member do
      get    :edit if parent_resource.actions.include?(:edit)
      get    :show if parent_resource.actions.include?(:show)
      put    :update if parent_resource.actions.include?(:update)
      delete :destroy if parent_resource.actions.include?(:destroy)
    end
  end

  self
end

#resources(*resources, &block) ⇒ Object

In Rails, a resourceful route provides a mapping between HTTP verbs and URLs and controller actions. By convention, each action also maps to particular CRUD operations in a database. A single entry in the routing file, such as

resources :photos

creates seven different routes in your application, all mapping to the Photos controller:

GET     /photos/new
POST    /photos
GET     /photos/:id
GET     /photos/:id/edit
PUT     /photos/:id
DELETE  /photos/:id

Supported options

:path_names

Allows you to change the paths of the seven default actions. Paths not specified are not changed.

resources :posts, :path_names => { :new => "brand_new" }

The above example will now change /posts/new to /posts/brand_new



943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
# File 'lib/action_dispatch/routing/mapper.rb', line 943

def resources(*resources, &block)
  options = resources.extract_options!

  if apply_common_behavior_for(:resources, resources, options, &block)
    return self
  end

  resource_scope(Resource.new(resources.pop, options)) do
    yield if block_given?

    collection do
      get  :index if parent_resource.actions.include?(:index)
      post :create if parent_resource.actions.include?(:create)
    end

    new do
      get :new
    end if parent_resource.actions.include?(:new)

    member do
      get    :edit if parent_resource.actions.include?(:edit)
      get    :show if parent_resource.actions.include?(:show)
      put    :update if parent_resource.actions.include?(:update)
      delete :destroy if parent_resource.actions.include?(:destroy)
    end
  end

  self
end

#resources_path_names(options) ⇒ Object



868
869
870
# File 'lib/action_dispatch/routing/mapper.rb', line 868

def resources_path_names(options)
  @scope[:path_names].merge!(options)
end

#root(options = {}) ⇒ Object



1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
# File 'lib/action_dispatch/routing/mapper.rb', line 1115

def root(options={})
  if @scope[:scope_level] == :resources
    with_scope_level(:root) do
      scope(parent_resource.path) do
        super(options)
      end
    end
  else
    super(options)
  end
end

#shallowObject



1062
1063
1064
1065
1066
# File 'lib/action_dispatch/routing/mapper.rb', line 1062

def shallow
  scope(:shallow => true) do
    yield
  end
end

#shallow?Boolean

Returns:

  • (Boolean)


1068
1069
1070
# File 'lib/action_dispatch/routing/mapper.rb', line 1068

def shallow?
  parent_resource.instance_of?(Resource) && @scope[:shallow]
end