mirror of
https://github.com/github/rails.git
synced 2026-01-30 00:38:00 -05:00
Added map.resources from the Simply Restful plugin (backwards incompatible with the plugin!) [DHH]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@4637 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
*SVN*
|
||||
|
||||
* Added map.resources from the Simply Restful plugin [DHH]. Examples (the API has changed to use plurals!):
|
||||
|
||||
map.resources :messages
|
||||
map.resources :messages, :comments
|
||||
map.resources :messages, :new => { :preview => :post }
|
||||
|
||||
* Fixed that integration simulation of XHRs should set Accept header as well [Edward Frederick]
|
||||
|
||||
* TestRequest#reset_session should restore a TestSession, not a hash [Koz]
|
||||
|
||||
@@ -2,6 +2,7 @@ require 'action_controller/mime_type'
|
||||
require 'action_controller/request'
|
||||
require 'action_controller/response'
|
||||
require 'action_controller/routing'
|
||||
require 'action_controller/resources'
|
||||
require 'action_controller/url_rewriter'
|
||||
require 'drb'
|
||||
require 'set'
|
||||
|
||||
@@ -15,7 +15,9 @@ module ActionController
|
||||
|
||||
# Returns the HTTP request method as a lowercase symbol (:get, for example)
|
||||
def method
|
||||
@request_method ||= @env['REQUEST_METHOD'].downcase.to_sym
|
||||
@request_method ||= (method = parameters[:_method] && method == :post) ?
|
||||
method.to_s.downcase.to_sym :
|
||||
@env['REQUEST_METHOD'].downcase.to_sym
|
||||
end
|
||||
|
||||
# Is this a GET request? Equivalent to request.method == :get
|
||||
|
||||
170
actionpack/lib/action_controller/resources.rb
Normal file
170
actionpack/lib/action_controller/resources.rb
Normal file
@@ -0,0 +1,170 @@
|
||||
module ActionController
|
||||
module Resources
|
||||
class Resource #:nodoc:
|
||||
attr_reader :collection_methods, :member_methods, :new_methods
|
||||
attr_reader :path_prefix, :name_prefix
|
||||
attr_reader :plural, :singular
|
||||
attr_reader :options
|
||||
|
||||
def initialize(entities, options)
|
||||
@plural = entities
|
||||
@singular = options[:singular] || plural.to_s.singularize
|
||||
|
||||
@options = options
|
||||
|
||||
arrange_actions
|
||||
add_default_actions
|
||||
set_prefixes
|
||||
end
|
||||
|
||||
def controller
|
||||
(options[:controller] || plural).to_s
|
||||
end
|
||||
|
||||
def path
|
||||
"#{path_prefix}/#{plural}"
|
||||
end
|
||||
|
||||
def new_path
|
||||
"#{path}/new"
|
||||
end
|
||||
|
||||
def member_path
|
||||
"#{path}/:id"
|
||||
end
|
||||
|
||||
def nesting_path_prefix
|
||||
"#{path_prefix}/#{plural}/:#{singular}_id"
|
||||
end
|
||||
|
||||
private
|
||||
def arrange_actions
|
||||
@collection_methods = arrange_actions_by_methods(options.delete(:collection))
|
||||
@member_methods = arrange_actions_by_methods(options.delete(:member))
|
||||
@new_methods = arrange_actions_by_methods(options.delete(:new))
|
||||
end
|
||||
|
||||
def add_default_actions
|
||||
add_default_action(collection_methods, :post, :create)
|
||||
add_default_action(member_methods, :get, :edit)
|
||||
add_default_action(member_methods, :put, :update)
|
||||
add_default_action(member_methods, :delete, :destroy)
|
||||
add_default_action(new_methods, :get, :new)
|
||||
end
|
||||
|
||||
def set_prefixes
|
||||
@path_prefix = options.delete(:path_prefix)
|
||||
@name_prefix = options.delete(:name_prefix)
|
||||
end
|
||||
|
||||
def arrange_actions_by_methods(actions)
|
||||
arrayize_values(flip_keys_and_values(actions || {}))
|
||||
end
|
||||
|
||||
def add_default_action(collection, method, action)
|
||||
(collection[method] ||= []).unshift(action)
|
||||
end
|
||||
|
||||
def flip_keys_and_values(hash)
|
||||
hash.inject({}) do |flipped_hash, (key, value)|
|
||||
flipped_hash[value] = key
|
||||
flipped_hash
|
||||
end
|
||||
end
|
||||
|
||||
def arrayize_values(hash)
|
||||
hash.each do |(key, value)|
|
||||
unless value.is_a?(Array)
|
||||
hash[key] = []
|
||||
hash[key] << value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def resources(*entities)
|
||||
options = entities.last.is_a?(Hash) ? entities.pop : { }
|
||||
entities.each { |entity| map_resource(entity, options) { yield if block_given? } }
|
||||
end
|
||||
|
||||
private
|
||||
def map_resource(entities, options = {}, &block)
|
||||
resource = Resource.new(entities, options)
|
||||
|
||||
with_options :controller => resource.controller do |map|
|
||||
map_collection_actions(map, resource)
|
||||
map_new_actions(map, resource)
|
||||
map_member_actions(map, resource)
|
||||
|
||||
if block_given?
|
||||
with_options(:path_prefix => resource.nesting_path_prefix, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def map_collection_actions(map, resource)
|
||||
resource.collection_methods.each do |method, actions|
|
||||
primary = actions.shift.to_s if method != :get
|
||||
route_options = requirements_for(method)
|
||||
|
||||
actions.each do |action|
|
||||
map.named_route(
|
||||
"#{resource.name_prefix}#{action}_#{resource.plural}",
|
||||
"#{resource.path};#{action}",
|
||||
route_options.merge(:action => action.to_s)
|
||||
)
|
||||
|
||||
map.named_route(
|
||||
"formatted_#{resource.name_prefix}#{action}_#{resource.plural}",
|
||||
"#{resource.path}.:format;#{action}",
|
||||
route_options.merge(:action => action.to_s)
|
||||
)
|
||||
end
|
||||
|
||||
unless primary.blank?
|
||||
map.connect(resource.path, route_options.merge(:action => primary))
|
||||
map.connect("#{resource.path}.:format", route_options.merge(:action => primary))
|
||||
end
|
||||
|
||||
map.named_route("#{resource.name_prefix}#{resource.plural}", resource.path, :action => "index", :conditions => { :method => :get })
|
||||
map.named_route("formatted_#{resource.name_prefix}#{resource.plural}", "#{resource.path}.:format", :action => "index", :conditions => { :method => :get })
|
||||
end
|
||||
end
|
||||
|
||||
def map_new_actions(map, resource)
|
||||
resource.new_methods.each do |method, actions|
|
||||
route_options = requirements_for(method)
|
||||
actions.each do |action|
|
||||
path = action == :new ? resource.new_path : "#{resource.new_path};#{action}"
|
||||
name = "new_#{resource.plural}"
|
||||
name = "#{action}_#{name}" unless action == :new
|
||||
|
||||
map.named_route("#{resource.name_prefix}#{name}", path, route_options.merge(:action => action.to_s))
|
||||
map.named_route("formatted_#{resource.name_prefix}#{name}", action == :new ? "#{resource.new_path}.:format" : "#{resource.new_path}.:format;#{action}", route_options.merge(:action => action.to_s))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def map_member_actions(map, resource)
|
||||
resource.member_methods.each do |method, actions|
|
||||
route_options = requirements_for(method)
|
||||
primary = actions.shift.to_s unless [ :get, :post, :any ].include?(method)
|
||||
|
||||
actions.each do |action|
|
||||
map.named_route("#{resource.name_prefix}#{action}_#{resource.singular}", "#{resource.member_path};#{action}", route_options.merge(:action => action.to_s))
|
||||
map.named_route("formatted_#{resource.name_prefix}#{action}_#{resource.singular}", "#{resource.member_path}.:format;#{action}", route_options.merge(:action => action.to_s))
|
||||
end
|
||||
|
||||
map.connect(resource.member_path, route_options.merge(:action => primary)) unless primary.blank?
|
||||
map.named_route("#{resource.name_prefix}#{resource.singular}", resource.member_path, :action => "show", :conditions => { :method => :get })
|
||||
map.named_route("formatted_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}.:format", :action => "show", :conditions => { :method => :get })
|
||||
end
|
||||
end
|
||||
|
||||
def requirements_for(method)
|
||||
method == :any ? {} : { :conditions => { :method => method } }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ActionController::Routing::RouteSet::Mapper.send :include, ActionController::Resources
|
||||
@@ -58,7 +58,7 @@ module ActionController
|
||||
use_controllers! nil
|
||||
end
|
||||
|
||||
def normalize_paths(paths=$LOAD_PATH)
|
||||
def normalize_paths(paths = $LOAD_PATH)
|
||||
# do the hokey-pokey of path normalization...
|
||||
paths = paths.collect do |path|
|
||||
path = path.
|
||||
@@ -351,7 +351,6 @@ module ActionController
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def requirement_for(key)
|
||||
return requirements[key] if requirements.key? key
|
||||
segments.each do |segment|
|
||||
@@ -415,7 +414,6 @@ module ActionController
|
||||
def optionality_implied?
|
||||
false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class StaticSegment < Segment
|
||||
@@ -450,11 +448,9 @@ module ActionController
|
||||
def to_s
|
||||
value
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class DividerSegment < StaticSegment
|
||||
|
||||
def initialize(value = nil)
|
||||
super(value)
|
||||
self.raw = true
|
||||
@@ -464,7 +460,6 @@ module ActionController
|
||||
def optionality_implied?
|
||||
true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class DynamicSegment < Segment
|
||||
@@ -760,7 +755,6 @@ module ActionController
|
||||
end
|
||||
|
||||
class RouteSet
|
||||
|
||||
# Mapper instances are used to build routes. The object passed to the draw
|
||||
# block in config/routes.rb is a Mapper instance.
|
||||
#
|
||||
@@ -839,7 +833,6 @@ module ActionController
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def url_helper_name(name, kind = :url)
|
||||
:"#{name}_#{kind}"
|
||||
end
|
||||
|
||||
109
actionpack/test/controller/resources_test.rb
Normal file
109
actionpack/test/controller/resources_test.rb
Normal file
@@ -0,0 +1,109 @@
|
||||
require File.dirname(__FILE__) + '/../abstract_unit'
|
||||
|
||||
class MessagesController < ActionController::Base
|
||||
def rescue_action(e) raise e end
|
||||
end
|
||||
|
||||
class CommentsController < ActionController::Base
|
||||
def rescue_action(e) raise e end
|
||||
end
|
||||
|
||||
class ResourcesTest < Test::Unit::TestCase
|
||||
def test_default_restful_routes
|
||||
with_restful_routing :messages do
|
||||
assert_restful_routes_for :messages do
|
||||
routing_options = {:controller => '/messages'}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_with_path_prefix
|
||||
with_restful_routing :messages, :path_prefix => '/thread/:thread_id' do
|
||||
assert_restful_routes_for :messages, :path_prefix => 'thread/5/', :options => { :thread_id => '5' }
|
||||
end
|
||||
end
|
||||
|
||||
def test_with_collection_action
|
||||
with_restful_routing :messages, :collection => { :rss => :get } do
|
||||
assert_restful_routes_for :messages do |options|
|
||||
assert_routing "/messages;rss", options.merge(:action => 'rss')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_with_member_action
|
||||
[:put, :post].each do |method|
|
||||
with_restful_routing :messages, :member => { :mark => method } do
|
||||
assert_restful_routes_for :messages do |options|
|
||||
assert_recognizes(
|
||||
options.merge(:action => 'mark', :id => '1'),
|
||||
{:path => "/messages/1;mark", :method => method})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_with_new_action
|
||||
with_restful_routing :messages, :new => { :preview => :post } do
|
||||
assert_restful_routes_for :messages do |options|
|
||||
assert_recognizes(
|
||||
options.merge(:action => 'preview'),
|
||||
{:path => "/messages/new;preview", :method => :post})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def xtest_nested_restful_routes
|
||||
with_routing do |set|
|
||||
set.draw do |map|
|
||||
map.resources(:messages) do
|
||||
map.resources(:comments)
|
||||
end
|
||||
end
|
||||
|
||||
with_options({ :controller => :comments }) do |controller|
|
||||
controller.assert_routing "/messages/1/comments", :action => 'index'
|
||||
controller.assert_routing "/messages/1/comments.xml" , :action => 'index', :format => 'xml'
|
||||
controller.assert_routing "/messages/1/comments/new", :action => 'new'
|
||||
controller.assert_routing "/messages/1/comments/1", :action => 'show', :id => '1'
|
||||
controller.assert_routing "/messages/1/comments/1;edit", :action => 'edit', :id => '1'
|
||||
controller.assert_routing "/messages/1/comments/1.xml", :action => 'show', :id => '1', :format => 'xml'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def with_restful_routing(resource, *args)
|
||||
with_routing do |set|
|
||||
set.draw { |map| map.resources(resource, *args) }
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
def assert_restful_routes_for(controller_name, options = {})
|
||||
(options[:options] ||= {})[:controller] = controller_name.to_s
|
||||
|
||||
with_options(options[:options]) do |controller|
|
||||
controller.assert_routing "/#{options[:path_prefix]}#{controller_name}", :action => 'index'
|
||||
controller.assert_routing "/#{options[:path_prefix]}#{controller_name}.xml" , :action => 'index', :format => 'xml'
|
||||
controller.assert_routing "/#{options[:path_prefix]}#{controller_name}/new", :action => 'new'
|
||||
controller.assert_routing "/#{options[:path_prefix]}#{controller_name}/1", :action => 'show', :id => '1'
|
||||
controller.assert_routing "/#{options[:path_prefix]}#{controller_name}/1;edit", :action => 'edit', :id => '1'
|
||||
controller.assert_routing "/#{options[:path_prefix]}#{controller_name}/1.xml", :action => 'show', :id => '1', :format => 'xml'
|
||||
end
|
||||
|
||||
assert_recognizes(
|
||||
options[:options].merge(:action => 'create'),
|
||||
{:path => "/#{options[:path_prefix]}#{controller_name}", :method => :post})
|
||||
|
||||
assert_recognizes(
|
||||
options[:options].merge(:action => 'update', :id => '1'),
|
||||
{:path => "/#{options[:path_prefix]}#{controller_name}/1", :method => :put})
|
||||
|
||||
assert_recognizes(
|
||||
options[:options].merge(:action => 'destroy', :id => '1'),
|
||||
{:path => "/#{options[:path_prefix]}#{controller_name}/1", :method => :delete})
|
||||
|
||||
yield options[:options] if block_given?
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user