josevalim-inherited_resources 0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|