volt 0.9.5.pre4 → 0.9.5.pre5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +13 -5
- data/app/volt/assets/css/{notices.css.scss → notices.scss} +0 -0
- data/app/volt/models/active_volt_instance.rb +1 -1
- data/app/volt/tasks/live_query/live_query.rb +11 -3
- data/app/volt/tasks/store_tasks.rb +14 -17
- data/lib/volt/cli.rb +22 -0
- data/lib/volt/cli/asset_compile.rb +63 -63
- data/lib/volt/cli/base_index_renderer.rb +26 -0
- data/lib/volt/cli/generate.rb +1 -1
- data/lib/volt/config.rb +1 -0
- data/lib/volt/controllers/model_controller.rb +37 -1
- data/lib/volt/extra_core/array.rb +22 -0
- data/lib/volt/models/array_model.rb +7 -1
- data/lib/volt/models/errors.rb +1 -1
- data/lib/volt/models/field_helpers.rb +36 -21
- data/lib/volt/models/model.rb +16 -0
- data/lib/volt/models/validations/validations.rb +21 -6
- data/lib/volt/models/validators/type_validator.rb +35 -3
- data/lib/volt/page/bindings/content_binding.rb +1 -1
- data/lib/volt/page/bindings/event_binding.rb +40 -16
- data/lib/volt/page/document_events.rb +8 -6
- data/lib/volt/reactive/reactive_array.rb +18 -1
- data/lib/volt/server/forking_server.rb +7 -1
- data/lib/volt/server/html_parser/attribute_scope.rb +26 -0
- data/lib/volt/server/html_parser/component_view_scope.rb +30 -22
- data/lib/volt/server/middleware/default_middleware_stack.rb +6 -1
- data/lib/volt/server/rack/asset_files.rb +5 -3
- data/lib/volt/server/rack/opal_files.rb +35 -23
- data/lib/volt/server/rack/sprockets_helpers_setup.rb +71 -0
- data/lib/volt/server/template_handlers/view_processor.rb +1 -2
- data/lib/volt/utils/promise_extensions.rb +1 -1
- data/lib/volt/version.rb +1 -1
- data/lib/volt/volt/app.rb +0 -2
- data/lib/volt/volt/client_setup/browser.rb +11 -0
- data/spec/apps/kitchen_sink/Gemfile +37 -14
- data/spec/apps/kitchen_sink/app/main/config/routes.rb +3 -0
- data/spec/apps/kitchen_sink/app/main/controllers/events_controller.rb +26 -0
- data/spec/apps/kitchen_sink/app/main/views/events/index.html +30 -0
- data/spec/apps/kitchen_sink/app/main/views/main/bindings.html +3 -0
- data/spec/apps/kitchen_sink/app/main/views/main/yield.html +1 -6
- data/spec/apps/kitchen_sink/app/main/views/{yield-component → yield_component}/index.html +0 -0
- data/spec/extra_core/array_spec.rb +26 -0
- data/spec/integration/bindings_spec.rb +9 -0
- data/spec/integration/event_spec.rb +19 -0
- data/spec/models/array_model_spec.rb +13 -0
- data/spec/models/field_helpers_spec.rb +2 -2
- data/spec/models/validations_spec.rb +31 -0
- data/spec/models/validators/type_validator_spec.rb +47 -1
- data/spec/reactive/reactive_array_spec.rb +46 -0
- data/spec/server/forking_server_spec.rb +27 -0
- data/spec/server/html_parser/view_scope_spec.rb +44 -0
- data/spec/server/rack/asset_files_spec.rb +2 -2
- data/templates/project/Gemfile.tt +8 -0
- data/templates/project/config/app.rb.tt +2 -1
- data/volt.gemspec +1 -1
- metadata +31 -5
@@ -13,6 +13,9 @@ client '/html_safe', action: 'html_safe'
|
|
13
13
|
client '/missing', action: 'missing'
|
14
14
|
client '/require_test', action: 'require_test'
|
15
15
|
|
16
|
+
# Events
|
17
|
+
client '/events', component: 'main', controller: 'events', action: 'index'
|
18
|
+
|
16
19
|
# Signup/login routes
|
17
20
|
client '/signup', component: 'user_templates', controller: 'signup'
|
18
21
|
client '/login', component: 'user_templates', controller: 'login'
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Main
|
2
|
+
class EventsController < Volt::ModelController
|
3
|
+
reactive_accessor :ran_some_event
|
4
|
+
reactive_accessor :ran_other_event
|
5
|
+
|
6
|
+
def trig_some_event
|
7
|
+
trigger('some_event', 'yes')
|
8
|
+
end
|
9
|
+
|
10
|
+
def some_event(passes_args, event)
|
11
|
+
if passes_args == 'yes' && event.is_a?(Volt::JSEvent)
|
12
|
+
self.ran_some_event = true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def trig_other_event
|
17
|
+
trigger('other_event', 'yes')
|
18
|
+
end
|
19
|
+
|
20
|
+
def other_event(passes_args)
|
21
|
+
if passes_args == 'yes'
|
22
|
+
self.ran_other_event = true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
<:Title>
|
2
|
+
Events
|
3
|
+
|
4
|
+
<:Body>
|
5
|
+
<h1>Events</h1>
|
6
|
+
|
7
|
+
<div e-some_event="some_event">
|
8
|
+
<:some-event-button />
|
9
|
+
</div>
|
10
|
+
|
11
|
+
|
12
|
+
<:other-event-button e-other-event="other_event" />
|
13
|
+
|
14
|
+
|
15
|
+
{{ if ran_some_event }}
|
16
|
+
ran some_event
|
17
|
+
{{ end }}
|
18
|
+
|
19
|
+
{{ if ran_other_event }}
|
20
|
+
ran other_event
|
21
|
+
{{ end }}
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
<:SomeEventButton>
|
27
|
+
<button e-click="trig_some_event">Run Some Event</button>
|
28
|
+
|
29
|
+
<:OtherEventButton>
|
30
|
+
<button e-click="trig_other_event">Run Other Event</button>
|
@@ -6,13 +6,8 @@
|
|
6
6
|
|
7
7
|
<:yield-component>This is my {{ content_string }}</:yield-component>
|
8
8
|
|
9
|
-
<:
|
9
|
+
<:YieldTest>
|
10
10
|
<h1>Yield Section</h1>
|
11
11
|
|
12
12
|
<p>{{ yield }}</p>
|
13
13
|
<p>{{ yield }}</p>
|
14
|
-
|
15
|
-
|
16
|
-
{{ if Volt.current_user }}
|
17
|
-
Email: {{ Volt.current_user._email }}
|
18
|
-
{{ end }}
|
File without changes
|
@@ -7,4 +7,30 @@ describe Array do
|
|
7
7
|
expect([1, 2, 3].sum).to eq(6)
|
8
8
|
end
|
9
9
|
end
|
10
|
+
|
11
|
+
describe "#to_sentence" do
|
12
|
+
it 'should return an empty string' do
|
13
|
+
expect([].to_sentence).to eq('')
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should return a single entry' do
|
17
|
+
expect([1].to_sentence).to eq('1')
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should combine an array into a string with a conjunection and commas' do
|
21
|
+
expect([1,2,3].to_sentence).to eq('1, 2, and 3')
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should allow you to build an incorrect sentence' do
|
25
|
+
expect([1,2,3].to_sentence(oxford: false)).to eq('1, 2 and 3')
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'let you change the conjunction' do
|
29
|
+
expect([1,2,3].to_sentence(conjunction: 'or')).to eq('1, 2, or 3')
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'let you change the comma' do
|
33
|
+
expect([1,2,3].to_sentence(comma: '!')).to eq('1! 2! and 3')
|
34
|
+
end
|
35
|
+
end
|
10
36
|
end
|
@@ -243,6 +243,15 @@ describe 'bindings test', type: :feature, sauce: true do
|
|
243
243
|
end
|
244
244
|
end
|
245
245
|
|
246
|
+
describe 'raw' do
|
247
|
+
it 'should print the raw version, and work with promises' do
|
248
|
+
visit '/bindings'
|
249
|
+
|
250
|
+
expect(page).to have_content("some \ncode")
|
251
|
+
expect(page).to have_content("some \nother code")
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
246
255
|
# NOTE: For some reason this spec fails randomly (capybara issue I think)
|
247
256
|
# describe "events" do
|
248
257
|
# it 'should handle focus and blur' do
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'events and bubbling', type: :feature, sauce: true do
|
4
|
+
it 'should bubble events through the dom' do
|
5
|
+
visit '/events'
|
6
|
+
|
7
|
+
click_button 'Run Some Event'
|
8
|
+
|
9
|
+
expect(page).to have_content('ran some_event')
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should let you specify an e- handler on components' do
|
13
|
+
visit '/events'
|
14
|
+
|
15
|
+
click_button 'Run Other Event'
|
16
|
+
|
17
|
+
expect(page).to have_content('ran other_event')
|
18
|
+
end
|
19
|
+
end
|
@@ -28,6 +28,7 @@ describe Volt::ArrayModel do
|
|
28
28
|
expect(array_model.index(2)).to eq(1)
|
29
29
|
end
|
30
30
|
|
31
|
+
|
31
32
|
it 'should flatten' do
|
32
33
|
array = Volt::ArrayModel.new([])
|
33
34
|
|
@@ -37,4 +38,16 @@ describe Volt::ArrayModel do
|
|
37
38
|
expect(array.flatten.size).to eq(6)
|
38
39
|
expect(array.to_a.flatten.size).to eq(6)
|
39
40
|
end
|
41
|
+
|
42
|
+
unless RUBY_PLATFORM == 'opal'
|
43
|
+
it 'should return a promise for store on .length, .size, and .count' do
|
44
|
+
store._items << {name: 'One'}
|
45
|
+
|
46
|
+
[:size, :count, :length].each do |method_name|
|
47
|
+
val = store._items.send(method_name)
|
48
|
+
expect(val.class).to eq(Promise)
|
49
|
+
expect(val.sync).to eq(1)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
40
53
|
end
|
@@ -26,7 +26,7 @@ describe 'field helpers' do
|
|
26
26
|
|
27
27
|
it 'should raise an error when an invalid cast type is provided' do
|
28
28
|
expect do
|
29
|
-
ExampleModelWithField2.field :awesome,
|
29
|
+
ExampleModelWithField2.field :awesome, Range
|
30
30
|
end.to raise_error(FieldHelpers::InvalidFieldClass)
|
31
31
|
end
|
32
32
|
|
@@ -47,6 +47,6 @@ describe 'field helpers' do
|
|
47
47
|
end
|
48
48
|
|
49
49
|
it 'should track the fields on the model class' do
|
50
|
-
expect(ExampleModelWithField.fields_data).to eq({:name=>[nil, {}], :value=>[Numeric, {}]})
|
50
|
+
expect(ExampleModelWithField.fields_data).to eq({:name=>[nil, {}], :value=>[[Numeric, NilClass], {}]})
|
51
51
|
end
|
52
52
|
end
|
@@ -165,4 +165,35 @@ describe Volt::Model do
|
|
165
165
|
expect(model._name).to eq('Jimmy')
|
166
166
|
end
|
167
167
|
|
168
|
+
describe 'custom validations' do
|
169
|
+
let(:model) { test_model_with_custom_validation.new }
|
170
|
+
let(:test_model_with_custom_validation) do
|
171
|
+
Class.new(Volt::Model) do
|
172
|
+
validations do
|
173
|
+
validate :something, presence: true
|
174
|
+
validate do
|
175
|
+
if _name.present?
|
176
|
+
{}
|
177
|
+
else
|
178
|
+
{name: ['must be present']}
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'should return errors for all failed validations' do
|
186
|
+
model.validate!
|
187
|
+
expect(model.errors).to eq(
|
188
|
+
name: ['must be present'],
|
189
|
+
something: ['must be specified']
|
190
|
+
)
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'should not return errors for all passing custom validations' do
|
194
|
+
model._name = 'something'
|
195
|
+
model.validate!
|
196
|
+
expect(model.errors).to eq({something: ["must be specified"]})
|
197
|
+
end
|
198
|
+
end
|
168
199
|
end
|
@@ -20,7 +20,7 @@ describe Volt::TypeValidator do
|
|
20
20
|
describe 'when count is a string' do
|
21
21
|
let(:count) { 'Cats' }
|
22
22
|
it do
|
23
|
-
expect(subject).to eq({count: ['must be
|
23
|
+
expect(subject).to eq({count: ['must be a number']})
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
@@ -41,6 +41,52 @@ describe Volt::TypeValidator do
|
|
41
41
|
expect(subject).to eq(count: ['must be a number'])
|
42
42
|
end
|
43
43
|
end
|
44
|
+
|
45
|
+
# Fails on opal because no True/False class
|
46
|
+
# describe 'when passing in multiple types' do
|
47
|
+
# let(:options) do
|
48
|
+
# { types: [TrueClass, FalseClass] }
|
49
|
+
# end
|
50
|
+
# let(:count) { 'a string' }
|
51
|
+
|
52
|
+
# it do
|
53
|
+
# expect(subject).to eq({count: ['must be true or false']})
|
54
|
+
# end
|
55
|
+
# end
|
56
|
+
|
57
|
+
describe 'when passing in Volt::Boolean' do
|
58
|
+
let(:options) do
|
59
|
+
{ type: Volt::Boolean }
|
60
|
+
end
|
61
|
+
let(:count) { 'a string' }
|
62
|
+
|
63
|
+
it do
|
64
|
+
expect(subject).to eq({count: ['must be true or false']})
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe 'when passing in multiple types' do
|
69
|
+
let(:options) do
|
70
|
+
{ types: [String, Float] }
|
71
|
+
end
|
72
|
+
let(:count) { 1..1 }
|
73
|
+
|
74
|
+
it do
|
75
|
+
expect(subject).to eq({count: ['must be a String or a number']})
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe 'when passing in multiple types with nil' do
|
80
|
+
let(:options) do
|
81
|
+
{ types: [String, Float, NilClass] }
|
82
|
+
end
|
83
|
+
let(:count) { 1..1 }
|
84
|
+
|
85
|
+
it do
|
86
|
+
expect(subject).to eq({count: ['must be a String or a number']})
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
44
90
|
end
|
45
91
|
end
|
46
92
|
end
|
@@ -38,6 +38,52 @@ describe Volt::ReactiveArray do
|
|
38
38
|
expect(values).to eq([nil, 4])
|
39
39
|
end
|
40
40
|
|
41
|
+
describe ".last" do
|
42
|
+
let(:array) { Volt::ReactiveArray.new([1,2,3]) }
|
43
|
+
|
44
|
+
it 'should trigger changed on .last when appending or inserting' do
|
45
|
+
values = []
|
46
|
+
-> { values << array.last }.watch!
|
47
|
+
|
48
|
+
expect(values).to eq([3])
|
49
|
+
|
50
|
+
array << 4
|
51
|
+
Volt::Computation.flush!
|
52
|
+
expect(values).to eq([3,4])
|
53
|
+
|
54
|
+
array.insert(1,2)
|
55
|
+
Volt::Computation.flush!
|
56
|
+
expect(values).to eq([3,4,4])
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should trigger changed on .last when the last value is changed' do
|
60
|
+
values = []
|
61
|
+
-> { values << array.last }.watch!
|
62
|
+
expect(values).to eq([3])
|
63
|
+
|
64
|
+
array[2] = 5
|
65
|
+
Volt::Computation.flush!
|
66
|
+
expect(values).to eq([3,5])
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should not trigger changed on .last when a value other than last changes' do
|
70
|
+
values = []
|
71
|
+
-> { values << array.last }.watch!
|
72
|
+
expect(values).to eq([3])
|
73
|
+
|
74
|
+
array[1] = 20
|
75
|
+
expect(values).to eq([3])
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should not raise an error on a negative lookup on an empty array' do
|
79
|
+
array = Volt::ArrayModel.new([])
|
80
|
+
|
81
|
+
expect do
|
82
|
+
array[-1]
|
83
|
+
end.not_to raise_error
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
41
87
|
it 'should trigger changes for each cell after index after insert' do
|
42
88
|
a = Volt::ReactiveArray.new([1, 2, 3])
|
43
89
|
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
unless RUBY_PLATFORM == 'opal'
|
3
|
+
require 'volt/server'
|
4
|
+
|
5
|
+
describe Volt::ForkingServer do
|
6
|
+
it 'should set polling an an option when using POLL_FS env' do
|
7
|
+
ENV['POLL_FS'] = 'true'
|
8
|
+
forking_server = Volt::ForkingServer.allocate
|
9
|
+
|
10
|
+
# Lots of stubs, since we're working with the FS
|
11
|
+
listener = double('listener')
|
12
|
+
expect(listener).to receive(:start)
|
13
|
+
expect(listener).to receive(:stop)
|
14
|
+
expect(Listen).to receive(:to).with('/app/', {force_polling: true}).and_return(listener)
|
15
|
+
expect(forking_server).to receive(:sync_mod_time)
|
16
|
+
|
17
|
+
server = double('server')
|
18
|
+
expect(server).to receive(:app_path).and_return('/app')
|
19
|
+
forking_server.instance_variable_set(:@server, server)
|
20
|
+
|
21
|
+
forking_server.start_change_listener
|
22
|
+
ENV.delete('POLL_FS')
|
23
|
+
|
24
|
+
forking_server.stop_change_listener
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Volt::ViewScope do
|
4
|
+
describe "methodize strings" do
|
5
|
+
def methodize(str)
|
6
|
+
Volt::ViewScope.methodize_string(str)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should methodize a method without args' do
|
10
|
+
code = methodize('something')
|
11
|
+
expect(code).to eq('method(:something)')
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should methodize a method without args2' do
|
15
|
+
code = methodize('something?')
|
16
|
+
expect(code).to eq('method(:something?)')
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should methodize a method wit args1' do
|
20
|
+
code = methodize('set_something(true)')
|
21
|
+
expect(code).to eq('set_something(true)')
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should not methodize a method call with args' do
|
25
|
+
code = methodize('something(item1, item2)')
|
26
|
+
expect(code).to eq('something(item1, item2)')
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should not methodize on assignment' do
|
30
|
+
code = methodize('params._something = 5')
|
31
|
+
expect(code).to eq('params._something = 5')
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should not methodize on hash lookup' do
|
35
|
+
code = methodize('hash[:something]')
|
36
|
+
expect(code).to eq('hash[:something]')
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should not methodize on instance variables' do
|
40
|
+
code = methodize('@something.call')
|
41
|
+
expect(code).to eq('@something.call')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -21,7 +21,7 @@ if RUBY_PLATFORM != 'opal'
|
|
21
21
|
context "when the component's dependencies.rb does not contain .disable_auto_import" do
|
22
22
|
it 'should list all JS files' do
|
23
23
|
main = Volt::AssetFiles.new('main', @component_paths)
|
24
|
-
expect(main.javascript(volt_app)).to eq([
|
24
|
+
expect(main.javascript(volt_app).reject{|v| v[0] != :src }).to eq([
|
25
25
|
[:src, '/assets/js/jquery-2.0.3.js'],
|
26
26
|
[:src, '/assets/js/volt_js_polyfills.js'],
|
27
27
|
[:src, '/assets/js/volt_watch.js'],
|
@@ -38,7 +38,7 @@ if RUBY_PLATFORM != 'opal'
|
|
38
38
|
context "when the component's dependencies.rb contains .disable_auto_import" do
|
39
39
|
it 'should list only the files included via the css_file helpers' do
|
40
40
|
disabled_auto = Volt::AssetFiles.new('disable_auto', @component_paths)
|
41
|
-
expect(disabled_auto.javascript(volt_app)).to eq([
|
41
|
+
expect(disabled_auto.javascript(volt_app).reject{|v| v[0] != :src }).to eq([
|
42
42
|
[:src, '/assets/js/jquery-2.0.3.js'],
|
43
43
|
[:src, '/assets/js/volt_js_polyfills.js'],
|
44
44
|
[:src, '/assets/js/volt_watch.js'],
|