reactive-record 0.7.20 → 0.7.22

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6c2c4283d8c482a6dc3b9ca9f0c9980ce4a9bad8
4
- data.tar.gz: 55dbf1be5bc98e3bfa115f4e13e10591e3febf78
3
+ metadata.gz: 8ac17cb9431cfb70da2b36ad364d6f6daddabfad
4
+ data.tar.gz: 1ce72b88c991bcd5bd7b2b643a64c10b0da1173b
5
5
  SHA512:
6
- metadata.gz: 9d827bab1ca8da5f8321808357a97e32b33151de2a04e2c7e89826b84b8e54ebd6b543edcbc76675b5ed3e501a447082a59a6a3e336b4e9bee98c7eb0f043de3
7
- data.tar.gz: 2f8e98cabf83f5bdf67c4e27e51aa0ead8917de7e25b0bc53d91a5f22bd473c8a600cb018c767d50f5d517cc54f287d8eb6d4c3e398fc24c374211683e826716
6
+ metadata.gz: 4f030a78f2bfb6be443e26f00a3782f33730abbfea6fcdf9897fde842436e14f113cd7c2d152ed2280536ef245e67ec91629c97080f1fbb4e6a882b8a00baabc
7
+ data.tar.gz: 8ced444a4e20aeabba7536f3acddfee6aad92c195f9ffbee484955e53ba0714b08b8f6b53c35d71f28694cc32a65d49d6076581b283fc9fd2fcd68f385d754f6
@@ -1,5 +1,5 @@
1
1
  if RUBY_ENGINE == 'opal'
2
-
2
+
3
3
  require "reactive-ruby"
4
4
  require "reactive_record/active_record/error"
5
5
  require "reactive_record/server_data_cache"
@@ -13,30 +13,30 @@ if RUBY_ENGINE == 'opal'
13
13
  require "reactive_record/active_record/instance_methods"
14
14
  require "reactive_record/active_record/base"
15
15
  require "reactive_record/interval"
16
-
16
+
17
17
  else
18
-
18
+
19
19
  module ::ActiveRecord
20
20
  module Core
21
21
  module ClassMethods
22
- def inherited(child_class)
22
+ def inherited(child_class)
23
23
  begin
24
24
  file = Rails.root.join('app','models',"#{child_class.name.underscore}.rb").to_s rescue nil
25
- begin
26
- require file
25
+ begin
26
+ require file
27
27
  rescue LoadError
28
28
  end
29
29
  # from active record:
30
30
  child_class.initialize_find_by_cache
31
- rescue
31
+ rescue
32
32
  end
33
33
  super
34
34
  end
35
35
  end
36
36
  end
37
37
  end
38
-
39
-
38
+
39
+
40
40
  require "opal"
41
41
  require "reactive_record/version"
42
42
  require "reactive_record/permissions"
@@ -47,5 +47,5 @@ else
47
47
 
48
48
  Opal.append_path File.expand_path('../', __FILE__).untaint
49
49
  Opal.append_path File.expand_path('../../vendor', __FILE__).untaint
50
-
50
+
51
51
  end
@@ -63,7 +63,7 @@ module ActiveRecord
63
63
 
64
64
  def scope(name, body)
65
65
  singleton_class.send(:define_method, name) do | *args |
66
- args = args.count == 0 ? name : [name, *args]
66
+ args = (args.count == 0) ? name : [name, *args]
67
67
  ReactiveRecord::Base.class_scopes(self)[name] ||= ReactiveRecord::Collection.new(self, nil, nil, self, args)
68
68
  end
69
69
  singleton_class.send(:define_method, "#{name}=") do |collection|
@@ -89,6 +89,10 @@ module ActiveRecord
89
89
  @backing_record.destroy &block
90
90
  end
91
91
 
92
+ def destroyed?
93
+ @backing_record.destroyed
94
+ end
95
+
92
96
  def new?
93
97
  @backing_record.new?
94
98
  end
@@ -32,6 +32,7 @@ module ReactiveRecord
32
32
  attr_accessor :changed_attributes
33
33
  attr_accessor :aggregate_owner
34
34
  attr_accessor :aggregate_attribute
35
+ attr_accessor :destroyed
35
36
 
36
37
  # While data is being loaded from the server certain internal behaviors need to change
37
38
  # for example records all record changes are synced as they happen.
@@ -156,7 +157,11 @@ module ReactiveRecord
156
157
 
157
158
  def reactive_get!(attribute)
158
159
  unless @destroyed
159
- apply_method(attribute) unless @attributes.has_key? attribute
160
+ if @attributes.has_key? attribute
161
+ attributes[attribute].notify if @attributes[attribute].is_a? DummyValue
162
+ else
163
+ apply_method(attribute)
164
+ end
160
165
  React::State.get_state(self, attribute) unless data_loading?
161
166
  attributes[attribute]
162
167
  end
@@ -261,7 +266,7 @@ module ReactiveRecord
261
266
  @synced_attributes[key] = value.dup_for_sync
262
267
  elsif aggregation = model.reflect_on_aggregation(key)
263
268
  value.backing_record.sync!
264
- else
269
+ elsif !model.reflect_on_association(key)
265
270
  @synced_attributes[key] = JSON.parse(value.to_json)
266
271
  end
267
272
  end
@@ -36,14 +36,24 @@ module ReactiveRecord
36
36
  end
37
37
  else
38
38
  @dummy_collection = ReactiveRecord::Base.load_from_db(*@vector, "*all")
39
- @dummy_record = ReactiveRecord::Base.new_from_vector(@target_klass, nil, *@vector, "*")
40
- @dummy_record.backing_record.attributes[@association.inverse_of] = @owner if @association and @association.inverse_of
41
- @collection << @dummy_record
39
+ @dummy_record = self[0]
42
40
  end
43
41
  end
44
42
  @collection
45
43
  end
46
44
 
45
+ def [](index)
46
+
47
+ if (@collection || all).length <= index and @dummy_collection
48
+ (@collection.length..index).each do |i|
49
+ new_dummy_record = ReactiveRecord::Base.new_from_vector(@target_klass, nil, *@vector, "*#{i}")
50
+ new_dummy_record.backing_record.attributes[@association.inverse_of] = @owner if @association and @association.inverse_of
51
+ @collection << new_dummy_record
52
+ end
53
+ end
54
+ @collection[index]
55
+ end
56
+
47
57
  def ==(other_collection)
48
58
  my_collection = (@collection || []).select { |target| target != @dummy_record }
49
59
  other_collection = (other_collection ? (other_collection.collection || []) : []).select { |target| target != other_collection.dummy_record }
@@ -57,6 +67,19 @@ module ReactiveRecord
57
67
  @scopes[scope] ||= Collection.new(@target_klass, @owner, @association, *@vector, [scope])
58
68
  end
59
69
 
70
+ def count
71
+ if @collection
72
+ @collection.count
73
+ elsif @count ||= ReactiveRecord::Base.fetch_from_db([*@vector, "*count"])
74
+ @count
75
+ else
76
+ ReactiveRecord::Base.load_from_db(*@vector, "*count")
77
+ @count = 1
78
+ end
79
+ end
80
+
81
+ alias_method :length, :count
82
+
60
83
  def proxy_association
61
84
  @association || self # returning self allows this to work with things like Model.all
62
85
  end
@@ -65,24 +88,21 @@ module ReactiveRecord
65
88
  @target_klass
66
89
  end
67
90
 
68
-
69
91
  def <<(item)
70
92
  backing_record = item.backing_record
71
- # if backing_record and @owner and @association and inverse_of = @association.inverse_of
72
- # item.attributes[inverse_of].attributes[@association.attribute].delete(item) if item.attributes[inverse_of] and item.attributes[inverse_of].attributes[@association.attribute]
73
- # item.attributes[inverse_of] = @owner
74
- # React::State.set_state(backing_record, inverse_of, @owner) unless backing_record.data_loading?
75
- # end
76
- #all << item unless all.include? item
77
- all << item unless all.include? item
93
+ all << item unless all.include? item # does this use == if so we are okay...
78
94
  if backing_record and @owner and @association and inverse_of = @association.inverse_of and item.attributes[inverse_of] != @owner
79
95
  current_association = item.attributes[inverse_of]
80
96
  backing_record.update_attribute(inverse_of, @owner)
81
97
  current_association.attributes[@association.attribute].delete(item) if current_association and current_association.attributes[@association.attribute]
82
98
  @owner.backing_record.update_attribute(@association.attribute) # forces a check if association contents have changed from synced values
83
99
  end
84
- @collection.delete(@dummy_record)
85
- @dummy_record = @dummy_collection = nil
100
+ if item.id and @dummy_record
101
+ @dummy_record.id = item.id
102
+ @collection.delete(@dummy_record)
103
+ @dummy_record = @collection.detect { |r| r.backing_record.vector.last =~ /^\*[0-9]+$/ }
104
+ @dummy_collection = nil
105
+ end
86
106
  self
87
107
  end
88
108
 
@@ -97,9 +117,20 @@ module ReactiveRecord
97
117
  end
98
118
 
99
119
  def replace(new_array)
100
- #return new_array if @collection == new_array #not sure we need this anymore
101
- @dummy_collection.notify if @dummy_collection
102
- @collection.dup.each { |item| delete(item) } if @collection
120
+
121
+ # not tested if you do all[n] where n > 0... this will create additional dummy items, that this will not sync up.
122
+ # probably just moving things around so the @dummy_collection and @dummy_record are updated AFTER the new items are pushed
123
+ # should work.
124
+
125
+ if @dummy_collection
126
+ @dummy_collection.notify
127
+ array = new_array.is_a?(Collection) ? new_array.collection : new_array
128
+ @collection.each_with_index do |r, i|
129
+ r.id = new_array[i].id if array[i] and array[i].id and r.backing_record.vector.last =~ /^\*[0-9]+$/
130
+ end
131
+ end
132
+
133
+ @collection.dup.each { |item| delete(item) } if @collection # this line is a big nop I think
103
134
  @collection = []
104
135
  if new_array.is_a? Collection
105
136
  @dummy_collection = new_array.dummy_collection
@@ -124,6 +155,11 @@ module ReactiveRecord
124
155
  end
125
156
  end
126
157
 
158
+ def loading?
159
+ all # need to force initialization at this point
160
+ @dummy_collection.loading?
161
+ end
162
+
127
163
  def method_missing(method, *args, &block)
128
164
  if [].respond_to? method
129
165
  all.send(method, *args, &block)
@@ -86,70 +86,95 @@ module ReactiveRecord
86
86
  DummyValue.new
87
87
  end
88
88
 
89
- class DummyValue < NilClass
89
+ if RUBY_ENGINE == 'opal'
90
+ class ::Object
90
91
 
91
- def notify
92
- unless ReactiveRecord::Base.data_loading?
93
- ReactiveRecord.loads_pending! #loads
94
- ReactiveRecord::WhileLoading.loading! #loads
92
+ def loaded?
93
+ !loading?
94
+ end
95
+
96
+ def loading?
97
+ false
98
+ end
99
+
100
+ def present?
101
+ !!self
95
102
  end
96
- end
97
103
 
98
- def initialize()
99
- notify
100
104
  end
101
105
 
102
- def method_missing(method, *args, &block)
103
- if 0.respond_to? method
104
- notify
105
- 0.send(method, *args, &block)
106
- elsif "".respond_to? method
106
+ class DummyValue < NilClass
107
+
108
+ def notify
109
+ unless ReactiveRecord::Base.data_loading?
110
+ ReactiveRecord.loads_pending! #loads
111
+ ReactiveRecord::WhileLoading.loading! #loads
112
+ end
113
+ end
114
+
115
+ def initialize()
107
116
  notify
108
- "".send(method, *args, &block)
109
- else
110
- super
111
117
  end
112
- end
113
118
 
114
- def coerce(s)
115
- [self.send("to_#{s.class.name.downcase}"), s]
116
- end
119
+ def method_missing(method, *args, &block)
120
+ if 0.respond_to? method
121
+ notify
122
+ 0.send(method, *args, &block)
123
+ elsif "".respond_to? method
124
+ notify
125
+ "".send(method, *args, &block)
126
+ else
127
+ super
128
+ end
129
+ end
117
130
 
118
- def ==(other_value)
119
- other_value.is_a? DummyValue
120
- end
131
+ def loading?
132
+ true
133
+ end
121
134
 
122
- def to_s
123
- notify
124
- ""
125
- end
135
+ def present?
136
+ false
137
+ end
126
138
 
127
- def to_f
128
- notify
129
- 0.0
130
- end
139
+ def coerce(s)
140
+ [self.send("to_#{s.class.name.downcase}"), s]
141
+ end
131
142
 
132
- def to_i
133
- notify
134
- 0
135
- end
143
+ def ==(other_value)
144
+ other_value.object_id == self.object_id
145
+ end
136
146
 
137
- def to_numeric
138
- notify
139
- 0
140
- end
147
+ def to_s
148
+ notify
149
+ ""
150
+ end
141
151
 
142
- def to_date
143
- notify
144
- "2001-01-01T00:00:00.000-00:00".to_date
145
- end
152
+ def to_f
153
+ notify
154
+ 0.0
155
+ end
146
156
 
147
- def acts_as_string?
148
- true
149
- end
157
+ def to_i
158
+ notify
159
+ 0
160
+ end
150
161
 
151
- end
162
+ def to_numeric
163
+ notify
164
+ 0
165
+ end
152
166
 
167
+ def to_date
168
+ notify
169
+ "2001-01-01T00:00:00.000-00:00".to_date
170
+ end
171
+
172
+ def acts_as_string?
173
+ true
174
+ end
175
+
176
+ end
177
+ end
153
178
 
154
179
  def self.schedule_fetch
155
180
  #ReactiveRecord.loads_pending!
@@ -166,7 +191,7 @@ module ReactiveRecord
166
191
  begin
167
192
  ReactiveRecord::Base.load_from_json(response.json)
168
193
  rescue Exception => e
169
- log("Exception raised while loading json from server: #{e}", :error)
194
+ log("Unexpected exception raised while loading json from server: #{e}", :error)
170
195
  end
171
196
  log(" Processed in: #{(Time.now-fetch_time).to_i}s")
172
197
  log([" Returned: %o", response.json.to_n])
@@ -339,6 +364,8 @@ module ReactiveRecord
339
364
  puts "aggregate attributes after merge = #{aggregate.attributes}"
340
365
  parent.send("#{association[:attribute]}=", aggregate)
341
366
  puts "updated is frozen? #{aggregate.frozen?}, parent attributes = #{parent.send(association[:attribute]).attributes}"
367
+ elsif parent.class.reflect_on_association(association[:attribute].to_sym).nil?
368
+ raise "Missing association :#{association[:attribute]} for #{parent.class.name}. Was association defined on opal side only?"
342
369
  elsif parent.class.reflect_on_association(association[:attribute].to_sym).collection?
343
370
  puts ">>>>>>>>>> #{parent.class.name}.send('#{association[:attribute]}') << #{reactive_records[association[:child_id]]})"
344
371
  #parent.send("#{association[:attribute]}") << reactive_records[association[:child_id]]
@@ -417,8 +444,13 @@ module ReactiveRecord
417
444
  promise.resolve({success: true})
418
445
  end
419
446
 
420
- @attributes = {}
421
- sync!
447
+ # DO NOT CLEAR ATTRIBUTES. Records that are not found, are destroyed, and if they are searched for again, we want to make
448
+ # sure to find them. We may want to change this, and provide a separate flag called not_found. In this case you
449
+ # would put these lines here:
450
+ # @attributes = {}
451
+ # sync!
452
+ # and modify server_data_cache so that it does NOT call destroy
453
+
422
454
  @destroyed = true
423
455
 
424
456
  promise
@@ -188,7 +188,11 @@ module ReactiveRecord
188
188
  end
189
189
 
190
190
  def apply_method(method)
191
- method[0] = "find" if method.is_a? Array and method.first == "find_by_id"
191
+ if method.is_a? Array and method.first == "find_by_id"
192
+ method[0] = "find"
193
+ elsif method.is_a? String and method =~ /^\*[0-9]+$/
194
+ method = "*"
195
+ end
192
196
  new_vector = vector + [method]
193
197
  @db_cache.detect { |cached_item| cached_item.vector == new_vector} || build_new_instances(method)
194
198
  end
@@ -196,6 +200,8 @@ module ReactiveRecord
196
200
  def build_new_instances(method)
197
201
  if method == "*all"
198
202
  apply_method_to_cache("*all") { |cache_item| cache_item.value.collect { |record| record.id } }
203
+ elsif method == "*count"
204
+ apply_method_to_cache("*count") { |cache_item| cache_item.value.count }
199
205
  elsif method == "*"
200
206
  if @ar_object and @ar_object.length > 0
201
207
  @ar_object.inject(nil) do | value, record | # just using inject so we will return the last value
@@ -251,36 +257,38 @@ module ReactiveRecord
251
257
  ignore_all = nil
252
258
 
253
259
  # have to process *all before any other items
254
- if sorted_collection = tree.delete("*all")
260
+ # we leave the "*all" key in just for debugging purposes, and then skip it below
261
+
262
+ if sorted_collection = tree["*all"]
255
263
  target.replace sorted_collection.collect { |id| target.proxy_association.klass.find(id) }
256
264
  end
257
265
 
258
266
  tree.each do |method, value|
259
267
  method = JSON.parse(method) rescue method
260
268
  new_target = nil
261
- if !target
269
+ if method == "*all"
270
+ next # its already been processed above
271
+ elsif !target
262
272
  load_from_json(value, Object.const_get(method))
263
- elsif method == "*all"
264
- target.replace value.collect { |id| target.proxy_association.klass.find(id) } unless ignore_all
273
+ elsif method == "*count"
274
+ target.instance_variable_set(:@count, value.first)
265
275
  elsif method.is_a? Integer or method =~ /^[0-9]+$/
266
- ignore_all = true
267
276
  target << (new_target = target.proxy_association.klass.find(method))
268
277
  elsif method.is_a? Array
269
278
  if !(target.class < ActiveRecord::Base)
270
- new_target = target.send *method unless value.is_a? Array # value is an array if scope returns nil
279
+ new_target = target.send *method
280
+ # value is an array if scope returns nil, so we destroy the bogus record
281
+ new_target.destroy and new_target = nil if value.is_a? Array
271
282
  else
272
283
  target.attributes[[method]] = value.first
273
284
  end
274
285
  elsif value.is_a? Array
275
- #target.send "#{method}=", value.first
276
- target.backing_record.reactive_set!(method, value.first)
286
+ target.send "#{method}=", value.first
277
287
  elsif value.is_a? Hash and value[:id] and value[:id].first #and
278
288
  association = target.class.reflect_on_association(method)
279
289
  new_target = association.klass.find(value[:id].first)
280
- #target.send "#{method}=", new_target
281
- target.backing_record.reactive_set!(method, new_target)
290
+ target.send "#{method}=", new_target
282
291
  else
283
- # target might be able to respond to method directly so we can't optimize out the target send
284
292
  new_target = target.send("#{method}=", target.send(method))
285
293
  end
286
294
  load_from_json(value, new_target) if new_target
@@ -1,3 +1,3 @@
1
1
  module ReactiveRecord
2
- VERSION = "0.7.20"
2
+ VERSION = "0.7.22"
3
3
  end
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.20
4
+ version: 0.7.22
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-30 00:00:00.000000000 Z
11
+ date: 2015-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails