Class: ReactiveRecord::Collection
- Inherits:
-
Object
- Object
- ReactiveRecord::Collection
show all
- Defined in:
- lib/reactive_record/active_record/reactive_record/collection.rb
Defined Under Namespace
Classes: DummySet
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
-
#==(other_collection) ⇒ Object
-
#[](index) ⇒ Object
-
#all ⇒ Object
-
#apply_scope(name, *vector) ⇒ Object
-
#build_child_scope(scope_description, *scope_vector) ⇒ Object
-
#child_scopes ⇒ Object
-
#collect(*args, &block) ⇒ Object
WHY IS THIS NEEDED? Perhaps it was just for debug.
-
#collector? ⇒ Boolean
-
#count ⇒ Object
(also: #length)
-
#delete(item) ⇒ Object
-
#delete_internal(item) {|item| ... } ⇒ Object
-
#dup_for_sync ⇒ Object
-
#empty? ⇒ Boolean
should be handled by method missing below, but opal-rspec does not deal well with method missing, so to test…
-
#filter? ⇒ Boolean
-
#filter_records(related_records) ⇒ Object
-
#force_push(item) ⇒ Object
-
#gather_related_records(record, related_records = Set.new) ⇒ Object
-
#initialize(target_klass, owner = nil, association = nil, *vector) ⇒ Collection
constructor
A new instance of Collection.
-
#internal_replace(new_array) ⇒ Object
-
#joins_with?(record) ⇒ Boolean
is it necessary to check @association in the next 2 methods???.
-
#klass ⇒ Object
-
#link_child(child) ⇒ Object
-
#link_to_parent ⇒ Object
-
#live_scopes ⇒ Object
-
#loading? ⇒ Boolean
-
#merge_related_records(record, related_records) ⇒ Object
-
#method_missing(method, *args, &block) ⇒ Object
-
#observed ⇒ Object
-
#proxy_association ⇒ Object
def each_known_child [*collection, *client_pushes].each { |i| yield i } end.
-
#push(item) ⇒ Object
(also: #<<)
-
#push_and_update_belongs_to(id) ⇒ Object
-
#related_records_for(record) ⇒ Object
-
#reload_from_db(force = nil) ⇒ Object
-
#replace(new_array) ⇒ Object
-
#set_belongs_to(child) ⇒ Object
-
#set_count_state(val) ⇒ Object
-
#set_pre_sync_related_records(related_records, _record = nil) ⇒ Object
-
#sort!(*args, &block) ⇒ Object
-
#sync_collection_with_parent ⇒ Object
-
#sync_scopes(related_records, record, filtering = true) ⇒ Object
-
#to_s ⇒ Object
-
#unsaved_children ⇒ Object
-
#update_child(item) ⇒ Object
Constructor Details
#initialize(target_klass, owner = nil, association = nil, *vector) ⇒ Collection
Returns a new instance of Collection.
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 29
def initialize(target_klass, owner = nil, association = nil, *vector)
@owner = owner @association = association
@target_klass = target_klass
if owner and !owner.id and vector.length <= 1
@collection = []
elsif vector.length > 0
@vector = vector
elsif owner
@vector = owner.backing_record.vector + [association.attribute]
else
@vector = [target_klass]
end
@scopes = {}
end
|
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args, &block) ⇒ Object
480
481
482
483
484
485
486
487
488
489
490
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 480
def method_missing(method, *args, &block)
if [].respond_to? method
all.send(method, *args, &block)
elsif ScopeDescription.find(@target_klass, method) || (args.count == 1 && method =~ /^find_by_/)
apply_scope(method, *args)
elsif @target_klass.respond_to?(method) && ScopeDescription.find(@target_klass, "_#{method}")
apply_scope("_#{method}", *args).first
else
super
end
end
|
Instance Attribute Details
#client_collection ⇒ Object
Returns the value of attribute client_collection.
343
344
345
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 343
def client_collection
@client_collection
end
|
#parent=(value) ⇒ Object
Sets the attribute parent
98
99
100
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 98
def parent=(value)
@parent = value
end
|
Returns the value of attribute pre_sync_related_records.
99
100
101
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 99
def pre_sync_related_records
@pre_sync_related_records
end
|
#scope_description=(value) ⇒ Object
Sets the attribute scope_description
97
98
99
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 97
def scope_description=(value)
@scope_description = value
end
|
todo move following to a separate module related to scope updates ******************
96
97
98
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 96
def vector
@vector
end
|
Class Method Details
.apply_to_all_collections(method, record, dont_gather) ⇒ Object
126
127
128
129
130
131
132
133
134
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 126
def apply_to_all_collections(method, record, dont_gather)
related_records = Set.new if dont_gather
Base.outer_scopes.each do |collection|
unless dont_gather
related_records = collection.gather_related_records(record)
end
collection.send method, related_records, record
end
end
|
.sync_scopes(broadcast) ⇒ Object
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 106
def sync_scopes(broadcast)
React::State.bulk_update do
record = broadcast.record_with_current_values
apply_to_all_collections(
:set_pre_sync_related_records,
record, broadcast.new?
) if record
record = broadcast.record_with_new_values
apply_to_all_collections(
:sync_scopes,
record, record.destroyed?
)
record.backing_record.sync_unscoped_collection! if record.destroyed? || broadcast.new?
end
end
|
Instance Method Details
#==(other_collection) ⇒ Object
82
83
84
85
86
87
88
89
90
91
92
93
94
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 82
def ==(other_collection)
observed
return !@collection unless other_collection.is_a? Collection
other_collection.observed
my_children = (@collection || []).select { |target| target != @dummy_record }
if other_collection
other_children = (other_collection.collection || []).select { |target| target != other_collection.dummy_record }
return false unless my_children == other_children
unsaved_children.to_a == other_collection.unsaved_children.to_a
else
my_children.empty? && unsaved_children.empty?
end
end
|
#[](index) ⇒ Object
70
71
72
73
74
75
76
77
78
79
80
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 70
def [](index)
observed
if (@collection || all).length <= index and @dummy_collection
(@collection.length..index).each do |i|
new_dummy_record = ReactiveRecord::Base.new_from_vector(@target_klass, nil, *@vector, "*#{i}")
new_dummy_record.backing_record.attributes[@association.inverse_of] = @owner if @association && !@association.through_association?
@collection << new_dummy_record
end
end
@collection[index]
end
|
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 53
def all
observed
@dummy_collection.notify if @dummy_collection
unless @collection
@collection = []
if ids = ReactiveRecord::Base.fetch_from_db([*@vector, "*all"])
ids.each do |id|
@collection << @target_klass.find_by(@target_klass.primary_key => id)
end
else
@dummy_collection = ReactiveRecord::Base.load_from_db(nil, *@vector, "*all")
@dummy_record = self[0]
end
end
@collection
end
|
#apply_scope(name, *vector) ⇒ Object
208
209
210
211
212
213
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 208
def apply_scope(name, *vector)
description = ScopeDescription.find(@target_klass, name)
collection = build_child_scope(description, *description.name, *vector)
collection.reload_from_db if name == "#{description.name}!"
collection
end
|
#build_child_scope(scope_description, *scope_vector) ⇒ Object
219
220
221
222
223
224
225
226
227
228
229
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 219
def build_child_scope(scope_description, *scope_vector)
child_scopes[scope_vector] ||= begin
new_vector = @vector
new_vector += [scope_vector] unless new_vector.nil? || scope_vector.empty?
child_scope = Collection.new(@target_klass, nil, nil, *new_vector)
child_scope.scope_description = scope_description
child_scope.parent = self
child_scope.extend ScopedCollection
child_scope
end
end
|
#child_scopes ⇒ Object
215
216
217
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 215
def child_scopes
@child_scopes ||= {}
end
|
#collect(*args, &block) ⇒ Object
WHY IS THIS NEEDED? Perhaps it was just for debug
307
308
309
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 307
def collect(*args, &block)
all.collect(*args, &block)
end
|
#collector? ⇒ Boolean
179
180
181
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 179
def collector?
false
end
|
#count ⇒ Object
Also known as:
length
292
293
294
295
296
297
298
299
300
301
302
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 292
def count
observed
if @collection
@collection.count
elsif @count ||= ReactiveRecord::Base.fetch_from_db([*@vector, "*count"])
@count
else
ReactiveRecord::Base.load_from_db(nil, *@vector, "*count")
@count = 1
end
end
|
#delete(item) ⇒ Object
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 444
def delete(item)
unsaved_children.delete(item)
notify_of_change(
if @owner && @association && !@association.through_association?
inverse_of = @association.inverse_of
if (backing_record = item.backing_record) && backing_record.attributes[inverse_of] == @owner
backing_record.update_attribute(inverse_of, nil)
end
delete_internal(item) { @owner.backing_record.update_attribute(@association.attribute) }
else
delete_internal(item)
end
)
end
|
#delete_internal(item) {|item| ... } ⇒ Object
461
462
463
464
465
466
467
468
469
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 461
def delete_internal(item)
if collection
all.delete(item)
elsif !@count.nil?
@count -= 1
end
yield item if block_given?
item
end
|
#dup_for_sync ⇒ Object
45
46
47
48
49
50
51
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 45
def dup_for_sync
self.dup.instance_eval do
@collection = @collection.dup if @collection
@scopes = @scopes.dup
self
end
end
|
#empty? ⇒ Boolean
should be handled by method missing below, but opal-rspec does not deal well with method missing, so to test…
476
477
478
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 476
def empty? all.empty?
end
|
#filter? ⇒ Boolean
152
153
154
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 152
def filter?
true
end
|
#filter_records(related_records) ⇒ Object
183
184
185
186
187
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 183
def filter_records(related_records)
scope_args = @vector.last.is_a?(Array) ? @vector.last[1..-1] : []
@scope_description.filter_records(related_records, scope_args)
end
|
#force_push(item) ⇒ Object
384
385
386
387
388
389
390
391
392
393
394
395
396
397
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 384
def force_push(item)
return delete(item) if item.destroyed? all << item unless all.include? item update_child(item)
if item.id and @dummy_record
@dummy_record.id = item.id
@collection.reject { |i| i.object_id == @dummy_record.object_id }
@dummy_record = @collection.detect { |r| r.backing_record.vector.last =~ /^\*[0-9]+$/ }
@dummy_collection = nil
end
notify_of_change self
end
|
137
138
139
140
141
142
143
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 137
def gather_related_records(record, related_records = Set.new)
merge_related_records(record, related_records)
live_scopes.each do |collection|
collection.gather_related_records(record, related_records)
end
related_records
end
|
#internal_replace(new_array) ⇒ Object
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 417
def internal_replace(new_array)
if @dummy_collection
@dummy_collection.notify
array = new_array.is_a?(Collection) ? new_array.collection : new_array
@collection.each_with_index do |r, i|
r.id = new_array[i].id if array[i] and array[i].id and !r.new? and r.backing_record.vector.last =~ /^\*[0-9]+$/
end
end
@collection.dup.each { |item| delete(item) } if @collection @collection = []
if new_array.is_a? Collection
@dummy_collection = new_array.dummy_collection
@dummy_record = new_array.dummy_record
new_array.collection.each { |item| self << item } if new_array.collection
else
@dummy_collection = @dummy_record = nil
new_array.each { |item| self << item }
end
notify_of_change new_array
end
|
#joins_with?(record) ⇒ Boolean
is it necessary to check @association in the next 2 methods???
158
159
160
161
162
163
164
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 158
def joins_with?(record)
if @association && @association.through_association
@association.through_association.klass == record.class
else
@target_klass == record.class
end
end
|
319
320
321
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 319
def klass
@target_klass
end
|
#link_child(child) ⇒ Object
243
244
245
246
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 243
def link_child(child)
live_scopes << child
link_to_parent
end
|
#link_to_parent ⇒ Object
231
232
233
234
235
236
237
238
239
240
241
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 231
def link_to_parent
return if @linked
@linked = true
if @parent
@parent.link_child self
sync_collection_with_parent unless collection
else
ReactiveRecord::Base.add_to_outer_scopes self
end
all if collector? end
|
#live_scopes ⇒ Object
189
190
191
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 189
def live_scopes
@live_scopes ||= Set.new
end
|
#loading? ⇒ Boolean
471
472
473
474
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 471
def loading?
all @dummy_collection.loading?
end
|
145
146
147
148
149
150
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 145
def merge_related_records(record, related_records)
if filter? && joins_with?(record)
related_records.merge(related_records_for(record))
end
related_records
end
|
273
274
275
276
277
278
279
280
281
282
283
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 273
def observed
return if @observing || ReactiveRecord::Base.data_loading?
begin
@observing = true
link_to_parent
reload_from_db(true) if @out_of_date
React::State.get_state(self, :collection)
ensure
@observing = false
end
end
|
#proxy_association ⇒ Object
def each_known_child
[*collection, *client_pushes].each { |i| yield i }
end
315
316
317
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 315
def proxy_association
@association || self end
|
#push(item) ⇒ Object
Also known as:
<<
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 362
def push(item)
item.itself if collection
self.force_push item
else
unsaved_children << item
update_child(item)
@owner.backing_record.update_attribute(@association.attribute) if @owner && @association
if !@count.nil?
@count += item.destroyed? ? -1 : 1
notify_of_change self
end
end
self
end
|
#push_and_update_belongs_to(id) ⇒ Object
323
324
325
326
327
328
329
330
331
332
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 323
def push_and_update_belongs_to(id)
child = proxy_association.klass.find(id)
push child
set_belongs_to child
end
|
166
167
168
169
170
171
172
173
174
175
176
177
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 166
def related_records_for(record)
return [] unless @association
attrs = record.backing_record.attributes
return [] unless attrs[@association.inverse_of] == @owner
if !@association.through_association
[record]
elsif (source = attrs[@association.source])
[source]
else
[]
end
end
|
#reload_from_db(force = nil) ⇒ Object
262
263
264
265
266
267
268
269
270
271
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 262
def reload_from_db(force = nil)
if force || React::State.has_observers?(self, :collection)
@out_of_date = false
ReactiveRecord::Base.load_from_db(nil, *@vector, '*all') if @collection
ReactiveRecord::Base.load_from_db(nil, *@vector, '*count')
else
@out_of_date = true
end
self
end
|
#replace(new_array) ⇒ Object
409
410
411
412
413
414
415
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 409
def replace(new_array)
unsaved_children.clear
new_array = new_array.to_a
return self if new_array == @collection
Base.load_data { internal_replace(new_array) }
notify_of_change new_array
end
|
#set_belongs_to(child) ⇒ Object
334
335
336
337
338
339
340
341
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 334
def set_belongs_to(child)
if @owner
child.send("#{@association.inverse_of}=", @owner) if @association
elsif @parent
@parent.set_belongs_to(child)
end
child
end
|
#set_count_state(val) ⇒ Object
285
286
287
288
289
290
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 285
def set_count_state(val)
unless ReactiveRecord::WhileLoading.has_observers?
React::State.set_state(self, :collection, collection, true)
end
@count = val
end
|
193
194
195
196
197
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 193
def set_pre_sync_related_records(related_records, _record = nil)
@pre_sync_related_records = related_records live_scopes.each { |scope| scope.set_pre_sync_related_records(@pre_sync_related_records) }
end
|
#sort!(*args, &block) ⇒ Object
380
381
382
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 380
def sort!(*args, &block)
replace(sort(*args, &block))
end
|
#sync_collection_with_parent ⇒ Object
248
249
250
251
252
253
254
255
256
257
258
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 248
def sync_collection_with_parent
if @parent.collection
if @parent.collection.empty?
@collection = []
elsif filter?
@collection = filter_records(@parent.collection)
end
elsif @parent.count.zero?
@count = 0
end
end
|
#sync_scopes(related_records, record, filtering = true) ⇒ Object
199
200
201
202
203
204
205
206
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 199
def sync_scopes(related_records, record, filtering = true)
live_scopes.each { |scope| scope.sync_scopes(related_records, record, filtering) }
notify_of_change unless related_records.empty?
ensure
@pre_sync_related_records = nil
end
|
101
102
103
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 101
def to_s
"<Coll-#{object_id} - #{vector}>"
end
|
#unsaved_children ⇒ Object
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 13
def unsaved_children
old_uc_already_being_called = @uc_already_being_called
if @owner && @association
@unsaved_children ||= Set.new
unless @uc_already_being_called
@uc_already_being_called = true
end
else
@unsaved_children ||= DummySet.new
end
@unsaved_children
ensure
@uc_already_being_called = old_uc_already_being_called
end
|
#update_child(item) ⇒ Object
350
351
352
353
354
355
356
357
358
359
360
|
# File 'lib/reactive_record/active_record/reactive_record/collection.rb', line 350
def update_child(item)
backing_record = item.backing_record
if backing_record && @owner && @association && !@association.through_association? && item.attributes[@association.inverse_of] != @owner
inverse_of = @association.inverse_of
current_association = item.attributes[inverse_of]
backing_record.virgin = false unless backing_record.data_loading?
backing_record.update_attribute(inverse_of, @owner)
current_association.attributes[@association.attribute].delete(item) if current_association and current_association.attributes[@association.attribute]
@owner.backing_record.update_attribute(@association.attribute) end
end
|