volt 0.7.23 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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