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