volt 0.9.6 → 0.9.7.pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/CHANGELOG.md +26 -0
  4. data/Gemfile +6 -1
  5. data/README.md +2 -0
  6. data/Rakefile +1 -0
  7. data/app/volt/models/active_volt_instance.rb +8 -6
  8. data/app/volt/models/user.rb +11 -2
  9. data/app/volt/models/volt_app_property.rb +8 -0
  10. data/app/volt/tasks/query_tasks.rb +23 -31
  11. data/app/volt/tasks/store_tasks.rb +2 -2
  12. data/app/volt/tasks/volt_admin_tasks.rb +24 -0
  13. data/docs/UPGRADE_GUIDE.md +6 -0
  14. data/lib/volt.rb +19 -12
  15. data/lib/volt/boot.rb +1 -0
  16. data/lib/volt/cli.rb +19 -8
  17. data/lib/volt/cli/console.rb +0 -1
  18. data/lib/volt/cli/generators.rb +14 -3
  19. data/lib/volt/cli/migrate.rb +26 -0
  20. data/lib/volt/config.rb +17 -4
  21. data/lib/volt/controllers/http_controller.rb +12 -0
  22. data/lib/volt/data_stores/base_adaptor_client.rb +2 -2
  23. data/lib/volt/data_stores/base_adaptor_server.rb +2 -0
  24. data/lib/volt/data_stores/data_store.rb +20 -14
  25. data/lib/volt/extra_core/class.rb +28 -14
  26. data/lib/volt/extra_core/hash.rb +5 -0
  27. data/lib/volt/extra_core/string.rb +3 -1
  28. data/lib/volt/helpers/time.rb +9 -43
  29. data/lib/volt/helpers/time/calculations.rb +204 -0
  30. data/lib/volt/helpers/time/distance.rb +63 -0
  31. data/lib/volt/helpers/time/duration.rb +71 -0
  32. data/lib/volt/helpers/time/local_calculations.rb +49 -0
  33. data/lib/volt/helpers/time/local_volt_time.rb +23 -0
  34. data/lib/volt/helpers/time/numeric.rb +59 -0
  35. data/lib/volt/helpers/time/volt_time.rb +170 -0
  36. data/lib/volt/models.rb +5 -0
  37. data/lib/volt/models/array_model.rb +33 -6
  38. data/lib/volt/models/associations.rb +146 -23
  39. data/lib/volt/models/buffer.rb +38 -41
  40. data/lib/volt/models/cursor.rb +15 -0
  41. data/lib/volt/models/errors.rb +11 -0
  42. data/lib/volt/models/field_helpers.rb +108 -68
  43. data/lib/volt/models/helpers/array_model.rb +4 -0
  44. data/lib/volt/models/helpers/base.rb +8 -1
  45. data/lib/volt/models/helpers/change_helpers.rb +31 -12
  46. data/lib/volt/models/helpers/defaults.rb +15 -0
  47. data/lib/volt/models/location.rb +20 -6
  48. data/lib/volt/models/migrations/migration.rb +23 -0
  49. data/lib/volt/models/migrations/migration_runner.rb +146 -0
  50. data/lib/volt/models/model.rb +38 -1
  51. data/lib/volt/models/permissions.rb +8 -1
  52. data/lib/volt/models/persistors/array_store.rb +87 -8
  53. data/lib/volt/models/persistors/base.rb +19 -0
  54. data/lib/volt/models/persistors/model_store.rb +1 -1
  55. data/lib/volt/models/persistors/page.rb +4 -1
  56. data/lib/volt/models/persistors/query/query_identifier.rb +102 -0
  57. data/lib/volt/models/persistors/query/query_listener.rb +57 -12
  58. data/lib/volt/models/root_models/root_models.rb +19 -0
  59. data/lib/volt/models/url.rb +11 -2
  60. data/lib/volt/models/validations/validations.rb +5 -2
  61. data/lib/volt/models/validators/type_validator.rb +11 -0
  62. data/lib/volt/models/validators/unique_validator.rb +2 -2
  63. data/lib/volt/page/bindings/attribute_binding.rb +23 -1
  64. data/lib/volt/page/targets/attribute_section.rb +7 -0
  65. data/lib/volt/page/targets/binding_document/component_node.rb +44 -18
  66. data/lib/volt/page/targets/binding_document/tag_node.rb +41 -0
  67. data/lib/volt/page/tasks.rb +16 -8
  68. data/lib/volt/queries/live_query.rb +109 -0
  69. data/lib/volt/queries/live_query_pool.rb +58 -0
  70. data/lib/volt/queries/live_subquery.rb +0 -0
  71. data/lib/volt/queries/query_association_splitter.rb +31 -0
  72. data/lib/volt/queries/query_diff.rb +100 -0
  73. data/lib/volt/queries/query_runner.rb +110 -0
  74. data/lib/volt/queries/query_subscription.rb +80 -0
  75. data/lib/volt/queries/query_subscription_pool.rb +37 -0
  76. data/lib/volt/reactive/eventable.rb +8 -0
  77. data/lib/volt/reactive/reactive_array.rb +0 -4
  78. data/lib/volt/router/routes.rb +81 -31
  79. data/lib/volt/server/message_bus/base_message_bus.rb +9 -3
  80. data/lib/volt/server/message_bus/peer_to_peer.rb +6 -6
  81. data/lib/volt/server/message_bus/peer_to_peer/server_tracker.rb +1 -1
  82. data/lib/volt/server/middleware/default_middleware_stack.rb +12 -8
  83. data/lib/volt/server/rack/component_paths.rb +31 -4
  84. data/lib/volt/server/rack/http_content_types.rb +62 -0
  85. data/lib/volt/server/rack/http_resource.rb +1 -1
  86. data/lib/volt/server/rack/index_files.rb +8 -1
  87. data/lib/volt/server/rack/opal_files.rb +16 -1
  88. data/lib/volt/server/rack/sprockets_helpers_setup.rb +32 -1
  89. data/lib/volt/server/socket_connection_handler.rb +16 -7
  90. data/lib/volt/server/template_handlers/sprockets_component_handler.rb +5 -3
  91. data/lib/volt/spec/capybara.rb +4 -3
  92. data/lib/volt/spec/setup.rb +5 -0
  93. data/lib/volt/tasks/dispatcher.rb +3 -1
  94. data/lib/volt/utils/data_transformer.rb +4 -4
  95. data/lib/volt/utils/ejson.rb +19 -6
  96. data/lib/volt/utils/promise_extensions.rb +1 -1
  97. data/lib/volt/utils/time_opal_patch.rb +749 -0
  98. data/lib/volt/utils/time_patch.rb +11 -4
  99. data/lib/volt/version.rb +1 -1
  100. data/lib/volt/volt/app.rb +19 -11
  101. data/lib/volt/volt/properties.rb +24 -0
  102. data/lib/volt/volt/server_setup/app.rb +30 -7
  103. data/lib/volt/volt/users.rb +15 -3
  104. data/spec/apps/kitchen_sink/Gemfile +5 -1
  105. data/spec/apps/kitchen_sink/app/main/config/routes.rb +1 -0
  106. data/spec/apps/kitchen_sink/app/main/controllers/save_controller.rb +1 -1
  107. data/spec/apps/kitchen_sink/app/main/controllers/server/simple_http_controller.rb +4 -0
  108. data/spec/apps/kitchen_sink/app/main/controllers/todos_controller.rb +4 -2
  109. data/spec/apps/kitchen_sink/app/main/models/post.rb +0 -1
  110. data/spec/apps/kitchen_sink/app/main/models/todo.rb +4 -0
  111. data/spec/apps/kitchen_sink/app/main/views/mailers/reset_password.html +10 -0
  112. data/spec/apps/kitchen_sink/app/main/views/todos/index.html +2 -0
  113. data/spec/apps/kitchen_sink/config/app.rb +2 -0
  114. data/spec/apps/migrations/config/db/migrations/1445111704_migration1.rb +7 -0
  115. data/spec/apps/migrations/config/db/migrations/1445113517_migration2.rb +7 -0
  116. data/spec/apps/migrations/config/db/migrations/1445115200_migration3.rb +7 -0
  117. data/spec/extra_core/class_spec.rb +10 -0
  118. data/spec/helpers/distance_spec.rb +35 -0
  119. data/spec/helpers/duration_spec.rb +160 -0
  120. data/spec/helpers/volt_time_spec.rb +275 -0
  121. data/spec/integration/callbacks_spec.rb +2 -1
  122. data/spec/integration/http_endpoints_spec.rb +4 -0
  123. data/spec/integration/save_spec.rb +1 -1
  124. data/spec/integration/todos_spec.rb +7 -5
  125. data/spec/models/array_model_spec.rb +17 -3
  126. data/spec/models/associations_spec.rb +48 -1
  127. data/spec/models/field_helpers_spec.rb +7 -3
  128. data/spec/models/migrations/migration_runner_spec.rb +69 -0
  129. data/spec/models/model_spec.rb +42 -8
  130. data/spec/models/permissions_spec.rb +20 -8
  131. data/spec/models/persistors/array_store_spec.rb +18 -0
  132. data/spec/models/persistors/page_spec.rb +15 -10
  133. data/spec/models/persistors/store_spec.rb +13 -3
  134. data/spec/models/url_spec.rb +4 -3
  135. data/spec/models/user_spec.rb +6 -3
  136. data/spec/models/user_validation_spec.rb +3 -3
  137. data/spec/models/validations_spec.rb +4 -0
  138. data/spec/models/validators/block_validations_spec.rb +9 -5
  139. data/spec/models/validators/email_validator_spec.rb +2 -0
  140. data/spec/models/validators/lifecycle_callbacks_spec.rb +86 -0
  141. data/spec/models/validators/unique_validator_spec.rb +1 -0
  142. data/spec/page/path_string_renderer_spec.rb +5 -0
  143. data/spec/queries/live_query_spec.rb +16 -0
  144. data/spec/queries/query_association_splitter_spec.rb +14 -0
  145. data/spec/queries/query_diff_spec.rb +132 -0
  146. data/spec/queries/query_identifier_spec.rb +98 -0
  147. data/spec/queries/query_runner_spec.rb +63 -0
  148. data/spec/queries/query_tracker_spec.rb +141 -0
  149. data/spec/router/routes_spec.rb +52 -21
  150. data/spec/server/middleware/rack_content_types_spec.rb +78 -0
  151. data/spec/server/rack/asset_files_spec.rb +38 -30
  152. data/spec/spec_helper.rb +8 -0
  153. data/spec/utils/ejson_spec.rb +9 -8
  154. data/spec/utils/ejson_volt_time_spec.rb +65 -0
  155. data/templates/migration/migration.rb.tt +9 -0
  156. data/templates/newgem/gitignore.tt +1 -0
  157. data/templates/project/Gemfile.tt +19 -2
  158. data/templates/project/README.md.tt +6 -1
  159. data/templates/project/app/main/config/dependencies.rb +6 -0
  160. data/templates/project/config/app.rb.tt +18 -4
  161. data/volt.gemspec +2 -2
  162. metadata +73 -16
  163. data/app/volt/tasks/live_query/live_query_pool.rb +0 -48
  164. data/app/volt/tasks/live_query/query_tracker.rb +0 -92
  165. data/spec/tasks/live_query_spec.rb +0 -18
  166. data/spec/tasks/query_tasks.rb +0 -7
  167. data/spec/tasks/query_tracker_spec.rb +0 -145
@@ -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
- "does not end in `_id`"
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 << [:find, { :"#{@model.path[-3].singularize}_id" => attrs[:id] }]
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
- query << args
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
@@ -159,7 +159,7 @@ module Volt
159
159
 
160
160
  if RUBY_PLATFORM != 'opal'
161
161
  def db
162
- @@db ||= Volt::DataStore.fetch
162
+ @@db ||= Volt::DataStore.fetch(Volt.current_app)
163
163
  end
164
164
 
165
165
  # Do the actual writing of data to the database, only runs on the backend.
@@ -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 on the backend when the results from
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.each do |index, data|
34
- store.add(index, data)
35
- end
37
+ if results.is_a?(Array)
38
+ results.each_with_index do |data, index|
39
+ store.add(index, data)
40
+ end
36
41
 
37
- store.model.change_state_to(:loaded_state, :loaded)
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 = "Error adding listener: #{err.inspect}"
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
- # Get an existing store to copy data from
68
- first_store_model = @stores.first.model
69
- first_store_model.each_with_index do |item, index|
70
- store.add(index, item.to_h)
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 added(index, data)
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