volt 0.9.3.pre2 → 0.9.3.pre3

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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/app/volt/tasks/query_tasks.rb +0 -7
  4. data/app/volt/tasks/store_tasks.rb +0 -6
  5. data/docs/UPGRADE_GUIDE.md +2 -0
  6. data/lib/volt/cli/asset_compile.rb +2 -2
  7. data/lib/volt/cli/console.rb +21 -0
  8. data/lib/volt/config.rb +0 -10
  9. data/lib/volt/controllers/collection_helpers.rb +18 -0
  10. data/lib/volt/controllers/model_controller.rb +2 -12
  11. data/lib/volt/extra_core/object.rb +19 -0
  12. data/lib/volt/models.rb +14 -9
  13. data/lib/volt/models/array_model.rb +62 -22
  14. data/lib/volt/models/associations.rb +16 -1
  15. data/lib/volt/models/model.rb +27 -15
  16. data/lib/volt/models/model_helpers/model_helpers.rb +29 -0
  17. data/lib/volt/models/permissions.rb +15 -4
  18. data/lib/volt/models/persistors/array_store.rb +40 -0
  19. data/lib/volt/models/persistors/model_store.rb +2 -2
  20. data/lib/volt/models/persistors/query/query_listener.rb +3 -1
  21. data/lib/volt/models/persistors/store.rb +2 -1
  22. data/lib/volt/models/root_models/root_models.rb +31 -0
  23. data/lib/volt/models/root_models/store_root.rb +36 -0
  24. data/lib/volt/models/validators/unique_validator.rb +1 -1
  25. data/lib/volt/page/bindings/each_binding.rb +56 -47
  26. data/lib/volt/page/page.rb +5 -5
  27. data/lib/volt/reactive/reactive_array.rb +9 -6
  28. data/lib/volt/server.rb +2 -2
  29. data/lib/volt/server/component_templates.rb +7 -4
  30. data/lib/volt/server/message_bus/message_encoder.rb +9 -1
  31. data/lib/volt/server/rack/component_code.rb +8 -1
  32. data/lib/volt/server/rack/index_files.rb +5 -2
  33. data/lib/volt/tasks/{task_handler.rb → task.rb} +6 -6
  34. data/lib/volt/utils/promise.rb +429 -0
  35. data/lib/volt/utils/promise_extensions.rb +79 -0
  36. data/lib/volt/version.rb +1 -1
  37. data/lib/volt/volt/app.rb +5 -2
  38. data/lib/volt/volt/server_setup/app.rb +28 -7
  39. data/spec/apps/kitchen_sink/app/main/controllers/server/simple_http_controller.rb +1 -1
  40. data/spec/apps/kitchen_sink/app/main/views/main/store.html +3 -0
  41. data/spec/extra_core/object_spec.rb +13 -0
  42. data/spec/integration/store_spec.rb +10 -0
  43. data/spec/models/associations_spec.rb +48 -26
  44. data/spec/models/model_spec.rb +23 -7
  45. data/spec/models/persistors/store_spec.rb +28 -0
  46. data/spec/models/validators/unique_validator_spec.rb +1 -1
  47. data/spec/spec_helper.rb +4 -1
  48. data/spec/utils/promise_extensions_spec.rb +42 -0
  49. data/templates/component/config/initializers/boot.rb +10 -0
  50. data/templates/{project/app → component/config/initializers/client}/.empty_directory +0 -0
  51. data/templates/component/config/initializers/server/.empty_directory +0 -0
  52. data/templates/newgem/app/newgem/config/initializers/client/.empty_directory +0 -0
  53. data/templates/newgem/app/newgem/config/initializers/server/.empty_directory +0 -0
  54. data/templates/project/Gemfile.tt +6 -2
  55. data/templates/project/app/main/config/initializers/boot.rb +10 -0
  56. data/templates/project/app/main/config/initializers/client/.empty_directory +0 -0
  57. data/templates/project/app/main/config/initializers/server/.empty_directory +0 -0
  58. data/templates/project/config/app.rb.tt +3 -0
  59. data/templates/project/config/initializers/client/.empty_directory +0 -0
  60. data/templates/project/config/initializers/server/.empty_directory +0 -0
  61. metadata +22 -5
  62. data/lib/volt/utils/promise_patch.rb +0 -70
data/lib/volt/server.rb CHANGED
@@ -10,7 +10,7 @@ require 'sprockets-sass'
10
10
 
11
11
  require 'volt'
12
12
  require 'volt/tasks/dispatcher'
13
- require 'volt/tasks/task_handler'
13
+ require 'volt/tasks/task'
14
14
  require 'volt/server/component_handler'
15
15
  require 'volt/server/rack/component_paths'
16
16
  require 'volt/server/rack/index_files'
@@ -133,7 +133,7 @@ module Volt
133
133
 
134
134
  # Serve the main html files from public, also figure out
135
135
  # which JS/CSS files to serve.
136
- @rack_app.use IndexFiles, @volt_app.component_paths, opal_files
136
+ @rack_app.use IndexFiles, @volt_app, @volt_app.component_paths, opal_files
137
137
 
138
138
  @rack_app.use HttpResource, @volt_app, @volt_app.router
139
139
 
@@ -1,5 +1,5 @@
1
1
  require 'volt/server/html_parser/view_parser'
2
- require 'volt/tasks/task_handler'
2
+ require 'volt/tasks/task'
3
3
 
4
4
  # Initialize with the path to a component and returns all the front-end
5
5
  # setup code (for controllers, models, views, and routes)
@@ -91,7 +91,7 @@ module Volt
91
91
 
92
92
  file_contents = File.read(view_path)
93
93
 
94
- template_calls = []
94
+ # template_calls = []
95
95
 
96
96
  # Process template if we have a handler for this file type
97
97
  if handler = ComponentTemplates.handler_for_extension(format)
@@ -112,7 +112,7 @@ module Volt
112
112
  binding_code = "{#{binding_code.join(', ')}}"
113
113
 
114
114
  code << "#{page_reference}.add_template(#{name.inspect}, #{template['html'].inspect}, #{binding_code})\n"
115
- template_calls << "template(#{name.inspect}, #{template['html'].inspect}, #{binding_code})"
115
+ # template_calls << "template(#{name.inspect}, #{template['html'].inspect}, #{binding_code})"
116
116
  end
117
117
  end
118
118
 
@@ -200,7 +200,10 @@ module Volt
200
200
  end
201
201
 
202
202
  def generate_initializers_code
203
- "\nrequire_tree '#{@component_path}/config/initializers/'\n"
203
+ code = "\nrequire_tree '#{@component_path}/config/initializers/'\n"
204
+ code << "require_tree '#{@component_path}/config/initializers/client'\n"
205
+
206
+ code
204
207
  end
205
208
 
206
209
  end
@@ -5,8 +5,16 @@ module Volt
5
5
  class MessageEncoder
6
6
  attr_reader :encrypted
7
7
  def initialize
8
+ # rbnacl is not supported on windows.
9
+ windows = Gem.win_platform?
10
+
11
+ if windows
12
+ Volt.logger.warn('Currently Message Bus encryption is not supported on windows.')
13
+ end
14
+
8
15
  # Message bus is encrypted by default
9
- @encrypted = (Volt.config.message_bus.try(:disable_encryption) != true)
16
+ disable = Volt.config.message_bus.try(:disable_encryption)
17
+ @encrypted = !windows && (disable != true)
10
18
 
11
19
  if @encrypted
12
20
  # Setup a RbNaCl simple box for handling encryption
@@ -27,7 +27,14 @@ module Volt
27
27
  end
28
28
 
29
29
  def generate_config_code
30
- "\nVolt.setup_client_config(#{Volt.config.public.to_h.inspect})\n"
30
+ # Setup Volt.config on the client
31
+ code = "\nVolt.setup_client_config(#{Volt.config.public.to_h.inspect})\n"
32
+
33
+ # Include the root initializers
34
+ code << "require_tree '#{Volt.root}/config/initializers'\n"
35
+ code << "require_tree '#{Volt.root}/config/initializers/client'\n"
36
+
37
+ code
31
38
  end
32
39
  end
33
40
  end
@@ -4,12 +4,15 @@ require 'volt/router/routes'
4
4
  # Serves the main pages
5
5
  module Volt
6
6
  class IndexFiles
7
- def initialize(app, component_paths, opal_files)
7
+ def initialize(app, volt_app, component_paths, opal_files)
8
8
  @app = app
9
+ @volt_app = volt_app
9
10
  @component_paths = component_paths
10
11
  @opal_files = opal_files
11
12
 
12
- @@router ||= Routes.new.define do
13
+ @@router = volt_app.router
14
+
15
+ @@router.define do
13
16
  # Load routes for each component
14
17
  component_paths.components.values.flatten.uniq.each do |component_path|
15
18
  routes_path = "#{component_path}/config/routes.rb"
@@ -1,3 +1,5 @@
1
+ require 'volt/controllers/collection_helpers'
2
+
1
3
  module Volt
2
4
  class Task
3
5
  if RUBY_PLATFORM == 'opal'
@@ -14,8 +16,11 @@ module Volt
14
16
  $page.tasks.call(self.name, name, meta_data, *args, &block)
15
17
  end
16
18
  else
19
+ include CollectionHelpers
20
+
17
21
  def initialize(volt_app, channel = nil, dispatcher = nil)
18
- @volt_app = volt_app
22
+ @volt_app = volt_app
23
+ @page = volt_app.page
19
24
  @channel = channel
20
25
  @dispatcher = dispatcher
21
26
  end
@@ -39,11 +44,6 @@ module Volt
39
44
  new(Volt.current_app, nil, nil).send(name, *args, &block)
40
45
  end.resolve(nil)
41
46
  end
42
-
43
- # Provide access to the store collection
44
- def store
45
- $page.store
46
- end
47
47
  end
48
48
  end
49
49
  end
@@ -0,0 +1,429 @@
1
+ # A copy of the opal 0.8 promise library. The one in 0.7.x has some bugs.
2
+
3
+ # {Promise} is used to help structure asynchronous code.
4
+ #
5
+ # It is available in the Opal standard library, and can be required in any Opal
6
+ # application:
7
+ #
8
+ # require 'promise'
9
+ #
10
+ # ## Basic Usage
11
+ #
12
+ # Promises are created and returned as objects with the assumption that they
13
+ # will eventually be resolved or rejected, but never both. A {Promise} has
14
+ # a {#then} and {#fail} method (or one of their aliases) that can be used to
15
+ # register a block that gets called once resolved or rejected.
16
+ #
17
+ # promise = Promise.new
18
+ #
19
+ # promise.then {
20
+ # puts "resolved!"
21
+ # }.fail {
22
+ # puts "rejected!"
23
+ # }
24
+ #
25
+ # # some time later
26
+ # promise.resolve
27
+ #
28
+ # # => "resolved!"
29
+ #
30
+ # It is important to remember that a promise can only be resolved or rejected
31
+ # once, so the block will only ever be called once (or not at all).
32
+ #
33
+ # ## Resolving Promises
34
+ #
35
+ # To resolve a promise, means to inform the {Promise} that it has succeeded
36
+ # or evaluated to a useful value. {#resolve} can be passed a value which is
37
+ # then passed into the block handler:
38
+ #
39
+ # def get_json
40
+ # promise = Promise.new
41
+ #
42
+ # HTTP.get("some_url") do |req|
43
+ # promise.resolve req.json
44
+ # end
45
+ #
46
+ # promise
47
+ # end
48
+ #
49
+ # get_json.then do |json|
50
+ # puts "got some JSON from server"
51
+ # end
52
+ #
53
+ # ## Rejecting Promises
54
+ #
55
+ # Promises are also designed to handle error cases, or situations where an
56
+ # outcome is not as expected. Taking the previous example, we can also pass
57
+ # a value to a {#reject} call, which passes that object to the registered
58
+ # {#fail} handler:
59
+ #
60
+ # def get_json
61
+ # promise = Promise.new
62
+ #
63
+ # HTTP.get("some_url") do |req|
64
+ # if req.ok?
65
+ # promise.resolve req.json
66
+ # else
67
+ # promise.reject req
68
+ # end
69
+ #
70
+ # promise
71
+ # end
72
+ #
73
+ # get_json.then {
74
+ # # ...
75
+ # }.fail { |req|
76
+ # puts "it went wrong: #{req.message}"
77
+ # }
78
+ #
79
+ # ## Chaining Promises
80
+ #
81
+ # Promises become even more useful when chained together. Each {#then} or
82
+ # {#fail} call returns a new {Promise} which can be used to chain more and more
83
+ # handlers together.
84
+ #
85
+ # promise.then { wait_for_something }.then { do_something_else }
86
+ #
87
+ # Rejections are propagated through the entire chain, so a "catch all" handler
88
+ # can be attached at the end of the tail:
89
+ #
90
+ # promise.then { ... }.then { ... }.fail { ... }
91
+ #
92
+ # ## Composing Promises
93
+ #
94
+ # {Promise.when} can be used to wait for more than one promise to resolve (or
95
+ # reject). Using the previous example, we could request two different json
96
+ # requests and wait for both to finish:
97
+ #
98
+ # Promise.when(get_json, get_json2).then |first, second|
99
+ # puts "got two json payloads: #{first}, #{second}"
100
+ # end
101
+ #
102
+ class Promise
103
+ def self.value(value)
104
+ new.resolve(value)
105
+ end
106
+
107
+ def self.error(value)
108
+ new.reject(value)
109
+ end
110
+
111
+ def self.when(*promises)
112
+ When.new(promises)
113
+ end
114
+
115
+ attr_reader :error, :prev, :next
116
+
117
+ def initialize(action = {})
118
+ @action = action
119
+
120
+ @realized = false
121
+ @exception = false
122
+ @value = nil
123
+ @error = nil
124
+ @delayed = false
125
+
126
+ @prev = nil
127
+ @next = nil
128
+ end
129
+
130
+ def value
131
+ if Promise === @value
132
+ @value.value
133
+ else
134
+ @value
135
+ end
136
+ end
137
+
138
+ def act?
139
+ @action.has_key?(:success) || @action.has_key?(:always)
140
+ end
141
+
142
+ def action
143
+ @action.keys
144
+ end
145
+
146
+ def exception?
147
+ @exception
148
+ end
149
+
150
+ def realized?
151
+ !!@realized
152
+ end
153
+
154
+ def resolved?
155
+ @realized == :resolve
156
+ end
157
+
158
+ def rejected?
159
+ @realized == :reject
160
+ end
161
+
162
+ def ^(promise)
163
+ promise << self
164
+ self >> promise
165
+
166
+ promise
167
+ end
168
+
169
+ def <<(promise)
170
+ @prev = promise
171
+
172
+ self
173
+ end
174
+
175
+ def >>(promise)
176
+ @next = promise
177
+
178
+ if exception?
179
+ promise.reject(@delayed[0])
180
+ elsif resolved?
181
+ promise.resolve(@delayed ? @delayed[0] : value)
182
+ elsif rejected?
183
+ if !@action.has_key?(:failure) || Promise === (@delayed ? @delayed[0] : @error)
184
+ promise.reject(@delayed ? @delayed[0] : error)
185
+ elsif promise.action.include?(:always)
186
+ promise.reject(@delayed ? @delayed[0] : error)
187
+ end
188
+ end
189
+
190
+ self
191
+ end
192
+
193
+ def resolve(value = nil)
194
+ if realized?
195
+ raise ArgumentError, 'the promise has already been realized'
196
+ end
197
+
198
+ if Promise === value
199
+ return (value << @prev) ^ self
200
+ end
201
+
202
+ begin
203
+ if block = @action[:success] || @action[:always]
204
+ value = block.call(value)
205
+ end
206
+
207
+ resolve!(value)
208
+ rescue Exception => e
209
+ exception!(e)
210
+ end
211
+
212
+ self
213
+ end
214
+
215
+ def resolve!(value)
216
+ @realized = :resolve
217
+ @value = value
218
+
219
+ if @next
220
+ @next.resolve(value)
221
+ else
222
+ @delayed = [value]
223
+ end
224
+ end
225
+
226
+ def reject(value = nil)
227
+ if realized?
228
+ raise ArgumentError, 'the promise has already been realized'
229
+ end
230
+
231
+ if Promise === value
232
+ return (value << @prev) ^ self
233
+ end
234
+
235
+ begin
236
+ if block = @action[:failure] || @action[:always]
237
+ value = block.call(value)
238
+ end
239
+
240
+ if @action.has_key?(:always)
241
+ resolve!(value)
242
+ else
243
+ reject!(value)
244
+ end
245
+ rescue Exception => e
246
+ exception!(e)
247
+ end
248
+
249
+ self
250
+ end
251
+
252
+ def reject!(value)
253
+ @realized = :reject
254
+ @error = value
255
+
256
+ if @next
257
+ @next.reject(value)
258
+ else
259
+ @delayed = [value]
260
+ end
261
+ end
262
+
263
+ def exception!(error)
264
+ @exception = true
265
+
266
+ reject!(error)
267
+ end
268
+
269
+ def then(&block)
270
+ if @next
271
+ raise ArgumentError, 'a promise has already been chained'
272
+ end
273
+
274
+ self ^ Promise.new(success: block)
275
+ end
276
+
277
+ alias do then
278
+
279
+ def fail(&block)
280
+ if @next
281
+ raise ArgumentError, 'a promise has already been chained'
282
+ end
283
+
284
+ self ^ Promise.new(failure: block)
285
+ end
286
+
287
+ alias rescue fail
288
+ alias catch fail
289
+
290
+ def always(&block)
291
+ if @next
292
+ raise ArgumentError, 'a promise has already been chained'
293
+ end
294
+
295
+ self ^ Promise.new(always: block)
296
+ end
297
+
298
+ alias finally always
299
+ alias ensure always
300
+
301
+ def trace(depth = nil, &block)
302
+ if @next
303
+ raise ArgumentError, 'a promise has already been chained'
304
+ end
305
+
306
+ self ^ Trace.new(depth, block)
307
+ end
308
+
309
+ def inspect
310
+ result = "#<#{self.class}(#{object_id})"
311
+
312
+ if @next
313
+ result += " >> #{@next.inspect}"
314
+ end
315
+
316
+ if realized?
317
+ result += ": #{(@value || @error).inspect}>"
318
+ else
319
+ result += ">"
320
+ end
321
+
322
+ result
323
+ end
324
+
325
+ class Trace < self
326
+ def self.it(promise)
327
+ current = []
328
+
329
+ if promise.act? || promise.prev.nil?
330
+ current.push(promise.value)
331
+ end
332
+
333
+ if prev = promise.prev
334
+ current.concat(it(prev))
335
+ else
336
+ current
337
+ end
338
+ end
339
+
340
+ def initialize(depth, block)
341
+ @depth = depth
342
+
343
+ super success: -> {
344
+ trace = Trace.it(self).reverse
345
+ trace.pop
346
+
347
+ if depth && depth <= trace.length
348
+ trace.shift(trace.length - depth)
349
+ end
350
+
351
+ block.call(*trace)
352
+ }
353
+ end
354
+ end
355
+
356
+ class When < self
357
+ def initialize(promises = [])
358
+ super()
359
+
360
+ @wait = []
361
+
362
+ promises.each {|promise|
363
+ wait promise
364
+ }
365
+ end
366
+
367
+ def each(&block)
368
+ raise ArgumentError, 'no block given' unless block
369
+
370
+ self.then {|values|
371
+ values.each(&block)
372
+ }
373
+ end
374
+
375
+ def collect(&block)
376
+ raise ArgumentError, 'no block given' unless block
377
+
378
+ self.then {|values|
379
+ When.new(values.map(&block))
380
+ }
381
+ end
382
+
383
+ def inject(*args, &block)
384
+ self.then {|values|
385
+ values.reduce(*args, &block)
386
+ }
387
+ end
388
+
389
+ alias map collect
390
+
391
+ alias reduce inject
392
+
393
+ def wait(promise)
394
+ unless Promise === promise
395
+ promise = Promise.value(promise)
396
+ end
397
+
398
+ if promise.act?
399
+ promise = promise.then
400
+ end
401
+
402
+ @wait << promise
403
+
404
+ promise.always {
405
+ try if @next
406
+ }
407
+
408
+ self
409
+ end
410
+
411
+ alias and wait
412
+
413
+ def >>(*)
414
+ super.tap {
415
+ try
416
+ }
417
+ end
418
+
419
+ def try
420
+ if @wait.all?(&:realized?)
421
+ if promise = @wait.find(&:rejected?)
422
+ reject(promise.error)
423
+ else
424
+ resolve(@wait.map(&:value))
425
+ end
426
+ end
427
+ end
428
+ end
429
+ end