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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/README.md +13 -5
  4. data/app/volt/assets/css/{notices.css.scss → notices.scss} +0 -0
  5. data/app/volt/models/active_volt_instance.rb +1 -1
  6. data/app/volt/tasks/live_query/live_query.rb +11 -3
  7. data/app/volt/tasks/store_tasks.rb +14 -17
  8. data/lib/volt/cli.rb +22 -0
  9. data/lib/volt/cli/asset_compile.rb +63 -63
  10. data/lib/volt/cli/base_index_renderer.rb +26 -0
  11. data/lib/volt/cli/generate.rb +1 -1
  12. data/lib/volt/config.rb +1 -0
  13. data/lib/volt/controllers/model_controller.rb +37 -1
  14. data/lib/volt/extra_core/array.rb +22 -0
  15. data/lib/volt/models/array_model.rb +7 -1
  16. data/lib/volt/models/errors.rb +1 -1
  17. data/lib/volt/models/field_helpers.rb +36 -21
  18. data/lib/volt/models/model.rb +16 -0
  19. data/lib/volt/models/validations/validations.rb +21 -6
  20. data/lib/volt/models/validators/type_validator.rb +35 -3
  21. data/lib/volt/page/bindings/content_binding.rb +1 -1
  22. data/lib/volt/page/bindings/event_binding.rb +40 -16
  23. data/lib/volt/page/document_events.rb +8 -6
  24. data/lib/volt/reactive/reactive_array.rb +18 -1
  25. data/lib/volt/server/forking_server.rb +7 -1
  26. data/lib/volt/server/html_parser/attribute_scope.rb +26 -0
  27. data/lib/volt/server/html_parser/component_view_scope.rb +30 -22
  28. data/lib/volt/server/middleware/default_middleware_stack.rb +6 -1
  29. data/lib/volt/server/rack/asset_files.rb +5 -3
  30. data/lib/volt/server/rack/opal_files.rb +35 -23
  31. data/lib/volt/server/rack/sprockets_helpers_setup.rb +71 -0
  32. data/lib/volt/server/template_handlers/view_processor.rb +1 -2
  33. data/lib/volt/utils/promise_extensions.rb +1 -1
  34. data/lib/volt/version.rb +1 -1
  35. data/lib/volt/volt/app.rb +0 -2
  36. data/lib/volt/volt/client_setup/browser.rb +11 -0
  37. data/spec/apps/kitchen_sink/Gemfile +37 -14
  38. data/spec/apps/kitchen_sink/app/main/config/routes.rb +3 -0
  39. data/spec/apps/kitchen_sink/app/main/controllers/events_controller.rb +26 -0
  40. data/spec/apps/kitchen_sink/app/main/views/events/index.html +30 -0
  41. data/spec/apps/kitchen_sink/app/main/views/main/bindings.html +3 -0
  42. data/spec/apps/kitchen_sink/app/main/views/main/yield.html +1 -6
  43. data/spec/apps/kitchen_sink/app/main/views/{yield-component → yield_component}/index.html +0 -0
  44. data/spec/extra_core/array_spec.rb +26 -0
  45. data/spec/integration/bindings_spec.rb +9 -0
  46. data/spec/integration/event_spec.rb +19 -0
  47. data/spec/models/array_model_spec.rb +13 -0
  48. data/spec/models/field_helpers_spec.rb +2 -2
  49. data/spec/models/validations_spec.rb +31 -0
  50. data/spec/models/validators/type_validator_spec.rb +47 -1
  51. data/spec/reactive/reactive_array_spec.rb +46 -0
  52. data/spec/server/forking_server_spec.rb +27 -0
  53. data/spec/server/html_parser/view_scope_spec.rb +44 -0
  54. data/spec/server/rack/asset_files_spec.rb +2 -2
  55. data/templates/project/Gemfile.tt +8 -0
  56. data/templates/project/config/app.rb.tt +2 -1
  57. data/volt.gemspec +1 -1
  58. 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>
@@ -154,4 +154,7 @@
154
154
  <div id="blurCount">{{ blur_count || 0 }}</div>
155
155
  <div id="focusCount">{{ focus_count || 0 }}</div>
156
156
 
157
+ <h2>Raw</h2>
157
158
 
159
+ <p>{{ raw 'some <br /> code' }}</p>
160
+ <p>{{ raw Promise.new.resolve('some <br /> other code') }}</p>
@@ -6,13 +6,8 @@
6
6
 
7
7
  <:yield-component>This is my {{ content_string }}</:yield-component>
8
8
 
9
- <:Yield-test>
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 }}
@@ -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, Array
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 of type Numeric']})
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'],