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.
- checksums.yaml +4 -4
- data/.travis.yml +8 -1
- data/CHANGELOG.md +22 -0
- data/Gemfile +8 -0
- data/Guardfile +2 -2
- data/Readme.md +139 -136
- data/VERSION +1 -1
- data/app/volt/assets/js/setImmediate.js +175 -0
- data/app/volt/tasks/live_query/data_store.rb +0 -2
- data/app/volt/tasks/live_query/live_query.rb +4 -4
- data/docs/GETTING_STARTED.md +24 -3
- data/docs/WHY.md +1 -22
- data/lib/volt.rb +20 -1
- data/lib/volt/console.rb +20 -0
- data/lib/volt/controllers/model_controller.rb +25 -11
- data/lib/volt/extra_core/object.rb +2 -14
- data/lib/volt/extra_core/string.rb +4 -0
- data/lib/volt/models.rb +0 -1
- data/lib/volt/models/array_model.rb +8 -16
- data/lib/volt/models/cursor.rb +1 -1
- data/lib/volt/models/model.rb +40 -60
- data/lib/volt/models/model_hash_behaviour.rb +10 -24
- data/lib/volt/models/model_helpers.rb +2 -2
- data/lib/volt/models/model_state.rb +1 -1
- data/lib/volt/models/model_wrapper.rb +4 -4
- data/lib/volt/models/persistors/array_store.rb +44 -28
- data/lib/volt/models/persistors/base.rb +1 -1
- data/lib/volt/models/persistors/model_store.rb +1 -1
- data/lib/volt/models/persistors/params.rb +5 -1
- data/lib/volt/models/persistors/query/query_listener.rb +2 -0
- data/lib/volt/models/persistors/store.rb +3 -2
- data/lib/volt/models/persistors/store_state.rb +7 -2
- data/lib/volt/models/url.rb +35 -29
- data/lib/volt/models/validations.rb +7 -17
- data/lib/volt/page/bindings/attribute_binding.rb +57 -39
- data/lib/volt/page/bindings/base_binding.rb +0 -14
- data/lib/volt/page/bindings/content_binding.rb +15 -18
- data/lib/volt/page/bindings/each_binding.rb +67 -34
- data/lib/volt/page/bindings/if_binding.rb +15 -12
- data/lib/volt/page/bindings/template_binding.rb +77 -59
- data/lib/volt/page/bindings/template_binding/grouped_controllers.rb +19 -4
- data/lib/volt/page/channel.rb +22 -38
- data/lib/volt/page/channel_stub.rb +3 -6
- data/lib/volt/page/page.rb +24 -26
- data/lib/volt/page/string_template_renderer.rb +46 -0
- data/lib/volt/page/sub_context.rb +7 -1
- data/lib/volt/page/targets/binding_document/component_node.rb +11 -9
- data/lib/volt/page/tasks.rb +3 -2
- data/lib/volt/page/url_tracker.rb +4 -3
- data/lib/volt/reactive/computation.rb +131 -0
- data/lib/volt/reactive/dependency.rb +71 -0
- data/lib/volt/reactive/eventable.rb +82 -0
- data/lib/volt/reactive/hash_dependency.rb +36 -0
- data/lib/volt/{controllers → reactive}/reactive_accessors.rb +8 -11
- data/lib/volt/reactive/reactive_array.rb +100 -193
- data/lib/volt/reactive/reactive_hash.rb +49 -0
- data/lib/volt/server/html_parser/attribute_scope.rb +24 -4
- data/lib/volt/server/html_parser/if_view_scope.rb +15 -15
- data/lib/volt/server/html_parser/view_scope.rb +31 -1
- data/spec/apps/kitchen_sink/Gemfile +4 -8
- data/spec/apps/kitchen_sink/app/main/config/dependencies.rb +8 -0
- data/spec/apps/kitchen_sink/app/main/config/routes.rb +8 -1
- data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +8 -0
- data/spec/apps/kitchen_sink/app/main/views/main/bindings.html +73 -0
- data/spec/apps/kitchen_sink/app/main/views/main/index.html +6 -1
- data/spec/apps/kitchen_sink/app/main/views/main/main.html +26 -6
- data/spec/apps/kitchen_sink/app/main/views/main/store.html +6 -0
- data/spec/controllers/reactive_accessors_spec.rb +13 -15
- data/spec/integration/bindings_spec.rb +159 -0
- data/spec/integration/templates_spec.rb +15 -0
- data/spec/models/model_spec.rb +130 -228
- data/spec/reactive/computation_spec.rb +63 -0
- data/spec/reactive/dependency_spec.rb +5 -0
- data/spec/reactive/eventable_spec.rb +48 -0
- data/spec/reactive/reactive_array_spec.rb +97 -0
- data/spec/router/routes_spec.rb +26 -27
- data/spec/server/html_parser/view_parser_spec.rb +3 -21
- data/spec/server/rack/asset_files_spec.rb +1 -1
- data/templates/project/app/main/views/main/main.html +2 -2
- metadata +29 -41
- data/lib/volt/extra_core/time.rb +0 -16
- data/lib/volt/page/draw_cycle.rb +0 -31
- data/lib/volt/page/memory_test.rb +0 -26
- data/lib/volt/page/reactive_template.rb +0 -32
- data/lib/volt/reactive/array_extensions.rb +0 -12
- data/lib/volt/reactive/destructive_methods.rb +0 -19
- data/lib/volt/reactive/event_chain.rb +0 -125
- data/lib/volt/reactive/events.rb +0 -216
- data/lib/volt/reactive/object_tracking.rb +0 -14
- data/lib/volt/reactive/reactive_block.rb +0 -88
- data/lib/volt/reactive/reactive_generator.rb +0 -44
- data/lib/volt/reactive/reactive_tags.rb +0 -71
- data/lib/volt/reactive/reactive_value.rb +0 -427
- data/lib/volt/reactive/string_extensions.rb +0 -31
- data/spec/integration/test_integration_spec.rb +0 -14
- data/spec/models/event_chain_spec.rb +0 -150
- data/spec/models/model_buffers_spec.rb +0 -9
- data/spec/models/old_model_spec.rb +0 -67
- data/spec/models/reactive_array_spec.rb +0 -364
- data/spec/models/reactive_block_spec.rb +0 -13
- data/spec/models/reactive_call_times_spec.rb +0 -28
- data/spec/models/reactive_generator_spec.rb +0 -58
- data/spec/models/reactive_tags_spec.rb +0 -35
- data/spec/models/reactive_value_spec.rb +0 -370
- data/spec/models/store_spec.rb +0 -16
- data/spec/models/string_extensions_spec.rb +0 -57
@@ -0,0 +1,159 @@
|
|
1
|
+
if ENV['BROWSER']
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe "bindings test", :type => :feature do
|
5
|
+
it "should load the page" do
|
6
|
+
visit '/'
|
7
|
+
|
8
|
+
expect(page).to have_content('Kitchen Sink')
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "text/fields" do
|
12
|
+
it 'should load the bindings page and update bindings' do
|
13
|
+
visit '/'
|
14
|
+
|
15
|
+
click_link 'Bindings'
|
16
|
+
|
17
|
+
# Fill in one field and see if it updates the rest
|
18
|
+
fill_in('pageName1', :with => 'Page bindings')
|
19
|
+
expect(find('#pageName1').value).to eq('Page bindings')
|
20
|
+
expect(find('#pageName2').value).to eq('Page bindings')
|
21
|
+
expect(find('#pageName3')).to have_content('Page bindings')
|
22
|
+
|
23
|
+
fill_in('pageName2', :with => 'Update everywhere')
|
24
|
+
expect(find('#pageName1').value).to eq('Update everywhere')
|
25
|
+
expect(find('#pageName2').value).to eq('Update everywhere')
|
26
|
+
expect(find('#pageName3')).to have_content('Update everywhere')
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should update params bindings and the url' do
|
30
|
+
visit '/'
|
31
|
+
|
32
|
+
click_link 'Bindings'
|
33
|
+
|
34
|
+
# phantom does not support the html5 history api
|
35
|
+
# TODO: We could probably polyfill this in phantom
|
36
|
+
if ENV['BROWSER'] != 'phantom'
|
37
|
+
expect(current_path).to eq('/bindings')
|
38
|
+
end
|
39
|
+
|
40
|
+
# Fill in one field and see if it updates the rest
|
41
|
+
fill_in('paramsName1', :with => 'Params bindings')
|
42
|
+
expect(find('#paramsName1').value).to eq('Params bindings')
|
43
|
+
expect(find('#paramsName2').value).to eq('Params bindings')
|
44
|
+
expect(find('#paramsName3')).to have_content('Params bindings')
|
45
|
+
|
46
|
+
fill_in('paramsName2', :with => 'Update everywhere')
|
47
|
+
expect(find('#paramsName1').value).to eq('Update everywhere')
|
48
|
+
expect(find('#paramsName2').value).to eq('Update everywhere')
|
49
|
+
expect(find('#paramsName3')).to have_content('Update everywhere')
|
50
|
+
|
51
|
+
if ENV['BROWSER'] != 'phantom'
|
52
|
+
expect(current_url).to match(/\/bindings[?]name[=]Update%20everywhere$/)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should update the url and fields when bound to a param in the route' do
|
57
|
+
visit '/'
|
58
|
+
|
59
|
+
click_link 'Bindings'
|
60
|
+
|
61
|
+
# phantom does not support the html5 history api
|
62
|
+
# TODO: We could probably polyfill this in phantom
|
63
|
+
if ENV['BROWSER'] != 'phantom'
|
64
|
+
expect(current_path).to eq('/bindings')
|
65
|
+
end
|
66
|
+
|
67
|
+
# Fill in one field and see if it updates the rest
|
68
|
+
fill_in('routesName1', :with => 'Routes bindings')
|
69
|
+
expect(find('#routesName1').value).to eq('Routes bindings')
|
70
|
+
expect(find('#routesName2').value).to eq('Routes bindings')
|
71
|
+
expect(find('#routesName3')).to have_content('Routes bindings')
|
72
|
+
|
73
|
+
fill_in('routesName2', :with => 'bound_url')
|
74
|
+
expect(find('#routesName1').value).to eq('bound_url')
|
75
|
+
expect(find('#routesName2').value).to eq('bound_url')
|
76
|
+
expect(find('#routesName3')).to have_content('bound_url')
|
77
|
+
|
78
|
+
if ENV['BROWSER'] != 'phantom'
|
79
|
+
expect(current_path).to eq('/bindings/bound_url')
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should go from a url and query to params' do
|
84
|
+
visit '/bindings/testing?name=cool'
|
85
|
+
|
86
|
+
expect(find('#paramsName3')).to have_content('cool')
|
87
|
+
expect(find('#routesName3')).to have_content('testing')
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'should load the bindings page and update bindings' do
|
91
|
+
visit '/'
|
92
|
+
|
93
|
+
click_link 'Bindings'
|
94
|
+
|
95
|
+
# Fill in one field and see if it updates the rest
|
96
|
+
fill_in('textareaName1', :with => 'Page bindings')
|
97
|
+
expect(find('#textareaName1').value).to eq('Page bindings')
|
98
|
+
expect(find('#textareaName2').value).to eq('Page bindings')
|
99
|
+
expect(find('#textareaName3')).to have_content('Page bindings')
|
100
|
+
|
101
|
+
fill_in('textareaName2', :with => 'Update everywhere')
|
102
|
+
expect(find('#textareaName1').value).to eq('Update everywhere')
|
103
|
+
expect(find('#textareaName2').value).to eq('Update everywhere')
|
104
|
+
expect(find('#textareaName3')).to have_content('Update everywhere')
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
describe "check boxes" do
|
110
|
+
it 'should load the bindings page and update checkboxes' do
|
111
|
+
visit '/'
|
112
|
+
|
113
|
+
click_link 'Bindings'
|
114
|
+
|
115
|
+
expect(find('#pageCheck3')).to have_content('')
|
116
|
+
# Fill in one field and see if it updates the rest
|
117
|
+
check('pageCheck1')
|
118
|
+
expect(find('#pageCheck1')).to be_checked
|
119
|
+
expect(find('#pageCheck2')).to be_checked
|
120
|
+
expect(find('#pageCheck3')).to have_content('true')
|
121
|
+
|
122
|
+
uncheck('pageCheck1')
|
123
|
+
expect(find('#pageCheck1')).to_not be_checked
|
124
|
+
expect(find('#pageCheck2')).to_not be_checked
|
125
|
+
expect(find('#pageCheck3')).to have_content('')
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'should load the bindings page and update checkboxes bound to params' do
|
129
|
+
visit '/'
|
130
|
+
|
131
|
+
click_link 'Bindings'
|
132
|
+
|
133
|
+
if ENV['BROWSER'] != 'phantom'
|
134
|
+
expect(current_path).to eq('/bindings')
|
135
|
+
end
|
136
|
+
|
137
|
+
expect(find('#paramsCheck3')).to have_content('')
|
138
|
+
# Fill in one field and see if it updates the rest
|
139
|
+
check('paramsCheck1')
|
140
|
+
expect(find('#paramsCheck1')).to be_checked
|
141
|
+
expect(find('#paramsCheck2')).to be_checked
|
142
|
+
expect(find('#paramsCheck3')).to have_content('true')
|
143
|
+
|
144
|
+
if ENV['BROWSER'] != 'phantom'
|
145
|
+
expect(current_url).to match(/\/bindings[?]check[=]true$/)
|
146
|
+
end
|
147
|
+
|
148
|
+
uncheck('paramsCheck1')
|
149
|
+
expect(find('#paramsCheck1')).to_not be_checked
|
150
|
+
expect(find('#paramsCheck2')).to_not be_checked
|
151
|
+
expect(find('#paramsCheck3')).to have_content('')
|
152
|
+
|
153
|
+
if ENV['BROWSER'] != 'phantom'
|
154
|
+
expect(current_url).to match(/\/bindings[?]check[=]false$/)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
if ENV['BROWSER']
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe "bindings test", :type => :feature do
|
5
|
+
it "should change the title when changing pages" do
|
6
|
+
visit '/'
|
7
|
+
|
8
|
+
expect(page).to have_title 'KitchenSink - KitchenSink'
|
9
|
+
click_link 'Bindings'
|
10
|
+
|
11
|
+
expect(page).to have_title 'Bindings - KitchenSink'
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/spec/models/model_spec.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'volt/models'
|
2
|
+
require 'volt/reactive/dependency'
|
3
|
+
require 'volt/reactive/computation'
|
2
4
|
|
3
5
|
|
4
6
|
class TestItem < Model
|
@@ -15,195 +17,143 @@ describe Model do
|
|
15
17
|
end
|
16
18
|
|
17
19
|
it "should update other values off the same model" do
|
18
|
-
a =
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
a = Model.new
|
21
|
+
|
22
|
+
values = []
|
23
|
+
-> { values << a._name }.watch!
|
24
|
+
|
25
|
+
expect(values).to eq([nil])
|
26
|
+
Computation.flush!
|
22
27
|
|
23
28
|
a._name = 'Bob'
|
24
|
-
|
29
|
+
|
30
|
+
Computation.flush!
|
31
|
+
expect(values).to eq([nil, 'Bob'])
|
25
32
|
end
|
26
33
|
|
27
34
|
it "should say unregistered attributes are nil" do
|
28
|
-
a =
|
35
|
+
a = Model.new
|
29
36
|
b = a._missing == nil
|
30
|
-
expect(b
|
37
|
+
expect(b).to eq(true)
|
31
38
|
end
|
32
39
|
|
33
40
|
it "should negate nil and false correctly" do
|
34
|
-
a =
|
35
|
-
expect((!a._missing)
|
41
|
+
a = Model.new
|
42
|
+
expect((!a._missing)).to eq(true)
|
36
43
|
|
37
44
|
a._mis1 = nil
|
38
45
|
a._false1 = false
|
39
46
|
|
40
|
-
expect(
|
41
|
-
expect(
|
47
|
+
expect(!a._mis1).to eq(true)
|
48
|
+
expect(!a._false1).to eq(true)
|
42
49
|
end
|
43
50
|
|
44
51
|
it "should return a nil model for an underscore value that doesn't exist" do
|
45
52
|
a = Model.new
|
46
|
-
expect(a._something.
|
53
|
+
expect(a._something.attributes).to eq(nil)
|
47
54
|
end
|
48
55
|
|
49
|
-
it "should let you bind before something is defined" do
|
50
|
-
a = ReactiveValue.new(Model.new)
|
51
|
-
|
52
|
-
b = a._one + 5
|
53
|
-
expect(b.cur.class).to eq(NoMethodError)
|
54
|
-
|
55
|
-
count = 0
|
56
|
-
b.on('changed') { count += 1 }
|
57
|
-
expect(count).to eq(0)
|
58
|
-
|
59
|
-
a._one = 1
|
60
|
-
expect(count).to eq(1)
|
61
|
-
expect(b.cur).to eq(6)
|
62
|
-
end
|
63
|
-
|
64
|
-
it "should let you access an array element before its defined" do
|
65
|
-
# TODO: ...
|
66
|
-
end
|
67
56
|
|
68
57
|
it "should trigger changed once when a new value is assigned." do
|
69
|
-
a =
|
58
|
+
a = Model.new
|
70
59
|
|
71
60
|
count = 0
|
72
|
-
a._blue
|
61
|
+
-> { a._blue ; count += 1 }.watch!
|
62
|
+
expect(count).to eq(1)
|
73
63
|
|
74
64
|
a._blue = 'one'
|
75
|
-
|
76
|
-
a._blue = 'two'
|
65
|
+
Computation.flush!
|
77
66
|
expect(count).to eq(2)
|
67
|
+
|
68
|
+
a._blue = 'two'
|
69
|
+
Computation.flush!
|
70
|
+
expect(count).to eq(3)
|
78
71
|
end
|
79
72
|
|
80
|
-
it "should not
|
81
|
-
a =
|
73
|
+
it "should not trigger changed on other attributes" do
|
74
|
+
a = Model.new
|
82
75
|
|
83
76
|
blue_count = 0
|
84
77
|
green_count = 0
|
85
|
-
a._blue.on('changed') { blue_count += 1 }
|
86
|
-
a._green.on('changed') { green_count += 1}
|
87
|
-
expect(blue_count).to eq(0)
|
88
|
-
expect(green_count).to eq(0)
|
89
78
|
|
90
|
-
a._green = 'one'
|
91
|
-
expect(blue_count).to eq(0)
|
92
|
-
expect(green_count).to eq(1)
|
93
79
|
|
94
|
-
a._blue
|
80
|
+
-> { a._blue ; blue_count += 1 }.watch!
|
81
|
+
-> { a._green ; green_count += 1 }.watch!
|
95
82
|
expect(blue_count).to eq(1)
|
96
83
|
expect(green_count).to eq(1)
|
97
84
|
|
85
|
+
a._green = 'one'
|
86
|
+
Computation.flush!
|
87
|
+
expect(blue_count).to eq(1)
|
88
|
+
expect(green_count).to eq(2)
|
89
|
+
|
90
|
+
a._blue = 'two'
|
91
|
+
Computation.flush!
|
92
|
+
expect(blue_count).to eq(2)
|
93
|
+
expect(green_count).to eq(2)
|
98
94
|
end
|
99
95
|
|
100
96
|
it "should call change through arguments" do
|
101
|
-
a =
|
97
|
+
a = Model.new
|
102
98
|
a._one = 1
|
103
99
|
a._two = 2
|
100
|
+
a._three = 3
|
104
101
|
|
105
|
-
c =
|
106
|
-
|
102
|
+
c = nil
|
107
103
|
count = 0
|
108
|
-
c.
|
109
|
-
expect(count).to eq(0)
|
104
|
+
-> { c = a._one + a._two ; count += 1 }.watch!
|
110
105
|
|
111
|
-
a._two = 5
|
112
106
|
expect(count).to eq(1)
|
113
107
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
it "should store reactive values in arrays and trigger updates when those values change" do
|
118
|
-
a = ReactiveValue.new(Model.new)
|
119
|
-
b = ReactiveValue.new('blue')
|
120
|
-
a._items << b
|
121
|
-
b.cur = 'two'
|
122
|
-
|
123
|
-
|
124
|
-
a = ReactiveValue.new(Model.new)
|
125
|
-
b = ReactiveValue.new('blue')
|
126
|
-
a._one = 1
|
127
|
-
a._items << 0
|
128
|
-
|
129
|
-
count_1 = 0
|
130
|
-
a._items[1].on('changed') { count_1 += 1 }
|
131
|
-
expect(count_1).to eq(0)
|
132
|
-
|
133
|
-
a._items << b
|
134
|
-
expect(count_1).to eq(1)
|
135
|
-
|
136
|
-
b.cur = 'update'
|
137
|
-
expect(count_1).to eq(2)
|
138
|
-
|
139
|
-
count_2 = 0
|
140
|
-
a._items[2].on('changed') { count_2 += 1 }
|
141
|
-
expect(count_2).to eq(0)
|
142
|
-
|
143
|
-
a._items << a._one
|
144
|
-
expect(count_2).to eq(1)
|
145
|
-
|
146
|
-
a._one = 'updated'
|
147
|
-
expect(count_1).to eq(2)
|
148
|
-
expect(count_2).to eq(2)
|
149
|
-
end
|
108
|
+
a._two = 5
|
109
|
+
Computation.flush!
|
110
|
+
expect(count).to eq(2)
|
150
111
|
|
151
|
-
|
152
|
-
|
153
|
-
count
|
154
|
-
a._something.on('changed') { count += 1 }
|
155
|
-
expect(count).to eq(0)
|
112
|
+
a._one = 6
|
113
|
+
Computation.flush!
|
114
|
+
expect(count).to eq(3)
|
156
115
|
|
157
|
-
a.
|
158
|
-
|
116
|
+
a._three = 7
|
117
|
+
Computation.flush!
|
118
|
+
expect(count).to eq(3)
|
159
119
|
end
|
160
120
|
|
161
|
-
it
|
162
|
-
model =
|
163
|
-
|
164
|
-
|
121
|
+
it 'should update through a normal array' do
|
122
|
+
model = Model.new
|
123
|
+
array = []
|
124
|
+
array << model
|
165
125
|
|
166
|
-
|
167
|
-
concat.on('changed') { count += 1 }
|
168
|
-
expect(count).to eq(0)
|
169
|
-
|
170
|
-
model._one = 'one'
|
171
|
-
expect(count).to eq(1)
|
126
|
+
values = []
|
172
127
|
|
173
|
-
|
174
|
-
expect(count).to eq(2)
|
128
|
+
-> { values << array[0]._prop }.watch!
|
175
129
|
|
176
|
-
expect(
|
177
|
-
end
|
130
|
+
expect(values).to eq([nil])
|
178
131
|
|
179
|
-
|
180
|
-
|
132
|
+
model._prop = 'one'
|
133
|
+
Computation.flush!
|
181
134
|
|
182
|
-
|
183
|
-
model._items << {_name: 'Two'}
|
135
|
+
expect(values).to eq([nil, 'one'])
|
184
136
|
|
185
|
-
current = model._items[model._index]
|
186
|
-
|
187
|
-
model._current_item = current
|
188
137
|
end
|
189
138
|
|
190
139
|
it "should trigger changed for any indicies after a deleted index" do
|
191
|
-
model =
|
140
|
+
model = Model.new
|
192
141
|
|
193
142
|
model._items << {_name: 'One'}
|
194
143
|
model._items << {_name: 'Two'}
|
195
144
|
model._items << {_name: 'Three'}
|
196
145
|
|
197
146
|
count = 0
|
198
|
-
model._items[2]
|
199
|
-
expect(count).to eq(
|
147
|
+
-> { model._items[2] ; count += 1 }.watch!
|
148
|
+
expect(count).to eq(1)
|
200
149
|
|
201
150
|
model._items.delete_at(1)
|
202
|
-
|
151
|
+
Computation.flush!
|
152
|
+
expect(count).to eq(2)
|
203
153
|
end
|
204
154
|
|
205
155
|
it "should change the size and length when an item gets added" do
|
206
|
-
model =
|
156
|
+
model = Model.new
|
207
157
|
|
208
158
|
model._items << {_name: 'One'}
|
209
159
|
size = model._items.size
|
@@ -211,151 +161,113 @@ describe Model do
|
|
211
161
|
|
212
162
|
count_size = 0
|
213
163
|
count_length = 0
|
214
|
-
|
215
|
-
|
216
|
-
expect(count_size).to eq(0)
|
217
|
-
expect(count_length).to eq(0)
|
218
|
-
|
219
|
-
model._items << {_name: 'Two'}
|
164
|
+
-> { model._items.size ; count_size += 1 }.watch!
|
165
|
+
-> { model._items.length ; count_length += 1 }.watch!
|
220
166
|
expect(count_size).to eq(1)
|
221
167
|
expect(count_length).to eq(1)
|
168
|
+
|
169
|
+
model._items << {_name: 'Two'}
|
170
|
+
Computation.flush!
|
171
|
+
|
172
|
+
expect(count_size).to eq(2)
|
173
|
+
expect(count_length).to eq(2)
|
222
174
|
end
|
223
175
|
|
224
176
|
it "should add doubly nested arrays" do
|
225
|
-
model =
|
177
|
+
model = Model.new
|
226
178
|
|
227
179
|
model._items << {_name: 'Cool', _lists: []}
|
228
180
|
model._items[0]._lists << {_name: 'worked'}
|
229
|
-
expect(model._items[0]._lists[0]._name
|
181
|
+
expect(model._items[0]._lists[0]._name).to eq('worked')
|
230
182
|
end
|
231
183
|
|
232
184
|
it "should make pushed subarrays into ArrayModels" do
|
233
|
-
model =
|
185
|
+
model = Model.new
|
234
186
|
|
235
187
|
model._items << {_name: 'Test', _lists: []}
|
236
|
-
expect(model._items[0]._lists.
|
188
|
+
expect(model._items[0]._lists.class).to eq(ArrayModel)
|
237
189
|
end
|
238
190
|
|
239
191
|
it "should make assigned subarrays into ArrayModels" do
|
240
|
-
model =
|
192
|
+
model = Model.new
|
241
193
|
|
242
194
|
model._item._name = 'Test'
|
243
195
|
model._item._lists = []
|
244
|
-
expect(model._item._lists.
|
196
|
+
expect(model._item._lists.class).to eq(ArrayModel)
|
245
197
|
end
|
246
198
|
|
247
199
|
it "should call changed when a the reference to a submodel is assigned to another value" do
|
248
|
-
a =
|
200
|
+
a = Model.new
|
249
201
|
|
250
202
|
count = 0
|
251
|
-
a._blue.
|
252
|
-
expect(count).to eq(
|
203
|
+
-> { a._blue && a._blue.respond_to?(:_green) && a._blue._green ; count += 1 }.watch!
|
204
|
+
expect(count).to eq(1)
|
253
205
|
|
254
206
|
a._blue._green = 5
|
207
|
+
Computation.flush!
|
255
208
|
|
256
|
-
# TODO: Should
|
209
|
+
# TODO: Should equal 2
|
257
210
|
expect(count).to eq(2)
|
258
211
|
|
259
212
|
a._blue = 22
|
213
|
+
Computation.flush!
|
260
214
|
expect(count).to eq(3)
|
215
|
+
|
216
|
+
a._blue = {_green: 50}
|
217
|
+
expect(a._blue._green).to eq(50)
|
218
|
+
Computation.flush!
|
219
|
+
expect(count).to eq(4)
|
261
220
|
end
|
262
221
|
|
263
222
|
it "should trigger changed when a value is deleted" do
|
264
|
-
a =
|
223
|
+
a = Model.new
|
265
224
|
|
266
225
|
count = 0
|
267
|
-
a._blue
|
268
|
-
expect(count).to eq(0)
|
269
|
-
|
270
|
-
a._blue = 1
|
226
|
+
-> { a._blue ; count += 1 }.watch!
|
271
227
|
expect(count).to eq(1)
|
272
228
|
|
273
|
-
a.
|
229
|
+
a._blue = 1
|
230
|
+
Computation.flush!
|
274
231
|
expect(count).to eq(2)
|
275
|
-
end
|
276
|
-
|
277
|
-
it "should not trigger a change if the new value is exactly the same" do
|
278
232
|
|
233
|
+
a.delete(:_blue)
|
234
|
+
Computation.flush!
|
235
|
+
expect(count).to eq(3)
|
279
236
|
end
|
280
237
|
|
281
238
|
it "should let you append nested hashes" do
|
282
239
|
a = Model.new
|
283
|
-
# TODO: Fails
|
284
|
-
# a._items << {_name: {_text: 'Name'}}
|
285
|
-
end
|
286
|
-
|
287
|
-
it "should work" do
|
288
|
-
store = ReactiveValue.new(Model.new)
|
289
|
-
# params = ReactiveValue.new(Params.new)
|
290
|
-
index = ReactiveValue.new(0)
|
291
|
-
|
292
|
-
a = store._todo_lists
|
293
|
-
store._current_todo = a#[index]
|
294
|
-
|
295
|
-
added_count = 0
|
296
|
-
changed_count = 0
|
297
|
-
# store._todo_lists.on('added') { added_count += 1 }
|
298
|
-
store._current_todo.on('changed') { changed_count += 1 }
|
299
|
-
# expect(added_count).to eq(0)
|
300
|
-
expect(changed_count).to eq(0)
|
301
|
-
|
302
|
-
a.cur = 1000
|
303
|
-
# store._todo_lists << {_name: 'List 1', _todos: []}
|
304
240
|
|
241
|
+
a._items << {_name: {_text: 'Name'}}
|
305
242
|
|
306
|
-
|
307
|
-
|
308
|
-
# expect(added_count).to eq(1)
|
309
|
-
# expect(changed_count).to eq(1)
|
243
|
+
expect(a._items[0]._name._text).to eq('Name')
|
310
244
|
end
|
311
245
|
|
312
|
-
it "should handle a basic todo list with no setup" do
|
313
|
-
store = ReactiveValue.new(Model.new)
|
314
|
-
params = ReactiveValue.new(Model.new({}, persistor: Persistors::Params))
|
315
|
-
|
316
|
-
a = store._todo_lists
|
317
|
-
store._current_todo = store._todo_lists[params._index.or(0).to_i]
|
318
|
-
|
319
|
-
added_count = 0
|
320
|
-
changed_count = 0
|
321
|
-
store._todo_lists.on('added') { added_count += 1 }
|
322
|
-
store._current_todo.on('changed') { changed_count += 1 }
|
323
|
-
expect(added_count).to eq(0)
|
324
|
-
expect(changed_count).to eq(0)
|
325
|
-
|
326
|
-
store._todo_lists << {_name: 'List 1', _todos: []}
|
327
|
-
store._todo_lists[0]._todos << {_name: 'Todo 1'}
|
328
|
-
|
329
|
-
expect(added_count).to eq(1)
|
330
|
-
# expect(changed_count).to eq(1)
|
331
|
-
end
|
332
246
|
|
333
247
|
it "should not call added too many times" do
|
334
|
-
a =
|
248
|
+
a = Model.new
|
335
249
|
a._list << 1
|
336
|
-
ac = a._current_list = a._list[0]
|
337
250
|
|
338
251
|
count = 0
|
339
|
-
passed_count = 0
|
340
252
|
a._list.on('added') { count += 1 }
|
341
|
-
a._current_list.on('added') { passed_count += 1 }
|
342
253
|
expect(count).to eq(0)
|
343
|
-
expect(passed_count).to eq(0)
|
344
254
|
|
345
255
|
a._list << 2
|
346
256
|
expect(count).to eq(1)
|
347
|
-
expect(passed_count).to eq(0)
|
348
257
|
end
|
349
258
|
|
350
259
|
it "should propigate to different branches" do
|
351
|
-
a =
|
260
|
+
a = Model.new
|
352
261
|
count = 0
|
353
|
-
|
354
|
-
|
355
|
-
|
262
|
+
-> do
|
263
|
+
count += 1
|
264
|
+
a._new_item._name
|
265
|
+
end.watch!
|
266
|
+
expect(count).to eq(1)
|
356
267
|
|
357
268
|
a._new_item._name = 'Testing'
|
358
|
-
|
269
|
+
Computation.flush!
|
270
|
+
expect(count).to eq(2)
|
359
271
|
end
|
360
272
|
|
361
273
|
describe "paths" do
|
@@ -389,45 +301,35 @@ describe Model do
|
|
389
301
|
end
|
390
302
|
|
391
303
|
it "should trigger added when added" do
|
392
|
-
a =
|
304
|
+
a = Model.new
|
393
305
|
count = 0
|
394
306
|
b = a._items
|
395
307
|
|
396
308
|
b.on('added') { count += 1 }
|
397
309
|
expect(count).to eq(0)
|
398
310
|
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
# TODO: Without fetching this again, this fails.
|
403
|
-
c = b.cur
|
404
|
-
|
405
|
-
c << {_name: 'two'}
|
311
|
+
b << {_name: 'one'}
|
312
|
+
b << {_name: 'two'}
|
406
313
|
|
407
314
|
expect(count).to eq(2)
|
408
315
|
end
|
409
316
|
end
|
410
317
|
|
411
318
|
it "should trigger on false assign" do
|
412
|
-
a =
|
319
|
+
a = Model.new
|
320
|
+
count = 0
|
413
321
|
|
414
|
-
|
415
|
-
count2 = 0
|
322
|
+
-> { count += 1 ; a._complete }.watch!
|
416
323
|
|
417
|
-
|
418
|
-
c = a._complete
|
419
|
-
b.on('changed') { count1 += 1 }
|
420
|
-
c.on('changed') { count2 += 1 }
|
421
|
-
expect(count1).to eq(0)
|
324
|
+
expect(count).to eq(1)
|
422
325
|
|
423
326
|
a._complete = true
|
424
|
-
|
425
|
-
expect(
|
327
|
+
Computation.flush!
|
328
|
+
expect(count).to eq(2)
|
426
329
|
|
427
330
|
a._complete = false
|
428
|
-
|
429
|
-
expect(
|
430
|
-
|
331
|
+
Computation.flush!
|
332
|
+
expect(count).to eq(3)
|
431
333
|
end
|
432
334
|
|
433
335
|
it "should delete from an ArrayModel" do
|
@@ -461,11 +363,11 @@ describe Model do
|
|
461
363
|
a._items << {_name: 'Test1', _other: {_time: 'Now'}}
|
462
364
|
a._items << {_name: 'Test2', _other: {_time: 'Later'}}
|
463
365
|
|
464
|
-
item1 = a._items[0].to_h
|
366
|
+
item1 = a._items[0].to_h
|
465
367
|
expect(item1[:_name]).to eq('Test1')
|
466
368
|
expect(item1[:_other][:_time]).to eq('Now')
|
467
369
|
|
468
|
-
all_items = a._items.to_a
|
370
|
+
all_items = a._items.to_a
|
469
371
|
|
470
372
|
a = [
|
471
373
|
{:_name => "Test1", :_other => {:_time => "Now"}},
|
@@ -477,23 +379,23 @@ describe Model do
|
|
477
379
|
|
478
380
|
describe "model paths" do
|
479
381
|
before do
|
480
|
-
@model =
|
382
|
+
@model = Model.new
|
481
383
|
end
|
482
384
|
|
483
385
|
it "should set the model path" do
|
484
386
|
@model._object._name = 'Test'
|
485
|
-
expect(@model._object.path
|
387
|
+
expect(@model._object.path).to eq([:_object])
|
486
388
|
end
|
487
389
|
|
488
390
|
it "should set the model path for a sub array" do
|
489
391
|
@model._items << {_name: 'Bob'}
|
490
|
-
expect(@model._items.path
|
491
|
-
expect(@model._items[0].path
|
392
|
+
expect(@model._items.path).to eq([:_items])
|
393
|
+
expect(@model._items[0].path).to eq([:_items, :[]])
|
492
394
|
end
|
493
395
|
|
494
396
|
it "should set the model path for sub sub arrays" do
|
495
397
|
@model._lists << {_name: 'List 1', _items: []}
|
496
|
-
expect(@model._lists[0]._items.path
|
398
|
+
expect(@model._lists[0]._items.path).to eq([:_lists, :[], :_items])
|
497
399
|
end
|
498
400
|
|
499
401
|
it "should update the path when added from a model instance to a collection" do
|