reactive-record 0.7.15 → 0.7.16
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/app/controllers/reactive_record/reactive_record_controller.rb +4 -4
- data/lib/reactive_record/active_record/class_methods.rb +4 -0
- data/lib/reactive_record/active_record/instance_methods.rb +30 -22
- data/lib/reactive_record/active_record/reactive_record/base.rb +69 -31
- data/lib/reactive_record/active_record/reactive_record/collection.rb +30 -20
- data/lib/reactive_record/active_record/reactive_record/isomorphic_base.rb +55 -19
- data/lib/reactive_record/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 139439c144b766200fccb72d67240cbfec0d6eae
|
4
|
+
data.tar.gz: 1d67f45f99d68e4e2dd0ac5db2ee88885b491e80
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4389f9eeecb8e5da8ff5788bdcb5886b2f086111e8a7ca7620e65e78cec26fcdb2b371fb2ab2d38fa0a7f3517a675478557a1a157915e99a93848e6d526b105d
|
7
|
+
data.tar.gz: 1c647b32c2b98182e8e8dbd12672ce1faca07bce3613405baec06c8c30ed56a21f15e27d342448fdd10969dddcd82b40d36acb04669693ea5116262b83969e63
|
@@ -3,16 +3,16 @@ require 'reactive_record/server_data_cache'
|
|
3
3
|
module ReactiveRecord
|
4
4
|
|
5
5
|
class ReactiveRecordController < ::ApplicationController
|
6
|
-
|
6
|
+
|
7
7
|
def fetch
|
8
8
|
render :json => ReactiveRecord::ServerDataCache[params[:pending_fetches], acting_user]
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
|
12
12
|
def save
|
13
|
-
render :json => ReactiveRecord::Base.save_records(params[:models], params[:associations], acting_user)
|
13
|
+
render :json => ReactiveRecord::Base.save_records(params[:models], params[:associations], acting_user, params[:validate])
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
def destroy
|
17
17
|
render :json => ReactiveRecord::Base.destroy_record(params[:model], params[:id], params[:vector], acting_user)
|
18
18
|
end
|
@@ -90,6 +90,10 @@ module ActiveRecord
|
|
90
90
|
Aggregations::AggregationReflection.new(base_class, :composed_of, name, opts)
|
91
91
|
end
|
92
92
|
|
93
|
+
def column_names
|
94
|
+
[] # it would be great to figure out how to get this information on the client! For now we just return an empty array
|
95
|
+
end
|
96
|
+
|
93
97
|
[
|
94
98
|
"table_name=", "before_validation", "with_options", "validates_presence_of", "validates_format_of",
|
95
99
|
"accepts_nested_attributes_for", "before_create", "after_create", "before_save", "after_save", "before_destroy", "where", "validate",
|
@@ -1,11 +1,11 @@
|
|
1
1
|
module ActiveRecord
|
2
|
-
|
2
|
+
|
3
3
|
module InstanceMethods
|
4
|
-
|
4
|
+
|
5
5
|
def attributes
|
6
6
|
@backing_record.attributes
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
def initialize(hash = {})
|
10
10
|
if hash.is_a? ReactiveRecord::Base
|
11
11
|
@backing_record = hash
|
@@ -14,19 +14,27 @@ module ActiveRecord
|
|
14
14
|
# we have to build the backing record first then initialize it so associations work correctly
|
15
15
|
@backing_record = ReactiveRecord::Base.new(self.class, {}, self)
|
16
16
|
@backing_record.instance_eval do
|
17
|
-
self.class.load_data
|
17
|
+
self.class.load_data do
|
18
|
+
hash.each do |attribute, value|
|
19
|
+
unless attribute == primary_key
|
20
|
+
reactive_set!(attribute, value) unless attribute == primary_key
|
21
|
+
changed_attributes << attribute
|
22
|
+
end
|
23
|
+
end
|
24
|
+
#changed_attributes << primary_key # insures that changed attributes has at least one element
|
25
|
+
end
|
18
26
|
end
|
19
27
|
end
|
20
28
|
end
|
21
|
-
|
29
|
+
|
22
30
|
def primary_key
|
23
31
|
self.class.primary_key
|
24
32
|
end
|
25
|
-
|
33
|
+
|
26
34
|
def id
|
27
35
|
@backing_record.reactive_get!(primary_key)
|
28
36
|
end
|
29
|
-
|
37
|
+
|
30
38
|
def id=(value)
|
31
39
|
@backing_record.id = value
|
32
40
|
end
|
@@ -35,19 +43,19 @@ module ActiveRecord
|
|
35
43
|
# in reality should return ActiveModel::Name object, blah blah
|
36
44
|
self.class.model_name
|
37
45
|
end
|
38
|
-
|
46
|
+
|
39
47
|
def revert
|
40
48
|
@backing_record.revert
|
41
49
|
end
|
42
|
-
|
50
|
+
|
43
51
|
def changed?
|
44
52
|
@backing_record.changed?
|
45
53
|
end
|
46
|
-
|
54
|
+
|
47
55
|
def ==(ar_instance)
|
48
56
|
@backing_record == ar_instance.instance_eval { @backing_record }
|
49
57
|
end
|
50
|
-
|
58
|
+
|
51
59
|
def method_missing(name, *args, &block)
|
52
60
|
if name =~ /_changed\?$/
|
53
61
|
@backing_record.changed?(name.gsub(/_changed\?$/,""))
|
@@ -55,33 +63,33 @@ module ActiveRecord
|
|
55
63
|
attribute_name = name.gsub(/=$/,"")
|
56
64
|
@backing_record.reactive_set!(attribute_name, args[0])
|
57
65
|
elsif args.count == 0 && !block
|
58
|
-
@backing_record.reactive_get!(name)
|
66
|
+
@backing_record.reactive_get!(name)
|
59
67
|
else
|
60
68
|
super
|
61
69
|
end
|
62
70
|
end
|
63
|
-
|
64
|
-
def save(&block)
|
65
|
-
@backing_record.save &block
|
71
|
+
|
72
|
+
def save(opts = {}, &block)
|
73
|
+
@backing_record.save(opts[:validate], &block)
|
66
74
|
end
|
67
|
-
|
75
|
+
|
68
76
|
def saving?
|
69
77
|
@backing_record.saving?
|
70
78
|
end
|
71
|
-
|
79
|
+
|
72
80
|
def destroy(&block)
|
73
81
|
@backing_record.destroy &block
|
74
82
|
end
|
75
|
-
|
83
|
+
|
76
84
|
def new?
|
77
85
|
@backing_record.new?
|
78
86
|
end
|
79
|
-
|
87
|
+
|
80
88
|
def errors
|
89
|
+
React::State.get_state(@backing_record, @backing_record)
|
81
90
|
@backing_record.errors
|
82
91
|
end
|
83
|
-
|
92
|
+
|
84
93
|
end
|
85
|
-
|
94
|
+
|
86
95
|
end
|
87
|
-
|
@@ -29,6 +29,9 @@ module ReactiveRecord
|
|
29
29
|
attr_accessor :ar_instance
|
30
30
|
attr_accessor :vector
|
31
31
|
attr_accessor :model
|
32
|
+
attr_accessor :changed_attributes
|
33
|
+
attr_accessor :aggregate_owner
|
34
|
+
attr_accessor :aggregate_attribute
|
32
35
|
|
33
36
|
# While data is being loaded from the server certain internal behaviors need to change
|
34
37
|
# for example records all record changes are synced as they happen.
|
@@ -84,7 +87,7 @@ module ReactiveRecord
|
|
84
87
|
record.ar_instance ||= infer_type_from_hash(model, record.attributes).new(record)
|
85
88
|
end
|
86
89
|
|
87
|
-
def self.new_from_vector(model,
|
90
|
+
def self.new_from_vector(model, aggregate_owner, *vector)
|
88
91
|
# this is the equivilent of find but for associations and aggregations
|
89
92
|
# because we are not fetching a specific attribute yet, there is NO communication with the
|
90
93
|
# server. That only happens during find.
|
@@ -97,6 +100,8 @@ module ReactiveRecord
|
|
97
100
|
record = new model
|
98
101
|
record.vector = vector
|
99
102
|
end
|
103
|
+
record.aggregate_owner = aggregate_owner
|
104
|
+
record.aggregate_attribute = vector.last
|
100
105
|
record.ar_instance ||= infer_type_from_hash(model, record.attributes).new(record)
|
101
106
|
end
|
102
107
|
|
@@ -105,6 +110,7 @@ module ReactiveRecord
|
|
105
110
|
@ar_instance = ar_instance
|
106
111
|
@synced_attributes = {}
|
107
112
|
@attributes = {}
|
113
|
+
@changed_attributes = []
|
108
114
|
records[model] << self
|
109
115
|
end
|
110
116
|
|
@@ -174,39 +180,54 @@ module ReactiveRecord
|
|
174
180
|
attributes[attribute].attributes[inverse_of] = nil
|
175
181
|
end
|
176
182
|
end
|
183
|
+
elsif @model.reflect_on_aggregation(attribute)
|
184
|
+
attributes[attribute].instance_variable_get(:@backing_record).aggregate_owner = nil if attributes[attribute]
|
185
|
+
aggregate = value.instance_variable_get(:@backing_record)
|
186
|
+
aggregate.aggregate_owner = self
|
187
|
+
aggregate.aggregate_attribute = attribute
|
177
188
|
end
|
178
|
-
|
179
|
-
attributes[attribute] = value
|
180
|
-
React::State.set_state(self, attribute, value) unless data_loading?
|
181
|
-
React::State.set_state(self, self, :changed) unless data_loading? or (was_changed == changed2({attribute => value}))
|
189
|
+
update_attribute(attribute, value)
|
182
190
|
end
|
183
191
|
value
|
184
192
|
end
|
185
193
|
|
186
|
-
def
|
187
|
-
|
188
|
-
|
189
|
-
@
|
194
|
+
def update_attribute(attribute, *args)
|
195
|
+
value = args[0]
|
196
|
+
changed = if args.count == 0
|
197
|
+
if association = @model.reflect_on_association(attribute) and association.collection?
|
198
|
+
attributes[attribute] != @synced_attributes[attribute]
|
199
|
+
else
|
200
|
+
!attributes[attribute].instance_variable_get(:@backing_record).changed_attributes.empty?
|
201
|
+
end
|
202
|
+
elsif association = @model.reflect_on_association(attribute) and association.collection?
|
203
|
+
value != @synced_attributes[attribute]
|
190
204
|
else
|
191
|
-
|
192
|
-
|
193
|
-
|
205
|
+
!@synced_attributes.has_key?(attribute) or @synced_attributes[attribute] != value
|
206
|
+
end
|
207
|
+
empty_before = changed_attributes.empty?
|
208
|
+
if !changed
|
209
|
+
changed_attributes.delete(attribute)
|
210
|
+
elsif !changed_attributes.include?(attribute)
|
211
|
+
changed_attributes << attribute
|
212
|
+
end
|
213
|
+
attributes[attribute] = value if args.count != 0
|
214
|
+
React::State.set_state(self, attribute, value) unless data_loading?
|
215
|
+
if empty_before != changed_attributes.empty?
|
216
|
+
React::State.set_state(self, "!CHANGED!", !changed_attributes.empty?) unless data_loading?
|
217
|
+
aggregate_owner.update_attribute(aggregate_attribute) if aggregate_owner
|
218
|
+
end
|
194
219
|
end
|
195
220
|
|
196
|
-
def
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
return true
|
204
|
-
end
|
221
|
+
def changed?(*args)
|
222
|
+
if args.count == 0
|
223
|
+
React::State.get_state(self, "!CHANGED!")
|
224
|
+
!changed_attributes.empty?
|
225
|
+
else
|
226
|
+
React::State.get_state(self, args[0])
|
227
|
+
changed_attributes.include? args[0]
|
205
228
|
end
|
206
|
-
false
|
207
229
|
end
|
208
230
|
|
209
|
-
|
210
231
|
def errors
|
211
232
|
@errors ||= ActiveModel::Error.new
|
212
233
|
end
|
@@ -214,15 +235,25 @@ module ReactiveRecord
|
|
214
235
|
def sync!(hash = {}) # does NOT notify (see saved! for notification)
|
215
236
|
@attributes.merge! hash
|
216
237
|
@synced_attributes = @attributes.dup
|
217
|
-
@synced_attributes.each
|
238
|
+
@synced_attributes.each do |key, value|
|
239
|
+
if value.is_a? Collection
|
240
|
+
@synced_attributes[key] = value.dup_for_sync
|
241
|
+
elsif aggregation = model.reflect_on_aggregation(key)
|
242
|
+
value.instance_variable_get(:@backing_record).sync!
|
243
|
+
end
|
244
|
+
end
|
245
|
+
@changed_attributes = []
|
218
246
|
@saving = false
|
219
247
|
@errors = nil
|
248
|
+
# set the vector - this only happens when a new record is saved
|
249
|
+
@vector = [@model, ["find_by_#{@model.primary_key}", id]] if (!vector or vector.empty?) and id and id != ""
|
220
250
|
self
|
221
251
|
end
|
222
252
|
|
223
253
|
def sync_attribute(attribute, value)
|
224
254
|
@synced_attributes[attribute] = attributes[attribute] = value
|
225
255
|
@synced_attributes[attribute] = value.dup if value.is_a? ReactiveRecord::Collection
|
256
|
+
@changed_attributes.delete(attribute)
|
226
257
|
value
|
227
258
|
end
|
228
259
|
|
@@ -231,6 +262,7 @@ module ReactiveRecord
|
|
231
262
|
@ar_instance.send("#{attribute}=", @synced_attributes[attribute])
|
232
263
|
end
|
233
264
|
@attributes.delete_if { |attribute, value| !@synced_attributes.has_key?(attribute) }
|
265
|
+
@changed_attributes = []
|
234
266
|
@errors = nil
|
235
267
|
end
|
236
268
|
|
@@ -239,14 +271,16 @@ module ReactiveRecord
|
|
239
271
|
@saving = true
|
240
272
|
end
|
241
273
|
|
242
|
-
def
|
274
|
+
def errors!(errors)
|
275
|
+
@errors = errors and ActiveModel::Error.new(errors)
|
276
|
+
end
|
277
|
+
|
278
|
+
def saved! # sets saving to false AND notifies
|
243
279
|
@saving = false
|
244
|
-
|
245
|
-
if errors
|
246
|
-
#errors.each { |attribute, error| React::State.set_state(self, attribute, attributes[attribute]) }
|
247
|
-
React::State.set_state(self, self, :errors)
|
248
|
-
elsif !data_loading?
|
280
|
+
if !@errors or @errors.empty?
|
249
281
|
React::State.set_state(self, self, :saved)
|
282
|
+
elsif !data_loading?
|
283
|
+
React::State.set_state(self, self, :error)
|
250
284
|
end
|
251
285
|
self
|
252
286
|
end
|
@@ -304,7 +338,11 @@ module ReactiveRecord
|
|
304
338
|
elsif association = @model.reflect_on_association(method) and association.collection?
|
305
339
|
@attributes[method] = Collection.new(association.klass, @ar_instance, association)
|
306
340
|
elsif aggregation = @model.reflect_on_aggregation(method)
|
307
|
-
@attributes[method] = aggregation.klass.new
|
341
|
+
@attributes[method] = aggregation.klass.new.tap do |aggregate|
|
342
|
+
backing_record = aggregate.instance_variable_get(:@backing_record)
|
343
|
+
backing_record.aggregate_owner = self
|
344
|
+
backing_record.aggregate_attribute = method
|
345
|
+
end
|
308
346
|
end
|
309
347
|
end
|
310
348
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module ReactiveRecord
|
2
2
|
|
3
3
|
class Collection
|
4
|
-
|
4
|
+
|
5
5
|
def initialize(target_klass, owner = nil, association = nil, *vector)
|
6
6
|
@owner = owner # can be nil if this is an outer most scope
|
7
7
|
@association = association
|
@@ -13,7 +13,7 @@ module ReactiveRecord
|
|
13
13
|
end
|
14
14
|
@scopes = {}
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def dup_for_sync
|
18
18
|
self.dup.instance_eval do
|
19
19
|
@collection = @collection.dup if @collection
|
@@ -52,11 +52,11 @@ module ReactiveRecord
|
|
52
52
|
scope = [scope, *args] if args.count > 0
|
53
53
|
@scopes[scope] ||= Collection.new(@target_klass, @owner, @association, *@vector, [scope])
|
54
54
|
end
|
55
|
-
|
55
|
+
|
56
56
|
def proxy_association
|
57
57
|
@association || self # returning self allows this to work with things like Model.all
|
58
58
|
end
|
59
|
-
|
59
|
+
|
60
60
|
def klass
|
61
61
|
@target_klass
|
62
62
|
end
|
@@ -64,17 +64,24 @@ module ReactiveRecord
|
|
64
64
|
|
65
65
|
def <<(item)
|
66
66
|
backing_record = item.instance_variable_get(:@backing_record)
|
67
|
-
if backing_record and @owner and @association and inverse_of = @association.inverse_of
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
67
|
+
# if backing_record and @owner and @association and inverse_of = @association.inverse_of
|
68
|
+
# item.attributes[inverse_of].attributes[@association.attribute].delete(item) if item.attributes[inverse_of] and item.attributes[inverse_of].attributes[@association.attribute]
|
69
|
+
# item.attributes[inverse_of] = @owner
|
70
|
+
# React::State.set_state(backing_record, inverse_of, @owner) unless backing_record.data_loading?
|
71
|
+
# end
|
72
|
+
#all << item unless all.include? item
|
72
73
|
all << item unless all.include? item
|
74
|
+
if backing_record and @owner and @association and inverse_of = @association.inverse_of and item.attributes[inverse_of] != @owner
|
75
|
+
current_association = item.attributes[inverse_of]
|
76
|
+
backing_record.update_attribute(inverse_of, @owner)
|
77
|
+
current_association.attributes[@association.attribute].delete(item) if current_association and current_association.attributes[@association.attribute]
|
78
|
+
@owner.instance_variable_get(:@backing_record).update_attribute(@association.attribute) # forces a check if association contents have changed from synced values
|
79
|
+
end
|
73
80
|
@collection.delete(@dummy_record)
|
74
81
|
@dummy_record = @dummy_collection = nil
|
75
82
|
self
|
76
83
|
end
|
77
|
-
|
84
|
+
|
78
85
|
[:first, :last].each do |method|
|
79
86
|
define_method method do |*args|
|
80
87
|
if args.count == 0
|
@@ -84,7 +91,7 @@ module ReactiveRecord
|
|
84
91
|
end
|
85
92
|
end
|
86
93
|
end
|
87
|
-
|
94
|
+
|
88
95
|
def replace(new_array)
|
89
96
|
#return new_array if @collection == new_array #not sure we need this anymore
|
90
97
|
@dummy_collection.notify if @dummy_collection
|
@@ -103,33 +110,36 @@ module ReactiveRecord
|
|
103
110
|
|
104
111
|
def delete(item)
|
105
112
|
if @owner and @association and inverse_of = @association.inverse_of
|
106
|
-
item.attributes[inverse_of]
|
107
|
-
|
108
|
-
|
113
|
+
if backing_record = item.instance_variable_get(:@backing_record) and backing_record.attributes[inverse_of] == @owner
|
114
|
+
# the if prevents double update if delete is being called from << (see << above)
|
115
|
+
backing_record.update_attribute(inverse_of, nil)
|
116
|
+
end
|
117
|
+
all.delete(item).tap { @owner.instance_variable_get(:@backing_record).update_attribute(@association.attribute) } # forces a check if association contents have changed from synced values
|
118
|
+
else
|
119
|
+
all.delete(item)
|
109
120
|
end
|
110
|
-
all.delete(item)
|
111
121
|
end
|
112
122
|
|
113
123
|
def method_missing(method, *args, &block)
|
114
124
|
if [].respond_to? method
|
115
125
|
all.send(method, *args, &block)
|
116
|
-
elsif @target_klass.respond_to? method
|
126
|
+
elsif @target_klass.respond_to?(method) or (args.count == 1 && method =~ /^find_by_/)
|
117
127
|
apply_scope(method, *args)
|
118
128
|
else
|
119
129
|
super
|
120
130
|
end
|
121
131
|
end
|
122
|
-
|
132
|
+
|
123
133
|
protected
|
124
|
-
|
134
|
+
|
125
135
|
def dummy_record
|
126
136
|
@dummy_record
|
127
137
|
end
|
128
|
-
|
138
|
+
|
129
139
|
def collection
|
130
140
|
@collection
|
131
141
|
end
|
132
|
-
|
142
|
+
|
133
143
|
def dummy_collection
|
134
144
|
@dummy_collection
|
135
145
|
end
|
@@ -35,7 +35,12 @@ module ReactiveRecord
|
|
35
35
|
|
36
36
|
isomorphic_method(:fetch_from_db) do |f, vector|
|
37
37
|
# vector must end with either "*all", or be a simple attribute
|
38
|
-
|
38
|
+
begin
|
39
|
+
f.send_to_server [vector.shift.name, *vector] if RUBY_ENGINE == 'opal'
|
40
|
+
rescue Exception => e
|
41
|
+
`debugger`
|
42
|
+
nil
|
43
|
+
end
|
39
44
|
f.when_on_server { @server_data_cache[*vector] }
|
40
45
|
end
|
41
46
|
|
@@ -188,7 +193,7 @@ module ReactiveRecord
|
|
188
193
|
|
189
194
|
if RUBY_ENGINE == 'opal'
|
190
195
|
|
191
|
-
def save(&block)
|
196
|
+
def save(validate, &block)
|
192
197
|
|
193
198
|
if data_loading?
|
194
199
|
|
@@ -208,13 +213,11 @@ module ReactiveRecord
|
|
208
213
|
backing_records = {self.object_id => self} # for quick lookup of records that have been or will be processed [record.object_id] => record
|
209
214
|
|
210
215
|
add_new_association = lambda do |record, attribute, assoc_record|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
backing_records[assoc_record.object_id] = assoc_record
|
215
|
-
end
|
216
|
-
associations << {parent_id: record.object_id, attribute: attribute, child_id: assoc_record.object_id}
|
216
|
+
unless backing_records[assoc_record.object_id]
|
217
|
+
records_to_process << assoc_record
|
218
|
+
backing_records[assoc_record.object_id] = assoc_record
|
217
219
|
end
|
220
|
+
associations << {parent_id: record.object_id, attribute: attribute, child_id: assoc_record.object_id}
|
218
221
|
end
|
219
222
|
|
220
223
|
record_index = 0
|
@@ -236,7 +239,7 @@ module ReactiveRecord
|
|
236
239
|
elsif record.changed?(attribute)
|
237
240
|
output_attributes[attribute] = value
|
238
241
|
end
|
239
|
-
end
|
242
|
+
end if record.changed?
|
240
243
|
record_index += 1
|
241
244
|
end
|
242
245
|
|
@@ -244,7 +247,7 @@ module ReactiveRecord
|
|
244
247
|
|
245
248
|
promise = Promise.new
|
246
249
|
|
247
|
-
HTTP.post(`window.ReactiveRecordEnginePath`+"/save", payload: {models: models, associations: associations}).then do |response|
|
250
|
+
HTTP.post(`window.ReactiveRecordEnginePath`+"/save", payload: {models: models, associations: associations, validate: validate}).then do |response|
|
248
251
|
begin
|
249
252
|
response.json[:models] = response.json[:saved_models].collect do |item|
|
250
253
|
backing_records[item[0]].ar_instance
|
@@ -253,17 +256,19 @@ module ReactiveRecord
|
|
253
256
|
if response.json[:success]
|
254
257
|
response.json[:saved_models].each { | item | backing_records[item[0]].sync!(item[2]) }
|
255
258
|
else
|
256
|
-
response.json[:saved_models].each { | item | backing_records[item[0]].saved! item[3] }
|
257
259
|
log("Reactive Record Save Failed: #{response.json[:message]}", :error)
|
258
260
|
response.json[:saved_models].each do | item |
|
259
261
|
log(" Model: #{item[1]}[#{item[0]}] Attributes: #{item[2]} Errors: #{item[3]}", :error) if item[3]
|
260
262
|
end
|
261
263
|
end
|
262
264
|
|
265
|
+
response.json[:saved_models].each { | item | backing_records[item[0]].errors! item[3] }
|
266
|
+
|
263
267
|
yield response.json[:success], response.json[:message], response.json[:models] if block
|
264
268
|
promise.resolve response.json
|
265
269
|
|
266
|
-
backing_records.each { |id, record| record.saved! }
|
270
|
+
backing_records.each { |id, record| record.saved! }
|
271
|
+
|
267
272
|
rescue Exception => e
|
268
273
|
puts "Save Failed: #{e}"
|
269
274
|
end
|
@@ -279,7 +284,7 @@ module ReactiveRecord
|
|
279
284
|
|
280
285
|
else
|
281
286
|
|
282
|
-
def self.save_records(models, associations, acting_user)
|
287
|
+
def self.save_records(models, associations, acting_user, validate)
|
283
288
|
|
284
289
|
reactive_records = {}
|
285
290
|
new_models = []
|
@@ -288,7 +293,7 @@ module ReactiveRecord
|
|
288
293
|
models.each do |model_to_save|
|
289
294
|
attributes = model_to_save[:attributes]
|
290
295
|
model = Object.const_get(model_to_save[:model])
|
291
|
-
id = attributes
|
296
|
+
id = attributes.delete(model.primary_key) # if we are saving existing model primary key value will be present
|
292
297
|
reactive_records[model_to_save[:id]] = if id
|
293
298
|
record = model.find(id)
|
294
299
|
keys = record.attributes.keys
|
@@ -315,27 +320,57 @@ module ReactiveRecord
|
|
315
320
|
end
|
316
321
|
end
|
317
322
|
|
323
|
+
puts "!!!!!!!!!!!!!!attributes updated"
|
324
|
+
|
318
325
|
ActiveRecord::Base.transaction do
|
319
326
|
|
320
327
|
associations.each do |association|
|
321
328
|
parent = reactive_records[association[:parent_id]]
|
322
329
|
parent.instance_variable_set("@reactive_record_#{association[:attribute]}_changed", true)
|
323
330
|
if parent.class.reflect_on_aggregation(association[:attribute].to_sym)
|
324
|
-
parent.send(
|
331
|
+
puts ">>>>>>AGGREGATE>>>> #{parent.class.name}.send('#{association[:attribute]}=', #{reactive_records[association[:child_id]]})"
|
332
|
+
aggregate = reactive_records[association[:child_id]]
|
333
|
+
current_attributes = parent.send(association[:attribute]).attributes
|
334
|
+
puts "current parent attributes = #{current_attributes}"
|
335
|
+
new_attributes = aggregate.attributes
|
336
|
+
puts "current child attributes = #{new_attributes}"
|
337
|
+
merged_attributes = current_attributes.merge(new_attributes) { |k, n, o| n or o }
|
338
|
+
puts "merged attributes = #{merged_attributes}"
|
339
|
+
aggregate.assign_attributes(merged_attributes)
|
340
|
+
puts "aggregate attributes after merge = #{aggregate.attributes}"
|
341
|
+
parent.send("#{association[:attribute]}=", aggregate)
|
342
|
+
puts "updated is frozen? #{aggregate.frozen?}, parent attributes = #{parent.send(association[:attribute]).attributes}"
|
325
343
|
elsif parent.class.reflect_on_association(association[:attribute].to_sym).collection?
|
326
|
-
parent.send(
|
344
|
+
puts ">>>>>>>>>> #{parent.class.name}.send('#{association[:attribute]}') << #{reactive_records[association[:child_id]]})"
|
345
|
+
#parent.send("#{association[:attribute]}") << reactive_records[association[:child_id]]
|
346
|
+
puts "Skipped (should be done by belongs to)"
|
327
347
|
else
|
348
|
+
puts ">>>>ASSOCIATION>>>> #{parent.class.name}.send('#{association[:attribute]}=', #{reactive_records[association[:child_id]]})"
|
328
349
|
parent.send("#{association[:attribute]}=", reactive_records[association[:child_id]])
|
350
|
+
puts "updated"
|
329
351
|
end
|
330
352
|
end if associations
|
331
353
|
|
354
|
+
puts "!!!!!!!!!!!!associations updated"
|
355
|
+
|
332
356
|
has_errors = false
|
333
357
|
|
334
358
|
saved_models = reactive_records.collect do |reactive_record_id, model|
|
335
|
-
|
336
|
-
|
359
|
+
puts "saving rr_id: #{reactive_record_id} model.object_id: #{model.object_id} frozen? <#{model.frozen?}>"
|
360
|
+
if model.frozen?
|
361
|
+
puts "validating frozen model #{model.class.name} #{model} (reactive_record_id = #{reactive_record_id})"
|
362
|
+
valid = model.valid?
|
363
|
+
puts "has_errors before = #{has_errors}, validate= #{validate}, !valid= #{!valid} (validate and !valid) #{validate and !valid}"
|
364
|
+
has_errors ||= (validate and !valid)
|
365
|
+
puts "validation complete errors = <#{!valid}>, #{model.errors.messages} has_errors #{has_errors}"
|
366
|
+
[reactive_record_id, model.class.name, model.attributes, (valid ? nil : model.errors.messages)]
|
367
|
+
elsif !model.id or model.changed?
|
368
|
+
puts "saving #{model.class.name} #{model} (reactive_record_id = #{reactive_record_id})"
|
369
|
+
saved = model.check_permission_with_acting_user(acting_user, new_models.include?(model) ? :create_permitted? : :update_permitted?).save(validate: validate)
|
337
370
|
has_errors ||= !saved
|
338
|
-
|
371
|
+
messages = model.errors.messages if (validate and !saved) or (!validate and !model.valid?)
|
372
|
+
puts "saved complete errors = <#{!saved}>, #{messages} has_errors #{has_errors}"
|
373
|
+
[reactive_record_id, model.class.name, model.attributes, messages]
|
339
374
|
end
|
340
375
|
end.compact
|
341
376
|
|
@@ -346,6 +381,7 @@ module ReactiveRecord
|
|
346
381
|
{success: true, saved_models: saved_models }
|
347
382
|
|
348
383
|
rescue Exception => e
|
384
|
+
puts "exception #{e}"
|
349
385
|
|
350
386
|
{success: false, saved_models: saved_models, message: e.message}
|
351
387
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reactive-record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.16
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mitch VanDuyn
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-09-
|
11
|
+
date: 2015-09-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|