restify 0.1.0
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 +7 -0
- data/LICENSE.txt +165 -0
- data/README.md +96 -0
- data/doc/file.README.html +107 -0
- data/lib/restify.rb +42 -0
- data/lib/restify/adapter.rb +74 -0
- data/lib/restify/client.rb +106 -0
- data/lib/restify/collection.rb +69 -0
- data/lib/restify/link.rb +100 -0
- data/lib/restify/parser/json.rb +16 -0
- data/lib/restify/relation.rb +33 -0
- data/lib/restify/relations.rb +35 -0
- data/lib/restify/request.rb +37 -0
- data/lib/restify/resource.rb +131 -0
- data/lib/restify/response.rb +142 -0
- data/lib/restify/version.rb +13 -0
- data/restify.gemspec +29 -0
- data/spec/restify/link_spec.rb +53 -0
- data/spec/restify/resource_spec.rb +96 -0
- data/spec/restify_spec.rb +219 -0
- data/spec/spec_helper.rb +21 -0
- metadata +175 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
require 'rack/utils'
|
|
2
|
+
|
|
3
|
+
module Restify
|
|
4
|
+
#
|
|
5
|
+
# A {Response} is returned from an {Adapter} and described
|
|
6
|
+
# a HTTP response. That includes status code, headers and
|
|
7
|
+
# body.
|
|
8
|
+
#
|
|
9
|
+
# A {Response} is also responsible for decoding its body
|
|
10
|
+
# according its content type.
|
|
11
|
+
#
|
|
12
|
+
class Response
|
|
13
|
+
#
|
|
14
|
+
# Map of status symbols to codes. From Rack::Utils.
|
|
15
|
+
#
|
|
16
|
+
# @example
|
|
17
|
+
# SYMBOL_TO_STATUS_CODE[:ok] #=> 200
|
|
18
|
+
#
|
|
19
|
+
SYMBOL_TO_STATUS_CODE = Rack::Utils::SYMBOL_TO_STATUS_CODE
|
|
20
|
+
|
|
21
|
+
# Map of status codes to symbols.
|
|
22
|
+
#
|
|
23
|
+
# @example
|
|
24
|
+
# STATUS_CODE_TO_SYMBOL[200] #=> :ok
|
|
25
|
+
#
|
|
26
|
+
STATUS_CODE_TO_SYMBOL = SYMBOL_TO_STATUS_CODE.invert
|
|
27
|
+
|
|
28
|
+
# Response body as string.
|
|
29
|
+
#
|
|
30
|
+
# @return [String] Response body.
|
|
31
|
+
#
|
|
32
|
+
attr_reader :body
|
|
33
|
+
|
|
34
|
+
# Response headers as hash.
|
|
35
|
+
#
|
|
36
|
+
# @return [Hash<String, String>] Response headers.
|
|
37
|
+
#
|
|
38
|
+
attr_reader :headers
|
|
39
|
+
|
|
40
|
+
# Response status code.
|
|
41
|
+
#
|
|
42
|
+
# @return [Fixnum] Status code.
|
|
43
|
+
#
|
|
44
|
+
attr_reader :code
|
|
45
|
+
|
|
46
|
+
# Response status symbol.
|
|
47
|
+
#
|
|
48
|
+
# @example
|
|
49
|
+
# response.status #=> :ok
|
|
50
|
+
#
|
|
51
|
+
# @return [Symbol] Status symbol.
|
|
52
|
+
#
|
|
53
|
+
attr_reader :status
|
|
54
|
+
|
|
55
|
+
# Response status message.
|
|
56
|
+
#
|
|
57
|
+
# @return [String] Status message.
|
|
58
|
+
#
|
|
59
|
+
attr_reader :message
|
|
60
|
+
|
|
61
|
+
# The request that led to this response.
|
|
62
|
+
#
|
|
63
|
+
# @return [Request] Request object.
|
|
64
|
+
#
|
|
65
|
+
attr_reader :request
|
|
66
|
+
|
|
67
|
+
# @api private
|
|
68
|
+
#
|
|
69
|
+
def initialize(request, code, headers, body)
|
|
70
|
+
@request = request
|
|
71
|
+
@code = code
|
|
72
|
+
@status = STATUS_CODE_TO_SYMBOL[code]
|
|
73
|
+
@headers = headers
|
|
74
|
+
@body = body
|
|
75
|
+
@message = Rack::Utils::HTTP_STATUS_CODES[code]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Return URL of this response.
|
|
79
|
+
#
|
|
80
|
+
def url
|
|
81
|
+
request.uri
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Return list of links from the Link header.
|
|
85
|
+
#
|
|
86
|
+
# @return [Array<Link>] Links.
|
|
87
|
+
#
|
|
88
|
+
def links
|
|
89
|
+
@links ||= begin
|
|
90
|
+
if headers['Link']
|
|
91
|
+
begin
|
|
92
|
+
Link.parse(headers['Link'])
|
|
93
|
+
rescue ArgumentError => e
|
|
94
|
+
warn e
|
|
95
|
+
[]
|
|
96
|
+
end
|
|
97
|
+
else
|
|
98
|
+
[]
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Return list of relations extracted from links.
|
|
104
|
+
#
|
|
105
|
+
# @return [Array<Relation>] Relations.
|
|
106
|
+
#
|
|
107
|
+
def relations(client)
|
|
108
|
+
relations = {}
|
|
109
|
+
links.each do |link|
|
|
110
|
+
if (rel = link.metadata['rel'])
|
|
111
|
+
relations[rel] = Relation.new(client, link.uri)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
relations
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Return decoded body according to content type.
|
|
118
|
+
# Will return `nil` if content cannot be decoded.
|
|
119
|
+
#
|
|
120
|
+
# @return [Array, Hash, NilClass] Decoded response body.
|
|
121
|
+
#
|
|
122
|
+
def decoded_body
|
|
123
|
+
@decoded_body ||= begin
|
|
124
|
+
case headers['Content-Type']
|
|
125
|
+
when /\Aapplication\/json($|;)/
|
|
126
|
+
MultiJson.load body
|
|
127
|
+
else
|
|
128
|
+
nil
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Check if response is successful e.g. the status code
|
|
134
|
+
# is on of 2XX.
|
|
135
|
+
#
|
|
136
|
+
# @return [Boolean] True if status code is 2XX otherwise false.
|
|
137
|
+
#
|
|
138
|
+
def success?
|
|
139
|
+
(200...300) === code
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
data/restify.gemspec
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'restify/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = 'restify'
|
|
8
|
+
spec.version = Restify::VERSION
|
|
9
|
+
spec.authors = ['Jan Graichen']
|
|
10
|
+
spec.email = ['jg@altimos.de']
|
|
11
|
+
spec.summary = %q{An experimental hypermedia REST client that uses parallel, keep-alive and pipelined requests by default.}
|
|
12
|
+
spec.description = %q{An experimental hypermedia REST client that uses parallel, keep-alive and pipelined requests by default.}
|
|
13
|
+
spec.homepage = 'https://github.com/jgraichen/restify'
|
|
14
|
+
spec.license = 'LGPLv3'
|
|
15
|
+
|
|
16
|
+
spec.files = Dir['**/*'].grep(%r{^((bin|lib|test|spec|features)/|.*\.gemspec|.*LICENSE.*|.*README.*|.*CHANGELOG.*)})
|
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
|
+
spec.require_paths = ['lib']
|
|
20
|
+
|
|
21
|
+
spec.add_runtime_dependency 'obligation', '~> 0.1'
|
|
22
|
+
spec.add_runtime_dependency 'addressable', '~> 2.3'
|
|
23
|
+
spec.add_runtime_dependency 'em-http-request', '~> 1.1'
|
|
24
|
+
spec.add_runtime_dependency 'activesupport', '>= 3.2', '< 5'
|
|
25
|
+
spec.add_runtime_dependency 'multi_json'
|
|
26
|
+
spec.add_runtime_dependency 'rack'
|
|
27
|
+
|
|
28
|
+
spec.add_development_dependency 'bundler', '~> 1.5'
|
|
29
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Restify::Link do
|
|
4
|
+
describe 'class' do
|
|
5
|
+
describe '#parse' do
|
|
6
|
+
it 'should parse link with quotes' do
|
|
7
|
+
links = described_class
|
|
8
|
+
.parse('<http://example.org/search{?query}>; rel="search"')
|
|
9
|
+
|
|
10
|
+
expect(links).to have(1).item
|
|
11
|
+
expect(links[0].uri.pattern).to eq 'http://example.org/search{?query}'
|
|
12
|
+
expect(links[0].metadata).to eq 'rel' => 'search'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'should parse link without quotes' do
|
|
16
|
+
links = described_class
|
|
17
|
+
.parse('<http://example.org/search{?query}>; rel=search')
|
|
18
|
+
|
|
19
|
+
expect(links).to have(1).item
|
|
20
|
+
expect(links[0].uri.pattern).to eq 'http://example.org/search{?query}'
|
|
21
|
+
expect(links[0].metadata).to eq 'rel' => 'search'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'should parse multiple links' do
|
|
25
|
+
links = described_class
|
|
26
|
+
.parse('<p://h.tld/p>; rel=abc, <p://h.tld/b>; a=b; c="d"')
|
|
27
|
+
|
|
28
|
+
expect(links).to have(2).item
|
|
29
|
+
expect(links[0].uri.pattern).to eq 'p://h.tld/p'
|
|
30
|
+
expect(links[0].metadata).to eq 'rel' => 'abc'
|
|
31
|
+
expect(links[1].uri.pattern).to eq 'p://h.tld/b'
|
|
32
|
+
expect(links[1].metadata).to eq 'a' => 'b', 'c' => 'd'
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'should parse link w/o meta' do
|
|
36
|
+
links = described_class.parse('<p://h.tld/b>')
|
|
37
|
+
|
|
38
|
+
expect(links[0].uri.pattern).to eq 'p://h.tld/b'
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'should parse on invalid URI' do
|
|
42
|
+
links = described_class.parse('<hp://:&*^/fwbhg3>')
|
|
43
|
+
|
|
44
|
+
expect(links[0].uri.pattern).to eq 'hp://:&*^/fwbhg3'
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'should error on invalid header' do
|
|
48
|
+
expect { described_class.parse('</>; rel="s", abc-invalid') }
|
|
49
|
+
.to raise_error ArgumentError, /Invalid token at \d+:/i
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Restify::Resource do
|
|
4
|
+
let(:client) { double 'client' }
|
|
5
|
+
let(:relations) { {} }
|
|
6
|
+
let(:attributes) { {} }
|
|
7
|
+
let(:res) { described_class.new(client, relations, attributes) }
|
|
8
|
+
|
|
9
|
+
describe '#rel?' do
|
|
10
|
+
before do
|
|
11
|
+
res.relations['users'] = true
|
|
12
|
+
res.relations[:projects] = true
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'should match relations' do
|
|
16
|
+
expect(res.rel?(:users)).to eq true
|
|
17
|
+
expect(res.rel?('users')).to eq true
|
|
18
|
+
expect(res.rel?(:projects)).to eq true
|
|
19
|
+
expect(res.rel?('projects')).to eq true
|
|
20
|
+
expect(res.rel?('fuu')).to eq false
|
|
21
|
+
|
|
22
|
+
expect(res).to have_rel :users
|
|
23
|
+
expect(res).to have_rel :projects
|
|
24
|
+
|
|
25
|
+
expect(res.relation?(:users)).to eq true
|
|
26
|
+
expect(res.relation?('users')).to eq true
|
|
27
|
+
expect(res.relation?(:projects)).to eq true
|
|
28
|
+
expect(res.relation?('projects')).to eq true
|
|
29
|
+
expect(res.relation?('fuu')).to eq false
|
|
30
|
+
|
|
31
|
+
expect(res).to have_relation :users
|
|
32
|
+
expect(res).to have_relation :projects
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe '#rel' do
|
|
37
|
+
let(:users) { double 'users rel' }
|
|
38
|
+
let(:projects) { double 'projects rel' }
|
|
39
|
+
before do
|
|
40
|
+
res.relations['users'] = users
|
|
41
|
+
res.relations[:projects] = projects
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'should return relation' do
|
|
45
|
+
expect(res.rel(:users)).to eq users
|
|
46
|
+
expect(res.rel('users')).to eq users
|
|
47
|
+
expect(res.rel(:projects)).to eq projects
|
|
48
|
+
expect(res.rel('projects')).to eq projects
|
|
49
|
+
expect { res.rel(:fuu) }.to raise_error KeyError
|
|
50
|
+
|
|
51
|
+
expect(res.relation(:users)).to eq users
|
|
52
|
+
expect(res.relation('users')).to eq users
|
|
53
|
+
expect(res.relation(:projects)).to eq projects
|
|
54
|
+
expect(res.relation('projects')).to eq projects
|
|
55
|
+
expect { res.relation(:fuu) }.to raise_error KeyError
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
describe '#key?' do
|
|
60
|
+
let(:attributes) { {a: 0, 'b' => 1, 0 => 2} }
|
|
61
|
+
|
|
62
|
+
it 'should test for key inclusion' do
|
|
63
|
+
expect(res.key?(:a)).to eq true
|
|
64
|
+
expect(res.key?(:b)).to eq true
|
|
65
|
+
expect(res.key?('a')).to eq true
|
|
66
|
+
expect(res.key?('b')).to eq true
|
|
67
|
+
expect(res.key?(0)).to eq true
|
|
68
|
+
|
|
69
|
+
expect(res.key?(:c)).to eq false
|
|
70
|
+
expect(res.key?('d')).to eq false
|
|
71
|
+
|
|
72
|
+
expect(res).to have_key :a
|
|
73
|
+
expect(res).to have_key :b
|
|
74
|
+
expect(res).to have_key 'a'
|
|
75
|
+
expect(res).to have_key 'b'
|
|
76
|
+
expect(res).to have_key 0
|
|
77
|
+
|
|
78
|
+
expect(res).to_not have_key :c
|
|
79
|
+
expect(res).to_not have_key 'd'
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
describe '#each' do
|
|
84
|
+
let(:attributes) { {a: 0, b: 1} }
|
|
85
|
+
|
|
86
|
+
it 'should yield' do
|
|
87
|
+
expect { |cb| res.each(&cb) }.to yield_control.twice
|
|
88
|
+
expect { |cb| res.each(&cb) }.to yield_successive_args ['a', 0], ['b', 1]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it 'should return enumerator' do
|
|
92
|
+
expect(res.each).to be_a Enumerator
|
|
93
|
+
expect(res.each.to_a).to eq [['a', 0], ['b', 1]]
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Restify do
|
|
4
|
+
context 'as a dynamic HATEOAS client' do
|
|
5
|
+
before do
|
|
6
|
+
stub_request(:get, 'http://localhost/base').to_return do
|
|
7
|
+
<<-EOF.gsub(/^ {10}/, '')
|
|
8
|
+
HTTP/1.1 200 OK
|
|
9
|
+
Content-Type: application/json
|
|
10
|
+
Transfer-Encoding: chunked
|
|
11
|
+
Link: <http://localhost/base/users{/id}>; rel="users"
|
|
12
|
+
Link: <http://localhost/base/courses{/id}>; rel="courses"
|
|
13
|
+
|
|
14
|
+
{
|
|
15
|
+
"profile_url": "http://localhost/base/profile",
|
|
16
|
+
"search_url": "http://localhost/base/search?q={query}",
|
|
17
|
+
"mirror_url": null
|
|
18
|
+
}
|
|
19
|
+
EOF
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
stub_request(:get, 'http://localhost/base/users').to_return do
|
|
23
|
+
<<-EOF.gsub(/^ {10}/, '')
|
|
24
|
+
HTTP/1.1 200 OK
|
|
25
|
+
Content-Type: application/json
|
|
26
|
+
Transfer-Encoding: chunked
|
|
27
|
+
|
|
28
|
+
[{
|
|
29
|
+
"name": "John Smith",
|
|
30
|
+
"url": "http://localhost/base/users/john.smith",
|
|
31
|
+
"blurb_url": "http://localhost/base/users/john.smith/blurb"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"name": "Jane Smith",
|
|
35
|
+
"self_url": "http://localhost/base/user/jane.smith"
|
|
36
|
+
}]
|
|
37
|
+
EOF
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
stub_request(:post, 'http://localhost/base/users')
|
|
41
|
+
.with(body: {})
|
|
42
|
+
.to_return do
|
|
43
|
+
<<-EOF.gsub(/^ {12}/, '')
|
|
44
|
+
HTTP/1.1 422 Unprocessable Entity
|
|
45
|
+
Content-Type: application/json
|
|
46
|
+
Transfer-Encoding: chunked
|
|
47
|
+
|
|
48
|
+
{"errors":{"name":["can't be blank"]}}
|
|
49
|
+
EOF
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
stub_request(:post, 'http://localhost/base/users')
|
|
53
|
+
.with(body: {name: 'John Smith'})
|
|
54
|
+
.to_return do
|
|
55
|
+
<<-EOF.gsub(/^ {12}/, '')
|
|
56
|
+
HTTP/1.1 201 Created
|
|
57
|
+
Content-Type: application/json
|
|
58
|
+
Location: http://localhost/base/users/john.smith
|
|
59
|
+
Transfer-Encoding: chunked
|
|
60
|
+
|
|
61
|
+
{
|
|
62
|
+
"name": "John Smith",
|
|
63
|
+
"url": "http://localhost/base/users/john.smith",
|
|
64
|
+
"blurb_url": "http://localhost/base/users/john.smith/blurb"
|
|
65
|
+
}
|
|
66
|
+
EOF
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
stub_request(:get, 'http://localhost/base/users/john.smith')
|
|
70
|
+
.to_return do <<-EOF.gsub(/^ {10}/, '')
|
|
71
|
+
HTTP/1.1 200 OK
|
|
72
|
+
Content-Type: application/json
|
|
73
|
+
Link: <http://localhost/base/users/john.smith>; rel="self"
|
|
74
|
+
Transfer-Encoding: chunked
|
|
75
|
+
|
|
76
|
+
{
|
|
77
|
+
"name": "John Smith",
|
|
78
|
+
"url": "http://localhost/base/users/john.smith"
|
|
79
|
+
}
|
|
80
|
+
EOF
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
stub_request(:get, 'http://localhost/base/users/john.smith/blurb')
|
|
84
|
+
.to_return do <<-EOF.gsub(/^ {10}/, '')
|
|
85
|
+
HTTP/1.1 200 OK
|
|
86
|
+
Content-Type: application/json
|
|
87
|
+
Link: <http://localhost/base/users/john.smith>; rel="user"
|
|
88
|
+
Transfer-Encoding: chunked
|
|
89
|
+
|
|
90
|
+
{
|
|
91
|
+
"title": "Prof. Dr. John Smith",
|
|
92
|
+
"image": "http://example.org/avatar.png"
|
|
93
|
+
}
|
|
94
|
+
EOF
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
let(:c) do
|
|
99
|
+
Restify.new('http://localhost/base').value
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
context 'within threads' do
|
|
103
|
+
it 'should consume the API' do
|
|
104
|
+
# Let's get all users
|
|
105
|
+
|
|
106
|
+
# Therefore we need the `users` relations of our root
|
|
107
|
+
# resource.
|
|
108
|
+
users_relation = c.rel(:users)
|
|
109
|
+
|
|
110
|
+
# The relation is a `Restify::Relation` and provides
|
|
111
|
+
# method to enqueue e.g. GET or POST requests with
|
|
112
|
+
# parameters to fill in possible URI template placeholders.
|
|
113
|
+
expect(users_relation).to be_a Restify::Relation
|
|
114
|
+
|
|
115
|
+
# Let's create a user first.
|
|
116
|
+
# This method returns instantly and returns an `Obligation`.
|
|
117
|
+
# This `Obligation` represents the future value.
|
|
118
|
+
# We can pass parameters to a request. They will be used
|
|
119
|
+
# to expand the URI template behind the relation. Additional
|
|
120
|
+
# fields will be encoding in e.g. JSON and send if not a GET
|
|
121
|
+
# request.
|
|
122
|
+
create_user_promise = users_relation.post
|
|
123
|
+
expect(create_user_promise).to be_a Obligation
|
|
124
|
+
|
|
125
|
+
# We can do other things while the request is processed in
|
|
126
|
+
# the background. When we need the response with can call
|
|
127
|
+
# {#value} on the promise that will block the thread until
|
|
128
|
+
# the result is here.
|
|
129
|
+
begin
|
|
130
|
+
create_user_promise.value
|
|
131
|
+
rescue Restify::ClientError => e
|
|
132
|
+
# Because we forgot to send a "name" the server complains
|
|
133
|
+
# with an error code that will lead to a raised error.
|
|
134
|
+
|
|
135
|
+
expect(e.status).to eq :unprocessable_entity
|
|
136
|
+
expect(e.code).to eq 422
|
|
137
|
+
expect(e.errors).to eq 'name' => ["can't be blank"]
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Let's try again.
|
|
141
|
+
created_user = users_relation.post(name: 'John Smith').value
|
|
142
|
+
|
|
143
|
+
# The server returns a 201 Created response with the created
|
|
144
|
+
# resource.
|
|
145
|
+
expect(created_user.status).to eq :created
|
|
146
|
+
expect(created_user.code).to eq 201
|
|
147
|
+
|
|
148
|
+
expect(created_user).to have_key :name
|
|
149
|
+
expect(created_user[:name]).to eq 'John Smith'
|
|
150
|
+
|
|
151
|
+
# Let's follow the "Location" header.
|
|
152
|
+
followed_resource = created_user.follow.value
|
|
153
|
+
|
|
154
|
+
expect(followed_resource.status).to eq :ok
|
|
155
|
+
expect(followed_resource.code).to eq 200
|
|
156
|
+
|
|
157
|
+
expect(followed_resource).to have_key :name
|
|
158
|
+
expect(followed_resource[:name]).to eq 'John Smith'
|
|
159
|
+
|
|
160
|
+
# Now we will fetch a list of all users.
|
|
161
|
+
users = users_relation.get.value
|
|
162
|
+
|
|
163
|
+
# We get a collection back (Restify::Collection).
|
|
164
|
+
expect(users).to have(2).items
|
|
165
|
+
|
|
166
|
+
# Let's get the first one.
|
|
167
|
+
user = users.first
|
|
168
|
+
|
|
169
|
+
# We have all our attributes and relations here as defined in the
|
|
170
|
+
# responses from the server.
|
|
171
|
+
expect(user).to have_key :name
|
|
172
|
+
expect(user[:name]).to eq 'John Smith'
|
|
173
|
+
expect(user).to have_relation :self
|
|
174
|
+
expect(user).to have_relation :blurb
|
|
175
|
+
|
|
176
|
+
# Let's get the blurb.
|
|
177
|
+
blurb = user.rel(:blurb).get.value
|
|
178
|
+
|
|
179
|
+
expect(blurb).to have_key :title
|
|
180
|
+
expect(blurb).to have_key :image
|
|
181
|
+
|
|
182
|
+
expect(blurb[:title]).to eq 'Prof. Dr. John Smith'
|
|
183
|
+
expect(blurb[:image]).to eq 'http://example.org/avatar.png'
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
context 'within eventmachine' do
|
|
188
|
+
it 'should consume the API' do
|
|
189
|
+
pending
|
|
190
|
+
|
|
191
|
+
EventMachine.run do
|
|
192
|
+
users_promise = c.rel(:users).get
|
|
193
|
+
users_promise.then do |users|
|
|
194
|
+
expect(users).to have(2).items
|
|
195
|
+
|
|
196
|
+
user = users.first
|
|
197
|
+
expect(user).to have_key :name
|
|
198
|
+
expect(user[:name]).to eq 'John Smith'
|
|
199
|
+
expect(user).to have_relation :self
|
|
200
|
+
expect(user).to have_relation :blurb
|
|
201
|
+
|
|
202
|
+
user.rel(:blurb).get.then do |blurb|
|
|
203
|
+
expect(blurb).to have_key :title
|
|
204
|
+
expect(blurb).to have_key :image
|
|
205
|
+
|
|
206
|
+
expect(blurb[:title]).to eq 'Prof. Dr. John Smith'
|
|
207
|
+
expect(blurb[:image]).to eq 'http://example.org/avatar.png'
|
|
208
|
+
|
|
209
|
+
EventMachine.stop
|
|
210
|
+
@done = true
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
expect(@done).to be true
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|