hyper-model 1.0.alpha1.1 → 1.0.alpha1.6
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.
- checksums.yaml +4 -4
- data/.gitignore +4 -1
- data/.rspec +0 -1
- data/Gemfile +6 -6
- data/Rakefile +27 -3
- data/hyper-model.gemspec +10 -19
- data/lib/active_record_base.rb +101 -33
- data/lib/hyper-model.rb +4 -2
- data/lib/hyper_model/version.rb +1 -1
- data/lib/hyper_react/input_tags.rb +2 -1
- data/lib/reactive_record/active_record/associations.rb +130 -34
- data/lib/reactive_record/active_record/base.rb +17 -0
- data/lib/reactive_record/active_record/class_methods.rb +124 -52
- data/lib/reactive_record/active_record/error.rb +2 -0
- data/lib/reactive_record/active_record/errors.rb +10 -6
- data/lib/reactive_record/active_record/instance_methods.rb +74 -6
- data/lib/reactive_record/active_record/reactive_record/backing_record_inspector.rb +22 -5
- data/lib/reactive_record/active_record/reactive_record/base.rb +56 -30
- data/lib/reactive_record/active_record/reactive_record/collection.rb +219 -70
- data/lib/reactive_record/active_record/reactive_record/dummy_polymorph.rb +22 -0
- data/lib/reactive_record/active_record/reactive_record/dummy_value.rb +27 -15
- data/lib/reactive_record/active_record/reactive_record/getters.rb +33 -10
- data/lib/reactive_record/active_record/reactive_record/isomorphic_base.rb +73 -46
- data/lib/reactive_record/active_record/reactive_record/lookup_tables.rb +5 -5
- data/lib/reactive_record/active_record/reactive_record/operations.rb +10 -3
- data/lib/reactive_record/active_record/reactive_record/scoped_collection.rb +3 -0
- data/lib/reactive_record/active_record/reactive_record/setters.rb +108 -71
- data/lib/reactive_record/active_record/reactive_record/while_loading.rb +258 -41
- data/lib/reactive_record/broadcast.rb +62 -25
- data/lib/reactive_record/interval.rb +3 -3
- data/lib/reactive_record/permissions.rb +14 -2
- data/lib/reactive_record/scope_description.rb +3 -2
- data/lib/reactive_record/server_data_cache.rb +99 -49
- data/polymorph-notes.md +143 -0
- data/spec_fails.txt +3 -0
- metadata +49 -162
- data/Gemfile.lock +0 -431
@@ -0,0 +1,22 @@
|
|
1
|
+
module ReactiveRecord
|
2
|
+
class DummyPolymorph
|
3
|
+
def initialize(vector)
|
4
|
+
@vector = vector
|
5
|
+
puts "VECTOR: #{@vector.inspect}"
|
6
|
+
Base.load_from_db(nil, *vector, 'id')
|
7
|
+
Base.load_from_db(nil, *vector, 'model_name')
|
8
|
+
end
|
9
|
+
|
10
|
+
def nil?
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing(*)
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.reflect_on_all_associations(*)
|
19
|
+
[]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -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,23 +57,26 @@ 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
|
73
77
|
|
74
78
|
def build_default_value_for_string
|
79
|
+
return @column_hash[:default] if @column_hash[:serialized?]
|
75
80
|
@column_hash[:default] || ''
|
76
81
|
end
|
77
82
|
|
@@ -91,10 +96,6 @@ module ReactiveRecord
|
|
91
96
|
false
|
92
97
|
end
|
93
98
|
|
94
|
-
def present?
|
95
|
-
false
|
96
|
-
end
|
97
|
-
|
98
99
|
def nil?
|
99
100
|
true
|
100
101
|
end
|
@@ -103,6 +104,11 @@ module ReactiveRecord
|
|
103
104
|
true
|
104
105
|
end
|
105
106
|
|
107
|
+
def class
|
108
|
+
notify
|
109
|
+
@object.class
|
110
|
+
end
|
111
|
+
|
106
112
|
def method_missing(method, *args, &block)
|
107
113
|
if method.start_with?("build_default_value_for_")
|
108
114
|
nil
|
@@ -157,7 +163,13 @@ module ReactiveRecord
|
|
157
163
|
|
158
164
|
alias inspect to_s
|
159
165
|
|
160
|
-
|
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
|
+
}
|
161
173
|
|
162
174
|
def to_f
|
163
175
|
notify
|
@@ -215,10 +227,10 @@ module ReactiveRecord
|
|
215
227
|
# to convert it to a string, for rendering
|
216
228
|
# advantage over a try(:method) is, that it doesnt raise und thus is faster
|
217
229
|
# which is important during render
|
218
|
-
def respond_to?(method)
|
230
|
+
def respond_to?(method, all = false)
|
219
231
|
return true if method == :acts_as_string?
|
220
232
|
return true if %i[inspect to_date to_f to_i to_numeric to_number to_s to_time].include? method
|
221
|
-
return @object.respond_to? if @object
|
233
|
+
return @object.respond_to?(method, all) if @object
|
222
234
|
false
|
223
235
|
end
|
224
236
|
|
@@ -4,9 +4,13 @@ module ReactiveRecord
|
|
4
4
|
module Getters
|
5
5
|
def get_belongs_to(assoc, reload = nil)
|
6
6
|
getter_common(assoc.attribute, reload) do |has_key, attr|
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
next if new?
|
8
|
+
if id.present?
|
9
|
+
value = fetch_by_id(attr, @model.primary_key)
|
10
|
+
klass = fetch_by_id(attr, 'model_name')
|
11
|
+
klass &&= Object.const_get(klass)
|
12
|
+
end
|
13
|
+
value = find_association(assoc, value, klass)
|
10
14
|
sync_ignore_dummy attr, value, has_key
|
11
15
|
end&.cast_to_current_sti_type
|
12
16
|
end
|
@@ -33,7 +37,8 @@ module ReactiveRecord
|
|
33
37
|
|
34
38
|
def get_server_method(attr, reload = nil)
|
35
39
|
non_relationship_getter_common(attr, reload) do |has_key|
|
36
|
-
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
|
37
42
|
end
|
38
43
|
end
|
39
44
|
|
@@ -75,10 +80,16 @@ module ReactiveRecord
|
|
75
80
|
if new?
|
76
81
|
yield has_key if block
|
77
82
|
elsif on_opal_client?
|
78
|
-
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
|
79
85
|
elsif id.present?
|
80
|
-
sync_attribute attr,
|
86
|
+
sync_attribute attr, fetch_by_id(attr)
|
81
87
|
else
|
88
|
+
# Not sure how to test this branch, it may never execute this line?
|
89
|
+
# If we are on opal_server then we should always be getting an id before getting here
|
90
|
+
# but if we do vector might not be set up properly to fetch the attribute
|
91
|
+
puts "*** Syncing attribute in getters.rb without an id. This may cause problems. ***"
|
92
|
+
puts "*** Report this to hyperstack.org if you see this message: vector = #{[*vector, attr]}"
|
82
93
|
sync_attribute attr, Base.fetch_from_db([*vector, attr])
|
83
94
|
end
|
84
95
|
end
|
@@ -99,18 +110,26 @@ module ReactiveRecord
|
|
99
110
|
else
|
100
111
|
virtual_fetch_on_server_warning(attribute) if on_opal_server? && changed?
|
101
112
|
yield false, attribute
|
102
|
-
end.tap {
|
113
|
+
end.tap { Hyperstack::Internal::State::Variable.get(self, attribute) unless data_loading? }
|
103
114
|
end
|
104
115
|
|
105
|
-
def find_association(association, id)
|
106
|
-
inverse_of = association.inverse_of
|
116
|
+
def find_association(association, id, klass)
|
107
117
|
instance = if id
|
108
|
-
find(
|
118
|
+
find(klass, klass.primary_key => id)
|
119
|
+
elsif association.polymorphic?
|
120
|
+
new_from_vector(nil, nil, *vector, association.attribute)
|
109
121
|
else
|
110
122
|
new_from_vector(association.klass, nil, *vector, association.attribute)
|
111
123
|
end
|
124
|
+
return instance if instance.is_a? DummyPolymorph
|
125
|
+
inverse_of = association.inverse_of(instance)
|
112
126
|
instance_backing_record_attributes = instance.attributes
|
113
127
|
inverse_association = association.klass.reflect_on_association(inverse_of)
|
128
|
+
# HMT-TODO: don't we need to do something with the through association case.
|
129
|
+
# perhaps we never hit this point...
|
130
|
+
if association.through_association?
|
131
|
+
IsomorphicHelpers.log "*********** called #{ar_instance}.find_association(#{association.attribute}) which is has many through!!!!!!!", :error
|
132
|
+
end
|
114
133
|
if inverse_association.collection?
|
115
134
|
instance_backing_record_attributes[inverse_of] = if id and id != ""
|
116
135
|
Collection.new(@model, instance, inverse_association, association.klass, ["find", id], inverse_of)
|
@@ -129,5 +148,9 @@ module ReactiveRecord
|
|
129
148
|
self.aggregate_attribute = attr
|
130
149
|
@ar_instance
|
131
150
|
end
|
151
|
+
|
152
|
+
def fetch_by_id(*vector)
|
153
|
+
Base.fetch_from_db([@model, *find_by_vector(@model.primary_key => id), *vector])
|
154
|
+
end
|
132
155
|
end
|
133
156
|
end
|
@@ -10,6 +10,7 @@ module ReactiveRecord
|
|
10
10
|
if RUBY_ENGINE != 'opal'
|
11
11
|
@server_data_cache = ReactiveRecord::ServerDataCache.new(context.controller.acting_user, {})
|
12
12
|
else
|
13
|
+
Hyperstack::Internal::State::Variable.set(WhileLoading, :quiet, true)
|
13
14
|
@public_columns_hash = get_public_columns_hash
|
14
15
|
define_attribute_methods
|
15
16
|
@outer_scopes = Set.new
|
@@ -46,6 +47,15 @@ module ReactiveRecord
|
|
46
47
|
self.class.instance_variable_get(:@records)
|
47
48
|
end
|
48
49
|
|
50
|
+
# constructs vector for find_by
|
51
|
+
def self.find_by_vector(attrs)
|
52
|
+
[:all, [:___hyperstack_internal_scoped_find_by, attrs], '*0']
|
53
|
+
end
|
54
|
+
|
55
|
+
def find_by_vector(attrs)
|
56
|
+
self.class.find_by_vector(attrs)
|
57
|
+
end
|
58
|
+
|
49
59
|
# Prerendering db access (returns nil if on client):
|
50
60
|
# at end of prerendering dumps all accessed records in the footer
|
51
61
|
|
@@ -57,7 +67,9 @@ module ReactiveRecord
|
|
57
67
|
|
58
68
|
isomorphic_method(:find_in_db) do |f, klass, attrs|
|
59
69
|
f.send_to_server klass.name, attrs if RUBY_ENGINE == 'opal'
|
60
|
-
f.when_on_server
|
70
|
+
f.when_on_server do
|
71
|
+
@server_data_cache[klass, *find_by_vector(attrs), 'id']
|
72
|
+
end
|
61
73
|
end
|
62
74
|
|
63
75
|
class << self
|
@@ -123,7 +135,6 @@ module ReactiveRecord
|
|
123
135
|
model.columns_hash[method_name] || model.server_methods[method_name]
|
124
136
|
end
|
125
137
|
|
126
|
-
|
127
138
|
class << self
|
128
139
|
|
129
140
|
attr_reader :pending_fetches
|
@@ -132,7 +143,7 @@ module ReactiveRecord
|
|
132
143
|
end
|
133
144
|
|
134
145
|
def self.schedule_fetch
|
135
|
-
Hyperstack::Internal::
|
146
|
+
Hyperstack::Internal::State::Variable.set(WhileLoading, :quiet, false) # moved from while loading module see loading! method
|
136
147
|
return if @fetch_scheduled
|
137
148
|
@current_fetch_id = Time.now
|
138
149
|
@fetch_scheduled = after(0) do
|
@@ -222,7 +233,7 @@ module ReactiveRecord
|
|
222
233
|
if association.collection?
|
223
234
|
# following line changed from .all to .collection on 10/28
|
224
235
|
[*value.collection, *value.unsaved_children].each do |assoc|
|
225
|
-
add_new_association.call(record, attribute, assoc.backing_record) if assoc.changed?(association.inverse_of) or assoc.
|
236
|
+
add_new_association.call(record, attribute, assoc.backing_record) if assoc.changed?(association.inverse_of(assoc)) or assoc.new_record?
|
226
237
|
end
|
227
238
|
elsif record.new? || record.changed?(attribute) || (record == record_being_saved && force)
|
228
239
|
if value.nil?
|
@@ -231,7 +242,7 @@ module ReactiveRecord
|
|
231
242
|
add_new_association.call record, attribute, value.backing_record
|
232
243
|
end
|
233
244
|
end
|
234
|
-
elsif aggregation = record.model.reflect_on_aggregation(attribute)
|
245
|
+
elsif (aggregation = record.model.reflect_on_aggregation(attribute)) && (aggregation.klass < ActiveRecord::Base)
|
235
246
|
add_new_association.call record, attribute, value.backing_record unless value.nil?
|
236
247
|
elsif aggregation
|
237
248
|
new_value = aggregation.serialize(value)
|
@@ -242,7 +253,7 @@ module ReactiveRecord
|
|
242
253
|
end if record.new? || record.changed? || (record == record_being_saved && force)
|
243
254
|
record_index += 1
|
244
255
|
end
|
245
|
-
[models, associations, backing_records]
|
256
|
+
[models.sort_by { |model| model[:id] }, associations, backing_records]
|
246
257
|
end
|
247
258
|
|
248
259
|
def save_or_validate(save, validate, force, &block)
|
@@ -252,8 +263,17 @@ module ReactiveRecord
|
|
252
263
|
HyperMesh.load do
|
253
264
|
ReactiveRecord.loads_pending! unless self.class.pending_fetches.empty?
|
254
265
|
end.then { send_save_to_server(save, validate, force, &block) }
|
255
|
-
#save_to_server(validate, force, &block)
|
256
266
|
else
|
267
|
+
if validate
|
268
|
+
# Handles the case where a model is valid, then some attribute is
|
269
|
+
# updated, and model.validate is called updating the error data.
|
270
|
+
# Now lets say the attribute changes back to the last synced value. In
|
271
|
+
# this case we need to revert the error records.
|
272
|
+
models, _, backing_records = self.class.gather_records([self], true, self)
|
273
|
+
models.each do |item|
|
274
|
+
backing_records[item[:id]].revert_errors!
|
275
|
+
end
|
276
|
+
end
|
257
277
|
promise = Promise.new
|
258
278
|
yield true, nil, [] if block
|
259
279
|
promise.resolve({success: true})
|
@@ -290,14 +310,13 @@ module ReactiveRecord
|
|
290
310
|
|
291
311
|
response[:saved_models].each do | item |
|
292
312
|
backing_records[item[0]].sync_unscoped_collection! if save
|
293
|
-
backing_records[item[0]].errors! item[3]
|
313
|
+
backing_records[item[0]].errors! item[3], save
|
294
314
|
end
|
295
315
|
|
296
316
|
yield response[:success], response[:message], response[:models] if block
|
297
317
|
promise.resolve response # TODO this could be problematic... there was no .json here, so .... what's to do?
|
298
318
|
|
299
319
|
rescue Exception => e
|
300
|
-
# debugger
|
301
320
|
log("Exception raised while saving - #{e}", :error)
|
302
321
|
ensure
|
303
322
|
backing_records.each { |_id, record| record.saved! rescue nil } if save
|
@@ -316,25 +335,29 @@ module ReactiveRecord
|
|
316
335
|
|
317
336
|
else
|
318
337
|
|
319
|
-
def self.find_record(model, id, vector, save)
|
338
|
+
def self.find_record(model, id, acting_user, vector, save)
|
320
339
|
if !save
|
321
340
|
found = vector[1..-1].inject(vector[0]) do |object, method|
|
322
341
|
if object.nil? # happens if you try to do an all on empty scope followed by more scopes
|
323
342
|
object
|
324
|
-
elsif method.is_a? Array
|
343
|
+
elsif method.is_a? Array #__secure_remote_access_to_
|
325
344
|
if method[0] == 'new'
|
326
345
|
object.new
|
327
346
|
else
|
328
|
-
object.send(*method)
|
347
|
+
object.send(:"__secure_remote_access_to_#{method[0]}", object, acting_user, *method[1..-1])
|
329
348
|
end
|
330
|
-
elsif method.is_a?
|
349
|
+
elsif method.is_a?(String) && method[0] == '*'
|
331
350
|
object[method.gsub(/^\*/,'').to_i]
|
332
351
|
else
|
333
|
-
object.send(method)
|
352
|
+
object.send(:"__secure_remote_access_to_#{method}", object, acting_user)
|
334
353
|
end
|
335
354
|
end
|
336
355
|
if id and (found.nil? or !(found.class <= model) or (found.id and found.id.to_s != id.to_s))
|
337
|
-
|
356
|
+
# TODO: the one case that this is okay is when we are doing a find(some_id) which
|
357
|
+
# does not exist. So the above check needs to deal with that if possible,
|
358
|
+
# otherwise we can just skip this check, as it was put in sometime back for
|
359
|
+
# debugging purposes, and is perhaps not necessary anymore
|
360
|
+
#raise "Inconsistent data sent to server - #{model.name}.find(#{id}) != [#{vector}]"
|
338
361
|
end
|
339
362
|
found
|
340
363
|
elsif id
|
@@ -362,13 +385,13 @@ module ReactiveRecord
|
|
362
385
|
id = attributes.delete(model.primary_key) if model.respond_to? :primary_key # if we are saving existing model primary key value will be present
|
363
386
|
vector = model_to_save[:vector]
|
364
387
|
vector = [vector[0].constantize] + vector[1..-1].collect do |method|
|
365
|
-
if method.is_a?(Array)
|
388
|
+
if method.is_a?(Array) && method.first == "find_by_id"
|
366
389
|
["find", method.last]
|
367
390
|
else
|
368
391
|
method
|
369
392
|
end
|
370
393
|
end
|
371
|
-
reactive_records[model_to_save[:id]] = vectors[vector] = record = find_record(model, id, vector, save) # ??? || validate ???
|
394
|
+
reactive_records[model_to_save[:id]] = vectors[vector] = record = find_record(model, id, acting_user, vector, save) # ??? || validate ???
|
372
395
|
next unless record
|
373
396
|
if attributes.empty?
|
374
397
|
dont_save_list << record unless save
|
@@ -434,24 +457,29 @@ module ReactiveRecord
|
|
434
457
|
parent.send("#{association[:attribute]}=", aggregate)
|
435
458
|
#puts "updated is frozen? #{aggregate.frozen?}, parent attributes = #{parent.send(association[:attribute]).attributes}"
|
436
459
|
elsif parent.class.reflect_on_association(association[:attribute].to_sym).nil?
|
437
|
-
|
460
|
+
raise "Missing association :#{association[:attribute]} for #{parent.class.name}. Was association defined on opal side only?"
|
438
461
|
elsif parent.class.reflect_on_association(association[:attribute].to_sym).collection?
|
439
462
|
#puts ">>>>>>>>>> #{parent.class.name}.send('#{association[:attribute]}') << #{reactive_records[association[:child_id]]})"
|
440
463
|
dont_save_list.delete(parent)
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
#
|
445
|
-
|
446
|
-
|
464
|
+
|
465
|
+
# if reactive_records[association[:child_id]]&.new_record?
|
466
|
+
# dont_save_list << reactive_records[association[:child_id]]
|
467
|
+
# end
|
468
|
+
|
469
|
+
if parent.new_record?
|
470
|
+
parent.send("#{association[:attribute]}") << reactive_records[association[:child_id]]
|
471
|
+
end
|
447
472
|
else
|
448
473
|
#puts ">>>>ASSOCIATION>>>> #{parent.class.name}.send('#{association[:attribute]}=', #{reactive_records[association[:child_id]]})"
|
449
474
|
parent.send("#{association[:attribute]}=", reactive_records[association[:child_id]])
|
450
475
|
dont_save_list.delete(parent)
|
451
|
-
|
476
|
+
|
477
|
+
# if parent.class.reflect_on_association(association[:attribute].to_sym).macro == :has_one &&
|
478
|
+
# reactive_records[association[:child_id]]&.new_record?
|
479
|
+
# dont_save_list << reactive_records[association[:child_id]]
|
480
|
+
# end
|
452
481
|
end
|
453
482
|
end if associations
|
454
|
-
|
455
483
|
# get rid of any records that don't require further processing, as a side effect
|
456
484
|
# we also save any records that need to be saved (these may be rolled back later.)
|
457
485
|
|
@@ -460,10 +488,13 @@ module ReactiveRecord
|
|
460
488
|
next true if record.frozen? # skip (but process later) frozen records
|
461
489
|
next true if dont_save_list.include?(record) # skip if the record is on the don't save list
|
462
490
|
next true if record.changed.include?(record.class.primary_key) # happens on an aggregate
|
463
|
-
next
|
491
|
+
#next true if record.persisted? # record may be have been saved as result of has_one assignment
|
492
|
+
# next false if record.id && !record.changed? # throw out any existing records with no changes
|
493
|
+
next record.persisted? if record.id && !record.changed?
|
464
494
|
# if we get to here save the record and return true to keep it
|
465
495
|
op = new_models.include?(record) ? :create_permitted? : :update_permitted?
|
466
|
-
|
496
|
+
|
497
|
+
record.check_permission_with_acting_user(acting_user, op).save(validate: validate) || true
|
467
498
|
end
|
468
499
|
|
469
500
|
# if called from ServerDataCache then save and validate are both false, and we just return the
|
@@ -476,14 +507,15 @@ module ReactiveRecord
|
|
476
507
|
# the all the error messages during a save so we can dump them to the server log.
|
477
508
|
|
478
509
|
all_messages = []
|
510
|
+
attributes = nil
|
479
511
|
|
480
512
|
saved_models = reactive_records.collect do |reactive_record_id, model|
|
481
513
|
messages = model.errors.messages if validate && !model.valid?
|
482
514
|
all_messages << [model, messages] if save && messages
|
483
515
|
attributes = model.__hyperstack_secure_attributes(acting_user)
|
516
|
+
attributes[model.class.primary_key] = model[model.class.primary_key]
|
484
517
|
[reactive_record_id, model.class.name, attributes, messages]
|
485
518
|
end
|
486
|
-
|
487
519
|
# if we are not saving (i.e. just validating) then we rollback the transaction
|
488
520
|
|
489
521
|
raise ActiveRecord::Rollback, 'This Rollback is intentional!' unless save
|
@@ -498,7 +530,6 @@ module ReactiveRecord
|
|
498
530
|
end
|
499
531
|
raise 'HyperModel saving records failed!'
|
500
532
|
end
|
501
|
-
|
502
533
|
end
|
503
534
|
|
504
535
|
{ success: true, saved_models: saved_models }
|
@@ -507,7 +538,7 @@ module ReactiveRecord
|
|
507
538
|
if save || validate
|
508
539
|
{success: false, saved_models: saved_models, message: e}
|
509
540
|
else
|
510
|
-
{}
|
541
|
+
raise e # was returning {} TODO verify that just raising the error at least reports the error
|
511
542
|
end
|
512
543
|
end
|
513
544
|
|
@@ -518,14 +549,12 @@ module ReactiveRecord
|
|
518
549
|
if RUBY_ENGINE == 'opal'
|
519
550
|
|
520
551
|
def destroy(&block)
|
552
|
+
return if @destroyed || @being_destroyed
|
521
553
|
|
522
|
-
|
523
|
-
|
524
|
-
#destroy_associations
|
554
|
+
# destroy_associations
|
525
555
|
|
526
556
|
promise = Promise.new
|
527
|
-
|
528
|
-
if !data_loading? and (id or vector)
|
557
|
+
if !data_loading? && (id || vector)
|
529
558
|
Operations::Destroy.run(model: ar_instance.model_name.to_s, id: id, vector: vector)
|
530
559
|
.then do |response|
|
531
560
|
Broadcast.to_self ar_instance
|
@@ -536,7 +565,7 @@ module ReactiveRecord
|
|
536
565
|
destroy_associations
|
537
566
|
# sync_unscoped_collection! # ? should we do this here was NOT being done before hypermesh integration
|
538
567
|
yield true, nil if block
|
539
|
-
promise.resolve(
|
568
|
+
promise.resolve(success: true)
|
540
569
|
end
|
541
570
|
|
542
571
|
# DO NOT CLEAR ATTRIBUTES. Records that are not found, are destroyed, and if they are searched for again, we want to make
|
@@ -556,18 +585,16 @@ module ReactiveRecord
|
|
556
585
|
def self.destroy_record(model, id, vector, acting_user)
|
557
586
|
model = Object.const_get(model)
|
558
587
|
record = if id
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
588
|
+
model.find(id)
|
589
|
+
else
|
590
|
+
ServerDataCache.new(acting_user, {})[*vector].value
|
591
|
+
end
|
564
592
|
|
565
593
|
record.check_permission_with_acting_user(acting_user, :destroy_permitted?).destroy
|
566
|
-
{success: true, attributes: {}}
|
567
|
-
|
594
|
+
{ success: true, attributes: {} }
|
568
595
|
rescue Exception => e
|
569
|
-
#ReactiveRecord::Pry.rescued(e)
|
570
|
-
{success: false, record: record, message: e}
|
596
|
+
# ReactiveRecord::Pry.rescued(e)
|
597
|
+
{ success: false, record: record, message: e }
|
571
598
|
end
|
572
599
|
end
|
573
600
|
end
|