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