karsthammer-inherited_resources 1.1.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 +119 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +516 -0
- data/Rakefile +43 -0
- data/lib/generators/rails/USAGE +10 -0
- data/lib/generators/rails/inherited_resources_controller_generator.rb +11 -0
- data/lib/generators/rails/templates/controller.rb +5 -0
- data/lib/inherited_resources.rb +37 -0
- data/lib/inherited_resources/actions.rb +67 -0
- data/lib/inherited_resources/base.rb +44 -0
- data/lib/inherited_resources/base_helpers.rb +270 -0
- data/lib/inherited_resources/belongs_to_helpers.rb +97 -0
- data/lib/inherited_resources/blank_slate.rb +12 -0
- data/lib/inherited_resources/class_methods.rb +267 -0
- data/lib/inherited_resources/dsl.rb +26 -0
- data/lib/inherited_resources/polymorphic_helpers.rb +155 -0
- data/lib/inherited_resources/responder.rb +6 -0
- data/lib/inherited_resources/singleton_helpers.rb +95 -0
- data/lib/inherited_resources/url_helpers.rb +188 -0
- data/lib/inherited_resources/version.rb +3 -0
- data/test/aliases_test.rb +144 -0
- data/test/association_chain_test.rb +125 -0
- data/test/base_test.rb +278 -0
- data/test/belongs_to_test.rb +105 -0
- data/test/class_methods_test.rb +132 -0
- data/test/customized_base_test.rb +168 -0
- data/test/customized_belongs_to_test.rb +76 -0
- data/test/defaults_test.rb +70 -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/singleton_test.rb +83 -0
- data/test/test_helper.rb +40 -0
- data/test/url_helpers_test.rb +665 -0
- metadata +142 -0
@@ -0,0 +1,97 @@
|
|
1
|
+
module InheritedResources
|
2
|
+
|
3
|
+
# = belongs_to
|
4
|
+
#
|
5
|
+
# Let's suppose that we have some tasks that belongs to projects. To specify
|
6
|
+
# this assoication in your controllers, just do:
|
7
|
+
#
|
8
|
+
# class TasksController < InheritedResources::Base
|
9
|
+
# belongs_to :project
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# belongs_to accepts several options to be able to configure the association.
|
13
|
+
# For example, if you want urls like /projects/:project_title/tasks, you
|
14
|
+
# can customize how InheritedResources find your projects:
|
15
|
+
#
|
16
|
+
# class TasksController < InheritedResources::Base
|
17
|
+
# belongs_to :project, :finder => :find_by_title!, :param => :project_title
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# It also accepts :route_name, :parent_class and :instance_name as options.
|
21
|
+
# Check the lib/inherited_resources/class_methods.rb for more.
|
22
|
+
#
|
23
|
+
# = nested_belongs_to
|
24
|
+
#
|
25
|
+
# Now, our Tasks get some Comments and you need to nest even deeper. Good
|
26
|
+
# practices says that you should never nest more than two resources, but sometimes
|
27
|
+
# you have to for security reasons. So this is an example of how you can do it:
|
28
|
+
#
|
29
|
+
# class CommentsController < InheritedResources::Base
|
30
|
+
# nested_belongs_to :project, :task
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# If you need to configure any of these belongs to, you can nested them using blocks:
|
34
|
+
#
|
35
|
+
# class CommentsController < InheritedResources::Base
|
36
|
+
# belongs_to :project, :finder => :find_by_title!, :param => :project_title do
|
37
|
+
# belongs_to :task
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# Warning: calling several belongs_to is the same as nesting them:
|
42
|
+
#
|
43
|
+
# class CommentsController < InheritedResources::Base
|
44
|
+
# belongs_to :project
|
45
|
+
# belongs_to :task
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# In other words, the code above is the same as calling nested_belongs_to.
|
49
|
+
#
|
50
|
+
module BelongsToHelpers
|
51
|
+
|
52
|
+
protected
|
53
|
+
|
54
|
+
# Parent is always true when belongs_to is called.
|
55
|
+
#
|
56
|
+
def parent?
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
def parent
|
61
|
+
@parent ||= association_chain[-1]
|
62
|
+
end
|
63
|
+
|
64
|
+
def parent_type
|
65
|
+
parent.class.name.underscore.to_sym
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
# Evaluate the parent given. This is used to nest parents in the
|
71
|
+
# association chain.
|
72
|
+
#
|
73
|
+
def evaluate_parent(parent_symbol, parent_config, chain = nil) #:nodoc:
|
74
|
+
instantiated_object = instance_variable_get("@#{parent_config[:instance_name]}")
|
75
|
+
return instantiated_object if instantiated_object
|
76
|
+
|
77
|
+
parent = if chain
|
78
|
+
chain.send(parent_config[:collection_name])
|
79
|
+
else
|
80
|
+
parent_config[:parent_class]
|
81
|
+
end
|
82
|
+
|
83
|
+
parent = parent.send(parent_config[:finder], params[parent_config[:param]])
|
84
|
+
|
85
|
+
instance_variable_set("@#{parent_config[:instance_name]}", parent)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Maps parents_symbols to build association chain. In this case, it
|
89
|
+
# simply return the parent_symbols, however on polymorphic belongs to,
|
90
|
+
# it has some customization.
|
91
|
+
#
|
92
|
+
def symbols_for_association_chain #:nodoc:
|
93
|
+
parents_symbols
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,267 @@
|
|
1
|
+
module InheritedResources
|
2
|
+
module ClassMethods
|
3
|
+
|
4
|
+
protected
|
5
|
+
|
6
|
+
# Used to overwrite the default assumptions InheritedResources do. Whenever
|
7
|
+
# this method is called, it should be on the top of your controller, since
|
8
|
+
# almost other methods depends on the values given to <<tt>>defaults</tt>.
|
9
|
+
#
|
10
|
+
# == Options
|
11
|
+
#
|
12
|
+
# * <tt>:resource_class</tt> - The resource class which by default is guessed
|
13
|
+
# by the controller name. Defaults to Project in
|
14
|
+
# ProjectsController.
|
15
|
+
#
|
16
|
+
# * <tt>:collection_name</tt> - The name of the collection instance variable which
|
17
|
+
# is set on the index action. Defaults to :projects in
|
18
|
+
# ProjectsController.
|
19
|
+
#
|
20
|
+
# * <tt>:instance_name</tt> - The name of the singular instance variable which
|
21
|
+
# is set on all actions besides index action. Defaults to
|
22
|
+
# :project in ProjectsController.
|
23
|
+
#
|
24
|
+
# * <tt>:route_collection_name</tt> - The name of the collection route. Defaults to :collection_name.
|
25
|
+
#
|
26
|
+
# * <tt>:route_instance_name</tt> - The name of the singular route. Defaults to :instance_name.
|
27
|
+
#
|
28
|
+
# * <tt>:route_prefix</tt> - The route prefix which is automically set in namespaced
|
29
|
+
# controllers. Default to :admin on Admin::ProjectsController.
|
30
|
+
#
|
31
|
+
# * <tt>:singleton</tt> - Tells if this controller is singleton or not.
|
32
|
+
#
|
33
|
+
def defaults(options)
|
34
|
+
raise ArgumentError, 'Class method :defaults expects a hash of options.' unless options.is_a? Hash
|
35
|
+
|
36
|
+
options.symbolize_keys!
|
37
|
+
options.assert_valid_keys(:resource_class, :collection_name, :instance_name,
|
38
|
+
:class_name, :route_prefix, :route_collection_name,
|
39
|
+
:route_instance_name, :singleton)
|
40
|
+
|
41
|
+
self.resource_class = options.delete(:resource_class) if options.key?(:resource_class)
|
42
|
+
self.resource_class = options.delete(:class_name).constantize if options.key?(:class_name)
|
43
|
+
|
44
|
+
acts_as_singleton! if options.delete(:singleton)
|
45
|
+
|
46
|
+
config = self.resources_configuration[:self]
|
47
|
+
config[:route_prefix] = options.delete(:route_prefix) if options.key?(:route_prefix)
|
48
|
+
|
49
|
+
options.each do |key, value|
|
50
|
+
config[key] = value.to_sym
|
51
|
+
end
|
52
|
+
|
53
|
+
create_resources_url_helpers!
|
54
|
+
end
|
55
|
+
|
56
|
+
# Defines wich actions to keep from the inherited controller.
|
57
|
+
# Syntax is borrowed from resource_controller.
|
58
|
+
#
|
59
|
+
# actions :index, :show, :edit
|
60
|
+
# actions :all, :except => :index
|
61
|
+
#
|
62
|
+
def actions(*actions_to_keep)
|
63
|
+
raise ArgumentError, 'Wrong number of arguments. You have to provide which actions you want to keep.' if actions_to_keep.empty?
|
64
|
+
|
65
|
+
options = actions_to_keep.extract_options!
|
66
|
+
actions_to_remove = Array(options[:except])
|
67
|
+
actions_to_remove += ACTIONS - actions_to_keep.map { |a| a.to_sym } unless actions_to_keep.first == :all
|
68
|
+
actions_to_remove.map! { |a| a.to_sym }.uniq!
|
69
|
+
(instance_methods.map { |m| m.to_sym } & actions_to_remove).each do |action|
|
70
|
+
undef_method action, "#{action}!"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Defines that this controller belongs to another resource.
|
75
|
+
#
|
76
|
+
# belongs_to :projects
|
77
|
+
#
|
78
|
+
# == Options
|
79
|
+
#
|
80
|
+
# * <tt>:parent_class</tt> - Allows you to specify what is the parent class.
|
81
|
+
#
|
82
|
+
# belongs_to :project, :parent_class => AdminProject
|
83
|
+
#
|
84
|
+
# * <tt>:class_name</tt> - Also allows you to specify the parent class, but you should
|
85
|
+
# give a string. Added for ActiveRecord belongs to compatibility.
|
86
|
+
#
|
87
|
+
# * <tt>:instance_name</tt> - The instance variable name. By default is the name of the association.
|
88
|
+
#
|
89
|
+
# belongs_to :project, :instance_name => :my_project
|
90
|
+
#
|
91
|
+
# * <tt>:finder</tt> - Specifies which method should be called to instantiate the parent.
|
92
|
+
#
|
93
|
+
# belongs_to :project, :finder => :find_by_title!
|
94
|
+
#
|
95
|
+
# This will make your projects be instantiated as:
|
96
|
+
#
|
97
|
+
# Project.find_by_title!(params[:project_id])
|
98
|
+
#
|
99
|
+
# Instead of:
|
100
|
+
#
|
101
|
+
# Project.find(params[:project_id])
|
102
|
+
#
|
103
|
+
# * <tt>:param</tt> - Allows you to specify params key to retrieve the id.
|
104
|
+
# Default is :association_id, which in this case is :project_id.
|
105
|
+
#
|
106
|
+
# * <tt>:route_name</tt> - Allows you to specify what is the route name in your url
|
107
|
+
# helper. By default is association name.
|
108
|
+
#
|
109
|
+
# * <tt>:collection_name</tt> - Tell how to retrieve the next collection. Let's
|
110
|
+
# suppose you have Tasks which belongs to Projects
|
111
|
+
# which belongs to companies. This will do somewhere
|
112
|
+
# down the road:
|
113
|
+
#
|
114
|
+
# @company.projects
|
115
|
+
#
|
116
|
+
# But if you want to retrieve instead:
|
117
|
+
#
|
118
|
+
# @company.admin_projects
|
119
|
+
#
|
120
|
+
# You supply the collection name.
|
121
|
+
#
|
122
|
+
# * <tt>:polymorphic</tt> - Tell the association is polymorphic.
|
123
|
+
#
|
124
|
+
# * <tt>:singleton</tt> - Tell it's a singleton association.
|
125
|
+
#
|
126
|
+
# * <tt>:optional</tt> - Tell the association is optional (it's a special
|
127
|
+
# type of polymorphic association)
|
128
|
+
#
|
129
|
+
def belongs_to(*symbols, &block)
|
130
|
+
options = symbols.extract_options!
|
131
|
+
|
132
|
+
options.symbolize_keys!
|
133
|
+
options.assert_valid_keys(:class_name, :parent_class, :instance_name, :param,
|
134
|
+
:finder, :route_name, :collection_name, :singleton,
|
135
|
+
:polymorphic, :optional)
|
136
|
+
|
137
|
+
optional = options.delete(:optional)
|
138
|
+
singleton = options.delete(:singleton)
|
139
|
+
polymorphic = options.delete(:polymorphic)
|
140
|
+
finder = options.delete(:finder)
|
141
|
+
|
142
|
+
include BelongsToHelpers if self.parents_symbols.empty?
|
143
|
+
|
144
|
+
acts_as_singleton! if singleton
|
145
|
+
acts_as_polymorphic! if polymorphic || optional
|
146
|
+
|
147
|
+
raise ArgumentError, 'You have to give me at least one association name.' if symbols.empty?
|
148
|
+
raise ArgumentError, 'You cannot define multiple associations with options: #{options.keys.inspect} to belongs to.' unless symbols.size == 1 || options.empty?
|
149
|
+
|
150
|
+
symbols.each do |symbol|
|
151
|
+
symbol = symbol.to_sym
|
152
|
+
|
153
|
+
if polymorphic || optional
|
154
|
+
self.parents_symbols << :polymorphic unless self.parents_symbols.include?(:polymorphic)
|
155
|
+
self.resources_configuration[:polymorphic][:symbols] << symbol
|
156
|
+
self.resources_configuration[:polymorphic][:optional] ||= optional
|
157
|
+
else
|
158
|
+
self.parents_symbols << symbol
|
159
|
+
end
|
160
|
+
|
161
|
+
config = self.resources_configuration[symbol] = {}
|
162
|
+
|
163
|
+
config[:parent_class] = options.delete(:parent_class) || begin
|
164
|
+
class_name = (options.delete(:class_name) || symbol).to_s.pluralize.classify
|
165
|
+
class_name.constantize
|
166
|
+
rescue NameError => e
|
167
|
+
raise unless e.message.include?(class_name)
|
168
|
+
nil
|
169
|
+
end
|
170
|
+
|
171
|
+
config[:collection_name] = options.delete(:collection_name) || symbol.to_s.pluralize.to_sym
|
172
|
+
config[:instance_name] = options.delete(:instance_name) || symbol
|
173
|
+
config[:param] = options.delete(:param) || :"#{symbol}_id"
|
174
|
+
config[:route_name] = options.delete(:route_name) || symbol
|
175
|
+
config[:finder] = finder || :find
|
176
|
+
end
|
177
|
+
|
178
|
+
if block_given?
|
179
|
+
class_eval(&block)
|
180
|
+
else
|
181
|
+
create_resources_url_helpers!
|
182
|
+
end
|
183
|
+
helper_method :parent, :parent?
|
184
|
+
end
|
185
|
+
alias :nested_belongs_to :belongs_to
|
186
|
+
|
187
|
+
# A quick method to declare polymorphic belongs to.
|
188
|
+
#
|
189
|
+
def polymorphic_belongs_to(*symbols, &block)
|
190
|
+
options = symbols.extract_options!
|
191
|
+
options.merge!(:polymorphic => true)
|
192
|
+
belongs_to(*symbols << options, &block)
|
193
|
+
end
|
194
|
+
|
195
|
+
# A quick method to declare singleton belongs to.
|
196
|
+
#
|
197
|
+
def singleton_belongs_to(*symbols, &block)
|
198
|
+
options = symbols.extract_options!
|
199
|
+
options.merge!(:singleton => true)
|
200
|
+
belongs_to(*symbols << options, &block)
|
201
|
+
end
|
202
|
+
|
203
|
+
# A quick method to declare optional belongs to.
|
204
|
+
#
|
205
|
+
def optional_belongs_to(*symbols, &block)
|
206
|
+
options = symbols.extract_options!
|
207
|
+
options.merge!(:optional => true)
|
208
|
+
belongs_to(*symbols << options, &block)
|
209
|
+
end
|
210
|
+
|
211
|
+
private
|
212
|
+
|
213
|
+
def acts_as_singleton! #:nodoc:
|
214
|
+
unless self.resources_configuration[:self][:singleton]
|
215
|
+
self.resources_configuration[:self][:singleton] = true
|
216
|
+
include SingletonHelpers
|
217
|
+
actions :all, :except => :index
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def acts_as_polymorphic! #:nodoc:
|
222
|
+
unless self.parents_symbols.include?(:polymorphic)
|
223
|
+
include PolymorphicHelpers
|
224
|
+
helper_method :parent_type, :parent_class
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
# Initialize resources class accessors and set their default values.
|
229
|
+
#
|
230
|
+
def initialize_resources_class_accessors! #:nodoc:
|
231
|
+
# Initialize resource class
|
232
|
+
self.resource_class = begin
|
233
|
+
class_name = self.controller_name.classify
|
234
|
+
class_name.constantize
|
235
|
+
rescue NameError => e
|
236
|
+
raise unless e.message.include?(class_name)
|
237
|
+
nil
|
238
|
+
end
|
239
|
+
|
240
|
+
# Initialize resources configuration hash
|
241
|
+
self.resources_configuration ||= {}
|
242
|
+
config = self.resources_configuration[:self] = {}
|
243
|
+
config[:collection_name] = self.controller_name.to_sym
|
244
|
+
config[:instance_name] = self.controller_name.singularize.to_sym
|
245
|
+
|
246
|
+
config[:route_collection_name] = config[:collection_name]
|
247
|
+
config[:route_instance_name] = config[:instance_name]
|
248
|
+
|
249
|
+
# Deal with namespaced controllers
|
250
|
+
namespaces = self.controller_path.split('/')[0..-2]
|
251
|
+
config[:route_prefix] = namespaces.join('_') unless namespaces.empty?
|
252
|
+
|
253
|
+
# Initialize polymorphic, singleton, scopes and belongs_to parameters
|
254
|
+
self.parents_symbols ||= []
|
255
|
+
self.resources_configuration[:polymorphic] ||= { :symbols => [], :optional => false }
|
256
|
+
end
|
257
|
+
|
258
|
+
# Hook called on inheritance.
|
259
|
+
#
|
260
|
+
def inherited(base) #:nodoc:
|
261
|
+
super(base)
|
262
|
+
base.send :initialize_resources_class_accessors!
|
263
|
+
base.send :create_resources_url_helpers!
|
264
|
+
end
|
265
|
+
|
266
|
+
end
|
267
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module InheritedResources
|
2
|
+
# Allows controllers to write actions using a class method DSL.
|
3
|
+
#
|
4
|
+
# class MyController < InheritedResources::Base
|
5
|
+
# create! do |success, failure|
|
6
|
+
# success.html { render :text => "It works!" }
|
7
|
+
# end
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
module DSL
|
11
|
+
def self.included(base)
|
12
|
+
ACTIONS.each do |action|
|
13
|
+
base.class_eval <<-WRITTER
|
14
|
+
def self.#{action}!(options={}, &block)
|
15
|
+
define_method :__#{action}, &block
|
16
|
+
class_eval <<-ACTION
|
17
|
+
def #{action}
|
18
|
+
super(\#{options.inspect}, &method(:__#{action}))
|
19
|
+
end
|
20
|
+
ACTION
|
21
|
+
end
|
22
|
+
WRITTER
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
module InheritedResources
|
2
|
+
|
3
|
+
# = polymorphic associations
|
4
|
+
#
|
5
|
+
# In some cases you have a resource that belongs to two different resources
|
6
|
+
# but not at the same time. For example, let's suppose you have File, Message
|
7
|
+
# and Task as resources and they are all commentable.
|
8
|
+
#
|
9
|
+
# Polymorphic associations allows you to create just one controller that will
|
10
|
+
# deal with each case.
|
11
|
+
#
|
12
|
+
# class Comment < InheritedResources::Base
|
13
|
+
# belongs_to :file, :message, :task, :polymorphic => true
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# Your routes should be something like:
|
17
|
+
#
|
18
|
+
# m.resources :files, :has_many => :comments #=> /files/13/comments
|
19
|
+
# m.resources :tasks, :has_many => :comments #=> /tasks/17/comments
|
20
|
+
# m.resources :messages, :has_many => :comments #=> /messages/11/comments
|
21
|
+
#
|
22
|
+
# When using polymorphic associations, you get some free helpers:
|
23
|
+
#
|
24
|
+
# parent? #=> true
|
25
|
+
# parent_type #=> :task
|
26
|
+
# parent_class #=> Task
|
27
|
+
# parent #=> @task
|
28
|
+
#
|
29
|
+
# This polymorphic controllers thing is a great idea by James Golick and he
|
30
|
+
# built it in resource_controller. Here is just a re-implementation.
|
31
|
+
#
|
32
|
+
# = optional polymorphic associations
|
33
|
+
#
|
34
|
+
# Let's take another break from ProjectsController. Let's suppose we are
|
35
|
+
# building a store, which sell products.
|
36
|
+
#
|
37
|
+
# On the website, we can show all products, but also products scoped to
|
38
|
+
# categories, brands, users. In this case case, the association is optional, and
|
39
|
+
# we deal with it in the following way:
|
40
|
+
#
|
41
|
+
# class ProductsController < InheritedResources::Base
|
42
|
+
# belongs_to :category, :brand, :user, :polymorphic => true, :optional => true
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# This will handle all those urls properly:
|
46
|
+
#
|
47
|
+
# /products/1
|
48
|
+
# /categories/2/products/5
|
49
|
+
# /brands/10/products/3
|
50
|
+
# /user/13/products/11
|
51
|
+
#
|
52
|
+
# = nested polymorphic associations
|
53
|
+
#
|
54
|
+
# You can have polymorphic associations with nested resources. Let's suppose
|
55
|
+
# that our File, Task and Message resources in the previous example belongs to
|
56
|
+
# a project.
|
57
|
+
#
|
58
|
+
# This way we can have:
|
59
|
+
#
|
60
|
+
# class CommentsController < InheritedResources::Base
|
61
|
+
# belongs_to :project {
|
62
|
+
# belongs_to :file, :message, :task, :polymorphic => true
|
63
|
+
# }
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# Or:
|
67
|
+
#
|
68
|
+
# class CommentsController < InheritedResources::Base
|
69
|
+
# nested_belongs_to :project
|
70
|
+
# nested_belongs_to :file, :message, :task, :polymorphic => true
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# Choose the syntax that makes more sense to you. :)
|
74
|
+
#
|
75
|
+
# Finally your routes should be something like:
|
76
|
+
#
|
77
|
+
# map.resources :projects do |m|
|
78
|
+
# m.resources :files, :has_many => :comments #=> /projects/1/files/13/comments
|
79
|
+
# m.resources :tasks, :has_many => :comments #=> /projects/1/tasks/17/comments
|
80
|
+
# m.resources :messages, :has_many => :comments #=> /projects/1/messages/11/comments
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# The helpers work in the same way as above.
|
84
|
+
#
|
85
|
+
module PolymorphicHelpers
|
86
|
+
|
87
|
+
protected
|
88
|
+
|
89
|
+
# Returns the parent type. A Comments class can have :task, :file, :note
|
90
|
+
# as parent types.
|
91
|
+
#
|
92
|
+
def parent_type
|
93
|
+
@parent_type
|
94
|
+
end
|
95
|
+
|
96
|
+
def parent_class
|
97
|
+
parent.class if @parent_type
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns the parent object. They are also available with the instance
|
101
|
+
# variable name: @task, @file, @note...
|
102
|
+
#
|
103
|
+
def parent
|
104
|
+
instance_variable_get("@#{@parent_type}") if @parent_type
|
105
|
+
end
|
106
|
+
|
107
|
+
# If the polymorphic association is optional, we might not have a parent.
|
108
|
+
#
|
109
|
+
def parent?
|
110
|
+
if resources_configuration[:polymorphic][:optional]
|
111
|
+
parents_symbols.size > 1 || !@parent_type.nil?
|
112
|
+
else
|
113
|
+
true
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
# Maps parents_symbols to build association chain.
|
120
|
+
#
|
121
|
+
# If the parents_symbols find :polymorphic, it goes through the
|
122
|
+
# params keys to see which polymorphic parent matches the given params.
|
123
|
+
#
|
124
|
+
# When optional is given, it does not raise errors if the polymorphic
|
125
|
+
# params are missing.
|
126
|
+
#
|
127
|
+
def symbols_for_association_chain #:nodoc:
|
128
|
+
polymorphic_config = resources_configuration[:polymorphic]
|
129
|
+
parents_symbols.map do |symbol|
|
130
|
+
if symbol == :polymorphic
|
131
|
+
params_keys = params.keys
|
132
|
+
|
133
|
+
keys = polymorphic_config[:symbols].map do |poly|
|
134
|
+
params_keys.include?(resources_configuration[poly][:param].to_s) ? poly : nil
|
135
|
+
end.compact
|
136
|
+
|
137
|
+
if keys.empty?
|
138
|
+
raise ScriptError, "Could not find param for polymorphic association. The request" <<
|
139
|
+
"parameters are #{params.keys.inspect} and the polymorphic " <<
|
140
|
+
"associations are #{polymorphic_config[:symbols].inspect}." unless polymorphic_config[:optional]
|
141
|
+
|
142
|
+
nil
|
143
|
+
else
|
144
|
+
@parent_type = keys[-1].to_sym
|
145
|
+
@parent_types = keys.map(&:to_sym)
|
146
|
+
end
|
147
|
+
else
|
148
|
+
symbol
|
149
|
+
end
|
150
|
+
end.flatten.compact
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|