volt 0.9.2 → 0.9.3.pre1

Sign up to get free protection for your applications and to get access to all the features.
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