make_resourceful 1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +31 -0
- data/Readme.rdoc +229 -0
- data/VERSION +1 -0
- data/lib/make_resourceful.rb +11 -0
- data/lib/resourceful/base.rb +63 -0
- data/lib/resourceful/builder.rb +405 -0
- data/lib/resourceful/default/accessors.rb +418 -0
- data/lib/resourceful/default/actions.rb +101 -0
- data/lib/resourceful/default/callbacks.rb +51 -0
- data/lib/resourceful/default/responses.rb +118 -0
- data/lib/resourceful/default/urls.rb +136 -0
- data/lib/resourceful/generators/resourceful_scaffold/resourceful_scaffold_generator.rb +87 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/controller.rb +5 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/fixtures.yml +10 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/functional_test.rb +50 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/helper.rb +2 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/migration.rb +13 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/model.rb +2 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/unit_test.rb +7 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/view__form.haml +5 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/view_edit.haml +11 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/view_index.haml +5 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/view_new.haml +9 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/view_partial.haml +12 -0
- data/lib/resourceful/generators/resourceful_scaffold/templates/view_show.haml +14 -0
- data/lib/resourceful/maker.rb +92 -0
- data/lib/resourceful/response.rb +33 -0
- data/lib/resourceful/serialize.rb +185 -0
- data/spec/accessors_spec.rb +474 -0
- data/spec/actions_spec.rb +310 -0
- data/spec/base_spec.rb +12 -0
- data/spec/builder_spec.rb +332 -0
- data/spec/callbacks_spec.rb +71 -0
- data/spec/integration_spec.rb +394 -0
- data/spec/maker_spec.rb +91 -0
- data/spec/response_spec.rb +37 -0
- data/spec/responses_spec.rb +314 -0
- data/spec/serialize_spec.rb +133 -0
- data/spec/urls_spec.rb +282 -0
- metadata +97 -0
@@ -0,0 +1,418 @@
|
|
1
|
+
module Resourceful
|
2
|
+
# This module contains various methods
|
3
|
+
# that are available from actions and callbacks.
|
4
|
+
# Default::Accessors and Default::URLs are the most useful to users;
|
5
|
+
# the rest are mostly used internally.
|
6
|
+
#
|
7
|
+
# However, if you want to poke around the internals a little,
|
8
|
+
# check out Default::Actions, which has the default Action definitions,
|
9
|
+
# and Default::Responses.included, which defines the default response_for[link:classes/Resourceful/Builder.html#M000061] blocks.
|
10
|
+
module Default
|
11
|
+
# This module contains all sorts of useful methods
|
12
|
+
# that allow access to the resources being worked with,
|
13
|
+
# metadata about the controller and action,
|
14
|
+
# and so forth.
|
15
|
+
#
|
16
|
+
# Many of these accessors call other accessors
|
17
|
+
# and are called by the default make_resourceful actions[link:classes/Resourceful/Default/Actions.html].
|
18
|
+
# This means that overriding one method
|
19
|
+
# can affect everything else.
|
20
|
+
#
|
21
|
+
# This can be dangerous, but it can also be very powerful.
|
22
|
+
# make_resourceful is designed to take advantage of overriding,
|
23
|
+
# so as long as the new methods accomplish the same purpose as the old ones,
|
24
|
+
# everything will just work.
|
25
|
+
# Even if you make a small mistake,
|
26
|
+
# it's hard to break the controller in any unexpected ways.
|
27
|
+
#
|
28
|
+
# For example, suppose your controller is called TagsController,
|
29
|
+
# but your model is called PhotoTag.
|
30
|
+
# All you have to do is override current_model_name:
|
31
|
+
#
|
32
|
+
# def current_model_name
|
33
|
+
# "PhotoTag"
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# Then current_model will return the PhotoTag model,
|
37
|
+
# current_object will call <tt>PhotoTag.find</tt>,
|
38
|
+
# and so forth.
|
39
|
+
#
|
40
|
+
# Overriding current_objects and current_object is particularly useful
|
41
|
+
# for providing customized model lookup logic.
|
42
|
+
module Accessors
|
43
|
+
# Returns an array of all the objects of the model corresponding to the controller.
|
44
|
+
# For UsersController, it essentially runs <tt>User.find(:all)</tt>.
|
45
|
+
#
|
46
|
+
# However, there are a few important differences.
|
47
|
+
# First, this method caches is results in the <tt>@current_objects</tt> instance variable.
|
48
|
+
# That way, multiple calls won't run multiple queries.
|
49
|
+
#
|
50
|
+
# Second, this method uses the current_model accessor,
|
51
|
+
# which provides a lot of flexibility
|
52
|
+
# (see the documentation for current_model for details).
|
53
|
+
def current_objects
|
54
|
+
@current_objects ||= current_model.find(:all)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Calls current_objects and stores
|
58
|
+
# the result in an instance variable
|
59
|
+
# named after the controller.
|
60
|
+
#
|
61
|
+
# This is called automatically by the default make_resourceful actions.
|
62
|
+
# You shouldn't need to use it directly unless you're creating a new action.
|
63
|
+
#
|
64
|
+
# For example, in UsersController,
|
65
|
+
# calling +load_objects+ sets <tt>@users = current_objects</tt>.
|
66
|
+
def load_objects
|
67
|
+
instance_variable_set("@#{instance_variable_name}", current_objects)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns the object referenced by the id parameter
|
71
|
+
# (or the newly-created object for the +new+ and +create+ actions).
|
72
|
+
# For UsersController, it essentially runs <tt>User.find(params[:id])</tt>.
|
73
|
+
#
|
74
|
+
# However, there are a few important differences.
|
75
|
+
# First, this method caches is results in the <tt>@current_objects</tt> instance variable.
|
76
|
+
# That way, multiple calls won't run multiple queries.
|
77
|
+
#
|
78
|
+
# Second, this method uses the current_model accessor,
|
79
|
+
# which provides a lot of flexibility
|
80
|
+
# (see the documentation for current_model for details).
|
81
|
+
#
|
82
|
+
# Note that this is different for a singleton controller,
|
83
|
+
# where there's only one resource per parent resource.
|
84
|
+
# Then this just returns that resource.
|
85
|
+
# For example, if Person has_one Hat,
|
86
|
+
# then in HatsController current_object essentially runs <tt>Person.find(params[:person_id]).hat</tt>.
|
87
|
+
def current_object
|
88
|
+
@current_object ||= if !parent? || plural?
|
89
|
+
current_model.find(params[:id]) if params[:id]
|
90
|
+
else
|
91
|
+
parent_object.send(instance_variable_name.singularize)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
# Calls current_object and stores
|
97
|
+
# the result in an instance variable
|
98
|
+
# named after the controller.
|
99
|
+
#
|
100
|
+
# This is called automatically by the default make_resourceful actions.
|
101
|
+
# You shouldn't need to use it directly unless you're creating a new action.
|
102
|
+
#
|
103
|
+
# For example, in UsersController,
|
104
|
+
# calling +load_object+ sets <tt>@user = current_object</tt>.
|
105
|
+
def load_object
|
106
|
+
instance_variable_set("@#{instance_variable_name.singularize}", current_object)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Creates a new object of the type of the current model
|
110
|
+
# with the current object's parameters.
|
111
|
+
# +current_object+ then returns this object for this action
|
112
|
+
# instead of looking up a new object.
|
113
|
+
#
|
114
|
+
# This is called automatically by the default make_resourceful actions.
|
115
|
+
# You shouldn't need to use it directly unless you're creating a new action.
|
116
|
+
#
|
117
|
+
# Note that if a parent object exists,
|
118
|
+
# the newly created object will automatically be a child of the parent object.
|
119
|
+
# For example, on POST /people/4/things,
|
120
|
+
#
|
121
|
+
# build_object
|
122
|
+
# current_object.person.id #=> 4
|
123
|
+
#
|
124
|
+
def build_object
|
125
|
+
@current_object = if current_model.respond_to? :build
|
126
|
+
current_model.build(object_parameters)
|
127
|
+
else
|
128
|
+
current_model.new(object_parameters).tap do |obj|
|
129
|
+
if singular? && parent?
|
130
|
+
obj.send("#{parent_name}_id=", parent_object.id)
|
131
|
+
obj.send("#{parent_name}_type=", parent_object.class.to_s) if polymorphic_parent?
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def namespaced_model_name
|
138
|
+
[self.class.model_namespace, current_model_name].compact.join('::')
|
139
|
+
end
|
140
|
+
|
141
|
+
# The string name of the current model.
|
142
|
+
# By default, this is derived from the name of the controller.
|
143
|
+
def current_model_name
|
144
|
+
controller_name.singularize.camelize
|
145
|
+
end
|
146
|
+
|
147
|
+
# An array of namespaces under which the current controller is.
|
148
|
+
# For example, in Admin::Content::PagesController:
|
149
|
+
#
|
150
|
+
# namespaces #=> [:admin, :content]
|
151
|
+
#
|
152
|
+
def namespaces
|
153
|
+
@namespaces ||= self.class.name.split('::').slice(0...-1).map(&:underscore).map(&:to_sym)
|
154
|
+
end
|
155
|
+
|
156
|
+
# The name of the instance variable that load_object and load_objects should assign to.
|
157
|
+
def instance_variable_name
|
158
|
+
controller_name
|
159
|
+
end
|
160
|
+
|
161
|
+
# The class of the current model.
|
162
|
+
# Note that if a parent object exists,
|
163
|
+
# this instead returns the association object.
|
164
|
+
# For example, in HatsController where Person has_many :hats,
|
165
|
+
#
|
166
|
+
# current_model #=> Person.find(params[:person_id]).hats
|
167
|
+
#
|
168
|
+
# This is useful because the association object uses duck typing
|
169
|
+
# to act like a model class.
|
170
|
+
# It supplies a find method that's automatically scoped
|
171
|
+
# to ensure that the object returned is actually a child of the parent,
|
172
|
+
# and so forth.
|
173
|
+
def current_model
|
174
|
+
if !parent? || singular?
|
175
|
+
namespaced_model_name.constantize
|
176
|
+
else
|
177
|
+
parent_object.send(instance_variable_name)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# Returns the hash passed as HTTP parameters
|
182
|
+
# that defines the new (or updated) attributes
|
183
|
+
# of the current object.
|
184
|
+
# This is only meaningful for +create+ or +update+.
|
185
|
+
def object_parameters
|
186
|
+
params[namespaced_model_name.underscore.tr('/', '_')]
|
187
|
+
end
|
188
|
+
|
189
|
+
# Returns a list of the names of all the potential parents of the current model.
|
190
|
+
# For a non-nested controller, this is <tt>[]</tt>.
|
191
|
+
# For example, in HatsController where Rack has_many :hats and Person has_many :hats,
|
192
|
+
#
|
193
|
+
# parents #=> ["rack", "person"]
|
194
|
+
#
|
195
|
+
# Note that the parents must be declared via Builder#belongs_to.
|
196
|
+
def parent_names
|
197
|
+
self.class.parents
|
198
|
+
end
|
199
|
+
|
200
|
+
# Returns true if an appropriate parent id parameter has been supplied.
|
201
|
+
# For example, in HatsController where Rack has_many :hats and Person has_many :hats,
|
202
|
+
# if <tt>params[:rack_id]</tt> or <tt>params[:person_id]</tt> is given,
|
203
|
+
#
|
204
|
+
# parent? #=> true
|
205
|
+
#
|
206
|
+
# Otherwise, if both <tt>params[:rack_id]</tt> and <tt>params[:rack_id]</tt> are nil,
|
207
|
+
#
|
208
|
+
# parent? #=> false
|
209
|
+
#
|
210
|
+
# Note that parents must be declared via Builder#belongs_to.
|
211
|
+
def parent?
|
212
|
+
!!parent_name
|
213
|
+
end
|
214
|
+
|
215
|
+
# Returns true if no parent id parameter can be found _and_ a belongs_to
|
216
|
+
# relationship on this controller was declared with a parent for shallow
|
217
|
+
# routing.
|
218
|
+
def shallow?
|
219
|
+
self.class.shallow_parent &&
|
220
|
+
(parent_name.nil? || parent_name == self.class.shallow_parent)
|
221
|
+
end
|
222
|
+
|
223
|
+
# Returns whether the parent (if it exists) is polymorphic
|
224
|
+
def polymorphic_parent?
|
225
|
+
!!polymorphic_parent_name
|
226
|
+
end
|
227
|
+
|
228
|
+
# Returns the name of the current parent object if a parent id is given,
|
229
|
+
# or nil otherwise. For example, in HatsController where Rack has_many
|
230
|
+
# :hats and Person has_many :hats, if <tt>params[:rack_id]</tt> is
|
231
|
+
# given,
|
232
|
+
#
|
233
|
+
# parent_name #=> "rack"
|
234
|
+
#
|
235
|
+
# If <tt>params[:person_id]</tt> is given,
|
236
|
+
#
|
237
|
+
# parent_name #=> "person"
|
238
|
+
#
|
239
|
+
# If both <tt>params[:rack_id]</tt> and <tt>params[:person_id]</tt> are
|
240
|
+
# nil,
|
241
|
+
#
|
242
|
+
# parent_name #=> nil
|
243
|
+
#
|
244
|
+
# There are several things to note about this method. First,
|
245
|
+
# make_resourceful only supports single-level model nesting. Thus, if
|
246
|
+
# neither <tt>params[:rack_id]</tt> nor <tt>params[:rack_id]</tt> are
|
247
|
+
# nil, the return value of +parent_name+ is undefined.
|
248
|
+
#
|
249
|
+
# Second, don't use parent_name to check whether a parent id is given.
|
250
|
+
# It's better to use the more semantic parent? method.
|
251
|
+
#
|
252
|
+
# Third, parent_name caches its return value in the
|
253
|
+
# <tt>@parent_name</tt> variable, which you should keep in mind if
|
254
|
+
# you're overriding it. However, because <tt>@parent_name == nil</tt>
|
255
|
+
# could mean that there is no parent _or_ that the method hasn't been
|
256
|
+
# run yet, it uses <tt>defined?(@parent_name)</tt> to do the caching
|
257
|
+
# rather than <tt>@parent_name ||=</tt>. See the source code.
|
258
|
+
#
|
259
|
+
# Finally, note that parents must be declared via Builder#belongs_to.
|
260
|
+
#
|
261
|
+
# FIXME - Perhaps this logic should be moved to parent?() or another
|
262
|
+
# init method
|
263
|
+
def parent_name
|
264
|
+
return @parent_name if defined?(@parent_name)
|
265
|
+
@parent_name = parent_names.find { |name| params["#{name}_id"] }
|
266
|
+
if @parent_name.nil?
|
267
|
+
# get any polymorphic parents through :as association inspection
|
268
|
+
names = params.keys.inject({}) do |hsh, key|
|
269
|
+
hsh[key] = key.chomp("_id") if key.to_s =~ /_id$/
|
270
|
+
hsh
|
271
|
+
end
|
272
|
+
names.each do |key, name|
|
273
|
+
begin
|
274
|
+
klass = name.camelize.constantize
|
275
|
+
if association = klass.reflect_on_all_associations.detect { |association| association.options[:as] && parent_names.include?(association.options[:as].to_s) }
|
276
|
+
@parent_name = name
|
277
|
+
@polymorphic_parent_name = association.options[:as].to_s
|
278
|
+
@parent_class_name = name.camelize
|
279
|
+
@parent_object = klass.find(params[key])
|
280
|
+
break
|
281
|
+
end
|
282
|
+
rescue
|
283
|
+
end
|
284
|
+
end
|
285
|
+
else
|
286
|
+
@parent_class_name = params["#{parent_name}_type"]
|
287
|
+
@polymorphic_parent = !@parent_class_name.nil? # NEVER USED
|
288
|
+
end
|
289
|
+
@parent_name
|
290
|
+
end
|
291
|
+
|
292
|
+
def polymorphic_parent_name
|
293
|
+
@polymorphic_parent_name
|
294
|
+
end
|
295
|
+
|
296
|
+
# Returns the class name of the current parent.
|
297
|
+
# For example, in HatsController where Person has_many :hats,
|
298
|
+
# if <tt>params[:person_id]</tt> is given,
|
299
|
+
#
|
300
|
+
# parent_class_name #=> 'Person'
|
301
|
+
#
|
302
|
+
# Note that parents must be declared via Builder#belongs_to.
|
303
|
+
def parent_class_name
|
304
|
+
parent_name # to init @parent_class_name
|
305
|
+
@parent_class_name ||= parent_name.nil? ? nil : parent_name.camelize
|
306
|
+
end
|
307
|
+
|
308
|
+
# Returns the model class of the current parent.
|
309
|
+
# For example, in HatsController where Person has_many :hats,
|
310
|
+
# if <tt>params[:person_id]</tt> is given,
|
311
|
+
#
|
312
|
+
# parent_models #=> Person
|
313
|
+
#
|
314
|
+
# Note that parents must be declared via Builder#belongs_to.
|
315
|
+
def parent_model
|
316
|
+
parent_class_name.nil? ? nil : parent_class_name.constantize
|
317
|
+
end
|
318
|
+
|
319
|
+
# Returns the current parent object for the current object.
|
320
|
+
# For example, in HatsController where Person has_many :hats,
|
321
|
+
# if <tt>params[:person_id]</tt> is given,
|
322
|
+
#
|
323
|
+
# parent_object #=> Person.find(params[:person_id])
|
324
|
+
#
|
325
|
+
# Note that parents must be declared via Builder#belongs_to.
|
326
|
+
#
|
327
|
+
# Note also that the results of this method are cached
|
328
|
+
# so that multiple calls don't result in multiple SQL queries.
|
329
|
+
def parent_object
|
330
|
+
@parent_object ||= parent_model.nil? ? nil : parent_model.find(params["#{parent_name}_id"])
|
331
|
+
end
|
332
|
+
|
333
|
+
# Assigns the current parent object, as given by parent_objects,
|
334
|
+
# to its proper instance variable, as given by parent_name.
|
335
|
+
#
|
336
|
+
# This is automatically added as a before_filter.
|
337
|
+
# You shouldn't need to use it directly unless you're creating a new action.
|
338
|
+
def load_parent_object
|
339
|
+
instance_variable_set("@#{parent_name}", parent_object) if parent?
|
340
|
+
instance_variable_set("@#{polymorphic_parent_name}", parent_object) if polymorphic_parent?
|
341
|
+
end
|
342
|
+
|
343
|
+
# Renders a 422 error if no parent id is given.
|
344
|
+
# This is meant to be used with before_filter
|
345
|
+
# to ensure that some actions are only called with a parent id.
|
346
|
+
# For example:
|
347
|
+
#
|
348
|
+
# before_filter :ensure_parent_exists, :only => [:create, :update]
|
349
|
+
#
|
350
|
+
def ensure_parent_exists
|
351
|
+
return true if parent?
|
352
|
+
render :text => 'No parent id given', :status => 422
|
353
|
+
return false
|
354
|
+
end
|
355
|
+
|
356
|
+
# Returns whether or not the database update in the +create+, +update+, and +destroy+
|
357
|
+
# was completed successfully.
|
358
|
+
def save_succeeded?
|
359
|
+
@save_succeeded
|
360
|
+
end
|
361
|
+
|
362
|
+
# Declares that the current databse update was completed successfully.
|
363
|
+
# Causes subsequent calls to <tt>save_succeeded?</tt> to return +true+.
|
364
|
+
#
|
365
|
+
# This is mostly meant to be used by the default actions,
|
366
|
+
# but it can be used by user-defined actions as well.
|
367
|
+
def save_succeeded!
|
368
|
+
@save_succeeded = true
|
369
|
+
end
|
370
|
+
|
371
|
+
# Declares that the current databse update was not completed successfully.
|
372
|
+
# Causes subsequent calls to <tt>save_succeeded?</tt> to return +false+.
|
373
|
+
#
|
374
|
+
# This is mostly meant to be used by the default actions,
|
375
|
+
# but it can be used by user-defined actions as well.
|
376
|
+
def save_failed!
|
377
|
+
@save_succeeded = false
|
378
|
+
end
|
379
|
+
|
380
|
+
# Returns whether or not the current action acts upon multiple objects.
|
381
|
+
# By default, the only such action is +index+.
|
382
|
+
def plural_action?
|
383
|
+
PLURAL_ACTIONS.include?(params[:action].to_sym)
|
384
|
+
end
|
385
|
+
|
386
|
+
# Returns whether or not the current action acts upon a single object.
|
387
|
+
# By default, this is the case for all actions but +index+.
|
388
|
+
def singular_action?
|
389
|
+
!plural_action?
|
390
|
+
end
|
391
|
+
|
392
|
+
# Returns whether the controller is a singleton,
|
393
|
+
# implying that there is only one such resource for each parent resource.
|
394
|
+
#
|
395
|
+
# Note that the way this is determined is based on the singularity of the controller name,
|
396
|
+
# so it may yield false positives for oddly-named controllers and need to be overridden.
|
397
|
+
#
|
398
|
+
# TODO: maybe we can define plural? and singular? as class_methods,
|
399
|
+
# so they are not visible to the world
|
400
|
+
def singular?
|
401
|
+
instance_variable_name.singularize == instance_variable_name
|
402
|
+
end
|
403
|
+
|
404
|
+
# Returns whether the controller is a normal plural controller,
|
405
|
+
# implying that there are multiple resources for each parent resource.
|
406
|
+
#
|
407
|
+
# Note that the way this is determined is based on the singularity of the controller name,
|
408
|
+
# so it may yield false negatives for oddly-named controllers.
|
409
|
+
# If this is the case, the singular? method should be overridden.
|
410
|
+
#
|
411
|
+
# TODO: maybe we can define plural? and singular? as class_methods,
|
412
|
+
# so they are not visible to the world
|
413
|
+
def plural?
|
414
|
+
!singular?
|
415
|
+
end
|
416
|
+
end
|
417
|
+
end
|
418
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Resourceful
|
2
|
+
module Default
|
3
|
+
# Contains the definitions of the default resourceful actions.
|
4
|
+
# These are made available with the Builder#actions method.
|
5
|
+
#
|
6
|
+
# These methods are very compact,
|
7
|
+
# so the best way to understand them is just to look at their source.
|
8
|
+
# Check out Resourceful::Accessors and Resourceful::Callbacks
|
9
|
+
# for the documentation of the methods called within the actions.
|
10
|
+
#
|
11
|
+
# Along with each action is listed the RESTful method
|
12
|
+
# which corresponds to the action.
|
13
|
+
# The controller in the examples is FoosController,
|
14
|
+
# and the id for single-object actions is 12.
|
15
|
+
module Actions
|
16
|
+
# GET /foos
|
17
|
+
def index
|
18
|
+
#load_objects
|
19
|
+
before :index
|
20
|
+
response_for :index
|
21
|
+
end
|
22
|
+
|
23
|
+
# GET /foos/12
|
24
|
+
def show
|
25
|
+
# NOTE - Moved this call to a more generic place
|
26
|
+
#load_object
|
27
|
+
before :show
|
28
|
+
response_for :show
|
29
|
+
rescue
|
30
|
+
response_for :show_fails
|
31
|
+
end
|
32
|
+
|
33
|
+
# POST /foos
|
34
|
+
def create
|
35
|
+
build_object
|
36
|
+
load_object
|
37
|
+
before :create
|
38
|
+
if current_object.save
|
39
|
+
save_succeeded!
|
40
|
+
after :create
|
41
|
+
response_for :create
|
42
|
+
else
|
43
|
+
save_failed!
|
44
|
+
after :create_fails
|
45
|
+
response_for :create_fails
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# PUT /foos/12
|
50
|
+
def update
|
51
|
+
#load_object
|
52
|
+
before :update
|
53
|
+
|
54
|
+
begin
|
55
|
+
result = current_object.update_attributes object_parameters
|
56
|
+
rescue ActiveRecord::StaleObjectError
|
57
|
+
current_object.reload
|
58
|
+
result = false
|
59
|
+
end
|
60
|
+
|
61
|
+
if result
|
62
|
+
save_succeeded!
|
63
|
+
after :update
|
64
|
+
response_for :update
|
65
|
+
else
|
66
|
+
save_failed!
|
67
|
+
after :update_fails
|
68
|
+
response_for :update_fails
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# GET /foos/new
|
73
|
+
def new
|
74
|
+
build_object
|
75
|
+
load_object
|
76
|
+
before :new
|
77
|
+
response_for :new
|
78
|
+
end
|
79
|
+
|
80
|
+
# GET /foos/12/edit
|
81
|
+
def edit
|
82
|
+
#load_object
|
83
|
+
before :edit
|
84
|
+
response_for :edit
|
85
|
+
end
|
86
|
+
|
87
|
+
# DELETE /foos/12
|
88
|
+
def destroy
|
89
|
+
#load_object
|
90
|
+
before :destroy
|
91
|
+
if current_object.destroy
|
92
|
+
after :destroy
|
93
|
+
response_for :destroy
|
94
|
+
else
|
95
|
+
after :destroy_fails
|
96
|
+
response_for :destroy_fails
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'resourceful/builder'
|
2
|
+
|
3
|
+
module Resourceful
|
4
|
+
module Default
|
5
|
+
# This module is mostly meant to be used by the make_resourceful default actions.
|
6
|
+
# It provides various methods that declare where callbacks set in the +make_resourceful+ block,
|
7
|
+
# like Builder#before and Builder#response_for,
|
8
|
+
# should be called.
|
9
|
+
module Callbacks
|
10
|
+
# Calls any +before+ callbacks set in the +make_resourceful+ block for the given event.
|
11
|
+
def before(event)
|
12
|
+
resourceful_fire(:before, event.to_sym)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Calls any +after+ callbacks set in the +make_resourceful+ block for the given event.
|
16
|
+
def after(event)
|
17
|
+
resourceful_fire(:after, event.to_sym)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Calls any +response_for+ callbacks set in the +make_resourceful+ block for the given event.
|
21
|
+
# Note that these aren't called directly,
|
22
|
+
# but instead passed along to Rails' respond_to method.
|
23
|
+
def response_for(event)
|
24
|
+
if responses = self.class.resourceful_responses[event.to_sym]
|
25
|
+
respond_to do |format|
|
26
|
+
responses.each do |key, value|
|
27
|
+
format.send(key, &scope(value))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns a block identical to the given block,
|
34
|
+
# but in the context of the current controller.
|
35
|
+
# The returned block accepts no arguments,
|
36
|
+
# even if the given block accepted them.
|
37
|
+
def scope(block)
|
38
|
+
proc do
|
39
|
+
instance_eval(&(block || proc {}))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def resourceful_fire(type, name)
|
46
|
+
callbacks = self.class.resourceful_callbacks[type][name] || []
|
47
|
+
callbacks.each { |callback| scope(callback).call }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module Resourceful
|
2
|
+
module Default
|
3
|
+
module Responses
|
4
|
+
# Sets the default flash message.
|
5
|
+
# This message can be overridden by passing in
|
6
|
+
# an HTTP parameter of the form "_flash[type]" via POST or GET.
|
7
|
+
#
|
8
|
+
# You can use this to easily have multiple forms
|
9
|
+
# post to the same create/edit/destroy actions
|
10
|
+
# but display different flash notices -
|
11
|
+
# without modifying the controller code at all.
|
12
|
+
#
|
13
|
+
# By default, the flash types are +notice+ when the database operation completes successfully
|
14
|
+
# and +error+ when it fails.
|
15
|
+
#
|
16
|
+
#--
|
17
|
+
# TODO: Move this out of here
|
18
|
+
#++
|
19
|
+
def set_default_flash(type, message)
|
20
|
+
flash[type] ||= (params[:_flash] && params[:_flash][type]) || message
|
21
|
+
end
|
22
|
+
|
23
|
+
# Sets the default redirect
|
24
|
+
# (the argument passed to +redirect_to+).
|
25
|
+
# This message can be overridden by passing in
|
26
|
+
# an HTTP parameter of the form "_redirect_on[status]" via POST or GET.
|
27
|
+
#
|
28
|
+
# You can use this to easily have multiple forms
|
29
|
+
# post to the same create/edit/destroy actions
|
30
|
+
# but redirect to different URLs -
|
31
|
+
# without modifying the controller code at all.
|
32
|
+
#
|
33
|
+
# By default, the redirect statuses are +success+ when the database operation completes successfully
|
34
|
+
# and +failure+ when it fails.
|
35
|
+
# Use the <tt>:status</tt> option to specify which status to run the redirect for.
|
36
|
+
# For example:
|
37
|
+
#
|
38
|
+
# set_default_redirect "/posts", :status => :failure
|
39
|
+
#
|
40
|
+
# This will run <tt>redirect_to params[:_redirect_on][:failure]</tt> if the parameter exists,
|
41
|
+
# or <tt>redirect_to "/posts"</tt> otherwise.
|
42
|
+
#
|
43
|
+
#--
|
44
|
+
# TODO: Move this out of here
|
45
|
+
#++
|
46
|
+
def set_default_redirect(to, options = {})
|
47
|
+
status = options[:status] || :success
|
48
|
+
redirect_to (params[:_redirect_on] && params[:_redirect_on][status]) || to
|
49
|
+
end
|
50
|
+
|
51
|
+
# This method is automatically run when this module is included in Resourceful::Base.
|
52
|
+
# It sets up the default responses for the default actions.
|
53
|
+
def self.included(base)
|
54
|
+
base.made_resourceful do
|
55
|
+
response_for(:show, :index, :edit, :new) do |format|
|
56
|
+
format.html
|
57
|
+
format.js
|
58
|
+
end
|
59
|
+
|
60
|
+
response_for(:show_fails) do |format|
|
61
|
+
not_found = Proc.new { render :text => I18n.t('make_resourceful.show.fails', :default => "No item found"), :status => 404 }
|
62
|
+
format.html ¬_found
|
63
|
+
format.js ¬_found
|
64
|
+
format.xml ¬_found
|
65
|
+
end
|
66
|
+
|
67
|
+
response_for(:create) do |format|
|
68
|
+
format.html do
|
69
|
+
set_default_flash :notice, I18n.t('make_resourceful.create.success', :default => "Create successful!")
|
70
|
+
set_default_redirect object_path
|
71
|
+
end
|
72
|
+
format.js
|
73
|
+
end
|
74
|
+
|
75
|
+
response_for(:create_fails) do |format|
|
76
|
+
format.html do
|
77
|
+
set_default_flash :error, I18n.t('make_resourceful.create.fails', :default => "There was a problem!")
|
78
|
+
render :action => :new, :status => 422
|
79
|
+
end
|
80
|
+
format.js
|
81
|
+
end
|
82
|
+
|
83
|
+
response_for(:update) do |format|
|
84
|
+
format.html do
|
85
|
+
set_default_flash :notice, I18n.t('make_resourceful.update.success', :default => "Save successful!")
|
86
|
+
set_default_redirect object_path
|
87
|
+
end
|
88
|
+
format.js
|
89
|
+
end
|
90
|
+
|
91
|
+
response_for(:update_fails) do |format|
|
92
|
+
format.html do
|
93
|
+
set_default_flash :error, I18n.t('make_resourceful.update.fails', :default => "There was a problem saving!")
|
94
|
+
render :action => :edit, :status => 422
|
95
|
+
end
|
96
|
+
format.js
|
97
|
+
end
|
98
|
+
|
99
|
+
response_for(:destroy) do |format|
|
100
|
+
format.html do
|
101
|
+
set_default_flash :notice, I18n.t('make_resourceful.destroy.success', :default => "Record deleted!")
|
102
|
+
set_default_redirect objects_path
|
103
|
+
end
|
104
|
+
format.js
|
105
|
+
end
|
106
|
+
|
107
|
+
response_for(:destroy_fails) do |format|
|
108
|
+
format.html do
|
109
|
+
set_default_flash :error, I18n.t('make_resourceful.destroy.fails', :default => "There was a problem deleting!")
|
110
|
+
set_default_redirect :back, :status => :failure
|
111
|
+
end
|
112
|
+
format.js
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|