zero 0.1.0

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