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.
- data/.gitignore +2 -0
- data/.rspec +3 -0
- data/.travis.yml +8 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +59 -0
- data/Guardfile +15 -0
- data/README.md +60 -0
- data/Thorfile +6 -0
- data/lib/zero.rb +7 -0
- data/lib/zero/controller.rb +46 -0
- data/lib/zero/rack_request.rb +44 -0
- data/lib/zero/renderer.rb +139 -0
- data/lib/zero/request.rb +100 -0
- data/lib/zero/request/accept.rb +28 -0
- data/lib/zero/request/accept_type.rb +58 -0
- data/lib/zero/request/client.rb +31 -0
- data/lib/zero/request/parameter.rb +101 -0
- data/lib/zero/request/server.rb +41 -0
- data/lib/zero/response.rb +80 -0
- data/lib/zero/router.rb +63 -0
- data/lib/zero/version.rb +3 -0
- data/spec/fixtures/templates/index.html.erb +1 -0
- data/spec/fixtures/templates/index.json.erb +1 -0
- data/spec/spec_helper.rb +65 -0
- data/spec/unit/controller/call_spec.rb +22 -0
- data/spec/unit/controller/renderer_spec.rb +11 -0
- data/spec/unit/renderer/read_template_path_spec.rb +53 -0
- data/spec/unit/renderer/render_spec.rb +50 -0
- data/spec/unit/renderer/template_path.rb +8 -0
- data/spec/unit/renderer/type_map_spec.rb +9 -0
- data/spec/unit/request/accept/encoding_spec.rb +9 -0
- data/spec/unit/request/accept/language_spec.rb +9 -0
- data/spec/unit/request/accept/types_spec.rb +9 -0
- data/spec/unit/request/accept_spec.rb +7 -0
- data/spec/unit/request/accepttype/each_spec.rb +10 -0
- data/spec/unit/request/accepttype/preferred_spec.rb +35 -0
- data/spec/unit/request/client/address_spec.rb +9 -0
- data/spec/unit/request/client/hostname_spec.rb +9 -0
- data/spec/unit/request/client/user_agent_spec.rb +9 -0
- data/spec/unit/request/client_spec.rb +8 -0
- data/spec/unit/request/content_type_spec.rb +16 -0
- data/spec/unit/request/create_spec.rb +21 -0
- data/spec/unit/request/delete_spec.rb +15 -0
- data/spec/unit/request/get_spec.rb +15 -0
- data/spec/unit/request/head_spec.rb +15 -0
- data/spec/unit/request/method_spec.rb +8 -0
- data/spec/unit/request/parameter/[]_spec.rb +56 -0
- data/spec/unit/request/parameter/custom_spec.rb +18 -0
- data/spec/unit/request/parameter/initialize_spec.rb +12 -0
- data/spec/unit/request/parameter/payload_spec.rb +33 -0
- data/spec/unit/request/parameter/query_spec.rb +25 -0
- data/spec/unit/request/params_spec.rb +8 -0
- data/spec/unit/request/patch_spec.rb +15 -0
- data/spec/unit/request/path_spec.rb +9 -0
- data/spec/unit/request/post_spec.rb +15 -0
- data/spec/unit/request/put_spec.rb +15 -0
- data/spec/unit/request/server/hostname_spec.rb +9 -0
- data/spec/unit/request/server/port_spec.rb +7 -0
- data/spec/unit/request/server/protocol_spec.rb +9 -0
- data/spec/unit/request/server/software_spec.rb +9 -0
- data/spec/unit/request/server_spec.rb +8 -0
- data/spec/unit/response/response_spec.rb +146 -0
- data/spec/unit/router/call_spec.rb +55 -0
- data/zero.gemspec +27 -0
- 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,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,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,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,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
|
data/zero.gemspec
ADDED
@@ -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
|