volt 0.8.14 → 0.8.15
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/.gitignore +1 -1
- data/Readme.md +8 -2
- data/VERSION +1 -1
- data/app/volt/controllers/notices_controller.rb +1 -1
- data/app/volt/models/user.rb +2 -2
- data/app/volt/tasks/live_query/live_query_pool.rb +1 -1
- data/app/volt/tasks/query_tasks.rb +1 -1
- data/app/volt/tasks/store_tasks.rb +1 -1
- data/app/volt/tasks/user_tasks.rb +2 -2
- data/lib/volt/boot.rb +2 -2
- data/lib/volt/cli/asset_compile.rb +31 -27
- data/lib/volt/cli.rb +64 -65
- data/lib/volt/config.rb +25 -23
- data/lib/volt/console.rb +17 -16
- data/lib/volt/controllers/model_controller.rb +82 -80
- data/lib/volt/data_stores/data_store.rb +2 -2
- data/lib/volt/data_stores/mongo_driver.rb +2 -2
- data/lib/volt/extra_core/inflections.rb +2 -2
- data/lib/volt/extra_core/inflector/inflections.rb +185 -183
- data/lib/volt/extra_core/inflector/methods.rb +50 -48
- data/lib/volt/extra_core/string.rb +2 -2
- data/lib/volt/models/array_model.rb +93 -92
- data/lib/volt/models/cursor.rb +3 -2
- data/lib/volt/models/model.rb +248 -251
- data/lib/volt/models/model_hash_behaviour.rb +44 -44
- data/lib/volt/models/model_helpers.rb +38 -36
- data/lib/volt/models/model_state.rb +16 -17
- data/lib/volt/models/model_wrapper.rb +25 -24
- data/lib/volt/models/persistors/array_store.rb +145 -143
- data/lib/volt/models/persistors/base.rb +18 -16
- data/lib/volt/models/persistors/flash.rb +24 -22
- data/lib/volt/models/persistors/local_store.rb +46 -44
- data/lib/volt/models/persistors/model_identity_map.rb +10 -8
- data/lib/volt/models/persistors/model_store.rb +76 -76
- data/lib/volt/models/persistors/params.rb +19 -17
- data/lib/volt/models/persistors/query/query_listener.rb +65 -63
- data/lib/volt/models/persistors/query/query_listener_pool.rb +12 -10
- data/lib/volt/models/persistors/store.rb +28 -28
- data/lib/volt/models/persistors/store_factory.rb +12 -10
- data/lib/volt/models/persistors/store_state.rb +33 -31
- data/lib/volt/models/url.rb +96 -104
- data/lib/volt/models/validations.rb +56 -54
- data/lib/volt/models/validators/length_validator.rb +24 -22
- data/lib/volt/models/validators/presence_validator.rb +14 -12
- data/lib/volt/page/bindings/attribute_binding.rb +106 -106
- data/lib/volt/page/bindings/base_binding.rb +23 -21
- data/lib/volt/page/bindings/component_binding.rb +3 -1
- data/lib/volt/page/bindings/content_binding.rb +34 -34
- data/lib/volt/page/bindings/each_binding.rb +113 -113
- data/lib/volt/page/bindings/event_binding.rb +38 -34
- data/lib/volt/page/bindings/if_binding.rb +56 -54
- data/lib/volt/page/bindings/template_binding/grouped_controllers.rb +24 -22
- data/lib/volt/page/bindings/template_binding.rb +182 -185
- data/lib/volt/page/channel.rb +79 -77
- data/lib/volt/page/channel_stub.rb +29 -27
- data/lib/volt/page/document.rb +6 -5
- data/lib/volt/page/document_events.rb +54 -52
- data/lib/volt/page/page.rb +139 -138
- data/lib/volt/page/string_template_renderer.rb +36 -36
- data/lib/volt/page/sub_context.rb +26 -25
- data/lib/volt/page/targets/attribute_section.rb +27 -25
- data/lib/volt/page/targets/attribute_target.rb +7 -6
- data/lib/volt/page/targets/base_section.rb +27 -26
- data/lib/volt/page/targets/binding_document/base_node.rb +3 -1
- data/lib/volt/page/targets/binding_document/component_node.rb +85 -82
- data/lib/volt/page/targets/binding_document/html_node.rb +11 -9
- data/lib/volt/page/targets/dom_section.rb +78 -77
- data/lib/volt/page/targets/dom_target.rb +8 -6
- data/lib/volt/page/targets/dom_template.rb +90 -88
- data/lib/volt/page/targets/helpers/comment_searchers.rb +51 -49
- data/lib/volt/page/tasks.rb +59 -57
- data/lib/volt/page/template_renderer.rb +17 -14
- data/lib/volt/page/url_tracker.rb +26 -24
- data/lib/volt/reactive/computation.rb +87 -88
- data/lib/volt/reactive/dependency.rb +30 -28
- data/lib/volt/reactive/eventable.rb +64 -62
- data/lib/volt/reactive/hash_dependency.rb +25 -23
- data/lib/volt/reactive/reactive_accessors.rb +34 -32
- data/lib/volt/reactive/reactive_array.rb +162 -162
- data/lib/volt/reactive/reactive_hash.rb +37 -35
- data/lib/volt/router/routes.rb +99 -101
- data/lib/volt/server/component_handler.rb +20 -21
- data/lib/volt/server/component_templates.rb +72 -70
- data/lib/volt/server/html_parser/attribute_scope.rb +109 -99
- data/lib/volt/server/html_parser/each_scope.rb +17 -16
- data/lib/volt/server/html_parser/if_view_scope.rb +51 -49
- data/lib/volt/server/html_parser/sandlebars_parser.rb +184 -177
- data/lib/volt/server/html_parser/textarea_scope.rb +24 -22
- data/lib/volt/server/html_parser/view_handler.rb +66 -65
- data/lib/volt/server/html_parser/view_parser.rb +23 -21
- data/lib/volt/server/html_parser/view_scope.rb +142 -141
- data/lib/volt/server/rack/asset_files.rb +81 -79
- data/lib/volt/server/rack/component_code.rb +17 -15
- data/lib/volt/server/rack/component_html_renderer.rb +14 -12
- data/lib/volt/server/rack/component_paths.rb +72 -71
- data/lib/volt/server/rack/index_files.rb +36 -39
- data/lib/volt/server/rack/opal_files.rb +43 -41
- data/lib/volt/server/rack/source_map_server.rb +23 -21
- data/lib/volt/server/socket_connection_handler.rb +46 -45
- data/lib/volt/server/socket_connection_handler_stub.rb +21 -19
- data/lib/volt/server.rb +60 -58
- data/lib/volt/spec/setup.rb +3 -3
- data/lib/volt/tasks/dispatcher.rb +24 -23
- data/lib/volt/tasks/task_handler.rb +35 -33
- data/lib/volt/utils/ejson.rb +8 -6
- data/lib/volt/utils/generic_counting_pool.rb +33 -31
- data/lib/volt/utils/generic_pool.rb +73 -70
- data/lib/volt/utils/local_storage.rb +42 -38
- data/lib/volt/volt/environment.rb +1 -1
- data/lib/volt.rb +44 -42
- data/spec/apps/kitchen_sink/app/main/assets/css/todos.css +28 -0
- data/spec/apps/kitchen_sink/app/main/config/routes.rb +1 -0
- data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +2 -2
- data/spec/apps/kitchen_sink/app/main/controllers/todos_controller.rb +17 -0
- data/spec/apps/kitchen_sink/app/main/views/main/main.html +1 -0
- data/spec/apps/kitchen_sink/app/main/views/todos/index.html +24 -0
- data/spec/apps/kitchen_sink/config.ru +1 -1
- data/spec/controllers/reactive_accessors_spec.rb +5 -5
- data/spec/extra_core/inflector_spec.rb +2 -2
- data/spec/integration/list_spec.rb +68 -0
- data/spec/models/model_spec.rb +57 -57
- data/spec/models/persistors/params_spec.rb +6 -6
- data/spec/models/persistors/store_spec.rb +7 -7
- data/spec/models/validations_spec.rb +3 -3
- data/spec/page/bindings/content_binding_spec.rb +7 -7
- data/spec/page/bindings/template_binding_spec.rb +4 -5
- data/spec/page/sub_context_spec.rb +2 -2
- data/spec/reactive/computation_spec.rb +10 -10
- data/spec/reactive/dependency_spec.rb +2 -2
- data/spec/reactive/eventable_spec.rb +4 -4
- data/spec/reactive/reactive_array_spec.rb +13 -13
- data/spec/router/routes_spec.rb +5 -5
- data/spec/server/html_parser/sandlebars_parser_spec.rb +9 -9
- data/spec/server/html_parser/view_parser_spec.rb +27 -27
- data/spec/server/rack/asset_files_spec.rb +5 -5
- data/spec/server/rack/component_paths_spec.rb +2 -2
- data/spec/tasks/live_query_spec.rb +2 -2
- data/spec/tasks/query_tasks.rb +1 -1
- data/spec/tasks/query_tracker_spec.rb +1 -1
- data/spec/templates/targets/binding_document/component_node_spec.rb +2 -2
- data/spec/utils/generic_counting_pool_spec.rb +2 -2
- data/spec/utils/generic_pool_spec.rb +2 -2
- data/templates/component/controllers/main_controller.rb +1 -1
- data/templates/model/model.rb.tt +2 -2
- data/templates/newgem/app/newgem/controllers/main_controller.rb.tt +2 -2
- data/templates/project/app/main/controllers/main_controller.rb +1 -1
- data/templates/project/config.ru +1 -1
- metadata +10 -3
- data/app/volt/assets/js/vertxbus.js +0 -216
data/lib/volt/models/model.rb
CHANGED
|
@@ -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
|
-
|
|
10
|
-
end
|
|
9
|
+
module Volt
|
|
11
10
|
|
|
11
|
+
class NilMethodCall < NoMethodError
|
|
12
|
+
end
|
|
12
13
|
|
|
13
|
-
class Model
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
class Model
|
|
15
|
+
include ModelWrapper
|
|
16
|
+
include ModelHelpers
|
|
17
|
+
include ModelHashBehaviour
|
|
18
|
+
include Validations
|
|
19
|
+
include ModelState
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
attr_reader :attributes
|
|
22
|
+
attr_reader :parent, :path, :persistor, :options
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
def initialize(attributes = {}, options = {}, initial_state = nil)
|
|
25
|
+
@deps = HashDependency.new
|
|
26
|
+
self.options = options
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
send(:attributes=, attributes, true)
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
34
|
-
|
|
34
|
+
@persistor.loaded(initial_state) if @persistor
|
|
35
|
+
end
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
def _id=(val)
|
|
43
|
+
self.__id = val
|
|
44
|
+
end
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
# Assign multiple attributes as a hash, directly.
|
|
56
|
+
def attributes=(attrs, initial_setup = false)
|
|
57
|
+
@attributes = {}
|
|
57
58
|
|
|
58
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
self._id = id if id
|
|
80
|
+
# Trigger and change all
|
|
81
|
+
@deps.changed_all!
|
|
82
|
+
@deps = HashDependency.new
|
|
64
83
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
80
|
-
@deps.changed_all!
|
|
81
|
-
@deps = HashDependency.new
|
|
82
|
-
|
|
83
|
-
unless initial_setup
|
|
95
|
+
alias_method :assign_attributes, :attributes=
|
|
84
96
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
#
|
|
89
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
# Pass through needed
|
|
109
|
+
def !
|
|
110
|
+
!attributes
|
|
111
|
+
end
|
|
110
112
|
|
|
111
113
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
-
|
|
134
|
+
value = args[0]
|
|
133
135
|
|
|
134
|
-
|
|
136
|
+
@attributes[attribute_name] = wrap_value(value, [attribute_name])
|
|
135
137
|
|
|
136
|
-
|
|
138
|
+
@deps.changed!(attribute_name)
|
|
137
139
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
140
|
+
# Let the persistor know something changed
|
|
141
|
+
@persistor.changed(attribute_name) if @persistor
|
|
142
|
+
end
|
|
141
143
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
-
|
|
167
|
-
|
|
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
|
-
|
|
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
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
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
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
204
|
+
def new_model(attributes, options)
|
|
205
|
+
class_at_path(options[:path]).new(attributes, options)
|
|
206
|
+
end
|
|
207
207
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
208
|
+
def new_array_model(attributes, options)
|
|
209
|
+
# Start with an empty query
|
|
210
|
+
options = options.dup
|
|
211
|
+
options[:query] = {}
|
|
212
212
|
|
|
213
|
-
|
|
214
|
-
|
|
213
|
+
ArrayModel.new(attributes, options)
|
|
214
|
+
end
|
|
215
215
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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
|
-
|
|
221
|
-
|
|
220
|
+
# TODO: Opal bug
|
|
221
|
+
args ||= []
|
|
222
222
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
251
|
-
|
|
252
|
-
|
|
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
|
-
|
|
255
|
-
|
|
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
|
-
|
|
261
|
-
|
|
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
|
-
|
|
264
|
-
|
|
260
|
+
# Add the new item
|
|
261
|
+
result << value
|
|
265
262
|
|
|
266
|
-
|
|
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
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
297
|
-
|
|
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
|
-
|
|
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
|
-
|
|
312
|
-
|
|
313
|
-
|
|
308
|
+
# Returns a buffered version of the model
|
|
309
|
+
def buffer
|
|
310
|
+
model_path = options[:path]
|
|
314
311
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
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
|
-
|
|
323
|
-
|
|
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
|
-
|
|
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
|
-
|
|
334
|
-
|
|
330
|
+
model
|
|
331
|
+
end
|
|
335
332
|
|
|
336
333
|
|
|
337
|
-
|
|
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
|