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.
- data/README.md +46 -67
- data/lib/acts_has_many/active_record/acts_has_many/child.rb +143 -0
- data/lib/acts_has_many/active_record/acts_has_many/parent.rb +106 -0
- data/lib/acts_has_many/active_record/acts_has_many.rb +100 -0
- data/lib/acts_has_many/version.rb +1 -1
- data/lib/acts_has_many.rb +5 -2
- data/spec/has_many_spec.rb +157 -190
- data/spec/has_many_through_spec.rb +102 -102
- data/spec/helper.rb +1 -0
- metadata +5 -3
- data/lib/acts_has_many/active_record/acts/has_many.rb +0 -358
@@ -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
|