zero 0.1.0

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 (65) hide show
  1. data/.gitignore +2 -0
  2. data/.rspec +3 -0
  3. data/.travis.yml +8 -0
  4. data/Gemfile +14 -0
  5. data/Gemfile.lock +59 -0
  6. data/Guardfile +15 -0
  7. data/README.md +60 -0
  8. data/Thorfile +6 -0
  9. data/lib/zero.rb +7 -0
  10. data/lib/zero/controller.rb +46 -0
  11. data/lib/zero/rack_request.rb +44 -0
  12. data/lib/zero/renderer.rb +139 -0
  13. data/lib/zero/request.rb +100 -0
  14. data/lib/zero/request/accept.rb +28 -0
  15. data/lib/zero/request/accept_type.rb +58 -0
  16. data/lib/zero/request/client.rb +31 -0
  17. data/lib/zero/request/parameter.rb +101 -0
  18. data/lib/zero/request/server.rb +41 -0
  19. data/lib/zero/response.rb +80 -0
  20. data/lib/zero/router.rb +63 -0
  21. data/lib/zero/version.rb +3 -0
  22. data/spec/fixtures/templates/index.html.erb +1 -0
  23. data/spec/fixtures/templates/index.json.erb +1 -0
  24. data/spec/spec_helper.rb +65 -0
  25. data/spec/unit/controller/call_spec.rb +22 -0
  26. data/spec/unit/controller/renderer_spec.rb +11 -0
  27. data/spec/unit/renderer/read_template_path_spec.rb +53 -0
  28. data/spec/unit/renderer/render_spec.rb +50 -0
  29. data/spec/unit/renderer/template_path.rb +8 -0
  30. data/spec/unit/renderer/type_map_spec.rb +9 -0
  31. data/spec/unit/request/accept/encoding_spec.rb +9 -0
  32. data/spec/unit/request/accept/language_spec.rb +9 -0
  33. data/spec/unit/request/accept/types_spec.rb +9 -0
  34. data/spec/unit/request/accept_spec.rb +7 -0
  35. data/spec/unit/request/accepttype/each_spec.rb +10 -0
  36. data/spec/unit/request/accepttype/preferred_spec.rb +35 -0
  37. data/spec/unit/request/client/address_spec.rb +9 -0
  38. data/spec/unit/request/client/hostname_spec.rb +9 -0
  39. data/spec/unit/request/client/user_agent_spec.rb +9 -0
  40. data/spec/unit/request/client_spec.rb +8 -0
  41. data/spec/unit/request/content_type_spec.rb +16 -0
  42. data/spec/unit/request/create_spec.rb +21 -0
  43. data/spec/unit/request/delete_spec.rb +15 -0
  44. data/spec/unit/request/get_spec.rb +15 -0
  45. data/spec/unit/request/head_spec.rb +15 -0
  46. data/spec/unit/request/method_spec.rb +8 -0
  47. data/spec/unit/request/parameter/[]_spec.rb +56 -0
  48. data/spec/unit/request/parameter/custom_spec.rb +18 -0
  49. data/spec/unit/request/parameter/initialize_spec.rb +12 -0
  50. data/spec/unit/request/parameter/payload_spec.rb +33 -0
  51. data/spec/unit/request/parameter/query_spec.rb +25 -0
  52. data/spec/unit/request/params_spec.rb +8 -0
  53. data/spec/unit/request/patch_spec.rb +15 -0
  54. data/spec/unit/request/path_spec.rb +9 -0
  55. data/spec/unit/request/post_spec.rb +15 -0
  56. data/spec/unit/request/put_spec.rb +15 -0
  57. data/spec/unit/request/server/hostname_spec.rb +9 -0
  58. data/spec/unit/request/server/port_spec.rb +7 -0
  59. data/spec/unit/request/server/protocol_spec.rb +9 -0
  60. data/spec/unit/request/server/software_spec.rb +9 -0
  61. data/spec/unit/request/server_spec.rb +8 -0
  62. data/spec/unit/response/response_spec.rb +146 -0
  63. data/spec/unit/router/call_spec.rb +55 -0
  64. data/zero.gemspec +27 -0
  65. metadata +345 -0
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zero::Request::Parameter, '#custom' do
4
+ subject { Zero::Request::Parameter.new(env) }
5
+ let(:env) { EnvGenerator.get('/foo') }
6
+
7
+ it 'returns a set custom parameter' do
8
+ subject['foo'] = 'bar'
9
+ expect(subject.custom['foo']).to eq('bar')
10
+ end
11
+
12
+ it 'returns the latest set value' do
13
+ subject['foo'] = 'first'
14
+ subject['foo'] = 'latest'
15
+
16
+ expect(subject.custom['foo']).to eq('latest')
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zero::Request::Parameter, '#initialize' do
4
+ subject { Zero::Request::Parameter.new(env) }
5
+ let(:env) { EnvGenerator.get('/get', {
6
+ 'zero.params.custom' => {'foo' => 'bar'} }) }
7
+
8
+ it 'does not overwrite parameters' do
9
+ subject
10
+ expect(env['zero.params.custom']).to have_key('foo')
11
+ end
12
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zero::Request::Parameter, '#payload' do
4
+ subject { Zero::Request::Parameter.new(env) }
5
+
6
+ context 'without parameters' do
7
+ let(:env) { EnvGenerator.get('/foo') }
8
+ its(:payload) { should == {} }
9
+ end
10
+
11
+ context 'with a query string' do
12
+ let(:env) { EnvGenerator.get('/foo?bar=baz') }
13
+ its(:payload) { should == {} }
14
+ end
15
+
16
+ context 'with a post body' do
17
+ let(:env) do
18
+ EnvGenerator.post('/foo', {
19
+ :input => 'bar=baz', 'CONTENT_TYPE' => 'multipart/form-data'
20
+ })
21
+ end
22
+ its(:payload) { should == {'bar' => 'baz'} }
23
+ end
24
+
25
+ context 'with special characters' do
26
+ let(:env) do
27
+ EnvGenerator.post('/foo', {
28
+ :input => 'bar=foo%20bar', 'CONTENT_TYPE' => 'multipart/form-data'
29
+ })
30
+ end
31
+ its(:payload) { should == {'bar' => 'foo bar'} }
32
+ end
33
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zero::Request::Parameter, '#query' do
4
+ subject { Zero::Request::Parameter.new(env) }
5
+
6
+ context 'without parameters' do
7
+ let(:env) { EnvGenerator.get('/foo') }
8
+ its(:query) { should == {} }
9
+ end
10
+
11
+ context 'with a query string' do
12
+ let(:env) { EnvGenerator.get('/foo?bar=baz') }
13
+ its(:query) { should == {'bar' => 'baz'} }
14
+ end
15
+
16
+ context 'with a post body' do
17
+ let(:env) { EnvGenerator.post('/foo', {:input => 'bar=baz'}) }
18
+ its(:query) { should == {} }
19
+ end
20
+
21
+ context 'with special characters' do
22
+ let(:env) { EnvGenerator.get('/foo?bar=foo%20bar') }
23
+ its(:query) { should == {'bar' => 'foo bar'} }
24
+ end
25
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zero::Request, '#params' do
4
+ subject { Zero::Request.new(env) }
5
+ let(:env) { EnvGenerator.get('/foo?bar=baz') }
6
+
7
+ its(:params) { should be_an_instance_of(Zero::Request::Parameter) }
8
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zero::Request, '#patch?' do
4
+ subject { Zero::Request.new(env) }
5
+
6
+ context "with a post request" do
7
+ let(:env) { EnvGenerator.post('/foo') }
8
+ its(:post?) { should be(true) }
9
+ end
10
+
11
+ context "with a get request" do
12
+ let(:env) { EnvGenerator.get('/foo') }
13
+ its(:post?) { should be(false) }
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zero::Request, '#path' do
4
+ subject { Zero::Request.new(env) }
5
+ let(:path) { '/foo' }
6
+ let(:env) { EnvGenerator.get(path) }
7
+
8
+ its(:path) { should == path }
9
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zero::Request, '#post?' do
4
+ subject { Zero::Request.new(env) }
5
+
6
+ context "with a post request" do
7
+ let(:env) { EnvGenerator.post('/foo') }
8
+ its(:post?) { should be(true) }
9
+ end
10
+
11
+ context "with a get request" do
12
+ let(:env) { EnvGenerator.get('/foo') }
13
+ its(:post?) { should be(false) }
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zero::Request, '#put?' do
4
+ subject { Zero::Request.new(env) }
5
+
6
+ context "with a put request" do
7
+ let(:env) { EnvGenerator.put('/foo') }
8
+ its(:put?) { should be(true) }
9
+ end
10
+
11
+ context "with a get request" do
12
+ let(:env) { EnvGenerator.get('/foo') }
13
+ its(:put?) { should be(false) }
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zero::Request::Server, '#hostname' do
4
+ subject { Zero::Request::Server.new(env) }
5
+ let(:hostname) { 'FooName' }
6
+ let(:env) { EnvGenerator.get('/foo', {'SERVER_NAME' => hostname}) }
7
+ its(:hostname) { should be(hostname) }
8
+ end
9
+
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zero::Request::Server, '#port' do
4
+ subject { Zero::Request::Server.new(env) }
5
+ let(:env) { EnvGenerator.get('/foo', {'SERVER_PORT' => 80}) }
6
+ its(:port) { should be(80) }
7
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zero::Request::Server, '#protocol' do
4
+ subject { Zero::Request::Server.new(env) }
5
+ let(:protocol) { 'HTTP FOO' }
6
+ let(:env) { EnvGenerator.get('/foo', {'SERVER_PROTOCOL' => protocol}) }
7
+ its(:protocol) { should be(protocol) }
8
+ end
9
+
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zero::Request::Server, '#software' do
4
+ subject { Zero::Request::Server.new(env) }
5
+ let(:software) { 'SpecWare Server' }
6
+ let(:env) { EnvGenerator.get('/foo', {'SERVER_SOFTWARE' => software}) }
7
+ its(:software) { should be(software) }
8
+ end
9
+
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zero::Request, '#server' do
4
+ subject { Zero::Request.new(env) }
5
+ let(:env) { EnvGenerator.get('/foo') }
6
+
7
+ its(:server) { should be_an_instance_of(Zero::Request::Server) }
8
+ end
@@ -0,0 +1,146 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ describe Zero::Response do
5
+ subject { Zero::Response.new() }
6
+
7
+ describe '#to_a' do
8
+ it "returns an array within status header and body" do
9
+ subject.status = 200
10
+ subject.header = {}
11
+ subject.body = []
12
+
13
+ value = subject.to_a
14
+
15
+ value.should be_an_instance_of(Array)
16
+ value[0].should eq(200) # Status code
17
+ value[1].should be_an_instance_of(Hash) # Headers
18
+ value[2].should eq([]) # Body
19
+ end
20
+
21
+ it "returns the content length in the header" do
22
+ subject.body = ['foobar']
23
+ value = subject.to_a
24
+
25
+ value[1]['Content-Length'].should eq('6') # Headers
26
+ end
27
+
28
+ it "does not fix the Content-Length, if it's already set" do
29
+ subject.body = ['foobar']
30
+ subject.header = {'Content-Length' => '3'}
31
+ value = subject.to_a
32
+
33
+ value[1]['Content-Length'].should eq('3') # Headers
34
+ end
35
+
36
+ it "returns the Content-Type in the header" do
37
+ subject.header = {'Content-Type' => 'application/json'}
38
+ value = subject.to_a
39
+
40
+ value[1]['Content-Type'].should eq('application/json') # Headers
41
+ end
42
+
43
+ it "returns as default 'text/html' as Content-Type" do
44
+ value = subject.to_a
45
+
46
+ value[1]['Content-Type'].should eq('text/html') # Headers
47
+ end
48
+
49
+ it "removes Content-Type, Content-Length and body on status code 204" do
50
+ subject.body.push '"foobar"'
51
+ subject.content_type = 'application/json'
52
+ subject.header['Content-Length'] = 8
53
+
54
+ subject.status = 204
55
+ value = subject.to_a
56
+
57
+ value[1].should eq({}) # Headers
58
+ end
59
+
60
+ it "removes Content-Type, Content-Length and body on status code 304" do
61
+ subject.body.push '"foobar"'
62
+ subject.content_type = 'application/json'
63
+ subject.header['Content-Length'] = 8
64
+
65
+ subject.status = 304
66
+ value = subject.to_a
67
+
68
+ value[1].should eq({}) # Headers
69
+ end
70
+ end
71
+
72
+ describe '#status' do
73
+ it "must return the status always as an integer" do
74
+ subject.status = "foobar"
75
+ subject.status.should eq(0)
76
+
77
+ subject.status = 240.5
78
+ subject.status.should eq(240)
79
+ end
80
+
81
+ it "must return 200, if no status code was set" do
82
+ subject.status.should eq(200)
83
+ end
84
+ end
85
+
86
+ describe '#header' do
87
+ it "must return an empty hash, if no header was set" do
88
+ subject.header.should eq({})
89
+ end
90
+ end
91
+
92
+ describe '#body' do
93
+ it "must return an empty array, if no body was set" do
94
+ subject.body.should eq([])
95
+ end
96
+ end
97
+
98
+ describe '#content_length' do
99
+ it "sets the Content-Length to '0', if there is no content" do
100
+ subject.content_length
101
+
102
+ subject.header['Content-Length'].should eq('0')
103
+ end
104
+
105
+ it "sets the Content-Length to the size of the message body" do
106
+ subject.body = ['foo', 'bar']
107
+ subject.content_length
108
+
109
+ subject.header['Content-Length'].should eq('6')
110
+ end
111
+
112
+ it "sets the Content-Length to the bytesize of the message body" do
113
+ subject.body = ['föö', 'bär']
114
+ subject.content_length
115
+
116
+ subject.header['Content-Length'].should eq('9')
117
+ end
118
+ end
119
+
120
+ describe '#content_type' do
121
+ it "sets the Content-Type to the given value" do
122
+ subject.content_type = 'application/json'
123
+
124
+ subject.header['Content-Type'].should eq('application/json')
125
+ end
126
+ end
127
+
128
+ describe '#redirect' do
129
+ it "sets the status to 302 and the given Location URL in header" do
130
+ subject.redirect 'http://foo.bar/relocated/thingy'
131
+ value = subject.to_a
132
+
133
+ value[0].should eq(302)
134
+ value[1]['Location'].should eq('http://foo.bar/relocated/thingy')
135
+ end
136
+
137
+ it "sets the given status code and the given Location" do
138
+ subject.redirect('http://foo.bar/relocated/other_thingy', 307)
139
+ value = subject.to_a
140
+
141
+ value[0].should eq(307)
142
+ value[1]['Location'].should eq('http://foo.bar/relocated/other_thingy')
143
+ end
144
+ end
145
+
146
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe Zero::Router, '#call' do
4
+ subject { Zero::Router.new(routes) }
5
+ let(:result) { ['success'] }
6
+
7
+ let(:wrong_app) do
8
+ lambda {|env| [200, {'Content-Type' => 'text/html'}, 'Wrong'] }
9
+ end
10
+ let(:app) { SpecApp }
11
+
12
+ shared_examples_for 'a sample app' do
13
+ it "returns a response" do
14
+ subject.call(env).to_a[2].should eq(result)
15
+ end
16
+ end
17
+
18
+ context 'with a single route' do
19
+ let(:routes) {{ '/' => app }}
20
+ let(:env) { EnvGenerator.get('/') }
21
+ it_behaves_like "a sample app"
22
+ end
23
+
24
+ context 'with multiple router' do
25
+ let(:routes) {{ '/foo' => app, '/wrong' => wrong_app }}
26
+ let(:env) { EnvGenerator.get('/foo') }
27
+ it_behaves_like "a sample app"
28
+ end
29
+
30
+ context 'with nested routes' do
31
+ let(:routes) {{ '/' => wrong_app, '/foo' => app, '/foo/bar' => wrong_app }}
32
+ let(:env) { EnvGenerator.get('/foo') }
33
+ it_behaves_like "a sample app"
34
+ end
35
+
36
+ context 'with a route not found' do
37
+ let(:routes) {{ '/foo' => wrong_app, '/foo/bar/baz' => app }}
38
+ let(:env) { EnvGenerator.get('/foo/bar') }
39
+ let(:result) { ['Not found!'] }
40
+ it_behaves_like "a sample app"
41
+ end
42
+
43
+ context 'with parameters' do
44
+ let(:routes) {{ '/foo/:id' => app }}
45
+ let(:env) { EnvGenerator.get('/foo/bar') }
46
+ let(:app) do
47
+ lambda do |env|
48
+ [200, {}, [Zero::Request.create(env).params['id']]]
49
+ end
50
+ end
51
+ let(:result) { ['bar'] }
52
+
53
+ it_behaves_like 'a sample app'
54
+ end
55
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/zero/version', __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'zero'
6
+ s.version = Zero::VERSION.dup
7
+
8
+ s.authors = ['Gibheer', 'Stormwind']
9
+ s.email = 'gibheer@gmail.com'
10
+ s.summary = 'a toolkit for building web services'
11
+ s.description = 'The focus of this project is to provide small helpers for building web services without the need of learning a complete new web stack from the bottom to the top.'
12
+ s.homepage = 'http://github.com/gibheer/zero'
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.require_paths = %w(lib)
17
+ s.extra_rdoc_files = %w(README.md)
18
+
19
+ s.rubygems_version = '1.8.24'
20
+
21
+ s.add_dependency('tilt')
22
+
23
+ s.add_development_dependency('thor')
24
+ s.add_development_dependency('rack')
25
+ s.add_development_dependency('rspec')
26
+ s.add_development_dependency('simplecov')
27
+ end