volt 0.6.5 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/Readme.md +47 -40
  3. data/VERSION +1 -1
  4. data/app/volt/controllers/notices_controller.rb +3 -3
  5. data/app/volt/tasks/live_query/data_store.rb +2 -2
  6. data/app/volt/tasks/live_query/live_query.rb +20 -20
  7. data/app/volt/tasks/live_query/live_query_pool.rb +6 -6
  8. data/app/volt/tasks/live_query/query_tracker.rb +15 -15
  9. data/app/volt/tasks/query_tasks.rb +13 -13
  10. data/app/volt/tasks/store_tasks.rb +7 -7
  11. data/app/volt/views/notices/index.html +17 -18
  12. data/lib/volt/assets/test.rb +2 -2
  13. data/lib/volt/benchmark/benchmark.rb +25 -23
  14. data/lib/volt/cli/asset_compile.rb +11 -0
  15. data/lib/volt/cli/new_gem.rb +16 -16
  16. data/lib/volt/cli.rb +14 -12
  17. data/lib/volt/console.rb +5 -6
  18. data/lib/volt/controllers/model_controller.rb +18 -18
  19. data/lib/volt/extra_core/array.rb +4 -4
  20. data/lib/volt/extra_core/hash.rb +3 -3
  21. data/lib/volt/extra_core/object.rb +6 -6
  22. data/lib/volt/extra_core/string.rb +6 -6
  23. data/lib/volt/extra_core/symbol.rb +5 -5
  24. data/lib/volt/extra_core/time.rb +4 -4
  25. data/lib/volt/extra_core/true_false.rb +6 -6
  26. data/lib/volt/extra_core/try.rb +9 -9
  27. data/lib/volt/models/array_model.rb +26 -26
  28. data/lib/volt/models/model.rb +35 -35
  29. data/lib/volt/models/model_hash_behaviour.rb +15 -15
  30. data/lib/volt/models/model_helpers.rb +8 -8
  31. data/lib/volt/models/model_wrapper.rb +6 -6
  32. data/lib/volt/models/persistors/array_store.rb +36 -36
  33. data/lib/volt/models/persistors/base.rb +6 -6
  34. data/lib/volt/models/persistors/flash.rb +5 -5
  35. data/lib/volt/models/persistors/model_identity_map.rb +2 -2
  36. data/lib/volt/models/persistors/model_store.rb +22 -22
  37. data/lib/volt/models/persistors/params.rb +3 -3
  38. data/lib/volt/models/persistors/query/query_listener.rb +14 -14
  39. data/lib/volt/models/persistors/query/query_listener_pool.rb +2 -2
  40. data/lib/volt/models/persistors/store.rb +8 -8
  41. data/lib/volt/models/persistors/store_factory.rb +2 -2
  42. data/lib/volt/models/url.rb +37 -37
  43. data/lib/volt/page/bindings/attribute_binding.rb +14 -14
  44. data/lib/volt/page/bindings/base_binding.rb +9 -9
  45. data/lib/volt/page/bindings/component_binding.rb +7 -7
  46. data/lib/volt/page/bindings/content_binding.rb +3 -3
  47. data/lib/volt/page/bindings/each_binding.rb +13 -13
  48. data/lib/volt/page/bindings/event_binding.rb +4 -4
  49. data/lib/volt/page/bindings/if_binding.rb +12 -12
  50. data/lib/volt/page/bindings/template_binding.rb +30 -30
  51. data/lib/volt/page/channel.rb +19 -19
  52. data/lib/volt/page/channel_stub.rb +6 -6
  53. data/lib/volt/page/document.rb +2 -2
  54. data/lib/volt/page/document_events.rb +4 -4
  55. data/lib/volt/page/draw_cycle.rb +3 -3
  56. data/lib/volt/page/memory_test.rb +6 -6
  57. data/lib/volt/page/page.rb +19 -19
  58. data/lib/volt/page/reactive_template.rb +9 -9
  59. data/lib/volt/page/sub_context.rb +5 -5
  60. data/lib/volt/page/targets/attribute_section.rb +9 -9
  61. data/lib/volt/page/targets/attribute_target.rb +3 -3
  62. data/lib/volt/page/targets/base_section.rb +2 -2
  63. data/lib/volt/page/targets/binding_document/component_node.rb +23 -23
  64. data/lib/volt/page/targets/binding_document/html_node.rb +2 -2
  65. data/lib/volt/page/targets/dom_section.rb +40 -38
  66. data/lib/volt/page/targets/dom_target.rb +2 -2
  67. data/lib/volt/page/tasks.rb +12 -12
  68. data/lib/volt/page/template_renderer.rb +4 -4
  69. data/lib/volt/page/url_tracker.rb +6 -6
  70. data/lib/volt/reactive/array_extensions.rb +2 -2
  71. data/lib/volt/reactive/destructive_methods.rb +5 -5
  72. data/lib/volt/reactive/event_chain.rb +25 -25
  73. data/lib/volt/reactive/events.rb +33 -33
  74. data/lib/volt/reactive/object_tracker.rb +21 -21
  75. data/lib/volt/reactive/object_tracking.rb +2 -2
  76. data/lib/volt/reactive/reactive_array.rb +57 -57
  77. data/lib/volt/reactive/reactive_tags.rb +16 -16
  78. data/lib/volt/reactive/reactive_value.rb +72 -72
  79. data/lib/volt/reactive/string_extensions.rb +3 -3
  80. data/lib/volt/router/routes.rb +22 -23
  81. data/lib/volt/server/component_handler.rb +5 -5
  82. data/lib/volt/server/component_templates.rb +14 -11
  83. data/lib/volt/server/html_parser/attribute_scope.rb +116 -0
  84. data/lib/volt/server/html_parser/each_scope.rb +18 -0
  85. data/lib/volt/server/html_parser/if_view_scope.rb +71 -0
  86. data/lib/volt/server/html_parser/sandlebars_parser.rb +219 -0
  87. data/lib/volt/server/html_parser/textarea_scope.rb +31 -0
  88. data/lib/volt/server/html_parser/view_handler.rb +82 -0
  89. data/lib/volt/server/html_parser/view_parser.rb +23 -0
  90. data/lib/volt/server/html_parser/view_scope.rb +145 -0
  91. data/lib/volt/server/rack/asset_files.rb +17 -17
  92. data/lib/volt/server/rack/component_paths.rb +18 -18
  93. data/lib/volt/server/rack/index_files.rb +8 -8
  94. data/lib/volt/server/rack/opal_files.rb +11 -11
  95. data/lib/volt/server/socket_connection_handler.rb +13 -13
  96. data/lib/volt/server/socket_connection_handler_stub.rb +2 -2
  97. data/lib/volt/server.rb +18 -18
  98. data/lib/volt/tasks/dispatcher.rb +5 -5
  99. data/lib/volt/utils/ejson.rb +2 -2
  100. data/lib/volt/utils/generic_counting_pool.rb +8 -8
  101. data/lib/volt/utils/generic_pool.rb +16 -16
  102. data/lib/volt/volt/environment.rb +4 -4
  103. data/lib/volt.rb +6 -6
  104. data/spec/integration/test_integration_spec.rb +2 -2
  105. data/spec/models/event_chain_spec.rb +38 -38
  106. data/spec/models/model_spec.rb +128 -128
  107. data/spec/models/old_model_spec.rb +17 -17
  108. data/spec/models/persistors/params_spec.rb +3 -3
  109. data/spec/models/persistors/store_spec.rb +7 -7
  110. data/spec/models/reactive_array_spec.rb +82 -82
  111. data/spec/models/reactive_generator_spec.rb +11 -11
  112. data/spec/models/reactive_tags_spec.rb +6 -6
  113. data/spec/models/reactive_value_spec.rb +70 -70
  114. data/spec/models/store_spec.rb +4 -4
  115. data/spec/models/string_extensions_spec.rb +13 -13
  116. data/spec/page/bindings/content_binding_spec.rb +6 -6
  117. data/spec/page/sub_context_spec.rb +1 -1
  118. data/spec/router/routes_spec.rb +3 -3
  119. data/spec/server/html_parser/sample_page.html +595 -0
  120. data/spec/server/html_parser/sandlebars_parser_spec.rb +192 -0
  121. data/spec/server/html_parser/view_parser_spec.rb +286 -0
  122. data/spec/server/rack/asset_files_spec.rb +6 -6
  123. data/spec/server/rack/component_paths_spec.rb +5 -5
  124. data/spec/spec_helper.rb +4 -5
  125. data/spec/store/mongo_spec.rb +3 -3
  126. data/spec/tasks/live_query_spec.rb +6 -6
  127. data/spec/tasks/query_tasks.rb +4 -4
  128. data/spec/tasks/query_tracker_spec.rb +20 -20
  129. data/spec/templates/targets/binding_document/component_node_spec.rb +4 -4
  130. data/spec/templates/template_binding_spec.rb +28 -28
  131. data/spec/utils/generic_counting_pool_spec.rb +5 -5
  132. data/spec/utils/generic_pool_spec.rb +14 -14
  133. data/templates/newgem/app/newgem/views/index/index.html +1 -2
  134. data/templates/project/app/home/config/dependencies.rb +1 -1
  135. data/templates/project/app/home/controllers/index_controller.rb +1 -1
  136. data/templates/project/app/home/views/index/about.html +4 -6
  137. data/templates/project/app/home/views/index/home.html +4 -5
  138. data/templates/project/app/home/views/index/index.html +8 -9
  139. data/templates/project/spec/spec_helper.rb +1 -1
  140. metadata +17 -8
  141. data/lib/volt/server/binding_setup.rb +0 -2
  142. data/lib/volt/server/if_binding_setup.rb +0 -31
  143. data/lib/volt/server/scope.rb +0 -43
  144. data/lib/volt/server/template_parser.rb +0 -453
  145. data/spec/server/template_parser_spec.rb +0 -50
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d799deb4ea372c6d37040e23d0e7317e1bae2820
4
- data.tar.gz: 1590f92d018a6344c3b60a9e10720135c6c0a27e
3
+ metadata.gz: 7a2b3f88f704f2e2438faffed5fc2039fa2ba4c2
4
+ data.tar.gz: 5030c30dbc621f0ceb393b8fad31abdab9cff17c
5
5
  SHA512:
6
- metadata.gz: d5a64ae3a5d148e115725f9a0fa2cc172df77f39e3c483896004b8683427df769b49fffb21de18b10aac7097dccf5ac563a7bd5444b65e114e98735913e9707a
7
- data.tar.gz: 858c561299e391deb865d4be3017110f55e7890a9bcc716c609d5f18c350f93dec900c97ee888164a106f90b4df924c5b91709e885324984e4ac2a8195de295f
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
- # Volt
5
+ ---
6
+ > NOTE: VOLT IS STILL IN DEVELOPMENT, DON'T USE IT FOR ANYTHING SERIOUS YET
7
+ ---
6
8
 
7
- NOTE: VOLT IS STILL IN DEVELOPMENT, DON'T USE IT FOR ANYTHING SERIOUS YET
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 dom (or anything other code wanting to know when a value updates) When something in the dom changes, Volt intellegent updates only the nodes that need to be changed.
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 .cur = to update the current value triggers a "changed" event.)
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>``` tag and end with ```</:SectionName>``` Volt will look first for a section in the same view.
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.6.5
1
+ 0.7.0
@@ -1,13 +1,13 @@
1
1
  class Volt
2
2
  class NoticesController < ModelController
3
3
  model :page
4
-
4
+
5
5
  def hey
6
6
  "yep"
7
7
  end
8
-
8
+
9
9
  def page
10
10
  $page.page
11
11
  end
12
12
  end
13
- end
13
+ end
@@ -5,8 +5,8 @@ class DataStore
5
5
  @@mongo_db ||= Mongo::MongoClient.new("localhost", 27017)
6
6
  @@db ||= @@mongo_db.db("development")
7
7
  end
8
-
8
+
9
9
  def query(collection, query)
10
10
  @@db[collection].find(query).to_a
11
11
  end
12
- end
12
+ end
@@ -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