volt 0.9.5.pre4 → 0.9.5.pre5

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 (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'],