volt 0.9.6 → 0.9.7.pre2
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 +2 -1
- data/CHANGELOG.md +26 -0
- data/Gemfile +6 -1
- data/README.md +2 -0
- data/Rakefile +1 -0
- data/app/volt/models/active_volt_instance.rb +8 -6
- data/app/volt/models/user.rb +11 -2
- data/app/volt/models/volt_app_property.rb +8 -0
- data/app/volt/tasks/query_tasks.rb +23 -31
- data/app/volt/tasks/store_tasks.rb +2 -2
- data/app/volt/tasks/volt_admin_tasks.rb +24 -0
- data/docs/UPGRADE_GUIDE.md +6 -0
- data/lib/volt.rb +19 -12
- data/lib/volt/boot.rb +1 -0
- data/lib/volt/cli.rb +19 -8
- data/lib/volt/cli/console.rb +0 -1
- data/lib/volt/cli/generators.rb +14 -3
- data/lib/volt/cli/migrate.rb +26 -0
- data/lib/volt/config.rb +17 -4
- data/lib/volt/controllers/http_controller.rb +12 -0
- data/lib/volt/data_stores/base_adaptor_client.rb +2 -2
- data/lib/volt/data_stores/base_adaptor_server.rb +2 -0
- data/lib/volt/data_stores/data_store.rb +20 -14
- data/lib/volt/extra_core/class.rb +28 -14
- data/lib/volt/extra_core/hash.rb +5 -0
- data/lib/volt/extra_core/string.rb +3 -1
- data/lib/volt/helpers/time.rb +9 -43
- data/lib/volt/helpers/time/calculations.rb +204 -0
- data/lib/volt/helpers/time/distance.rb +63 -0
- data/lib/volt/helpers/time/duration.rb +71 -0
- data/lib/volt/helpers/time/local_calculations.rb +49 -0
- data/lib/volt/helpers/time/local_volt_time.rb +23 -0
- data/lib/volt/helpers/time/numeric.rb +59 -0
- data/lib/volt/helpers/time/volt_time.rb +170 -0
- data/lib/volt/models.rb +5 -0
- data/lib/volt/models/array_model.rb +33 -6
- data/lib/volt/models/associations.rb +146 -23
- data/lib/volt/models/buffer.rb +38 -41
- data/lib/volt/models/cursor.rb +15 -0
- data/lib/volt/models/errors.rb +11 -0
- data/lib/volt/models/field_helpers.rb +108 -68
- data/lib/volt/models/helpers/array_model.rb +4 -0
- data/lib/volt/models/helpers/base.rb +8 -1
- data/lib/volt/models/helpers/change_helpers.rb +31 -12
- data/lib/volt/models/helpers/defaults.rb +15 -0
- data/lib/volt/models/location.rb +20 -6
- data/lib/volt/models/migrations/migration.rb +23 -0
- data/lib/volt/models/migrations/migration_runner.rb +146 -0
- data/lib/volt/models/model.rb +38 -1
- data/lib/volt/models/permissions.rb +8 -1
- data/lib/volt/models/persistors/array_store.rb +87 -8
- data/lib/volt/models/persistors/base.rb +19 -0
- data/lib/volt/models/persistors/model_store.rb +1 -1
- data/lib/volt/models/persistors/page.rb +4 -1
- data/lib/volt/models/persistors/query/query_identifier.rb +102 -0
- data/lib/volt/models/persistors/query/query_listener.rb +57 -12
- data/lib/volt/models/root_models/root_models.rb +19 -0
- data/lib/volt/models/url.rb +11 -2
- data/lib/volt/models/validations/validations.rb +5 -2
- data/lib/volt/models/validators/type_validator.rb +11 -0
- data/lib/volt/models/validators/unique_validator.rb +2 -2
- data/lib/volt/page/bindings/attribute_binding.rb +23 -1
- data/lib/volt/page/targets/attribute_section.rb +7 -0
- data/lib/volt/page/targets/binding_document/component_node.rb +44 -18
- data/lib/volt/page/targets/binding_document/tag_node.rb +41 -0
- data/lib/volt/page/tasks.rb +16 -8
- data/lib/volt/queries/live_query.rb +109 -0
- data/lib/volt/queries/live_query_pool.rb +58 -0
- data/lib/volt/queries/live_subquery.rb +0 -0
- data/lib/volt/queries/query_association_splitter.rb +31 -0
- data/lib/volt/queries/query_diff.rb +100 -0
- data/lib/volt/queries/query_runner.rb +110 -0
- data/lib/volt/queries/query_subscription.rb +80 -0
- data/lib/volt/queries/query_subscription_pool.rb +37 -0
- data/lib/volt/reactive/eventable.rb +8 -0
- data/lib/volt/reactive/reactive_array.rb +0 -4
- data/lib/volt/router/routes.rb +81 -31
- data/lib/volt/server/message_bus/base_message_bus.rb +9 -3
- data/lib/volt/server/message_bus/peer_to_peer.rb +6 -6
- data/lib/volt/server/message_bus/peer_to_peer/server_tracker.rb +1 -1
- data/lib/volt/server/middleware/default_middleware_stack.rb +12 -8
- data/lib/volt/server/rack/component_paths.rb +31 -4
- data/lib/volt/server/rack/http_content_types.rb +62 -0
- data/lib/volt/server/rack/http_resource.rb +1 -1
- data/lib/volt/server/rack/index_files.rb +8 -1
- data/lib/volt/server/rack/opal_files.rb +16 -1
- data/lib/volt/server/rack/sprockets_helpers_setup.rb +32 -1
- data/lib/volt/server/socket_connection_handler.rb +16 -7
- data/lib/volt/server/template_handlers/sprockets_component_handler.rb +5 -3
- data/lib/volt/spec/capybara.rb +4 -3
- data/lib/volt/spec/setup.rb +5 -0
- data/lib/volt/tasks/dispatcher.rb +3 -1
- data/lib/volt/utils/data_transformer.rb +4 -4
- data/lib/volt/utils/ejson.rb +19 -6
- data/lib/volt/utils/promise_extensions.rb +1 -1
- data/lib/volt/utils/time_opal_patch.rb +749 -0
- data/lib/volt/utils/time_patch.rb +11 -4
- data/lib/volt/version.rb +1 -1
- data/lib/volt/volt/app.rb +19 -11
- data/lib/volt/volt/properties.rb +24 -0
- data/lib/volt/volt/server_setup/app.rb +30 -7
- data/lib/volt/volt/users.rb +15 -3
- data/spec/apps/kitchen_sink/Gemfile +5 -1
- data/spec/apps/kitchen_sink/app/main/config/routes.rb +1 -0
- data/spec/apps/kitchen_sink/app/main/controllers/save_controller.rb +1 -1
- data/spec/apps/kitchen_sink/app/main/controllers/server/simple_http_controller.rb +4 -0
- data/spec/apps/kitchen_sink/app/main/controllers/todos_controller.rb +4 -2
- data/spec/apps/kitchen_sink/app/main/models/post.rb +0 -1
- data/spec/apps/kitchen_sink/app/main/models/todo.rb +4 -0
- data/spec/apps/kitchen_sink/app/main/views/mailers/reset_password.html +10 -0
- data/spec/apps/kitchen_sink/app/main/views/todos/index.html +2 -0
- data/spec/apps/kitchen_sink/config/app.rb +2 -0
- data/spec/apps/migrations/config/db/migrations/1445111704_migration1.rb +7 -0
- data/spec/apps/migrations/config/db/migrations/1445113517_migration2.rb +7 -0
- data/spec/apps/migrations/config/db/migrations/1445115200_migration3.rb +7 -0
- data/spec/extra_core/class_spec.rb +10 -0
- data/spec/helpers/distance_spec.rb +35 -0
- data/spec/helpers/duration_spec.rb +160 -0
- data/spec/helpers/volt_time_spec.rb +275 -0
- data/spec/integration/callbacks_spec.rb +2 -1
- data/spec/integration/http_endpoints_spec.rb +4 -0
- data/spec/integration/save_spec.rb +1 -1
- data/spec/integration/todos_spec.rb +7 -5
- data/spec/models/array_model_spec.rb +17 -3
- data/spec/models/associations_spec.rb +48 -1
- data/spec/models/field_helpers_spec.rb +7 -3
- data/spec/models/migrations/migration_runner_spec.rb +69 -0
- data/spec/models/model_spec.rb +42 -8
- data/spec/models/permissions_spec.rb +20 -8
- data/spec/models/persistors/array_store_spec.rb +18 -0
- data/spec/models/persistors/page_spec.rb +15 -10
- data/spec/models/persistors/store_spec.rb +13 -3
- data/spec/models/url_spec.rb +4 -3
- data/spec/models/user_spec.rb +6 -3
- data/spec/models/user_validation_spec.rb +3 -3
- data/spec/models/validations_spec.rb +4 -0
- data/spec/models/validators/block_validations_spec.rb +9 -5
- data/spec/models/validators/email_validator_spec.rb +2 -0
- data/spec/models/validators/lifecycle_callbacks_spec.rb +86 -0
- data/spec/models/validators/unique_validator_spec.rb +1 -0
- data/spec/page/path_string_renderer_spec.rb +5 -0
- data/spec/queries/live_query_spec.rb +16 -0
- data/spec/queries/query_association_splitter_spec.rb +14 -0
- data/spec/queries/query_diff_spec.rb +132 -0
- data/spec/queries/query_identifier_spec.rb +98 -0
- data/spec/queries/query_runner_spec.rb +63 -0
- data/spec/queries/query_tracker_spec.rb +141 -0
- data/spec/router/routes_spec.rb +52 -21
- data/spec/server/middleware/rack_content_types_spec.rb +78 -0
- data/spec/server/rack/asset_files_spec.rb +38 -30
- data/spec/spec_helper.rb +8 -0
- data/spec/utils/ejson_spec.rb +9 -8
- data/spec/utils/ejson_volt_time_spec.rb +65 -0
- data/templates/migration/migration.rb.tt +9 -0
- data/templates/newgem/gitignore.tt +1 -0
- data/templates/project/Gemfile.tt +19 -2
- data/templates/project/README.md.tt +6 -1
- data/templates/project/app/main/config/dependencies.rb +6 -0
- data/templates/project/config/app.rb.tt +18 -4
- data/volt.gemspec +2 -2
- metadata +73 -16
- data/app/volt/tasks/live_query/live_query_pool.rb +0 -48
- data/app/volt/tasks/live_query/query_tracker.rb +0 -92
- data/spec/tasks/live_query_spec.rb +0 -18
- data/spec/tasks/query_tasks.rb +0 -7
- data/spec/tasks/query_tracker_spec.rb +0 -145
data/lib/volt/models/model.rb
CHANGED
|
@@ -13,6 +13,7 @@ require 'volt/models/validators/user_validation'
|
|
|
13
13
|
require 'volt/models/helpers/dirty'
|
|
14
14
|
require 'volt/models/helpers/listener_tracker'
|
|
15
15
|
require 'volt/models/helpers/change_helpers'
|
|
16
|
+
require 'volt/models/helpers/defaults'
|
|
16
17
|
require 'volt/models/permissions'
|
|
17
18
|
require 'volt/models/associations'
|
|
18
19
|
require 'volt/reactive/class_eventable'
|
|
@@ -43,6 +44,7 @@ module Volt
|
|
|
43
44
|
include FieldHelpers
|
|
44
45
|
include UserValidatorHelpers
|
|
45
46
|
include Models::Helpers::Dirty
|
|
47
|
+
include Models::Helpers::Defaults
|
|
46
48
|
include ClassEventable
|
|
47
49
|
include Modes
|
|
48
50
|
include Models::Helpers::ListenerTracker
|
|
@@ -93,6 +95,8 @@ module Volt
|
|
|
93
95
|
change_state_to(:loaded_state, initial_state || :loaded, false)
|
|
94
96
|
end
|
|
95
97
|
|
|
98
|
+
setup_defaults
|
|
99
|
+
|
|
96
100
|
# Trigger the new event, pass in :new
|
|
97
101
|
trigger!(:new, :new)
|
|
98
102
|
end
|
|
@@ -288,6 +292,10 @@ module Volt
|
|
|
288
292
|
opts = @options.merge(parent: self, path: path + [method_name])
|
|
289
293
|
|
|
290
294
|
if method_name.plural?
|
|
295
|
+
if self.class != Volt::Model
|
|
296
|
+
opts[:associate] = self.class.collection_name
|
|
297
|
+
end
|
|
298
|
+
|
|
291
299
|
return new_array_model([], opts)
|
|
292
300
|
else
|
|
293
301
|
return new_model({}, opts)
|
|
@@ -306,12 +314,27 @@ module Volt
|
|
|
306
314
|
options = options.dup
|
|
307
315
|
options[:query] = []
|
|
308
316
|
|
|
317
|
+
check_require_model_class(options)
|
|
318
|
+
|
|
309
319
|
Volt::ArrayModel.class_at_path(options[:path]).new(attributes, options)
|
|
310
320
|
end
|
|
311
321
|
|
|
322
|
+
# For collections that don't allow on the fly models, we check to see if
|
|
323
|
+
# a model class has been defined.
|
|
324
|
+
def check_require_model_class(options)
|
|
325
|
+
model_klass = Volt::Model.class_at_path(options[:path])
|
|
326
|
+
|
|
327
|
+
if model_klass == Volt::Model && @persistor && !@persistor.on_the_fly_collections?
|
|
328
|
+
raise "The `#{root.repo_name}` repo (at `#{options[:path].join('.')}`) does not allow on the fly collections, create a class for the model before using it."
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
312
332
|
def inspect
|
|
313
333
|
Computation.run_without_tracking do
|
|
314
334
|
str = "#<#{self.class}"
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
str += ":(buffer)" if buffer?
|
|
315
338
|
# str += ":#{object_id}"
|
|
316
339
|
|
|
317
340
|
# First, select all of the non-ArrayModel values
|
|
@@ -319,7 +342,7 @@ module Volt
|
|
|
319
342
|
|
|
320
343
|
# Show the :id first, then sort the rest of the attributes
|
|
321
344
|
id = attrs.delete(:id)
|
|
322
|
-
id = id[0..3] + '..' + id[-4..-1] if id
|
|
345
|
+
id = id[0..3] + '..' + id[-4..-1] if id && id.size > 5
|
|
323
346
|
|
|
324
347
|
attrs = attrs.sort
|
|
325
348
|
attrs.insert(0, [:id, id]) if id
|
|
@@ -354,6 +377,20 @@ module Volt
|
|
|
354
377
|
to_h.to_json
|
|
355
378
|
end
|
|
356
379
|
|
|
380
|
+
# return the collection name for this model class
|
|
381
|
+
def self.collection_name
|
|
382
|
+
name = @collection_name || self.name
|
|
383
|
+
if name
|
|
384
|
+
name.underscore.pluralize.to_sym
|
|
385
|
+
else
|
|
386
|
+
raise "Class does not have a name, assign one with collection_name or use a named class."
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
def self.set_collection_name(collection_name)
|
|
391
|
+
@collection_name = collection_name.to_s
|
|
392
|
+
end
|
|
393
|
+
|
|
357
394
|
# Update tries to update the model and returns
|
|
358
395
|
def update(attrs)
|
|
359
396
|
old_attrs = @attributes.dup
|
|
@@ -14,7 +14,7 @@ module Volt
|
|
|
14
14
|
belongs_to key.to_s.gsub(pattern, '')
|
|
15
15
|
else
|
|
16
16
|
raise "You tried to auto associate a model using #{key}, but #{key} "\
|
|
17
|
-
|
|
17
|
+
"does not end in `_id`"
|
|
18
18
|
end # When the model is created, assign it the user_id (if the user is logged in)
|
|
19
19
|
on(:new) do
|
|
20
20
|
# Only assign the user_id if there isn't already one and the user is logged in.
|
|
@@ -173,6 +173,13 @@ module Volt
|
|
|
173
173
|
res.each do |key, value|
|
|
174
174
|
if value.is_a?(Model)
|
|
175
175
|
value = value.filtered_attributes
|
|
176
|
+
elsif value.is_a?(ArrayModel)
|
|
177
|
+
new_val = []
|
|
178
|
+
value.all.each do |sub_model|
|
|
179
|
+
new_val << sub_model.filtered_attributes.sync
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
value = new_val
|
|
176
183
|
end
|
|
177
184
|
keys << key
|
|
178
185
|
values << value
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
require 'volt/models/persistors/store'
|
|
2
2
|
require 'volt/models/persistors/store_state'
|
|
3
3
|
require 'volt/models/persistors/query/query_listener_pool'
|
|
4
|
+
require 'volt/models/persistors/query/query_identifier'
|
|
4
5
|
require 'volt/utils/timers'
|
|
5
6
|
|
|
6
7
|
module Volt
|
|
7
8
|
module Persistors
|
|
8
9
|
class ArrayStore < Store
|
|
9
10
|
include StoreState
|
|
10
|
-
|
|
11
|
+
include ReactiveAccessors
|
|
11
12
|
|
|
12
13
|
@@query_pool = QueryListenerPool.new
|
|
13
14
|
|
|
@@ -72,6 +73,7 @@ module Volt
|
|
|
72
73
|
|
|
73
74
|
# Called by child models to track their listeners
|
|
74
75
|
def listener_removed
|
|
76
|
+
# puts "LIST REMOVE: #{inspect}"
|
|
75
77
|
@listener_event_counter.remove
|
|
76
78
|
end
|
|
77
79
|
|
|
@@ -137,7 +139,7 @@ module Volt
|
|
|
137
139
|
if parent && !@model.is_a?(Cursor) && (attrs = parent.attributes) && attrs[:id]
|
|
138
140
|
query = query.dup
|
|
139
141
|
|
|
140
|
-
query << [:
|
|
142
|
+
query << [:where, { :"#{@model.path[-3].singularize}_id" => attrs[:id] }]
|
|
141
143
|
end
|
|
142
144
|
end
|
|
143
145
|
|
|
@@ -157,16 +159,89 @@ module Volt
|
|
|
157
159
|
# This will then be passed to the backend to run the query.
|
|
158
160
|
#
|
|
159
161
|
# @return [Cursor] a new cursor
|
|
160
|
-
def add_query_part(*args)
|
|
162
|
+
def add_query_part(*args, &block)
|
|
161
163
|
opts = @model.options
|
|
162
164
|
query = opts[:query] ? opts[:query].deep_clone : []
|
|
163
|
-
|
|
165
|
+
|
|
166
|
+
if args[0] == :where && block
|
|
167
|
+
# Change the query name to show that we were passed a block
|
|
168
|
+
args[0] = :where_with_block
|
|
169
|
+
# Block was passed to where query, pass in the QueryIdentifier
|
|
170
|
+
result = block.call(QueryIdentifier.new)
|
|
171
|
+
|
|
172
|
+
query << [*args, result.to_query]
|
|
173
|
+
else
|
|
174
|
+
query << args
|
|
175
|
+
end
|
|
164
176
|
|
|
165
177
|
# Make a new opts hash with changed query
|
|
166
178
|
opts = opts.merge(query: query)
|
|
167
179
|
Cursor.new([], opts)
|
|
168
180
|
end
|
|
169
181
|
|
|
182
|
+
# When doing things like .count, we get back a value, not an Array of
|
|
183
|
+
# Models. To handle this, the updates from the server just resolve the
|
|
184
|
+
# value directly.
|
|
185
|
+
def resolve_value(val)
|
|
186
|
+
if @resolve_promises
|
|
187
|
+
# Resolve all waiting promises
|
|
188
|
+
resolve_promises.each do |promise|
|
|
189
|
+
promise.resolve(val)
|
|
190
|
+
end
|
|
191
|
+
else
|
|
192
|
+
# Only invalidate to generate a new promise if the previous one is
|
|
193
|
+
# already realized.
|
|
194
|
+
resolve_dep.changed!
|
|
195
|
+
@resolve_dep = nil
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# @root_dep.changed!
|
|
199
|
+
|
|
200
|
+
@model.value = val
|
|
201
|
+
@resolve_promises = nil
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def value
|
|
205
|
+
# proc do |comp|
|
|
206
|
+
@root_dep.depend
|
|
207
|
+
resolve_dep.depend
|
|
208
|
+
# comp.stop
|
|
209
|
+
# end.watch!
|
|
210
|
+
unless Computation.current
|
|
211
|
+
# Load the data now manually not running inside of a computation.
|
|
212
|
+
load_data
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
if @value.respond_to?(:has_value?) && @model.has_value? && (resval = self.resolved_value)
|
|
216
|
+
# Value is already there, return a resolved promise
|
|
217
|
+
return Promise.new.resolve(resval)
|
|
218
|
+
else
|
|
219
|
+
# Otherwise generate a new promise and queue it for resolving
|
|
220
|
+
promise = Promise.new
|
|
221
|
+
resolve_promises << promise
|
|
222
|
+
|
|
223
|
+
return promise
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Return a promise for resolving a value from something like .count
|
|
228
|
+
def resolve_promises
|
|
229
|
+
@resolve_promises ||= []
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def resolve_dep
|
|
234
|
+
@resolve_dep ||= Dependency.new
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def resolved_value
|
|
238
|
+
if @value.respond_to?(:has_value?) && @model.has_value?
|
|
239
|
+
@model.value
|
|
240
|
+
else
|
|
241
|
+
nil
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
170
245
|
# Call a method on the model once the model is loaded. Return a promise
|
|
171
246
|
# that will resolve when the model is loaded
|
|
172
247
|
def run_once_loaded
|
|
@@ -191,6 +266,12 @@ module Volt
|
|
|
191
266
|
# passed block will be passed to the promises then. Then will be passed the model.
|
|
192
267
|
def fetch(&block)
|
|
193
268
|
Volt.logger.warn('Deprication warning: in 0.9.3.pre4, all query methods on store now return Promises, so you can juse use .all or .first instead of .fetch')
|
|
269
|
+
begin
|
|
270
|
+
raise
|
|
271
|
+
rescue => e
|
|
272
|
+
puts e.inspect
|
|
273
|
+
puts e.backtrace.join("\n")
|
|
274
|
+
end
|
|
194
275
|
promise = Promise.new
|
|
195
276
|
|
|
196
277
|
# Run the block after resolve if a block is passed in
|
|
@@ -211,10 +292,6 @@ module Volt
|
|
|
211
292
|
promise
|
|
212
293
|
end
|
|
213
294
|
|
|
214
|
-
# Alias then for now
|
|
215
|
-
# TODO: Deprecate
|
|
216
|
-
alias_method :then, :fetch
|
|
217
|
-
|
|
218
295
|
# Called from backend when an item is added
|
|
219
296
|
def add(index, data)
|
|
220
297
|
$loading_models = true
|
|
@@ -293,6 +370,8 @@ module Volt
|
|
|
293
370
|
def async?
|
|
294
371
|
true
|
|
295
372
|
end
|
|
373
|
+
|
|
374
|
+
private
|
|
296
375
|
end
|
|
297
376
|
end
|
|
298
377
|
end
|
|
@@ -46,6 +46,25 @@ module Volt
|
|
|
46
46
|
false
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
+
# Stores can allow or not allow "on the fly collections", this means a
|
|
50
|
+
# collection will be created when saved to, even if the collection did
|
|
51
|
+
# not exist previously and was not created with a class.
|
|
52
|
+
#
|
|
53
|
+
# If we allow on the fly collections, we could do the following:
|
|
54
|
+
# store._somethings
|
|
55
|
+
# ^ where no Something model class had been created
|
|
56
|
+
def on_the_fly_collections?
|
|
57
|
+
true
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Count gets passed down to the persistor, since different persistors
|
|
61
|
+
# may implement it different ways. #reactive_count just aliases normal
|
|
62
|
+
# count for now.
|
|
63
|
+
def count(*args, &block)
|
|
64
|
+
@model.reactive_count(*args, &block)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
|
|
49
68
|
# Find the root for this model
|
|
50
69
|
def root_model
|
|
51
70
|
node = @model
|
|
@@ -8,7 +8,7 @@ module Volt
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def where(query)
|
|
11
|
-
@model.select do |model|
|
|
11
|
+
result = @model.select do |model|
|
|
12
12
|
# Run through each key in the query and make sure the value matches.
|
|
13
13
|
# We use .all? because once one fails to match, we can return false,
|
|
14
14
|
# because it wouldn't match as a whole.
|
|
@@ -16,6 +16,9 @@ module Volt
|
|
|
16
16
|
model.get(key) == value
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
|
+
|
|
20
|
+
options = @model.options.merge(parent: @model, path: @model.path)
|
|
21
|
+
@model.new_array_model(result, options)
|
|
19
22
|
end
|
|
20
23
|
end
|
|
21
24
|
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Volt allows you to pass blocks to .where queries. The block will receive a
|
|
2
|
+
# single argument, which sumulates the row in the database. On the row, you
|
|
3
|
+
# can call comparitors and use logical and/or (| and &) to build up queries.
|
|
4
|
+
#
|
|
5
|
+
# The QueryIdentifier class stores all methods called on it, and uses the
|
|
6
|
+
# QueryOp class to store logical operations. You can call .to_query on the
|
|
7
|
+
# result to get an array representing the query. This can then be normalized
|
|
8
|
+
# and called on the backend to answer the query.
|
|
9
|
+
|
|
10
|
+
module Volt
|
|
11
|
+
class QueryIdentifier
|
|
12
|
+
def initialize(from='ident')
|
|
13
|
+
@from = from
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def method_missing(method_name, *args)
|
|
17
|
+
method_name = method_name.to_s
|
|
18
|
+
# c for call
|
|
19
|
+
|
|
20
|
+
args.map do |arg|
|
|
21
|
+
if arg.is_a?(Array)
|
|
22
|
+
# Escape the array
|
|
23
|
+
arg.unshift('a')
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
QueryIdentifier.new(['c', @from, method_name, *args])
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def ==(val)
|
|
31
|
+
method_missing(:==, val)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# def !
|
|
35
|
+
# method_missing(:!)
|
|
36
|
+
# end
|
|
37
|
+
|
|
38
|
+
# Special methods in ruby don't respond to method_missing, so setup methods
|
|
39
|
+
# for them.
|
|
40
|
+
# We need < and > until https://github.com/opal/opal/issues/1137 is fixed.
|
|
41
|
+
['=~', '!~', '&', '|', '<', '>', '<=', '>='].each do |op|
|
|
42
|
+
define_method(op) do |val|
|
|
43
|
+
__op(op, val)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# And for unary ops
|
|
48
|
+
def ~
|
|
49
|
+
__op('~')
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def +(val)
|
|
53
|
+
method_missing(:+, val)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def __op(*args)
|
|
57
|
+
QueryOp.new(['c', self, *args])
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def coerce(other)
|
|
61
|
+
unless other.is_a?(Volt::QueryIdentifier)
|
|
62
|
+
other = Volt::QueryIdentifier.new(other)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
[other, self]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def inspect
|
|
69
|
+
"(#{@from.join(' ')})"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def to_s
|
|
73
|
+
inspect
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def to_query
|
|
77
|
+
if @from.is_a?(Array)
|
|
78
|
+
@from.map do |val|
|
|
79
|
+
if val.is_a?(QueryIdentifier)
|
|
80
|
+
val.to_query
|
|
81
|
+
else
|
|
82
|
+
val
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
else
|
|
86
|
+
@from
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
class QueryOp < QueryIdentifier
|
|
92
|
+
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# def where
|
|
97
|
+
# ident = Volt::QueryIdentifier.new
|
|
98
|
+
# yield(ident)
|
|
99
|
+
# end
|
|
100
|
+
|
|
101
|
+
# a = where {|v| ((v.name < 10) | (v.name > 5)) & ~(v.name == 'Ryan') }
|
|
102
|
+
# b = where {|v| 1 < v.name }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
module Volt
|
|
2
|
-
# The query listener is what gets notified
|
|
2
|
+
# The query listener is what gets notified from the backend when the results from
|
|
3
3
|
# a query have changed. It then will make the necessary changes to any ArrayStore's
|
|
4
4
|
# to get them to display the new data.
|
|
5
5
|
class QueryListener
|
|
@@ -12,6 +12,7 @@ module Volt
|
|
|
12
12
|
|
|
13
13
|
@collection = collection
|
|
14
14
|
@query = query
|
|
15
|
+
@data = []
|
|
15
16
|
|
|
16
17
|
@listening = false
|
|
17
18
|
end
|
|
@@ -23,6 +24,9 @@ module Volt
|
|
|
23
24
|
QueryTasks.add_listener(@collection, @query).then do |ret|
|
|
24
25
|
results, errors = ret
|
|
25
26
|
|
|
27
|
+
# Store the data
|
|
28
|
+
@data = results
|
|
29
|
+
|
|
26
30
|
# When the initial data comes back, add it into the stores.
|
|
27
31
|
@stores.dup.each do |store|
|
|
28
32
|
# Clear if there are existing items
|
|
@@ -30,11 +34,15 @@ module Volt
|
|
|
30
34
|
store.model.clear if store.model.size > 0
|
|
31
35
|
end
|
|
32
36
|
|
|
33
|
-
results.
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
if results.is_a?(Array)
|
|
38
|
+
results.each_with_index do |data, index|
|
|
39
|
+
store.add(index, data)
|
|
40
|
+
end
|
|
36
41
|
|
|
37
|
-
|
|
42
|
+
store.model.change_state_to(:loaded_state, :loaded)
|
|
43
|
+
else
|
|
44
|
+
store.resolve_value(results)
|
|
45
|
+
end
|
|
38
46
|
|
|
39
47
|
# if Volt.server?
|
|
40
48
|
# store.model.change_state_to(:loaded_state, :dirty)
|
|
@@ -42,7 +50,7 @@ module Volt
|
|
|
42
50
|
end
|
|
43
51
|
end.fail do |err|
|
|
44
52
|
# TODO: need to make it so we can re-raise out of this promise
|
|
45
|
-
msg = "
|
|
53
|
+
msg = "Database Query Error: #{err.inspect}"
|
|
46
54
|
msg += "\n#{err.backtrace.join("\n")}" if err.respond_to?(:backtrace)
|
|
47
55
|
Volt.logger.error(msg)
|
|
48
56
|
|
|
@@ -64,13 +72,20 @@ module Volt
|
|
|
64
72
|
# copy the data from the existing model.
|
|
65
73
|
store.model.clear
|
|
66
74
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
store.
|
|
75
|
+
first_store = @stores.first
|
|
76
|
+
|
|
77
|
+
if (resolved_value = first_store.resolved_value)
|
|
78
|
+
store.resolve_value(resolved_value)
|
|
79
|
+
else
|
|
80
|
+
# Get an existing store to copy data from
|
|
81
|
+
first_store_model = first_store.model
|
|
82
|
+
first_store_model.each_with_index do |item, index|
|
|
83
|
+
store.add(index, item.to_h)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
store.model.change_state_to(:loaded_state, first_store_model.loaded_state)
|
|
71
87
|
end
|
|
72
88
|
|
|
73
|
-
store.model.change_state_to(:loaded_state, first_store_model.loaded_state)
|
|
74
89
|
else
|
|
75
90
|
# First time we've added a store, setup the listener and get
|
|
76
91
|
# the initial data.
|
|
@@ -94,7 +109,30 @@ module Volt
|
|
|
94
109
|
end
|
|
95
110
|
end
|
|
96
111
|
|
|
97
|
-
def
|
|
112
|
+
def updated(diff)
|
|
113
|
+
diff.each do |op|
|
|
114
|
+
operation, arg1, arg2 = op
|
|
115
|
+
|
|
116
|
+
case operation
|
|
117
|
+
when 'i'
|
|
118
|
+
# insert
|
|
119
|
+
inserted(arg1, arg2)
|
|
120
|
+
when 'r'
|
|
121
|
+
# remove
|
|
122
|
+
removed([arg1])
|
|
123
|
+
when 'c'
|
|
124
|
+
# changed
|
|
125
|
+
changed(arg1, arg2)
|
|
126
|
+
when 'm'
|
|
127
|
+
# move
|
|
128
|
+
when 'u'
|
|
129
|
+
# single value, update it
|
|
130
|
+
update(arg1)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def inserted(index, data)
|
|
98
136
|
@stores.each do |store|
|
|
99
137
|
store.add(index, data)
|
|
100
138
|
end
|
|
@@ -106,7 +144,14 @@ module Volt
|
|
|
106
144
|
end
|
|
107
145
|
end
|
|
108
146
|
|
|
147
|
+
def update(data)
|
|
148
|
+
@stores.each do |store|
|
|
149
|
+
store.resolve_value(data)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
109
153
|
def changed(model_id, data)
|
|
154
|
+
puts "CH: #{model_id.inspect} - #{data.inspect}"
|
|
110
155
|
$loading_models = true
|
|
111
156
|
Persistors::ModelStore.changed(model_id, data)
|
|
112
157
|
$loading_models = false
|