volt 0.7.23 → 0.8.0

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.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +8 -1
  3. data/CHANGELOG.md +22 -0
  4. data/Gemfile +8 -0
  5. data/Guardfile +2 -2
  6. data/Readme.md +139 -136
  7. data/VERSION +1 -1
  8. data/app/volt/assets/js/setImmediate.js +175 -0
  9. data/app/volt/tasks/live_query/data_store.rb +0 -2
  10. data/app/volt/tasks/live_query/live_query.rb +4 -4
  11. data/docs/GETTING_STARTED.md +24 -3
  12. data/docs/WHY.md +1 -22
  13. data/lib/volt.rb +20 -1
  14. data/lib/volt/console.rb +20 -0
  15. data/lib/volt/controllers/model_controller.rb +25 -11
  16. data/lib/volt/extra_core/object.rb +2 -14
  17. data/lib/volt/extra_core/string.rb +4 -0
  18. data/lib/volt/models.rb +0 -1
  19. data/lib/volt/models/array_model.rb +8 -16
  20. data/lib/volt/models/cursor.rb +1 -1
  21. data/lib/volt/models/model.rb +40 -60
  22. data/lib/volt/models/model_hash_behaviour.rb +10 -24
  23. data/lib/volt/models/model_helpers.rb +2 -2
  24. data/lib/volt/models/model_state.rb +1 -1
  25. data/lib/volt/models/model_wrapper.rb +4 -4
  26. data/lib/volt/models/persistors/array_store.rb +44 -28
  27. data/lib/volt/models/persistors/base.rb +1 -1
  28. data/lib/volt/models/persistors/model_store.rb +1 -1
  29. data/lib/volt/models/persistors/params.rb +5 -1
  30. data/lib/volt/models/persistors/query/query_listener.rb +2 -0
  31. data/lib/volt/models/persistors/store.rb +3 -2
  32. data/lib/volt/models/persistors/store_state.rb +7 -2
  33. data/lib/volt/models/url.rb +35 -29
  34. data/lib/volt/models/validations.rb +7 -17
  35. data/lib/volt/page/bindings/attribute_binding.rb +57 -39
  36. data/lib/volt/page/bindings/base_binding.rb +0 -14
  37. data/lib/volt/page/bindings/content_binding.rb +15 -18
  38. data/lib/volt/page/bindings/each_binding.rb +67 -34
  39. data/lib/volt/page/bindings/if_binding.rb +15 -12
  40. data/lib/volt/page/bindings/template_binding.rb +77 -59
  41. data/lib/volt/page/bindings/template_binding/grouped_controllers.rb +19 -4
  42. data/lib/volt/page/channel.rb +22 -38
  43. data/lib/volt/page/channel_stub.rb +3 -6
  44. data/lib/volt/page/page.rb +24 -26
  45. data/lib/volt/page/string_template_renderer.rb +46 -0
  46. data/lib/volt/page/sub_context.rb +7 -1
  47. data/lib/volt/page/targets/binding_document/component_node.rb +11 -9
  48. data/lib/volt/page/tasks.rb +3 -2
  49. data/lib/volt/page/url_tracker.rb +4 -3
  50. data/lib/volt/reactive/computation.rb +131 -0
  51. data/lib/volt/reactive/dependency.rb +71 -0
  52. data/lib/volt/reactive/eventable.rb +82 -0
  53. data/lib/volt/reactive/hash_dependency.rb +36 -0
  54. data/lib/volt/{controllers → reactive}/reactive_accessors.rb +8 -11
  55. data/lib/volt/reactive/reactive_array.rb +100 -193
  56. data/lib/volt/reactive/reactive_hash.rb +49 -0
  57. data/lib/volt/server/html_parser/attribute_scope.rb +24 -4
  58. data/lib/volt/server/html_parser/if_view_scope.rb +15 -15
  59. data/lib/volt/server/html_parser/view_scope.rb +31 -1
  60. data/spec/apps/kitchen_sink/Gemfile +4 -8
  61. data/spec/apps/kitchen_sink/app/main/config/dependencies.rb +8 -0
  62. data/spec/apps/kitchen_sink/app/main/config/routes.rb +8 -1
  63. data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +8 -0
  64. data/spec/apps/kitchen_sink/app/main/views/main/bindings.html +73 -0
  65. data/spec/apps/kitchen_sink/app/main/views/main/index.html +6 -1
  66. data/spec/apps/kitchen_sink/app/main/views/main/main.html +26 -6
  67. data/spec/apps/kitchen_sink/app/main/views/main/store.html +6 -0
  68. data/spec/controllers/reactive_accessors_spec.rb +13 -15
  69. data/spec/integration/bindings_spec.rb +159 -0
  70. data/spec/integration/templates_spec.rb +15 -0
  71. data/spec/models/model_spec.rb +130 -228
  72. data/spec/reactive/computation_spec.rb +63 -0
  73. data/spec/reactive/dependency_spec.rb +5 -0
  74. data/spec/reactive/eventable_spec.rb +48 -0
  75. data/spec/reactive/reactive_array_spec.rb +97 -0
  76. data/spec/router/routes_spec.rb +26 -27
  77. data/spec/server/html_parser/view_parser_spec.rb +3 -21
  78. data/spec/server/rack/asset_files_spec.rb +1 -1
  79. data/templates/project/app/main/views/main/main.html +2 -2
  80. metadata +29 -41
  81. data/lib/volt/extra_core/time.rb +0 -16
  82. data/lib/volt/page/draw_cycle.rb +0 -31
  83. data/lib/volt/page/memory_test.rb +0 -26
  84. data/lib/volt/page/reactive_template.rb +0 -32
  85. data/lib/volt/reactive/array_extensions.rb +0 -12
  86. data/lib/volt/reactive/destructive_methods.rb +0 -19
  87. data/lib/volt/reactive/event_chain.rb +0 -125
  88. data/lib/volt/reactive/events.rb +0 -216
  89. data/lib/volt/reactive/object_tracking.rb +0 -14
  90. data/lib/volt/reactive/reactive_block.rb +0 -88
  91. data/lib/volt/reactive/reactive_generator.rb +0 -44
  92. data/lib/volt/reactive/reactive_tags.rb +0 -71
  93. data/lib/volt/reactive/reactive_value.rb +0 -427
  94. data/lib/volt/reactive/string_extensions.rb +0 -31
  95. data/spec/integration/test_integration_spec.rb +0 -14
  96. data/spec/models/event_chain_spec.rb +0 -150
  97. data/spec/models/model_buffers_spec.rb +0 -9
  98. data/spec/models/old_model_spec.rb +0 -67
  99. data/spec/models/reactive_array_spec.rb +0 -364
  100. data/spec/models/reactive_block_spec.rb +0 -13
  101. data/spec/models/reactive_call_times_spec.rb +0 -28
  102. data/spec/models/reactive_generator_spec.rb +0 -58
  103. data/spec/models/reactive_tags_spec.rb +0 -35
  104. data/spec/models/reactive_value_spec.rb +0 -370
  105. data/spec/models/store_spec.rb +0 -16
  106. data/spec/models/string_extensions_spec.rb +0 -57
@@ -23,6 +23,10 @@ class String
23
23
  Inflector.singularize(self)
24
24
  end
25
25
 
26
+ def titleize
27
+ self.gsub('_', ' ').split(' ').map {|w| w.capitalize }.join(' ')
28
+ end
29
+
26
30
  def plural?
27
31
  # TODO: Temp implementation
28
32
  self.pluralize == self
@@ -1,5 +1,4 @@
1
1
  require 'volt/extra_core/extra_core'
2
- require 'volt/reactive/reactive_value'
3
2
  require 'volt/models/model'
4
3
  require 'volt/models/cursor'
5
4
  require 'volt/models/persistors/store_factory'
@@ -1,3 +1,4 @@
1
+ require 'volt/reactive/reactive_array'
1
2
  require 'volt/models/model_wrapper'
2
3
  require 'volt/models/model_helpers'
3
4
  require 'volt/models/model_state'
@@ -36,21 +37,14 @@ class ArrayModel < ReactiveArray
36
37
  @persistor.loaded if @persistor
37
38
  end
38
39
 
39
- tag_method(:find) do
40
- destructive!
41
- pass_reactive!
42
- end
43
- def find(*args)
40
+ def find(*args, &block)
44
41
  if @persistor
45
- return @persistor.find(*args)
42
+ return @persistor.find(*args, &block)
46
43
  else
47
44
  raise "this model's persistance layer does not support find, try using store"
48
45
  end
49
46
  end
50
47
 
51
- tag_method(:then) do
52
- destructive!
53
- end
54
48
  def then(*args, &block)
55
49
  if @persistor
56
50
  return @persistor.then(*args, &block)
@@ -65,9 +59,10 @@ class ArrayModel < ReactiveArray
65
59
 
66
60
  # Make sure it gets wrapped
67
61
  def <<(model)
68
- if model.cur.is_a?(Model)
62
+ # TODORW: handle changes
63
+ if model.is_a?(Model)
69
64
  # Set the new path
70
- model.cur.options = @options.merge(path: @options[:path] + [:[]])
65
+ model.options = @options.merge(path: @options[:path] + [:[]])
71
66
  else
72
67
  model = wrap_values([model]).first
73
68
  end
@@ -117,12 +112,9 @@ class ArrayModel < ReactiveArray
117
112
  end
118
113
 
119
114
  # Otherwise inspect normally
120
- super
115
+ return super
121
116
  end
122
117
 
123
- tag_method(:buffer) do
124
- destructive!
125
- end
126
118
  def buffer
127
119
  model_path = options[:path] + [:[]]
128
120
  model_klass = class_at_path(model_path)
@@ -130,7 +122,7 @@ class ArrayModel < ReactiveArray
130
122
  new_options = options.merge(path: model_path, save_to: self).reject {|k,_| k.to_sym == :persistor }
131
123
  model = model_klass.new({}, new_options)
132
124
 
133
- return ReactiveValue.new(model)
125
+ return model
134
126
  end
135
127
 
136
128
  private
@@ -1,6 +1,6 @@
1
1
  require 'volt/models/array_model'
2
2
 
3
3
  class Cursor < ArrayModel
4
-
4
+
5
5
 
6
6
  end
@@ -1,42 +1,34 @@
1
1
  require 'volt/models/model_wrapper'
2
2
  require 'volt/models/array_model'
3
3
  require 'volt/models/model_helpers'
4
- require 'volt/reactive/object_tracking'
5
4
  require 'volt/models/model_hash_behaviour'
6
5
  require 'volt/models/validations'
7
6
  require 'volt/models/model_state'
8
-
7
+ require 'volt/reactive/reactive_hash'
9
8
 
10
9
  class NilMethodCall < NoMethodError
11
- def true?
12
- false
13
- end
14
-
15
- def false?
16
- true
17
- end
18
10
  end
19
11
 
12
+
20
13
  class Model
21
- include ReactiveTags
22
14
  include ModelWrapper
23
- include ObjectTracking
24
15
  include ModelHelpers
25
16
  include ModelHashBehaviour
26
17
  include Validations
27
18
  include ModelState
28
19
 
29
- attr_accessor :attributes
20
+ attr_reader :attributes
30
21
  attr_reader :parent, :path, :persistor, :options
31
22
 
32
23
  def initialize(attributes={}, options={}, initial_state=nil)
24
+ @deps = HashDependency.new
33
25
  self.options = options
34
26
 
35
27
  self.send(:attributes=, attributes, true)
36
28
 
37
29
  @cache = {}
38
30
 
39
- # Models stat in a loaded state since they are normally setup from an
31
+ # Models start in a loaded state since they are normally setup from an
40
32
  # ArrayModel, which will have the data when they get added.
41
33
  @state = :loaded
42
34
 
@@ -56,8 +48,11 @@ class Model
56
48
  def attributes=(attrs, initial_setup=false)
57
49
  @attributes = wrap_values(attrs)
58
50
 
51
+ # Trigger and change all
52
+ @deps.changed_all!
53
+ @deps = HashDependency.new
54
+
59
55
  unless initial_setup
60
- trigger!('changed')
61
56
 
62
57
  # Let the persistor know something changed
63
58
  if @persistor
@@ -86,11 +81,6 @@ class Model
86
81
  end
87
82
 
88
83
 
89
- tag_all_methods do
90
- pass_reactive! do |method_name|
91
- method_name[0] == '_' && method_name[-1] == '='
92
- end
93
- end
94
84
  def method_missing(method_name, *args, &block)
95
85
  if method_name[0] == '_'
96
86
  if method_name[-1] == '='
@@ -116,10 +106,9 @@ class Model
116
106
  attribute_name = method_name[0..-2].to_sym
117
107
 
118
108
  value = args[0]
119
- __assign_element(attribute_name, value)
120
109
 
121
110
  attributes[attribute_name] = wrap_value(value, [attribute_name])
122
- trigger_by_attribute!('changed', attribute_name)
111
+ @deps.changed!(attribute_name)
123
112
 
124
113
  # Let the persistor know something changed
125
114
  @persistor.changed(attribute_name) if @persistor
@@ -144,6 +133,9 @@ class Model
144
133
  # Also check @cache
145
134
  value ||= (@cache && @cache[method_name])
146
135
 
136
+ # Track dependency
137
+ @deps.depend(method_name)
138
+
147
139
  if value
148
140
  # key was in attributes or cache
149
141
  return value
@@ -162,9 +154,14 @@ class Model
162
154
  # Get a new model, make it easy to override
163
155
  def read_new_model(method_name)
164
156
  if @persistor && @persistor.respond_to?(:read_new_model)
165
- @persistor.read_new_model(method_name)
157
+ return @persistor.read_new_model(method_name)
166
158
  else
167
- return new_model(nil, @options.merge(parent: self, path: path + [method_name]))
159
+ opts = @options.merge(parent: self, path: path + [method_name])
160
+ if method_name.plural?
161
+ return new_array_model([], opts)
162
+ else
163
+ return new_model(nil, opts)
164
+ end
168
165
  end
169
166
  end
170
167
 
@@ -225,9 +222,6 @@ class Model
225
222
  end
226
223
  end
227
224
 
228
- tag_method(:<<) do
229
- pass_reactive!
230
- end
231
225
  # Initialize an empty array and append to it
232
226
  def <<(value)
233
227
  if @parent
@@ -249,14 +243,20 @@ class Model
249
243
  # Add the new item
250
244
  result << value
251
245
 
252
- trigger!('added', nil, 0)
253
- trigger!('changed')
246
+ # TODORW:
247
+ # trigger!('added', nil, 0)
248
+ # trigger!('changed')
254
249
 
255
250
  return nil
256
251
  end
257
252
 
258
253
  def inspect
259
- "<#{self.class.to_s}:#{object_id} #{attributes.inspect}>"
254
+ str = nil
255
+ Computation.run_without_tracking do
256
+ str = "<#{self.class.to_s}:#{object_id} #{attributes.inspect}>"
257
+ end
258
+
259
+ return str
260
260
  end
261
261
 
262
262
  def deep_cur
@@ -294,7 +294,6 @@ class Model
294
294
  self.class.validations.keys.each do |key|
295
295
  mark_field!(key.to_sym)
296
296
  end
297
- trigger_for_methods!('changed', :errors, :marked_errors)
298
297
 
299
298
  return Promise.new.reject(errors)
300
299
  end
@@ -302,12 +301,15 @@ class Model
302
301
 
303
302
 
304
303
  # Returns a buffered version of the model
305
- tag_method(:buffer) do
306
- destructive!
307
- end
308
304
  def buffer
309
305
  model_path = options[:path]
310
- model_klass = class_at_path(model_path)
306
+
307
+ # When we grab a buffer off of a plual class (subcollection), we get it as a model.
308
+ if model_path.last.plural? && model_path[-1] != :[]
309
+ model_klass = class_at_path(model_path + [:[]])
310
+ else
311
+ model_klass = class_at_path(model_path)
312
+ end
311
313
 
312
314
  new_options = options.merge(path: model_path, save_to: self).reject {|k,_| k.to_sym == :persistor }
313
315
  model = model_klass.new({}, new_options, :loading)
@@ -320,36 +322,14 @@ class Model
320
322
  end
321
323
  end
322
324
 
323
- return ReactiveValue.new(model)
325
+ return model
324
326
  end
325
327
 
326
328
 
327
329
  private
328
- def setup_buffer(model)
329
- model.attributes = self.attributes
330
- model.change_state_to(:loaded)
331
- end
332
-
333
- # Clear the previous value and assign a new one
334
- def __assign_element(key, value)
335
- __clear_element(key)
336
- __track_element(key, value)
337
- end
338
-
339
- # TODO: Somewhat duplicated from ReactiveArray
340
- def __clear_element(key)
341
- # Cleanup any tracking on an index
342
- # TODO: is this send a security risk?
343
- if @reactive_element_listeners && @reactive_element_listeners[key]
344
- @reactive_element_listeners[key].remove
345
- @reactive_element_listeners.delete(key)
346
- end
347
- end
348
-
349
- def __track_element(key, value)
350
- __setup_tracking(key, value) do |event, key, args|
351
- trigger_by_attribute!(event, key, *args)
352
- end
330
+ def setup_buffer(model)
331
+ model.attributes = self.attributes
332
+ model.change_state_to(:loaded)
353
333
  end
354
334
 
355
335
  # Takes the persistor if there is one and
@@ -2,18 +2,17 @@
2
2
  # Moving this into a module cleans up the main Model class for things that
3
3
  # make it behave like a model.
4
4
  module ModelHashBehaviour
5
- def self.included(base)
6
- # In modules, since we need to tag on the main class, we setup the
7
- # tags with included.
8
- base.tag_method(:delete) do
9
- destructive!
10
- end
11
5
 
12
- base.tag_method(:clear) do
13
- destructive!
14
- end
15
- end
6
+ def delete(name)
7
+ name = name.to_sym
16
8
 
9
+ value = attributes.delete(name)
10
+ @deps.delete(name)
11
+
12
+ @persistor.removed(name) if @persistor
13
+
14
+ return value
15
+ end
17
16
 
18
17
  def nil?
19
18
  attributes.nil?
@@ -27,25 +26,12 @@ module ModelHashBehaviour
27
26
  attributes.true?
28
27
  end
29
28
 
30
- def delete(name)
31
- name = name.to_sym
32
- __clear_element(name)
33
- value = attributes.delete(name)
34
- trigger_by_attribute!('changed', name)
35
-
36
- @persistor.removed(name) if @persistor
37
-
38
- return value
39
- end
40
-
41
-
42
29
  def clear
43
30
  attributes.each_pair do |key,value|
44
- __clear_element(key)
31
+ @deps.changed!(key)
45
32
  end
46
33
 
47
34
  attributes.clear
48
- trigger!('changed')
49
35
 
50
36
  @persistor.removed(nil) if @persistor
51
37
  end
@@ -12,8 +12,8 @@ module ModelHelpers
12
12
  end
13
13
 
14
14
  # Pass to the persisotr
15
- def event_added(event, scope_provider, first, first_for_event)
16
- @persistor.event_added(event, scope_provider, first, first_for_event) if @persistor
15
+ def event_added(event, first, first_for_event)
16
+ @persistor.event_added(event, first, first_for_event) if @persistor
17
17
  end
18
18
 
19
19
  # Pass to the persistor
@@ -15,7 +15,7 @@ module ModelState
15
15
  end
16
16
 
17
17
  def loaded?
18
- state == :loaded
18
+ self.state == :loaded
19
19
  end
20
20
 
21
21
 
@@ -2,9 +2,9 @@ module ModelWrapper
2
2
  # For cretain values, we wrap them to make the behave as a
3
3
  # model.
4
4
  def wrap_value(value, lookup)
5
- if value.cur.is_a?(Array)
5
+ if value.is_a?(Array)
6
6
  value = new_array_model(value, @options.merge(parent: self, path: path + lookup))
7
- elsif value.cur.is_a?(Hash)
7
+ elsif value.is_a?(Hash)
8
8
  value = new_model(value, @options.merge(parent: self, path: path + lookup))
9
9
  end
10
10
 
@@ -12,10 +12,10 @@ module ModelWrapper
12
12
  end
13
13
 
14
14
  def wrap_values(values, lookup=[])
15
- if values.cur.is_a?(Array)
15
+ if values.is_a?(Array)
16
16
  # Coming from an array
17
17
  values = values.map {|v| wrap_value(v,lookup + [:[]]) }
18
- elsif values.cur.is_a?(Hash)
18
+ elsif values.is_a?(Hash)
19
19
  pairs = values.map do |k,v|
20
20
  path = lookup + [k]
21
21
 
@@ -17,28 +17,32 @@ module Persistors
17
17
  def initialize(model, tasks=nil)
18
18
  super
19
19
 
20
- query = @model.options[:query]
21
-
22
- @query = ReactiveValue.from_hash(query || {})
20
+ @query = @model.options[:query]
23
21
  end
24
22
 
25
- def event_added(event, scope_provider, first, first_for_event)
23
+ def event_added(event, first, first_for_event)
26
24
  # First event, we load the data.
27
- load_data if first
25
+ if first
26
+ @has_events = true
27
+ load_data
28
+ end
28
29
  end
29
30
 
30
31
  def event_removed(event, last, last_for_event)
31
32
  # Remove listener where there are no more events on this model
32
- stop_listening if last
33
+ if last
34
+ @has_events = false
35
+ stop_listening
36
+ end
33
37
  end
34
38
 
35
39
  # Called when an event is removed and we no longer want to keep in
36
40
  # sync with the database.
37
- def stop_listening
38
- if @query_changed_listener
39
- @query_changed_listener.remove
40
- @query_changed_listener = nil
41
- end
41
+ def stop_listening(stop_watching_query=true)
42
+ return if @has_events
43
+ return if @fetch_promises && @fetch_promises.size > 0
44
+
45
+ @query_computation.stop if @query_computation && stop_watching_query
42
46
 
43
47
  if @query_listener
44
48
  @query_listener.remove_store(self)
@@ -52,22 +56,21 @@ module Persistors
52
56
  def load_data
53
57
  # Don't load data from any queried
54
58
  if @state == :not_loaded || @state == :dirty
55
- # puts "Load Data at #{@model.path.inspect} - query: #{@query.inspect}"# on #{@model.inspect}"
59
+ # puts "Load Data at #{@model.path.inspect} - query: #{@query.inspect} on #{self.inspect}"
56
60
  change_state_to :loading
57
61
 
58
- @query_changed_listener.remove if @query_changed_listener
59
- if @query.reactive?
60
- # puts "SETUP REACTIVE QUERY LISTENER: #{@query.inspect}"
61
- # Query might change, change the query when it does
62
- @query_changed_listener = @query.on('changed') do
63
- stop_listening
62
+ if @query.is_a?(Proc)
63
+ @query_computation = -> do
64
+ puts "Run Query Again"
65
+ stop_listening(false)
64
66
 
65
- # Don't load again if all of the listeners are gone
66
- load_data if @model.has_listeners?
67
- end
68
- end
67
+ change_state_to :loading
69
68
 
70
- run_query(@model, @query.deep_cur)
69
+ run_query(@model, @query.call)
70
+ end.watch!
71
+ else
72
+ run_query(@model, @query)
73
+ end
71
74
  end
72
75
  end
73
76
 
@@ -78,6 +81,9 @@ module Persistors
78
81
  end
79
82
 
80
83
  def run_query(model, query={})
84
+ @model.clear
85
+
86
+ # puts "Run Query: #{query.inspect}"
81
87
  collection = model.path.last
82
88
  # Scope to the parent
83
89
  if model.path.size > 1
@@ -98,10 +104,20 @@ module Persistors
98
104
  @query_listener.add_store(self)
99
105
  end
100
106
 
101
- def find(query={})
102
- model = Cursor.new([], @model.options.merge(:query => query))
107
+ # Find can take either a query object, or a block that returns a query object. Use
108
+ # the block style if you need reactive updating queries
109
+ def find(query=nil, &block)
110
+ # Set a default query if there is no block
111
+ if block
112
+ if query
113
+ raise "Query should not be passed in to a find if a block is specified"
114
+ end
115
+ query = block
116
+ else
117
+ query ||= {}
118
+ end
103
119
 
104
- return ReactiveValue.new(model)
120
+ return Cursor.new([], @model.options.merge(:query => query))
105
121
  end
106
122
 
107
123
  # Returns a promise that is resolved/rejected when the query is complete. Any
@@ -130,7 +146,7 @@ module Persistors
130
146
  new_options = @model.options.merge(path: @model.path + [:[]], parent: @model)
131
147
 
132
148
  # Don't add if the model is already in the ArrayModel
133
- if !@model.cur.array.find {|v| v['_id'] == data['_id'] }
149
+ if !@model.array.find {|v| v['_id'] == data['_id'] }
134
150
  # Find the existing model, or create one
135
151
  new_model = @@identity_map.find(data['_id']) { @model.new_model(data.symbolize_keys, new_options, :loaded) }
136
152
 
@@ -159,9 +175,9 @@ module Persistors
159
175
  @model.path[-1]
160
176
  end
161
177
 
162
-
163
178
  # When a model is added to this collection, we call its "changed"
164
179
  # method. This should trigger a save.
180
+ # TODORW:
165
181
  def added(model, index)
166
182
  if model.persistor
167
183
  # Tell the persistor it was added