snusnu-merb_resource_controller 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile CHANGED
@@ -200,84 +200,222 @@ how the controllers behave. Of course you are also free to override all the meth
200
200
  <pre>
201
201
  <code>
202
202
  def index
203
+ set_action_specific_provides(:index)
203
204
  load_resource
204
205
  display requested_resource
205
206
  end
206
207
 
207
208
  def show
209
+ set_action_specific_provides(:show)
208
210
  load_resource
209
211
  raise Merb::ControllerExceptions::NotFound unless requested_resource
210
212
  display requested_resource
211
213
  end
212
214
 
213
215
  def new
214
- only_provides :html
216
+ set_action_specific_provides(:new)
215
217
  load_resource
216
218
  set_member(new_member)
217
219
  display member
218
220
  end
219
221
 
220
222
  def edit
221
- only_provides :html
223
+ set_action_specific_provides(:edit)
222
224
  load_resource
223
225
  raise Merb::ControllerExceptions::NotFound unless requested_resource
224
226
  display requested_resource
225
227
  end
226
228
 
227
229
  def create
230
+ set_action_specific_provides(:create)
228
231
  load_resource
229
- set_member(new_member(params[member_name]))
232
+ set_member(new_member)
230
233
  if member.save
231
- options = flash_supported? ? { :message => successful_create_messages } : {}
232
- redirect redirect_on_successful_create, options
234
+ handle_successful_create
233
235
  else
234
- message.merge!(failed_create_messages) if flash_supported?
235
- render :new
236
+ handle_failed_create
236
237
  end
237
238
  end
238
239
 
239
240
  def update
241
+ set_action_specific_provides(:update)
240
242
  load_resource
241
243
  raise Merb::ControllerExceptions::NotFound unless requested_resource
242
244
  if requested_resource.update_attributes(params[member_name])
243
- options = flash_supported? ? { :message => successful_update_messages } : {}
244
- redirect redirect_on_successful_update, options
245
+ handle_successful_update
245
246
  else
246
- message.merge!(failed_update_messages) if flash_supported?
247
- display requested_resource, :edit
247
+ handle_failed_update
248
248
  end
249
249
  end
250
250
 
251
251
  def destroy
252
+ set_action_specific_provides(:destroy)
252
253
  load_resource
253
254
  raise Merb::ControllerExceptions::NotFound unless requested_resource
254
255
  if requested_resource.destroy
255
- options = flash_supported? ? { :message => successful_destroy_messages } : {}
256
- redirect redirect_on_successful_destroy, options
256
+ handle_successful_destroy
257
257
  else
258
- raise Merb::ControllerExceptions::InternalServerError
258
+ handle_failed_destroy
259
259
  end
260
260
  end
261
261
  </code>
262
262
  </pre>
263
263
 
264
- h2. Additional Helper Methods
264
+ h2. Additional Methods to override
265
+
266
+ In addition to the actions and the methods they are using, you will probably want to override some other methods that
267
+ @merb_resource_controller@ uses to do its thing. Here are some default method implementations you can override to this effect:
265
268
 
266
- In addition to the actions and the methods they are using, you will probably want to override the redirection targets
267
- These are the default method implementations you can override to this effect:
269
+ h3. Customize the create action
268
270
 
269
271
  <pre>
270
272
  <code>
273
+ def handle_successful_create
274
+ handle_content_type(:create, content_type, :success)
275
+ end
276
+
277
+ def handle_failed_create
278
+ handle_content_type(:create, content_type, :failure)
279
+ end
280
+
281
+
282
+ def html_response_on_successful_create
283
+ options = flash_messages_for?(:create) ? { :message => successful_create_messages } : {}
284
+ redirect redirect_on_successful_create, options
285
+ end
286
+
287
+ def html_response_on_failed_create
288
+ message.merge!(failed_create_messages) if flash_messages_for?(:create)
289
+ render :new, :status => 406
290
+ end
291
+
292
+
293
+ def xml_response_on_successful_create
294
+ display member, :status => 201, :location => resource(member)
295
+ end
296
+
297
+ def xml_response_on_failed_create
298
+ display member.errors, :status => 422
299
+ end
300
+
301
+
302
+ def json_response_on_successful_create
303
+ display member, :status => 201, :location => resource(member)
304
+ end
305
+
306
+ def json_response_on_failed_create
307
+ display member.errors, :status => 422
308
+ end
309
+
310
+
271
311
  def redirect_on_successful_create
272
312
  target = singleton_controller? ? member_name : member
273
313
  resource(*(has_parent? ? parents + [ target ] : [ target ]))
274
314
  end
275
315
 
316
+ # These are available by default (i.e. if all actions are available)
317
+ # If you specify specific actions inside the block passed to #controlling
318
+ # you need to pass the :flash option to the ResourceProxy#action like so:
319
+ # action :create, :flash => true
320
+
321
+ def successful_create_messages
322
+ { :notice => "#{member.class.name} was successfully created" }
323
+ end
324
+
325
+ def failed_create_messages
326
+ { :error => "Failed to create new #{member.class.name}" }
327
+ end
328
+ </code>
329
+ </pre>
330
+
331
+ h3. Customize the update action
332
+
333
+ <code>
334
+ <pre>
335
+ def handle_successful_update
336
+ handle_content_type(:update, content_type, :success)
337
+ end
338
+
339
+ def handle_failed_update
340
+ handle_content_type(:update, content_type, :failure)
341
+ end
342
+
343
+
344
+ def html_response_on_successful_update
345
+ options = flash_messages_for?(:update) ? { :message => successful_update_messages } : {}
346
+ redirect redirect_on_successful_update, options
347
+ end
348
+
349
+ def html_response_on_failed_update
350
+ message.merge!(failed_update_messages) if flash_messages_for?(:update)
351
+ display requested_resource, :edit, :status => 406
352
+ end
353
+
354
+
355
+ def xml_response_on_successful_update
356
+ "" # render no content, just 200 (OK) status.
357
+ end
358
+
359
+ def xml_response_on_failed_update
360
+ display member.errors, :status => 422
361
+ end
362
+
363
+
364
+ def json_response_on_successful_update
365
+ "" # render no content, just 200 (OK) status.
366
+ end
367
+
368
+ def json_response_on_failed_update
369
+ display member.errors, :status => 422
370
+ end
371
+
276
372
  def redirect_on_successful_update
277
373
  target = singleton_controller? ? member_name : member
278
374
  resource(*(has_parent? ? parents + [ target ] : [ target ]))
279
375
  end
280
376
 
377
+ # These are available by default (i.e. if all actions are available)
378
+ # If you specify specific actions inside the block passed to #controlling
379
+ # you need to pass the :flash option to the ResourceProxy#action like so:
380
+ # action :update, :flash => true
381
+
382
+ def successful_update_messages
383
+ { :notice => "#{member.class.name} was successfully updated" }
384
+ end
385
+
386
+ def failed_update_messages
387
+ { :error => "Failed to update #{member.class.name}" }
388
+ end
389
+ </code>
390
+ </pre>
391
+
392
+ h3. Customize the destroy action
393
+
394
+ <code>
395
+ <pre>
396
+ def handle_successful_destroy
397
+ handle_content_type(:destroy, content_type, :success)
398
+ end
399
+
400
+ def handle_failed_destroy
401
+ raise Merb::ControllerExceptions::InternalServerError
402
+ end
403
+
404
+
405
+ def html_response_on_successful_destroy
406
+ options = flash_messages_for?(:destroy) ? { :message => successful_destroy_messages } : {}
407
+ redirect redirect_on_successful_destroy, options
408
+ end
409
+
410
+ def xml_response_on_successful_destroy
411
+ "" # render no content, just 200 (OK) status.
412
+ end
413
+
414
+ def json_response_on_successful_destroy
415
+ "" # render no content, just 200 (OK) status.
416
+ end
417
+
418
+
281
419
  def redirect_on_successful_destroy
282
420
  if singleton_controller?
283
421
  has_parent? ? resource(parent) : '/'
@@ -285,6 +423,19 @@ def redirect_on_successful_destroy
285
423
  resource(*(has_parent? ? parents + [ collection_name ] : [ collection_name ]))
286
424
  end
287
425
  end
426
+
427
+ # These are available by default (i.e. if all actions are available)
428
+ # If you specify specific actions inside the block passed to #controlling
429
+ # you need to pass the :flash option to the ResourceProxy#action like so:
430
+ # action :destroy, :flash => true
431
+
432
+ def successful_destroy_messages
433
+ { :notice => "#{member.class.name} was successfully destroyed" }
434
+ end
435
+
436
+ def failed_destroy_messages
437
+ { :error => "Failed to destroy #{member.class.name}" }
438
+ end
288
439
  </code>
289
440
  </pre>
290
441
 
@@ -301,6 +452,5 @@ behaves under the given circumstances.
301
452
  h2. TODO
302
453
 
303
454
  # Infer route nesting strategy from @Merb::Router@
304
- # Customizable support for @provides@ and @only_provides@
305
455
  # Support for user stamps (aka created_by and updated_by)
306
456
  # Support for pagination once an _official_ merb pagination solution exists
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require 'merb-core/tasks/merb'
6
6
  require "spec/rake/spectask"
7
7
 
8
8
  GEM_NAME = "merb_resource_controller"
9
- GEM_VERSION = "0.1.0"
9
+ GEM_VERSION = "0.2.0"
10
10
  AUTHOR = "Martin Gamsjaeger"
11
11
  EMAIL = "gamsnjaga@gmail.com"
12
12
  HOMEPAGE = "http://merbivore.com/"
data/TODO CHANGED
@@ -2,6 +2,5 @@ TODO
2
2
  ====
3
3
 
4
4
  # Infer route nesting strategy from @Merb::Router@
5
- # Customizable support for @provides@ and @only_provides@
6
5
  # Support for user stamps (aka created_by and updated_by)
7
6
  # Support for pagination once an _official_ merb pagination solution exists
@@ -11,9 +11,10 @@ if defined?(Merb::Plugins)
11
11
  Merb::BootLoader.before_app_loads do
12
12
  # require code that must be loaded before the application
13
13
  mrc = File.join(File.dirname(__FILE__), 'merb_resource_controller')
14
- require mrc / 'resource_proxy'
15
14
  require mrc / 'actions'
15
+ require mrc / 'action_descriptor'
16
16
  require mrc / 'resource_controller'
17
+ require mrc / 'resource_proxy'
17
18
  if Merb::Plugins.config[:merb_resource_controller][:identity_map]
18
19
  require mrc / 'identity_map_support'
19
20
  end
@@ -0,0 +1,107 @@
1
+ module Merb
2
+ module ResourceController
3
+
4
+
5
+ FLASH_SUPPORT_MODULE = "FlashSupport"
6
+
7
+ def self.action_module(action)
8
+ Actions.const_get(Extlib::Inflection.classify(action))
9
+ end
10
+
11
+
12
+ class ActionDescriptor
13
+
14
+ SUPPORTED_SCENARIOS = [ :success, :failure ]
15
+ FORMAT_RESTRICTION_APIS = [ :provides, :only_provides, :does_not_provide ]
16
+
17
+ attr_reader :action_name, :provided_formats, :options
18
+
19
+ def initialize(name, provided_formats, options = {})
20
+ raise unless valid_format_restriction?(options)
21
+ @action_name, @provided_formats, @options = name.to_sym, provided_formats, options
22
+ @content_type_handlers = { :success => {}, :failure => {} }
23
+ handle_default_content_types if options[:default_formats]
24
+ end
25
+
26
+
27
+ def action_module
28
+ Merb::ResourceController.action_module(@action_name)
29
+ end
30
+
31
+
32
+ def supports_flash_messages?
33
+ !!@options[:flash] && has_flash_module?
34
+ end
35
+
36
+ def has_flash_module?
37
+ !flash_module.nil?
38
+ end
39
+
40
+ def flash_module
41
+ action_module.const_get Merb::ResourceController::FLASH_SUPPORT_MODULE
42
+ rescue
43
+ nil
44
+ end
45
+
46
+
47
+ def handle_default_content_types
48
+ handle
49
+ end
50
+
51
+ def handle(format = nil, scenario = nil)
52
+ raise "#{scenario} is not supported" unless SUPPORTED_SCENARIOS.include?(scenario) || scenario.nil?
53
+ (format ? format.is_a?(Array) ? format : [ format ] : @provided_formats).each do |f|
54
+ (scenario ? [ scenario ] : SUPPORTED_SCENARIOS).each do |s|
55
+ @content_type_handlers[s][f] = content_type_handler_method(f, s)
56
+ end
57
+ end
58
+ end
59
+
60
+ def content_type_handler(format, scenario)
61
+ @content_type_handlers[scenario.to_sym][format.to_sym]
62
+ end
63
+
64
+ def content_type_handler_method(format, scenario)
65
+ case scenario
66
+ when :success then "#{format}_response_on_successful_#{@action_name}"
67
+ when :failure then "#{format}_response_on_failed_#{@action_name}"
68
+ else
69
+ raise "default_content_type_handler doesn't support #{scenario.inspect} (only :on_success and :on_failure)"
70
+ end
71
+ end
72
+
73
+
74
+ def valid_format_restriction?(options = nil)
75
+ format_restriction(options || @options).size <= 1 # empty is valid too
76
+ end
77
+
78
+ def has_format_restriction?(options = nil)
79
+ format_restriction(options).size > 0
80
+ end
81
+
82
+ def format_restrictor_method(options = nil)
83
+ o = options || @options
84
+ lambda { send(format_restriction_api(o), restricted_formats(o)) }
85
+ end
86
+
87
+
88
+ def format_restriction_api(options = nil)
89
+ format_restriction(options || @options).map { |kv| kv[0] }.first
90
+ end
91
+
92
+ def restricted_formats(options = nil)
93
+ format_restriction(options || @options).map { |kv| kv[1] }.flatten
94
+ end
95
+
96
+ def format_restriction(options = nil)
97
+ (options || @options).select { |k,v| format_restriction_apis.include?(k) }
98
+ end
99
+
100
+ def format_restriction_apis
101
+ FORMAT_RESTRICTION_APIS
102
+ end
103
+
104
+ end
105
+
106
+ end
107
+ end
@@ -11,6 +11,7 @@ module Merb
11
11
  # end
12
12
 
13
13
  def index
14
+ set_action_specific_provides(:index)
14
15
  load_resource
15
16
  display requested_resource
16
17
  end
@@ -26,6 +27,7 @@ module Merb
26
27
  # end
27
28
 
28
29
  def show
30
+ set_action_specific_provides(:show)
29
31
  load_resource
30
32
  raise Merb::ControllerExceptions::NotFound unless requested_resource
31
33
  display requested_resource
@@ -42,7 +44,7 @@ module Merb
42
44
  # end
43
45
 
44
46
  def new
45
- only_provides :html
47
+ set_action_specific_provides(:new)
46
48
  load_resource
47
49
  set_member(new_member)
48
50
  display member
@@ -60,7 +62,7 @@ module Merb
60
62
  # end
61
63
 
62
64
  def edit
63
- only_provides :html
65
+ set_action_specific_provides(:edit)
64
66
  load_resource
65
67
  raise Merb::ControllerExceptions::NotFound unless requested_resource
66
68
  display requested_resource
@@ -81,22 +83,75 @@ module Merb
81
83
  # end
82
84
 
83
85
  def create
86
+ set_action_specific_provides(:create)
84
87
  load_resource
85
88
  set_member(new_member)
86
89
  if member.save
87
- options = flash_supported? ? { :message => successful_create_messages } : {}
88
- redirect redirect_on_successful_create, options
90
+ handle_successful_create
89
91
  else
90
- message.merge!(failed_create_messages) if flash_supported?
91
- render :new, :status => 406
92
+ handle_failed_create
92
93
  end
93
94
  end
94
95
 
96
+ protected
97
+
98
+ def handle_successful_create
99
+ handle_content_type(:create, content_type, :success)
100
+ end
101
+
102
+ def handle_failed_create
103
+ handle_content_type(:create, content_type, :failure)
104
+ end
105
+
106
+
107
+ def html_response_on_successful_create
108
+ options = flash_messages_for?(:create) ? { :message => successful_create_messages } : {}
109
+ redirect redirect_on_successful_create, options
110
+ end
111
+
112
+ def html_response_on_failed_create
113
+ message.merge!(failed_create_messages) if flash_messages_for?(:create)
114
+ render :new, :status => 406
115
+ end
116
+
117
+
118
+ def xml_response_on_successful_create
119
+ display member, :status => 201, :location => resource(member)
120
+ end
121
+
122
+ def xml_response_on_failed_create
123
+ display member.errors, :status => 422
124
+ end
125
+
126
+
127
+ def json_response_on_successful_create
128
+ display member, :status => 201, :location => resource(member)
129
+ end
130
+
131
+ def json_response_on_failed_create
132
+ display member.errors, :status => 422
133
+ end
134
+
135
+
95
136
  def redirect_on_successful_create
96
137
  target = singleton_controller? ? member_name : member
97
138
  resource(*(has_parent? ? parents + [ target ] : [ target ]))
98
139
  end
99
140
 
141
+ module FlashSupport
142
+
143
+ protected
144
+
145
+ def successful_create_messages
146
+ { :notice => "#{member.class.name} was successfully created" }
147
+ end
148
+
149
+ def failed_create_messages
150
+ { :error => "Failed to create new #{member.class.name}" }
151
+ end
152
+
153
+ end
154
+
100
155
  end
101
156
 
102
157
  module Update
@@ -112,22 +167,74 @@ module Merb
112
167
  # end
113
168
 
114
169
  def update
170
+ set_action_specific_provides(:update)
115
171
  load_resource
116
172
  raise Merb::ControllerExceptions::NotFound unless requested_resource
117
173
  if requested_resource.update_attributes(params[member_name])
118
- options = flash_supported? ? { :message => successful_update_messages } : {}
119
- redirect redirect_on_successful_update, options
174
+ handle_successful_update
120
175
  else
121
- message.merge!(failed_update_messages) if flash_supported?
122
- display requested_resource, :edit, :status => 406
176
+ handle_failed_update
123
177
  end
124
178
  end
125
179
 
180
+ protected
181
+
182
+ def handle_successful_update
183
+ handle_content_type(:update, content_type, :success)
184
+ end
185
+
186
+ def handle_failed_update
187
+ handle_content_type(:update, content_type, :failure)
188
+ end
189
+
190
+
191
+ def html_response_on_successful_update
192
+ options = flash_messages_for?(:update) ? { :message => successful_update_messages } : {}
193
+ redirect redirect_on_successful_update, options
194
+ end
195
+
196
+ def html_response_on_failed_update
197
+ message.merge!(failed_update_messages) if flash_messages_for?(:update)
198
+ display requested_resource, :edit, :status => 406
199
+ end
200
+
201
+
202
+ def xml_response_on_successful_update
203
+ "" # render no content, just 200 (OK) status.
204
+ end
205
+
206
+ def xml_response_on_failed_update
207
+ display member.errors, :status => 422
208
+ end
209
+
210
+
211
+ def json_response_on_successful_update
212
+ "" # render no content, just 200 (OK) status.
213
+ end
214
+
215
+ def json_response_on_failed_update
216
+ display member.errors, :status => 422
217
+ end
218
+
126
219
  def redirect_on_successful_update
127
220
  target = singleton_controller? ? member_name : member
128
221
  resource(*(has_parent? ? parents + [ target ] : [ target ]))
129
222
  end
130
223
 
224
+ module FlashSupport
225
+
226
+ protected
227
+
228
+ def successful_update_messages
229
+ { :notice => "#{member.class.name} was successfully updated" }
230
+ end
231
+
232
+ def failed_update_messages
233
+ { :error => "Failed to update #{member.class.name}" }
234
+ end
235
+
236
+ end
237
+
131
238
  end
132
239
 
133
240
  module Destroy
@@ -143,16 +250,41 @@ module Merb
143
250
  # end
144
251
 
145
252
  def destroy
253
+ set_action_specific_provides(:destroy)
146
254
  load_resource
147
255
  raise Merb::ControllerExceptions::NotFound unless requested_resource
148
256
  if requested_resource.destroy
149
- options = flash_supported? ? { :message => successful_destroy_messages } : {}
150
- redirect redirect_on_successful_destroy, options
257
+ handle_successful_destroy
151
258
  else
152
- raise Merb::ControllerExceptions::InternalServerError
259
+ handle_failed_destroy
153
260
  end
154
261
  end
155
262
 
263
+ protected
264
+
265
+ def handle_successful_destroy
266
+ handle_content_type(:destroy, content_type, :success)
267
+ end
268
+
269
+ def handle_failed_destroy
270
+ raise Merb::ControllerExceptions::InternalServerError
271
+ end
272
+
273
+
274
+ def html_response_on_successful_destroy
275
+ options = flash_messages_for?(:destroy) ? { :message => successful_destroy_messages } : {}
276
+ redirect redirect_on_successful_destroy, options
277
+ end
278
+
279
+ def xml_response_on_successful_destroy
280
+ "" # render no content, just 200 (OK) status.
281
+ end
282
+
283
+ def json_response_on_successful_destroy
284
+ "" # render no content, just 200 (OK) status.
285
+ end
286
+
287
+
156
288
  def redirect_on_successful_destroy
157
289
  if singleton_controller?
158
290
  has_parent? ? resource(parent) : '/'
@@ -161,6 +293,20 @@ module Merb
161
293
  end
162
294
  end
163
295
 
296
+ module FlashSupport
297
+
298
+ protected
299
+
300
+ def successful_destroy_messages
301
+ { :notice => "#{member.class.name} was successfully destroyed" }
302
+ end
303
+
304
+ def failed_destroy_messages
305
+ { :error => "Failed to destroy #{member.class.name}" }
306
+ end
307
+
308
+ end
309
+
164
310
  end
165
311
 
166
312
  end