Changeset 5701
- Timestamp:
- 12/07/06 15:36:08 (1 year ago)
- Files:
-
- trunk/actionpack/CHANGELOG (modified) (1 diff)
- trunk/actionpack/lib/action_controller/resources.rb (modified) (5 diffs)
- trunk/actionpack/test/controller/resources_test.rb (modified) (6 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/actionpack/CHANGELOG
r5696 r5701 1 1 *SVN* 2 3 * Add singleton resources. [Rick Olson] 4 5 map.resource :account 6 7 GET /account 8 GET /account;edit 9 UPDATE /account 10 DELEE /account 2 11 3 12 * respond_to recognizes JSON. render :json => @person.to_json automatically sets the content type and takes a :callback option to specify a client-side function to call using the rendered JSON as an argument. #4185 [Scott Raymond, eventualbuddha] trunk/actionpack/lib/action_controller/resources.rb
r5195 r5701 19 19 20 20 def controller 21 (options[:controller] || plural).to_s21 @controller ||= (options[:controller] || plural).to_s 22 22 end 23 23 24 24 def path 25 "#{path_prefix}/#{plural}"25 @path ||= "#{path_prefix}/#{plural}" 26 26 end 27 27 28 28 def new_path 29 "#{path}/new"29 @new_path ||= "#{path}/new" 30 30 end 31 31 32 32 def member_path 33 "#{path}/:id"33 @member_path ||= "#{path}/:id" 34 34 end 35 35 36 36 def nesting_path_prefix 37 "#{path}/:#{singular}_id"38 end 39 40 pr ivate37 @nesting_path_prefix ||= "#{path}/:#{singular}_id" 38 end 39 40 protected 41 41 def arrange_actions 42 42 @collection_methods = arrange_actions_by_methods(options.delete(:collection)) … … 66 66 end 67 67 end 68 68 69 class SingletonResource < Resource #:nodoc: 70 def initialize(entity, options) 71 @singular = entity 72 @plural = options[:plural] || singular.to_s.pluralize 73 74 @options = options 75 arrange_actions 76 add_default_actions 77 set_prefixes 78 end 79 80 def controller 81 @controller ||= (options[:controller] || singular).to_s 82 end 83 84 def path 85 @path ||= "#{path_prefix}/#{singular}" 86 end 87 88 def new_path 89 nil 90 end 91 92 alias_method :member_path, :path 93 alias_method :nesting_path_prefix, :path 94 end 95 69 96 # Creates named routes for implementing verb-oriented controllers. This is 70 97 # useful for implementing REST API's, where a single resource has different … … 210 237 end 211 238 239 # Creates named routes for implementing verb-oriented controllers for a singleton resource. 240 # A singleton resource is global to the current user visiting the application, such as a user's 241 # /account profile. 242 # 243 # See map.resources for general conventions. These are the main differences: 244 # - a singular name is given to map.resource. The default controller name is taken from the singular name. 245 # - To specify a custom plural name, use the :plural option. There is no :singular option 246 # - No default index, new, or create routes are created for the singleton resource controller. 247 # - When nesting singleton resources, only the singular name is used as the path prefix (example: 'account/messages/1') 248 # 249 # Example: 250 # 251 # map.resource :account 252 # 253 # class AccountController < ActionController::Base 254 # # GET account_url 255 # def show 256 # # find and return a specific message 257 # end 258 # 259 # # GET edit_account_url 260 # def edit 261 # # return an HTML form for editing a specific message 262 # end 263 # 264 # # PUT account_url 265 # def update 266 # # find and update a specific message 267 # end 268 # 269 # # DELETE account_url 270 # def destroy 271 # # delete a specific message 272 # end 273 # end 274 # 275 # Along with the routes themselves, #resource generates named routes for use in 276 # controllers and views. <tt>map.resource :account</tt> produces the following named routes and helpers: 277 # 278 # Named Route Helpers 279 # account account_url(id), hash_for_account_url(id), 280 # account_path(id), hash_for_account_path(id) 281 # edit_account edit_account_url(id), hash_for_edit_account_url(id), 282 # edit_account_path(id), hash_for_edit_account_path(id) 283 def resource(*entities, &block) 284 options = entities.last.is_a?(Hash) ? entities.pop : { } 285 entities.each { |entity| map_singleton_resource entity, options.dup, &block } 286 end 287 212 288 private 213 289 def map_resource(entities, options = {}, &block) … … 221 297 if block_given? 222 298 with_options(:path_prefix => resource.nesting_path_prefix, &block) 299 end 300 end 301 end 302 303 def map_singleton_resource(entities, options = {}, &block) 304 resource = SingletonResource.new(entities, options) 305 306 with_options :controller => resource.controller do |map| 307 map_member_actions(map, resource) 308 309 if block_given? 310 with_options(:path_prefix => resource.singular, &block) 223 311 end 224 312 end … … 285 373 map.connect("#{resource.member_path}.:format", :action => "destroy", :conditions => { :method => :delete }) 286 374 end 287 375 288 376 def requirements_for(method) 289 377 method == :any ? {} : { :conditions => { :method => method } } trunk/actionpack/test/controller/resources_test.rb
r5195 r5701 3 3 class ResourcesController < ActionController::Base 4 4 def index() render :nothing => true end 5 alias_method :show, :index 5 6 def rescue_action(e) raise e end 6 7 end … … 10 11 class CommentsController < ResourcesController; end 11 12 13 class AccountController < ResourcesController; end 14 class AdminController < ResourcesController; end 12 15 13 16 class ResourcesTest < Test::Unit::TestCase … … 173 176 end 174 177 178 def test_should_create_singleton_resource_routes 179 with_singleton_resources :account do 180 assert_singleton_restful_for :account 181 end 182 end 183 184 def test_should_create_multiple_singleton_resource_routes 185 with_singleton_resources :account, :admin do 186 assert_singleton_restful_for :account 187 assert_singleton_restful_for :admin 188 end 189 end 190 191 def test_should_create_nested_singleton_resource_routes 192 with_routing do |set| 193 set.draw do |map| 194 map.resource :admin do |admin| 195 admin.resource :account 196 end 197 end 198 199 assert_singleton_restful_for :admin 200 assert_singleton_restful_for :account, :path_prefix => 'admin/' 201 end 202 end 203 204 def test_singleton_resource_with_member_action 205 [:put, :post].each do |method| 206 with_singleton_resources :account, :member => { :reset => method } do 207 reset_options = {:action => 'reset'} 208 reset_path = "/account;reset" 209 assert_singleton_routes_for :account do |options| 210 assert_recognizes(options.merge(reset_options), :path => reset_path, :method => method) 211 end 212 213 assert_singleton_named_routes_for :account do |options| 214 assert_named_route reset_path, :reset_account_path, reset_options 215 end 216 end 217 end 218 end 219 220 def test_singleton_resource_with_two_member_actions_with_same_method 221 [:put, :post].each do |method| 222 with_singleton_resources :account, :member => { :reset => method, :disable => method } do 223 %w(reset disable).each do |action| 224 action_options = {:action => action} 225 action_path = "/account;#{action}" 226 assert_singleton_routes_for :account do |options| 227 assert_recognizes(options.merge(action_options), :path => action_path, :method => method) 228 end 229 230 assert_singleton_named_routes_for :account do |options| 231 assert_named_route action_path, "#{action}_account_path".to_sym, action_options 232 end 233 end 234 end 235 end 236 end 237 175 238 protected 176 239 def with_restful_routing(*args) … … 180 243 end 181 244 end 245 246 def with_singleton_resources(*args) 247 with_routing do |set| 248 set.draw { |map| map.resource(*args) } 249 yield 250 end 251 end 182 252 183 253 # runs assert_restful_routes_for and assert_restful_named_routes for on the controller_name and options, without passing a block. … … 185 255 assert_restful_routes_for controller_name, options 186 256 assert_restful_named_routes_for controller_name, options 257 end 258 259 def assert_singleton_restful_for(singleton_name, options = {}) 260 assert_singleton_routes_for singleton_name, options 261 assert_singleton_named_routes_for singleton_name, options 187 262 end 188 263 … … 243 318 end 244 319 320 def assert_singleton_routes_for(singleton_name, options = {}) 321 (options[:options] ||= {})[:controller] ||= singleton_name.to_s 322 323 full_path = "/#{options[:path_prefix]}#{singleton_name}" 324 with_options options[:options] do |controller| 325 controller.assert_routing full_path, :action => 'show' 326 controller.assert_routing "#{full_path}.xml", :action => 'show', :format => 'xml' 327 end 328 329 assert_recognizes(options[:options].merge(:action => 'update'), :path => full_path, :method => :put) 330 assert_recognizes(options[:options].merge(:action => 'destroy'), :path => full_path, :method => :delete) 331 332 yield options[:options] if block_given? 333 end 334 335 def assert_singleton_named_routes_for(singleton_name, options = {}) 336 (options[:options] ||= {})[:controller] ||= singleton_name.to_s 337 @controller = "#{options[:options][:controller].camelize}Controller".constantize.new 338 @request = ActionController::TestRequest.new 339 @response = ActionController::TestResponse.new 340 get :show, options[:options] 341 options[:options].delete :action 342 343 full_path = "/#{options[:path_prefix]}#{singleton_name}" 344 345 assert_named_route "#{full_path}", "#{singleton_name}_path", options[:options] 346 assert_named_route "#{full_path}.xml", "formatted_#{singleton_name}_path", options[:options].merge(:format => 'xml') 347 end 348 245 349 def assert_named_route(expected, route, options) 246 350 actual = @controller.send(route, options) rescue $!.class.name