Skip to content

Commit

Permalink
Add :only/:except options to map.resources
Browse files Browse the repository at this point in the history
This allows people with huge numbers of resource routes to cut down on the memory consumption caused by the generated code.

Signed-off-by: Michael Koziarski <michael@koziarski.com>
[rails#1215 state:committed]
  • Loading branch information
tomstuart authored and NZKoz committed Nov 12, 2008
1 parent c65075f commit 44a3009
Show file tree
Hide file tree
Showing 2 changed files with 247 additions and 37 deletions.
122 changes: 85 additions & 37 deletions actionpack/lib/action_controller/resources.rb
Expand Up @@ -42,7 +42,11 @@ module ActionController
# #
# Read more about REST at http://en.wikipedia.org/wiki/Representational_State_Transfer # Read more about REST at http://en.wikipedia.org/wiki/Representational_State_Transfer
module Resources module Resources
INHERITABLE_OPTIONS = :namespace, :shallow, :only, :except

class Resource #:nodoc: class Resource #:nodoc:
DEFAULT_ACTIONS = :index, :create, :new, :edit, :show, :update, :destroy

attr_reader :collection_methods, :member_methods, :new_methods attr_reader :collection_methods, :member_methods, :new_methods
attr_reader :path_prefix, :name_prefix, :path_segment attr_reader :path_prefix, :name_prefix, :path_segment
attr_reader :plural, :singular attr_reader :plural, :singular
Expand All @@ -57,6 +61,7 @@ def initialize(entities, options)


arrange_actions arrange_actions
add_default_actions add_default_actions
set_allowed_actions
set_prefixes set_prefixes
end end


Expand Down Expand Up @@ -113,6 +118,10 @@ def uncountable?
@singular.to_s == @plural.to_s @singular.to_s == @plural.to_s
end end


def has_action?(action)
!DEFAULT_ACTIONS.include?(action) || action_allowed?(action)
end

protected protected
def arrange_actions def arrange_actions
@collection_methods = arrange_actions_by_methods(options.delete(:collection)) @collection_methods = arrange_actions_by_methods(options.delete(:collection))
Expand All @@ -125,6 +134,30 @@ def add_default_actions
add_default_action(new_methods, :get, :new) add_default_action(new_methods, :get, :new)
end end


def set_allowed_actions
only, except = @options.values_at(:only, :except)
@allowed_actions ||= {}

if only == :all || except == :none
only = nil
except = []
elsif only == :none || except == :all
only = []
except = nil
end

if only
@allowed_actions[:only] = Array(only).map(&:to_sym)
elsif except
@allowed_actions[:except] = Array(except).map(&:to_sym)
end
end

def action_allowed?(action)
only, except = @allowed_actions.values_at(:only, :except)
(!only || only.include?(action)) && (!except || !except.include?(action))
end

def set_prefixes def set_prefixes
@path_prefix = options.delete(:path_prefix) @path_prefix = options.delete(:path_prefix)
@name_prefix = options.delete(:name_prefix) @name_prefix = options.delete(:name_prefix)
Expand Down Expand Up @@ -353,6 +386,25 @@ def initialize(entity, options)
# #
# map.resources :users, :has_many => { :posts => :comments }, :shallow => true # map.resources :users, :has_many => { :posts => :comments }, :shallow => true
# #
# * <tt>:only</tt> and <tt>:except</tt> - Specify which of the seven default actions should be routed to.
#
# <tt>:only</tt> and <tt>:except</tt> may be set to <tt>:all</tt>, <tt>:none</tt>, an action name or a
# list of action names. By default, routes are generated for all seven actions.
#
# For example:
#
# map.resources :posts, :only => [:index, :show] do |post|
# post.resources :comments, :except => [:update, :destroy]
# end
# # --> GET /posts (maps to the PostsController#index action)
# # --> POST /posts (fails)
# # --> GET /posts/1 (maps to the PostsController#show action)
# # --> DELETE /posts/1 (fails)
# # --> POST /posts/1/comments (maps to the CommentsController#create action)
# # --> PUT /posts/1/comments/1 (fails)
#
# The <tt>:only</tt> and <tt>:except</tt> options are inherited by any nested resource(s).
#
# If <tt>map.resources</tt> is called with multiple resources, they all get the same options applied. # If <tt>map.resources</tt> is called with multiple resources, they all get the same options applied.
# #
# Examples: # Examples:
Expand Down Expand Up @@ -478,7 +530,7 @@ def map_resource(entities, options = {}, &block)
map_associations(resource, options) map_associations(resource, options)


if block_given? if block_given?
with_options(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :namespace => options[:namespace], :shallow => options[:shallow], &block) with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block)
end end
end end
end end
Expand All @@ -495,7 +547,7 @@ def map_singleton_resource(entities, options = {}, &block)
map_associations(resource, options) map_associations(resource, options)


if block_given? if block_given?
with_options(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :namespace => options[:namespace], :shallow => options[:shallow], &block) with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block)
end end
end end
end end
Expand All @@ -507,7 +559,7 @@ def map_associations(resource, options)
name_prefix = "#{options.delete(:name_prefix)}#{resource.nesting_name_prefix}" name_prefix = "#{options.delete(:name_prefix)}#{resource.nesting_name_prefix}"


Array(options[:has_one]).each do |association| Array(options[:has_one]).each do |association|
resource(association, :path_prefix => path_prefix, :name_prefix => name_prefix, :namespace => options[:namespace], :shallow => options[:shallow]) resource(association, options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => path_prefix, :name_prefix => name_prefix))
end end
end end


Expand All @@ -522,7 +574,7 @@ def map_has_many_associations(resource, associations, options)
map_has_many_associations(resource, association, options) map_has_many_associations(resource, association, options)
end end
when Symbol, String when Symbol, String
resources(associations, :path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :namespace => options[:namespace], :shallow => options[:shallow], :has_many => options[:has_many]) resources(associations, options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :has_many => options[:has_many]))
else else
end end
end end
Expand All @@ -531,41 +583,39 @@ def map_collection_actions(map, resource)
resource.collection_methods.each do |method, actions| resource.collection_methods.each do |method, actions|
actions.each do |action| actions.each do |action|
[method].flatten.each do |m| [method].flatten.each do |m|
action_options = action_options_for(action, resource, m) map_resource_routes(map, resource, action, "#{resource.path}#{resource.action_separator}#{action}", "#{action}_#{resource.name_prefix}#{resource.plural}", m)
map_named_routes(map, "#{action}_#{resource.name_prefix}#{resource.plural}", "#{resource.path}#{resource.action_separator}#{action}", action_options)
end end
end end
end end
end end


def map_default_collection_actions(map, resource) def map_default_collection_actions(map, resource)
index_action_options = action_options_for("index", resource)
index_route_name = "#{resource.name_prefix}#{resource.plural}" index_route_name = "#{resource.name_prefix}#{resource.plural}"


if resource.uncountable? if resource.uncountable?
index_route_name << "_index" index_route_name << "_index"
end end


map_named_routes(map, index_route_name, resource.path, index_action_options) map_resource_routes(map, resource, :index, resource.path, index_route_name)

map_resource_routes(map, resource, :create, resource.path)
create_action_options = action_options_for("create", resource)
map_unnamed_routes(map, resource.path, create_action_options)
end end


def map_default_singleton_actions(map, resource) def map_default_singleton_actions(map, resource)
create_action_options = action_options_for("create", resource) map_resource_routes(map, resource, :create, resource.path)
map_unnamed_routes(map, resource.path, create_action_options)
end end


def map_new_actions(map, resource) def map_new_actions(map, resource)
resource.new_methods.each do |method, actions| resource.new_methods.each do |method, actions|
actions.each do |action| actions.each do |action|
action_options = action_options_for(action, resource, method) route_path = resource.new_path
if action == :new route_name = "new_#{resource.name_prefix}#{resource.singular}"
map_named_routes(map, "new_#{resource.name_prefix}#{resource.singular}", resource.new_path, action_options)
else unless action == :new
map_named_routes(map, "#{action}_new_#{resource.name_prefix}#{resource.singular}", "#{resource.new_path}#{resource.action_separator}#{action}", action_options) route_path = "#{route_path}#{resource.action_separator}#{action}"
route_name = "#{action}_#{route_name}"
end end

map_resource_routes(map, resource, action, route_path, route_name, method)
end end
end end
end end
Expand All @@ -574,34 +624,32 @@ def map_member_actions(map, resource)
resource.member_methods.each do |method, actions| resource.member_methods.each do |method, actions|
actions.each do |action| actions.each do |action|
[method].flatten.each do |m| [method].flatten.each do |m|
action_options = action_options_for(action, resource, m)

action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash) action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash)
action_path ||= Base.resources_path_names[action] || action action_path ||= Base.resources_path_names[action] || action


map_named_routes(map, "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", "#{resource.member_path}#{resource.action_separator}#{action_path}", action_options) map_resource_routes(map, resource, action, "#{resource.member_path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", m)
end end
end end
end end


show_action_options = action_options_for("show", resource) map_resource_routes(map, resource, :show, resource.member_path, "#{resource.shallow_name_prefix}#{resource.singular}")
map_named_routes(map, "#{resource.shallow_name_prefix}#{resource.singular}", resource.member_path, show_action_options) map_resource_routes(map, resource, :update, resource.member_path)

map_resource_routes(map, resource, :destroy, resource.member_path)
update_action_options = action_options_for("update", resource)
map_unnamed_routes(map, resource.member_path, update_action_options)

destroy_action_options = action_options_for("destroy", resource)
map_unnamed_routes(map, resource.member_path, destroy_action_options)
end end


def map_unnamed_routes(map, path_without_format, options) def map_resource_routes(map, resource, action, route_path, route_name = nil, method = nil)
map.connect(path_without_format, options) if resource.has_action?(action)
map.connect("#{path_without_format}.:format", options) action_options = action_options_for(action, resource, method)
end formatted_route_path = "#{route_path}.:format"


def map_named_routes(map, name, path_without_format, options) if route_name
map.named_route(name, path_without_format, options) map.named_route(route_name, route_path, action_options)
map.named_route("formatted_#{name}", "#{path_without_format}.:format", options) map.named_route("formatted_#{route_name}", formatted_route_path, action_options)
else
map.connect(route_path, action_options)
map.connect(formatted_route_path, action_options)
end
end
end end


def add_conditions_for(conditions, method) def add_conditions_for(conditions, method)
Expand Down

0 comments on commit 44a3009

Please sign in to comment.