hyper-model 1.0.alpha1.5 → 1.0.alpha1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +0 -1
- data/Gemfile +6 -5
- data/Rakefile +1 -1
- data/hyper-model.gemspec +11 -19
- data/lib/active_record_base.rb +58 -12
- data/lib/hyper-model.rb +3 -1
- data/lib/hyper_model/version.rb +1 -1
- data/lib/reactive_record/active_record/associations.rb +8 -3
- data/lib/reactive_record/active_record/base.rb +1 -1
- data/lib/reactive_record/active_record/class_methods.rb +54 -32
- data/lib/reactive_record/active_record/instance_methods.rb +48 -3
- data/lib/reactive_record/active_record/reactive_record/base.rb +2 -2
- data/lib/reactive_record/active_record/reactive_record/collection.rb +77 -27
- data/lib/reactive_record/active_record/reactive_record/dummy_value.rb +26 -15
- data/lib/reactive_record/active_record/reactive_record/getters.rb +4 -2
- data/lib/reactive_record/active_record/reactive_record/isomorphic_base.rb +17 -23
- data/lib/reactive_record/active_record/reactive_record/operations.rb +7 -1
- data/lib/reactive_record/active_record/reactive_record/setters.rb +2 -3
- data/lib/reactive_record/active_record/reactive_record/while_loading.rb +22 -1
- data/lib/reactive_record/broadcast.rb +21 -10
- data/lib/reactive_record/server_data_cache.rb +56 -44
- metadata +44 -150
@@ -38,6 +38,7 @@ module ReactiveRecord
|
|
38
38
|
attr_accessor :aggregate_owner
|
39
39
|
attr_accessor :aggregate_attribute
|
40
40
|
attr_accessor :destroyed
|
41
|
+
attr_accessor :being_destroyed
|
41
42
|
attr_accessor :updated_during
|
42
43
|
attr_accessor :synced_attributes
|
43
44
|
attr_accessor :virgin
|
@@ -310,7 +311,7 @@ module ReactiveRecord
|
|
310
311
|
@saving = true
|
311
312
|
end
|
312
313
|
|
313
|
-
def errors!(hash, saving)
|
314
|
+
def errors!(hash, saving = false)
|
314
315
|
@errors_at_last_sync = hash if saving
|
315
316
|
notify_waiting_for_save
|
316
317
|
errors.clear && return unless hash
|
@@ -323,7 +324,6 @@ module ReactiveRecord
|
|
323
324
|
end
|
324
325
|
|
325
326
|
def revert_errors!
|
326
|
-
puts "#{inspect}.revert_errors! @errors_at_last_sync: #{@errors_at_last_sync}"
|
327
327
|
errors!(@errors_at_last_sync)
|
328
328
|
end
|
329
329
|
|
@@ -141,7 +141,10 @@ To determine this sync_scopes first asks if the record being changed is in the s
|
|
141
141
|
|
142
142
|
|
143
143
|
=end
|
144
|
+
attr_accessor :broadcast_updated_at
|
145
|
+
|
144
146
|
def sync_scopes(broadcast)
|
147
|
+
self.broadcast_updated_at = broadcast.updated_at
|
145
148
|
# record_with_current_values will return nil if data between
|
146
149
|
# the broadcast record and the value on the client is out of sync
|
147
150
|
# not running set_pre_sync_related_records will cause sync scopes
|
@@ -159,6 +162,8 @@ To determine this sync_scopes first asks if the record being changed is in the s
|
|
159
162
|
)
|
160
163
|
record.backing_record.sync_unscoped_collection! if record.destroyed? || broadcast.new?
|
161
164
|
end
|
165
|
+
ensure
|
166
|
+
self.broadcast_updated_at = nil
|
162
167
|
end
|
163
168
|
|
164
169
|
def apply_to_all_collections(method, record, dont_gather)
|
@@ -336,26 +341,26 @@ To determine this sync_scopes first asks if the record being changed is in the s
|
|
336
341
|
end
|
337
342
|
|
338
343
|
def observed
|
339
|
-
return if @observing || ReactiveRecord::Base.data_loading?
|
344
|
+
return self if @observing || ReactiveRecord::Base.data_loading?
|
340
345
|
begin
|
341
346
|
@observing = true
|
342
347
|
link_to_parent
|
343
348
|
reload_from_db(true) if @out_of_date
|
344
349
|
Hyperstack::Internal::State::Variable.get(self, :collection)
|
350
|
+
self
|
345
351
|
ensure
|
346
352
|
@observing = false
|
347
353
|
end
|
348
354
|
end
|
349
355
|
|
350
|
-
def
|
356
|
+
def count_state=(val)
|
351
357
|
unless ReactiveRecord::WhileLoading.observed?
|
352
358
|
Hyperstack::Internal::State::Variable.set(self, :collection, collection, true)
|
353
359
|
end
|
360
|
+
@count_updated_at = ReactiveRecord::Operations::Base.last_response_sent_at
|
354
361
|
@count = val
|
355
362
|
end
|
356
363
|
|
357
|
-
|
358
|
-
|
359
364
|
def _count_internal(load_from_client)
|
360
365
|
# when count is called on a leaf, count_internal is called for each
|
361
366
|
# ancestor. Only the outermost count has load_from_client == true
|
@@ -462,16 +467,18 @@ To determine this sync_scopes first asks if the record being changed is in the s
|
|
462
467
|
alias << push
|
463
468
|
|
464
469
|
def _internal_push(item)
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
470
|
+
insure_sync do
|
471
|
+
item.itself # force get of at least the id
|
472
|
+
if collection
|
473
|
+
self.force_push item
|
474
|
+
else
|
475
|
+
unsaved_children << item
|
476
|
+
update_child(item)
|
477
|
+
@owner.backing_record.sync_has_many(@association.attribute) if @owner && @association
|
478
|
+
if !@count.nil?
|
479
|
+
@count += (item.destroyed? ? -1 : 1)
|
480
|
+
notify_of_change self
|
481
|
+
end
|
475
482
|
end
|
476
483
|
end
|
477
484
|
self
|
@@ -557,9 +564,9 @@ To determine this sync_scopes first asks if the record being changed is in the s
|
|
557
564
|
notify_of_change new_array
|
558
565
|
end
|
559
566
|
|
560
|
-
def
|
561
|
-
|
562
|
-
|
567
|
+
def destroy_non_habtm(item)
|
568
|
+
Hyperstack::Internal::State::Mapper.bulk_update do
|
569
|
+
unsaved_children.delete(item)
|
563
570
|
if @owner && @association
|
564
571
|
inverse_of = @association.inverse_of
|
565
572
|
if (backing_record = item.backing_record) && item.attributes[inverse_of] == @owner && !@association.through_association?
|
@@ -569,17 +576,44 @@ To determine this sync_scopes first asks if the record being changed is in the s
|
|
569
576
|
delete_internal(item) { @owner.backing_record.sync_has_many(@association.attribute) }
|
570
577
|
else
|
571
578
|
delete_internal(item)
|
572
|
-
end
|
579
|
+
end.tap { Hyperstack::Internal::State::Variable.set(self, :collection, collection) }
|
580
|
+
end
|
581
|
+
end
|
582
|
+
|
583
|
+
def destroy(item)
|
584
|
+
return destroy_non_habtm(item) unless @association&.habtm?
|
585
|
+
|
586
|
+
ta = @association.through_association
|
587
|
+
item_foreign_key = @association.source_belongs_to_association.association_foreign_key
|
588
|
+
join_record = ta.klass.find_by(
|
589
|
+
ta.association_foreign_key => @owner.id,
|
590
|
+
item_foreign_key => item.id
|
573
591
|
)
|
592
|
+
return destroy_non_habtm(item) if join_record.nil? ||
|
593
|
+
join_record.backing_record.being_destroyed
|
594
|
+
|
595
|
+
join_record&.destroy
|
596
|
+
end
|
597
|
+
|
598
|
+
def insure_sync
|
599
|
+
if Collection.broadcast_updated_at && @count_updated_at && Collection.broadcast_updated_at < @count_updated_at
|
600
|
+
reload_from_db
|
601
|
+
else
|
602
|
+
yield
|
603
|
+
end
|
574
604
|
end
|
575
605
|
|
606
|
+
alias delete destroy
|
607
|
+
|
576
608
|
def delete_internal(item)
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
609
|
+
insure_sync do
|
610
|
+
if collection
|
611
|
+
all.delete(item)
|
612
|
+
elsif !@count.nil?
|
613
|
+
@count -= 1
|
614
|
+
end
|
615
|
+
yield if block_given? # was yield item, but item is not used
|
581
616
|
end
|
582
|
-
yield if block_given? # was yield item, but item is not used
|
583
617
|
item
|
584
618
|
end
|
585
619
|
|
@@ -594,8 +628,10 @@ To determine this sync_scopes first asks if the record being changed is in the s
|
|
594
628
|
r.backing_record.sync_attributes(attrs).set_ar_instance!
|
595
629
|
end
|
596
630
|
|
597
|
-
def find(
|
598
|
-
|
631
|
+
def find(*args)
|
632
|
+
args = args[0] if args[0].is_a? Array
|
633
|
+
return args.collect { |id| find(id) } if args.count > 1
|
634
|
+
find_by(@target_klass.primary_key => args[0])
|
599
635
|
end
|
600
636
|
|
601
637
|
def _find_by_initializer(scope, attrs)
|
@@ -616,8 +652,22 @@ To determine this sync_scopes first asks if the record being changed is in the s
|
|
616
652
|
count.zero?
|
617
653
|
end
|
618
654
|
|
619
|
-
def any?
|
620
|
-
|
655
|
+
def any?(*args, &block)
|
656
|
+
# If there are any args passed in, then the collection is being used in the condition
|
657
|
+
# and we must load it all into memory.
|
658
|
+
return all.any?(*args, &block) if args&.length&.positive? || block.present?
|
659
|
+
|
660
|
+
# Otherwise we can just check the count for efficiency
|
661
|
+
!empty?
|
662
|
+
end
|
663
|
+
|
664
|
+
def none?(*args, &block)
|
665
|
+
# If there are any args passed in, then the collection is being used in the condition
|
666
|
+
# and we must load it all into memory.
|
667
|
+
return all.none?(*args, &block) if args&.length&.positive? || block.present?
|
668
|
+
|
669
|
+
# Otherwise we can just check the count for efficiency
|
670
|
+
empty?
|
621
671
|
end
|
622
672
|
|
623
673
|
def method_missing(method, *args, &block)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# add
|
1
|
+
# add methods to Object to determine if this is a dummy object or not
|
2
2
|
class Object
|
3
3
|
def loaded?
|
4
4
|
!loading?
|
@@ -7,10 +7,6 @@ class Object
|
|
7
7
|
def loading?
|
8
8
|
false
|
9
9
|
end
|
10
|
-
|
11
|
-
def present?
|
12
|
-
!!self
|
13
|
-
end
|
14
10
|
end
|
15
11
|
|
16
12
|
module ReactiveRecord
|
@@ -36,6 +32,12 @@ module ReactiveRecord
|
|
36
32
|
@column_hash[:default] || nil
|
37
33
|
end
|
38
34
|
|
35
|
+
def build_default_value_for_json
|
36
|
+
::JSON.parse(@column_hash[:default]) if @column_hash[:default]
|
37
|
+
end
|
38
|
+
|
39
|
+
alias build_default_value_for_jsonb build_default_value_for_json
|
40
|
+
|
39
41
|
def build_default_value_for_datetime
|
40
42
|
if @column_hash[:default]
|
41
43
|
::Time.parse(@column_hash[:default].gsub(' ','T')+'+00:00')
|
@@ -55,18 +57,20 @@ module ReactiveRecord
|
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
60
|
+
FALSY_VALUES = [false, nil, 0, "0", "f", "F", "false", "FALSE", "off", "OFF"]
|
61
|
+
|
58
62
|
def build_default_value_for_boolean
|
59
|
-
@column_hash[:default]
|
63
|
+
!FALSY_VALUES.include?(@column_hash[:default])
|
60
64
|
end
|
61
65
|
|
62
66
|
def build_default_value_for_float
|
63
|
-
@column_hash[:default] || Float(0.0)
|
67
|
+
@column_hash[:default]&.to_f || Float(0.0)
|
64
68
|
end
|
65
69
|
|
66
70
|
alias build_default_value_for_decimal build_default_value_for_float
|
67
71
|
|
68
72
|
def build_default_value_for_integer
|
69
|
-
@column_hash[:default] || Integer(0)
|
73
|
+
@column_hash[:default]&.to_i || Integer(0)
|
70
74
|
end
|
71
75
|
|
72
76
|
alias build_default_value_for_bigint build_default_value_for_integer
|
@@ -92,10 +96,6 @@ module ReactiveRecord
|
|
92
96
|
false
|
93
97
|
end
|
94
98
|
|
95
|
-
def present?
|
96
|
-
false
|
97
|
-
end
|
98
|
-
|
99
99
|
def nil?
|
100
100
|
true
|
101
101
|
end
|
@@ -104,6 +104,11 @@ module ReactiveRecord
|
|
104
104
|
true
|
105
105
|
end
|
106
106
|
|
107
|
+
def class
|
108
|
+
notify
|
109
|
+
@object.class
|
110
|
+
end
|
111
|
+
|
107
112
|
def method_missing(method, *args, &block)
|
108
113
|
if method.start_with?("build_default_value_for_")
|
109
114
|
nil
|
@@ -158,7 +163,13 @@ module ReactiveRecord
|
|
158
163
|
|
159
164
|
alias inspect to_s
|
160
165
|
|
161
|
-
|
166
|
+
%x{
|
167
|
+
if (Opal.Object.$$proto) {
|
168
|
+
#{self}.$$proto.toString = Opal.Object.$$proto.toString
|
169
|
+
} else {
|
170
|
+
#{self}.$$prototype.toString = Opal.Object.$$prototype.toString
|
171
|
+
}
|
172
|
+
}
|
162
173
|
|
163
174
|
def to_f
|
164
175
|
notify
|
@@ -216,10 +227,10 @@ module ReactiveRecord
|
|
216
227
|
# to convert it to a string, for rendering
|
217
228
|
# advantage over a try(:method) is, that it doesnt raise und thus is faster
|
218
229
|
# which is important during render
|
219
|
-
def respond_to?(method)
|
230
|
+
def respond_to?(method, all = false)
|
220
231
|
return true if method == :acts_as_string?
|
221
232
|
return true if %i[inspect to_date to_f to_i to_numeric to_number to_s to_time].include? method
|
222
|
-
return @object.respond_to? if @object
|
233
|
+
return @object.respond_to?(method, all) if @object
|
223
234
|
false
|
224
235
|
end
|
225
236
|
|
@@ -37,7 +37,8 @@ module ReactiveRecord
|
|
37
37
|
|
38
38
|
def get_server_method(attr, reload = nil)
|
39
39
|
non_relationship_getter_common(attr, reload) do |has_key|
|
40
|
-
sync_ignore_dummy attr, Base.load_from_db(self, *(vector ? vector : [nil]), attr), has_key
|
40
|
+
# SPLAT BUG: was sync_ignore_dummy attr, Base.load_from_db(self, *(vector ? vector : [nil]), attr), has_key
|
41
|
+
sync_ignore_dummy attr, Base.load_from_db(self, *(vector || [nil]), *attr), has_key
|
41
42
|
end
|
42
43
|
end
|
43
44
|
|
@@ -79,7 +80,8 @@ module ReactiveRecord
|
|
79
80
|
if new?
|
80
81
|
yield has_key if block
|
81
82
|
elsif on_opal_client?
|
82
|
-
sync_ignore_dummy attr, Base.load_from_db(self, *(vector ? vector : [nil]), attr), has_key
|
83
|
+
# SPLAT BUG: was sync_ignore_dummy attr, Base.load_from_db(self, *(vector ? vector : [nil]), attr), has_key
|
84
|
+
sync_ignore_dummy attr, Base.load_from_db(self, *(vector || [nil]), *attr), has_key
|
83
85
|
elsif id.present?
|
84
86
|
sync_attribute attr, fetch_by_id(attr)
|
85
87
|
else
|
@@ -457,21 +457,18 @@ module ReactiveRecord
|
|
457
457
|
parent.send("#{association[:attribute]}=", aggregate)
|
458
458
|
#puts "updated is frozen? #{aggregate.frozen?}, parent attributes = #{parent.send(association[:attribute]).attributes}"
|
459
459
|
elsif parent.class.reflect_on_association(association[:attribute].to_sym).nil?
|
460
|
-
|
460
|
+
raise "Missing association :#{association[:attribute]} for #{parent.class.name}. Was association defined on opal side only?"
|
461
461
|
elsif parent.class.reflect_on_association(association[:attribute].to_sym).collection?
|
462
462
|
#puts ">>>>>>>>>> #{parent.class.name}.send('#{association[:attribute]}') << #{reactive_records[association[:child_id]]})"
|
463
463
|
dont_save_list.delete(parent)
|
464
464
|
|
465
|
-
|
466
465
|
# if reactive_records[association[:child_id]]&.new_record?
|
467
466
|
# dont_save_list << reactive_records[association[:child_id]]
|
468
467
|
# end
|
469
|
-
|
470
|
-
|
471
|
-
#
|
472
|
-
|
473
|
-
#puts "skipped"
|
474
|
-
#end
|
468
|
+
|
469
|
+
if parent.new_record?
|
470
|
+
parent.send("#{association[:attribute]}") << reactive_records[association[:child_id]]
|
471
|
+
end
|
475
472
|
else
|
476
473
|
#puts ">>>>ASSOCIATION>>>> #{parent.class.name}.send('#{association[:attribute]}=', #{reactive_records[association[:child_id]]})"
|
477
474
|
parent.send("#{association[:attribute]}=", reactive_records[association[:child_id]])
|
@@ -496,7 +493,8 @@ module ReactiveRecord
|
|
496
493
|
next record.persisted? if record.id && !record.changed?
|
497
494
|
# if we get to here save the record and return true to keep it
|
498
495
|
op = new_models.include?(record) ? :create_permitted? : :update_permitted?
|
499
|
-
|
496
|
+
|
497
|
+
record.check_permission_with_acting_user(acting_user, op).save(validate: validate) || true
|
500
498
|
end
|
501
499
|
|
502
500
|
# if called from ServerDataCache then save and validate are both false, and we just return the
|
@@ -551,13 +549,11 @@ module ReactiveRecord
|
|
551
549
|
if RUBY_ENGINE == 'opal'
|
552
550
|
|
553
551
|
def destroy(&block)
|
552
|
+
return if @destroyed || @being_destroyed
|
554
553
|
|
555
|
-
|
556
|
-
|
557
|
-
#destroy_associations
|
554
|
+
# destroy_associations
|
558
555
|
|
559
556
|
promise = Promise.new
|
560
|
-
|
561
557
|
if !data_loading? && (id || vector)
|
562
558
|
Operations::Destroy.run(model: ar_instance.model_name.to_s, id: id, vector: vector)
|
563
559
|
.then do |response|
|
@@ -569,7 +565,7 @@ module ReactiveRecord
|
|
569
565
|
destroy_associations
|
570
566
|
# sync_unscoped_collection! # ? should we do this here was NOT being done before hypermesh integration
|
571
567
|
yield true, nil if block
|
572
|
-
promise.resolve(
|
568
|
+
promise.resolve(success: true)
|
573
569
|
end
|
574
570
|
|
575
571
|
# DO NOT CLEAR ATTRIBUTES. Records that are not found, are destroyed, and if they are searched for again, we want to make
|
@@ -589,18 +585,16 @@ module ReactiveRecord
|
|
589
585
|
def self.destroy_record(model, id, vector, acting_user)
|
590
586
|
model = Object.const_get(model)
|
591
587
|
record = if id
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
588
|
+
model.find(id)
|
589
|
+
else
|
590
|
+
ServerDataCache.new(acting_user, {})[*vector].value
|
591
|
+
end
|
597
592
|
|
598
593
|
record.check_permission_with_acting_user(acting_user, :destroy_permitted?).destroy
|
599
|
-
{success: true, attributes: {}}
|
600
|
-
|
594
|
+
{ success: true, attributes: {} }
|
601
595
|
rescue Exception => e
|
602
|
-
#ReactiveRecord::Pry.rescued(e)
|
603
|
-
{success: false, record: record, message: e}
|
596
|
+
# ReactiveRecord::Pry.rescued(e)
|
597
|
+
{ success: false, record: record, message: e }
|
604
598
|
end
|
605
599
|
end
|
606
600
|
end
|
@@ -12,6 +12,10 @@ module ReactiveRecord
|
|
12
12
|
|
13
13
|
FORMAT = '0x%x'
|
14
14
|
|
15
|
+
class << self
|
16
|
+
attr_accessor :last_response_sent_at
|
17
|
+
end
|
18
|
+
|
15
19
|
def self.serialize_params(hash)
|
16
20
|
hash['associations'].each do |assoc|
|
17
21
|
assoc['parent_id'] = FORMAT % assoc['parent_id']
|
@@ -38,6 +42,7 @@ module ReactiveRecord
|
|
38
42
|
response[:saved_models].each do |saved_model|
|
39
43
|
saved_model[0] = FORMAT % saved_model[0]
|
40
44
|
end if response.is_a?(Hash) && response[:saved_models]
|
45
|
+
response[:sent_at] = Time.now.to_f
|
41
46
|
response
|
42
47
|
end
|
43
48
|
|
@@ -45,6 +50,7 @@ module ReactiveRecord
|
|
45
50
|
response[:saved_models].each do |saved_model|
|
46
51
|
saved_model[0] = saved_model[0].to_i(16)
|
47
52
|
end if response.is_a?(Hash) && response[:saved_models]
|
53
|
+
Base.last_response_sent_at = response.delete(:sent_at)
|
48
54
|
response
|
49
55
|
end
|
50
56
|
end
|
@@ -93,7 +99,7 @@ module ReactiveRecord
|
|
93
99
|
class Destroy < Base
|
94
100
|
param :acting_user, nils: true
|
95
101
|
param :model
|
96
|
-
param :id
|
102
|
+
param :id, nils: true
|
97
103
|
param :vector
|
98
104
|
step do
|
99
105
|
ReactiveRecord::Base.destroy_record(
|
@@ -94,7 +94,7 @@ module ReactiveRecord
|
|
94
94
|
end
|
95
95
|
|
96
96
|
def set_attribute_change_status_and_notify(attr, changed, new_value)
|
97
|
-
if @virgin
|
97
|
+
if @virgin || @being_destroyed
|
98
98
|
@attributes[attr] = new_value
|
99
99
|
else
|
100
100
|
change_status_and_notify_helper(attr, changed) do |had_key, current_value|
|
@@ -108,14 +108,13 @@ module ReactiveRecord
|
|
108
108
|
end
|
109
109
|
|
110
110
|
def set_change_status_and_notify_only(attr, changed)
|
111
|
-
return if @virgin
|
111
|
+
return if @virgin || @being_destroyed
|
112
112
|
change_status_and_notify_helper(attr, changed) do
|
113
113
|
Hyperstack::Internal::State::Variable.set(self, attr, nil) unless data_loading?
|
114
114
|
end
|
115
115
|
end
|
116
116
|
|
117
117
|
def change_status_and_notify_helper(attr, changed)
|
118
|
-
return if @being_destroyed
|
119
118
|
empty_before = changed_attributes.empty?
|
120
119
|
if !changed || data_loading?
|
121
120
|
changed_attributes.delete(attr)
|