inherited_resources 1.4.0 → 1.4.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.
- checksums.yaml +7 -0
- data/CHANGELOG +5 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +65 -69
- data/README.md +709 -0
- data/inherited_resources.gemspec +3 -2
- data/lib/inherited_resources.rb +1 -1
- data/lib/inherited_resources/actions.rb +4 -4
- data/lib/inherited_resources/base_helpers.rb +16 -10
- data/lib/inherited_resources/class_methods.rb +6 -6
- data/lib/inherited_resources/version.rb +1 -1
- data/test/strong_parameters_test.rb +3 -0
- data/test/test_helper.rb +2 -2
- metadata +15 -19
- data/README.rdoc +0 -559
data/inherited_resources.gemspec
CHANGED
@@ -11,6 +11,7 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.homepage = "http://github.com/josevalim/inherited_resources"
|
12
12
|
s.description = "Inherited Resources speeds up development by making your controllers inherit all restful actions so you just have to focus on what is important."
|
13
13
|
s.authors = ['José Valim']
|
14
|
+
s.license = "MIT"
|
14
15
|
|
15
16
|
s.rubyforge_project = "inherited_resources"
|
16
17
|
|
@@ -18,6 +19,6 @@ Gem::Specification.new do |s|
|
|
18
19
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
20
|
s.require_paths = ["lib"]
|
20
21
|
|
21
|
-
s.add_dependency("responders", "~> 0.
|
22
|
-
s.add_dependency("has_scope", "~> 0.
|
22
|
+
s.add_dependency("responders", "~> 1.0.0.rc")
|
23
|
+
s.add_dependency("has_scope", "~> 0.6.0.rc")
|
23
24
|
end
|
data/lib/inherited_resources.rb
CHANGED
@@ -34,7 +34,7 @@ end
|
|
34
34
|
|
35
35
|
class ActionController::Base
|
36
36
|
# If you cannot inherit from InheritedResources::Base you can call
|
37
|
-
#
|
37
|
+
# inherit_resources in your controller to have all the required modules and
|
38
38
|
# funcionality included.
|
39
39
|
def self.inherit_resources
|
40
40
|
InheritedResources::Base.inherit_resources(self)
|
@@ -4,25 +4,25 @@ module InheritedResources
|
|
4
4
|
|
5
5
|
# GET /resources
|
6
6
|
def index(options={}, &block)
|
7
|
-
respond_with(*
|
7
|
+
respond_with(*with_chain(collection), options, &block)
|
8
8
|
end
|
9
9
|
alias :index! :index
|
10
10
|
|
11
11
|
# GET /resources/1
|
12
12
|
def show(options={}, &block)
|
13
|
-
respond_with(*
|
13
|
+
respond_with(*with_chain(resource), options, &block)
|
14
14
|
end
|
15
15
|
alias :show! :show
|
16
16
|
|
17
17
|
# GET /resources/new
|
18
18
|
def new(options={}, &block)
|
19
|
-
respond_with(*
|
19
|
+
respond_with(*with_chain(build_resource), options, &block)
|
20
20
|
end
|
21
21
|
alias :new! :new
|
22
22
|
|
23
23
|
# GET /resources/1/edit
|
24
24
|
def edit(options={}, &block)
|
25
|
-
respond_with(*
|
25
|
+
respond_with(*with_chain(resource), options, &block)
|
26
26
|
end
|
27
27
|
alias :edit! :edit
|
28
28
|
|
@@ -22,7 +22,14 @@ module InheritedResources
|
|
22
22
|
def collection
|
23
23
|
get_collection_ivar || begin
|
24
24
|
c = end_of_association_chain
|
25
|
-
|
25
|
+
if defined?(ActiveRecord::DeprecatedFinders)
|
26
|
+
# ActiveRecord::Base#scoped and ActiveRecord::Relation#all
|
27
|
+
# are deprecated in Rails 4. If it's a relation just use
|
28
|
+
# it, otherwise use .all to get a relation.
|
29
|
+
set_collection_ivar(c.is_a?(ActiveRecord::Relation) ? c : c.all)
|
30
|
+
else
|
31
|
+
set_collection_ivar(c.respond_to?(:scoped) ? c.scoped : c.all)
|
32
|
+
end
|
26
33
|
end
|
27
34
|
end
|
28
35
|
|
@@ -126,18 +133,17 @@ module InheritedResources
|
|
126
133
|
# current resource).
|
127
134
|
#
|
128
135
|
def association_chain
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
end
|
136
|
+
@association_chain ||= begin
|
137
|
+
symbol_chain = if resources_configuration[:self][:singleton]
|
138
|
+
symbols_for_association_chain.reverse
|
139
|
+
else
|
140
|
+
symbols_for_association_chain
|
141
|
+
end
|
136
142
|
|
137
|
-
@association_chain =
|
138
143
|
symbol_chain.inject([begin_of_association_chain]) do |chain, symbol|
|
139
144
|
chain << evaluate_parent(symbol, resources_configuration[symbol], chain.last)
|
140
145
|
end.compact.freeze
|
146
|
+
end
|
141
147
|
end
|
142
148
|
|
143
149
|
# Overwrite this method to provide other interpolation options when
|
@@ -312,7 +318,7 @@ module InheritedResources
|
|
312
318
|
|
313
319
|
# extract attributes from params
|
314
320
|
def build_resource_params
|
315
|
-
parameters = respond_to?(:permitted_params) ? permitted_params : params
|
321
|
+
parameters = respond_to?(:permitted_params, true) ? permitted_params : params
|
316
322
|
rparams = [parameters[resource_request_name] || parameters[resource_instance_name] || {}]
|
317
323
|
if without_protection_given?
|
318
324
|
rparams << without_protection
|
@@ -32,7 +32,7 @@ module InheritedResources
|
|
32
32
|
#
|
33
33
|
# * <tt>:finder</tt> - Specifies which method should be called to instantiate the resource.
|
34
34
|
#
|
35
|
-
# defaults :
|
35
|
+
# defaults :finder => :find_by_slug
|
36
36
|
#
|
37
37
|
def defaults(options)
|
38
38
|
raise ArgumentError, 'Class method :defaults expects a hash of options.' unless options.is_a? Hash
|
@@ -154,7 +154,7 @@ module InheritedResources
|
|
154
154
|
acts_as_shallow! if shallow
|
155
155
|
|
156
156
|
raise ArgumentError, 'You have to give me at least one association name.' if symbols.empty?
|
157
|
-
raise ArgumentError,
|
157
|
+
raise ArgumentError, "You cannot define multiple associations with options: #{options.keys.inspect} to belongs to." unless symbols.size == 1 || options.empty?
|
158
158
|
|
159
159
|
symbols.each do |symbol|
|
160
160
|
symbol = symbol.to_sym
|
@@ -201,7 +201,7 @@ module InheritedResources
|
|
201
201
|
def polymorphic_belongs_to(*symbols, &block)
|
202
202
|
options = symbols.extract_options!
|
203
203
|
options.merge!(:polymorphic => true)
|
204
|
-
belongs_to(*symbols
|
204
|
+
belongs_to(*symbols, options, &block)
|
205
205
|
end
|
206
206
|
|
207
207
|
# A quick method to declare singleton belongs to.
|
@@ -209,7 +209,7 @@ module InheritedResources
|
|
209
209
|
def singleton_belongs_to(*symbols, &block)
|
210
210
|
options = symbols.extract_options!
|
211
211
|
options.merge!(:singleton => true)
|
212
|
-
belongs_to(*symbols
|
212
|
+
belongs_to(*symbols, options, &block)
|
213
213
|
end
|
214
214
|
|
215
215
|
# A quick method to declare optional belongs to.
|
@@ -217,7 +217,7 @@ module InheritedResources
|
|
217
217
|
def optional_belongs_to(*symbols, &block)
|
218
218
|
options = symbols.extract_options!
|
219
219
|
options.merge!(:optional => true)
|
220
|
-
belongs_to(*symbols
|
220
|
+
belongs_to(*symbols, options, &block)
|
221
221
|
end
|
222
222
|
|
223
223
|
# Defines custom restful actions by resource or collection basis.
|
@@ -358,7 +358,7 @@ module InheritedResources
|
|
358
358
|
def create_custom_action(resource_or_collection, action)
|
359
359
|
class_eval <<-CUSTOM_ACTION, __FILE__, __LINE__
|
360
360
|
def #{action}(options={}, &block)
|
361
|
-
respond_with(*
|
361
|
+
respond_with(*with_chain(#{resource_or_collection}), options, &block)
|
362
362
|
end
|
363
363
|
alias :#{action}! :#{action}
|
364
364
|
protected :#{action}!
|
@@ -12,6 +12,9 @@ class StrongParametersTest < ActionController::TestCase
|
|
12
12
|
@controller = WidgetsController.new
|
13
13
|
@controller.stubs(:widget_url).returns("/")
|
14
14
|
@controller.stubs(:permitted_params).returns(:widget => {:permitted => 'param'})
|
15
|
+
class << @controller
|
16
|
+
private :permitted_params
|
17
|
+
end
|
15
18
|
end
|
16
19
|
|
17
20
|
def test_permitted_params_from_new
|
data/test/test_helper.rb
CHANGED
@@ -27,8 +27,8 @@ ActionController::Base.view_paths = File.join(File.dirname(__FILE__), 'views')
|
|
27
27
|
|
28
28
|
InheritedResources::Routes = ActionDispatch::Routing::RouteSet.new
|
29
29
|
InheritedResources::Routes.draw do
|
30
|
-
|
31
|
-
|
30
|
+
get ':controller(/:action(/:id))'
|
31
|
+
get ':controller(/:action)'
|
32
32
|
resources 'posts'
|
33
33
|
root :to => 'posts#index'
|
34
34
|
end
|
metadata
CHANGED
@@ -1,48 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inherited_resources
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4.
|
5
|
-
prerelease:
|
4
|
+
version: 1.4.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- José Valim
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-07-31 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: responders
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
17
|
- - ~>
|
20
18
|
- !ruby/object:Gem::Version
|
21
|
-
version:
|
19
|
+
version: 1.0.0.rc
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
24
|
- - ~>
|
28
25
|
- !ruby/object:Gem::Version
|
29
|
-
version:
|
26
|
+
version: 1.0.0.rc
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: has_scope
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
31
|
- - ~>
|
36
32
|
- !ruby/object:Gem::Version
|
37
|
-
version: 0.
|
33
|
+
version: 0.6.0.rc
|
38
34
|
type: :runtime
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
38
|
- - ~>
|
44
39
|
- !ruby/object:Gem::Version
|
45
|
-
version: 0.
|
40
|
+
version: 0.6.0.rc
|
46
41
|
description: Inherited Resources speeds up development by making your controllers
|
47
42
|
inherit all restful actions so you just have to focus on what is important.
|
48
43
|
email: developers@plataformatec.com.br
|
@@ -55,7 +50,7 @@ files:
|
|
55
50
|
- Gemfile
|
56
51
|
- Gemfile.lock
|
57
52
|
- MIT-LICENSE
|
58
|
-
- README.
|
53
|
+
- README.md
|
59
54
|
- Rakefile
|
60
55
|
- app/controllers/inherited_resources/base.rb
|
61
56
|
- inherited_resources.gemspec
|
@@ -175,28 +170,28 @@ files:
|
|
175
170
|
- test/views/venue/show.html.erb
|
176
171
|
- test/views/widgets/new.html.erb
|
177
172
|
homepage: http://github.com/josevalim/inherited_resources
|
178
|
-
licenses:
|
173
|
+
licenses:
|
174
|
+
- MIT
|
175
|
+
metadata: {}
|
179
176
|
post_install_message:
|
180
177
|
rdoc_options: []
|
181
178
|
require_paths:
|
182
179
|
- lib
|
183
180
|
required_ruby_version: !ruby/object:Gem::Requirement
|
184
|
-
none: false
|
185
181
|
requirements:
|
186
|
-
- -
|
182
|
+
- - '>='
|
187
183
|
- !ruby/object:Gem::Version
|
188
184
|
version: '0'
|
189
185
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
190
|
-
none: false
|
191
186
|
requirements:
|
192
|
-
- -
|
187
|
+
- - '>='
|
193
188
|
- !ruby/object:Gem::Version
|
194
189
|
version: '0'
|
195
190
|
requirements: []
|
196
191
|
rubyforge_project: inherited_resources
|
197
|
-
rubygems_version:
|
192
|
+
rubygems_version: 2.0.6
|
198
193
|
signing_key:
|
199
|
-
specification_version:
|
194
|
+
specification_version: 4
|
200
195
|
summary: Inherited Resources speeds up development by making your controllers inherit
|
201
196
|
all restful actions so you just have to focus on what is important.
|
202
197
|
test_files:
|
@@ -299,3 +294,4 @@ test_files:
|
|
299
294
|
- test/views/users/update.js.erb
|
300
295
|
- test/views/venue/show.html.erb
|
301
296
|
- test/views/widgets/new.html.erb
|
297
|
+
has_rdoc:
|
data/README.rdoc
DELETED
@@ -1,559 +0,0 @@
|
|
1
|
-
== Inherited Resources
|
2
|
-
|
3
|
-
Inherited Resources speeds up development by making your controllers inherit
|
4
|
-
all restful actions so you just have to focus on what is important. It makes
|
5
|
-
your controllers more powerful and cleaner at the same time.
|
6
|
-
|
7
|
-
In addition to making your controllers follow a pattern, it helps you to write better
|
8
|
-
code by following fat models and skinny controllers convention. There are
|
9
|
-
two screencasts available besides this README:
|
10
|
-
|
11
|
-
* http://railscasts.com/episodes/230-inherited-resources
|
12
|
-
* http://akitaonrails.com/2009/09/01/screencast-real-thin-restful-controllers-with-inherited-resources
|
13
|
-
|
14
|
-
== Installation
|
15
|
-
|
16
|
-
=== Rails 3
|
17
|
-
|
18
|
-
You can let bundler install Inherited Resources by adding this line to your application's Gemfile:
|
19
|
-
|
20
|
-
gem 'inherited_resources'
|
21
|
-
|
22
|
-
And then execute:
|
23
|
-
|
24
|
-
bundle install
|
25
|
-
|
26
|
-
Or install it yourself with:
|
27
|
-
|
28
|
-
gem install inherited_resources
|
29
|
-
|
30
|
-
=== Rails 2.3.x
|
31
|
-
|
32
|
-
If you want to use the Rails 2.3.x version, you should install:
|
33
|
-
|
34
|
-
gem install inherited_resources --version=1.0.6
|
35
|
-
|
36
|
-
Or checkout from the v1.0 branch:
|
37
|
-
|
38
|
-
http://github.com/josevalim/inherited_resources/tree/v1.0
|
39
|
-
|
40
|
-
== HasScope
|
41
|
-
|
42
|
-
Since Inherited Resources 1.0, has_scope is not part of its core anymore but
|
43
|
-
a gem dependency. Be sure to check the documentation to see how you can use it:
|
44
|
-
|
45
|
-
http://github.com/plataformatec/has_scope
|
46
|
-
|
47
|
-
And it can be installed as:
|
48
|
-
|
49
|
-
gem install has_scope
|
50
|
-
|
51
|
-
== Responders
|
52
|
-
|
53
|
-
Since Inherited Resources 1.0, responders are not part of its core anymore,
|
54
|
-
but is set as Inherited Resources dependency and it's used by default by
|
55
|
-
InheritedResources controllers. Be sure to check the documentation to see
|
56
|
-
how it will change your application:
|
57
|
-
|
58
|
-
http://github.com/plataformatec/responders
|
59
|
-
|
60
|
-
And it can be installed with:
|
61
|
-
|
62
|
-
gem install responders
|
63
|
-
|
64
|
-
Using responders will set the flash message to :notice and :alert. You can change
|
65
|
-
that through the following configuration value:
|
66
|
-
|
67
|
-
InheritedResources.flash_keys = [ :success, :failure ]
|
68
|
-
|
69
|
-
Notice the CollectionResponder won't work with InheritedResources, as InheritedResources hardcodes the redirect path based on the current scope (like belongs to, polymorphic associations, etc).
|
70
|
-
|
71
|
-
== Basic Usage
|
72
|
-
|
73
|
-
To use Inherited Resources you just have to inherit (duh) it:
|
74
|
-
|
75
|
-
class ProjectsController < InheritedResources::Base
|
76
|
-
end
|
77
|
-
|
78
|
-
And all actions are defined and working, check it! Your projects collection
|
79
|
-
(in the index action) is still available in the instance variable @projects
|
80
|
-
and your project resource (all other actions) is available as @project.
|
81
|
-
|
82
|
-
The next step is to define which mime types this controller provides:
|
83
|
-
|
84
|
-
class ProjectsController < InheritedResources::Base
|
85
|
-
respond_to :html, :xml, :json
|
86
|
-
end
|
87
|
-
|
88
|
-
You can also specify them per action:
|
89
|
-
|
90
|
-
class ProjectsController < InheritedResources::Base
|
91
|
-
respond_to :html, :xml, :json
|
92
|
-
respond_to :js, :only => :create
|
93
|
-
respond_to :iphone, :except => [ :edit, :update ]
|
94
|
-
end
|
95
|
-
|
96
|
-
For each request, it first checks if the "controller/action.format" file is
|
97
|
-
available (for example "projects/create.xml") and if it's not, it checks if
|
98
|
-
the resource respond to :to_format (in this case, :to_xml). Otherwise returns 404.
|
99
|
-
|
100
|
-
Another option is to specify which actions the controller will inherit from
|
101
|
-
the InheritedResources::Base:
|
102
|
-
|
103
|
-
class ProjectsController < InheritedResources::Base
|
104
|
-
actions :index, :show, :new, :create
|
105
|
-
end
|
106
|
-
|
107
|
-
Or:
|
108
|
-
|
109
|
-
class ProjectsController < InheritedResources::Base
|
110
|
-
actions :all, :except => [ :edit, :update, :destroy ]
|
111
|
-
end
|
112
|
-
|
113
|
-
In your views, you will get the following helpers:
|
114
|
-
|
115
|
-
resource #=> @project
|
116
|
-
collection #=> @projects
|
117
|
-
resource_class #=> Project
|
118
|
-
|
119
|
-
As you might expect, collection (@projects instance variable) is only available
|
120
|
-
on index actions.
|
121
|
-
|
122
|
-
If for some reason you cannot inherit from InheritedResources::Base, you can
|
123
|
-
call inherit_resources in your controller class scope:
|
124
|
-
|
125
|
-
class AccountsController < ApplicationController
|
126
|
-
inherit_resources
|
127
|
-
end
|
128
|
-
|
129
|
-
One reason to use the "inherit_resources" macro would be to ensure that your controller never responds with the html mime-type. InheritedResources::Base already responds_to :html, and the respond_to macro is strictly additive. Therefore, if you want to create a controller that, for example, responds ONLY via :js, you will have write it this way:
|
130
|
-
|
131
|
-
class AccountsController < ApplicationController
|
132
|
-
respond_to :js
|
133
|
-
inherit_resources
|
134
|
-
end
|
135
|
-
|
136
|
-
|
137
|
-
== Overwriting defaults
|
138
|
-
|
139
|
-
Whenever you inherit from InheritedResources, several defaults are assumed.
|
140
|
-
For example you can have an AccountsController for account management while the
|
141
|
-
resource is a User:
|
142
|
-
|
143
|
-
class AccountsController < InheritedResources::Base
|
144
|
-
defaults :resource_class => User, :collection_name => 'users', :instance_name => 'user'
|
145
|
-
end
|
146
|
-
|
147
|
-
In the case above, in your views you will have @users and @user variables, but
|
148
|
-
the routes used will still be accounts_url and account_url. If you plan also to
|
149
|
-
change the routes, you can use :route_collection_name and :route_instance_name.
|
150
|
-
|
151
|
-
Namespaced controllers work out of the box, but if you need to specify a
|
152
|
-
different route prefix you can do the following:
|
153
|
-
|
154
|
-
class Administrators::PeopleController < InheritedResources::Base
|
155
|
-
defaults :route_prefix => 'admin'
|
156
|
-
end
|
157
|
-
|
158
|
-
Then your named routes will be: 'admin_people_url', 'admin_person_url' instead
|
159
|
-
of 'administrators_people_url' and 'administrators_person_url'.
|
160
|
-
|
161
|
-
If you want to customize how resources are retrieved you can overwrite
|
162
|
-
collection and resource methods. The first is called on index action and the
|
163
|
-
second on all other actions. Let's suppose you want to add pagination to your
|
164
|
-
projects collection:
|
165
|
-
|
166
|
-
class ProjectsController < InheritedResources::Base
|
167
|
-
protected
|
168
|
-
def collection
|
169
|
-
@projects ||= end_of_association_chain.paginate(:page => params[:page])
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
The end_of_association_chain returns your resource after nesting all associations
|
174
|
-
and scopes (more about this below).
|
175
|
-
|
176
|
-
InheritedResources also introduces another method called begin_of_association_chain.
|
177
|
-
It's mostly used when you want to create resources based on the @current_user and
|
178
|
-
you have urls like "account/projects". In such cases you have to do
|
179
|
-
@current_user.projects.find or @current_user.projects.build in your actions.
|
180
|
-
|
181
|
-
You can deal with it just by doing:
|
182
|
-
|
183
|
-
class ProjectsController < InheritedResources::Base
|
184
|
-
protected
|
185
|
-
def begin_of_association_chain
|
186
|
-
@current_user
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
== Overwriting actions
|
191
|
-
|
192
|
-
Let's suppose that after destroying a project you want to redirect to your
|
193
|
-
root url instead of redirecting to projects url. You just have to do:
|
194
|
-
|
195
|
-
class ProjectsController < InheritedResources::Base
|
196
|
-
def destroy
|
197
|
-
super do |format|
|
198
|
-
format.html { redirect_to root_url }
|
199
|
-
end
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
You are opening your action and giving the parent action a new behavior. On
|
204
|
-
the other hand, I have to agree that calling super is not very readable. That's
|
205
|
-
why all methods have aliases. So this is equivalent:
|
206
|
-
|
207
|
-
class ProjectsController < InheritedResources::Base
|
208
|
-
def destroy
|
209
|
-
destroy! do |format|
|
210
|
-
format.html { redirect_to root_url }
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
Since most of the time when you change a create, update or destroy
|
216
|
-
action you do so because you want to to change its redirect url, a shortcut is
|
217
|
-
provided. So you can do:
|
218
|
-
|
219
|
-
class ProjectsController < InheritedResources::Base
|
220
|
-
def destroy
|
221
|
-
destroy! { root_url }
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
|
-
If you simply want to change the flash message for a particular action, you can
|
226
|
-
pass the message to the parent action using the keys :notice and :alert (as you
|
227
|
-
would with flash):
|
228
|
-
|
229
|
-
class ProjectsController < InheritedResources::Base
|
230
|
-
def create
|
231
|
-
create!(:notice => "Dude! Nice job creating that project.")
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
You can still pass the block to change the redirect, as mentioned above:
|
236
|
-
|
237
|
-
class ProjectsController < InheritedResources::Base
|
238
|
-
def create
|
239
|
-
create!(:notice => "Dude! Nice job creating that project.") { root_url }
|
240
|
-
end
|
241
|
-
end
|
242
|
-
|
243
|
-
Now let's suppose that before create a project you have to do something special
|
244
|
-
but you don't want to create a before filter for it:
|
245
|
-
|
246
|
-
class ProjectsController < InheritedResources::Base
|
247
|
-
def create
|
248
|
-
@project = Project.new(params[:project])
|
249
|
-
@project.something_special!
|
250
|
-
create!
|
251
|
-
end
|
252
|
-
end
|
253
|
-
|
254
|
-
Yes, it's that simple! The nice part is since you already set the instance variable
|
255
|
-
@project, it will not build a project again.
|
256
|
-
|
257
|
-
Before we finish this topic, we should talk about one more thing: "success/failure
|
258
|
-
blocks". Let's suppose that when we update our project, in case of failure, we
|
259
|
-
want to redirect to the project url instead of re-rendering the edit template.
|
260
|
-
|
261
|
-
Our first attempt to do this would be:
|
262
|
-
|
263
|
-
class ProjectsController < InheritedResources::Base
|
264
|
-
def update
|
265
|
-
update! do |format|
|
266
|
-
unless @project.errors.empty? # failure
|
267
|
-
format.html { redirect_to project_url(@project) }
|
268
|
-
end
|
269
|
-
end
|
270
|
-
end
|
271
|
-
end
|
272
|
-
|
273
|
-
Looks too verbose, right? We can actually do:
|
274
|
-
|
275
|
-
class ProjectsController < InheritedResources::Base
|
276
|
-
def update
|
277
|
-
update! do |success, failure|
|
278
|
-
failure.html { redirect_to project_url(@project) }
|
279
|
-
end
|
280
|
-
end
|
281
|
-
end
|
282
|
-
|
283
|
-
Much better! So explaining everything: when you give a block which expects one
|
284
|
-
argument it will be executed in both scenarios: success and failure. But if you
|
285
|
-
give a block that expects two arguments, the first will be executed only in
|
286
|
-
success scenarios and the second in failure scenarios. You keep everything
|
287
|
-
clean and organized inside the same action.
|
288
|
-
|
289
|
-
== Smart redirects
|
290
|
-
|
291
|
-
Although the syntax above is a nice shortcut, you won't need to do it frequently
|
292
|
-
because (since version 1.2) Inherited Resources has smart redirects. Redirects
|
293
|
-
in actions calculates depending on the existent controller methods.
|
294
|
-
|
295
|
-
Redirects in create and update actions calculates in the following order resource_url,
|
296
|
-
collection_url, parent_url (which we are going to see later), and root_url. Redirect
|
297
|
-
in destroy action calculate in following order collection_url, parent_url, root_url.
|
298
|
-
|
299
|
-
Example:
|
300
|
-
|
301
|
-
class ButtonsController < InheritedResources::Base
|
302
|
-
belongs_to :window
|
303
|
-
actions :all, :except => [:show, :index]
|
304
|
-
end
|
305
|
-
|
306
|
-
This controller redirect to parent window after all CUD actions.
|
307
|
-
|
308
|
-
== Success and failure scenarios on destroy
|
309
|
-
|
310
|
-
The destroy action can also fail, this usually happens when you have a
|
311
|
-
before_destroy callback in your model which returns false. However, in
|
312
|
-
order to tell InheritedResources that it really failed, you need to add
|
313
|
-
errors to your model. So your before_destroy callback on the model should
|
314
|
-
be something like this:
|
315
|
-
|
316
|
-
def before_destroy
|
317
|
-
if cant_be_destroyed?
|
318
|
-
errors.add(:base, "not allowed")
|
319
|
-
false
|
320
|
-
end
|
321
|
-
end
|
322
|
-
|
323
|
-
== Belongs to
|
324
|
-
|
325
|
-
Finally, our Projects are going to get some Tasks. Then you create a
|
326
|
-
TasksController and do:
|
327
|
-
|
328
|
-
class TasksController < InheritedResources::Base
|
329
|
-
belongs_to :project
|
330
|
-
end
|
331
|
-
|
332
|
-
belongs_to accepts several options to be able to configure the association.
|
333
|
-
For example, if you want urls like /projects/:project_title/tasks, you can
|
334
|
-
customize how InheritedResources find your projects:
|
335
|
-
|
336
|
-
class TasksController < InheritedResources::Base
|
337
|
-
belongs_to :project, :finder => :find_by_title!, :param => :project_title
|
338
|
-
end
|
339
|
-
|
340
|
-
It also accepts :route_name, :parent_class and :instance_name as options.
|
341
|
-
Check the lib/inherited_resources/class_methods.rb for more.
|
342
|
-
|
343
|
-
== Nested belongs to
|
344
|
-
|
345
|
-
Now, our Tasks get some Comments and you need to nest even deeper. Good
|
346
|
-
practices says that you should never nest more than two resources, but sometimes
|
347
|
-
you have to for security reasons. So this is an example of how you can do it:
|
348
|
-
|
349
|
-
class CommentsController < InheritedResources::Base
|
350
|
-
nested_belongs_to :project, :task
|
351
|
-
end
|
352
|
-
|
353
|
-
If you need to configure any of these belongs to, you can nest them using blocks:
|
354
|
-
|
355
|
-
class CommentsController < InheritedResources::Base
|
356
|
-
belongs_to :project, :finder => :find_by_title!, :param => :project_title do
|
357
|
-
belongs_to :task
|
358
|
-
end
|
359
|
-
end
|
360
|
-
|
361
|
-
Warning: calling several belongs_to is the same as nesting them:
|
362
|
-
|
363
|
-
class CommentsController < InheritedResources::Base
|
364
|
-
belongs_to :project
|
365
|
-
belongs_to :task
|
366
|
-
end
|
367
|
-
|
368
|
-
In other words, the code above is the same as calling nested_belongs_to.
|
369
|
-
|
370
|
-
== Polymorphic belongs to
|
371
|
-
|
372
|
-
We can go even further. Let's suppose our Projects can now have Files, Messages
|
373
|
-
and Tasks, and they are all commentable. In this case, the best solution is to
|
374
|
-
use polymorphism:
|
375
|
-
|
376
|
-
class CommentsController < InheritedResources::Base
|
377
|
-
belongs_to :task, :file, :message, :polymorphic => true
|
378
|
-
# polymorphic_belongs_to :task, :file, :message
|
379
|
-
end
|
380
|
-
|
381
|
-
You can even use it with nested resources:
|
382
|
-
|
383
|
-
class CommentsController < InheritedResources::Base
|
384
|
-
belongs_to :project do
|
385
|
-
belongs_to :task, :file, :message, :polymorphic => true
|
386
|
-
end
|
387
|
-
end
|
388
|
-
|
389
|
-
The url in such cases can be:
|
390
|
-
|
391
|
-
/project/1/task/13/comments
|
392
|
-
/project/1/file/11/comments
|
393
|
-
/project/1/message/9/comments
|
394
|
-
|
395
|
-
When using polymorphic associations, you get some free helpers:
|
396
|
-
|
397
|
-
parent? #=> true
|
398
|
-
parent_type #=> :task
|
399
|
-
parent_class #=> Task
|
400
|
-
parent #=> @task
|
401
|
-
|
402
|
-
Right now, Inherited Resources is limited and does not allow you
|
403
|
-
to have two polymorphic associations nested.
|
404
|
-
|
405
|
-
== Optional belongs to
|
406
|
-
|
407
|
-
Later you decide to create a view to show all comments, independent if they belong
|
408
|
-
to a task, file or message. You can reuse your polymorphic controller just doing:
|
409
|
-
|
410
|
-
class CommentsController < InheritedResources::Base
|
411
|
-
belongs_to :task, :file, :message, :optional => true
|
412
|
-
# optional_belongs_to :task, :file, :message
|
413
|
-
end
|
414
|
-
|
415
|
-
This will handle all those urls properly:
|
416
|
-
|
417
|
-
/comment/1
|
418
|
-
/tasks/2/comment/5
|
419
|
-
/files/10/comment/3
|
420
|
-
/messages/13/comment/11
|
421
|
-
|
422
|
-
This is treated as a special type of polymorphic associations, thus all helpers
|
423
|
-
are available. As you expect, when no parent is found, the helpers return:
|
424
|
-
|
425
|
-
parent? #=> false
|
426
|
-
parent_type #=> nil
|
427
|
-
parent_class #=> nil
|
428
|
-
parent #=> nil
|
429
|
-
|
430
|
-
== Singletons
|
431
|
-
|
432
|
-
Now we are going to add manager to projects. We say that Manager is a singleton
|
433
|
-
resource because a Project has just one manager. You should declare it as
|
434
|
-
has_one (or resource) in your routes.
|
435
|
-
|
436
|
-
To declare an association as singleton, you just have to give the :singleton
|
437
|
-
option.
|
438
|
-
|
439
|
-
class ManagersController < InheritedResources::Base
|
440
|
-
belongs_to :project, :singleton => true
|
441
|
-
# singleton_belongs_to :project
|
442
|
-
end
|
443
|
-
|
444
|
-
It will deal with everything again and hide the action :index from you.
|
445
|
-
|
446
|
-
== Namespaced Controllers
|
447
|
-
|
448
|
-
Namespaced controllers works out the box.
|
449
|
-
|
450
|
-
class Forum::PostsController < InheritedResources::Base
|
451
|
-
end
|
452
|
-
|
453
|
-
Inherited Resources prioritizes the default resource class for the namespaced controller in
|
454
|
-
this order:
|
455
|
-
|
456
|
-
Forum::Post
|
457
|
-
ForumPost
|
458
|
-
Post
|
459
|
-
|
460
|
-
== URL Helpers
|
461
|
-
|
462
|
-
When you use InheritedResources it creates some URL helpers.
|
463
|
-
And they handle everything for you. :)
|
464
|
-
|
465
|
-
# /posts/1/comments
|
466
|
-
resource_url # => /posts/1/comments/#{@comment.to_param}
|
467
|
-
resource_url(comment) # => /posts/1/comments/#{comment.to_param}
|
468
|
-
new_resource_url # => /posts/1/comments/new
|
469
|
-
edit_resource_url # => /posts/1/comments/#{@comment.to_param}/edit
|
470
|
-
edit_resource_url(comment) # => /posts/1/comments/#{comment.to_param}/edit
|
471
|
-
collection_url # => /posts/1/comments
|
472
|
-
parent_url # => /posts/1
|
473
|
-
|
474
|
-
# /projects/1/tasks
|
475
|
-
resource_url # => /projects/1/tasks/#{@task.to_param}
|
476
|
-
resource_url(task) # => /projects/1/tasks/#{task.to_param}
|
477
|
-
new_resource_url # => /projects/1/tasks/new
|
478
|
-
edit_resource_url # => /projects/1/tasks/#{@task.to_param}/edit
|
479
|
-
edit_resource_url(task) # => /projects/1/tasks/#{task.to_param}/edit
|
480
|
-
collection_url # => /projects/1/tasks
|
481
|
-
parent_url # => /projects/1
|
482
|
-
|
483
|
-
# /users
|
484
|
-
resource_url # => /users/#{@user.to_param}
|
485
|
-
resource_url(user) # => /users/#{user.to_param}
|
486
|
-
new_resource_url # => /users/new
|
487
|
-
edit_resource_url # => /users/#{@user.to_param}/edit
|
488
|
-
edit_resource_url(user) # => /users/#{user.to_param}/edit
|
489
|
-
collection_url # => /users
|
490
|
-
parent_url # => /
|
491
|
-
|
492
|
-
Those urls helpers also accepts a hash as options, just as in named routes.
|
493
|
-
|
494
|
-
# /projects/1/tasks
|
495
|
-
collection_url(:page => 1, :limit => 10) #=> /projects/1/tasks?page=1&limit=10
|
496
|
-
|
497
|
-
In polymorphic cases, you can also give the parent as parameter to collection_url.
|
498
|
-
|
499
|
-
Another nice thing is that those urls are not guessed during runtime. They are
|
500
|
-
all created when your application is loaded (except for polymorphic
|
501
|
-
associations, that relies on Rails polymorphic_url).
|
502
|
-
|
503
|
-
== Custom actions
|
504
|
-
|
505
|
-
Since version 1.2, Inherited Resources allows you to define custom actions in controller:
|
506
|
-
|
507
|
-
class ButtonsController < InheritedResources::Base
|
508
|
-
custom_actions :resource => :delete, :collection => :search
|
509
|
-
end
|
510
|
-
|
511
|
-
This code creates delete and search actions in controller (they behaves like show and
|
512
|
-
index actions accordingly). Also, it will produce delete_resource_{path,url} and
|
513
|
-
search_resources_{path,url} url helpers.
|
514
|
-
|
515
|
-
== What about views?
|
516
|
-
|
517
|
-
Sometimes just DRYing up the controllers is not enough. If you need to DRY up your views,
|
518
|
-
check this Wiki page:
|
519
|
-
|
520
|
-
https://github.com/josevalim/inherited_resources/wiki/Views-Inheritance
|
521
|
-
|
522
|
-
Notice that Rails 3.1 ships with view inheritance built-in.
|
523
|
-
|
524
|
-
== Some DSL
|
525
|
-
|
526
|
-
For those DSL lovers, InheritedResources won't leave you alone. You can overwrite
|
527
|
-
your success/failure blocks straight from your class binding. For it, you just
|
528
|
-
need to add a DSL module to your application controller:
|
529
|
-
|
530
|
-
class ApplicationController < ActionController::Base
|
531
|
-
include InheritedResources::DSL
|
532
|
-
end
|
533
|
-
|
534
|
-
And then you can rewrite the last example as:
|
535
|
-
|
536
|
-
class ProjectsController < InheritedResources::Base
|
537
|
-
update! do |success, failure|
|
538
|
-
failure.html { redirect_to project_url(@project) }
|
539
|
-
end
|
540
|
-
end
|
541
|
-
|
542
|
-
== Strong Parameters
|
543
|
-
|
544
|
-
If your controller defines a method named permitted_params, Inherited Resources will call it where it would normally call params. This allows for easy integration with the strong_parameters gem:
|
545
|
-
|
546
|
-
def permitted_params
|
547
|
-
params.permit(:widget => [:permitted_field, :other_permitted_field])
|
548
|
-
end
|
549
|
-
|
550
|
-
Note that this doesn't work if you use strong_parameters' require method instead of permit, because whereas permit returns the entire sanitized parameter hash, require returns only the sanitized params below the parameter you required.
|
551
|
-
|
552
|
-
== Bugs and Feedback
|
553
|
-
|
554
|
-
If you discover any bugs, please describe it in the issues tracker, including Rails and Inherited Resources versions.
|
555
|
-
|
556
|
-
Questions are better handled on StackOverflow.
|
557
|
-
|
558
|
-
Copyright (c) 2009-2012 José Valim http://blog.plataformatec.com.br
|
559
|
-
See the attached MIT License.
|