angelo 0.1.9 → 0.1.10
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.
- 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
|