inherited_resources 0.9.2
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 +103 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +524 -0
- data/Rakefile +40 -0
- data/lib/inherited_resources.rb +23 -0
- data/lib/inherited_resources/actions.rb +79 -0
- data/lib/inherited_resources/base.rb +42 -0
- data/lib/inherited_resources/base_helpers.rb +363 -0
- data/lib/inherited_resources/belongs_to_helpers.rb +89 -0
- data/lib/inherited_resources/class_methods.rb +338 -0
- data/lib/inherited_resources/dsl.rb +26 -0
- data/lib/inherited_resources/dumb_responder.rb +20 -0
- data/lib/inherited_resources/has_scope_helpers.rb +83 -0
- data/lib/inherited_resources/legacy/respond_to.rb +156 -0
- data/lib/inherited_resources/legacy/responder.rb +200 -0
- data/lib/inherited_resources/polymorphic_helpers.rb +155 -0
- data/lib/inherited_resources/singleton_helpers.rb +95 -0
- data/lib/inherited_resources/url_helpers.rb +179 -0
- data/test/aliases_test.rb +139 -0
- data/test/association_chain_test.rb +125 -0
- data/test/base_test.rb +225 -0
- data/test/belongs_to_test.rb +87 -0
- data/test/class_methods_test.rb +138 -0
- data/test/customized_base_test.rb +162 -0
- data/test/customized_belongs_to_test.rb +76 -0
- data/test/defaults_test.rb +70 -0
- data/test/flash_test.rb +88 -0
- data/test/has_scope_test.rb +139 -0
- data/test/nested_belongs_to_test.rb +108 -0
- data/test/optional_belongs_to_test.rb +164 -0
- data/test/polymorphic_test.rb +186 -0
- data/test/redirect_to_test.rb +51 -0
- data/test/respond_to_test.rb +155 -0
- data/test/singleton_test.rb +83 -0
- data/test/test_helper.rb +38 -0
- data/test/url_helpers_test.rb +537 -0
- metadata +89 -0
data/CHANGELOG
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# Version 0.9
|
2
|
+
|
3
|
+
* Added :if and :unless to has_scope (thanks to Jack Danger);
|
4
|
+
* Added create_resource, update_resource and delete_resource hooks (thanks to Carlos Antonio da Silva);
|
5
|
+
* Backported ActionController::Responder from Rails 3;
|
6
|
+
* Added parent_url helper;
|
7
|
+
* Added association_chain helper (as suggested by http://github.com/emmanuel);
|
8
|
+
|
9
|
+
# Version 0.8
|
10
|
+
|
11
|
+
* Fixed a small bug on optional belongs to with namespaced controllers.
|
12
|
+
* Allow a parameter to be given to collection_url in polymorphic cases to replace
|
13
|
+
the parent.
|
14
|
+
* Allow InheritedResources to be called without inheritance.
|
15
|
+
* Ensure that controllers that inherit from a controller with InheritedResources
|
16
|
+
works properly.
|
17
|
+
|
18
|
+
# Version 0.7
|
19
|
+
|
20
|
+
* Allow procs as default value in has scope to be able to use values from session, for example.
|
21
|
+
* Allow blocks with arity 0 or -1 to be given as the redirect url:
|
22
|
+
|
23
|
+
def destroy
|
24
|
+
destroy!{ project_url(@project) }
|
25
|
+
end
|
26
|
+
|
27
|
+
* Allow interpolation_options to be set in the application controller.
|
28
|
+
* Added has_scope to controller (an interface for named_scopes).
|
29
|
+
* Added polymorphic_belongs_to, optional_belongs_to and singleton_belongs_to
|
30
|
+
as quick methods.
|
31
|
+
* Only load belongs_to, singleton and polymorphic helpers if they are actually
|
32
|
+
required. base_helpers, class_methods, dumb_responder and url_helpers are loaded
|
33
|
+
when you inherited from base for the first time.
|
34
|
+
|
35
|
+
# Version 0.6
|
36
|
+
|
37
|
+
* Ensure that the default template is not rendered if the default_template_format
|
38
|
+
is not accepted. This is somehow related with the security breach report:
|
39
|
+
|
40
|
+
http://www.rorsecurity.info/journal/2009/4/24/hidden-actions-render-templates.html
|
41
|
+
|
42
|
+
IR forbids based on mime types. For example: respond_to :html, :except => :index
|
43
|
+
ensures that the index.html.erb view is not rendered, making your IR controllers
|
44
|
+
safer.
|
45
|
+
|
46
|
+
* Fixed a bug that happens only when format.xml is given to blocks and then it
|
47
|
+
acts as default, instead of format.html.
|
48
|
+
* Fixed a strange bug where when you have create.html.erb or update.html.erb,
|
49
|
+
it makes IE6 and IE7 return unprocessable entity (because they send Mime::ALL).
|
50
|
+
* Stop rescueing any error when constantizing the resource class and allow
|
51
|
+
route_prefix to be nil.
|
52
|
+
* Cleaned up tests and responder structure. Whenever you pass a block to aliases
|
53
|
+
and this block responds to the request, the other blocks are not parsed improving performance.
|
54
|
+
* [BACKWARDS INCOMPATIBLE] By default, Inherited Resources respond only :html requests.
|
55
|
+
* Added a quick way to overwrite the redirect to url in :create, :update and :destroy.
|
56
|
+
|
57
|
+
# Version 0.5
|
58
|
+
|
59
|
+
* Decoupled routes name from :instance_name and :collection_name. This way we
|
60
|
+
have more flexibility. Use route_instance_name and route_collection_name to
|
61
|
+
to change routes.
|
62
|
+
* Avoid calling human_name on nil when a resource class is not defined.
|
63
|
+
* Only call I18n if it's defined.
|
64
|
+
|
65
|
+
# Version 0.4
|
66
|
+
|
67
|
+
* Dealing with namespaced controllers out of the box.
|
68
|
+
* Added support to namespaced routes through :route_prefix.
|
69
|
+
* Added fix when resource_url is not defined.
|
70
|
+
* Added better handling for namespaced controllers.
|
71
|
+
* Added flash messages scoped by namespaced controllers.
|
72
|
+
* Deprecated {{resource}} in I18n, use {{resource_name}} instead.
|
73
|
+
* rspec bug fix is not automatically required anymore. User has to do it
|
74
|
+
explicitly.
|
75
|
+
* Added a file which fix a rspec bug when render is called inside a method
|
76
|
+
which receives a block.
|
77
|
+
* parent? does not take begin_of_association_chain into account anymore
|
78
|
+
* Added options to url helpers.
|
79
|
+
* Added :optional to belongs_to associations. It allows you to deal with
|
80
|
+
categories/1/products/2 and /products/2 with just one controller.
|
81
|
+
* Cleaned up tests.
|
82
|
+
|
83
|
+
# Version 0.3
|
84
|
+
|
85
|
+
* Minor bump after three bug fixes.
|
86
|
+
* Bug fix when showing warning of constant redefinition.
|
87
|
+
* Bug fix with ApplicationController not being unloaded properly on development.
|
88
|
+
* Bug fix when having root singleton resources. Calling collection_url would
|
89
|
+
raise "NoMethodError _url", not it will call root_url.
|
90
|
+
* More comments on UrlHelpers.
|
91
|
+
|
92
|
+
# Version 0.2
|
93
|
+
|
94
|
+
* Bug fix when ApplicationController is already loaded when we load respond_to.
|
95
|
+
* Added support success/failure blocks.
|
96
|
+
* Eager loading of files to work properly in multithreaded environments.
|
97
|
+
|
98
|
+
# Version 0.1
|
99
|
+
|
100
|
+
* Added more helper_methods.
|
101
|
+
* Added Rails 2.3.0 and changed tests to work with ActionController::TestCase.
|
102
|
+
* First release. Support to I18n, singleton controllers, polymorphic
|
103
|
+
controllers, belongs_to, nested_belongs_to and url helpers.
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 José Valim
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,524 @@
|
|
1
|
+
Inherited Resources
|
2
|
+
License: MIT
|
3
|
+
Version: 0.9.1
|
4
|
+
|
5
|
+
== Description
|
6
|
+
|
7
|
+
Inherited Resources speeds up development by making your controllers inherit
|
8
|
+
all restful actions so you just have to focus on what is important. It makes
|
9
|
+
your controllers more powerful and cleaner at the same time.
|
10
|
+
|
11
|
+
Plus, making your controllers follow a pattern, it helps you to write better
|
12
|
+
code by following fat models and skinny controllers convention. There is
|
13
|
+
a screencast made by Fabio Akita about its features:
|
14
|
+
|
15
|
+
http://akitaonrails.com/2009/09/01/screencast-real-thin-restful-controllers-with-inherited-resources
|
16
|
+
|
17
|
+
Inherited Resources is tested and compatible with Rails 2.2.x and Rails 2.3.x.
|
18
|
+
|
19
|
+
keywords: resources, controller, singleton, belongs_to, polymorphic, named_scope and I18n
|
20
|
+
|
21
|
+
== Installation
|
22
|
+
|
23
|
+
Install Inherited Resources is very easy. It is stored in GitHub, so just run
|
24
|
+
the following:
|
25
|
+
|
26
|
+
gem sources -a http://gems.github.com
|
27
|
+
sudo gem install josevalim-inherited_resources
|
28
|
+
|
29
|
+
If you want it as plugin, just do:
|
30
|
+
|
31
|
+
script/plugin install git://github.com/josevalim/inherited_resources.git
|
32
|
+
|
33
|
+
== Rspec known bug
|
34
|
+
|
35
|
+
When used with integrate_views equals to false, rspec overwrites default_render,
|
36
|
+
render and some other controller methods which makes Inherited Resources not work
|
37
|
+
properly. In such cases, you have to set integrate_views to true.
|
38
|
+
|
39
|
+
== Basic Usage
|
40
|
+
|
41
|
+
To use Inherited Resources you just have to inherit (duh) it:
|
42
|
+
|
43
|
+
class ProjectsController < InheritedResources::Base
|
44
|
+
end
|
45
|
+
|
46
|
+
And all actions are defined and working, check it! Your projects collection
|
47
|
+
(in the index action) is still available in the instance variable @projects
|
48
|
+
and your project resource (all other actions) is available as @ project.
|
49
|
+
|
50
|
+
The next step is to define which mime types this controller provides:
|
51
|
+
|
52
|
+
class ProjectsController < InheritedResources::Base
|
53
|
+
respond_to :html, :xml, :json
|
54
|
+
end
|
55
|
+
|
56
|
+
You can also specify them based per action:
|
57
|
+
|
58
|
+
class ProjectsController < InheritedResources::Base
|
59
|
+
respond_to :html, :xml, :json
|
60
|
+
respond_to :js, :only => :create
|
61
|
+
respond_to :iphone, :except => [ :edit, :update ]
|
62
|
+
end
|
63
|
+
|
64
|
+
For each request, it first checkes if the "controller/action.format" file is
|
65
|
+
available (for example "projects/create.xml") and if it's not, it checks if
|
66
|
+
the resource respond to :to_format (in this case, :to_xml). Otherwise returns 404.
|
67
|
+
|
68
|
+
Another option is to specify which actions the controller will inherit from
|
69
|
+
the InheritedResources::Base:
|
70
|
+
|
71
|
+
class ProjectsController < InheritedResources::Base
|
72
|
+
actions :index, :show, :new, :create
|
73
|
+
end
|
74
|
+
|
75
|
+
Or:
|
76
|
+
|
77
|
+
class ProjectsController < InheritedResources::Base
|
78
|
+
actions :all, :except => [ :edit, :update, :destroy ]
|
79
|
+
end
|
80
|
+
|
81
|
+
In your views, you will get the following helpers:
|
82
|
+
|
83
|
+
resource #=> @project
|
84
|
+
collection #=> @projects
|
85
|
+
resource_class #=> Project
|
86
|
+
|
87
|
+
As you might expect, collection (@projects instance variable) is only available
|
88
|
+
on index actions.
|
89
|
+
|
90
|
+
If for some reason you cannot inherit from InheritedResources::Base, you can
|
91
|
+
call inherit_resources in your controller class scope:
|
92
|
+
|
93
|
+
class AccountsController < ApplicationController
|
94
|
+
inherit_resources
|
95
|
+
end
|
96
|
+
|
97
|
+
== Overwriting defaults
|
98
|
+
|
99
|
+
Whenever you inherit from InheritedResources, several defaults are assumed.
|
100
|
+
For example you can have an AccountsController to account management while the
|
101
|
+
resource is an User:
|
102
|
+
|
103
|
+
class AccountsController < InheritedResources::Base
|
104
|
+
defaults :resource_class => User, :collection_name => 'users', :instance_name => 'user'
|
105
|
+
end
|
106
|
+
|
107
|
+
In the case above, in your views you will have @users and @user variables, but
|
108
|
+
the routes used will still be accounts_url and account_url. If you plan also to
|
109
|
+
change the routes, you can use :route_collection_name and :route_instance_name.
|
110
|
+
|
111
|
+
Namespaced controllers work out of the box, but if you need to specify a
|
112
|
+
different route prefix, you can do the following:
|
113
|
+
|
114
|
+
class Administrators::PeopleController < InheritedResources::Base
|
115
|
+
defaults :route_prefix => 'admin'
|
116
|
+
end
|
117
|
+
|
118
|
+
Then your named routes will be: 'admin_people_url', 'admin_person_url' instead
|
119
|
+
of 'administrators_people_url' and 'administrators_person_url'.
|
120
|
+
|
121
|
+
If you want to customize how resources are retrieved you can overwrite
|
122
|
+
collection and resource methods. The first is called on index action and the
|
123
|
+
second on all other actions. Let's suppose you want to add pagination to your
|
124
|
+
projects collection:
|
125
|
+
|
126
|
+
class ProjectsController < InheritedResources::Base
|
127
|
+
protected
|
128
|
+
def collection
|
129
|
+
@projects ||= end_of_association_chain.paginate(:page => params[:page])
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
The end_of_association_chain returns your resource after nesting all associations
|
134
|
+
and scopes (more about this below).
|
135
|
+
|
136
|
+
InheritedResources also introduces another method called begin_of_association_chain.
|
137
|
+
It's mostly used when you want to create resources based on the @current_user and
|
138
|
+
you have urls like "account/projects". In such cases, you have to do
|
139
|
+
@current_user.projects.find or @current_user.projects.build in your actions.
|
140
|
+
|
141
|
+
You can deal with it just doing:
|
142
|
+
|
143
|
+
class ProjectsController < InheritedResources::Base
|
144
|
+
protected
|
145
|
+
def begin_of_association_chain
|
146
|
+
@current_user
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
== Overwriting actions
|
151
|
+
|
152
|
+
Let's suppose that after destroying a project you want to redirect to your
|
153
|
+
root url instead of redirecting to projects url. You just have to do:
|
154
|
+
|
155
|
+
class ProjectsController < InheritedResources::Base
|
156
|
+
def destroy
|
157
|
+
super do |format|
|
158
|
+
format.html { redirect_to root_url }
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
You are opening your action and giving the parent action a new behavior. On
|
164
|
+
the other hand, I have to agree that calling super is not very readable. That's
|
165
|
+
why all methods have aliases. So this is equivalent:
|
166
|
+
|
167
|
+
class ProjectsController < InheritedResources::Base
|
168
|
+
def destroy
|
169
|
+
destroy! do |format|
|
170
|
+
format.html { redirect_to root_url }
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
Even more, since most of the times when you change a create, update or destroy
|
176
|
+
action is because you want to to change to where it redirects, a shortcut is
|
177
|
+
provided. So you can do:
|
178
|
+
|
179
|
+
class ProjectsController < InheritedResources::Base
|
180
|
+
def destroy
|
181
|
+
destroy!{ root_url }
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
Now let's suppose that before create a project you have to do something special
|
186
|
+
but you don't want to create a before filter for it:
|
187
|
+
|
188
|
+
class ProjectsController < InheritedResources::Base
|
189
|
+
def create
|
190
|
+
@project = Project.new(params[:project])
|
191
|
+
@project.something_special!
|
192
|
+
create!
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
Yes, that simple! The nice part is since you already set the instance variable
|
197
|
+
@project, it will not build a project again.
|
198
|
+
|
199
|
+
Before we finish this topic, we should talk about one more thing: "success/failure
|
200
|
+
blocks". Let's suppose that when we update our project, in case of failure, we
|
201
|
+
want to redirect to the project url instead of re-rendering the edit template.
|
202
|
+
|
203
|
+
Our first attempt to do this would be:
|
204
|
+
|
205
|
+
class ProjectsController < InheritedResources::Base
|
206
|
+
def update
|
207
|
+
update! do |format|
|
208
|
+
unless @project.errors.empty? # failure
|
209
|
+
format.html { redirect_to project_url(@project) }
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
Looks to verbose, right? We can actually do:
|
216
|
+
|
217
|
+
class ProjectsController < InheritedResources::Base
|
218
|
+
def update
|
219
|
+
update! do |success, failure|
|
220
|
+
failure.html { redirect_to project_url(@project) }
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
Much better! So explaining everything: when you give a block which expects one
|
226
|
+
argument it will be executed in both scenarios: success and failure. But If you
|
227
|
+
give a block that expects two arguments, the first will be executed only in
|
228
|
+
success scenarios and the second in failure scenarios. You keep everything
|
229
|
+
clean and organized inside the same action.
|
230
|
+
|
231
|
+
== Some DSL
|
232
|
+
|
233
|
+
For those DSL lovers, InheritedResources won't leave you alone. You can overwrite
|
234
|
+
your success/failure blocks straight from your class binding. For it, you just
|
235
|
+
need to add a DSL block to your application controller:
|
236
|
+
|
237
|
+
class ApplicationController < ActionController::Base
|
238
|
+
include InheritedResources::DSL
|
239
|
+
end
|
240
|
+
|
241
|
+
And then you can rewrite the last example as:
|
242
|
+
|
243
|
+
class ProjectsController < InheritedResources::Base
|
244
|
+
update! do |success, failure|
|
245
|
+
failure.html { redirect_to project_url(@project) }
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
== Flash messages and I18n
|
250
|
+
|
251
|
+
Flash messages are powered by I18n api. It checks for messages in the following
|
252
|
+
order:
|
253
|
+
|
254
|
+
flash.controller_name.action_name.status
|
255
|
+
flash.actions.action_name.status
|
256
|
+
|
257
|
+
If none is available, a default message in english set. In a create action
|
258
|
+
on projects controller, it will search for:
|
259
|
+
|
260
|
+
flash.projects.create.status
|
261
|
+
flash.actions.create.status
|
262
|
+
|
263
|
+
The status can be :notice (when the object can be created, updated
|
264
|
+
or destroyed with success) or :error (when the objecy cannot be created
|
265
|
+
or updated).
|
266
|
+
|
267
|
+
Those messages are interpolated by using the resource class human name, which
|
268
|
+
is also localized and it means you can set:
|
269
|
+
|
270
|
+
flash:
|
271
|
+
actions:
|
272
|
+
create:
|
273
|
+
notice: "Hooray! {{resource_name}} was successfully created!"
|
274
|
+
|
275
|
+
It will replace {{resource_name}} by the human name of the resource class,
|
276
|
+
which is "Project" in this case.
|
277
|
+
|
278
|
+
But sometimes, flash messages are not that simple. Sometimes you want to say
|
279
|
+
the title of the project while updating a project. Well, that's easy also:
|
280
|
+
|
281
|
+
flash:
|
282
|
+
projects:
|
283
|
+
update:
|
284
|
+
notice: "Hooray! The project "{{project_title}}" was updated!"
|
285
|
+
|
286
|
+
Since :project_title is not available for interpolation by default, you have
|
287
|
+
to overwrite interpolation_options.
|
288
|
+
|
289
|
+
def interpolation_options
|
290
|
+
{ :project_title => @project.title }
|
291
|
+
end
|
292
|
+
|
293
|
+
Then you will finally have:
|
294
|
+
|
295
|
+
"Hooray! The project "Plataforma" was updated!"
|
296
|
+
|
297
|
+
By default, resource name is capitalized. If you want to make it lower case, you
|
298
|
+
can add to your application controller:
|
299
|
+
|
300
|
+
def interpolation_options
|
301
|
+
{ :resource_name => resource_class.human_name.downcase }
|
302
|
+
end
|
303
|
+
|
304
|
+
Finally, if your controller is namespaced, for example Admin::ProjectsController,
|
305
|
+
the messages will be checked in the following order:
|
306
|
+
|
307
|
+
flash.admin.projects.create.notice
|
308
|
+
flash.admin.actions.create.notice
|
309
|
+
flash.projects.create.notice
|
310
|
+
flash.actions.create.notice
|
311
|
+
|
312
|
+
== Has Scope
|
313
|
+
|
314
|
+
InheritedResources tries to integrate nicely with your model. In order to do so,
|
315
|
+
it also is named_scope fluent. Let's suppose our Project model with the scopes:
|
316
|
+
|
317
|
+
class Project < ActiveRecord::Base
|
318
|
+
named_scope :featured, :conditions => { :featured => true }
|
319
|
+
named_scope :by_methodology, proc {|methodology| { :conditions => { :methodology => methodology } } }
|
320
|
+
named_scope :limit, proc{|limit| :limit => limit.to_i }
|
321
|
+
end
|
322
|
+
|
323
|
+
Your controller:
|
324
|
+
|
325
|
+
class ProjectsController < InheritedResources::Base
|
326
|
+
has_scope :featured, :boolean => true, :only => :index
|
327
|
+
has_scope :by_methodology
|
328
|
+
has_scope :limit, :default => 10
|
329
|
+
end
|
330
|
+
|
331
|
+
Then for each request:
|
332
|
+
|
333
|
+
/projects
|
334
|
+
#=> acts like a normal request, but returning 10 projects
|
335
|
+
|
336
|
+
/projects?featured=true
|
337
|
+
#=> calls the featured named scope and bring 10 featured projects
|
338
|
+
|
339
|
+
/projects?featured=true&by_methodology=agile&limit=20
|
340
|
+
#=> brings 20 featured projects with methodology agile
|
341
|
+
|
342
|
+
You can retrieve the current scopes in use with :current_scopes method.
|
343
|
+
In the last case, it would return:
|
344
|
+
|
345
|
+
{ :featured => "true", :by_methodology => "agile", :limit => "20" }
|
346
|
+
|
347
|
+
Finally, let's suppose you store on the session how many projects the user sees
|
348
|
+
per page. In such cases, you can give a proc as default value:
|
349
|
+
|
350
|
+
has_scope :limit, :default => proc{|c| c.session[:limit] || 10 }
|
351
|
+
|
352
|
+
== Belongs to
|
353
|
+
|
354
|
+
Finally, our Projects are going to get some Tasks. Then you create a
|
355
|
+
TasksController and do:
|
356
|
+
|
357
|
+
class TasksController < InheritedResources::Base
|
358
|
+
belongs_to :project
|
359
|
+
end
|
360
|
+
|
361
|
+
belongs_to accepts several options to be able to configure the association.
|
362
|
+
For example, if you want urls like /projects/:project_title/tasks, you can
|
363
|
+
customize how InheritedResources find your projects:
|
364
|
+
|
365
|
+
class TasksController < InheritedResources::Base
|
366
|
+
belongs_to :project, :finder => :find_by_title!, :param => :project_title
|
367
|
+
end
|
368
|
+
|
369
|
+
It also accepts :route_name, :parent_class and :instance_name as options.
|
370
|
+
Check the lib/inherited_resources/class_methods.rb for more.
|
371
|
+
|
372
|
+
== Nested belongs to
|
373
|
+
|
374
|
+
Now, our Tasks get some Comments and you need to nest even deeper. Good
|
375
|
+
practices says that you should never nest more than two resources, but sometimes
|
376
|
+
you have to for security reasons. So this is an example of how you can do it:
|
377
|
+
|
378
|
+
class CommentsController < InheritedResources::Base
|
379
|
+
nested_belongs_to :project, :task
|
380
|
+
end
|
381
|
+
|
382
|
+
If you need to configure any of these belongs to, you can nest them using blocks:
|
383
|
+
|
384
|
+
class CommentsController < InheritedResources::Base
|
385
|
+
belongs_to :project, :finder => :find_by_title!, :param => :project_title do
|
386
|
+
belongs_to :task
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
Warning: calling several belongs_to is the same as nesting them:
|
391
|
+
|
392
|
+
class CommentsConroller < InheritedResources::Base
|
393
|
+
belongs_to :project
|
394
|
+
belongs_to :task
|
395
|
+
end
|
396
|
+
|
397
|
+
In other words, the code above is the same as calling nested_belongs_to.
|
398
|
+
|
399
|
+
== Polymorphic belongs to
|
400
|
+
|
401
|
+
We can go even further. Let's suppose our Projects can now have Files, Messages
|
402
|
+
and Tasks, and they are all commentable. In this case, the best solution is to
|
403
|
+
use polymorphism:
|
404
|
+
|
405
|
+
class CommentsController < InheritedResources::Base
|
406
|
+
belongs_to :task, :file, :message, :polymorphic => true
|
407
|
+
# polymorphic_belongs_to :task, :file, :message
|
408
|
+
end
|
409
|
+
|
410
|
+
You can even use it with nested resources:
|
411
|
+
|
412
|
+
class CommentsController < InheritedResources::Base
|
413
|
+
belongs_to :project do
|
414
|
+
belongs_to :task, :file, :message, :polymorphic => true
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
The url in such cases can be:
|
419
|
+
|
420
|
+
/project/1/task/13/comments
|
421
|
+
/project/1/file/11/comments
|
422
|
+
/project/1/message/9/comments
|
423
|
+
|
424
|
+
When using polymorphic associations, you get some free helpers:
|
425
|
+
|
426
|
+
parent? #=> true
|
427
|
+
parent_type #=> :task
|
428
|
+
parent_class #=> Task
|
429
|
+
parent #=> @task
|
430
|
+
|
431
|
+
Right now, Inherited Resources is limited and does not allow you
|
432
|
+
to have two polymorphic associations nested.
|
433
|
+
|
434
|
+
== Optional belongs to
|
435
|
+
|
436
|
+
Later you decide to create a view to show all comments, independent if they belong
|
437
|
+
to a task, file or message. You can reuse your polymorphic controller just doing:
|
438
|
+
|
439
|
+
class ProjectsController < InheritedResources::Base
|
440
|
+
belongs_to :task, :file, :message, :optional => true
|
441
|
+
# optional_belongs_to :task, :file, :message
|
442
|
+
end
|
443
|
+
|
444
|
+
This will handle all those urls properly:
|
445
|
+
|
446
|
+
/comment/1
|
447
|
+
/tasks/2/comment/5
|
448
|
+
/files/10/comment/3
|
449
|
+
/messages/13/comment/11
|
450
|
+
|
451
|
+
This is treated as a special type of polymorphic associations, thus all helpers
|
452
|
+
are available. As you expect, when no parent is found, the helpers return:
|
453
|
+
|
454
|
+
parent? #=> false
|
455
|
+
parent_type #=> nil
|
456
|
+
parent_class #=> nil
|
457
|
+
parent #=> nil
|
458
|
+
|
459
|
+
== Singletons
|
460
|
+
|
461
|
+
Now we are going to add manager to projects. We say that Manager is a singleton
|
462
|
+
resource because a Project has just one manager. You should declare it as
|
463
|
+
has_one (or resource) in your routes.
|
464
|
+
|
465
|
+
To declare an association as singleton, you just have to give the :singleton
|
466
|
+
option.
|
467
|
+
|
468
|
+
class ManagersController < InheritedResources::Base
|
469
|
+
belongs_to :project, :singleton => true
|
470
|
+
# singleton_belongs_to :project
|
471
|
+
end
|
472
|
+
|
473
|
+
It will deal with everything again and hide the action :index from you.
|
474
|
+
|
475
|
+
== URL Helpers
|
476
|
+
|
477
|
+
When you use InheritedResources it creates some URL helpers.
|
478
|
+
And they handle everything for you. :)
|
479
|
+
|
480
|
+
# /posts/1/comments
|
481
|
+
resource_url # => /posts/1/comments/#{@comment.to_param}
|
482
|
+
resource_url(comment) # => /posts/1/comments/#{comment.to_param}
|
483
|
+
new_resource_url # => /posts/1/comments/new
|
484
|
+
edit_resource_url # => /posts/1/comments/#{@comment.to_param}/edit
|
485
|
+
edit_resource_url(comment) #=> /posts/1/comments/#{comment.to_param}/edit
|
486
|
+
collection_url # => /posts/1/comments
|
487
|
+
parent_url # => /posts/1
|
488
|
+
|
489
|
+
# /projects/1/tasks
|
490
|
+
resource_url # => /projects/1/tasks/#{@task.to_param}
|
491
|
+
resource_url(task) # => /projects/1/tasks/#{task.to_param}
|
492
|
+
new_resource_url # => /projects/1/tasks/new
|
493
|
+
edit_resource_url # => /projects/1/tasks/#{@task.to_param}/edit
|
494
|
+
edit_resource_url(task) # => /projects/1/tasks/#{task.to_param}/edit
|
495
|
+
collection_url # => /projects/1/tasks
|
496
|
+
parent_url # => /projects/1
|
497
|
+
|
498
|
+
# /users
|
499
|
+
resource_url # => /users/#{@user.to_param}
|
500
|
+
resource_url(user) # => /users/#{user.to_param}
|
501
|
+
new_resource_url # => /users/new
|
502
|
+
edit_resource_url # => /users/#{@user.to_param}/edit
|
503
|
+
edit_resource_url(user) # => /users/#{user.to_param}/edit
|
504
|
+
collection_url # => /users
|
505
|
+
parent_url # => /
|
506
|
+
|
507
|
+
Those urls helpers also accepts a hash as options, just as in named routes.
|
508
|
+
|
509
|
+
# /projects/1/tasks
|
510
|
+
collection_url(:page => 1, :limit => 10) #=> /projects/1/tasks?page=1&limit=10
|
511
|
+
|
512
|
+
In polymorphic cases, you can also give the parent as parameter to collection_url.
|
513
|
+
|
514
|
+
Another nice thing is that those urls are not guessed during runtime. They are
|
515
|
+
all created when your application is loaded (except for polymorphic
|
516
|
+
associations, that relies on Rails polymorphic_url).
|
517
|
+
|
518
|
+
== Bugs and Feedback
|
519
|
+
|
520
|
+
If you discover any bugs or want to drop a line, join us in the mailing list:
|
521
|
+
|
522
|
+
http://groups.google.com/group/inherited_resources
|
523
|
+
|
524
|
+
Copyright (c) 2009 José Valim http://blog.plataformatec.com.br
|