volt 0.7.23 → 0.8.0

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 (106) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +8 -1
  3. data/CHANGELOG.md +22 -0
  4. data/Gemfile +8 -0
  5. data/Guardfile +2 -2
  6. data/Readme.md +139 -136
  7. data/VERSION +1 -1
  8. data/app/volt/assets/js/setImmediate.js +175 -0
  9. data/app/volt/tasks/live_query/data_store.rb +0 -2
  10. data/app/volt/tasks/live_query/live_query.rb +4 -4
  11. data/docs/GETTING_STARTED.md +24 -3
  12. data/docs/WHY.md +1 -22
  13. data/lib/volt.rb +20 -1
  14. data/lib/volt/console.rb +20 -0
  15. data/lib/volt/controllers/model_controller.rb +25 -11
  16. data/lib/volt/extra_core/object.rb +2 -14
  17. data/lib/volt/extra_core/string.rb +4 -0
  18. data/lib/volt/models.rb +0 -1
  19. data/lib/volt/models/array_model.rb +8 -16
  20. data/lib/volt/models/cursor.rb +1 -1
  21. data/lib/volt/models/model.rb +40 -60
  22. data/lib/volt/models/model_hash_behaviour.rb +10 -24
  23. data/lib/volt/models/model_helpers.rb +2 -2
  24. data/lib/volt/models/model_state.rb +1 -1
  25. data/lib/volt/models/model_wrapper.rb +4 -4
  26. data/lib/volt/models/persistors/array_store.rb +44 -28
  27. data/lib/volt/models/persistors/base.rb +1 -1
  28. data/lib/volt/models/persistors/model_store.rb +1 -1
  29. data/lib/volt/models/persistors/params.rb +5 -1
  30. data/lib/volt/models/persistors/query/query_listener.rb +2 -0
  31. data/lib/volt/models/persistors/store.rb +3 -2
  32. data/lib/volt/models/persistors/store_state.rb +7 -2
  33. data/lib/volt/models/url.rb +35 -29
  34. data/lib/volt/models/validations.rb +7 -17
  35. data/lib/volt/page/bindings/attribute_binding.rb +57 -39
  36. data/lib/volt/page/bindings/base_binding.rb +0 -14
  37. data/lib/volt/page/bindings/content_binding.rb +15 -18
  38. data/lib/volt/page/bindings/each_binding.rb +67 -34
  39. data/lib/volt/page/bindings/if_binding.rb +15 -12
  40. data/lib/volt/page/bindings/template_binding.rb +77 -59
  41. data/lib/volt/page/bindings/template_binding/grouped_controllers.rb +19 -4
  42. data/lib/volt/page/channel.rb +22 -38
  43. data/lib/volt/page/channel_stub.rb +3 -6
  44. data/lib/volt/page/page.rb +24 -26
  45. data/lib/volt/page/string_template_renderer.rb +46 -0
  46. data/lib/volt/page/sub_context.rb +7 -1
  47. data/lib/volt/page/targets/binding_document/component_node.rb +11 -9
  48. data/lib/volt/page/tasks.rb +3 -2
  49. data/lib/volt/page/url_tracker.rb +4 -3
  50. data/lib/volt/reactive/computation.rb +131 -0
  51. data/lib/volt/reactive/dependency.rb +71 -0
  52. data/lib/volt/reactive/eventable.rb +82 -0
  53. data/lib/volt/reactive/hash_dependency.rb +36 -0
  54. data/lib/volt/{controllers → reactive}/reactive_accessors.rb +8 -11
  55. data/lib/volt/reactive/reactive_array.rb +100 -193
  56. data/lib/volt/reactive/reactive_hash.rb +49 -0
  57. data/lib/volt/server/html_parser/attribute_scope.rb +24 -4
  58. data/lib/volt/server/html_parser/if_view_scope.rb +15 -15
  59. data/lib/volt/server/html_parser/view_scope.rb +31 -1
  60. data/spec/apps/kitchen_sink/Gemfile +4 -8
  61. data/spec/apps/kitchen_sink/app/main/config/dependencies.rb +8 -0
  62. data/spec/apps/kitchen_sink/app/main/config/routes.rb +8 -1
  63. data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +8 -0
  64. data/spec/apps/kitchen_sink/app/main/views/main/bindings.html +73 -0
  65. data/spec/apps/kitchen_sink/app/main/views/main/index.html +6 -1
  66. data/spec/apps/kitchen_sink/app/main/views/main/main.html +26 -6
  67. data/spec/apps/kitchen_sink/app/main/views/main/store.html +6 -0
  68. data/spec/controllers/reactive_accessors_spec.rb +13 -15
  69. data/spec/integration/bindings_spec.rb +159 -0
  70. data/spec/integration/templates_spec.rb +15 -0
  71. data/spec/models/model_spec.rb +130 -228
  72. data/spec/reactive/computation_spec.rb +63 -0
  73. data/spec/reactive/dependency_spec.rb +5 -0
  74. data/spec/reactive/eventable_spec.rb +48 -0
  75. data/spec/reactive/reactive_array_spec.rb +97 -0
  76. data/spec/router/routes_spec.rb +26 -27
  77. data/spec/server/html_parser/view_parser_spec.rb +3 -21
  78. data/spec/server/rack/asset_files_spec.rb +1 -1
  79. data/templates/project/app/main/views/main/main.html +2 -2
  80. metadata +29 -41
  81. data/lib/volt/extra_core/time.rb +0 -16
  82. data/lib/volt/page/draw_cycle.rb +0 -31
  83. data/lib/volt/page/memory_test.rb +0 -26
  84. data/lib/volt/page/reactive_template.rb +0 -32
  85. data/lib/volt/reactive/array_extensions.rb +0 -12
  86. data/lib/volt/reactive/destructive_methods.rb +0 -19
  87. data/lib/volt/reactive/event_chain.rb +0 -125
  88. data/lib/volt/reactive/events.rb +0 -216
  89. data/lib/volt/reactive/object_tracking.rb +0 -14
  90. data/lib/volt/reactive/reactive_block.rb +0 -88
  91. data/lib/volt/reactive/reactive_generator.rb +0 -44
  92. data/lib/volt/reactive/reactive_tags.rb +0 -71
  93. data/lib/volt/reactive/reactive_value.rb +0 -427
  94. data/lib/volt/reactive/string_extensions.rb +0 -31
  95. data/spec/integration/test_integration_spec.rb +0 -14
  96. data/spec/models/event_chain_spec.rb +0 -150
  97. data/spec/models/model_buffers_spec.rb +0 -9
  98. data/spec/models/old_model_spec.rb +0 -67
  99. data/spec/models/reactive_array_spec.rb +0 -364
  100. data/spec/models/reactive_block_spec.rb +0 -13
  101. data/spec/models/reactive_call_times_spec.rb +0 -28
  102. data/spec/models/reactive_generator_spec.rb +0 -58
  103. data/spec/models/reactive_tags_spec.rb +0 -35
  104. data/spec/models/reactive_value_spec.rb +0 -370
  105. data/spec/models/store_spec.rb +0 -16
  106. data/spec/models/string_extensions_spec.rb +0 -57
@@ -1,13 +0,0 @@
1
- require 'volt/models'
2
-
3
- describe ReactiveBlock do
4
- it "should call cur through the reactive count to the number" do
5
- model = ReactiveValue.new(Model.new)
6
-
7
- model._items << {_name: 'ok'}
8
-
9
- count = model._items.count {|m| m._name == 'ok' }
10
-
11
- expect(count.cur).to eq(1)
12
- end
13
- end
@@ -1,28 +0,0 @@
1
- # require 'volt/models'
2
- #
3
- # class Test1 < Model
4
- # def test
5
- # puts "YEP"
6
- #
7
- # {:yes => true}
8
- # end
9
- # end
10
- #
11
- # describe ReactiveValue do
12
- # it "should not trigger a model method multiple times" do
13
- # a = ReactiveValue.new(Test1.new)
14
- #
15
- # test = a.test
16
- #
17
- # test.on('changed') { puts "CH" }
18
- # a._name.on('changed') { puts "NAME CH" }
19
- #
20
- # puts "--------"
21
- # a._name = 'ok'
22
- #
23
- #
24
- #
25
- #
26
- # # a.test
27
- # end
28
- # end
@@ -1,58 +0,0 @@
1
- require 'volt/models'
2
-
3
- describe ReactiveGenerator do
4
- before do
5
- @object = {
6
- name: ReactiveValue.new('bob'),
7
- location: {
8
- town: ReactiveValue.new('Bozeman'),
9
- places: [
10
- ReactiveValue.new('The Garage'),
11
- ReactiveValue.new('Ale Works')
12
- ]
13
- }
14
- }
15
- end
16
-
17
- it "should find all reactive values in any object" do
18
- values = ReactiveGenerator.find_reactives(@object)
19
-
20
- expect(values.map(&:cur)).to eq(['bob', 'Bozeman', 'The Garage', 'Ale Works'])
21
- end
22
-
23
- it "should return a reactive value that changes whenever a child reactive value changes" do
24
- values = ReactiveGenerator.from_hash(@object)
25
-
26
- count = 0
27
- values.on('changed') { count += 1 }
28
- expect(count).to eq(0)
29
-
30
- @object[:name].cur = 'jim'
31
-
32
- expect(count).to eq(1)
33
-
34
- @object[:location][:places].last.cur = 'Starkies'
35
- expect(count).to eq(2)
36
-
37
- expect(values.to_h).to eq({
38
- name: 'jim',
39
- location: {
40
- town: 'Bozeman',
41
- places: [
42
- 'The Garage',
43
- 'Starkies'
44
- ]
45
- }
46
- })
47
- end
48
-
49
- it "should optionally return a normal hash if there are no child reactive values" do
50
- values = ReactiveGenerator.from_hash({name: 'bob'})
51
- expect(values.reactive?).to eq(true)
52
- expect(values.is_a?(Hash)).to eq(false)
53
-
54
- values = ReactiveGenerator.from_hash({name: 'bob'}, true)
55
- expect(values.reactive?).to eq(false)
56
- expect(values.is_a?(Hash)).to eq(true)
57
- end
58
- end
@@ -1,35 +0,0 @@
1
- require 'volt/models'
2
-
3
- class TestCustomClass
4
- include ReactiveTags
5
-
6
- tag_method(:cool) do
7
- destructive!
8
- end
9
- end
10
-
11
- class TestSubClass < TestCustomClass
12
-
13
- end
14
-
15
- class TestInherit < TestCustomClass
16
- tag_method(:cool) do
17
- pass_reactive!
18
- end
19
- end
20
-
21
- describe ReactiveTags do
22
- it "should tag correctly" do
23
- expect(TestCustomClass.new.reactive_method_tag(:cool, :destructive)).to eq(true)
24
- end
25
-
26
- it "should include tags in a subclass" do
27
- expect(TestSubClass.new.reactive_method_tag(:cool, :destructive)).to eq(true)
28
- expect(TestSubClass.new.reactive_method_tag(:cool, :pass_reactive)).to eq(nil)
29
- end
30
-
31
- it "should inherit" do
32
- expect(TestInherit.new.reactive_method_tag(:cool, :destructive)).to eq(true)
33
- expect(TestInherit.new.reactive_method_tag(:cool, :pass_reactive)).to eq(true)
34
- end
35
- end
@@ -1,370 +0,0 @@
1
- require 'volt/models'
2
-
3
- class TestYield
4
- def call_with_yield
5
- yield(1)
6
- yield(2)
7
- end
8
- end
9
- class SampleClass
10
- include ReactiveTags
11
-
12
- tag_method(:break_stuff) do
13
- destructive!
14
- end
15
- def break_stuff
16
- 5
17
- end
18
-
19
- tag_method(:receive_reactive?) do
20
- pass_reactive!
21
- end
22
- def receive_reactive?(arg1)
23
- return arg1.reactive?
24
- end
25
-
26
- def receive_non_reactive?(arg1)
27
- return arg1.reactive?
28
- end
29
-
30
- tag_method(:destructive_receive_non_reactive?) do
31
- destructive!
32
- end
33
- def destructive_receive_non_reactive?(arg1)
34
- return arg1.reactive?
35
- end
36
- end
37
-
38
-
39
- class SampleTriggerClass
40
- include ReactiveTags
41
-
42
- tag_method(:method_that_triggers) { destructive! }
43
- def method_that_triggers
44
- trigger!('changed')
45
- end
46
- end
47
-
48
- class SampleNonTriggerClass
49
- include ReactiveTags
50
-
51
- tag_method(:method_that_triggers) { destructive! }
52
- def method_that_triggers
53
- # does not trigger
54
- end
55
- end
56
-
57
- class SampleTriggerClass2
58
- include ReactiveTags
59
-
60
- tag_method(:method_that_triggers) { destructive! }
61
- def method_that_triggers
62
- trigger!('other')
63
- end
64
- end
65
-
66
-
67
- describe ReactiveValue do
68
- describe "the basics" do
69
- it "should have a with method that returns a new reactive value" do
70
- a = ReactiveValue.new(1)
71
- b = a.with { |a| a + 1 }
72
- expect(a.cur).to eq(1)
73
- expect(b.cur).to eq(2)
74
-
75
- a.cur = 5
76
- expect(b.cur).to eq(6)
77
- end
78
-
79
- it "should say its reactive" do
80
- a = 1
81
- b = ReactiveValue.new(a)
82
-
83
- expect(a.reactive?).to eq(false)
84
- expect(b.reactive?).to eq(true)
85
- end
86
-
87
- it "should return a reactive value from any method call" do
88
- a = ReactiveValue.new(5)
89
- b = 10
90
-
91
- c = a + b
92
- d = b + a
93
- e = c + d
94
-
95
- expect(c.reactive?).to eq(true)
96
- expect(d.reactive?).to eq(true)
97
- expect(e.reactive?).to eq(true)
98
- end
99
-
100
- it "should work with objects not just numbers" do
101
- a = ReactiveValue.new([1,2,3])
102
- b = [4]
103
-
104
- c = a + b
105
- d = b + a
106
- e = c + d
107
-
108
- expect(c.reactive?).to eq(true)
109
- expect(d.reactive?).to eq(true)
110
- expect(e.reactive?).to eq(true)
111
- end
112
-
113
- it "should return different listeners" do
114
- a = ReactiveValue.new(1)
115
-
116
- count = 0
117
- listener1 = a.on('changed') { count += 1 }
118
- listener2 = a.on('changed') { count += 1 }
119
-
120
- expect(listener1).not_to eq(listener2)
121
- end
122
-
123
- it "should return a reactive on .is_a?" do
124
- a = ReactiveValue.new(1)
125
- b = a.is_a?(Fixnum)
126
-
127
- expect(b.reactive?).to eq(true)
128
- expect(b.cur).to eq(true)
129
- end
130
-
131
- it "should return true for a nil? on a nil value" do
132
- a = ReactiveValue.new(nil)
133
- b = a.nil?
134
- expect(b.reactive?).to eq(true)
135
- expect(a.cur).to eq(nil)
136
-
137
- a = ReactiveValue.new(1)
138
- b = a.nil?
139
- expect(b.reactive?).to eq(true)
140
- expect(a.cur).not_to eq(nil)
141
- end
142
-
143
- it "should only chain one event up" do
144
- a = ReactiveValue.new('1')
145
- b = a.to_i
146
-
147
- count = 0
148
- b.on('changed') { count += 1 }
149
- b.on('changed') { count += 1 }
150
-
151
- expect(a.reactive_manager.listeners[:changed].size).to eq(1)
152
- end
153
- end
154
-
155
- describe "events" do
156
- it "should bind and trigger" do
157
- a = ReactiveValue.new(1)
158
- count = 0
159
- a.on('changed') { count += 1 }
160
- expect(count).to eq(0)
161
-
162
- a.trigger!('changed')
163
- expect(count).to eq(1)
164
- end
165
-
166
- it "should bind and trigger on children" do
167
- a = ReactiveValue.new(1)
168
- b = a + 10
169
-
170
- count = 0
171
- b.on('changed') { count += 1 }
172
- expect(count).to eq(0)
173
-
174
- a.trigger!('changed')
175
- expect(count).to eq(1)
176
- end
177
-
178
- it "should handle events, even when triggered from the parent" do
179
- a = ReactiveValue.new(5)
180
- b = ReactiveValue.new(20)
181
-
182
- c = a + b
183
-
184
- @called = false
185
- c.on('changed') { @called = true }
186
- expect(@called).to eq(false)
187
-
188
- a.trigger!('changed')
189
- expect(@called).to eq(true)
190
-
191
- @called = false
192
-
193
- b.trigger!('changed')
194
- expect(@called).to eq(true)
195
-
196
- end
197
- end
198
-
199
- describe "arrays" do
200
- it "should add wrapped arrays" do
201
- a = ReactiveValue.new([1,2])
202
- b = ReactiveValue.new([3,4])
203
-
204
- c = a + b
205
- expect(c.size.cur).to eq(4)
206
- end
207
- end
208
-
209
- describe "tagged methods" do
210
-
211
- it "should let a class specify methods as destructive" do
212
- a = ReactiveValue.new(SampleClass.new)
213
- result = a.break_stuff
214
- expect(result).to eq(5)
215
- expect(result.reactive?).to eq(false)
216
- end
217
-
218
- it "should pass reactive values when asked" do
219
- a = ReactiveValue.new(SampleClass.new)
220
- expect(a.receive_reactive?(ReactiveValue.new(1)).cur).to eq(true)
221
- end
222
-
223
- it "should not pass reactive when not asked" do
224
- a = ReactiveValue.new(SampleClass.new)
225
- expect(a.receive_non_reactive?(ReactiveValue.new(1)).cur).to eq(false)
226
- end
227
-
228
- it "should not pass a reactive value to a destructive method unless it asked for it" do
229
- a = ReactiveValue.new(SampleClass.new)
230
- expect(a.destructive_receive_non_reactive?(ReactiveValue.new(1)).cur).to eq(false)
231
- end
232
- end
233
-
234
- describe "triggers from methods" do
235
-
236
- it "should trigger on any ReactiveValue's that are wrapping it" do
237
- a = ReactiveValue.new(SampleTriggerClass.new)
238
-
239
- count = 0
240
- a.on('changed') { count += 1 }
241
- expect(count).to eq(0)
242
-
243
- a.method_that_triggers
244
- expect(count).to eq(1)
245
- end
246
-
247
- it "should trigger on any ReactiveValue's that are wrapping it" do
248
- a = ReactiveValue.new(SampleNonTriggerClass.new)
249
-
250
- count = 0
251
- a.on('changed') { count += 1 }
252
- expect(count).to eq(0)
253
-
254
- a.method_that_triggers
255
- expect(count).to eq(0)
256
-
257
- a.cur = SampleTriggerClass.new
258
- expect(count).to eq(1)
259
-
260
- a.method_that_triggers
261
- expect(count).to eq(2)
262
- end
263
-
264
- it "should trigger the correct event" do
265
- a = ReactiveValue.new(SampleNonTriggerClass.new)
266
-
267
- count = 0
268
- other_count = 0
269
- a.on('changed') { count += 1 }
270
- a.on('other') { other_count += 1 }
271
- expect(count).to eq(0)
272
-
273
- a.method_that_triggers
274
- expect(count).to eq(0)
275
-
276
- a.cur = SampleTriggerClass.new
277
- expect(count).to eq(1)
278
-
279
- a.method_that_triggers
280
- expect(count).to eq(2)
281
-
282
- # Note: .cur= triggers changed
283
- a.cur = SampleTriggerClass2.new
284
- expect(other_count).to eq(0)
285
-
286
- a.method_that_triggers
287
- expect(count).to eq(3)
288
- expect(other_count).to eq(1)
289
- end
290
-
291
- it "should trigger through two different paths" do
292
- source = SampleTriggerClass.new
293
- a = ReactiveValue.new(source)
294
- b = ReactiveValue.new(source)
295
-
296
- count = 0
297
- count2 = 0
298
- a.on('changed') { count += 1 }
299
- b.on('changed') { count2 += 1 }
300
-
301
- expect(count).to eq(0)
302
- expect(count2).to eq(0)
303
-
304
- a.method_that_triggers
305
-
306
- expect(count).to eq(1)
307
- expect(count2).to eq(1)
308
- end
309
- end
310
-
311
- it "should setup a ReactiveManager" do
312
- a = ReactiveValue.new(1)
313
- expect(a.reactive_manager.class).to eq(ReactiveManager)
314
- end
315
-
316
- describe "similar to base object" do
317
- it "should return a reactive comparator" do
318
- a = ReactiveValue.new(1)
319
- b = ReactiveValue.new(2)
320
-
321
- compare = (a == b)
322
- expect(compare.cur).to eq(false)
323
- b.cur = 1
324
- expect(compare.cur).to eq(true)
325
- end
326
- end
327
-
328
- describe "blocks" do
329
- before do
330
-
331
- end
332
- it "should call blocks through the reactive value, and the returned reactive value should depend on the results of the block" do
333
- a = ReactiveValue.new(TestYield.new)
334
-
335
- count = 0
336
- a.call_with_yield do |value|
337
- count += 1
338
- # value.reactive?.should == true
339
- # value.even?
340
- end.cur
341
-
342
- expect(count).to eq(2)
343
- end
344
- end
345
-
346
- it "should give you back the object without any ReactiveValue's if you call .deep_cur on it." do
347
- a = ReactiveValue.new({_names: [ReactiveValue.new('bob'), ReactiveValue.new('jim')]})
348
-
349
- expect(a.deep_cur).to eq({_names: ['bob', 'jim']})
350
- end
351
-
352
- it "should remove any event bindings bound through a reactive value when the value changes" do
353
- a = ReactiveValue.new(Model.new)
354
-
355
- a._info = {_name: 'Test'}
356
- info = a._info.cur
357
-
358
- expect(info.listeners.size).to eq(0)
359
-
360
- listener = a._info.on('changed') { }
361
-
362
- expect(info.listeners.size).to eq(1)
363
-
364
- # Listener should be moved to the new object
365
- a._info = {}
366
-
367
- expect(info.listeners.size).to eq(0)
368
- end
369
-
370
- end