restfulness 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +98 -7
- data/example/README.md +1 -1
- data/example/app.rb +1 -1
- data/lib/restfulness.rb +3 -0
- data/lib/restfulness/dispatchers/rack.rb +6 -19
- data/lib/restfulness/exceptions.rb +5 -5
- data/lib/restfulness/request.rb +1 -1
- data/lib/restfulness/resource.rb +9 -14
- data/lib/restfulness/resources/events.rb +45 -0
- data/lib/restfulness/response.rb +36 -14
- data/lib/restfulness/version.rb +1 -1
- data/spec/unit/exceptions_spec.rb +2 -2
- data/spec/unit/resource_spec.rb +0 -18
- data/spec/unit/resources/events_spec.rb +68 -0
- data/spec/unit/response_spec.rb +71 -5
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 656fc9b27618b6b00f0cdc31dff51fc59081881b
|
4
|
+
data.tar.gz: eed7ea6153eb14c67d0888643287ed911bfb477a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d06a2ee69944bfd705f22516c246ec49293488e69bc7bc5f7357cbe3830ac1b283e5e5046063c4e22e427d5ddf2541811c2d49b0e285eb742c63c0f132b64564
|
7
|
+
data.tar.gz: b297d63290e7df33142641c2c8d072d97bb4494ece946b8ec6f2ddb4c44e10ac6cd997729689ede5fefb299f492c77b74e82b9646f04793ca49f77c473a99e70
|
data/README.md
CHANGED
@@ -27,7 +27,7 @@ module Twitter
|
|
27
27
|
end
|
28
28
|
|
29
29
|
desc "Return a personal timeline."
|
30
|
-
|
30
|
+
get :home_timeline do
|
31
31
|
authenticate!
|
32
32
|
current_user.statuses.limit(20)
|
33
33
|
end
|
@@ -128,8 +128,6 @@ run Rack::URLMap.new(
|
|
128
128
|
)
|
129
129
|
```
|
130
130
|
|
131
|
-
By default, Restfulness comes with a Rack compatible dispatcher, but in the future it might make sense to add others.
|
132
|
-
|
133
131
|
If you want to run Restfulness standalone, simply create a `config.ru` that will load up your application:
|
134
132
|
|
135
133
|
```ruby
|
@@ -143,7 +141,7 @@ You can then run this with rackup:
|
|
143
141
|
bundle exec rackup
|
144
142
|
```
|
145
143
|
|
146
|
-
For
|
144
|
+
For a very simple example project, checkout the `/example` directory in the source code.
|
147
145
|
|
148
146
|
|
149
147
|
### Routes
|
@@ -179,6 +177,7 @@ Resources are like Controllers in a Rails project. They handle the basic HTTP ac
|
|
179
177
|
* `get`
|
180
178
|
* `head`
|
181
179
|
* `post`
|
180
|
+
* `patch`
|
182
181
|
* `put`
|
183
182
|
* `delete`
|
184
183
|
* `options` - this is the only action provded by default
|
@@ -192,8 +191,8 @@ class ProjectResource < Restfulness::Resource
|
|
192
191
|
project
|
193
192
|
end
|
194
193
|
|
195
|
-
# Update the object
|
196
|
-
def
|
194
|
+
# Update the existing object with some new attributes
|
195
|
+
def patch
|
197
196
|
project.update(params)
|
198
197
|
end
|
199
198
|
|
@@ -290,11 +289,99 @@ request.body # "{'key':'value'}" - string payload
|
|
290
289
|
request.params # {'key' => 'value'} - usually a JSON deserialized object
|
291
290
|
```
|
292
291
|
|
292
|
+
## Error Handling
|
293
|
+
|
294
|
+
If you want your application to return anything other than a 200 (or 202) status, you have a couple of options that allow you to send codes back to the client.
|
295
|
+
|
296
|
+
The easiest method is probably just to update the `response` code. Take the following example where we set a 403 response and the model's errors object in the payload:
|
297
|
+
|
298
|
+
```ruby
|
299
|
+
class ProjectResource < Restfulness::Resource
|
300
|
+
def patch
|
301
|
+
if project.update_attributes(request.params)
|
302
|
+
project
|
303
|
+
else
|
304
|
+
response.status = 403
|
305
|
+
project.errors
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
```
|
310
|
+
|
311
|
+
The favourite method in Restfulness however is to use the `HTTPException` class and helper methods that will raise the error for you. For example:
|
312
|
+
|
313
|
+
```ruby
|
314
|
+
class ProjectResource < Restfulness::Resource
|
315
|
+
def patch
|
316
|
+
unless project.update_attributes(request.params)
|
317
|
+
forbidden!(project.errors)
|
318
|
+
end
|
319
|
+
project
|
320
|
+
end
|
321
|
+
end
|
322
|
+
```
|
323
|
+
|
324
|
+
The `forbidden!` bang method will call the `error!` method, which in turn will raise an `HTTPException` with the appropriate status code. Exceptions are permitted to include a payload also, so you could override the `error!` method if you wished with code that will automatically re-format the payload. Another example:
|
325
|
+
|
326
|
+
```ruby
|
327
|
+
# Regular resource
|
328
|
+
class ProjectResource < ApplicationResource
|
329
|
+
def patch
|
330
|
+
unless project.update_attributes(request.params)
|
331
|
+
forbidden!(project) # only send the project object!
|
332
|
+
end
|
333
|
+
project
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
# Main Application Resource
|
338
|
+
class ApplicationResource < Restfulness::Resource
|
339
|
+
# Overwrite the regular error handler so we can provide
|
340
|
+
# our own format.
|
341
|
+
def error!(status, payload = "", opts = {})
|
342
|
+
case payload
|
343
|
+
when ActiveRecord::Base # or your favourite ORM
|
344
|
+
payload = {
|
345
|
+
:errors => payload.errors.full_messages
|
346
|
+
}
|
347
|
+
end
|
348
|
+
super(status, payload, opts)
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
```
|
353
|
+
|
354
|
+
This can be a really nice way to mold your errors into a standard format. All HTTP exceptions generated inside resources will pass through `error!`, even those that a triggered by a callback. It gives a great way to provide your own more complete result, or even just resort to a simple string.
|
355
|
+
|
356
|
+
The currently built in error methods are:
|
357
|
+
|
358
|
+
* `not_modified!`
|
359
|
+
* `bad_request!`
|
360
|
+
* `unauthorized!`
|
361
|
+
* `payment_required!`
|
362
|
+
* `forbidden!`
|
363
|
+
* `resource_not_found!`
|
364
|
+
* `request_timeout!`
|
365
|
+
* `conflict!`
|
366
|
+
* `gone!`
|
367
|
+
* `unprocessable_entity!`
|
368
|
+
|
369
|
+
If you'd like to see me more, please send us a pull request! Failing that, you can create your own by writing something along the lines of:
|
370
|
+
|
371
|
+
```ruby
|
372
|
+
def im_a_teapot!(payload = "")
|
373
|
+
error!(418, payload)
|
374
|
+
end
|
375
|
+
```
|
376
|
+
|
377
|
+
|
293
378
|
## Caveats and TODOs
|
294
379
|
|
295
380
|
Restfulness is still very much a work in progress. Here is a list of things that we'd like to improve or fix:
|
296
381
|
|
297
|
-
* Support for more serializers, not just JSON.
|
382
|
+
* Support for more serializers and content types, not just JSON.
|
383
|
+
* Support path methods for automatic URL generation.
|
384
|
+
* Support redirect exceptions.
|
298
385
|
* Reloading is a PITA (see note below).
|
299
386
|
* Needs more functional testing.
|
300
387
|
* Support for before and after filters in resources, although I'm slightly aprehensive about this.
|
@@ -335,6 +422,10 @@ Restfulness was created by Sam Lown <me@samlown.com> as a solution for building
|
|
335
422
|
|
336
423
|
## History
|
337
424
|
|
425
|
+
### 0.2.0 - October 17, 2013
|
426
|
+
|
427
|
+
Refactoring error handling and reporting so that it is easier to use and simpler.
|
428
|
+
|
338
429
|
### 0.1.0 - October 16, 2013
|
339
430
|
|
340
431
|
First release!
|
data/example/README.md
CHANGED
@@ -33,7 +33,7 @@ Curl is your friend!
|
|
33
33
|
curl -v http://localhost:9292/projects
|
34
34
|
|
35
35
|
# Try updating it
|
36
|
-
curl -v -X
|
36
|
+
curl -v -X PATCH http://localhost:9292/project/project1 -H "Content-Type: application/json" -d "{\"name\":\"First Updated Project\"}"
|
37
37
|
|
38
38
|
# Finally remove it and check the list is empty
|
39
39
|
curl -v -X DELETE http://localhost:9292/project/project1
|
data/example/app.rb
CHANGED
data/lib/restfulness.rb
CHANGED
@@ -11,27 +11,12 @@ module Restfulness
|
|
11
11
|
request = Request.new(app)
|
12
12
|
prepare_request(env, rack_req, request)
|
13
13
|
|
14
|
-
|
15
14
|
# Prepare a suitable response
|
16
15
|
response = Response.new(request)
|
17
16
|
response.run
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
log_response(response.code)
|
22
|
-
[response.code, response.headers, [response.payload || ""]]
|
23
|
-
|
24
|
-
rescue HTTPException => e
|
25
|
-
log_response(e.code)
|
26
|
-
[e.code, {}, [e.payload || ""]]
|
27
|
-
|
28
|
-
#rescue Exception => e
|
29
|
-
# log_response(500)
|
30
|
-
# puts
|
31
|
-
# puts e.message
|
32
|
-
# puts e.backtrace
|
33
|
-
# # Something unknown went wrong
|
34
|
-
# [500, {}, [STATUSES[500]]]
|
18
|
+
log_response(response.status)
|
19
|
+
[response.status, response.headers, [response.payload || ""]]
|
35
20
|
end
|
36
21
|
|
37
22
|
protected
|
@@ -60,6 +45,8 @@ module Restfulness
|
|
60
45
|
:post
|
61
46
|
when 'PUT'
|
62
47
|
:put
|
48
|
+
when 'PATCH'
|
49
|
+
:patch
|
63
50
|
when 'OPTIONS'
|
64
51
|
:options
|
65
52
|
else
|
@@ -67,8 +54,8 @@ module Restfulness
|
|
67
54
|
end
|
68
55
|
end
|
69
56
|
|
70
|
-
def log_response(
|
71
|
-
logger.info("Completed #{
|
57
|
+
def log_response(status)
|
58
|
+
logger.info("Completed #{status} #{STATUSES[status]}")
|
72
59
|
end
|
73
60
|
|
74
61
|
def prepare_headers(env)
|
@@ -3,13 +3,13 @@ module Restfulness
|
|
3
3
|
|
4
4
|
class HTTPException < ::StandardError
|
5
5
|
|
6
|
-
attr_accessor :
|
6
|
+
attr_accessor :status, :payload, :headers
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
@
|
8
|
+
def initialize(status, payload = "", opts = {})
|
9
|
+
@status = status
|
10
10
|
@payload = payload
|
11
|
-
@headers = opts[:headers]
|
12
|
-
super(opts[:message] || STATUSES[
|
11
|
+
@headers = opts[:headers] || {}
|
12
|
+
super(opts[:message] || STATUSES[status])
|
13
13
|
end
|
14
14
|
|
15
15
|
end
|
data/lib/restfulness/request.rb
CHANGED
data/lib/restfulness/resource.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module Restfulness
|
2
2
|
|
3
3
|
class Resource
|
4
|
+
include Resources::Events
|
4
5
|
|
5
6
|
attr_reader :request, :response
|
6
7
|
|
@@ -18,6 +19,8 @@ module Restfulness
|
|
18
19
|
end
|
19
20
|
|
20
21
|
def call
|
22
|
+
# At some point, we might add custom callbacks here. If you really need them though,
|
23
|
+
# you can wrap around the call method easily.
|
21
24
|
send(request.action)
|
22
25
|
end
|
23
26
|
|
@@ -49,14 +52,14 @@ module Restfulness
|
|
49
52
|
|
50
53
|
def check_callbacks
|
51
54
|
# Access control
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
+
method_not_allowed! unless method_allowed?
|
56
|
+
unauthorized! unless authorized?
|
57
|
+
forbidden! unless allowed?
|
55
58
|
|
56
59
|
# The following callbacks only make sense for certain methods
|
57
60
|
if [:head, :get, :put, :delete].include?(request.action)
|
58
61
|
|
59
|
-
|
62
|
+
resource_not_found! unless exists?
|
60
63
|
|
61
64
|
if [:get, :head].include?(request.action)
|
62
65
|
# Resource status
|
@@ -66,26 +69,18 @@ module Restfulness
|
|
66
69
|
end
|
67
70
|
end
|
68
71
|
|
69
|
-
##
|
70
|
-
|
71
|
-
|
72
72
|
protected
|
73
73
|
|
74
|
-
def error(code, payload = nil, opts = {})
|
75
|
-
raise HTTPException.new(code, payload, opts)
|
76
|
-
end
|
77
|
-
|
78
74
|
def logger
|
79
75
|
Restfulness.logger
|
80
76
|
end
|
81
77
|
|
82
|
-
|
83
78
|
private
|
84
79
|
|
85
80
|
def check_if_modified
|
86
81
|
date = request.headers[:if_modified_since]
|
87
82
|
if date && date == last_modified.to_s
|
88
|
-
|
83
|
+
not_modified!
|
89
84
|
end
|
90
85
|
response.headers['Last-Modified'] = last_modified
|
91
86
|
end
|
@@ -93,7 +88,7 @@ module Restfulness
|
|
93
88
|
def check_etag
|
94
89
|
tag = request.headers[:if_none_match]
|
95
90
|
if tag && tag == etag.to_s
|
96
|
-
|
91
|
+
not_modified!
|
97
92
|
end
|
98
93
|
response.headers['ETag'] = etag
|
99
94
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Restfulness
|
2
|
+
module Resources
|
3
|
+
|
4
|
+
# Special events that can be used in replies. The idea here is to cover
|
5
|
+
# the basic messages that most applications will deal with in their
|
6
|
+
# resources.
|
7
|
+
module Events
|
8
|
+
|
9
|
+
# Event definitions go here. We only support a limited subset
|
10
|
+
# so that we don't end up with loads of methods that are not used.
|
11
|
+
# If you'd like to see another, please send us a pull request!
|
12
|
+
SUPPORTED_EVENTS = [
|
13
|
+
# 300 Events
|
14
|
+
[304, :not_modified],
|
15
|
+
|
16
|
+
# 400 Events
|
17
|
+
[400, :bad_request],
|
18
|
+
[401, :unauthorized],
|
19
|
+
[402, :payment_required],
|
20
|
+
[403, :forbidden],
|
21
|
+
[404, :resource_not_found],
|
22
|
+
[405, :method_not_allowed],
|
23
|
+
[408, :request_timeout],
|
24
|
+
[409, :conflict],
|
25
|
+
[410, :gone],
|
26
|
+
[422, :unprocessable_entity]
|
27
|
+
]
|
28
|
+
|
29
|
+
# Main error event handler
|
30
|
+
def error!(code, payload = "", opts = {})
|
31
|
+
raise HTTPException.new(code, payload, opts)
|
32
|
+
end
|
33
|
+
|
34
|
+
SUPPORTED_EVENTS.each do |row|
|
35
|
+
define_method("#{row[1]}!") do |*args|
|
36
|
+
payload = args.shift || ""
|
37
|
+
opts = args.shift || {}
|
38
|
+
error!(row[0], payload, opts)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
data/lib/restfulness/response.rb
CHANGED
@@ -7,14 +7,11 @@ module Restfulness
|
|
7
7
|
attr_reader :request
|
8
8
|
|
9
9
|
# Outgoing data
|
10
|
-
attr_reader :
|
11
|
-
|
10
|
+
attr_reader :status, :headers, :payload
|
12
11
|
|
13
12
|
def initialize(request)
|
14
13
|
@request = request
|
15
|
-
|
16
|
-
# Default headers
|
17
|
-
@headers = {'Content-Type' => 'application/json; charset=utf-8'}
|
14
|
+
@headers = {}
|
18
15
|
end
|
19
16
|
|
20
17
|
def run
|
@@ -31,15 +28,15 @@ module Restfulness
|
|
31
28
|
# Perform the actual work
|
32
29
|
result = resource.call
|
33
30
|
|
34
|
-
|
35
|
-
@payload = MultiJson.encode(result)
|
31
|
+
update_status_and_payload(result.nil? ? 204 : 200, result)
|
36
32
|
else
|
37
|
-
|
38
|
-
# This is not something we can deal with, pass it on
|
39
|
-
@code = 404
|
40
|
-
@payload = ""
|
33
|
+
update_status_and_payload(404)
|
41
34
|
end
|
42
|
-
|
35
|
+
|
36
|
+
rescue HTTPException => e # Deal with HTTP exceptions
|
37
|
+
logger.error(e.message)
|
38
|
+
headers.update(e.headers)
|
39
|
+
update_status_and_payload(e.status, e.payload)
|
43
40
|
end
|
44
41
|
|
45
42
|
def logger
|
@@ -47,9 +44,34 @@ module Restfulness
|
|
47
44
|
end
|
48
45
|
|
49
46
|
protected
|
47
|
+
|
48
|
+
def update_status_and_payload(status, payload = "")
|
49
|
+
self.status = status
|
50
|
+
self.payload = payload
|
51
|
+
end
|
52
|
+
|
53
|
+
def status=(code)
|
54
|
+
@status = code
|
55
|
+
end
|
56
|
+
|
57
|
+
def payload=(body)
|
58
|
+
if body.nil? || body.is_a?(String)
|
59
|
+
@payload = body.to_s
|
60
|
+
update_content_headers(:text)
|
61
|
+
else
|
62
|
+
@payload = MultiJson.encode(body)
|
63
|
+
update_content_headers(:json)
|
64
|
+
end
|
65
|
+
end
|
50
66
|
|
51
|
-
def
|
52
|
-
|
67
|
+
def update_content_headers(type = :json)
|
68
|
+
case type
|
69
|
+
when :json
|
70
|
+
headers['Content-Type'] = 'application/json; charset=utf-8'
|
71
|
+
else # Assume text
|
72
|
+
headers['Content-Type'] = 'text/plain; charset=utf-8'
|
73
|
+
end
|
74
|
+
headers['Content-Length'] = payload.to_s.bytesize.to_s
|
53
75
|
end
|
54
76
|
|
55
77
|
end
|
data/lib/restfulness/version.rb
CHANGED
@@ -5,13 +5,13 @@ describe Restfulness::HTTPException do
|
|
5
5
|
describe "#initialize" do
|
6
6
|
it "should assign variables" do
|
7
7
|
obj = Restfulness::HTTPException.new(200, "payload", :message => 'foo', :headers => {})
|
8
|
-
obj.
|
8
|
+
obj.status.should eql(200)
|
9
9
|
obj.payload.should eql("payload")
|
10
10
|
obj.message.should eql('foo')
|
11
11
|
obj.headers.should eql({})
|
12
12
|
end
|
13
13
|
|
14
|
-
it "should use status
|
14
|
+
it "should use status status for message if none provided" do
|
15
15
|
obj = Restfulness::HTTPException.new(200, "payload")
|
16
16
|
obj.message.should eql('OK')
|
17
17
|
end
|
data/spec/unit/resource_spec.rb
CHANGED
@@ -224,22 +224,4 @@ describe Restfulness::Resource do
|
|
224
224
|
end
|
225
225
|
end
|
226
226
|
|
227
|
-
describe "#error" do
|
228
|
-
|
229
|
-
class Get418Resource < Restfulness::Resource
|
230
|
-
def get
|
231
|
-
error(418, {})
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
it "should raise a new exception" do
|
236
|
-
klass = Get418Resource
|
237
|
-
obj = klass.new(request, response)
|
238
|
-
expect {
|
239
|
-
obj.get
|
240
|
-
}.to raise_error(Restfulness::HTTPException, "I'm A Teapot")
|
241
|
-
end
|
242
|
-
|
243
|
-
end
|
244
|
-
|
245
227
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Restfulness::Resources::Events do
|
5
|
+
|
6
|
+
let :app do
|
7
|
+
Class.new(Restfulness::Application) do
|
8
|
+
routes do
|
9
|
+
# empty
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
let :request do
|
14
|
+
Restfulness::Request.new(app)
|
15
|
+
end
|
16
|
+
let :response do
|
17
|
+
Restfulness::Response.new(request)
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
describe "#error" do
|
22
|
+
|
23
|
+
class Get418Resource < Restfulness::Resource
|
24
|
+
def get
|
25
|
+
error!(418, {})
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should raise a new exception" do
|
30
|
+
klass = Get418Resource
|
31
|
+
obj = klass.new(request, response)
|
32
|
+
expect {
|
33
|
+
obj.get
|
34
|
+
}.to raise_error(Restfulness::HTTPException, "I'm A Teapot")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "generic bang error events" do
|
39
|
+
|
40
|
+
let :klass do
|
41
|
+
Class.new(Restfulness::Resource)
|
42
|
+
end
|
43
|
+
|
44
|
+
let :obj do
|
45
|
+
klass.new(request, response)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should support bad_request!" do
|
49
|
+
expect {
|
50
|
+
obj.instance_eval do
|
51
|
+
bad_request!
|
52
|
+
end
|
53
|
+
}.to raise_error(Restfulness::HTTPException, "Bad Request")
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should support bad_request! with paramters" do
|
57
|
+
obj.should_receive(:error!).with(400, {:pay => 'load'}, {})
|
58
|
+
obj.instance_eval do
|
59
|
+
bad_request!({:pay => 'load'}, {})
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
|
68
|
+
end
|
data/spec/unit/response_spec.rb
CHANGED
@@ -26,8 +26,9 @@ describe Restfulness::Response do
|
|
26
26
|
describe "#initialize" do
|
27
27
|
it "should assign request and headers" do
|
28
28
|
obj.request.should eql(request)
|
29
|
-
obj.headers.should eql({
|
30
|
-
obj.
|
29
|
+
obj.headers.should eql({})
|
30
|
+
obj.status.should be_nil
|
31
|
+
obj.payload.should be_nil
|
31
32
|
end
|
32
33
|
end
|
33
34
|
|
@@ -36,8 +37,9 @@ describe Restfulness::Response do
|
|
36
37
|
it "should not do anything" do
|
37
38
|
request.stub(:route).and_return(nil)
|
38
39
|
obj.run
|
39
|
-
obj.
|
40
|
+
obj.status.should eql(404)
|
40
41
|
obj.payload.should be_empty
|
42
|
+
obj.headers['Content-Type'].should match(/text\/plain/)
|
41
43
|
obj.headers['Content-Length'].should eql(0.to_s)
|
42
44
|
end
|
43
45
|
end
|
@@ -54,12 +56,76 @@ describe Restfulness::Response do
|
|
54
56
|
resource.should_receive(:call).and_return({:foo => 'bar'})
|
55
57
|
route.stub(:build_resource).and_return(resource)
|
56
58
|
obj.run
|
57
|
-
obj.
|
59
|
+
obj.status.should eql(200)
|
58
60
|
str = "{\"foo\":\"bar\"}"
|
59
61
|
obj.payload.should eql(str)
|
62
|
+
obj.headers['Content-Type'].should match(/application\/json/)
|
60
63
|
obj.headers['Content-Length'].should eql(str.bytesize.to_s)
|
61
|
-
end
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should call resource and set 204 result if no content" do
|
67
|
+
request.stub(:route).and_return(route)
|
68
|
+
request.action = :get
|
69
|
+
resource = double(:Resource)
|
70
|
+
resource.should_receive(:check_callbacks)
|
71
|
+
resource.should_receive(:call).and_return(nil)
|
72
|
+
route.stub(:build_resource).and_return(resource)
|
73
|
+
obj.run
|
74
|
+
obj.status.should eql(204)
|
75
|
+
obj.headers['Content-Type'].should match(/text\/plain/)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should set string content type if payload is a string" do
|
79
|
+
request.stub(:route).and_return(route)
|
80
|
+
request.action = :get
|
81
|
+
resource = double(:Resource)
|
82
|
+
resource.should_receive(:check_callbacks)
|
83
|
+
resource.should_receive(:call).and_return("This is a text message")
|
84
|
+
route.stub(:build_resource).and_return(resource)
|
85
|
+
obj.run
|
86
|
+
obj.status.should eql(200)
|
87
|
+
obj.headers['Content-Type'].should match(/text\/plain/)
|
88
|
+
end
|
62
89
|
end
|
90
|
+
|
91
|
+
context "with exceptions" do
|
92
|
+
let :route do
|
93
|
+
app.router.routes.first
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should update the status and payload" do
|
97
|
+
request.stub(:route).and_return(route)
|
98
|
+
request.action = :get
|
99
|
+
resource = double(:Resource)
|
100
|
+
txt = "This is a text error"
|
101
|
+
resource.stub(:check_callbacks) do
|
102
|
+
raise Restfulness::HTTPException.new(418, txt)
|
103
|
+
end
|
104
|
+
route.stub(:build_resource).and_return(resource)
|
105
|
+
obj.run
|
106
|
+
obj.status.should eql(418)
|
107
|
+
obj.headers['Content-Type'].should match(/text\/plain/)
|
108
|
+
obj.payload.should eql(txt)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should update the status and provide JSON payload" do
|
112
|
+
request.stub(:route).and_return(route)
|
113
|
+
request.action = :get
|
114
|
+
resource = double(:Resource)
|
115
|
+
err = {:error => "This is a text error"}
|
116
|
+
resource.stub(:check_callbacks) do
|
117
|
+
raise Restfulness::HTTPException.new(418, err)
|
118
|
+
end
|
119
|
+
route.stub(:build_resource).and_return(resource)
|
120
|
+
obj.run
|
121
|
+
obj.status.should eql(418)
|
122
|
+
obj.headers['Content-Type'].should match(/application\/json/)
|
123
|
+
obj.payload.should eql(err.to_json)
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
end
|
128
|
+
|
63
129
|
end
|
64
130
|
|
65
131
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: restfulness
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Lown
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-10-
|
11
|
+
date: 2013-10-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -134,6 +134,7 @@ files:
|
|
134
134
|
- lib/restfulness/path.rb
|
135
135
|
- lib/restfulness/request.rb
|
136
136
|
- lib/restfulness/resource.rb
|
137
|
+
- lib/restfulness/resources/events.rb
|
137
138
|
- lib/restfulness/response.rb
|
138
139
|
- lib/restfulness/route.rb
|
139
140
|
- lib/restfulness/router.rb
|
@@ -148,6 +149,7 @@ files:
|
|
148
149
|
- spec/unit/path_spec.rb
|
149
150
|
- spec/unit/request_spec.rb
|
150
151
|
- spec/unit/resource_spec.rb
|
152
|
+
- spec/unit/resources/events_spec.rb
|
151
153
|
- spec/unit/response_spec.rb
|
152
154
|
- spec/unit/route_spec.rb
|
153
155
|
- spec/unit/router_spec.rb
|
@@ -184,6 +186,7 @@ test_files:
|
|
184
186
|
- spec/unit/path_spec.rb
|
185
187
|
- spec/unit/request_spec.rb
|
186
188
|
- spec/unit/resource_spec.rb
|
189
|
+
- spec/unit/resources/events_spec.rb
|
187
190
|
- spec/unit/response_spec.rb
|
188
191
|
- spec/unit/route_spec.rb
|
189
192
|
- spec/unit/router_spec.rb
|