josevalim-inherited_resources 0.1
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/CHANGELOG +4 -0
- data/MIT-LICENSE +21 -0
- data/README +362 -0
- data/Rakefile +19 -0
- data/init.rb +1 -0
- data/lib/inherited_resources.rb +4 -0
- data/lib/inherited_resources/base.rb +272 -0
- data/lib/inherited_resources/base_helpers.rb +199 -0
- data/lib/inherited_resources/belongs_to.rb +227 -0
- data/lib/inherited_resources/belongs_to_helpers.rb +89 -0
- data/lib/inherited_resources/class_methods.rb +155 -0
- data/lib/inherited_resources/polymorphic_helpers.rb +19 -0
- data/lib/inherited_resources/respond_to.rb +324 -0
- data/lib/inherited_resources/singleton_helpers.rb +53 -0
- data/lib/inherited_resources/url_helpers.rb +147 -0
- data/test/aliases_test.rb +71 -0
- data/test/base_helpers_test.rb +130 -0
- data/test/base_test.rb +219 -0
- data/test/belongs_to_base_test.rb +268 -0
- data/test/belongs_to_test.rb +109 -0
- data/test/class_methods_test.rb +73 -0
- data/test/fixtures/en.yml +9 -0
- data/test/nested_belongs_to_test.rb +138 -0
- data/test/polymorphic_base_test.rb +282 -0
- data/test/respond_to_test.rb +282 -0
- data/test/singleton_base_test.rb +226 -0
- data/test/test_helper.rb +37 -0
- data/test/url_helpers_test.rb +284 -0
- data/test/views/cities/edit.html.erb +1 -0
- data/test/views/cities/index.html.erb +1 -0
- data/test/views/cities/new.html.erb +1 -0
- data/test/views/cities/show.html.erb +1 -0
- data/test/views/comments/edit.html.erb +1 -0
- data/test/views/comments/index.html.erb +1 -0
- data/test/views/comments/new.html.erb +1 -0
- data/test/views/comments/show.html.erb +1 -0
- data/test/views/employees/edit.html.erb +1 -0
- data/test/views/employees/index.html.erb +1 -0
- data/test/views/employees/new.html.erb +1 -0
- data/test/views/employees/show.html.erb +1 -0
- data/test/views/managers/edit.html.erb +1 -0
- data/test/views/managers/new.html.erb +1 -0
- data/test/views/managers/show.html.erb +1 -0
- data/test/views/pets/edit.html.erb +1 -0
- data/test/views/professors/edit.html.erb +1 -0
- data/test/views/professors/index.html.erb +1 -0
- data/test/views/professors/new.html.erb +1 -0
- data/test/views/professors/show.html.erb +1 -0
- data/test/views/projects/index.html.erb +1 -0
- data/test/views/projects/respond_to_with_resource.html.erb +1 -0
- data/test/views/students/edit.html.erb +1 -0
- data/test/views/students/new.html.erb +1 -0
- data/test/views/users/edit.html.erb +1 -0
- data/test/views/users/index.html.erb +1 -0
- data/test/views/users/new.html.erb +1 -0
- data/test/views/users/show.html.erb +1 -0
- metadata +108 -0
data/CHANGELOG
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2006 Coda Hale
|
2
|
+
Copyright (c) 2008 José Valim (jose.valim at gmail dot com)
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,362 @@
|
|
1
|
+
Inherited Resources
|
2
|
+
License: MIT
|
3
|
+
Version: 0.1
|
4
|
+
|
5
|
+
You can also read this README in pretty html at the GitHub project Wiki page:
|
6
|
+
|
7
|
+
http://github.com/josevalim/inherited_resources/wikis/home
|
8
|
+
|
9
|
+
Description
|
10
|
+
-----------
|
11
|
+
|
12
|
+
Inherited Resources speeds up development by making your controllers inherit
|
13
|
+
all restful actions so you just have to focus on what is important. It makes
|
14
|
+
your controllers more powerful and cleaner at the same time.
|
15
|
+
|
16
|
+
keywords: resources, controller, singleton, belongs_to, polymorphic and I18n
|
17
|
+
|
18
|
+
Installation
|
19
|
+
------------
|
20
|
+
|
21
|
+
Install Inherited Resources is very easy. It is stored in GitHub, so just run
|
22
|
+
the following:
|
23
|
+
|
24
|
+
gem sources -a http://gems.github.com
|
25
|
+
sudo gem install josevalim-inherited_resources
|
26
|
+
|
27
|
+
If you want it as plugin, just do:
|
28
|
+
|
29
|
+
script/plugin install git://github.com/josevalim/inherited_resources.git
|
30
|
+
|
31
|
+
Basic Usage
|
32
|
+
-----------
|
33
|
+
|
34
|
+
To use Inherited Resources you just have to inherit (duh) it:
|
35
|
+
|
36
|
+
class ProjectsController < ResourceController::Base
|
37
|
+
end
|
38
|
+
|
39
|
+
And all actions are defined and working, check it! Your projects collection
|
40
|
+
(in the index action) is still available in the instance variable @projects
|
41
|
+
and your project resource (all other actions) is available as @ project.
|
42
|
+
|
43
|
+
The next step is to define which mime types this controller provides:
|
44
|
+
|
45
|
+
class ProjectsController < InheritedResources::Base
|
46
|
+
respond_to :html, :xml, :json
|
47
|
+
end
|
48
|
+
|
49
|
+
You can also specify them based per action:
|
50
|
+
|
51
|
+
class ProjectsController < InheritedResources::Base
|
52
|
+
respond_to :html, :xml, :json
|
53
|
+
respond_to :js, :only => :create
|
54
|
+
respond_to :iphone, :except => [ :edit, :update ]
|
55
|
+
end
|
56
|
+
|
57
|
+
Let's take a request on create with :rjs as example of how it works. After the
|
58
|
+
action is processed, it will check if the view "projects/create.js.rjs" exist.
|
59
|
+
If it does exist, it will render it, otherwise it will check if the resource
|
60
|
+
@project respond to :to_js. If it is true, it will render the result of :to_js,
|
61
|
+
otherwise it will render a 404.
|
62
|
+
|
63
|
+
Another option is to specify which actions the controller will inherit from
|
64
|
+
the InheritedResources::Base:
|
65
|
+
|
66
|
+
class ProjectsController < InheritedResources::Base
|
67
|
+
actions :index, :show, :new, :create
|
68
|
+
end
|
69
|
+
|
70
|
+
Or:
|
71
|
+
|
72
|
+
class ProjectsController < InheritedResources::Base
|
73
|
+
actions :all, :except => [ :edit, :update, :destroy ]
|
74
|
+
end
|
75
|
+
|
76
|
+
Overwriting defaults
|
77
|
+
--------------------
|
78
|
+
|
79
|
+
Now that you learned the default behavior and basic configuration, let's see
|
80
|
+
how to extend it.
|
81
|
+
|
82
|
+
Assuming that you have a PeopleController, but the resource is actually called
|
83
|
+
User, you could overwrite the resource name, collection name and instance name
|
84
|
+
just doing:
|
85
|
+
|
86
|
+
class PeopleController < InheritedResources::Base
|
87
|
+
defaults :resource_class => User, :collection_name => 'users', :instance_name => 'user'
|
88
|
+
end
|
89
|
+
|
90
|
+
Further extensions is done by overwriting two methods. Let's suppose you want
|
91
|
+
to add pagination to your projects collection:
|
92
|
+
|
93
|
+
class ProjectsController < InheritedResources::Base
|
94
|
+
protected
|
95
|
+
def collection
|
96
|
+
@projects ||= end_of_association_chain.paginate(params[:page]).all
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
In this case you could just have done:
|
101
|
+
|
102
|
+
class ProjectsController < InheritedResources::Base
|
103
|
+
protected
|
104
|
+
def collection
|
105
|
+
@projects ||= Project.paginate(params[:page]).all
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
But you should get used if end_of_association_chain. When you have nested
|
110
|
+
resources it's very handy and it will deal with all nesting stuff. It was first
|
111
|
+
introduced in resource_controller by Jamis Golick and we will talk more about it
|
112
|
+
soon.
|
113
|
+
|
114
|
+
On the other hand, if you just added pretty urls for in your ProjectsController
|
115
|
+
(/projects/my-project-name), you can overwrite how projects are being found by:
|
116
|
+
|
117
|
+
class ProjectsController < InheritedResources::Base
|
118
|
+
protected
|
119
|
+
def resource
|
120
|
+
@project ||= end_of_association_chain.find_by_title!(params[:title])
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
And now since you is acquainted to end_of_association_chain let's meet its
|
125
|
+
twin brother: begin_of_association_chain.
|
126
|
+
|
127
|
+
It's mostly used when you want to create resources based on the @current_user.
|
128
|
+
In such cases, you don't have your @current_user in the url, but in the session.
|
129
|
+
|
130
|
+
So if you have to do: @current_user.projects.find or @current_user.projects.build
|
131
|
+
you can deal with both just doing:
|
132
|
+
|
133
|
+
class ProjectsController < InheritedResources::Base
|
134
|
+
protected
|
135
|
+
def begin_of_association_chain
|
136
|
+
@current_user
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
Overwriting actions
|
141
|
+
-------------------
|
142
|
+
|
143
|
+
Let's suppose that after destroying a project you want to redirect to your
|
144
|
+
root url instead of redirecting to projects url. You just have to do:
|
145
|
+
|
146
|
+
class ProjectsController < InheritedResources::Base
|
147
|
+
def destroy
|
148
|
+
super do |format|
|
149
|
+
format.html { redirect_to root_url }
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
You are opening your action and giving the parent action a new behavior. No
|
155
|
+
tricks, no DSL, just Ruby.
|
156
|
+
|
157
|
+
On the other hand, I have to agree that calling super is the right thing but
|
158
|
+
is not very readable. That's why all methods have aliases. So this is
|
159
|
+
equivalent:
|
160
|
+
|
161
|
+
class ProjectsController < InheritedResources::Base
|
162
|
+
def destroy
|
163
|
+
destroy! do |format|
|
164
|
+
format.html { redirect_to projects_url }
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
Now let's suppose that before create a project you have to do something special
|
170
|
+
but you don't want to create a before filter for it:
|
171
|
+
|
172
|
+
class ProjectsController < InheritedResources::Base
|
173
|
+
def create
|
174
|
+
@project = Project.new(params[:project])
|
175
|
+
@project.something_special!
|
176
|
+
create!
|
177
|
+
end end
|
178
|
+
|
179
|
+
Yeap, that simple! The nice part is since you already set the instance variable
|
180
|
+
@project, it will not do it again and overwrite your @project with something
|
181
|
+
special. :)
|
182
|
+
|
183
|
+
Flash messages and I18n
|
184
|
+
-----------------------
|
185
|
+
|
186
|
+
Flash messages are powered by I18n api. It checks for messages in the following
|
187
|
+
order:
|
188
|
+
|
189
|
+
flash.controller_name.action_name.status
|
190
|
+
flash.actions.action_name.status
|
191
|
+
|
192
|
+
If none is available, a default message in english set. Let's have a break from
|
193
|
+
projects and talk about CarsController. So in a create action, it will check for
|
194
|
+
flash messages in the following order:
|
195
|
+
|
196
|
+
flash.cars.create.status
|
197
|
+
flash.actions.create.status
|
198
|
+
|
199
|
+
The status can be :notice (when the object can be created, updated
|
200
|
+
or destroyed with success) or :error (when the objecy cannot be created
|
201
|
+
or updated).
|
202
|
+
|
203
|
+
Those messages are interpolated by using the resource class human name, which
|
204
|
+
is also localized and it means you can set:
|
205
|
+
|
206
|
+
flash:
|
207
|
+
actions:
|
208
|
+
create:
|
209
|
+
notice: "Hooray! {{resource}} was successfully created!"
|
210
|
+
|
211
|
+
It will replace {{resource}} for Car properly.
|
212
|
+
|
213
|
+
But sometimes, flash messages are not that simple. Going back
|
214
|
+
to cars example, you might want to say the brand of the car when it's
|
215
|
+
updated. Well, that's easy also:
|
216
|
+
|
217
|
+
flash:
|
218
|
+
cars:
|
219
|
+
update:
|
220
|
+
notice: "Hooray! You just tuned your {{car_brand}}!"
|
221
|
+
|
222
|
+
Since :car_name is not available for interpolation by default, you have
|
223
|
+
to overwrite interpolation_options.
|
224
|
+
|
225
|
+
def interpolation_options
|
226
|
+
{ :car_brand => @car.brand }
|
227
|
+
end
|
228
|
+
|
229
|
+
Then you will finally have:
|
230
|
+
|
231
|
+
'Hooray! You just tuned your Aston Martin!'
|
232
|
+
|
233
|
+
Belongs to
|
234
|
+
----------
|
235
|
+
|
236
|
+
Finally, our Projects are going to get some Tasks. Then you create a
|
237
|
+
TasksController and do:
|
238
|
+
|
239
|
+
class TasksController < InheritedResources::Base
|
240
|
+
belongs_to :project
|
241
|
+
end
|
242
|
+
|
243
|
+
belongs_to accepts several options to be able to configure the association.
|
244
|
+
Remember that our projects have pretty urls? So if you thought that url like
|
245
|
+
/projects/:project_title/tasks would be a problem, I can assure you it won't:
|
246
|
+
|
247
|
+
class TasksController < InheritedResources::Base
|
248
|
+
belongs_to :project, :finder => :find_by_title!, :param => :project_title
|
249
|
+
end
|
250
|
+
|
251
|
+
Check belongs_to file for more customization. :)
|
252
|
+
|
253
|
+
Nested belongs to
|
254
|
+
-----------------
|
255
|
+
|
256
|
+
Now, our Tasks get some Comments and you need to nest even deeper. Good
|
257
|
+
practices says that you should never nest more than two resources, but sometimes
|
258
|
+
you have to for security reasons. So this is an example of how you can do it:
|
259
|
+
|
260
|
+
class CommentsController < InheritedResources::Base
|
261
|
+
nested_belongs_to :project
|
262
|
+
nested_belongs_to :task
|
263
|
+
end
|
264
|
+
|
265
|
+
nested_belongs_to is actually just an alias to belongs_to. This is an
|
266
|
+
alternative declaration:
|
267
|
+
|
268
|
+
class CommentsController < InheritedResources::Base
|
269
|
+
belongs_to :project do
|
270
|
+
belongs_to :task
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
Polymorphic belongs to
|
275
|
+
----------------------
|
276
|
+
|
277
|
+
Now let's go even further. Our Projects is getting some Files and Messages
|
278
|
+
besides Tasks, and they are all commentable:
|
279
|
+
|
280
|
+
class CommentsController < InheritedResources::Base
|
281
|
+
belongs_to :task, :file, :message, :polymorphic => true
|
282
|
+
end
|
283
|
+
|
284
|
+
You can even use it with nested resources:
|
285
|
+
|
286
|
+
class CommentsController < InheritedResources::Base
|
287
|
+
belongs_to :project do
|
288
|
+
belongs_to :task, :file, :message, :polymorphic => true
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
When using polymorphic associations, you get some free helpers:
|
293
|
+
|
294
|
+
parent? #=> true
|
295
|
+
parent_type #=> :task
|
296
|
+
parent_class #=> Task
|
297
|
+
parent_instance #=> @task
|
298
|
+
|
299
|
+
Polymorphic controller is another great idea by James Golick and he also uses
|
300
|
+
that on resource_controller.
|
301
|
+
|
302
|
+
Singletons
|
303
|
+
----------
|
304
|
+
|
305
|
+
Now we are going to add manager to projects. We say that Manager is a singleton
|
306
|
+
resource because a Project has just one manager. You should declare it as
|
307
|
+
has_one (or resource) in your routes.
|
308
|
+
|
309
|
+
To declare an association as singleton, you just have to give the :singleton
|
310
|
+
option.
|
311
|
+
|
312
|
+
class ManagersController < InheritedResources::Base
|
313
|
+
belongs_to :project, :singleton => true
|
314
|
+
end
|
315
|
+
|
316
|
+
It will deal with everything again and hide the action :index from you.
|
317
|
+
|
318
|
+
URL Helpers
|
319
|
+
-----------
|
320
|
+
|
321
|
+
When you use InheritedResources it creates some URL helpers for you.
|
322
|
+
And they handle everything for you.
|
323
|
+
|
324
|
+
# /posts/1/comments
|
325
|
+
resource_url # => /posts/1/comments/#{@comment.to_param}
|
326
|
+
resource_url(comment) # => /posts/1/comments/#{comment.to_param}
|
327
|
+
new_resource_url # => /posts/1/comments/new
|
328
|
+
edit_resource_url # => /posts/1/comments/#{@comment.to_param}/edit
|
329
|
+
collection_url # => /posts/1/comments
|
330
|
+
|
331
|
+
# /projects/1/tasks
|
332
|
+
resource_url # => /products/1/tasks/#{@task.to_param}
|
333
|
+
resource_url(task) # => /products/1/tasks/#{task.to_param}
|
334
|
+
new_resource_url # => /products/1/tasks/new
|
335
|
+
edit_resource_url # => /products/1/tasks/#{@task.to_param}/edit
|
336
|
+
collection_url # => /products/1/tasks
|
337
|
+
|
338
|
+
# /users
|
339
|
+
resource_url # => /users/#{@user.to_param}
|
340
|
+
resource_url(user) # => /users/#{user.to_param}
|
341
|
+
new_resource_url # => /users/new
|
342
|
+
edit_resource_url # => /users/#{@user.to_param}/edit
|
343
|
+
collection_url # => /users
|
344
|
+
|
345
|
+
The nice thing is that those urls are not guessed during runtime. They are
|
346
|
+
all created when your application is loaded (except for polymorphic
|
347
|
+
associations, that relies on Rails polymorphic_url).
|
348
|
+
|
349
|
+
What's next
|
350
|
+
-----------
|
351
|
+
|
352
|
+
I'm working on generators and some nice things to speed up and make easier
|
353
|
+
your controllers tests with mocking and stubs. :)
|
354
|
+
|
355
|
+
Bugs and Feedback
|
356
|
+
-----------------
|
357
|
+
|
358
|
+
If you discover any bugs, please send an e-mail to jose.valim@gmail.com
|
359
|
+
If you just want to give some positive feedback or drop a line, that's fine too!
|
360
|
+
|
361
|
+
Copyright (c) 2009 José Valim
|
362
|
+
http://josevalim.blogspot.com/
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Run tests for InheritedResources.'
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
7
|
+
t.pattern = 'test/**/*_test.rb'
|
8
|
+
t.verbose = true
|
9
|
+
end
|
10
|
+
|
11
|
+
desc 'Generate documentation for InheritedResources.'
|
12
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
13
|
+
rdoc.rdoc_dir = 'rdoc'
|
14
|
+
rdoc.title = 'InheritedResources'
|
15
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
16
|
+
rdoc.rdoc_files.include('README')
|
17
|
+
rdoc.rdoc_files.include('MIT-LICENSE')
|
18
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
19
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'lib', 'inherited_resources')
|
@@ -0,0 +1,272 @@
|
|
1
|
+
# = Inheriting
|
2
|
+
#
|
3
|
+
# To use InheritedResources you have to inherit from InheritedResources::Base
|
4
|
+
# class. This class have all Rails REST actions defined (index, show, new, edit
|
5
|
+
# update, create and destroy). The following definition is the same as a Rails
|
6
|
+
# scaffolded controller:
|
7
|
+
#
|
8
|
+
# class ProjectController < InheritedResources::Base
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# All actions are defined, check it!
|
12
|
+
#
|
13
|
+
# The next step is to define which mime types this controller provides:
|
14
|
+
#
|
15
|
+
# class ProjectController < InheritedResources::Base
|
16
|
+
# respond_to :html, :xml, :json
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# You just said that this controller will respond to :html, :xml and :json. You
|
20
|
+
# can also specify it based on actions:
|
21
|
+
#
|
22
|
+
# class ProjectController < InheritedResources::Base
|
23
|
+
# respond_to :html, :xml, :json
|
24
|
+
# respond_to :js, :only => :create
|
25
|
+
# respond_to :csv, :except => [ :destroy ]
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# How it works is simple. Let's suppose you have a json request on the action
|
29
|
+
# show. It will first try to render "projects/show.json.something". If it can't
|
30
|
+
# be found, it will call :to_json in the resource, which in this case is
|
31
|
+
# @project.
|
32
|
+
#
|
33
|
+
# If the resource @project doesn't respond to :to_json, we will render a 404
|
34
|
+
# Not Found.
|
35
|
+
#
|
36
|
+
# If you don't want to inherit all actions from InheritedResources::Base, call
|
37
|
+
# actions method with the actions you want to inherit:
|
38
|
+
#
|
39
|
+
# class ProjectController < InheritedResources::Base
|
40
|
+
# actions :index, :show, :new, :create, :edit, :update
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# Or:
|
44
|
+
#
|
45
|
+
# class ProjectController < InheritedResources::Base
|
46
|
+
# actions :all, :except => :destroy
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# = Extending the default behaviour
|
50
|
+
#
|
51
|
+
# Let's suppose that after destroying a project you want to redirect to your
|
52
|
+
# root url instead of redirecting to projects url. You just have to do:
|
53
|
+
#
|
54
|
+
# class ProjectController < InheritedResources::Base
|
55
|
+
# def destroy
|
56
|
+
# super do |format|
|
57
|
+
# format.html { redirect_to projects_url }
|
58
|
+
# end
|
59
|
+
# end
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# super? Yes, we agree that calling super is the right thing but it does not
|
63
|
+
# look nice. That's why all methods have aliases. So this is equivalent:
|
64
|
+
#
|
65
|
+
# class ProjectController < InheritedResources::Base
|
66
|
+
# def destroy
|
67
|
+
# destroy! do |format|
|
68
|
+
# format.html { redirect_to projects_url }
|
69
|
+
# end
|
70
|
+
# end
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# Since this is actually Ruby (and not a new DSL), if you want to do something
|
74
|
+
# before creating the project that is to small to deserve a before_filter, you
|
75
|
+
# could simply do:
|
76
|
+
#
|
77
|
+
# class ProjectController < InheritedResources::Base
|
78
|
+
# def create
|
79
|
+
# # do something different!
|
80
|
+
# create!
|
81
|
+
# end
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# And as instance variables are shared you can do more nice things.
|
85
|
+
# Let's suppose you want to create a project based on the current user:
|
86
|
+
#
|
87
|
+
# class ProjectController < InheritedResources::Base
|
88
|
+
# def create
|
89
|
+
# @project = @current_user.projects.build(params[:project])
|
90
|
+
# create!
|
91
|
+
# end
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
# When you call create! the instance variable @project is already defined,
|
95
|
+
# so the method won't instanciate it again.
|
96
|
+
#
|
97
|
+
# The great thing is that we are not using blocks or nothing in special. We are
|
98
|
+
# just inheriting and calling the parent (super). You can extend even more
|
99
|
+
# without using blocks, please check helpers.rb for more info.
|
100
|
+
#
|
101
|
+
# = Flash and I18n
|
102
|
+
#
|
103
|
+
# Flash messages are changed through I18n API. If you have a ProjectsController,
|
104
|
+
# when a resource is updated with success, it will search for messages in the
|
105
|
+
# following order:
|
106
|
+
#
|
107
|
+
# 'flash.projects.update.notice'
|
108
|
+
# 'flash.actions.update.notice'
|
109
|
+
#
|
110
|
+
# If none of them are not available, it will show the default message:
|
111
|
+
#
|
112
|
+
# Project was successfully updated.
|
113
|
+
#
|
114
|
+
# The message will be set into flash[:notice].
|
115
|
+
# Messages can be interpolated, so you can do the following in your I18n files:
|
116
|
+
#
|
117
|
+
# flash:
|
118
|
+
# actions:
|
119
|
+
# update:
|
120
|
+
# notice: "Hooray! {{resource}} was updated with success!"
|
121
|
+
#
|
122
|
+
# It will replace {{resource}} by Project.human_name, which is also localized
|
123
|
+
# (check http://rails-i18n.org/wiki/pages/i18n-rails-guide for more info).
|
124
|
+
#
|
125
|
+
# But sometimes, flash messages are not that simple. You might want to say the
|
126
|
+
# the name of the project when it's updated. Well, that's easy also:
|
127
|
+
#
|
128
|
+
# flash:
|
129
|
+
# projects:
|
130
|
+
# update:
|
131
|
+
# notice: "Dear manager, {{project_name}} was successfully updated!"
|
132
|
+
#
|
133
|
+
# Since :project_name is not available for interpolation by default, you
|
134
|
+
# have to overwrite interpolation_options method on your controller.
|
135
|
+
#
|
136
|
+
# def interpolation_options
|
137
|
+
# { :project_name => @project.quoted_name }
|
138
|
+
# end
|
139
|
+
#
|
140
|
+
# Then you will finally have:
|
141
|
+
#
|
142
|
+
# 'Dear manager, "Make Rails Scale" was successfully updated!'
|
143
|
+
#
|
144
|
+
# Success messages appear on :create, :update and :destroy actions. Failure
|
145
|
+
# messages appear only on :create and :update.
|
146
|
+
#
|
147
|
+
# = Changing assumptions
|
148
|
+
#
|
149
|
+
# When you inherit from InheritedResources::Base, we make some assumptions on
|
150
|
+
# what is your resource_class, instance_name and collection_name.
|
151
|
+
#
|
152
|
+
# You can change those values by calling the class method defaults:
|
153
|
+
#
|
154
|
+
# class PeopleController < InheritedResources::Base
|
155
|
+
# defaults :resource_class => User, :instance_name => 'user', :collection_name => 'users'
|
156
|
+
# end
|
157
|
+
#
|
158
|
+
# Further customizations can be done replacing some methods. Check
|
159
|
+
# base_helpers.rb file for more information.
|
160
|
+
#
|
161
|
+
module InheritedResources
|
162
|
+
RESOURCES_ACTIONS = [ :index, :show, :new, :edit, :create, :update, :destroy ]
|
163
|
+
|
164
|
+
class Base < ::ApplicationController
|
165
|
+
include InheritedResources::BaseHelpers
|
166
|
+
extend InheritedResources::BelongsTo
|
167
|
+
extend InheritedResources::ClassMethods
|
168
|
+
|
169
|
+
helper_method :collection_url, :collection_path, :resource_url, :resource_path,
|
170
|
+
:new_resource_url, :new_resource_path, :edit_resource_url, :edit_resource_path
|
171
|
+
|
172
|
+
def self.inherited(base)
|
173
|
+
base.class_eval do
|
174
|
+
# Make all resources actions public
|
175
|
+
public *RESOURCES_ACTIONS
|
176
|
+
end
|
177
|
+
|
178
|
+
# Call super to register class in ApplicationController
|
179
|
+
super
|
180
|
+
|
181
|
+
# Creates and sets class accessors default values
|
182
|
+
initialize_resources_class_accessors!(base)
|
183
|
+
end
|
184
|
+
|
185
|
+
protected
|
186
|
+
|
187
|
+
# GET /resources
|
188
|
+
def index(&block)
|
189
|
+
respond_to(:with => collection, &block)
|
190
|
+
end
|
191
|
+
alias :index! :index
|
192
|
+
|
193
|
+
# GET /resources/1
|
194
|
+
def show(&block)
|
195
|
+
respond_to(:with => resource, &block)
|
196
|
+
end
|
197
|
+
alias :show! :show
|
198
|
+
|
199
|
+
# GET /resources/new
|
200
|
+
def new(&block)
|
201
|
+
respond_to(:with => build_resource, &block)
|
202
|
+
end
|
203
|
+
alias :new! :new
|
204
|
+
|
205
|
+
# GET /resources/1/edit
|
206
|
+
def edit(&block)
|
207
|
+
respond_to(:with => resource, &block)
|
208
|
+
end
|
209
|
+
alias :edit! :edit
|
210
|
+
|
211
|
+
# POST /resources
|
212
|
+
def create(&block)
|
213
|
+
object = build_resource(params[resource_instance_name])
|
214
|
+
|
215
|
+
if object.save
|
216
|
+
set_flash_message!(:notice, '{{resource}} was successfully created.')
|
217
|
+
|
218
|
+
respond_to(:with => object, :status => :created, :location => resource_url) do |format|
|
219
|
+
yield(format) if block_given?
|
220
|
+
format.html { redirect_to(resource_url) }
|
221
|
+
end
|
222
|
+
else
|
223
|
+
set_flash_message!(:error)
|
224
|
+
|
225
|
+
respond_to(:with => object.errors, :status => :unprocessable_entity) do |format|
|
226
|
+
yield(format) if block_given?
|
227
|
+
format.html { render :action => "new" }
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
alias :create! :create
|
232
|
+
|
233
|
+
# PUT /resources/1
|
234
|
+
def update(&block)
|
235
|
+
object = resource
|
236
|
+
|
237
|
+
if object.update_attributes(params[resource_instance_name])
|
238
|
+
set_flash_message!(:notice, '{{resource}} was successfully updated.')
|
239
|
+
|
240
|
+
respond_to do |format|
|
241
|
+
yield(format) if block_given?
|
242
|
+
format.html { redirect_to(resource_url) }
|
243
|
+
format.all { head :ok }
|
244
|
+
end
|
245
|
+
else
|
246
|
+
set_flash_message!(:error)
|
247
|
+
|
248
|
+
respond_to(:with => object.errors, :status => :unprocessable_entity) do |format|
|
249
|
+
yield(format) if block_given?
|
250
|
+
format.html { render :action => "edit" }
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
alias :update! :update
|
255
|
+
|
256
|
+
# DELETE /resources/1
|
257
|
+
def destroy(&block)
|
258
|
+
resource.destroy
|
259
|
+
|
260
|
+
set_flash_message!(:notice, '{{resource}} was successfully destroyed.')
|
261
|
+
|
262
|
+
respond_to do |format|
|
263
|
+
yield(format) if block_given?
|
264
|
+
format.html { redirect_to(collection_url) }
|
265
|
+
format.all { head :ok }
|
266
|
+
end
|
267
|
+
end
|
268
|
+
alias :destroy! :destroy
|
269
|
+
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|