angelo 0.1.9 → 0.1.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +65 -0
- data/lib/angelo/base.rb +21 -10
- data/lib/angelo/minitest/helpers.rb +24 -4
- data/lib/angelo/responder.rb +9 -2
- data/lib/angelo/version.rb +1 -1
- data/lib/angelo.rb +25 -0
- data/test/angelo/error_spec.rb +110 -0
- data/test/angelo/mustermann_spec.rb +1 -1
- data/test/angelo/static_spec.rb +4 -4
- data/test/angelo/websocket_spec.rb +1 -1
- data/test/angelo_spec.rb +1 -0
- data/test/test_app_root/public/test.js +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 809986637c600091f82231a4d8b5abc98d8901f6
|
4
|
+
data.tar.gz: 4d85c02a50c030f4405fcb7c957e44fd7c27e3d4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a61261e0ff70da449f4f3b15c55ec9261947d8b49b7b6747a4d660f76862268e25d50254977ef5e428215dbfb732ae03885f9c0c4ee07a14f9ac69b405e0a1b0
|
7
|
+
data.tar.gz: 5558f936798f39acb2a2f56ae98a3ad90fc66fd3a47be8992753f1cf6f5fec24cee5a79833e7a4cad26e2022258aed89084bf54b420c7430479ff37eb876b134
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -157,6 +157,71 @@ get '/' do
|
|
157
157
|
end
|
158
158
|
```
|
159
159
|
|
160
|
+
### Errors
|
161
|
+
|
162
|
+
In Sinatra, one can `halt` and optionally pass status codes and a body. While that functionality
|
163
|
+
is not (yet?) present in Angelo, the ability to `raise` a `RequestError` is. Raising an instance
|
164
|
+
of this always\* causes a 400 status code response, and the message in the instance is the body
|
165
|
+
of the the response. If the route or class was set to respond with JSON, the body is converted
|
166
|
+
to a JSON object with one key, `error`, that has a value of the message. If the message is a
|
167
|
+
`Hash`, the hash is converted to a JSON object, or to a string for other content types.
|
168
|
+
|
169
|
+
\* If you want to return a different status code, you can pass it as a second argument to
|
170
|
+
`RequestError.new`. See example below.
|
171
|
+
|
172
|
+
##### Example
|
173
|
+
|
174
|
+
```ruby
|
175
|
+
get '/' do
|
176
|
+
raise RequestError.new '"foo" is a required parameter' unless params[:foo]
|
177
|
+
params[:foo]
|
178
|
+
end
|
179
|
+
|
180
|
+
get '/json' do
|
181
|
+
content_type :json
|
182
|
+
raise RequestError.new foo: "required!"
|
183
|
+
{foo: params[:foo]}
|
184
|
+
end
|
185
|
+
|
186
|
+
get '/not_found' do
|
187
|
+
raise RequestError.new 'not found', 404
|
188
|
+
end
|
189
|
+
```
|
190
|
+
|
191
|
+
```
|
192
|
+
$ curl -i http://127.0.0.1:4567/
|
193
|
+
HTTP/1.1 400 Bad Request
|
194
|
+
Content-Type: text/html
|
195
|
+
Connection: Keep-Alive
|
196
|
+
Content-Length: 29
|
197
|
+
|
198
|
+
"foo" is a required parameter
|
199
|
+
|
200
|
+
$ curl -i http://127.0.0.1:4567/?foo=bar
|
201
|
+
HTTP/1.1 200 OK
|
202
|
+
Content-Type: text/html
|
203
|
+
Connection: Keep-Alive
|
204
|
+
Content-Length: 3
|
205
|
+
|
206
|
+
bar
|
207
|
+
|
208
|
+
$ curl -i http://127.0.0.1:4567/json
|
209
|
+
HTTP/1.1 400 Bad Request
|
210
|
+
Content-Type: application/json
|
211
|
+
Connection: Keep-Alive
|
212
|
+
Content-Length: 29
|
213
|
+
|
214
|
+
{"error":{"foo":"required!"}}
|
215
|
+
|
216
|
+
$ curl -i http://127.0.0.1:4567/not_found
|
217
|
+
HTTP/1.1 404 Not Found
|
218
|
+
Content-Type: text/html
|
219
|
+
Connection: Keep-Alive
|
220
|
+
Content-Length: 9
|
221
|
+
|
222
|
+
not found
|
223
|
+
```
|
224
|
+
|
160
225
|
### WORK LEFT TO DO
|
161
226
|
|
162
227
|
Lots of work left to do!
|
data/lib/angelo/base.rb
CHANGED
@@ -27,20 +27,31 @@ module Angelo
|
|
27
27
|
attr_accessor :app_file, :server
|
28
28
|
|
29
29
|
def inherited subclass
|
30
|
+
|
31
|
+
# set app_file from caller stack
|
32
|
+
#
|
30
33
|
subclass.app_file = caller(1).map {|l| l.split(/:(?=|in )/, 3)[0,1]}.flatten[0]
|
31
34
|
|
32
|
-
|
33
|
-
|
34
|
-
end
|
35
|
+
# bring RequestError into this namespace
|
36
|
+
#
|
37
|
+
subclass.class_eval 'class RequestError < Angelo::RequestError; end'
|
35
38
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
39
|
+
class << subclass
|
40
|
+
|
41
|
+
def root
|
42
|
+
@root ||= File.expand_path '..', app_file
|
43
|
+
end
|
44
|
+
|
45
|
+
def view_dir
|
46
|
+
v = self.class_variable_get(:@@views) rescue DEFAULT_VIEW_DIR
|
47
|
+
File.join root, v
|
48
|
+
end
|
49
|
+
|
50
|
+
def public_dir
|
51
|
+
p = self.class_variable_get(:@@public_dir) rescue DEFAULT_PUBLIC_DIR
|
52
|
+
File.join root, p
|
53
|
+
end
|
40
54
|
|
41
|
-
def subclass.public_dir
|
42
|
-
p = self.class_variable_get(:@@public_dir) rescue DEFAULT_PUBLIC_DIR
|
43
|
-
File.join root, p
|
44
55
|
end
|
45
56
|
|
46
57
|
end
|
@@ -29,9 +29,7 @@ module Angelo
|
|
29
29
|
|
30
30
|
def hc
|
31
31
|
@hc ||= HTTPClient.new
|
32
|
-
@hc
|
33
32
|
end
|
34
|
-
private :hc
|
35
33
|
|
36
34
|
def hc_req method, path, params = {}, headers = {}
|
37
35
|
url = HTTP_URL % [DEFAULT_ADDR, DEFAULT_PORT]
|
@@ -39,8 +37,30 @@ module Angelo
|
|
39
37
|
end
|
40
38
|
private :hc_req
|
41
39
|
|
40
|
+
def http_req method, path, params = {}, headers = {}
|
41
|
+
url = HTTP_URL % [DEFAULT_ADDR, DEFAULT_PORT] + path
|
42
|
+
params = case params
|
43
|
+
when String; {body: params}
|
44
|
+
when Hash
|
45
|
+
case method
|
46
|
+
when :get, :delete, :options
|
47
|
+
{params: params}
|
48
|
+
else
|
49
|
+
{form: params}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
@last_response = case
|
53
|
+
when !headers.empty?
|
54
|
+
::HTTP.with(headers).__send__ method, url, params
|
55
|
+
else
|
56
|
+
::HTTP.__send__ method, url, params
|
57
|
+
end
|
58
|
+
end
|
59
|
+
private :http_req
|
60
|
+
|
42
61
|
[:get, :post, :put, :delete, :options, :head].each do |m|
|
43
62
|
define_method m do |path, params = {}, headers = {}|
|
63
|
+
# http_req m, path, params, headers
|
44
64
|
hc_req m, path, params, headers
|
45
65
|
end
|
46
66
|
end
|
@@ -63,13 +83,13 @@ module Angelo
|
|
63
83
|
|
64
84
|
def last_response_must_be_html body = ''
|
65
85
|
last_response.status.must_equal 200
|
66
|
-
last_response.body.must_equal body
|
86
|
+
last_response.body.to_s.must_equal body
|
67
87
|
last_response.headers['Content-Type'].split(';').must_include HTML_TYPE
|
68
88
|
end
|
69
89
|
|
70
90
|
def last_response_must_be_json obj = {}
|
71
91
|
last_response.status.must_equal 200
|
72
|
-
JSON.parse(last_response.body).must_equal obj
|
92
|
+
JSON.parse(last_response.body.to_s).must_equal obj
|
73
93
|
last_response.headers['Content-Type'].split(';').must_include JSON_TYPE
|
74
94
|
end
|
75
95
|
|
data/lib/angelo/responder.rb
CHANGED
@@ -49,7 +49,6 @@ module Angelo
|
|
49
49
|
@redirect = nil
|
50
50
|
@request = request
|
51
51
|
handle_request
|
52
|
-
respond
|
53
52
|
end
|
54
53
|
|
55
54
|
def handle_request
|
@@ -57,6 +56,7 @@ module Angelo
|
|
57
56
|
@base.before if @base.respond_to? :before
|
58
57
|
@body = @response_handler.bind(@base).call || ''
|
59
58
|
@base.after if @base.respond_to? :after
|
59
|
+
respond
|
60
60
|
else
|
61
61
|
raise NotImplementedError
|
62
62
|
end
|
@@ -64,6 +64,8 @@ module Angelo
|
|
64
64
|
handle_error jpe, :bad_request, false
|
65
65
|
rescue FormEncodingError => fee
|
66
66
|
handle_error fee, :bad_request, false
|
67
|
+
rescue RequestError => re
|
68
|
+
handle_error re, re.type, false
|
67
69
|
rescue => e
|
68
70
|
handle_error e
|
69
71
|
end
|
@@ -84,7 +86,12 @@ module Angelo
|
|
84
86
|
when respond_with?(:json)
|
85
87
|
{ error: _error.message }.to_json
|
86
88
|
else
|
87
|
-
_error.message
|
89
|
+
case _error.message
|
90
|
+
when Hash
|
91
|
+
_error.message.to_s
|
92
|
+
else
|
93
|
+
_error.message
|
94
|
+
end
|
88
95
|
end
|
89
96
|
end
|
90
97
|
|
data/lib/angelo/version.rb
CHANGED
data/lib/angelo.rb
CHANGED
@@ -73,6 +73,31 @@ module Angelo
|
|
73
73
|
|
74
74
|
end
|
75
75
|
|
76
|
+
class RequestError < Reel::RequestError
|
77
|
+
|
78
|
+
attr_accessor :type
|
79
|
+
alias_method :code=, :type=
|
80
|
+
|
81
|
+
def initialize msg = nil, type = nil
|
82
|
+
case msg
|
83
|
+
when Hash
|
84
|
+
@msg_hash = msg
|
85
|
+
else
|
86
|
+
super(msg)
|
87
|
+
end
|
88
|
+
self.type = type if type
|
89
|
+
end
|
90
|
+
|
91
|
+
def type
|
92
|
+
@type ||= :bad_request
|
93
|
+
end
|
94
|
+
|
95
|
+
def message
|
96
|
+
@msg_hash || super
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
76
101
|
end
|
77
102
|
|
78
103
|
require 'angelo/version'
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe Angelo::Base do
|
4
|
+
|
5
|
+
describe :handle_error do
|
6
|
+
|
7
|
+
define_app do
|
8
|
+
|
9
|
+
Angelo::HTTPABLE.each do |m|
|
10
|
+
__send__ m, '/' do
|
11
|
+
|
12
|
+
# this specificity (self.class::) is uneeded in actual practice
|
13
|
+
# something about the anonymous class nature of self at this point
|
14
|
+
# see Angelo::Minittest::Helpers#define_app
|
15
|
+
#
|
16
|
+
raise self.class::RequestError.new 'error message'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Angelo::HTTPABLE.each do |m|
|
21
|
+
__send__ m, '/json' do
|
22
|
+
content_type :json
|
23
|
+
raise self.class::RequestError.new 'error message' # see above
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
Angelo::HTTPABLE.each do |m|
|
28
|
+
__send__ m, '/msg_hash' do
|
29
|
+
raise self.class::RequestError.new msg: 'error', foo: 'bar' # see above
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
Angelo::HTTPABLE.each do |m|
|
34
|
+
__send__ m, '/msg_hash_json' do
|
35
|
+
content_type :json
|
36
|
+
raise self.class::RequestError.new msg: 'error', foo: 'bar' # see above
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
Angelo::HTTPABLE.each do |m|
|
41
|
+
__send__ m, '/not_found' do
|
42
|
+
raise self.class::RequestError.new 'not found', 404 # see above
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
Angelo::HTTPABLE.each do |m|
|
47
|
+
__send__ m, '/enhance_your_calm' do
|
48
|
+
raise self.class::RequestError.new 'enhance your calm, bro', 420 # see above
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'handles raised errors correctly' do
|
55
|
+
Angelo::HTTPABLE.each do |m|
|
56
|
+
__send__ m, '/'
|
57
|
+
last_response.status.must_equal 400
|
58
|
+
last_response.headers['Content-Type'].split(';').must_include Angelo::HTML_TYPE
|
59
|
+
last_response.body.to_s.must_equal 'error message'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'handles raised errors with json content type correctly' do
|
64
|
+
Angelo::HTTPABLE.each do |m|
|
65
|
+
__send__ m, '/json'
|
66
|
+
last_response.status.must_equal 400
|
67
|
+
last_response.headers['Content-Type'].split(';').must_include Angelo::JSON_TYPE
|
68
|
+
last_response.body.to_s.must_equal({error: 'error message'}.to_json)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'handles raised errors with hash messages correctly' do
|
73
|
+
Angelo::HTTPABLE.each do |m|
|
74
|
+
__send__ m, '/msg_hash'
|
75
|
+
last_response.status.must_equal 400
|
76
|
+
last_response.headers['Content-Type'].split(';').must_include Angelo::HTML_TYPE
|
77
|
+
last_response.body.to_s.must_equal '{:msg=>"error", :foo=>"bar"}'
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'handles raised errors with hash messages with json content type correctly' do
|
82
|
+
Angelo::HTTPABLE.each do |m|
|
83
|
+
__send__ m, '/msg_hash_json'
|
84
|
+
last_response.status.must_equal 400
|
85
|
+
last_response.headers['Content-Type'].split(';').must_include Angelo::JSON_TYPE
|
86
|
+
last_response.body.to_s.must_equal({error: {msg: 'error', foo: 'bar'}}.to_json)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'handles raising errors with other status codes correctly' do
|
91
|
+
|
92
|
+
Angelo::HTTPABLE.each do |m|
|
93
|
+
__send__ m, '/not_found'
|
94
|
+
last_response.status.must_equal 404
|
95
|
+
last_response.headers['Content-Type'].split(';').must_include Angelo::HTML_TYPE
|
96
|
+
last_response.body.to_s.must_equal 'not found'
|
97
|
+
end
|
98
|
+
|
99
|
+
Angelo::HTTPABLE.each do |m|
|
100
|
+
__send__ m, '/enhance_your_calm'
|
101
|
+
last_response.status.must_equal 420
|
102
|
+
last_response.headers['Content-Type'].split(';').must_include Angelo::HTML_TYPE
|
103
|
+
last_response.body.to_s.must_equal 'enhance your calm, bro'
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
data/test/angelo/static_spec.rb
CHANGED
@@ -27,8 +27,8 @@ describe Angelo::Server do
|
|
27
27
|
last_response.headers['Content-Disposition'].must_equal 'attachment; filename=test.css'
|
28
28
|
last_response.headers['Content-Length'].must_equal '116'
|
29
29
|
last_response.headers['Etag'].must_equal css_etag
|
30
|
-
last_response.body.length.must_equal 116
|
31
|
-
last_response.body.must_equal File.read(File.join TEST_APP_ROOT, 'public', 'test.css')
|
30
|
+
last_response.body.to_s.length.must_equal 116
|
31
|
+
last_response.body.to_s.must_equal File.read(File.join TEST_APP_ROOT, 'public', 'test.css')
|
32
32
|
end
|
33
33
|
|
34
34
|
it 'serves headers for static files on head' do
|
@@ -38,7 +38,7 @@ describe Angelo::Server do
|
|
38
38
|
last_response.headers['Content-Disposition'].must_equal 'attachment; filename=test.css'
|
39
39
|
last_response.headers['Content-Length'].must_equal '116'
|
40
40
|
last_response.headers['Etag'].must_equal css_etag
|
41
|
-
last_response.body.length.must_equal 0
|
41
|
+
last_response.body.to_s.length.must_equal 0
|
42
42
|
end
|
43
43
|
|
44
44
|
it 'serves static file over route' do
|
@@ -46,7 +46,7 @@ describe Angelo::Server do
|
|
46
46
|
last_response.status.must_equal 200
|
47
47
|
last_response.headers['Content-Type'].must_equal 'text/html'
|
48
48
|
last_response.headers['Content-Disposition'].must_equal 'attachment; filename=test.html'
|
49
|
-
last_response.body.must_equal File.read(File.join TEST_APP_ROOT, 'public', 'test.html')
|
49
|
+
last_response.body.to_s.must_equal File.read(File.join TEST_APP_ROOT, 'public', 'test.html')
|
50
50
|
end
|
51
51
|
|
52
52
|
it 'not modifieds when if-none-match matched etag' do
|
@@ -3,7 +3,7 @@ require_relative '../spec_helper'
|
|
3
3
|
describe Angelo::WebsocketResponder do
|
4
4
|
|
5
5
|
def websocket_wait_for path, latch, expectation, key = :swf, &block
|
6
|
-
Reactor.testers[key] = Array.new
|
6
|
+
Reactor.testers[key] = Array.new CONCURRENCY do
|
7
7
|
wsh = websocket_helper path
|
8
8
|
wsh.on_message = ->(e) {
|
9
9
|
expectation[e] if Proc === expectation
|
data/test/angelo_spec.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
setTimeout(function(){ alert('hi'); }, 3000);
|
1
|
+
// setTimeout(function(){ alert('hi'); }, 3000);
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: angelo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kenichi Nakamura
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-06-
|
11
|
+
date: 2014-06-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: reel
|
@@ -65,6 +65,7 @@ files:
|
|
65
65
|
- lib/angelo/tilt/erb.rb
|
66
66
|
- lib/angelo/version.rb
|
67
67
|
- test/angelo/erb_spec.rb
|
68
|
+
- test/angelo/error_spec.rb
|
68
69
|
- test/angelo/mustermann_spec.rb
|
69
70
|
- test/angelo/params_spec.rb
|
70
71
|
- test/angelo/static_spec.rb
|