volt 0.6.5 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Readme.md +47 -40
- data/VERSION +1 -1
- data/app/volt/controllers/notices_controller.rb +3 -3
- data/app/volt/tasks/live_query/data_store.rb +2 -2
- data/app/volt/tasks/live_query/live_query.rb +20 -20
- data/app/volt/tasks/live_query/live_query_pool.rb +6 -6
- data/app/volt/tasks/live_query/query_tracker.rb +15 -15
- data/app/volt/tasks/query_tasks.rb +13 -13
- data/app/volt/tasks/store_tasks.rb +7 -7
- data/app/volt/views/notices/index.html +17 -18
- data/lib/volt/assets/test.rb +2 -2
- data/lib/volt/benchmark/benchmark.rb +25 -23
- data/lib/volt/cli/asset_compile.rb +11 -0
- data/lib/volt/cli/new_gem.rb +16 -16
- data/lib/volt/cli.rb +14 -12
- data/lib/volt/console.rb +5 -6
- data/lib/volt/controllers/model_controller.rb +18 -18
- data/lib/volt/extra_core/array.rb +4 -4
- data/lib/volt/extra_core/hash.rb +3 -3
- data/lib/volt/extra_core/object.rb +6 -6
- data/lib/volt/extra_core/string.rb +6 -6
- data/lib/volt/extra_core/symbol.rb +5 -5
- data/lib/volt/extra_core/time.rb +4 -4
- data/lib/volt/extra_core/true_false.rb +6 -6
- data/lib/volt/extra_core/try.rb +9 -9
- data/lib/volt/models/array_model.rb +26 -26
- data/lib/volt/models/model.rb +35 -35
- data/lib/volt/models/model_hash_behaviour.rb +15 -15
- data/lib/volt/models/model_helpers.rb +8 -8
- data/lib/volt/models/model_wrapper.rb +6 -6
- data/lib/volt/models/persistors/array_store.rb +36 -36
- data/lib/volt/models/persistors/base.rb +6 -6
- data/lib/volt/models/persistors/flash.rb +5 -5
- data/lib/volt/models/persistors/model_identity_map.rb +2 -2
- data/lib/volt/models/persistors/model_store.rb +22 -22
- data/lib/volt/models/persistors/params.rb +3 -3
- data/lib/volt/models/persistors/query/query_listener.rb +14 -14
- data/lib/volt/models/persistors/query/query_listener_pool.rb +2 -2
- data/lib/volt/models/persistors/store.rb +8 -8
- data/lib/volt/models/persistors/store_factory.rb +2 -2
- data/lib/volt/models/url.rb +37 -37
- data/lib/volt/page/bindings/attribute_binding.rb +14 -14
- data/lib/volt/page/bindings/base_binding.rb +9 -9
- data/lib/volt/page/bindings/component_binding.rb +7 -7
- data/lib/volt/page/bindings/content_binding.rb +3 -3
- data/lib/volt/page/bindings/each_binding.rb +13 -13
- data/lib/volt/page/bindings/event_binding.rb +4 -4
- data/lib/volt/page/bindings/if_binding.rb +12 -12
- data/lib/volt/page/bindings/template_binding.rb +30 -30
- data/lib/volt/page/channel.rb +19 -19
- data/lib/volt/page/channel_stub.rb +6 -6
- data/lib/volt/page/document.rb +2 -2
- data/lib/volt/page/document_events.rb +4 -4
- data/lib/volt/page/draw_cycle.rb +3 -3
- data/lib/volt/page/memory_test.rb +6 -6
- data/lib/volt/page/page.rb +19 -19
- data/lib/volt/page/reactive_template.rb +9 -9
- data/lib/volt/page/sub_context.rb +5 -5
- data/lib/volt/page/targets/attribute_section.rb +9 -9
- data/lib/volt/page/targets/attribute_target.rb +3 -3
- data/lib/volt/page/targets/base_section.rb +2 -2
- data/lib/volt/page/targets/binding_document/component_node.rb +23 -23
- data/lib/volt/page/targets/binding_document/html_node.rb +2 -2
- data/lib/volt/page/targets/dom_section.rb +40 -38
- data/lib/volt/page/targets/dom_target.rb +2 -2
- data/lib/volt/page/tasks.rb +12 -12
- data/lib/volt/page/template_renderer.rb +4 -4
- data/lib/volt/page/url_tracker.rb +6 -6
- data/lib/volt/reactive/array_extensions.rb +2 -2
- data/lib/volt/reactive/destructive_methods.rb +5 -5
- data/lib/volt/reactive/event_chain.rb +25 -25
- data/lib/volt/reactive/events.rb +33 -33
- data/lib/volt/reactive/object_tracker.rb +21 -21
- data/lib/volt/reactive/object_tracking.rb +2 -2
- data/lib/volt/reactive/reactive_array.rb +57 -57
- data/lib/volt/reactive/reactive_tags.rb +16 -16
- data/lib/volt/reactive/reactive_value.rb +72 -72
- data/lib/volt/reactive/string_extensions.rb +3 -3
- data/lib/volt/router/routes.rb +22 -23
- data/lib/volt/server/component_handler.rb +5 -5
- data/lib/volt/server/component_templates.rb +14 -11
- data/lib/volt/server/html_parser/attribute_scope.rb +116 -0
- data/lib/volt/server/html_parser/each_scope.rb +18 -0
- data/lib/volt/server/html_parser/if_view_scope.rb +71 -0
- data/lib/volt/server/html_parser/sandlebars_parser.rb +219 -0
- data/lib/volt/server/html_parser/textarea_scope.rb +31 -0
- data/lib/volt/server/html_parser/view_handler.rb +82 -0
- data/lib/volt/server/html_parser/view_parser.rb +23 -0
- data/lib/volt/server/html_parser/view_scope.rb +145 -0
- data/lib/volt/server/rack/asset_files.rb +17 -17
- data/lib/volt/server/rack/component_paths.rb +18 -18
- data/lib/volt/server/rack/index_files.rb +8 -8
- data/lib/volt/server/rack/opal_files.rb +11 -11
- data/lib/volt/server/socket_connection_handler.rb +13 -13
- data/lib/volt/server/socket_connection_handler_stub.rb +2 -2
- data/lib/volt/server.rb +18 -18
- data/lib/volt/tasks/dispatcher.rb +5 -5
- data/lib/volt/utils/ejson.rb +2 -2
- data/lib/volt/utils/generic_counting_pool.rb +8 -8
- data/lib/volt/utils/generic_pool.rb +16 -16
- data/lib/volt/volt/environment.rb +4 -4
- data/lib/volt.rb +6 -6
- data/spec/integration/test_integration_spec.rb +2 -2
- data/spec/models/event_chain_spec.rb +38 -38
- data/spec/models/model_spec.rb +128 -128
- data/spec/models/old_model_spec.rb +17 -17
- data/spec/models/persistors/params_spec.rb +3 -3
- data/spec/models/persistors/store_spec.rb +7 -7
- data/spec/models/reactive_array_spec.rb +82 -82
- data/spec/models/reactive_generator_spec.rb +11 -11
- data/spec/models/reactive_tags_spec.rb +6 -6
- data/spec/models/reactive_value_spec.rb +70 -70
- data/spec/models/store_spec.rb +4 -4
- data/spec/models/string_extensions_spec.rb +13 -13
- data/spec/page/bindings/content_binding_spec.rb +6 -6
- data/spec/page/sub_context_spec.rb +1 -1
- data/spec/router/routes_spec.rb +3 -3
- data/spec/server/html_parser/sample_page.html +595 -0
- data/spec/server/html_parser/sandlebars_parser_spec.rb +192 -0
- data/spec/server/html_parser/view_parser_spec.rb +286 -0
- data/spec/server/rack/asset_files_spec.rb +6 -6
- data/spec/server/rack/component_paths_spec.rb +5 -5
- data/spec/spec_helper.rb +4 -5
- data/spec/store/mongo_spec.rb +3 -3
- data/spec/tasks/live_query_spec.rb +6 -6
- data/spec/tasks/query_tasks.rb +4 -4
- data/spec/tasks/query_tracker_spec.rb +20 -20
- data/spec/templates/targets/binding_document/component_node_spec.rb +4 -4
- data/spec/templates/template_binding_spec.rb +28 -28
- data/spec/utils/generic_counting_pool_spec.rb +5 -5
- data/spec/utils/generic_pool_spec.rb +14 -14
- data/templates/newgem/app/newgem/views/index/index.html +1 -2
- data/templates/project/app/home/config/dependencies.rb +1 -1
- data/templates/project/app/home/controllers/index_controller.rb +1 -1
- data/templates/project/app/home/views/index/about.html +4 -6
- data/templates/project/app/home/views/index/home.html +4 -5
- data/templates/project/app/home/views/index/index.html +8 -9
- data/templates/project/spec/spec_helper.rb +1 -1
- metadata +17 -8
- data/lib/volt/server/binding_setup.rb +0 -2
- data/lib/volt/server/if_binding_setup.rb +0 -31
- data/lib/volt/server/scope.rb +0 -43
- data/lib/volt/server/template_parser.rb +0 -453
- data/spec/server/template_parser_spec.rb +0 -50
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7a2b3f88f704f2e2438faffed5fc2039fa2ba4c2
|
4
|
+
data.tar.gz: 5030c30dbc621f0ceb393b8fad31abdab9cff17c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f88181450bad8a2aa0552a0bc52e69f5c0c1c5901e8f2f44edacf80b9f98dd92ca87345f90b51615955451af41d8089010f2e7e5ee5d99ccc9ed8789efd2cb8
|
7
|
+
data.tar.gz: e4b3a917332a57e3d524129c817787246b76ba78333b305c942c2fccf393d49981b6e4a0c28e48aaf60f4e91212dc5a869341e031cb1c28fb9b44cc4e2453609
|
data/Readme.md
CHANGED
@@ -2,15 +2,17 @@
|
|
2
2
|
[![Code Climate](https://codeclimate.com/github/voltrb/volt.png)](https://codeclimate.com/github/voltrb/volt)
|
3
3
|
[![Build Status](https://travis-ci.org/voltrb/volt.png?branch=master)](https://travis-ci.org/voltrb/volt)
|
4
4
|
|
5
|
-
|
5
|
+
---
|
6
|
+
> NOTE: VOLT IS STILL IN DEVELOPMENT, DON'T USE IT FOR ANYTHING SERIOUS YET
|
7
|
+
---
|
6
8
|
|
7
|
-
|
9
|
+
# Volt
|
8
10
|
|
9
11
|
Volt is a ruby web framework where your ruby code runs on both the server and the client (via [opal](https://github.com/opal/opal).) The dom automatically update as the user interacts with the page. Page state can be stored in the url, if the user hits a url directly, the HTML will first be rendered on the server for faster load times and easier indexing by search engines.
|
10
12
|
|
11
13
|
Instead of syncing data between the client and server via HTTP, volt uses a persistent connection between the client and server. When data updated on one client, it is updated in the database and any other listening clients. (With almost no setup code needed)
|
12
14
|
|
13
|
-
Pages HTML is written in a handlebars like template language. Volt uses data flow/reactive programming to automatically and intellegently propigate changes to the
|
15
|
+
Pages HTML is written in a handlebars like template language. Volt uses data flow/reactive programming to automatically and intellegently propigate changes to the DOM (or anything other code wanting to know when a value updates) When something in the DOM changes, Volt intelligently updates only the nodes that need to be changed.
|
14
16
|
|
15
17
|
See a quick demo video here: [http://www.youtube.com/watch?v=j0vFIRMzarI](http://www.youtube.com/watch?v=j0vFIRMzarI)
|
16
18
|
|
@@ -51,7 +53,7 @@ To get started, install volt:
|
|
51
53
|
Then create a new project:
|
52
54
|
|
53
55
|
volt new project_name
|
54
|
-
|
56
|
+
|
55
57
|
This will setup a basic project. Now lets run the server.
|
56
58
|
|
57
59
|
volt server
|
@@ -126,17 +128,17 @@ When you call a method on a ReactiveValue, you get back a new reactive value tha
|
|
126
128
|
a = ReactiveValue.new(1)
|
127
129
|
a.reactive?
|
128
130
|
# => true
|
129
|
-
|
131
|
+
|
130
132
|
a.cur
|
131
133
|
# => 1
|
132
|
-
|
134
|
+
|
133
135
|
b = a + 5
|
134
136
|
b.reactive?
|
135
137
|
# => true
|
136
|
-
|
138
|
+
|
137
139
|
b.cur
|
138
140
|
# => 6
|
139
|
-
|
141
|
+
|
140
142
|
a.cur = 2
|
141
143
|
b.cur
|
142
144
|
# => 7
|
@@ -159,14 +161,14 @@ These events propigate to any reactive value's created off of a reactive value.
|
|
159
161
|
a = ReactiveValue.new(1)
|
160
162
|
b = a + 5
|
161
163
|
b.on('changed') { puts "B changed" }
|
162
|
-
|
164
|
+
|
163
165
|
a.trigger!('changed')
|
164
166
|
# => B changed
|
165
167
|
```
|
166
168
|
|
167
169
|
This event flow lets us know when an object has changed, so we can update everything that depended on that object.
|
168
170
|
|
169
|
-
Lastly, we can also pass in other reactive value's as arguments to methods on a reactive value. The dependencies will be tracked for both and events will propigate down from both. (Also, note that doing
|
171
|
+
Lastly, we can also pass in other reactive value's as arguments to methods on a reactive value. The dependencies will be tracked for both and events will propigate down from both. (Also, note that doing `.cur =` to update the current value triggers a "changed" event.)
|
170
172
|
|
171
173
|
```ruby
|
172
174
|
a = ReactiveValue.new(1)
|
@@ -176,11 +178,11 @@ Lastly, we can also pass in other reactive value's as arguments to methods on a
|
|
176
178
|
a.on('changed') { puts "A changed" }
|
177
179
|
b.on('changed') { puts "B changed" }
|
178
180
|
c.on('changed') { puts "C changed" }
|
179
|
-
|
181
|
+
|
180
182
|
a.cur = 3
|
181
183
|
# => A changed
|
182
184
|
# => C changed
|
183
|
-
|
185
|
+
|
184
186
|
b.cur = 5
|
185
187
|
# => B changed
|
186
188
|
# => C changed
|
@@ -208,14 +210,14 @@ Because a method on a reactive value always returns another reactive value, and
|
|
208
210
|
|
209
211
|
One common place we use a truthy check is in setting up default values with || (logical or) Volt provides a convience method that does the same thing .or, but works with ReactiveValue's.
|
210
212
|
|
211
|
-
Instead of
|
213
|
+
Instead of
|
212
214
|
|
213
215
|
```ruby
|
214
216
|
a || b
|
215
217
|
```
|
216
218
|
|
217
219
|
Simply use:
|
218
|
-
|
220
|
+
|
219
221
|
```ruby
|
220
222
|
a.or(b)
|
221
223
|
```
|
@@ -255,7 +257,7 @@ An if binding lets you provide basic flow control.
|
|
255
257
|
{#if _some_check?}
|
256
258
|
<p>render this</p>
|
257
259
|
{/}
|
258
|
-
|
260
|
+
|
259
261
|
Blocks are closed with a {/}
|
260
262
|
|
261
263
|
When the #if binding is rendered, it will run the ruby code after #if. If the code is true it will render the code below. Again, if the returned value is reactive, it will update as that value changes.
|
@@ -305,7 +307,7 @@ Bindings can also be placed inside of attributes.
|
|
305
307
|
There are some special features provided to make for elements work as "two way bindings"
|
306
308
|
|
307
309
|
<input type="text" value="{_name}" />
|
308
|
-
|
310
|
+
|
309
311
|
In the example above, if _name changes, the field will update and if the field is updated, _name will be changed.
|
310
312
|
|
311
313
|
<input type="checkbox" checked="{_checked}" />
|
@@ -327,7 +329,7 @@ Volt comes with many built-in models, one is called 'page'. If you call #page o
|
|
327
329
|
page._name
|
328
330
|
# => @'Ryan'
|
329
331
|
```
|
330
|
-
|
332
|
+
|
331
333
|
Models act like a hash that you can access with getters and setters that start with an _ Prefixing with an underscore makes sure we don't accidentally try to call a method that doesn't exist and get back nil. There is no need to define which fields a model has, they act similar to a hash, but with a shorter access and assign syntax.
|
332
334
|
|
333
335
|
Models also let you nest data:
|
@@ -336,11 +338,11 @@ Models also let you nest data:
|
|
336
338
|
page._settings._color = 'blue'
|
337
339
|
page._settings._color
|
338
340
|
# => @'blue'
|
339
|
-
|
341
|
+
|
340
342
|
page._settings
|
341
343
|
# => @#<Model:_settings {:_color=>"blue"}>
|
342
344
|
```
|
343
|
-
|
345
|
+
|
344
346
|
Nested data is automatically setup when assigned. In this case, page._settings is a model that is part of the page model.
|
345
347
|
|
346
348
|
You can also append to a model if its not defined yet.
|
@@ -349,7 +351,7 @@ You can also append to a model if its not defined yet.
|
|
349
351
|
page._items << 'item 1'
|
350
352
|
page._items
|
351
353
|
# => @#<ArrayModel ["item 1", "item 2"]>
|
352
|
-
|
354
|
+
|
353
355
|
page._items[0]
|
354
356
|
# => @"item 1"
|
355
357
|
```
|
@@ -381,11 +383,11 @@ Models trigger events when their data is updated. Currently models emit three e
|
|
381
383
|
|
382
384
|
```ruby
|
383
385
|
model = Model.new
|
384
|
-
|
386
|
+
|
385
387
|
model._name.on('changed') { puts 'name changed' }
|
386
388
|
model._name = 'Ryan'
|
387
389
|
# => name changed
|
388
|
-
|
390
|
+
|
389
391
|
model._items.on('added') { puts 'item added' }
|
390
392
|
model._items << 1
|
391
393
|
# => item added
|
@@ -408,15 +410,15 @@ For convience, when placing a hash inside of another model, it is automatically
|
|
408
410
|
twitter: 'http://www.twitter.com/ryanstout',
|
409
411
|
dribbble: 'http://dribbble.com/ryanstout'
|
410
412
|
}
|
411
|
-
|
413
|
+
|
412
414
|
user._name
|
413
415
|
# => "Ryan"
|
414
416
|
user._profiles._twitter
|
415
417
|
# => "http://www.twitter.com/ryanstout"
|
416
418
|
user._profiles.class
|
417
419
|
# => Model
|
418
|
-
```
|
419
|
-
|
420
|
+
```
|
421
|
+
|
420
422
|
Models are accessed differently from hashes. Instead of using model[:symbol] to access, you call a method model.method_name. This provides a dynamic unified store where setters and getters can be added without changing any access code.
|
421
423
|
|
422
424
|
You can get a ruby hash back out by calling .to_h on a Model.
|
@@ -430,11 +432,11 @@ Arrays inside of models are automatically converted to an instance of ArrayModel
|
|
430
432
|
model._items << {_name: 'item 1'}
|
431
433
|
model._items.class
|
432
434
|
# => ArrayModel
|
433
|
-
|
435
|
+
|
434
436
|
model._items[0].class
|
435
437
|
# => Model
|
436
438
|
model._items[0]
|
437
|
-
```
|
439
|
+
```
|
438
440
|
|
439
441
|
|
440
442
|
To convert a Model or an ArrayModel back to a normal hash, call .to_h or .to_a respectively. To convert them to a JavaScript Object (for passing to some JavaScript code), call .to_n (to native).
|
@@ -446,10 +448,10 @@ To convert a Model or an ArrayModel back to a normal hash, call .to_h or .to_a r
|
|
446
448
|
twitter: 'http://www.twitter.com/ryanstout',
|
447
449
|
dribbble: 'http://dribbble.com/ryanstout'
|
448
450
|
}
|
449
|
-
|
451
|
+
|
450
452
|
user._profiles.to_h
|
451
453
|
# => {twitter: 'http://www.twitter.com/ryanstout', dribbble: 'http://dribbble.com/ryanstout'}
|
452
|
-
|
454
|
+
|
453
455
|
items = ArrayModel.new([1,2,3,4])
|
454
456
|
items
|
455
457
|
```
|
@@ -465,13 +467,13 @@ A controller can be any class in Volt, however it is common to have that class i
|
|
465
467
|
```ruby
|
466
468
|
class TodosController < ModelController
|
467
469
|
model :page
|
468
|
-
|
470
|
+
|
469
471
|
# ...
|
470
472
|
end
|
471
473
|
```
|
472
474
|
|
473
475
|
This can also be done at anytime on the controller instance:
|
474
|
-
|
476
|
+
|
475
477
|
```ruby
|
476
478
|
class TodosController < ModelController
|
477
479
|
def initialize
|
@@ -479,7 +481,7 @@ This can also be done at anytime on the controller instance:
|
|
479
481
|
end
|
480
482
|
end
|
481
483
|
```
|
482
|
-
|
484
|
+
|
483
485
|
See the [provided collections](#provided-collections) section for a list of the available collection models.
|
484
486
|
|
485
487
|
You can also provide your own object to model.
|
@@ -518,7 +520,7 @@ Sometimes you may need to include an externally hosted JS file from a component.
|
|
518
520
|
javascript_file 'http://code.jquery.com/jquery-2.0.3.min.js'
|
519
521
|
css_file '//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css'
|
520
522
|
```
|
521
|
-
|
523
|
+
|
522
524
|
Note above though that jquery and bootstrap are currently included by default. Using javascript_file and css_file will be mixed in with your component assets at the correct locations according to the order they occur in the dependencies.rb files.
|
523
525
|
|
524
526
|
## Assets
|
@@ -561,13 +563,13 @@ Volt automatically places ```<:volt:notices />``` into views. This shows notice
|
|
561
563
|
|
562
564
|
As part of the notices component explained above, you can append messages to any collection on the flash model.
|
563
565
|
|
564
|
-
Each collection represents a different type of "flash". Common examples are ```_notices, _warnings, and _errors``` Using different collections allows you to change how you want the flash displayed. For example, you might want ```_notices``` and ```_errors``` to show with different colors.
|
566
|
+
Each collection represents a different type of "flash". Common examples are ```_notices, _warnings, and _errors``` Using different collections allows you to change how you want the flash displayed. For example, you might want ```_notices``` and ```_errors``` to show with different colors.
|
565
567
|
|
566
568
|
```ruby
|
567
569
|
flash._notices << "message to flash"
|
568
570
|
```
|
569
571
|
|
570
|
-
These messages will show for 5 seconds, then disappear (both from the screen and the collection).
|
572
|
+
These messages will show for 5 seconds, then disappear (both from the screen and the collection).
|
571
573
|
|
572
574
|
# Controls
|
573
575
|
|
@@ -578,7 +580,7 @@ To render a control, simply use a tag like so:
|
|
578
580
|
```html
|
579
581
|
<:control-name />
|
580
582
|
```
|
581
|
-
|
583
|
+
|
582
584
|
or
|
583
585
|
|
584
586
|
```html
|
@@ -602,7 +604,7 @@ To find the control's views and optional controller, Volt will search the follow
|
|
602
604
|
Each part is explained below:
|
603
605
|
|
604
606
|
1. section
|
605
|
-
Views are composed of sections. Sections start with a ```<:SectionName>```
|
607
|
+
Views are composed of sections. Sections start with a ```<:SectionName>``` and are not closed. Volt will look first for a section in the same view.
|
606
608
|
|
607
609
|
2. views
|
608
610
|
Next Volt will look for a view file that with the control name. If found, it will render the body section of that view.
|
@@ -621,7 +623,7 @@ When you create a control, you can also specify multiple parts of the search pat
|
|
621
623
|
```html
|
622
624
|
<:blog:comments />
|
623
625
|
```
|
624
|
-
|
626
|
+
|
625
627
|
The above would search the following:
|
626
628
|
|
627
629
|
| Component | View Folder | View File | Section |
|
@@ -659,7 +661,7 @@ Route path's can also contain variables similar to bindings.
|
|
659
661
|
```ruby
|
660
662
|
get "/todos/{_index}", _view: 'todos'
|
661
663
|
```
|
662
|
-
|
664
|
+
|
663
665
|
In the case above, if any url matches /todos/*, (where * is anything but a slash), it will be the active route. ```params._view``` would be set to 'todos', and ```params._index``` would be set to the value in the path.
|
664
666
|
|
665
667
|
If ```params._view``` is 'todos' and ```params._index``` is not nil, the route would be matched.
|
@@ -671,7 +673,7 @@ Routes are matched top to bottom in a routes file.
|
|
671
673
|
An in browser irb is in the works. We also have source maps support, but they are currently disabled due by default. To enable them run:
|
672
674
|
|
673
675
|
MAPS=true volt s
|
674
|
-
|
676
|
+
|
675
677
|
They are disabled by default because they slow down page rendering because so many files are rendered. We're working with the opal and sprockets teams to make it so everything is still served in one big source maps file (which would show the files as they originated on disk)
|
676
678
|
|
677
679
|
|
@@ -710,3 +712,8 @@ store._things
|
|
710
712
|
| loading | maybe | someone either accessed the data or bound an event |
|
711
713
|
| loaded | yes | data is loaded and there is an event bound |
|
712
714
|
| dirty | no | data was either accessed without binding an event, or an event was bound, but later unbound. |
|
715
|
+
|
716
|
+
|
717
|
+
# Why Volt is Awesome
|
718
|
+
|
719
|
+
- only the relevant dom is updated. There is no match and patch algorithm to update from strings like other frameworks, all associations are tracked through our reactive values, so we know exactly what needs to be updated without the need to generate any extra html. This has a few advantages, namely that things like input fields are retained, so any properties (focus, tab position, etc...) are also retained.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.7.0
|
@@ -1,86 +1,86 @@
|
|
1
1
|
require_relative 'query_tracker'
|
2
2
|
|
3
|
-
# Tracks a channel and a query on a collection. Alerts
|
3
|
+
# Tracks a channel and a query on a collection. Alerts
|
4
4
|
# the listener when the data in the query changes.
|
5
5
|
class LiveQuery
|
6
6
|
attr_reader :current_ids, :collection, :query
|
7
|
-
|
7
|
+
|
8
8
|
def initialize(pool, data_store, collection, query)
|
9
9
|
@pool = pool
|
10
10
|
@collection = collection
|
11
11
|
@query = query
|
12
|
-
|
12
|
+
|
13
13
|
@channels = []
|
14
14
|
@data_store = data_store
|
15
|
-
|
15
|
+
|
16
16
|
@query_tracker = QueryTracker.new(self, @data_store)
|
17
|
-
|
17
|
+
|
18
18
|
run
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
def run(skip_channel=nil)
|
22
22
|
@query_tracker.run(skip_channel)
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
def notify_removed(ids, skip_channel)
|
26
26
|
notify! do |channel|
|
27
27
|
# puts "Removed: #{ids.inspect} to #{channel.inspect}"
|
28
28
|
channel.send_message("removed", nil, @collection, @query, ids)
|
29
29
|
end
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
def notify_added(index, data, skip_channel)
|
33
33
|
notify!(skip_channel) do |channel|
|
34
34
|
# puts "Added: #{index} - #{data.inspect} to #{channel.inspect}"
|
35
35
|
channel.send_message("added", nil, @collection, @query, index, data)
|
36
36
|
end
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
def notify_moved(id, new_position, skip_channel)
|
40
40
|
notify! do |channel|
|
41
41
|
# puts "Moved: #{id}, #{new_position} to #{channel.inspect}"
|
42
42
|
channel.send_message("moved", nil, @collection, @query, id, new_position)
|
43
43
|
end
|
44
44
|
end
|
45
|
-
|
45
|
+
|
46
46
|
def notify_changed(id, data, skip_channel)
|
47
47
|
notify!(skip_channel) do |channel|
|
48
48
|
# puts "Changed: #{id}, #{data} to #{channel.inspect}"
|
49
49
|
channel.send_message("changed", nil, @collection, @query, id, data)
|
50
50
|
end
|
51
51
|
end
|
52
|
-
|
53
|
-
|
52
|
+
|
53
|
+
|
54
54
|
# return the query results the first time a channel connects
|
55
55
|
def initial_data
|
56
56
|
@query_tracker.results.map.with_index {|data, index| [index, data] }
|
57
57
|
end
|
58
|
-
|
58
|
+
|
59
59
|
def add_channel(channel)
|
60
60
|
@channels << channel
|
61
61
|
end
|
62
|
-
|
62
|
+
|
63
63
|
def remove_channel(channel)
|
64
64
|
@channels.delete(channel)
|
65
|
-
|
65
|
+
|
66
66
|
if @channels.size == 0
|
67
67
|
# remove this query, no one is listening anymore
|
68
68
|
@pool.remove(@collection, @query)
|
69
69
|
end
|
70
70
|
end
|
71
|
-
|
71
|
+
|
72
72
|
def notify!(skip_channel=nil, only_channel=nil)
|
73
73
|
if only_channel
|
74
74
|
channels = [only_channel]
|
75
75
|
else
|
76
76
|
channels = @channels
|
77
77
|
end
|
78
|
-
|
78
|
+
|
79
79
|
channels = channels.reject {|c| c == skip_channel }
|
80
|
-
|
80
|
+
|
81
81
|
channels.each do |channel|
|
82
82
|
yield(channel)
|
83
83
|
end
|
84
84
|
end
|
85
|
-
|
86
|
-
end
|
85
|
+
|
86
|
+
end
|
@@ -6,20 +6,20 @@ class LiveQueryPool < GenericPool
|
|
6
6
|
super()
|
7
7
|
@data_store = data_store
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
def lookup(collection, query)
|
11
11
|
query = normalize_query(query)
|
12
|
-
|
12
|
+
|
13
13
|
return super(collection, query)
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
def updated_collection(collection, skip_channel)
|
17
17
|
lookup_all(collection).each do |live_query|
|
18
18
|
# puts "RUN ON: #{live_query} with #{live_query.instance_variable_get('@channels').inspect}"
|
19
19
|
live_query.run(skip_channel)
|
20
20
|
end
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
private
|
24
24
|
# Creates the live query if it doesn't exist, and stores it so it
|
25
25
|
# can be found later.
|
@@ -27,10 +27,10 @@ class LiveQueryPool < GenericPool
|
|
27
27
|
# If not already setup, create a new one for this collection/query
|
28
28
|
return LiveQuery.new(self, @data_store, collection, query)
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
def normalize_query(query)
|
32
32
|
# TODO: add something to sort query properties so the queries are
|
33
33
|
# always compared the same.
|
34
34
|
return query
|
35
35
|
end
|
36
|
-
end
|
36
|
+
end
|
@@ -5,29 +5,29 @@ class QueryTracker
|
|
5
5
|
def initialize(live_query, data_store)
|
6
6
|
@live_query = live_query
|
7
7
|
@data_store = data_store
|
8
|
-
|
8
|
+
|
9
9
|
# Stores the list of id's currently associated with this query
|
10
10
|
@current_ids = []
|
11
11
|
@results = []
|
12
12
|
@results_hash = {}
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
# Runs the query, stores the results and updates the current_ids
|
16
16
|
def run(skip_channel=nil)
|
17
17
|
@previous_results = @results
|
18
18
|
@previous_results_hash = @results_hash
|
19
19
|
@previous_ids = @current_ids
|
20
|
-
|
20
|
+
|
21
21
|
# Run the query again
|
22
22
|
@results = @data_store.query(@live_query.collection, @live_query.query)
|
23
|
-
|
23
|
+
|
24
24
|
# Update the current_ids
|
25
25
|
@current_ids = @results.map {|r| r['_id'] }
|
26
26
|
@results_hash = Hash[@results.map {|r| [r['_id'], r] }]
|
27
|
-
|
27
|
+
|
28
28
|
process_changes(skip_channel)
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
# Looks at the changes in the last run and sends out notices
|
32
32
|
# all changes.
|
33
33
|
def process_changes(skip_channel)
|
@@ -39,18 +39,18 @@ class QueryTracker
|
|
39
39
|
detect_changed(skip_channel)
|
40
40
|
end
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
def detect_removed(skip_channel)
|
44
44
|
# Removed models
|
45
45
|
removed_ids = @previous_ids - @current_ids
|
46
46
|
if removed_ids.size > 0
|
47
47
|
@live_query.notify_removed(removed_ids, skip_channel)
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
50
|
# Update @previous_ids to relect the removed
|
51
51
|
@previous_ids = @previous_ids & @current_ids
|
52
52
|
end
|
53
|
-
|
53
|
+
|
54
54
|
# Loop through the new list, tracking in the old, notifies of any that
|
55
55
|
# have been added or moved.
|
56
56
|
def detect_added_and_moved(skip_channel)
|
@@ -61,15 +61,15 @@ class QueryTracker
|
|
61
61
|
previous_index += 1
|
62
62
|
next
|
63
63
|
end
|
64
|
-
|
64
|
+
|
65
65
|
# We have an item that didn't match the current position's previous
|
66
66
|
# TODO: make a hash so we don't have to do include?
|
67
67
|
if @previous_ids.include?(id)
|
68
68
|
# The location from the previous has changed, move to correct location.
|
69
|
-
|
69
|
+
|
70
70
|
# Remove from previous_ids, since it will be moved and we will be past it.
|
71
71
|
@previous_ids.delete(id)
|
72
|
-
|
72
|
+
|
73
73
|
@live_query.notify_moved(id, index, skip_channel)
|
74
74
|
else
|
75
75
|
# TODO: Faster lookup
|
@@ -78,12 +78,12 @@ class QueryTracker
|
|
78
78
|
end
|
79
79
|
end
|
80
80
|
end
|
81
|
-
|
81
|
+
|
82
82
|
# Finds all items in the previous results that have new values, and alerts
|
83
83
|
# of changes.
|
84
84
|
def detect_changed(skip_channel)
|
85
85
|
not_added_or_removed = @previous_ids & @current_ids
|
86
|
-
|
86
|
+
|
87
87
|
not_added_or_removed.each do |id|
|
88
88
|
if @previous_results_hash[id] != (data = @results_hash[id])
|
89
89
|
# Data hash changed
|
@@ -92,4 +92,4 @@ class QueryTracker
|
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
95
|
-
end
|
95
|
+
end
|
@@ -4,48 +4,48 @@ require_relative 'live_query/live_query_pool'
|
|
4
4
|
class QueryTasks
|
5
5
|
@@live_query_pool = LiveQueryPool.new(DataStore.new)
|
6
6
|
@@channel_live_queries = {}
|
7
|
-
|
7
|
+
|
8
8
|
def self.live_query_pool
|
9
9
|
@@live_query_pool
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
# The dispatcher passes its self in
|
13
13
|
def initialize(channel, dispatcher=nil)
|
14
14
|
@channel = channel
|
15
15
|
@dispatcher = dispatcher
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
def add_listener(collection, query)
|
19
19
|
live_query = @@live_query_pool.lookup(collection, query)
|
20
20
|
track_channel_in_live_query(live_query)
|
21
|
-
|
21
|
+
|
22
22
|
live_query.add_channel(@channel)
|
23
|
-
|
23
|
+
|
24
24
|
# Return the initial data
|
25
25
|
return live_query.initial_data
|
26
26
|
end
|
27
|
-
|
28
|
-
# Remove a listening channel, the LiveQuery will automatically remove
|
27
|
+
|
28
|
+
# Remove a listening channel, the LiveQuery will automatically remove
|
29
29
|
# itsself from the pool when there are no channels.
|
30
30
|
def remove_listener(collection, query)
|
31
31
|
live_query = @@live_query_pool.lookup(collection, query)
|
32
32
|
live_query.remove_channel(@channel)
|
33
33
|
end
|
34
|
-
|
35
|
-
|
34
|
+
|
35
|
+
|
36
36
|
# Removes a channel from all associated live queries
|
37
37
|
def close!
|
38
38
|
live_queries = @@channel_live_queries[@channel]
|
39
|
-
|
39
|
+
|
40
40
|
if live_queries
|
41
41
|
live_queries.each do |live_query|
|
42
42
|
live_query.remove_channel(@channel)
|
43
43
|
end
|
44
44
|
end
|
45
|
-
|
45
|
+
|
46
46
|
@@channel_live_queries.delete(@channel)
|
47
47
|
end
|
48
|
-
|
48
|
+
|
49
49
|
private
|
50
50
|
# Tracks that this channel will be notified from the live query.
|
51
51
|
def track_channel_in_live_query(live_query)
|
@@ -54,4 +54,4 @@ class QueryTasks
|
|
54
54
|
end
|
55
55
|
|
56
56
|
|
57
|
-
end
|
57
|
+
end
|