volt 0.8.27.beta6 → 0.8.27.beta7

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 +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)