acts_has_many 0.1.5 → 0.2.0

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.
@@ -1,358 +0,0 @@
1
- module ActiveRecord
2
- module Acts #:nodoc:
3
- module HasMany
4
-
5
- # class methods for use in model
6
- # +acts_has_many_for+
7
- # +acts_has_many+
8
- #
9
- # the last method added:
10
- #
11
- # class method:
12
- # +has_many_through_update+
13
- # +dependent_relations+
14
- # +compare+
15
- #
16
- # Instance methods:
17
- # +model+
18
- # +has_many_update+
19
- # +has_many_update!+
20
- # +update_with_<relation>+
21
- # +actuale?+
22
- #
23
- # set +before_destroy+ callback;
24
-
25
- def self.included base
26
- base.extend ClassMethods
27
- end
28
-
29
- #
30
- # Acts Has Many gem is for added functional to work
31
- # with +has_many+ relation (additional is has_many :trhough)
32
- #
33
- # +acts_has_many+ and +acts_has_many_for+ are class methods for all model,
34
- # and you can use them for connection acts_has_many functional to necessary model
35
- # acts_has_many set in model where is has_many relation and
36
- # acts_has_many_for in model where use model with acts_has_many
37
- #
38
- # class Education < ActiveRecord::Base
39
- # belongs_to :location
40
- #
41
- # acts_has_many_for :location
42
- # end
43
- #
44
- # class Location < ActiveRecord::Base
45
- # has_many :educaitons
46
- # acts_has_many
47
- # end
48
- #
49
- # You can use +acts_has_many+ methods without +acts_has_many_for+
50
- #
51
- # class Education < ActiveRecord::Base
52
- # belongs_to :location
53
- #
54
- # end
55
- #
56
- # class Location < ActiveRecord::Base
57
- # has_many :education
58
- # acts_has_many
59
- # end
60
- #
61
- # in this case you can use nex function:
62
- #
63
- # education = Education.first
64
- # location = education.location # get location from education
65
- #
66
- # new_id, del_id = location.has_many_update(data: {title:"Kyiv"}, relation: :educations)
67
- # # or simple
68
- # new_id, del_id = location.has_many_update({title:"Kyiv"}, :educations)
69
- # # or with dinamic methods helper
70
- # new_id, del_id = location.update_with_educations {title:"Kyiv"}
71
- #
72
- # # you can also use has_many_update! which updated relation with parent row without you
73
- # location.has_many_update!({title: "Kyiv"}, education)
74
- #
75
- # with usage +acts_has_many_for+ you don't need set relation and give parent row in case with has_many_update!
76
- # but you need get row for update get per ralation from parent row (see above with location row) in other case
77
- # you can't use it becaus methods will not know about relation and paren row for update it.
78
- #
79
- # new_id, del_id = location.has_many_update {title:"Kyiv"}
80
- # # and
81
- # location.has_many_update! {title:"Kyiv"}
82
- #
83
-
84
- module ClassMethods
85
-
86
- #
87
- # +acts_has_many_for+: use with +acts_has_many+
88
- # use for set link between parent row and child
89
- # options: list relations (symbol)
90
- #
91
-
92
- def acts_has_many_for *relations
93
- relations.each do |relation|
94
- relation = relation.to_s
95
- class_eval <<-EOV
96
- def #{relation}
97
- if #{relation.foreign_key}
98
- row = #{relation.classify}.find #{relation.foreign_key}
99
- if row.is_a? #{relation.classify}
100
- row.tmp_parrent_id = id
101
- row.tmp_current_relation = '#{self.name.tableize}'
102
- end
103
- row
104
- else
105
- super
106
- end
107
- end
108
- EOV
109
- end
110
- end
111
-
112
- #
113
- # +acts_has_many+ - available method in all model and switch on
114
- # extension functional in concrete model (need located this method
115
- # after relation which include in dependence, or anywhere but set depend relation)
116
- # options
117
- # :compare( symbol, string)- name column for compare with other element in table
118
- # :relations( array) - concrete name of depended relation
119
- # :through( boolean) - off or on has_many_through_update method
120
- #
121
-
122
- def acts_has_many *opt
123
- options = { compare: :title, through: false }
124
- options.update opt.extract_options!
125
- options.assert_valid_keys :compare, :through, :relations
126
- if options[:relations]
127
- ActiveSupport::Deprecation.warn "Use simple list relations insted 'relation: Array'! Parameter 'relation: []' will be romoved in v1.0!"
128
- end
129
- options[:relations] ||= opt
130
-
131
- options[:relations] = self.reflect_on_all_associations(:has_many)
132
- .map(&:name) if options[:relations].blank?
133
-
134
- dependent_relations = []
135
- options[:relations].each do |relation|
136
- if reflect_on_association relation.to_sym
137
- dependent_relations << relation.to_s.tableize
138
- else
139
- raise ArgumentError, "No association found for name `#{relation}'. Has it been defined yet?"
140
- end
141
- end
142
-
143
- #
144
- # +has_many_through_update+ (return array) [ 1 - array objects records, 2 - array delete ids ]
145
- # options
146
- # :update( array) - data for update (id and data)
147
- # :new( array) - data for create record (data)
148
- #
149
- # +for delete need use method destroy !!!+
150
- #
151
-
152
- has_many_through = ''
153
- if options[:through]
154
- has_many_through = """
155
- def self.has_many_through_update(options)
156
- record_add = []
157
- record_del = []
158
-
159
- # update
160
- options[:update].each do |id, data|
161
- add, del = #{self}.find(id).has_many_update data, options[:relation]
162
- record_add << add unless add.nil?
163
- record_del << del unless del.nil?
164
- end unless options[:update].nil?
165
-
166
- # new
167
- unless options[:new].nil?
168
- options[:new].uniq!
169
- options[:new].each do |data|
170
- data = data.symbolize_keys
171
- record_add << #{self}
172
- .where('#{options[:compare]}' => data['#{options[:compare]}'.to_sym])
173
- .first_or_create(data)
174
- record_del.delete record_add.last.id
175
- end
176
- end
177
-
178
- record_add = #{self}.where('id IN (?)', record_add) unless record_add.empty?
179
- [record_add, record_del]
180
- end """
181
- end
182
-
183
- # add dinamic methods for example
184
- # update_with_<relation>(data) equal has_many_update(data, relation)
185
- extend_methods = ''
186
- dependent_relations.each do |relation|
187
- extend_methods += """
188
- def update_with_#{relation}(data)
189
- has_many_update data, :#{relation}
190
- end
191
- """
192
- end
193
-
194
- class_eval <<-EOV
195
- include ActiveRecord::Acts::HasMany::InstanceMethods
196
- class << self
197
- def dependent_relations
198
- #{dependent_relations}
199
- end
200
- def compare
201
- '#{options[:compare]}'.to_sym
202
- end
203
- end
204
-
205
- def model
206
- #{self}
207
- end
208
-
209
- #{extend_methods}
210
- #{has_many_through}
211
-
212
- attr_accessor :tmp_current_relation, :tmp_parrent_id
213
- validates :#{options[:compare]}, uniqueness: true, presence: true
214
- before_destroy :destroy_filter
215
- EOV
216
- end
217
- end
218
-
219
- module InstanceMethods
220
-
221
- #
222
- # +has_many_update!+ identicaly to has_many_update but
223
- # You can use this method when you use +acts_has_many_for+
224
- # and get object for update with help of +relation+ or give parent row
225
- # option
226
- # data - date for update
227
- # obj - parrent row (maybe miss)
228
- #
229
-
230
- def has_many_update! *data
231
- if data.size == 2
232
- self.tmp_current_relation = data[1].class.name.tableize
233
- self.tmp_parrent_id = data[1].id
234
- end
235
-
236
- if tmp_current_relation.nil? or tmp_parrent_id.nil?
237
- raise ArgumentError, """has_many_update don't have data about parent object,
238
- * maybe you use 'acts_has_many_for' incorrectly,
239
- * if you don't use 'acts_has_many_for' in parent model you can give parent object"""
240
- end
241
-
242
- new_id, del_id = has_many_update data[0]
243
- parrent = eval(tmp_current_relation.classify).find tmp_parrent_id
244
- parrent.update_attributes("#{model.name.foreign_key}" => new_id)
245
- parrent.save!
246
-
247
- destroy unless del_id.nil?
248
- model.find new_id
249
- end
250
-
251
- #
252
- # +has_many_update+ ( return array) [new_id, remove_id]
253
- # options maybe Hash, or list parameters
254
- # data ( type: hash) - data for updte
255
- # relation( type: str, symbol) - modifi with tableize (maybe miss)
256
- #
257
-
258
- def has_many_update *options
259
- if options.size == 1 && options.first.include?(:data) && options[0].include?(:relation)
260
- data = options.first[:data]
261
- relation = options.first[:relation]
262
- ActiveSupport::Deprecation.warn "Use simple list parameters (data, relation), parameter with 'Hash' type will be romoved in v1.0!"
263
- elsif options.size == 2
264
- data = options.first
265
- relation = options[1]
266
- else
267
- relation = tmp_current_relation
268
- data = options.first
269
- end
270
-
271
- data = { model.compare => ''}.merge data
272
-
273
- if relation.blank?
274
- warn "[ARRGUMENT MISSING]: 'has_many_update' don't know about current relation, and check all relations"
275
- end
276
-
277
- has_many_cleaner data.symbolize_keys, relation
278
- end
279
-
280
- #
281
- # +actuale?+ - check the acutuality of element in has_many table
282
- # options:
283
- # relation (String, Symbol) - for exclude current relation
284
- #
285
-
286
- def actuale? opt={relation: ""}
287
- if opt.is_a? Hash
288
- opt.assert_valid_keys :relation
289
- ActiveSupport::Deprecation.warn "Use simple parameter 'String' or 'Symbol', parameter with 'Hash' type will be romoved in v1.0!"
290
- relation = opt[:relation].to_s.tableize
291
- else
292
- relation = opt.to_s.tableize
293
- end
294
-
295
- actuale = false
296
- model.dependent_relations.each do |dependent_relation|
297
- tmp = self.send dependent_relation
298
- if relation == dependent_relation
299
- actuale ||= tmp.all.size > 1
300
- else
301
- actuale ||= tmp.exists?
302
- end
303
- end
304
- actuale
305
- end
306
- end
307
-
308
- private
309
-
310
- #
311
- # +destroy_filter+ - method for before_destroy, check actuale record and
312
- # return true for delete or false for leave
313
- #
314
-
315
- def destroy_filter
316
- not actuale?
317
- end
318
-
319
- #
320
- # base operations in this gem
321
- #
322
-
323
- def has_many_cleaner data, relation
324
- compare = { model.compare => data[model.compare] }
325
-
326
- object_id = id
327
- delete_id = nil
328
-
329
- if actuale? relation
330
- # create new object and finish
331
- object = model.where(compare).first_or_create data
332
- object_id = object.id
333
- else
334
- object_tmp = model.where(compare).first
335
- unless object_tmp.nil?
336
- # set new object and delete old
337
- delete_id = (object_id == object_tmp.id) ? nil : object_id
338
- object_id = object_tmp.id
339
- else
340
- # update old object
341
- if object_id.nil?
342
- object = model.where(compare).first_or_create data
343
- object_id = object.id
344
- else
345
- if data[model.compare].blank?
346
- delete_id = object_id
347
- object_id = nil
348
- else
349
- update_attributes data
350
- end
351
- end
352
- end
353
- end
354
- [object_id, delete_id]
355
- end
356
- end
357
- end
358
- end