volt 0.9.2 → 0.9.3.pre1

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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/CONTRIBUTING.md +4 -0
  4. data/Gemfile +3 -0
  5. data/app/volt/assets/js/volt_js_polyfills.js +0 -1
  6. data/app/volt/assets/js/volt_watch.js +217 -0
  7. data/app/volt/models/user.rb +7 -2
  8. data/app/volt/tasks/query_tasks.rb +6 -0
  9. data/lib/volt/boot.rb +1 -1
  10. data/lib/volt/cli/generate.rb +1 -1
  11. data/lib/volt/config.rb +12 -2
  12. data/lib/volt/controllers/model_controller.rb +1 -1
  13. data/lib/volt/data_stores/base_adaptor_client.rb +34 -0
  14. data/lib/volt/data_stores/{base.rb → base_adaptor_server.rb} +1 -1
  15. data/lib/volt/data_stores/data_store.rb +23 -7
  16. data/lib/volt/models/array_model.rb +3 -2
  17. data/lib/volt/models/model.rb +29 -91
  18. data/lib/volt/models/{dirty.rb → model_helpers/dirty.rb} +0 -0
  19. data/lib/volt/models/{listener_tracker.rb → model_helpers/listener_tracker.rb} +0 -0
  20. data/lib/volt/models/model_helpers/model_change_helpers.rb +76 -0
  21. data/lib/volt/models/{model_helpers.rb → model_helpers/model_helpers.rb} +0 -0
  22. data/lib/volt/models/persistors/array_store.rb +2 -23
  23. data/lib/volt/models/persistors/query/normalizer.rb +0 -44
  24. data/{templates/project/lib/.empty_directory → lib/volt/models/validations/errors.rb} +0 -0
  25. data/lib/volt/models/{validations.rb → validations/validations.rb} +80 -26
  26. data/lib/volt/page/bindings/attribute_binding.rb +17 -3
  27. data/lib/volt/page/page.rb +1 -0
  28. data/lib/volt/reactive/eventable.rb +1 -0
  29. data/lib/volt/server.rb +2 -1
  30. data/lib/volt/server/component_templates.rb +66 -16
  31. data/lib/volt/server/forking_server.rb +16 -14
  32. data/lib/volt/server/html_parser/sandlebars_parser.rb +2 -0
  33. data/lib/volt/server/html_parser/view_scope.rb +2 -0
  34. data/lib/volt/server/rack/component_paths.rb +4 -2
  35. data/lib/volt/server/rack/opal_files.rb +4 -2
  36. data/lib/volt/server/socket_connection_handler.rb +5 -1
  37. data/lib/volt/server/template_handlers/handlers.rb +0 -0
  38. data/lib/volt/spec/setup.rb +23 -8
  39. data/lib/volt/tasks/dispatcher.rb +4 -0
  40. data/lib/volt/utils/promise_patch.rb +3 -0
  41. data/lib/volt/version.rb +1 -1
  42. data/spec/apps/kitchen_sink/Gemfile +5 -0
  43. data/spec/apps/kitchen_sink/app/main/config/routes.rb +1 -0
  44. data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +10 -0
  45. data/spec/apps/kitchen_sink/app/main/views/main/bindings.html +20 -0
  46. data/spec/apps/kitchen_sink/app/main/views/main/form.html +73 -0
  47. data/spec/apps/kitchen_sink/app/main/views/main/main.html +1 -0
  48. data/spec/integration/bindings_spec.rb +33 -0
  49. data/spec/integration/user_spec.rb +51 -21
  50. data/spec/models/associations_spec.rb +8 -0
  51. data/spec/models/model_spec.rb +7 -0
  52. data/spec/models/user_spec.rb +20 -0
  53. data/spec/models/validations_spec.rb +2 -1
  54. data/spec/models/validators/block_validations_spec.rb +53 -0
  55. data/spec/page/bindings/template_binding/view_lookup_for_path_spec.rb +10 -0
  56. data/spec/page/path_string_renderer_spec.rb +6 -0
  57. data/spec/reactive/eventable_spec.rb +24 -6
  58. data/spec/server/component_templates_spec.rb +21 -0
  59. data/spec/server/html_parser/sandlebars_parser_spec.rb +12 -13
  60. data/spec/server/html_parser/view_parser_spec.rb +3 -0
  61. data/spec/server/rack/asset_files_spec.rb +2 -2
  62. data/spec/server/rack/http_resource_spec.rb +10 -0
  63. data/spec/tasks/dispatcher_spec.rb +5 -0
  64. data/spec/tasks/user_tasks_spec.rb +59 -0
  65. data/spec/utils/task_argument_filtererer_spec.rb +6 -0
  66. data/templates/newgem/app/newgem/config/initializers/boot.rb +10 -0
  67. data/templates/newgem/lib/newgem.rb.tt +13 -0
  68. data/templates/project/Gemfile.tt +3 -0
  69. data/templates/project/app/main/lib/.empty_directory +0 -0
  70. data/volt.gemspec +3 -1
  71. metadata +24 -25
  72. data/lib/volt/data_stores/mongo_driver.rb +0 -69
@@ -113,6 +113,26 @@
113
113
  </tr>
114
114
  </table>
115
115
 
116
+ <h2>If Bindings</h2>
117
+ <p id='ifbinding'>
118
+ {{ if page._show && true }}
119
+ <span>If _show</span>
120
+ {{ else }}
121
+ <span>If false _show</span>
122
+ {{ end }}
123
+ </p>
124
+
125
+ <h2>Unless Bindings</h2>
126
+ <p id='unlessbinding'>
127
+ {{ unless page._show && true }}
128
+ <span>Unless _show</span>
129
+ {{ else }}
130
+ <span>Unless false _show</span>
131
+ {{ end }}
132
+ </p>
133
+
134
+ <a id='showtrue' e-click='set_show(true)'>set _show true</a>
135
+ <a id='showfalse' e-click='set_show(false)'>set _show false</a>
116
136
 
117
137
  <h2>Content</h2>
118
138
 
@@ -0,0 +1,73 @@
1
+ <:Title>
2
+ Form
3
+
4
+ <:Body>
5
+ <h1>Form Example</h1>
6
+ <h2 id='title'></h2>
7
+
8
+ <form role="form">
9
+ <div class="form-group">
10
+ <label>Name</label>
11
+ <span id='name-display'>{{ _name }}</span>
12
+ <label>Location</label>
13
+ <span id='location-display'>{{ _location }}</span>
14
+ </div>
15
+ <input class="form-control" id="name" type="hidden" value="{{ _name }}" />
16
+ <div class="form-group">
17
+ <label>Location</label>
18
+ <select id='location' value='{{ _location }}'>
19
+ <option value="">State</option>
20
+ <option value="AL">Alabama</option>
21
+ <option value="AK">Alaska</option>
22
+ <option value="AZ">Arizona</option>
23
+ <option value="AR">Arkansas</option>
24
+ <option value="CA">California</option>
25
+ <option value="CO">Colorado</option>
26
+ <option value="CT">Connecticut</option>
27
+ <option value="DE">Delaware</option>
28
+ <option value="DC">District Of Columbia</option>
29
+ <option value="FL">Florida</option>
30
+ <option value="GA">Georgia</option>
31
+ <option value="HI">Hawaii</option>
32
+ <option value="ID">Idaho</option>
33
+ <option value="IL">Illinois</option>
34
+ <option value="IN">Indiana</option>
35
+ <option value="IA">Iowa</option>
36
+ <option value="KS">Kansas</option>
37
+ <option value="KY">Kentucky</option>
38
+ <option value="LA">Louisiana</option>
39
+ <option value="ME">Maine</option>
40
+ <option value="MD">Maryland</option>
41
+ <option value="MA">Massachusetts</option>
42
+ <option value="MI">Michigan</option>
43
+ <option value="MN">Minnesota</option>
44
+ <option value="MS">Mississippi</option>
45
+ <option value="MO">Missouri</option>
46
+ <option value="MT">Montana</option>
47
+ <option value="NE">Nebraska</option>
48
+ <option value="NV">Nevada</option>
49
+ <option value="NH">New Hampshire</option>
50
+ <option value="NJ">New Jersey</option>
51
+ <option value="NM">New Mexico</option>
52
+ <option value="NY">New York</option>
53
+ <option value="NC">North Carolina</option>
54
+ <option value="ND">North Dakota</option>
55
+ <option value="OH">Ohio</option>
56
+ <option value="OK">Oklahoma</option>
57
+ <option value="OR">Oregon</option>
58
+ <option value="PA">Pennsylvania</option>
59
+ <option value="RI">Rhode Island</option>
60
+ <option value="SC">South Carolina</option>
61
+ <option value="SD">South Dakota</option>
62
+ <option value="TN">Tennessee</option>
63
+ <option value="TX">Texas</option>
64
+ <option value="UT">Utah</option>
65
+ <option value="VT">Vermont</option>
66
+ <option value="VA">Virginia</option>
67
+ <option value="WA">Washington</option>
68
+ <option value="WV">West Virginia</option>
69
+ <option value="WI">Wisconsin</option>
70
+ <option value="WY">Wyoming</option>
71
+ </select>
72
+ </div>
73
+ </form>
@@ -6,6 +6,7 @@
6
6
  <div class="header">
7
7
  <ul class="nav nav-pills pull-right">
8
8
  <:nav href="/" text="Home" />
9
+ <:nav href="/form" text="Form" />
9
10
  <:nav href="/bindings" text="Bindings" />
10
11
  <:nav href="/todos" text="Todos" />
11
12
  <:nav href="/flash" text="Flash" />
@@ -186,6 +186,24 @@ describe 'bindings test', type: :feature, sauce: true do
186
186
  end
187
187
  end
188
188
 
189
+ describe 'if/unless binding' do
190
+ it 'should show corret text' do
191
+ visit '/'
192
+
193
+ click_link 'Bindings'
194
+
195
+ click_on 'showtrue'
196
+
197
+ expect(find('#ifbinding')).to have_content('If _show')
198
+ expect(find('#unlessbinding')).to have_content('Unless false _show')
199
+
200
+ click_on 'showfalse'
201
+
202
+ expect(find('#ifbinding')).to have_content('If false _show')
203
+ expect(find('#unlessbinding')).to have_content('Unless _show')
204
+ end
205
+ end
206
+
189
207
  describe 'content escaping' do
190
208
  it 'should escape in a tripple stash' do
191
209
  visit '/'
@@ -195,4 +213,19 @@ describe 'bindings test', type: :feature, sauce: true do
195
213
  expect(find('#escapeContent')).to have_content('this is {{escaped}}')
196
214
  end
197
215
  end
216
+
217
+ if ENV['BROWSER'] != 'phantom'
218
+ describe 'input hidden and select' do
219
+ it 'should display binding value' do
220
+ visit '/'
221
+
222
+ click_link 'Form'
223
+
224
+ expect(find('body')).to have_content('Form Example')
225
+ expect(find('#title')).to have_content('form_ready')
226
+ expect(find('#name-display')).to have_content('Test')
227
+ expect(find('#location-display')).to have_content('AL')
228
+ end
229
+ end
230
+ end
198
231
  end
@@ -19,27 +19,6 @@ describe 'user accounts', type: :feature, sauce: true do
19
19
  expect(page).to have_content('Test Account 9550')
20
20
  end
21
21
 
22
- it 'should login and logout' do
23
- visit '/'
24
-
25
- # Add the user
26
- store._users! << { email: 'test@test.com', password: 'awes0mesEcRet', name: 'Test Account 9550' }
27
-
28
- click_link 'Login'
29
-
30
- fields = all(:css, 'form .form-control')
31
- fields[0].set('test@test.com')
32
- fields[1].set('awes0mesEcRet')
33
- click_button 'Login'
34
-
35
- expect(page).to have_content('Test Account 9550')
36
-
37
- # Click the logout link
38
- click_link 'Test Account 9550'
39
- click_link 'Logout'
40
-
41
- expect(page).to_not have_content('Test Account 9550')
42
- end
43
22
 
44
23
  it 'should fail to create an account without a valid email and password' do
45
24
  visit '/'
@@ -60,4 +39,55 @@ describe 'user accounts', type: :feature, sauce: true do
60
39
 
61
40
  expect(page).to have_content('must be at least 8 characters')
62
41
  end
42
+
43
+ describe "with a user" do
44
+ before do
45
+ # Add the user
46
+ store._users! << { email: 'test@test.com', password: 'awes0mesEcRet', name: 'Test Account 9550' }
47
+ end
48
+
49
+ it 'should login and logout' do
50
+ visit '/'
51
+
52
+ click_link 'Login'
53
+
54
+ fields = all(:css, 'form .form-control')
55
+ fields[0].set('test@test.com')
56
+ fields[1].set('awes0mesEcRet')
57
+ click_button 'Login'
58
+
59
+ expect(page).to have_content('Test Account 9550')
60
+
61
+ # Click the logout link
62
+ click_link 'Test Account 9550'
63
+ click_link 'Logout'
64
+
65
+ expect(page).to_not have_content('Test Account 9550')
66
+ end
67
+
68
+ it 'should show an error for invalid email' do
69
+ visit '/'
70
+ click_link 'Login'
71
+
72
+ fields = all(:css, 'form .form-control')
73
+ fields[0].set('notausersemail@gmail.com')
74
+ fields[1].set('awes0mesEcRet')
75
+ click_button 'Login'
76
+
77
+ expect(page).to have_content('User could not be found')
78
+ end
79
+
80
+ it 'should show an error for an invalid password' do
81
+ visit '/'
82
+ click_link 'Login'
83
+
84
+ fields = all(:css, 'form .form-control')
85
+ fields[0].set('test@test.com')
86
+ fields[1].set('wrongpassword')
87
+ click_button 'Login'
88
+
89
+ expect(page).to have_content('Password did not match')
90
+ end
91
+ end
92
+
63
93
  end
@@ -30,5 +30,13 @@ describe Volt::Associations do
30
30
  expect(addresses.size).to eq(2)
31
31
  expect(addresses[0]._city).to eq('Bozeman')
32
32
  end
33
+
34
+ it 'warns users if persistor is not a ModelStore' do
35
+ store = Volt::Model.new({}, persistor: Volt::Persistors::Flash)
36
+ expect do
37
+ store.send(:association_with_root_model, :blah)
38
+ end.to raise_error("blah currently only works on the store collection "\
39
+ "(support for other collections coming soon)")
40
+ end
33
41
  end
34
42
  end
@@ -13,6 +13,13 @@ class TestAssignsMethod < Volt::Model
13
13
  end
14
14
 
15
15
  describe Volt::Model do
16
+ it 'delegates unary operator to its attributes' do
17
+ model = Volt::Model.new
18
+ expect(!model).to eq(!model.attributes)
19
+ model = Volt::Model.new(has: 'attrs')
20
+ expect(!model).to eq(!model.attributes)
21
+ end
22
+
16
23
  it 'should allow _ methods to be used to store values without predefining them' do
17
24
  a = Volt::Model.new
18
25
  a._stash = 'yes'
@@ -60,8 +60,28 @@ describe Volt::User do
60
60
  expect(user._hashed_password).to eq 'hashed-password'
61
61
  end
62
62
  end
63
+
64
+ it 'should allow updates without validating the password' do
65
+ bob = store._users.buffer(name: 'Bob', email: 'bob@bob.com', password: '39sdjkdf932jklsd')
66
+ bob.save!
67
+
68
+ expect(bob._password).to eq(nil)
69
+
70
+ bob_buf = bob.buffer
71
+
72
+ bob_buf._name = 'Jimmy'
73
+
74
+ saved = false
75
+ bob_buf.save! do
76
+ saved = true
77
+ end
78
+
79
+ expect(saved).to eq(true)
80
+ end
63
81
  end
64
82
 
83
+
84
+
65
85
  describe 'when it is not a Volt server' do
66
86
  before do
67
87
  allow(Volt).to receive(:server?).and_return false
@@ -15,6 +15,7 @@ describe Volt::Model do
15
15
  end
16
16
  end
17
17
 
18
+
18
19
  let(:test_model_with_promises) do
19
20
  Class.new(Volt::Model) do
20
21
  attr_accessor :ran_promise
@@ -41,7 +42,6 @@ describe Volt::Model do
41
42
 
42
43
  it 'should show all fields in marked errors once saved' do
43
44
  buffer = model.buffer
44
-
45
45
  buffer.save!
46
46
 
47
47
  expect(buffer.marked_errors.keys).to eq(
@@ -164,4 +164,5 @@ describe Volt::Model do
164
164
  model._name = 'ok' # fails again
165
165
  expect(model._name).to eq('Jimmy')
166
166
  end
167
+
167
168
  end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ unless RUBY_PLATFORM == 'opal'
4
+ describe 'validations block' do
5
+ let(:model) { test_model_class.new }
6
+
7
+ let(:test_model_class) do
8
+ Class.new(Volt::Model) do
9
+ validations do
10
+ if _is_ready == true
11
+ validate :name, length: 5
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ let(:test_model_action_pass_class) do
18
+ Class.new(Volt::Model) do
19
+ validations do |action|
20
+ # Only validation the name on update
21
+ if action == :update
22
+ validate :name, length: 5
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ it 'should run conditional validations in the validations block' do
29
+ a = test_model_class.new(name: 'Jo')
30
+
31
+ a.validate!.sync
32
+ expect(a.errors.size).to eq(0)
33
+
34
+ a._is_ready = true
35
+ a.validate!.sync
36
+
37
+ expect(a.errors.size).to eq(1)
38
+ end
39
+
40
+ it 'should send the action name to the validations block' do
41
+ jo = test_model_action_pass_class.new(name: 'Jo')
42
+
43
+ jo.validate!.sync
44
+ expect(jo.errors.size).to eq(0)
45
+
46
+ store._people << jo
47
+
48
+ jo.validate!.sync
49
+ expect(jo.errors.size).to eq(1)
50
+
51
+ end
52
+ end
53
+ end
@@ -140,4 +140,14 @@ describe Volt::ViewBinding do
140
140
 
141
141
  expect(@view_lookup.path_for_template('nav', 'title').first).to eq('nav/main/index/title')
142
142
  end
143
+
144
+ it 'should return nils when given a non-existant path' do
145
+ @templates = {
146
+ 'nav/main/index/title' => '',
147
+ 'auth/login/new/title' => ''
148
+ }
149
+
150
+ expect(@view_lookup.path_for_template('not/a/real/item/')).to eq([nil, nil])
151
+ end
152
+
143
153
  end
@@ -18,5 +18,11 @@ unless RUBY_PLATFORM == 'opal'
18
18
  html = Volt::PathStringRenderer.new('main/mailers/welcome/html', { name: 'Jimmy' }, @page).html
19
19
  expect(html).to eq("\n <h1>Welcome Jimmy</h1>\n\n <p>Glad you signed up!</p>\n\n")
20
20
  end
21
+
22
+ it 'Raises raises ViewLookupException if full_path is nil' do
23
+ expect do
24
+ Volt::PathStringRenderer.new('', { name: 'Jimmy' }, Volt::Page.new).html
25
+ end.to raise_error(Volt::ViewLookupException)
26
+ end
21
27
  end
22
28
  end
@@ -3,6 +3,15 @@ require 'volt/reactive/eventable'
3
3
 
4
4
  class TestEventable
5
5
  include Volt::Eventable
6
+ attr_reader :events_removed
7
+
8
+ def initialize
9
+ @events_removed = []
10
+ end
11
+
12
+ def event_removed(event, last, last_for_event)
13
+ @events_removed.push(event => [last, last_for_event])
14
+ end
6
15
 
7
16
  def trigger_works_event!
8
17
  trigger!('works', 20)
@@ -10,9 +19,9 @@ class TestEventable
10
19
  end
11
20
 
12
21
  describe Volt::Eventable do
13
- it 'should allow events to be bound with on' do
14
- test_eventable = TestEventable.new
22
+ let(:test_eventable) { TestEventable.new }
15
23
 
24
+ it 'should allow events to be bound with on' do
16
25
  count = 0
17
26
  test_eventable.on('works') do |val|
18
27
  count += 1
@@ -25,8 +34,6 @@ describe Volt::Eventable do
25
34
  end
26
35
 
27
36
  it 'should allow events to be removed with .remove' do
28
- test_eventable = TestEventable.new
29
-
30
37
  count = 0
31
38
  listener = test_eventable.on('works') do
32
39
  count += 1
@@ -47,8 +54,6 @@ describe Volt::Eventable do
47
54
  end
48
55
 
49
56
  it 'should allow multiple events' do
50
- test_eventable = TestEventable.new
51
-
52
57
  called = false
53
58
  listener = test_eventable.on(:broken, :works) do |arg|
54
59
  expect(arg).to eq(20)
@@ -66,4 +71,17 @@ describe Volt::Eventable do
66
71
  test_eventable.trigger_works_event!
67
72
  expect(called).to eq(false)
68
73
  end
74
+
75
+ it 'Shows object ID and events when inspected' do
76
+ tested = TestEventable.new.on("test") { nil }
77
+ inspected = tested.inspect
78
+ expect(inspected).to include(tested.object_id.to_s)
79
+ expect(inspected).to include(tested.events.first.to_s)
80
+ end
81
+
82
+ it 'calls event_removed on the class included on removal of event' do
83
+ listener = test_eventable.on("test") { nil }
84
+ listener.remove
85
+ expect(test_eventable.events_removed).to include(test: [true, true])
86
+ end
69
87
  end