hyper-model 1.0.alpha1.2 → 1.0.alpha1.7
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 -5
- data/Rakefile +27 -3
- data/hyper-model.gemspec +11 -19
- data/lib/active_record_base.rb +105 -33
- data/lib/enumerable/pluck.rb +3 -2
- data/lib/hyper-model.rb +4 -1
- 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 +32 -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 +8 -4
- data/lib/reactive_record/active_record/instance_methods.rb +73 -5
- data/lib/reactive_record/active_record/public_columns_hash.rb +25 -26
- data/lib/reactive_record/active_record/reactive_record/backing_record_inspector.rb +22 -5
- data/lib/reactive_record/active_record/reactive_record/base.rb +50 -24
- data/lib/reactive_record/active_record/reactive_record/collection.rb +226 -68
- 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 +81 -51
- 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 +105 -68
- data/lib/reactive_record/active_record/reactive_record/while_loading.rb +249 -32
- 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 +54 -153
- data/Gemfile.lock +0 -421
@@ -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
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'json'
|
2
|
-
|
3
1
|
module ReactiveRecord
|
4
2
|
|
5
3
|
class Base
|
@@ -10,6 +8,7 @@ module ReactiveRecord
|
|
10
8
|
if RUBY_ENGINE != 'opal'
|
11
9
|
@server_data_cache = ReactiveRecord::ServerDataCache.new(context.controller.acting_user, {})
|
12
10
|
else
|
11
|
+
Hyperstack::Internal::State::Variable.set(WhileLoading, :quiet, true)
|
13
12
|
@public_columns_hash = get_public_columns_hash
|
14
13
|
define_attribute_methods
|
15
14
|
@outer_scopes = Set.new
|
@@ -46,6 +45,15 @@ module ReactiveRecord
|
|
46
45
|
self.class.instance_variable_get(:@records)
|
47
46
|
end
|
48
47
|
|
48
|
+
# constructs vector for find_by
|
49
|
+
def self.find_by_vector(attrs)
|
50
|
+
[:all, [:___hyperstack_internal_scoped_find_by, attrs], '*0']
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_by_vector(attrs)
|
54
|
+
self.class.find_by_vector(attrs)
|
55
|
+
end
|
56
|
+
|
49
57
|
# Prerendering db access (returns nil if on client):
|
50
58
|
# at end of prerendering dumps all accessed records in the footer
|
51
59
|
|
@@ -57,7 +65,9 @@ module ReactiveRecord
|
|
57
65
|
|
58
66
|
isomorphic_method(:find_in_db) do |f, klass, attrs|
|
59
67
|
f.send_to_server klass.name, attrs if RUBY_ENGINE == 'opal'
|
60
|
-
f.when_on_server
|
68
|
+
f.when_on_server do
|
69
|
+
@server_data_cache[klass, *find_by_vector(attrs), 'id']
|
70
|
+
end
|
61
71
|
end
|
62
72
|
|
63
73
|
class << self
|
@@ -123,7 +133,6 @@ module ReactiveRecord
|
|
123
133
|
model.columns_hash[method_name] || model.server_methods[method_name]
|
124
134
|
end
|
125
135
|
|
126
|
-
|
127
136
|
class << self
|
128
137
|
|
129
138
|
attr_reader :pending_fetches
|
@@ -222,7 +231,7 @@ module ReactiveRecord
|
|
222
231
|
if association.collection?
|
223
232
|
# following line changed from .all to .collection on 10/28
|
224
233
|
[*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.
|
234
|
+
add_new_association.call(record, attribute, assoc.backing_record) if assoc.changed?(association.inverse_of(assoc)) or assoc.new_record?
|
226
235
|
end
|
227
236
|
elsif record.new? || record.changed?(attribute) || (record == record_being_saved && force)
|
228
237
|
if value.nil?
|
@@ -231,7 +240,7 @@ module ReactiveRecord
|
|
231
240
|
add_new_association.call record, attribute, value.backing_record
|
232
241
|
end
|
233
242
|
end
|
234
|
-
elsif aggregation = record.model.reflect_on_aggregation(attribute)
|
243
|
+
elsif (aggregation = record.model.reflect_on_aggregation(attribute)) && (aggregation.klass < ActiveRecord::Base)
|
235
244
|
add_new_association.call record, attribute, value.backing_record unless value.nil?
|
236
245
|
elsif aggregation
|
237
246
|
new_value = aggregation.serialize(value)
|
@@ -242,7 +251,7 @@ module ReactiveRecord
|
|
242
251
|
end if record.new? || record.changed? || (record == record_being_saved && force)
|
243
252
|
record_index += 1
|
244
253
|
end
|
245
|
-
[models, associations, backing_records]
|
254
|
+
[models.sort_by { |model| model[:id] }, associations, backing_records]
|
246
255
|
end
|
247
256
|
|
248
257
|
def save_or_validate(save, validate, force, &block)
|
@@ -252,8 +261,17 @@ module ReactiveRecord
|
|
252
261
|
HyperMesh.load do
|
253
262
|
ReactiveRecord.loads_pending! unless self.class.pending_fetches.empty?
|
254
263
|
end.then { send_save_to_server(save, validate, force, &block) }
|
255
|
-
#save_to_server(validate, force, &block)
|
256
264
|
else
|
265
|
+
if validate
|
266
|
+
# Handles the case where a model is valid, then some attribute is
|
267
|
+
# updated, and model.validate is called updating the error data.
|
268
|
+
# Now lets say the attribute changes back to the last synced value. In
|
269
|
+
# this case we need to revert the error records.
|
270
|
+
models, _, backing_records = self.class.gather_records([self], true, self)
|
271
|
+
models.each do |item|
|
272
|
+
backing_records[item[:id]].revert_errors!
|
273
|
+
end
|
274
|
+
end
|
257
275
|
promise = Promise.new
|
258
276
|
yield true, nil, [] if block
|
259
277
|
promise.resolve({success: true})
|
@@ -290,14 +308,13 @@ module ReactiveRecord
|
|
290
308
|
|
291
309
|
response[:saved_models].each do | item |
|
292
310
|
backing_records[item[0]].sync_unscoped_collection! if save
|
293
|
-
backing_records[item[0]].errors! item[3]
|
311
|
+
backing_records[item[0]].errors! item[3], save
|
294
312
|
end
|
295
313
|
|
296
314
|
yield response[:success], response[:message], response[:models] if block
|
297
315
|
promise.resolve response # TODO this could be problematic... there was no .json here, so .... what's to do?
|
298
316
|
|
299
317
|
rescue Exception => e
|
300
|
-
# debugger
|
301
318
|
log("Exception raised while saving - #{e}", :error)
|
302
319
|
ensure
|
303
320
|
backing_records.each { |_id, record| record.saved! rescue nil } if save
|
@@ -316,25 +333,29 @@ module ReactiveRecord
|
|
316
333
|
|
317
334
|
else
|
318
335
|
|
319
|
-
def self.find_record(model, id, vector, save)
|
336
|
+
def self.find_record(model, id, acting_user, vector, save)
|
320
337
|
if !save
|
321
338
|
found = vector[1..-1].inject(vector[0]) do |object, method|
|
322
339
|
if object.nil? # happens if you try to do an all on empty scope followed by more scopes
|
323
340
|
object
|
324
|
-
elsif method.is_a? Array
|
341
|
+
elsif method.is_a? Array #__secure_remote_access_to_
|
325
342
|
if method[0] == 'new'
|
326
343
|
object.new
|
327
344
|
else
|
328
|
-
object.send(*method)
|
345
|
+
object.send(:"__secure_remote_access_to_#{method[0]}", object, acting_user, *method[1..-1])
|
329
346
|
end
|
330
|
-
elsif method.is_a?
|
347
|
+
elsif method.is_a?(String) && method[0] == '*'
|
331
348
|
object[method.gsub(/^\*/,'').to_i]
|
332
349
|
else
|
333
|
-
object.send(method)
|
350
|
+
object.send(:"__secure_remote_access_to_#{method}", object, acting_user)
|
334
351
|
end
|
335
352
|
end
|
336
353
|
if id and (found.nil? or !(found.class <= model) or (found.id and found.id.to_s != id.to_s))
|
337
|
-
|
354
|
+
# TODO: the one case that this is okay is when we are doing a find(some_id) which
|
355
|
+
# does not exist. So the above check needs to deal with that if possible,
|
356
|
+
# otherwise we can just skip this check, as it was put in sometime back for
|
357
|
+
# debugging purposes, and is perhaps not necessary anymore
|
358
|
+
#raise "Inconsistent data sent to server - #{model.name}.find(#{id}) != [#{vector}]"
|
338
359
|
end
|
339
360
|
found
|
340
361
|
elsif id
|
@@ -362,13 +383,13 @@ module ReactiveRecord
|
|
362
383
|
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
384
|
vector = model_to_save[:vector]
|
364
385
|
vector = [vector[0].constantize] + vector[1..-1].collect do |method|
|
365
|
-
if method.is_a?(Array)
|
386
|
+
if method.is_a?(Array) && method.first == "find_by_id"
|
366
387
|
["find", method.last]
|
367
388
|
else
|
368
389
|
method
|
369
390
|
end
|
370
391
|
end
|
371
|
-
reactive_records[model_to_save[:id]] = vectors[vector] = record = find_record(model, id, vector, save) # ??? || validate ???
|
392
|
+
reactive_records[model_to_save[:id]] = vectors[vector] = record = find_record(model, id, acting_user, vector, save) # ??? || validate ???
|
372
393
|
next unless record
|
373
394
|
if attributes.empty?
|
374
395
|
dont_save_list << record unless save
|
@@ -434,24 +455,29 @@ module ReactiveRecord
|
|
434
455
|
parent.send("#{association[:attribute]}=", aggregate)
|
435
456
|
#puts "updated is frozen? #{aggregate.frozen?}, parent attributes = #{parent.send(association[:attribute]).attributes}"
|
436
457
|
elsif parent.class.reflect_on_association(association[:attribute].to_sym).nil?
|
437
|
-
|
458
|
+
raise "Missing association :#{association[:attribute]} for #{parent.class.name}. Was association defined on opal side only?"
|
438
459
|
elsif parent.class.reflect_on_association(association[:attribute].to_sym).collection?
|
439
460
|
#puts ">>>>>>>>>> #{parent.class.name}.send('#{association[:attribute]}') << #{reactive_records[association[:child_id]]})"
|
440
461
|
dont_save_list.delete(parent)
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
#
|
445
|
-
|
446
|
-
|
462
|
+
|
463
|
+
# if reactive_records[association[:child_id]]&.new_record?
|
464
|
+
# dont_save_list << reactive_records[association[:child_id]]
|
465
|
+
# end
|
466
|
+
|
467
|
+
if parent.new_record?
|
468
|
+
parent.send("#{association[:attribute]}") << reactive_records[association[:child_id]]
|
469
|
+
end
|
447
470
|
else
|
448
471
|
#puts ">>>>ASSOCIATION>>>> #{parent.class.name}.send('#{association[:attribute]}=', #{reactive_records[association[:child_id]]})"
|
449
472
|
parent.send("#{association[:attribute]}=", reactive_records[association[:child_id]])
|
450
473
|
dont_save_list.delete(parent)
|
451
|
-
|
474
|
+
|
475
|
+
# if parent.class.reflect_on_association(association[:attribute].to_sym).macro == :has_one &&
|
476
|
+
# reactive_records[association[:child_id]]&.new_record?
|
477
|
+
# dont_save_list << reactive_records[association[:child_id]]
|
478
|
+
# end
|
452
479
|
end
|
453
480
|
end if associations
|
454
|
-
|
455
481
|
# get rid of any records that don't require further processing, as a side effect
|
456
482
|
# we also save any records that need to be saved (these may be rolled back later.)
|
457
483
|
|
@@ -460,10 +486,13 @@ module ReactiveRecord
|
|
460
486
|
next true if record.frozen? # skip (but process later) frozen records
|
461
487
|
next true if dont_save_list.include?(record) # skip if the record is on the don't save list
|
462
488
|
next true if record.changed.include?(record.class.primary_key) # happens on an aggregate
|
463
|
-
next
|
489
|
+
#next true if record.persisted? # record may be have been saved as result of has_one assignment
|
490
|
+
# next false if record.id && !record.changed? # throw out any existing records with no changes
|
491
|
+
next record.persisted? if record.id && !record.changed?
|
464
492
|
# if we get to here save the record and return true to keep it
|
465
493
|
op = new_models.include?(record) ? :create_permitted? : :update_permitted?
|
466
|
-
|
494
|
+
|
495
|
+
record.check_permission_with_acting_user(acting_user, op).save(validate: validate) || true
|
467
496
|
end
|
468
497
|
|
469
498
|
# if called from ServerDataCache then save and validate are both false, and we just return the
|
@@ -476,14 +505,15 @@ module ReactiveRecord
|
|
476
505
|
# the all the error messages during a save so we can dump them to the server log.
|
477
506
|
|
478
507
|
all_messages = []
|
508
|
+
attributes = nil
|
479
509
|
|
480
510
|
saved_models = reactive_records.collect do |reactive_record_id, model|
|
481
511
|
messages = model.errors.messages if validate && !model.valid?
|
482
512
|
all_messages << [model, messages] if save && messages
|
483
513
|
attributes = model.__hyperstack_secure_attributes(acting_user)
|
514
|
+
attributes[model.class.primary_key] = model[model.class.primary_key]
|
484
515
|
[reactive_record_id, model.class.name, attributes, messages]
|
485
516
|
end
|
486
|
-
|
487
517
|
# if we are not saving (i.e. just validating) then we rollback the transaction
|
488
518
|
|
489
519
|
raise ActiveRecord::Rollback, 'This Rollback is intentional!' unless save
|
@@ -498,7 +528,6 @@ module ReactiveRecord
|
|
498
528
|
end
|
499
529
|
raise 'HyperModel saving records failed!'
|
500
530
|
end
|
501
|
-
|
502
531
|
end
|
503
532
|
|
504
533
|
{ success: true, saved_models: saved_models }
|
@@ -507,7 +536,7 @@ module ReactiveRecord
|
|
507
536
|
if save || validate
|
508
537
|
{success: false, saved_models: saved_models, message: e}
|
509
538
|
else
|
510
|
-
{}
|
539
|
+
raise e # was returning {} TODO verify that just raising the error at least reports the error
|
511
540
|
end
|
512
541
|
end
|
513
542
|
|
@@ -518,25 +547,31 @@ module ReactiveRecord
|
|
518
547
|
if RUBY_ENGINE == 'opal'
|
519
548
|
|
520
549
|
def destroy(&block)
|
550
|
+
return if @destroyed || @being_destroyed
|
521
551
|
|
522
|
-
|
523
|
-
|
524
|
-
#destroy_associations
|
552
|
+
# destroy_associations
|
525
553
|
|
526
554
|
promise = Promise.new
|
527
|
-
|
528
|
-
if !data_loading? and (id or vector)
|
555
|
+
if !data_loading? && (id || vector)
|
529
556
|
Operations::Destroy.run(model: ar_instance.model_name.to_s, id: id, vector: vector)
|
530
557
|
.then do |response|
|
531
|
-
|
558
|
+
#[reactive_record_id, model.class.name, attributes, messages] model.errors.messages
|
559
|
+
|
560
|
+
if response[:success]
|
561
|
+
@destroyed = true
|
562
|
+
Broadcast.to_self ar_instance
|
563
|
+
else
|
564
|
+
errors! response[:messages]
|
565
|
+
end
|
532
566
|
yield response[:success], response[:message] if block
|
533
567
|
promise.resolve response
|
534
568
|
end
|
535
569
|
else
|
536
570
|
destroy_associations
|
537
571
|
# sync_unscoped_collection! # ? should we do this here was NOT being done before hypermesh integration
|
572
|
+
@destroyed = true
|
538
573
|
yield true, nil if block
|
539
|
-
promise.resolve(
|
574
|
+
promise.resolve(success: true)
|
540
575
|
end
|
541
576
|
|
542
577
|
# DO NOT CLEAR ATTRIBUTES. Records that are not found, are destroyed, and if they are searched for again, we want to make
|
@@ -546,8 +581,6 @@ module ReactiveRecord
|
|
546
581
|
# sync!
|
547
582
|
# and modify server_data_cache so that it does NOT call destroy
|
548
583
|
|
549
|
-
@destroyed = true
|
550
|
-
|
551
584
|
promise
|
552
585
|
end
|
553
586
|
|
@@ -556,18 +589,15 @@ module ReactiveRecord
|
|
556
589
|
def self.destroy_record(model, id, vector, acting_user)
|
557
590
|
model = Object.const_get(model)
|
558
591
|
record = if id
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
record.check_permission_with_acting_user(acting_user, :destroy_permitted?).destroy
|
566
|
-
{success: true, attributes: {}}
|
592
|
+
model.find(id)
|
593
|
+
else
|
594
|
+
ServerDataCache.new(acting_user, {})[*vector].value
|
595
|
+
end
|
567
596
|
|
597
|
+
success = record.check_permission_with_acting_user(acting_user, :destroy_permitted?).destroy
|
598
|
+
{ success: success, attributes: {}, messages: record.errors.messages }
|
568
599
|
rescue Exception => e
|
569
|
-
|
570
|
-
{success: false, record: record, message: e}
|
600
|
+
{ success: false, record: record, message: e }
|
571
601
|
end
|
572
602
|
end
|
573
603
|
end
|