angelo 0.1.10 → 0.1.11
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 +5 -1
- data/README.md +41 -8
- data/lib/angelo/base.rb +5 -1
- data/lib/angelo/responder.rb +24 -13
- data/lib/angelo/server.rb +1 -1
- data/lib/angelo/version.rb +1 -1
- data/lib/angelo.rb +3 -1
- data/test/angelo/error_spec.rb +95 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5dd4947d9238c95169d7a38db1ff5964db35efad
|
4
|
+
data.tar.gz: 65d2a79bfca8c5879721516d31029fd756ebc1c5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1da99edfa1e946edd6f62764a95ab09e999cc6eccef9d915d0741e6ed8912ed8629d28b58605c7960972c0795c5ab912b2824bb018322c82bd90b1c7fee08be9
|
7
|
+
data.tar.gz: 194fda3f3e9bf9f7a7b0ce2156bf8e34d2134ba7013e1366a7a8363e1cbba72c826daed7b8f1c934894e9431c8bcc87ab1d9c2b905a0741151d212d9a6c238b2
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -29,6 +29,9 @@ route handlers denoted by HTTP verb and path. Unlike Sinatra, the only acceptabl
|
|
29
29
|
route block is the body of the response in full. Chunked response support was recently added to Reel,
|
30
30
|
and look for that support in Angelo soon.
|
31
31
|
|
32
|
+
Angelo also features `before` and `after` blocks, just like Sinatra. The one difference lies in how
|
33
|
+
after blocks are handled. See the Errors section below for more info.
|
34
|
+
|
32
35
|
There is also [Mustermann](#mustermann) support for full-on, Sinatra-like path
|
33
36
|
matching and params.
|
34
37
|
|
@@ -157,18 +160,35 @@ get '/' do
|
|
157
160
|
end
|
158
161
|
```
|
159
162
|
|
160
|
-
### Errors
|
163
|
+
### Errors and Halting
|
164
|
+
|
165
|
+
Angelo gives you two ordained methods of stopping route processing:
|
166
|
+
|
167
|
+
* raise an instance of `RequestError`
|
168
|
+
* `halt` with a status code and message
|
169
|
+
|
170
|
+
The main difference is that `halt` will still run an `after` block, and raising `RequestError`
|
171
|
+
will bypass the `after` block.
|
161
172
|
|
162
|
-
|
163
|
-
|
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.
|
173
|
+
Any other exceptions or errors raised by your route handler will be handled with a 500 status
|
174
|
+
code and the message will be the body of the response.
|
168
175
|
|
169
|
-
|
176
|
+
#### RequestError
|
177
|
+
|
178
|
+
Raising an instance of `Angelo::RequestError` causes a 400 status code response, and the message
|
179
|
+
in the instance is the body of the the response. If the route or class was set to respond with
|
180
|
+
JSON, the body is converted to a JSON object with one key, `error`, that has a value of the message.
|
181
|
+
If the message is a `Hash`, the hash is converted to a JSON object, or to a string for other content
|
182
|
+
types.
|
183
|
+
|
184
|
+
If you want to return a different status code, you can pass it as a second argument to
|
170
185
|
`RequestError.new`. See example below.
|
171
186
|
|
187
|
+
#### Halting
|
188
|
+
|
189
|
+
You can `halt` from within any route handler, optionally passing status code and a body. The
|
190
|
+
body is handled the same way as raising `RequestError`.
|
191
|
+
|
172
192
|
##### Example
|
173
193
|
|
174
194
|
```ruby
|
@@ -186,6 +206,11 @@ end
|
|
186
206
|
get '/not_found' do
|
187
207
|
raise RequestError.new 'not found', 404
|
188
208
|
end
|
209
|
+
|
210
|
+
get '/halt' do
|
211
|
+
halt 200, "everything's fine"
|
212
|
+
raise RequestError.new "won't get here"
|
213
|
+
end
|
189
214
|
```
|
190
215
|
|
191
216
|
```
|
@@ -220,6 +245,14 @@ Connection: Keep-Alive
|
|
220
245
|
Content-Length: 9
|
221
246
|
|
222
247
|
not found
|
248
|
+
|
249
|
+
$ curl -i http://127.0.0.1:4567/halt
|
250
|
+
HTTP/1.1 200 OK
|
251
|
+
Content-Type: text/html
|
252
|
+
Connection: Keep-Alive
|
253
|
+
Content-Length: 18
|
254
|
+
|
255
|
+
everything's fine
|
223
256
|
```
|
224
257
|
|
225
258
|
### WORK LEFT TO DO
|
data/lib/angelo/base.rb
CHANGED
@@ -86,7 +86,7 @@ module Angelo
|
|
86
86
|
end
|
87
87
|
|
88
88
|
def websocket path, &block
|
89
|
-
routes[:
|
89
|
+
routes[:websocket][path] = WebsocketResponder.new &block
|
90
90
|
end
|
91
91
|
|
92
92
|
def on_pong &block
|
@@ -166,6 +166,10 @@ module Angelo
|
|
166
166
|
end
|
167
167
|
end
|
168
168
|
|
169
|
+
def halt status = 400, body = ''
|
170
|
+
throw :halt, HALT_STRUCT.new(status, body)
|
171
|
+
end
|
172
|
+
|
169
173
|
end
|
170
174
|
|
171
175
|
end
|
data/lib/angelo/responder.rb
CHANGED
@@ -47,6 +47,7 @@ module Angelo
|
|
47
47
|
def request= request
|
48
48
|
@params = nil
|
49
49
|
@redirect = nil
|
50
|
+
@body = nil
|
50
51
|
@request = request
|
51
52
|
handle_request
|
52
53
|
end
|
@@ -54,7 +55,7 @@ module Angelo
|
|
54
55
|
def handle_request
|
55
56
|
if @response_handler
|
56
57
|
@base.before if @base.respond_to? :before
|
57
|
-
@body = @response_handler.bind(@base).call || ''
|
58
|
+
@body = catch(:halt) { @response_handler.bind(@base).call || '' }
|
58
59
|
@base.after if @base.respond_to? :after
|
59
60
|
respond
|
60
61
|
else
|
@@ -122,18 +123,28 @@ module Angelo
|
|
122
123
|
end
|
123
124
|
|
124
125
|
def respond
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
126
|
+
status = nil
|
127
|
+
case @body
|
128
|
+
when HALT_STRUCT
|
129
|
+
status = @body.status
|
130
|
+
@body = @body.body
|
131
|
+
if Hash === @body
|
132
|
+
@body = {error: @body} if status != :ok or status < 200 && status >= 300
|
133
|
+
@body = @body.to_json if respond_with? :json
|
134
|
+
end
|
135
|
+
|
136
|
+
when String
|
137
|
+
JSON.parse @body if respond_with? :json # for the raises
|
138
|
+
|
139
|
+
when Hash
|
140
|
+
raise 'html response requires String' if respond_with? :html
|
141
|
+
@body = @body.to_json if respond_with? :json
|
142
|
+
|
143
|
+
when NilClass
|
144
|
+
@body = EMPTY_STRING
|
145
|
+
end
|
146
|
+
|
147
|
+
status ||= @redirect.nil? ? :ok : :moved_permanently
|
137
148
|
headers LOCATION_HEADER_KEY => @redirect if @redirect
|
138
149
|
|
139
150
|
Angelo.log @connection, @request, nil, status, @body.size
|
data/lib/angelo/server.rb
CHANGED
@@ -19,7 +19,7 @@ module Angelo
|
|
19
19
|
def on_connection connection
|
20
20
|
# RubyProf.resume
|
21
21
|
connection.each_request do |request|
|
22
|
-
meth = request.websocket? ? :
|
22
|
+
meth = request.websocket? ? :websocket : request.method.downcase.to_sym
|
23
23
|
dispatch! meth, connection, request
|
24
24
|
end
|
25
25
|
# RubyProf.pause
|
data/lib/angelo/version.rb
CHANGED
data/lib/angelo.rb
CHANGED
@@ -14,7 +14,7 @@ module Angelo
|
|
14
14
|
DELETE = 'DELETE'
|
15
15
|
OPTIONS = 'OPTIONS'
|
16
16
|
|
17
|
-
ROUTABLE = [:get, :post, :put, :delete, :
|
17
|
+
ROUTABLE = [:get, :post, :put, :delete, :websocket]
|
18
18
|
HTTPABLE = [:get, :post, :put, :delete]
|
19
19
|
STATICABLE = [:get, :head]
|
20
20
|
|
@@ -51,6 +51,8 @@ module Angelo
|
|
51
51
|
DASH = '-'
|
52
52
|
EMPTY_STRING = ''
|
53
53
|
|
54
|
+
HALT_STRUCT = Struct.new :status, :body
|
55
|
+
|
54
56
|
def self.log connection, request, socket, status, body_size = '-'
|
55
57
|
|
56
58
|
remote_ip = ->{
|
data/test/angelo/error_spec.rb
CHANGED
@@ -4,8 +4,17 @@ describe Angelo::Base do
|
|
4
4
|
|
5
5
|
describe :handle_error do
|
6
6
|
|
7
|
+
after_ran = false
|
8
|
+
before do
|
9
|
+
after_ran = false
|
10
|
+
end
|
11
|
+
|
7
12
|
define_app do
|
8
13
|
|
14
|
+
after do
|
15
|
+
after_ran = true
|
16
|
+
end
|
17
|
+
|
9
18
|
Angelo::HTTPABLE.each do |m|
|
10
19
|
__send__ m, '/' do
|
11
20
|
|
@@ -105,6 +114,92 @@ describe Angelo::Base do
|
|
105
114
|
|
106
115
|
end
|
107
116
|
|
117
|
+
it 'does not run after blocks when handling a raised error' do
|
118
|
+
Angelo::HTTPABLE.each do |m|
|
119
|
+
__send__ m, '/'
|
120
|
+
last_response.status.must_equal 400
|
121
|
+
last_response.headers['Content-Type'].split(';').must_include Angelo::HTML_TYPE
|
122
|
+
last_response.body.to_s.must_equal 'error message'
|
123
|
+
refute after_ran, 'after block should not have ran'
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
describe :halt do
|
130
|
+
|
131
|
+
after_ran = false
|
132
|
+
before do
|
133
|
+
after_ran = false
|
134
|
+
end
|
135
|
+
|
136
|
+
define_app do
|
137
|
+
|
138
|
+
after do
|
139
|
+
after_ran = true
|
140
|
+
end
|
141
|
+
|
142
|
+
Angelo::HTTPABLE.each do |m|
|
143
|
+
__send__ m, '/halt' do
|
144
|
+
halt
|
145
|
+
raise RequestError.new "shouldn't get here"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
Angelo::HTTPABLE.each do |m|
|
150
|
+
__send__ m, '/teapot' do
|
151
|
+
halt 418, "i'm a teapot"
|
152
|
+
raise RequestError.new "shouldn't get here"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
Angelo::HTTPABLE.each do |m|
|
157
|
+
__send__ m, '/calm_json' do
|
158
|
+
content_type :json
|
159
|
+
halt 420, {calm: 'enhance'}
|
160
|
+
raise RequestError.new "shouldn't get here"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'halts properly' do
|
167
|
+
Angelo::HTTPABLE.each do |m|
|
168
|
+
__send__ m, '/halt'
|
169
|
+
last_response.status.must_equal 400
|
170
|
+
last_response.headers['Content-Type'].split(';').must_include Angelo::HTML_TYPE
|
171
|
+
last_response.body.to_s.must_equal ''
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'halts with different status code properly' do
|
176
|
+
Angelo::HTTPABLE.each do |m|
|
177
|
+
__send__ m, '/teapot'
|
178
|
+
last_response.status.must_equal 418
|
179
|
+
last_response.headers['Content-Type'].split(';').must_include Angelo::HTML_TYPE
|
180
|
+
last_response.body.to_s.must_equal "i'm a teapot"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'halts with json content type correctly' do
|
185
|
+
Angelo::HTTPABLE.each do |m|
|
186
|
+
__send__ m, '/calm_json'
|
187
|
+
last_response.status.must_equal 420
|
188
|
+
last_response.headers['Content-Type'].split(';').must_include Angelo::JSON_TYPE
|
189
|
+
last_response.body.to_s.must_equal({error: {calm: 'enhance'}}.to_json)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'runs after blocks when halting' do
|
194
|
+
Angelo::HTTPABLE.each do |m|
|
195
|
+
__send__ m, '/halt'
|
196
|
+
last_response.status.must_equal 400
|
197
|
+
last_response.headers['Content-Type'].split(';').must_include Angelo::HTML_TYPE
|
198
|
+
last_response.body.to_s.must_equal ''
|
199
|
+
assert after_ran, 'after block should have ran'
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
108
203
|
end
|
109
204
|
|
110
205
|
end
|
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.11
|
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-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: reel
|