hyper-model 1.0.alpha1.1 → 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/.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
|