volt 0.8.27.beta6 → 0.8.27.beta7

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 +16 -3
  3. data/VERSION +1 -1
  4. data/app/volt/models/user.rb +1 -1
  5. data/app/volt/tasks/query_tasks.rb +2 -2
  6. data/app/volt/tasks/store_tasks.rb +14 -4
  7. data/app/volt/tasks/user_tasks.rb +1 -1
  8. data/lib/volt/controllers/http_controller.rb +60 -0
  9. data/lib/volt/controllers/model_controller.rb +5 -1
  10. data/lib/volt/extra_core/string.rb +6 -0
  11. data/lib/volt/models/array_model.rb +6 -2
  12. data/lib/volt/models/associations.rb +1 -1
  13. data/lib/volt/models/buffer.rb +14 -3
  14. data/lib/volt/models/model.rb +28 -60
  15. data/lib/volt/models/permissions.rb +4 -4
  16. data/lib/volt/reactive/computation.rb +15 -15
  17. data/lib/volt/reactive/reactive_array.rb +1 -0
  18. data/lib/volt/router/routes.rb +67 -27
  19. data/lib/volt/server.rb +37 -6
  20. data/lib/volt/server/component_templates.rb +2 -2
  21. data/lib/volt/server/rack/http_request.rb +50 -0
  22. data/lib/volt/server/rack/http_resource.rb +41 -0
  23. data/lib/volt/server/rack/http_response_header.rb +33 -0
  24. data/lib/volt/server/rack/http_response_renderer.rb +41 -0
  25. data/lib/volt/spec/setup.rb +4 -2
  26. data/lib/volt/tasks/dispatcher.rb +7 -7
  27. data/lib/volt/tasks/task_handler.rb +1 -1
  28. data/lib/volt/volt/users.rb +12 -6
  29. data/spec/apps/kitchen_sink/app/main/config/routes.rb +18 -10
  30. data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +2 -2
  31. data/spec/apps/kitchen_sink/app/main/controllers/server/simple_http_controller.rb +15 -0
  32. data/spec/apps/kitchen_sink/app/main/controllers/upload_controller.rb +22 -0
  33. data/spec/apps/kitchen_sink/app/main/views/main/yield.html +2 -2
  34. data/spec/apps/kitchen_sink/app/main/views/upload/index.html +15 -0
  35. data/spec/controllers/http_controller_spec.rb +130 -0
  36. data/spec/extra_core/string_transformation_test_cases.rb +8 -0
  37. data/spec/extra_core/string_transformations_spec.rb +12 -0
  38. data/spec/integration/http_endpoints_spec.rb +29 -0
  39. data/spec/integration/user_spec.rb +42 -42
  40. data/spec/models/associations_spec.rb +4 -4
  41. data/spec/models/buffer_spec.rb +15 -0
  42. data/spec/models/model_spec.rb +70 -25
  43. data/spec/models/model_state_spec.rb +1 -1
  44. data/spec/models/permissions_spec.rb +64 -2
  45. data/spec/models/persistors/params_spec.rb +8 -8
  46. data/spec/models/persistors/store_spec.rb +1 -1
  47. data/spec/models/user_validation_spec.rb +1 -1
  48. data/spec/router/routes_spec.rb +111 -43
  49. data/spec/server/rack/http_request_spec.rb +50 -0
  50. data/spec/server/rack/http_resource_spec.rb +59 -0
  51. data/spec/server/rack/http_response_header_spec.rb +34 -0
  52. data/spec/server/rack/http_response_renderer_spec.rb +33 -0
  53. data/spec/tasks/dispatcher_spec.rb +2 -2
  54. data/templates/component/config/routes.rb +2 -2
  55. data/templates/project/Gemfile.tt +3 -5
  56. data/templates/project/app/main/config/routes.rb +4 -4
  57. data/volt.gemspec +2 -2
  58. metadata +33 -8
@@ -4,14 +4,14 @@ require 'volt/models'
4
4
  describe Volt::Persistors::Params do
5
5
  it 'should stay as params classes when used' do
6
6
  a = Volt::Model.new({}, persistor: Volt::Persistors::Params)
7
- expect(a._test.class).to eq(Volt::Model)
7
+ # expect(a._test!.class).to eq(Volt::Model)
8
+ #
9
+ # expect(a._test!._cool!.persistor.class).to eq(Volt::Persistors::Params)
8
10
 
9
- expect(a._test._cool.persistor.class).to eq(Volt::Persistors::Params)
10
-
11
- a._items << { name: 'Test' }
12
-
13
- expect(a._items.persistor.class).to eq(Volt::Persistors::Params)
14
- expect(a._items[0].persistor.class).to eq(Volt::Persistors::Params)
15
- expect(a._items[0]._name.class).to eq(String)
11
+ a._items! << { name: 'Test' }
12
+ #
13
+ # expect(a._items.persistor.class).to eq(Volt::Persistors::Params)
14
+ # expect(a._items[0].persistor.class).to eq(Volt::Persistors::Params)
15
+ # expect(a._items[0]._name!.class).to eq(String)
16
16
  end
17
17
  end
@@ -8,7 +8,7 @@ describe Volt::Persistors::Store do
8
8
  expect(persistor_instance).to receive(:loaded)
9
9
  expect(persistor).to receive(:new).and_return(persistor_instance)
10
10
 
11
- @model = Volt::Model.new(nil, persistor: persistor)
11
+ @model = Volt::Model.new({}, persistor: persistor)
12
12
 
13
13
  expect(persistor_instance).to receive(:changed)
14
14
 
@@ -4,7 +4,7 @@ require 'spec_helper'
4
4
  describe Volt::UserValidatorHelpers do
5
5
  context "with user" do
6
6
  before do
7
- allow(Volt).to receive(:user_id) { 294 }
7
+ allow(Volt).to receive(:current_user_id) { 294 }
8
8
  end
9
9
 
10
10
  it 'should assign user_id when owning by a user' do
@@ -9,41 +9,91 @@ end
9
9
  describe Volt::Routes do
10
10
  it 'should setup direct routes' do
11
11
  routes do
12
- get '/', view: 'index'
13
- get '/page1', view: 'first_page'
12
+ client '/', view: 'index'
13
+ client '/page1', view: 'first_page'
14
+ get '/page2', controller: 'page', action: 'show'
14
15
  end
15
16
 
16
17
  direct_routes = @routes.instance_variable_get(:@direct_routes)
17
- expect(direct_routes).to eq('/' => { view: 'index' }, '/page1' => { view: 'first_page' })
18
+ expect(direct_routes[:client]).to eq('/' => { view: 'index' }, '/page1' => { view: 'first_page' })
19
+ expect(direct_routes[:get]).to eq('/page2' => { controller: 'page', action: 'show' })
18
20
  end
19
21
 
20
22
  it 'should setup indirect routes' do
21
23
  routes do
22
- get '/blog/{{ id }}/edit', view: 'blog/edit'
23
- get '/blog/{{ id }}', view: 'blog/show'
24
+ client '/blog/{{ id }}/edit', view: 'blog/edit'
25
+ client '/blog/{{ id }}', view: 'blog/show'
26
+ get '/comments/{{ id }}/edit', controller: 'comments', action: 'edit'
27
+ get '/comments/{{ id }}', controller: 'comments', action: 'show'
24
28
  end
25
29
 
26
30
  indirect_routes = @routes.instance_variable_get(:@indirect_routes)
27
- expect(indirect_routes).to eq(
31
+ expect(indirect_routes[:client]).to eq(
28
32
  'blog' => {
29
33
  '*' => {
30
34
  'edit' => {
31
35
  nil => { view: 'blog/edit', id: 1 }
32
- },
33
- nil => { view: 'blog/show', id: 1 }
36
+ },
37
+ nil => { view: 'blog/show', id: 1 }
38
+ }
34
39
  }
35
- }
36
- )
40
+ )
41
+
42
+ expect(indirect_routes[:get]).to eq(
43
+ 'comments' => {
44
+ '*' => {
45
+ 'edit' => {
46
+ nil => { controller: 'comments', action: 'edit', id: 1 }
47
+ },
48
+ nil => { controller: 'comments', action: 'show', id: 1 }
49
+ }
50
+ }
51
+ )
37
52
  end
38
53
 
54
+ it 'should setup param matchers' do
55
+ routes do
56
+ client '/blog', view: 'blog'
57
+ client '/blog/{{ id }}', view: 'blog/show'
58
+ client '/blog/{{ id }}/edit', view: 'blog/edit'
59
+ client '/blog/tags/{{ tag }}', view: 'blog/tag'
60
+ client '/login/{{ name }}/user/{{ id }}', view: 'login', action: 'user'
61
+ get '/articles', controller: 'articles', action: 'index'
62
+ get '/articles/{{ id }}', controller: 'articles', action: 'show'
63
+ end
64
+
65
+ param_matches = @routes.instance_variable_get(:@param_matches)
66
+ expect(param_matches[:client].map { |v| v[0] }).to eq([
67
+ { view: 'blog' },
68
+ { view: 'blog/show', id: nil },
69
+ { view: 'blog/edit', id: nil },
70
+ { view: 'blog/tag', tag: nil },
71
+ { view: 'login', action: 'user', name: nil, id: nil }
72
+ ])
73
+
74
+ expect(param_matches[:get].map { |v| v[0] }).to eq([
75
+ { controller: 'articles', action: 'index' },
76
+ { controller: 'articles', action: 'show', id: nil },
77
+ ])
78
+ end
79
+
80
+
39
81
  it 'should match routes' do
40
82
  routes do
41
- get '/blog', view: 'blog'
42
- get '/blog/{{ id }}', view: 'blog/show'
43
- get '/blog/{{ id }}/draft', view: 'blog/draft', action: 'draft'
44
- get '/blog/{{ id }}/edit', view: 'blog/edit'
45
- get '/blog/tags/{{ _tag }}', view: 'blog/tag'
46
- get '/login/{{ name }}/user/{{ id }}', view: 'login', action: 'user'
83
+ client '/blog', view: 'blog'
84
+ client '/blog/{{ id }}', view: 'blog/show'
85
+ client '/blog/{{ id }}/draft', view: 'blog/draft', action: 'draft'
86
+ client '/blog/{{ id }}/edit', view: 'blog/edit'
87
+ client '/blog/tags/{{ tag }}', view: 'blog/tag'
88
+ client '/login/{{ name }}/user/{{ id }}', view: 'login', action: 'user'
89
+ get '/articles', controller: 'articles', action: 'index'
90
+ get '/articles/{{ id }}', controller: 'articles', action: 'show'
91
+ put '/articles/{{ articles_id }}/comments/{{ id }}', controller: 'comments', action: 'update'
92
+ post '/comments', controller: 'comments', action: 'create'
93
+ put '/people', controller: 'people', action: 'update'
94
+ patch '/people/1', controller: 'people', action: 'update'
95
+ delete '/people/2', controller: 'people', action: 'destroy'
96
+
47
97
  end
48
98
 
49
99
  params = @routes.url_to_params('/blog')
@@ -56,7 +106,7 @@ describe Volt::Routes do
56
106
  expect(params).to eq(view: 'blog/show', id: '55')
57
107
 
58
108
  params = @routes.url_to_params('/blog/tags/good')
59
- expect(params).to eq(view: 'blog/tag', _tag: 'good')
109
+ expect(params).to eq(view: 'blog/tag', tag: 'good')
60
110
 
61
111
  params = @routes.url_to_params('/blog/55/draft')
62
112
  expect(params).to eq(view: 'blog/draft', id: '55', action: 'draft')
@@ -66,34 +116,44 @@ describe Volt::Routes do
66
116
 
67
117
  params = @routes.url_to_params('/login/cool')
68
118
  expect(params).to eq(false)
69
- end
70
119
 
71
- it 'should setup param matchers' do
72
- routes do
73
- get '/blog', view: 'blog'
74
- get '/blog/{{ id }}', view: 'blog/show'
75
- get '/blog/{{ id }}/edit', view: 'blog/edit'
76
- get '/blog/tags/{{ _tag }}', view: 'blog/tag'
77
- get '/login/{{ name }}/user/{{ id }}', view: 'login', action: 'user'
78
- end
120
+ params = @routes.url_to_params(:get, '/articles')
121
+ expect(params).to eq(controller: 'articles', action: 'index')
79
122
 
80
- param_matches = @routes.instance_variable_get(:@param_matches)
81
- expect(param_matches.map { |v| v[0] }).to eq([
82
- { view: 'blog' },
83
- { view: 'blog/show', id: nil },
84
- { view: 'blog/edit', id: nil },
85
- { view: 'blog/tag', _tag: nil },
86
- { view: 'login', action: 'user', name: nil, id: nil }
87
- ])
123
+ params = @routes.url_to_params('get', '/articles')
124
+ expect(params).to eq(controller: 'articles', action: 'index')
125
+
126
+ params = @routes.url_to_params(:post, '/articles')
127
+ expect(params).to be_nil
128
+
129
+ params = @routes.url_to_params(:post, '/comments')
130
+ expect(params).to eq(controller: 'comments', action: 'create')
131
+
132
+ params = @routes.url_to_params(:put, '/people')
133
+ expect(params).to eq(controller: 'people', action: 'update')
134
+
135
+ params = @routes.url_to_params(:patch, '/people/1')
136
+ expect(params).to eq(controller: 'people', action: 'update')
137
+
138
+ params = @routes.url_to_params(:delete, '/people/2')
139
+ expect(params).to eq(controller: 'people', action: 'destroy')
140
+
141
+ params = @routes.url_to_params(:get, '/articles/2')
142
+ expect(params).to eq(controller: 'articles', action: 'show', id: '2')
143
+
144
+ params = @routes.url_to_params(:put, '/articles/2/comments/9')
145
+ expect(params).to eq(controller: 'comments', action: 'update', articles_id: '2', id: '9')
88
146
  end
89
147
 
90
148
  it 'should go from params to url' do
91
149
  routes do
92
- get '/blog', view: 'blog'
93
- get '/blog/{{ id }}', view: 'blog/show'
94
- get '/blog/{{ id }}/edit', view: 'blog/edit'
95
- get '/blog/tags/{{ _tag }}', view: 'blog/tag'
96
- get '/login/{{ name }}/user/{{ id }}', view: 'login', action: 'user'
150
+ client '/blog', view: 'blog'
151
+ client '/blog/{{ id }}', view: 'blog/show'
152
+ client '/blog/{{ id }}/edit', view: 'blog/edit'
153
+ client '/blog/tags/{{ tag }}', view: 'blog/tag'
154
+ client '/login/{{ name }}/user/{{ id }}', view: 'login', action: 'user'
155
+ get '/articles/{{ id }}', controller: 'articles', action: 'show'
156
+ put '/articles/{{ id }}', controller: 'articles', action: 'update'
97
157
  end
98
158
 
99
159
  url, params = @routes.params_to_url(view: 'blog/show', id: '55')
@@ -107,6 +167,14 @@ describe Volt::Routes do
107
167
  url, params = @routes.params_to_url(view: 'blog/edit', id: '100', other: 'should_pass')
108
168
  expect(url).to eq('/blog/100/edit')
109
169
  expect(params).to eq(other: 'should_pass')
170
+
171
+ url, params = @routes.params_to_url(controller: 'articles', action: 'show', method: :get, id: 10)
172
+ expect(url).to eq('/articles/10')
173
+ expect(params).to eq({})
174
+
175
+ url, params = @routes.params_to_url(controller: 'articles', action: 'update', method: :put, id: 99, other: 'xyz')
176
+ expect(url).to eq('/articles/99')
177
+ expect(params).to eq({other: 'xyz'})
110
178
  end
111
179
 
112
180
  it 'should test that params match a param matcher' do
@@ -142,8 +210,8 @@ describe Volt::Routes do
142
210
  params._index = '5'
143
211
 
144
212
  routes do
145
- get '/', controller: 'index'
146
- get '/blog', controller: 'blog'
213
+ client '/', controller: 'index'
214
+ client '/blog', controller: 'blog'
147
215
  end
148
216
 
149
217
  path, cleaned_params = @routes.params_to_url(params.to_h)
@@ -155,8 +223,8 @@ describe Volt::Routes do
155
223
  params = Volt::Model.new({}, persistor: Volt::Persistors::Params)
156
224
 
157
225
  routes do
158
- get '/', controller: 'index'
159
- get '/blog/{{ id }}', controller: 'blog'
226
+ client '/', controller: 'index'
227
+ client '/blog/{{ id }}', controller: 'blog'
160
228
  end
161
229
 
162
230
  params = @routes.url_to_params('/blog/20')
@@ -0,0 +1,50 @@
1
+ if RUBY_PLATFORM != 'opal'
2
+ require 'volt/server/rack/http_request'
3
+
4
+ describe Volt::HttpRequest do
5
+ def env_for(url, opts = {})
6
+ Rack::MockRequest.env_for(url, opts)
7
+ end
8
+
9
+ it 'should report the correct format' do
10
+ env = env_for('http://example.com/test.html',
11
+ 'CONTENT_TYPE' => 'text/plain;charset=utf-8')
12
+ request = Volt::HttpRequest.new(env)
13
+ expect(request.format).to eq('html')
14
+
15
+ env = env_for('http://example.com/test',
16
+ 'CONTENT_TYPE' => 'text/plain;charset=utf-8')
17
+ request = Volt::HttpRequest.new(env)
18
+ expect(request.format).to eq('text/plain')
19
+ end
20
+
21
+ it 'should remove the format from the path' do
22
+ env = env_for('http://example.com/test.html',
23
+ 'CONTENT_TYPE' => 'text/plain;charset=utf-8')
24
+ request = Volt::HttpRequest.new(env)
25
+ expect(request.path).to eq('/test')
26
+ end
27
+
28
+ it 'should return the correct http method' do
29
+ env = env_for('http://example.com/test.html', method: 'GET')
30
+ request = Volt::HttpRequest.new(env)
31
+ expect(request.method).to eq(:get)
32
+
33
+ env = env_for('http://example.com/test.html',
34
+ method: 'POST', params: { _method: 'put' })
35
+ request = Volt::HttpRequest.new(env)
36
+ expect(request.method).to eq(:put)
37
+ end
38
+
39
+ it 'should return the params with symbolized keys' do
40
+ env = env_for(
41
+ 'http://example.com/test.html',
42
+ method: 'POST',
43
+ params: { 'some' => 'params', 'as' => 'strings', and: 'symbols' })
44
+ request = Volt::HttpRequest.new(env)
45
+
46
+ wanted = { some: 'params', as: 'strings', and: 'symbols' }
47
+ expect(request.params).to eq(wanted)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,59 @@
1
+ if RUBY_PLATFORM != 'opal'
2
+ require 'volt/server/rack/http_resource'
3
+ require 'volt/controllers/http_controller'
4
+ require 'volt/server/rack/http_request'
5
+ require 'volt/router/routes'
6
+
7
+ describe Volt::HttpResource do
8
+ def routes(&block)
9
+ @routes = Volt::Routes.new
10
+ @routes.define(&block)
11
+ end
12
+
13
+ class SimpleController < Volt::HttpController
14
+ attr_reader :action_called
15
+
16
+ def index
17
+ @action_called = true
18
+ render text: 'just some text'
19
+ end
20
+
21
+ def show
22
+ render text: "show with id #{params[:stuff_id]} " \
23
+ "and #{params[:test]} called"
24
+ end
25
+ end
26
+
27
+ let(:app) { ->(env) { [404, env, 'app'] } }
28
+
29
+ before(:each) do
30
+ routes do
31
+ get '/stuff', controller: 'simple', action: 'index'
32
+ get '/stuff/{{ stuff_id }}', controller: 'simple', action: 'show'
33
+ end
34
+ end
35
+
36
+ it 'should initialize the correct controller and call the correct action' do
37
+ http_resource = Volt::HttpResource.new(app, @routes)
38
+ env = Rack::MockRequest.env_for('http://example.com/stuff')
39
+ request = Volt::HttpRequest.new(env)
40
+ controller = SimpleController.new({}, request)
41
+ expect(SimpleController).to receive(:new).and_return(controller)
42
+
43
+ response = http_resource.call(env)
44
+ expect(response.status).to eq(200)
45
+ expect(response.body).to eq(['just some text'])
46
+ expect(controller.action_called).to eq(true)
47
+ end
48
+
49
+ it 'should parse the correct params to the controller' do
50
+ http_resource = Volt::HttpResource.new(app, @routes)
51
+ env = Rack::MockRequest.env_for('http://example.com/stuff/99?test=another_param')
52
+ request = Volt::HttpRequest.new(env)
53
+
54
+ response = http_resource.call(env)
55
+ expect(response.status).to eq(200)
56
+ expect(response.body).to eq(['show with id 99 and another_param called'])
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,34 @@
1
+ require 'volt/server/rack/http_response_header'
2
+
3
+ describe Volt::HttpResponseHeader do
4
+ it 'it should headerize the keys' do
5
+ header = Volt::HttpResponseHeader.new
6
+ header[:content_type] = 'test'
7
+ expect(header['Content-Type']).to eq('test')
8
+ expect(header['content-type']).to eq('test')
9
+ expect(header['content_type']).to eq('test')
10
+ expect(header[:content_type]).to eq('test')
11
+ expect(header.keys).to eq(['Content-Type'])
12
+ end
13
+
14
+ it 'should delete keys' do
15
+ header = Volt::HttpResponseHeader.new
16
+ header[:content_type] = 'test'
17
+ expect(header.delete(:content_type)).to eq('test')
18
+ expect(header.size).to eq 0
19
+ end
20
+
21
+ it 'should merge other plain hashes and headerize their keys' do
22
+ header = Volt::HttpResponseHeader.new
23
+ header[:content_type] = 'test'
24
+
25
+ hash = {}
26
+ hash[:transfer_encoding] = 'encoding'
27
+
28
+ expect(header.merge(hash)).to be_a(Volt::HttpResponseHeader)
29
+ expect(header.merge(hash)['Transfer-Encoding']).to eq('encoding')
30
+
31
+ header.merge!(hash)
32
+ expect(header['Transfer-Encoding']).to eq('encoding')
33
+ end
34
+ end
@@ -0,0 +1,33 @@
1
+ require 'volt/server/rack/http_response_renderer'
2
+
3
+ describe Volt::HttpResponseRenderer do
4
+ let(:renderer) { Volt::HttpResponseRenderer.new }
5
+
6
+ it 'should render json' do
7
+ hash = { a: 'aa', bb: 'bbb' }
8
+ body, additional_headers = renderer.render json: hash
9
+ expect(body).to eq(hash.to_json)
10
+ expect(additional_headers[:content_type]).to eq('application/json')
11
+ end
12
+
13
+ it 'should render plain text' do
14
+ text = 'just some text'
15
+ body, additional_headers = renderer.render(text: text)
16
+ expect(body).to eq(text)
17
+ expect(additional_headers[:content_type]).to eq('text/plain')
18
+ end
19
+
20
+ it 'should default to text/plain if no suitable renderer could be found' do
21
+ body, additional_headers = renderer.render(some: 'text')
22
+ expect(body).to eq('')
23
+ expect(additional_headers[:content_type]).to eq('text/plain')
24
+ end
25
+
26
+ it 'should add all remaining keys as additional_headers' do
27
+ text = 'just some text'
28
+ body, additional_headers = renderer.render(text: text,
29
+ additional: 'headers')
30
+ expect(body).to eq(text)
31
+ expect(additional_headers[:additional]).to eq('headers')
32
+ end
33
+ end
@@ -1,5 +1,5 @@
1
1
  if RUBY_PLATFORM != 'opal'
2
- class TestTask < Volt::TaskHandler
2
+ class TestTask < Volt::Task
3
3
  def allowed_method(arg1, arg2)
4
4
  return 'yes' + arg1 + arg2
5
5
  end
@@ -10,7 +10,7 @@ if RUBY_PLATFORM != 'opal'
10
10
  Volt.logger = spy('Volt::VoltLogger')
11
11
  end
12
12
 
13
- it 'should only allow method calls on TaskHandler or above in the inheritance chain' do
13
+ it 'should only allow method calls on Task or above in the inheritance chain' do
14
14
  channel = double('channel')
15
15
 
16
16
  expect(channel).to receive(:send_message).with('response', 0, 'yes it works', nil)