volt 0.8.27.beta3 → 0.8.27.beta4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -0
- data/CHANGELOG.md +11 -0
- data/CONTRIBUTING.md +3 -2
- data/{Readme.md → README.md} +9 -12
- data/Rakefile +2 -9
- data/VERSION +1 -1
- data/app/volt/models/user.rb +8 -0
- data/app/volt/tasks/live_query/data_store.rb +13 -5
- data/app/volt/tasks/live_query/live_query.rb +45 -3
- data/app/volt/tasks/live_query/live_query_pool.rb +9 -1
- data/app/volt/tasks/query_tasks.rb +20 -2
- data/app/volt/tasks/store_tasks.rb +37 -20
- data/app/volt/tasks/user_tasks.rb +15 -13
- data/lib/volt/boot.rb +5 -3
- data/lib/volt/cli/console.rb +1 -0
- data/lib/volt/cli/generate.rb +15 -0
- data/lib/volt/cli.rb +19 -12
- data/lib/volt/config.rb +1 -1
- data/lib/volt/controllers/model_controller.rb +13 -3
- data/lib/volt/extra_core/extra_core.rb +1 -0
- data/lib/volt/extra_core/hash.rb +26 -0
- data/lib/volt/extra_core/object.rb +5 -1
- data/lib/volt/models/array_model.rb +86 -35
- data/lib/volt/models/associations.rb +53 -0
- data/lib/volt/models/buffer.rb +22 -10
- data/lib/volt/models/dirty.rb +88 -0
- data/lib/volt/models/errors.rb +21 -0
- data/lib/volt/models/field_helpers.rb +2 -2
- data/lib/volt/models/listener_tracker.rb +17 -0
- data/lib/volt/models/model.rb +213 -69
- data/lib/volt/models/model_helpers.rb +27 -17
- data/lib/volt/models/permissions.rb +246 -0
- data/lib/volt/models/persistors/array_store.rb +149 -81
- data/lib/volt/models/persistors/base.rb +16 -0
- data/lib/volt/models/persistors/cookies.rb +14 -9
- data/lib/volt/models/persistors/flash.rb +3 -0
- data/lib/volt/models/persistors/local_store.rb +0 -16
- data/lib/volt/models/persistors/model_store.rb +1 -2
- data/lib/volt/models/persistors/query/normalizer.rb +51 -0
- data/lib/volt/models/persistors/query/query_listener.rb +21 -5
- data/lib/volt/models/persistors/query/query_listener_pool.rb +0 -9
- data/lib/volt/models/persistors/store.rb +8 -0
- data/lib/volt/models/persistors/store_state.rb +4 -27
- data/lib/volt/models/state_helpers.rb +11 -0
- data/lib/volt/models/state_manager.rb +43 -0
- data/lib/volt/models/url.rb +5 -5
- data/lib/volt/models/validations.rb +38 -41
- data/lib/volt/models/validators/email_validator.rb +4 -9
- data/lib/volt/models/validators/format_validator.rb +23 -8
- data/lib/volt/models/validators/length_validator.rb +2 -2
- data/lib/volt/models/validators/numericality_validator.rb +7 -3
- data/lib/volt/models/validators/phone_number_validator.rb +4 -9
- data/lib/volt/models/validators/presence_validator.rb +2 -2
- data/lib/volt/models/validators/unique_validator.rb +2 -2
- data/lib/volt/models/validators/user_validation.rb +6 -0
- data/lib/volt/models.rb +8 -3
- data/lib/volt/page/bindings/attribute_binding.rb +10 -4
- data/lib/volt/page/bindings/content_binding.rb +9 -5
- data/lib/volt/page/bindings/if_binding.rb +25 -2
- data/lib/volt/page/bindings/template_binding.rb +19 -1
- data/lib/volt/page/bindings/yield_binding.rb +31 -0
- data/lib/volt/page/page.rb +11 -16
- data/lib/volt/reactive/class_eventable.rb +71 -0
- data/lib/volt/reactive/computation.rb +79 -10
- data/lib/volt/reactive/dependency.rb +27 -8
- data/lib/volt/reactive/eventable.rb +36 -22
- data/lib/volt/reactive/reactive_array.rb +2 -3
- data/lib/volt/reactive/reactive_hash.rb +8 -3
- data/lib/volt/router/routes.rb +2 -1
- data/lib/volt/server/component_templates.rb +0 -2
- data/lib/volt/server/html_parser/component_view_scope.rb +59 -0
- data/lib/volt/server/html_parser/view_handler.rb +3 -0
- data/lib/volt/server/html_parser/view_parser.rb +1 -0
- data/lib/volt/server/html_parser/view_scope.rb +17 -41
- data/lib/volt/server/rack/component_paths.rb +1 -10
- data/lib/volt/server/rack/index_files.rb +9 -4
- data/lib/volt/server/rack/opal_files.rb +22 -14
- data/lib/volt/server/rack/quiet_common_logger.rb +1 -1
- data/lib/volt/server/socket_connection_handler.rb +4 -0
- data/lib/volt/spec/setup.rb +26 -0
- data/lib/volt/tasks/dispatcher.rb +11 -0
- data/lib/volt/utils/event_counter.rb +29 -0
- data/lib/volt/utils/generic_pool.rb +12 -0
- data/lib/volt/utils/modes.rb +40 -0
- data/lib/volt/utils/promise_patch.rb +66 -0
- data/lib/volt/utils/timers.rb +33 -0
- data/lib/volt/volt/users.rb +48 -5
- data/lib/volt.rb +4 -0
- data/spec/apps/kitchen_sink/Gemfile +3 -1
- data/spec/apps/kitchen_sink/app/main/config/routes.rb +9 -8
- data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +9 -0
- data/spec/apps/kitchen_sink/app/main/controllers/yield_component_controller.rb +5 -0
- data/spec/apps/kitchen_sink/app/main/views/main/cookie_test.html +1 -1
- data/spec/apps/kitchen_sink/app/main/views/main/index.html +1 -1
- data/spec/apps/kitchen_sink/app/main/views/main/main.html +2 -1
- data/spec/apps/kitchen_sink/app/main/views/main/yield.html +18 -0
- data/spec/apps/kitchen_sink/app/main/views/yield-component/index.html +4 -0
- data/spec/extra_core/logger_spec.rb +4 -2
- data/spec/integration/user_spec.rb +42 -42
- data/spec/integration/yield_spec.rb +18 -0
- data/spec/models/associations_spec.rb +37 -0
- data/spec/models/dirty_spec.rb +102 -0
- data/spec/models/model_spec.rb +64 -8
- data/spec/models/model_state_spec.rb +24 -0
- data/spec/models/permissions_spec.rb +96 -0
- data/spec/models/user_spec.rb +8 -5
- data/spec/models/user_validation_spec.rb +24 -0
- data/spec/models/validations_spec.rb +44 -5
- data/spec/models/validators/email_validator_spec.rb +109 -82
- data/spec/models/validators/format_validator_spec.rb +4 -107
- data/spec/models/validators/length_validator_spec.rb +9 -9
- data/spec/models/validators/phone_number_validator_spec.rb +60 -103
- data/spec/models/validators/shared_examples_for_validators.rb +123 -0
- data/spec/reactive/class_eventable_spec.rb +37 -0
- data/spec/reactive/computation_spec.rb +68 -3
- data/spec/reactive/dependency_spec.rb +71 -0
- data/spec/reactive/eventable_spec.rb +21 -0
- data/spec/reactive/reactive_hash_spec.rb +12 -0
- data/spec/router/routes_spec.rb +50 -50
- data/spec/server/html_parser/view_parser_spec.rb +0 -3
- data/spec/server/rack/component_paths_spec.rb +11 -0
- data/spec/server/rack/quite_common_logger_spec.rb +3 -4
- data/spec/spec_helper.rb +7 -3
- data/templates/component/config/dependencies.rb +1 -7
- data/templates/component/config/routes.rb +1 -1
- data/templates/component/controllers/main_controller.rb.tt +20 -0
- data/templates/component/views/{index → main}/index.html.tt +0 -0
- data/templates/newgem/lib/newgem.rb.tt +1 -3
- data/templates/project/app/main/config/routes.rb +3 -3
- data/templates/project/app/main/views/main/main.html.tt +4 -4
- data/templates/project/config/app.rb.tt +6 -0
- data/volt.gemspec +11 -7
- metadata +96 -42
- data/lib/volt/models/model_state.rb +0 -21
- data/templates/component/controllers/main_controller.rb +0 -18
data/lib/volt/models/model.rb
CHANGED
@@ -3,10 +3,20 @@ require 'volt/models/array_model'
|
|
3
3
|
require 'volt/models/model_helpers'
|
4
4
|
require 'volt/models/model_hash_behaviour'
|
5
5
|
require 'volt/models/validations'
|
6
|
-
require 'volt/
|
6
|
+
require 'volt/utils/modes'
|
7
|
+
require 'volt/models/state_manager'
|
8
|
+
require 'volt/models/state_helpers'
|
7
9
|
require 'volt/models/buffer'
|
8
10
|
require 'volt/models/field_helpers'
|
9
11
|
require 'volt/reactive/reactive_hash'
|
12
|
+
require 'volt/models/validators/user_validation'
|
13
|
+
require 'volt/models/dirty'
|
14
|
+
require 'volt/models/listener_tracker'
|
15
|
+
require 'volt/models/permissions'
|
16
|
+
require 'volt/models/associations'
|
17
|
+
require 'volt/reactive/class_eventable'
|
18
|
+
require 'volt/utils/event_counter'
|
19
|
+
require 'thread'
|
10
20
|
|
11
21
|
module Volt
|
12
22
|
class NilMethodCall < NoMethodError
|
@@ -21,13 +31,20 @@ module Volt
|
|
21
31
|
include ModelWrapper
|
22
32
|
include ModelHelpers
|
23
33
|
include ModelHashBehaviour
|
34
|
+
include StateManager
|
35
|
+
include StateHelpers
|
24
36
|
include Validations
|
25
|
-
include ModelState
|
26
37
|
include Buffer
|
27
38
|
include FieldHelpers
|
39
|
+
include UserValidatorHelpers
|
40
|
+
include Dirty
|
41
|
+
include ClassEventable
|
42
|
+
include Modes
|
43
|
+
include ListenerTracker
|
44
|
+
include Permissions
|
45
|
+
include Associations
|
28
46
|
|
29
|
-
attr_reader :attributes
|
30
|
-
attr_reader :parent, :path, :persistor, :options
|
47
|
+
attr_reader :attributes, :parent, :path, :persistor, :options
|
31
48
|
|
32
49
|
INVALID_FIELD_NAMES = {
|
33
50
|
:attributes => true,
|
@@ -38,17 +55,49 @@ module Volt
|
|
38
55
|
}
|
39
56
|
|
40
57
|
def initialize(attributes = {}, options = {}, initial_state = nil)
|
58
|
+
# The listener event counter keeps track of how many computations are listening on this model
|
59
|
+
@listener_event_counter = EventCounter.new(
|
60
|
+
-> { parent.try(:persistor).try(:listener_added) },
|
61
|
+
-> { parent.try(:persistor).try(:listener_removed) }
|
62
|
+
)
|
63
|
+
|
64
|
+
# The root dependency is used to track if anything is using the data from this
|
65
|
+
# model. That information is relayed to the ArrayModel so it knows when it can
|
66
|
+
# stop subscribing.
|
67
|
+
# @root_dep = Dependency.new(@listener_event_counter.method(:add), @listener_event_counter.method(:remove))
|
68
|
+
@root_dep = Dependency.new(-> { add_list }, -> { remove_list })
|
69
|
+
|
41
70
|
@deps = HashDependency.new
|
42
71
|
@size_dep = Dependency.new
|
43
72
|
self.options = options
|
44
73
|
|
45
|
-
|
74
|
+
@new = (initial_state != :loaded)
|
75
|
+
|
76
|
+
assign_attributes(attributes, true)
|
46
77
|
|
47
|
-
#
|
48
|
-
#
|
49
|
-
@
|
78
|
+
# The persistor is usually responsible for setting up the loaded_state, if
|
79
|
+
# there is no persistor, we set it to loaded
|
80
|
+
if @persistor
|
81
|
+
@persistor.loaded(initial_state)
|
82
|
+
else
|
83
|
+
change_state_to(:loaded_state, :loaded, false)
|
84
|
+
end
|
50
85
|
|
51
|
-
|
86
|
+
# Trigger the new event, pass in :new
|
87
|
+
trigger!(:new, :new)
|
88
|
+
end
|
89
|
+
|
90
|
+
def add_list
|
91
|
+
@listener_event_counter.add
|
92
|
+
end
|
93
|
+
|
94
|
+
def remove_list
|
95
|
+
@listener_event_counter.remove
|
96
|
+
end
|
97
|
+
|
98
|
+
def state_for(*args)
|
99
|
+
@root_dep.depend
|
100
|
+
super
|
52
101
|
end
|
53
102
|
|
54
103
|
# the id is stored in a field named _id, so we setup _id to proxy to this
|
@@ -60,6 +109,11 @@ module Volt
|
|
60
109
|
self.__id = val
|
61
110
|
end
|
62
111
|
|
112
|
+
# Return true if the model hasn't been saved yet
|
113
|
+
def new?
|
114
|
+
@new
|
115
|
+
end
|
116
|
+
|
63
117
|
# Update the options
|
64
118
|
def options=(options)
|
65
119
|
@options = options
|
@@ -70,31 +124,22 @@ module Volt
|
|
70
124
|
end
|
71
125
|
|
72
126
|
# Assign multiple attributes as a hash, directly.
|
73
|
-
def
|
127
|
+
def assign_attributes(attrs, initial_setup = false)
|
74
128
|
@attributes = {}
|
75
129
|
|
76
130
|
attrs = wrap_values(attrs)
|
77
131
|
|
78
132
|
if attrs
|
79
|
-
#
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
Model.nosave do
|
84
|
-
self._id = id if id
|
85
|
-
|
86
|
-
# Assign each attribute using setters
|
87
|
-
attrs.each_pair do |key, value|
|
88
|
-
if self.respond_to?(:"#{key}=")
|
89
|
-
# If a method without an underscore is defined, call that.
|
90
|
-
send(:"#{key}=", value)
|
91
|
-
else
|
92
|
-
# Otherwise, use the _ version
|
93
|
-
send(:"_#{key}=", value)
|
94
|
-
end
|
133
|
+
# When doing a mass-assign, we don't validate or save until the end.
|
134
|
+
if initial_setup
|
135
|
+
Model.no_change_tracking do
|
136
|
+
assign_all_attributes(attrs)
|
95
137
|
end
|
138
|
+
else
|
139
|
+
assign_all_attributes(attrs)
|
96
140
|
end
|
97
141
|
else
|
142
|
+
# Assign to nil
|
98
143
|
@attributes = attrs
|
99
144
|
end
|
100
145
|
|
@@ -102,18 +147,22 @@ module Volt
|
|
102
147
|
@deps.changed_all!
|
103
148
|
@deps = HashDependency.new
|
104
149
|
|
105
|
-
|
150
|
+
# Save the changes
|
151
|
+
if initial_setup
|
152
|
+
# Run initial validation
|
153
|
+
errs = Volt.in_mode?(:no_validate) ? nil : validate!
|
106
154
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
return @persistor.changed
|
155
|
+
if errs && errs.size > 0
|
156
|
+
return Promise.new.reject(errs)
|
157
|
+
else
|
158
|
+
return Promise.new.resolve(nil)
|
112
159
|
end
|
160
|
+
else
|
161
|
+
return run_changed
|
113
162
|
end
|
114
163
|
end
|
115
164
|
|
116
|
-
alias_method :
|
165
|
+
alias_method :attributes=, :assign_attributes
|
117
166
|
|
118
167
|
# Pass the comparison through
|
119
168
|
def ==(val)
|
@@ -133,14 +182,13 @@ module Volt
|
|
133
182
|
|
134
183
|
def method_missing(method_name, *args, &block)
|
135
184
|
if method_name[0] == '_'
|
136
|
-
|
137
185
|
# Remove underscore
|
138
186
|
method_name = method_name[1..-1]
|
139
187
|
if method_name[-1] == '='
|
140
188
|
# Assigning an attribute without the =
|
141
|
-
|
189
|
+
set(method_name[0..-2], args[0], &block)
|
142
190
|
else
|
143
|
-
|
191
|
+
get(method_name)
|
144
192
|
end
|
145
193
|
else
|
146
194
|
# Call on parent
|
@@ -149,19 +197,21 @@ module Volt
|
|
149
197
|
end
|
150
198
|
|
151
199
|
# Do the assignment to a model and trigger a changed event
|
152
|
-
def
|
200
|
+
def set(attribute_name, value, &block)
|
153
201
|
self.expand!
|
154
202
|
# Assign, without the =
|
155
|
-
attribute_name =
|
203
|
+
attribute_name = attribute_name.to_sym
|
156
204
|
|
157
205
|
check_valid_field_name(attribute_name)
|
158
206
|
|
159
|
-
value = args[0]
|
160
|
-
|
161
207
|
old_value = @attributes[attribute_name]
|
162
208
|
new_value = wrap_value(value, [attribute_name])
|
163
209
|
|
164
210
|
if old_value != new_value
|
211
|
+
# Track the old value, skip if we are in no_validate
|
212
|
+
attribute_will_change!(attribute_name, old_value) unless Volt.in_mode?(:no_change_tracking)
|
213
|
+
|
214
|
+
# Assign the new value
|
165
215
|
@attributes[attribute_name] = new_value
|
166
216
|
|
167
217
|
@deps.changed!(attribute_name)
|
@@ -172,13 +222,10 @@ module Volt
|
|
172
222
|
|
173
223
|
# TODO: Can we make this so it doesn't need to be handled for non store collections
|
174
224
|
# (maybe move it to persistor, though thats weird since buffers don't have a persistor)
|
175
|
-
clear_server_errors(attribute_name) if @server_errors
|
225
|
+
clear_server_errors(attribute_name) if @server_errors.present?
|
176
226
|
|
177
|
-
#
|
178
|
-
|
179
|
-
# Let the persistor know something changed
|
180
|
-
@persistor.changed(attribute_name) if @persistor
|
181
|
-
end
|
227
|
+
# Save the changes
|
228
|
+
run_changed(attribute_name) unless Volt.in_mode?(:no_change_tracking)
|
182
229
|
end
|
183
230
|
end
|
184
231
|
|
@@ -186,14 +233,14 @@ module Volt
|
|
186
233
|
# 1) a nil model, which returns a wrapped error
|
187
234
|
# 2) reading directly from attributes
|
188
235
|
# 3) trying to read a key that doesn't exist.
|
189
|
-
def
|
236
|
+
def get(attr_name)
|
190
237
|
# Reading an attribute, we may get back a nil model.
|
191
238
|
attr_name = attr_name.to_sym
|
192
239
|
|
193
240
|
check_valid_field_name(attr_name)
|
194
241
|
|
195
|
-
# Track
|
196
|
-
|
242
|
+
# Track that something is listening
|
243
|
+
@root_dep.depend
|
197
244
|
|
198
245
|
# See if the value is in attributes
|
199
246
|
if @attributes && @attributes.key?(attr_name)
|
@@ -223,6 +270,10 @@ module Volt
|
|
223
270
|
end
|
224
271
|
end
|
225
272
|
|
273
|
+
def respond_to_missing?(method_name, include_private=false)
|
274
|
+
method_name.to_s.start_with?('_') || super
|
275
|
+
end
|
276
|
+
|
226
277
|
# Get a new model, make it easy to override
|
227
278
|
def read_new_model(method_name)
|
228
279
|
if @persistor && @persistor.respond_to?(:read_new_model)
|
@@ -238,13 +289,13 @@ module Volt
|
|
238
289
|
end
|
239
290
|
|
240
291
|
def new_model(attributes, options)
|
241
|
-
class_at_path(options[:path]).new(attributes, options)
|
292
|
+
Volt::Model.class_at_path(options[:path]).new(attributes, options)
|
242
293
|
end
|
243
294
|
|
244
295
|
def new_array_model(attributes, options)
|
245
296
|
# Start with an empty query
|
246
297
|
options = options.dup
|
247
|
-
options[:query] =
|
298
|
+
options[:query] = []
|
248
299
|
|
249
300
|
ArrayModel.new(attributes, options)
|
250
301
|
end
|
@@ -287,29 +338,43 @@ module Volt
|
|
287
338
|
end
|
288
339
|
|
289
340
|
def inspect
|
290
|
-
|
341
|
+
Computation.run_without_tracking do
|
342
|
+
str = "<#{self.class}:#{object_id}"
|
343
|
+
|
344
|
+
# Get path, loaded_state, and persistor, but cache in local var
|
345
|
+
path = self.path
|
346
|
+
str += " path:#{path}" if path
|
347
|
+
|
348
|
+
loaded_state = self.loaded_state
|
349
|
+
str += " state:#{loaded_state}" if loaded_state
|
350
|
+
|
351
|
+
persistor = self.persistor
|
352
|
+
# str += " persistor:#{persistor.inspect}" if persistor
|
353
|
+
str += " #{attributes.inspect}>"
|
354
|
+
|
355
|
+
str
|
356
|
+
end
|
291
357
|
end
|
292
358
|
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
359
|
+
def destroy
|
360
|
+
if parent
|
361
|
+
result = parent.delete(self)
|
362
|
+
|
363
|
+
# Wrap result in a promise if it isn't one
|
364
|
+
return Promise.new.then { result }
|
365
|
+
else
|
366
|
+
fail "Model does not have a parent and cannot be deleted."
|
298
367
|
end
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
ensure
|
306
|
-
Thread.current['nosave'] = previous
|
307
|
-
end
|
368
|
+
end
|
369
|
+
|
370
|
+
# Setup run mode helpers
|
371
|
+
[:no_save, :no_validate, :no_change_tracking].each do |method_name|
|
372
|
+
define_singleton_method(method_name) do |&block|
|
373
|
+
Volt.run_in_mode(method_name, &block)
|
308
374
|
end
|
309
375
|
end
|
310
376
|
|
311
377
|
private
|
312
|
-
|
313
378
|
# Volt provides a few access methods to get more data about the model,
|
314
379
|
# we want to prevent these from being assigned or accessed through
|
315
380
|
# underscore methods.
|
@@ -320,8 +385,14 @@ module Volt
|
|
320
385
|
end
|
321
386
|
|
322
387
|
def setup_buffer(model)
|
323
|
-
|
324
|
-
|
388
|
+
Volt::Model.no_validate do
|
389
|
+
model.assign_attributes(attributes, true)
|
390
|
+
end
|
391
|
+
|
392
|
+
model.change_state_to(:loaded_state, :loaded)
|
393
|
+
|
394
|
+
# Set new to the same as the main model the buffer is from
|
395
|
+
model.instance_variable_set('@new', @new)
|
325
396
|
end
|
326
397
|
|
327
398
|
# Takes the persistor if there is one and
|
@@ -330,5 +401,78 @@ module Volt
|
|
330
401
|
@persistor = persistor.new(self)
|
331
402
|
end
|
332
403
|
end
|
404
|
+
|
405
|
+
# Used internally from other methods that assign all attributes
|
406
|
+
def assign_all_attributes(attrs)
|
407
|
+
# Assign each attribute using setters
|
408
|
+
attrs.each_pair do |key, value|
|
409
|
+
key = key.to_sym
|
410
|
+
|
411
|
+
# Track the change, since assign_all_attributes runs with no_change_tracking
|
412
|
+
# attribute_will_change!(key, @attributes[key])
|
413
|
+
|
414
|
+
if self.respond_to?(:"#{key}=")
|
415
|
+
# If a method without an underscore is defined, call that.
|
416
|
+
send(:"#{key}=", value)
|
417
|
+
else
|
418
|
+
# Otherwise, use the _ version
|
419
|
+
send(:"_#{key}=", value)
|
420
|
+
end
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
# Called when something in the model changes. Saves
|
425
|
+
# the model if there is a persistor, and changes the
|
426
|
+
# model to not be new.
|
427
|
+
#
|
428
|
+
# @return [Promise|nil] a promise for when the save is
|
429
|
+
# complete
|
430
|
+
def run_changed(attribute_name=nil)
|
431
|
+
result = nil
|
432
|
+
|
433
|
+
# no_validate mode should only be used internally. no_validate mode is a
|
434
|
+
# performance optimization that prevents validation from running after each
|
435
|
+
# change when assigning multile attributes.
|
436
|
+
unless Volt.in_mode?(:no_validate)
|
437
|
+
# Run the validations for all fields
|
438
|
+
validate!
|
439
|
+
|
440
|
+
# Buffers are allowed to be in an invalid state
|
441
|
+
unless buffer?
|
442
|
+
# First check that all local validations pass
|
443
|
+
if error_in_changed_attributes?
|
444
|
+
# Some errors are present, revert changes
|
445
|
+
revert_changes!
|
446
|
+
|
447
|
+
# After we revert, we need to validate again to get the error messages back
|
448
|
+
# TODO: Could probably cache the previous errors.
|
449
|
+
errs = validate!
|
450
|
+
|
451
|
+
result = Promise.new.reject(errs)
|
452
|
+
else
|
453
|
+
# No errors, tell the persistor to handle the change (usually save)
|
454
|
+
|
455
|
+
# Don't save right now if we're in a nosave block
|
456
|
+
unless Volt.in_mode?(:no_save)
|
457
|
+
# the changed method on a persistor should return a promise that will
|
458
|
+
# be resolved when the save is complete, or fail with a hash of errors.
|
459
|
+
if @persistor
|
460
|
+
result = @persistor.changed(attribute_name)
|
461
|
+
else
|
462
|
+
result = Promise.new.resolve(nil)
|
463
|
+
end
|
464
|
+
|
465
|
+
# Saved, no longer new
|
466
|
+
@new = false
|
467
|
+
|
468
|
+
# Clear the change tracking
|
469
|
+
clear_tracked_changes!
|
470
|
+
end
|
471
|
+
end
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
return result
|
476
|
+
end
|
333
477
|
end
|
334
478
|
end
|
@@ -21,29 +21,39 @@ module Volt
|
|
21
21
|
@persistor.event_removed(event, last, last_for_event) if @persistor
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
24
|
+
module ClassMethods
|
25
|
+
# Gets the class for a model at the specified path.
|
26
|
+
def class_at_path(path)
|
27
|
+
if path
|
28
|
+
begin
|
29
|
+
# remove the _ and then singularize
|
30
|
+
if path.last == :[]
|
31
|
+
index = -2
|
32
|
+
else
|
33
|
+
index = -1
|
34
|
+
end
|
35
|
+
|
36
|
+
klass_name = path[index].singularize.camelize
|
34
37
|
|
35
|
-
|
38
|
+
# Lookup the class
|
39
|
+
klass = Object.const_get(klass_name)
|
36
40
|
|
37
|
-
|
38
|
-
|
39
|
-
|
41
|
+
# Use it if it is a model
|
42
|
+
klass = Model unless klass < Model
|
43
|
+
rescue NameError => e
|
44
|
+
# Ignore exception, just means the model isn't defined
|
45
|
+
klass = Model
|
46
|
+
end
|
47
|
+
else
|
40
48
|
klass = Model
|
41
49
|
end
|
42
|
-
|
43
|
-
klass
|
50
|
+
|
51
|
+
klass
|
44
52
|
end
|
53
|
+
end
|
45
54
|
|
46
|
-
|
55
|
+
def self.included(base)
|
56
|
+
base.send :extend, ClassMethods
|
47
57
|
end
|
48
58
|
end
|
49
59
|
end
|