acts_has_many 0.1.5 → 0.2.0

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