volt 0.8.14 → 0.8.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/Readme.md +8 -2
  4. data/VERSION +1 -1
  5. data/app/volt/controllers/notices_controller.rb +1 -1
  6. data/app/volt/models/user.rb +2 -2
  7. data/app/volt/tasks/live_query/live_query_pool.rb +1 -1
  8. data/app/volt/tasks/query_tasks.rb +1 -1
  9. data/app/volt/tasks/store_tasks.rb +1 -1
  10. data/app/volt/tasks/user_tasks.rb +2 -2
  11. data/lib/volt/boot.rb +2 -2
  12. data/lib/volt/cli/asset_compile.rb +31 -27
  13. data/lib/volt/cli.rb +64 -65
  14. data/lib/volt/config.rb +25 -23
  15. data/lib/volt/console.rb +17 -16
  16. data/lib/volt/controllers/model_controller.rb +82 -80
  17. data/lib/volt/data_stores/data_store.rb +2 -2
  18. data/lib/volt/data_stores/mongo_driver.rb +2 -2
  19. data/lib/volt/extra_core/inflections.rb +2 -2
  20. data/lib/volt/extra_core/inflector/inflections.rb +185 -183
  21. data/lib/volt/extra_core/inflector/methods.rb +50 -48
  22. data/lib/volt/extra_core/string.rb +2 -2
  23. data/lib/volt/models/array_model.rb +93 -92
  24. data/lib/volt/models/cursor.rb +3 -2
  25. data/lib/volt/models/model.rb +248 -251
  26. data/lib/volt/models/model_hash_behaviour.rb +44 -44
  27. data/lib/volt/models/model_helpers.rb +38 -36
  28. data/lib/volt/models/model_state.rb +16 -17
  29. data/lib/volt/models/model_wrapper.rb +25 -24
  30. data/lib/volt/models/persistors/array_store.rb +145 -143
  31. data/lib/volt/models/persistors/base.rb +18 -16
  32. data/lib/volt/models/persistors/flash.rb +24 -22
  33. data/lib/volt/models/persistors/local_store.rb +46 -44
  34. data/lib/volt/models/persistors/model_identity_map.rb +10 -8
  35. data/lib/volt/models/persistors/model_store.rb +76 -76
  36. data/lib/volt/models/persistors/params.rb +19 -17
  37. data/lib/volt/models/persistors/query/query_listener.rb +65 -63
  38. data/lib/volt/models/persistors/query/query_listener_pool.rb +12 -10
  39. data/lib/volt/models/persistors/store.rb +28 -28
  40. data/lib/volt/models/persistors/store_factory.rb +12 -10
  41. data/lib/volt/models/persistors/store_state.rb +33 -31
  42. data/lib/volt/models/url.rb +96 -104
  43. data/lib/volt/models/validations.rb +56 -54
  44. data/lib/volt/models/validators/length_validator.rb +24 -22
  45. data/lib/volt/models/validators/presence_validator.rb +14 -12
  46. data/lib/volt/page/bindings/attribute_binding.rb +106 -106
  47. data/lib/volt/page/bindings/base_binding.rb +23 -21
  48. data/lib/volt/page/bindings/component_binding.rb +3 -1
  49. data/lib/volt/page/bindings/content_binding.rb +34 -34
  50. data/lib/volt/page/bindings/each_binding.rb +113 -113
  51. data/lib/volt/page/bindings/event_binding.rb +38 -34
  52. data/lib/volt/page/bindings/if_binding.rb +56 -54
  53. data/lib/volt/page/bindings/template_binding/grouped_controllers.rb +24 -22
  54. data/lib/volt/page/bindings/template_binding.rb +182 -185
  55. data/lib/volt/page/channel.rb +79 -77
  56. data/lib/volt/page/channel_stub.rb +29 -27
  57. data/lib/volt/page/document.rb +6 -5
  58. data/lib/volt/page/document_events.rb +54 -52
  59. data/lib/volt/page/page.rb +139 -138
  60. data/lib/volt/page/string_template_renderer.rb +36 -36
  61. data/lib/volt/page/sub_context.rb +26 -25
  62. data/lib/volt/page/targets/attribute_section.rb +27 -25
  63. data/lib/volt/page/targets/attribute_target.rb +7 -6
  64. data/lib/volt/page/targets/base_section.rb +27 -26
  65. data/lib/volt/page/targets/binding_document/base_node.rb +3 -1
  66. data/lib/volt/page/targets/binding_document/component_node.rb +85 -82
  67. data/lib/volt/page/targets/binding_document/html_node.rb +11 -9
  68. data/lib/volt/page/targets/dom_section.rb +78 -77
  69. data/lib/volt/page/targets/dom_target.rb +8 -6
  70. data/lib/volt/page/targets/dom_template.rb +90 -88
  71. data/lib/volt/page/targets/helpers/comment_searchers.rb +51 -49
  72. data/lib/volt/page/tasks.rb +59 -57
  73. data/lib/volt/page/template_renderer.rb +17 -14
  74. data/lib/volt/page/url_tracker.rb +26 -24
  75. data/lib/volt/reactive/computation.rb +87 -88
  76. data/lib/volt/reactive/dependency.rb +30 -28
  77. data/lib/volt/reactive/eventable.rb +64 -62
  78. data/lib/volt/reactive/hash_dependency.rb +25 -23
  79. data/lib/volt/reactive/reactive_accessors.rb +34 -32
  80. data/lib/volt/reactive/reactive_array.rb +162 -162
  81. data/lib/volt/reactive/reactive_hash.rb +37 -35
  82. data/lib/volt/router/routes.rb +99 -101
  83. data/lib/volt/server/component_handler.rb +20 -21
  84. data/lib/volt/server/component_templates.rb +72 -70
  85. data/lib/volt/server/html_parser/attribute_scope.rb +109 -99
  86. data/lib/volt/server/html_parser/each_scope.rb +17 -16
  87. data/lib/volt/server/html_parser/if_view_scope.rb +51 -49
  88. data/lib/volt/server/html_parser/sandlebars_parser.rb +184 -177
  89. data/lib/volt/server/html_parser/textarea_scope.rb +24 -22
  90. data/lib/volt/server/html_parser/view_handler.rb +66 -65
  91. data/lib/volt/server/html_parser/view_parser.rb +23 -21
  92. data/lib/volt/server/html_parser/view_scope.rb +142 -141
  93. data/lib/volt/server/rack/asset_files.rb +81 -79
  94. data/lib/volt/server/rack/component_code.rb +17 -15
  95. data/lib/volt/server/rack/component_html_renderer.rb +14 -12
  96. data/lib/volt/server/rack/component_paths.rb +72 -71
  97. data/lib/volt/server/rack/index_files.rb +36 -39
  98. data/lib/volt/server/rack/opal_files.rb +43 -41
  99. data/lib/volt/server/rack/source_map_server.rb +23 -21
  100. data/lib/volt/server/socket_connection_handler.rb +46 -45
  101. data/lib/volt/server/socket_connection_handler_stub.rb +21 -19
  102. data/lib/volt/server.rb +60 -58
  103. data/lib/volt/spec/setup.rb +3 -3
  104. data/lib/volt/tasks/dispatcher.rb +24 -23
  105. data/lib/volt/tasks/task_handler.rb +35 -33
  106. data/lib/volt/utils/ejson.rb +8 -6
  107. data/lib/volt/utils/generic_counting_pool.rb +33 -31
  108. data/lib/volt/utils/generic_pool.rb +73 -70
  109. data/lib/volt/utils/local_storage.rb +42 -38
  110. data/lib/volt/volt/environment.rb +1 -1
  111. data/lib/volt.rb +44 -42
  112. data/spec/apps/kitchen_sink/app/main/assets/css/todos.css +28 -0
  113. data/spec/apps/kitchen_sink/app/main/config/routes.rb +1 -0
  114. data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +2 -2
  115. data/spec/apps/kitchen_sink/app/main/controllers/todos_controller.rb +17 -0
  116. data/spec/apps/kitchen_sink/app/main/views/main/main.html +1 -0
  117. data/spec/apps/kitchen_sink/app/main/views/todos/index.html +24 -0
  118. data/spec/apps/kitchen_sink/config.ru +1 -1
  119. data/spec/controllers/reactive_accessors_spec.rb +5 -5
  120. data/spec/extra_core/inflector_spec.rb +2 -2
  121. data/spec/integration/list_spec.rb +68 -0
  122. data/spec/models/model_spec.rb +57 -57
  123. data/spec/models/persistors/params_spec.rb +6 -6
  124. data/spec/models/persistors/store_spec.rb +7 -7
  125. data/spec/models/validations_spec.rb +3 -3
  126. data/spec/page/bindings/content_binding_spec.rb +7 -7
  127. data/spec/page/bindings/template_binding_spec.rb +4 -5
  128. data/spec/page/sub_context_spec.rb +2 -2
  129. data/spec/reactive/computation_spec.rb +10 -10
  130. data/spec/reactive/dependency_spec.rb +2 -2
  131. data/spec/reactive/eventable_spec.rb +4 -4
  132. data/spec/reactive/reactive_array_spec.rb +13 -13
  133. data/spec/router/routes_spec.rb +5 -5
  134. data/spec/server/html_parser/sandlebars_parser_spec.rb +9 -9
  135. data/spec/server/html_parser/view_parser_spec.rb +27 -27
  136. data/spec/server/rack/asset_files_spec.rb +5 -5
  137. data/spec/server/rack/component_paths_spec.rb +2 -2
  138. data/spec/tasks/live_query_spec.rb +2 -2
  139. data/spec/tasks/query_tasks.rb +1 -1
  140. data/spec/tasks/query_tracker_spec.rb +1 -1
  141. data/spec/templates/targets/binding_document/component_node_spec.rb +2 -2
  142. data/spec/utils/generic_counting_pool_spec.rb +2 -2
  143. data/spec/utils/generic_pool_spec.rb +2 -2
  144. data/templates/component/controllers/main_controller.rb +1 -1
  145. data/templates/model/model.rb.tt +2 -2
  146. data/templates/newgem/app/newgem/controllers/main_controller.rb.tt +2 -2
  147. data/templates/project/app/main/controllers/main_controller.rb +1 -1
  148. data/templates/project/config.ru +1 -1
  149. metadata +10 -3
  150. data/app/volt/assets/js/vertxbus.js +0 -216
@@ -6,335 +6,332 @@ require 'volt/models/validations'
6
6
  require 'volt/models/model_state'
7
7
  require 'volt/reactive/reactive_hash'
8
8
 
9
- class NilMethodCall < NoMethodError
10
- end
9
+ module Volt
11
10
 
11
+ class NilMethodCall < NoMethodError
12
+ end
12
13
 
13
- class Model
14
- include ModelWrapper
15
- include ModelHelpers
16
- include ModelHashBehaviour
17
- include Validations
18
- include ModelState
14
+ class Model
15
+ include ModelWrapper
16
+ include ModelHelpers
17
+ include ModelHashBehaviour
18
+ include Validations
19
+ include ModelState
19
20
 
20
- attr_reader :attributes
21
- attr_reader :parent, :path, :persistor, :options
21
+ attr_reader :attributes
22
+ attr_reader :parent, :path, :persistor, :options
22
23
 
23
- def initialize(attributes={}, options={}, initial_state=nil)
24
- @deps = HashDependency.new
25
- self.options = options
24
+ def initialize(attributes = {}, options = {}, initial_state = nil)
25
+ @deps = HashDependency.new
26
+ self.options = options
26
27
 
27
- self.send(:attributes=, attributes, true)
28
+ send(:attributes=, attributes, true)
28
29
 
29
- # Models start in a loaded state since they are normally setup from an
30
- # ArrayModel, which will have the data when they get added.
31
- @state = :loaded
30
+ # Models start in a loaded state since they are normally setup from an
31
+ # ArrayModel, which will have the data when they get added.
32
+ @state = :loaded
32
33
 
33
- @persistor.loaded(initial_state) if @persistor
34
- end
34
+ @persistor.loaded(initial_state) if @persistor
35
+ end
35
36
 
36
- # the id is stored in a field named _id, so we setup _id to proxy to this
37
- def _id
38
- @attributes && @attributes[:_id]
39
- end
37
+ # the id is stored in a field named _id, so we setup _id to proxy to this
38
+ def _id
39
+ @attributes && @attributes[:_id]
40
+ end
40
41
 
41
- def _id=(val)
42
- self.__id = val
43
- end
42
+ def _id=(val)
43
+ self.__id = val
44
+ end
44
45
 
45
- # Update the options
46
- def options=(options)
47
- @options = options
48
- @parent = options[:parent]
49
- @path = options[:path] || []
50
- @class_paths = options[:class_paths]
51
- @persistor = setup_persistor(options[:persistor])
52
- end
46
+ # Update the options
47
+ def options=(options)
48
+ @options = options
49
+ @parent = options[:parent]
50
+ @path = options[:path] || []
51
+ @class_paths = options[:class_paths]
52
+ @persistor = setup_persistor(options[:persistor])
53
+ end
53
54
 
54
- # Assign multiple attributes as a hash, directly.
55
- def attributes=(attrs, initial_setup=false)
56
- @attributes = {}
55
+ # Assign multiple attributes as a hash, directly.
56
+ def attributes=(attrs, initial_setup = false)
57
+ @attributes = {}
57
58
 
58
- attrs = wrap_values(attrs)
59
+ attrs = wrap_values(attrs)
60
+
61
+ if attrs
62
+ # Assign id first
63
+ id = attrs.delete(:_id)
64
+ self._id = id if id
65
+
66
+ # Assign each attribute using setters
67
+ attrs.each_pair do |key, value|
68
+ if self.respond_to?(:"#{key}=")
69
+ # If a method without an underscore is defined, call that.
70
+ send(:"#{key}=", value)
71
+ else
72
+ # Otherwise, use the _ version
73
+ send(:"_#{key}=", value)
74
+ end
75
+ end
76
+ else
77
+ @attributes = attrs
78
+ end
59
79
 
60
- if attrs
61
- # Assign id first
62
- id = attrs.delete(:_id)
63
- self._id = id if id
80
+ # Trigger and change all
81
+ @deps.changed_all!
82
+ @deps = HashDependency.new
64
83
 
65
- # Assign each attribute using setters
66
- attrs.each_pair do |key, value|
67
- if self.respond_to?(:"#{key}=")
68
- # If a method without an underscore is defined, call that.
69
- self.send(:"#{key}=", value)
70
- else
71
- # Otherwise, use the _ version
72
- self.send(:"_#{key}=", value)
84
+ unless initial_setup
85
+
86
+ # Let the persistor know something changed
87
+ if @persistor
88
+ # the changed method on a persistor should return a promise that will
89
+ # be resolved when the save is complete, or fail with a hash of errors.
90
+ return @persistor.changed
73
91
  end
74
92
  end
75
- else
76
- @attributes = attrs
77
93
  end
78
94
 
79
- # Trigger and change all
80
- @deps.changed_all!
81
- @deps = HashDependency.new
82
-
83
- unless initial_setup
95
+ alias_method :assign_attributes, :attributes=
84
96
 
85
- # Let the persistor know something changed
86
- if @persistor
87
- # the changed method on a persistor should return a promise that will
88
- # be resolved when the save is complete, or fail with a hash of errors.
89
- return @persistor.changed
97
+ # Pass the comparison through
98
+ def ==(val)
99
+ if val.is_a?(Model)
100
+ # Use normal comparison for a model
101
+ super
102
+ else
103
+ # Compare to attributes otherwise
104
+ attributes == val
90
105
  end
91
106
  end
92
- end
93
- alias_method :assign_attributes, :attributes=
94
-
95
- # Pass the comparison through
96
- def ==(val)
97
- if val.is_a?(Model)
98
- # Use normal comparison for a model
99
- return super
100
- else
101
- # Compare to attributes otherwise
102
- return attributes == val
103
- end
104
- end
105
107
 
106
- # Pass through needed
107
- def !
108
- !attributes
109
- end
108
+ # Pass through needed
109
+ def !
110
+ !attributes
111
+ end
110
112
 
111
113
 
112
- def method_missing(method_name, *args, &block)
113
- if method_name[0] == '_'
114
- if method_name[-1] == '='
115
- # Assigning an attribute with =
116
- assign_attribute(method_name, *args, &block)
114
+ def method_missing(method_name, *args, &block)
115
+ if method_name[0] == '_'
116
+ if method_name[-1] == '='
117
+ # Assigning an attribute with =
118
+ assign_attribute(method_name, *args, &block)
119
+ else
120
+ read_attribute(method_name)
121
+ end
117
122
  else
118
- read_attribute(method_name)
123
+ # Call on parent
124
+ super
119
125
  end
120
- else
121
- # Call on parent
122
- super
123
126
  end
124
- end
125
127
 
126
- # Do the assignment to a model and trigger a changed event
127
- def assign_attribute(method_name, *args, &block)
128
- self.expand!
129
- # Assign, without the =
130
- attribute_name = method_name[1..-2].to_sym
128
+ # Do the assignment to a model and trigger a changed event
129
+ def assign_attribute(method_name, *args, &block)
130
+ self.expand!
131
+ # Assign, without the =
132
+ attribute_name = method_name[1..-2].to_sym
131
133
 
132
- value = args[0]
134
+ value = args[0]
133
135
 
134
- @attributes[attribute_name] = wrap_value(value, [attribute_name])
136
+ @attributes[attribute_name] = wrap_value(value, [attribute_name])
135
137
 
136
- @deps.changed!(attribute_name)
138
+ @deps.changed!(attribute_name)
137
139
 
138
- # Let the persistor know something changed
139
- @persistor.changed(attribute_name) if @persistor
140
- end
140
+ # Let the persistor know something changed
141
+ @persistor.changed(attribute_name) if @persistor
142
+ end
141
143
 
142
- # When reading an attribute, we need to handle reading on:
143
- # 1) a nil model, which returns a wrapped error
144
- # 2) reading directly from attributes
145
- # 3) trying to read a key that doesn't exist.
146
- def read_attribute(method_name)
147
- # Reading an attribute, we may get back a nil model.
148
- method_name = method_name.to_sym
149
-
150
- if method_name[0] != '_' && @attributes == nil
151
- # The method we are calling is on a nil model, return a wrapped
152
- # exception.
153
- return return_undefined_method(method_name)
154
- else
155
- attr_name = method_name[1..-1].to_sym
156
- # See if the value is in attributes
157
- value = (@attributes && @attributes[attr_name])
158
-
159
- # Track dependency
160
- @deps.depend(attr_name)
161
-
162
- if value
163
- # key was in attributes or cache
164
- return value
144
+ # When reading an attribute, we need to handle reading on:
145
+ # 1) a nil model, which returns a wrapped error
146
+ # 2) reading directly from attributes
147
+ # 3) trying to read a key that doesn't exist.
148
+ def read_attribute(method_name)
149
+ # Reading an attribute, we may get back a nil model.
150
+ method_name = method_name.to_sym
151
+
152
+ if method_name[0] != '_' && @attributes == nil
153
+ # The method we are calling is on a nil model, return a wrapped
154
+ # exception.
155
+ return_undefined_method(method_name)
165
156
  else
166
- # TODO: implement a timed out cache flusing
167
- new_model = read_new_model(attr_name)
168
- @attributes ||= {}
169
- @attributes[attr_name] = new_model
157
+ attr_name = method_name[1..-1].to_sym
158
+ # See if the value is in attributes
159
+ value = (@attributes && @attributes[attr_name])
170
160
 
171
- return new_model
161
+ # Track dependency
162
+ @deps.depend(attr_name)
163
+
164
+ if value
165
+ # key was in attributes or cache
166
+ value
167
+ else
168
+ new_model = read_new_model(attr_name)
169
+ @attributes ||= {}
170
+ @attributes[attr_name] = new_model
171
+ new_model
172
+ end
172
173
  end
173
174
  end
174
- end
175
175
 
176
- # Get a new model, make it easy to override
177
- def read_new_model(method_name)
178
- if @persistor && @persistor.respond_to?(:read_new_model)
179
- return @persistor.read_new_model(method_name)
180
- else
181
- opts = @options.merge(parent: self, path: path + [method_name])
182
- if method_name.plural?
183
- return new_array_model([], opts)
176
+ # Get a new model, make it easy to override
177
+ def read_new_model(method_name)
178
+ if @persistor && @persistor.respond_to?(:read_new_model)
179
+ return @persistor.read_new_model(method_name)
184
180
  else
185
- return new_model(nil, opts)
181
+ opts = @options.merge(parent: self, path: path + [method_name])
182
+ if method_name.plural?
183
+ return new_array_model([], opts)
184
+ else
185
+ return new_model(nil, opts)
186
+ end
186
187
  end
187
188
  end
188
- end
189
189
 
190
- def return_undefined_method(method_name)
191
- # Methods called on nil capture an error so the user can know where
192
- # their nil calls are. This error can be re-raised at a later point.
193
- begin
194
- raise NilMethodCall.new("undefined method `#{method_name}' for #{self.to_s}")
195
- rescue => e
196
- result = e
197
-
198
- # Cleanup backtrace
199
- # TODO: this could be better
200
- result.backtrace.reject! {|line| line['lib/models/model.rb'] || line['lib/models/live_value.rb'] }
190
+ def return_undefined_method(method_name)
191
+ # Methods called on nil capture an error so the user can know where
192
+ # their nil calls are. This error can be re-raised at a later point.
193
+ begin
194
+ raise NilMethodCall.new("undefined method `#{method_name}' for #{self.to_s}")
195
+ rescue => e
196
+ result = e
197
+
198
+ # Cleanup backtrace
199
+ # TODO: this could be better
200
+ result.backtrace.reject! { |line| line['lib/models/model.rb'] || line['lib/models/live_value.rb'] }
201
+ end
201
202
  end
202
- end
203
203
 
204
- def new_model(attributes, options)
205
- class_at_path(options[:path]).new(attributes, options)
206
- end
204
+ def new_model(attributes, options)
205
+ class_at_path(options[:path]).new(attributes, options)
206
+ end
207
207
 
208
- def new_array_model(attributes, options)
209
- # Start with an empty query
210
- options = options.dup
211
- options[:query] = {}
208
+ def new_array_model(attributes, options)
209
+ # Start with an empty query
210
+ options = options.dup
211
+ options[:query] = {}
212
212
 
213
- ArrayModel.new(attributes, options)
214
- end
213
+ ArrayModel.new(attributes, options)
214
+ end
215
215
 
216
- def trigger_by_attribute!(event_name, attribute, *passed_args)
217
- trigger_by_scope!(event_name, *passed_args) do |scope|
218
- method_name, *args, block = scope
216
+ def trigger_by_attribute!(event_name, attribute, *passed_args)
217
+ trigger_by_scope!(event_name, *passed_args) do |scope|
218
+ method_name, *args, block = scope
219
219
 
220
- # TODO: Opal bug
221
- args ||= []
220
+ # TODO: Opal bug
221
+ args ||= []
222
222
 
223
- # Any methods without _ are not directly related to one attribute, so
224
- # they should all trigger
225
- !method_name || method_name[0] != '_' || (method_name == attribute.to_sym && args.size == 0)
223
+ # Any methods without _ are not directly related to one attribute, so
224
+ # they should all trigger
225
+ !method_name || method_name[0] != '_' || (method_name == attribute.to_sym && args.size == 0)
226
+ end
226
227
  end
227
- end
228
228
 
229
- # If this model is nil, it makes it into a hash model, then
230
- # sets it up to track from the parent.
231
- def expand!
232
- if attributes.nil?
233
- @attributes = {}
234
- if @parent
235
- @parent.expand!
229
+ # If this model is nil, it makes it into a hash model, then
230
+ # sets it up to track from the parent.
231
+ def expand!
232
+ if attributes.nil?
233
+ @attributes = {}
234
+ if @parent
235
+ @parent.expand!
236
236
 
237
- @parent.send(:"_#{@path.last}=", self)
237
+ @parent.send(:"_#{@path.last}=", self)
238
+ end
238
239
  end
239
240
  end
240
- end
241
-
242
- # Initialize an empty array and append to it
243
- def <<(value)
244
- if @parent
245
- @parent.expand!
246
- else
247
- raise "Model data should be stored in sub collections."
248
- end
249
241
 
250
- # Grab the last section of the path, so we can do the assign on the parent
251
- path = @path.last
252
- result = @parent.send(path)
242
+ # Initialize an empty array and append to it
243
+ def <<(value)
244
+ if @parent
245
+ @parent.expand!
246
+ else
247
+ raise "Model data should be stored in sub collections."
248
+ end
253
249
 
254
- if result.nil?
255
- # If this isn't a model yet, instantiate it
256
- @parent.send(:"#{path}=", new_array_model([], @options))
250
+ # Grab the last section of the path, so we can do the assign on the parent
251
+ path = @path.last
257
252
  result = @parent.send(path)
258
- end
259
253
 
260
- # Add the new item
261
- result << value
254
+ if result.nil?
255
+ # If this isn't a model yet, instantiate it
256
+ @parent.send(:"#{path}=", new_array_model([], @options))
257
+ result = @parent.send(path)
258
+ end
262
259
 
263
- return nil
264
- end
260
+ # Add the new item
261
+ result << value
265
262
 
266
- def inspect
267
- str = nil
268
- Computation.run_without_tracking do
269
- str = "<#{self.class.to_s}:#{object_id} #{attributes.inspect}>"
263
+ nil
270
264
  end
271
265
 
272
- return str
273
- end
274
-
275
- def save!
276
- # Compute the erros once
277
- errors = self.errors
278
-
279
- if errors.size == 0
280
- save_to = options[:save_to]
281
- if save_to
282
- if save_to.is_a?(ArrayModel)
283
- # Add to the collection
284
- new_model = save_to << self.attributes
285
-
286
- # Set the buffer's id to track the main model's id
287
- self.attributes[:_id] = new_model._id
288
- options[:save_to] = new_model
266
+ def inspect
267
+ Computation.run_without_tracking do
268
+ "<#{self.class.to_s}:#{object_id} #{attributes.inspect}>"
269
+ end
270
+ end
289
271
 
290
- # TODO: return a promise that resolves if the append works
272
+ def save!
273
+ # Compute the erros once
274
+ errors = self.errors
275
+
276
+ if errors.size == 0
277
+ save_to = options[:save_to]
278
+ if save_to
279
+ if save_to.is_a?(ArrayModel)
280
+ # Add to the collection
281
+ new_model = save_to << self.attributes
282
+
283
+ # Set the buffer's id to track the main model's id
284
+ self.attributes[:_id] = new_model._id
285
+ options[:save_to] = new_model
286
+
287
+ # TODO: return a promise that resolves if the append works
288
+ else
289
+ # We have a saved model
290
+ return save_to.assign_attributes(self.attributes)
291
+ end
291
292
  else
292
- # We have a saved model
293
- return save_to.assign_attributes(self.attributes)
293
+ raise "Model is not a buffer, can not be saved, modifications should be persisted as they are made."
294
294
  end
295
+
296
+ Promise.new.resolve({})
295
297
  else
296
- raise "Model is not a buffer, can not be saved, modifications should be persisted as they are made."
297
- end
298
+ # Some errors, mark all fields
299
+ self.class.validations.keys.each do |key|
300
+ mark_field!(key.to_sym)
301
+ end
298
302
 
299
- return Promise.new.resolve({})
300
- else
301
- # Some errors, mark all fields
302
- self.class.validations.keys.each do |key|
303
- mark_field!(key.to_sym)
303
+ Promise.new.reject(errors)
304
304
  end
305
-
306
- return Promise.new.reject(errors)
307
305
  end
308
- end
309
306
 
310
307
 
311
- # Returns a buffered version of the model
312
- def buffer
313
- model_path = options[:path]
308
+ # Returns a buffered version of the model
309
+ def buffer
310
+ model_path = options[:path]
314
311
 
315
- # When we grab a buffer off of a plual class (subcollection), we get it as a model.
316
- if model_path.last.plural? && model_path[-1] != :[]
317
- model_klass = class_at_path(model_path + [:[]])
318
- else
319
- model_klass = class_at_path(model_path)
320
- end
312
+ # When we grab a buffer off of a plual class (subcollection), we get it as a model.
313
+ if model_path.last.plural? && model_path[-1] != :[]
314
+ model_klass = class_at_path(model_path + [:[]])
315
+ else
316
+ model_klass = class_at_path(model_path)
317
+ end
321
318
 
322
- new_options = options.merge(path: model_path, save_to: self).reject {|k,_| k.to_sym == :persistor }
323
- model = model_klass.new({}, new_options, :loading)
319
+ new_options = options.merge(path: model_path, save_to: self).reject { |k, _| k.to_sym == :persistor }
320
+ model = model_klass.new({}, new_options, :loading)
324
321
 
325
- if state == :loaded
326
- setup_buffer(model)
327
- else
328
- self.parent.then do
322
+ if state == :loaded
329
323
  setup_buffer(model)
324
+ else
325
+ self.parent.then do
326
+ setup_buffer(model)
327
+ end
330
328
  end
331
- end
332
329
 
333
- return model
334
- end
330
+ model
331
+ end
335
332
 
336
333
 
337
- private
334
+ private
338
335
  def setup_buffer(model)
339
336
  model.attributes = self.attributes
340
337
  model.change_state_to(:loaded)
@@ -346,5 +343,5 @@ class Model
346
343
  @persistor = persistor.new(self)
347
344
  end
348
345
  end
349
-
346
+ end
350
347
  end