hal-client 3.13.0 → 3.14.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 +2 -2
- data/lib/hal_client/curie_resolver.rb +1 -2
- data/lib/hal_client/link.rb +137 -0
- data/lib/hal_client/links_section.rb +0 -2
- data/lib/hal_client/representation.rb +41 -2
- data/lib/hal_client/version.rb +1 -1
- data/lib/hal_client.rb +1 -0
- data/spec/hal_client/link_spec.rb +276 -0
- data/spec/hal_client/representation_spec.rb +57 -3
- 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: 19eeb7d4e250354bcc747d7c65e66540d9bd2461
|
4
|
+
data.tar.gz: 6623c0304f0bc356a99ce59e88f976e5564d8e43
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a125acaab0e6eb493977b9f916d6b4246ac590bc80f59a3e2867d34666b9b4d8d4722aad7909e6f1d5a42c6f67799c7af2db0df092601c55f3d5d8003374686
|
7
|
+
data.tar.gz: 504e9b1eb3a3b8e74ec0e956ff47b6803aadcf0520d42e268a782b6c3ed24c69dc558ae4ab69d8ca73de4163e3012349195761781a3ea73182b6dd6f5a85e898
|
data/README.md
CHANGED
@@ -9,7 +9,7 @@ An easy to use client interface for REST APIs that use [HAL](http://stateless.co
|
|
9
9
|
Usage
|
10
10
|
-----
|
11
11
|
|
12
|
-
The first step in using a HAL based API is getting a representation of one of its entry
|
12
|
+
The first step in using a HAL based API is getting a representation of one of its entry points. The simplest way to do this is using the `get` class method of `HalClient`.
|
13
13
|
|
14
14
|
blog = HalClient.get("http://blog.me/")
|
15
15
|
# => #<Representation: http://blog.me/>
|
@@ -39,7 +39,7 @@ In the example above `item` is the link rel. The `#related` method extracts embe
|
|
39
39
|
|
40
40
|
#### Request evaluation order
|
41
41
|
|
42
|
-
If the `author` relationship was a regular link (that is, not embedded) in the above example the HTTP GET to retrieve Bob's representation from the server does not happen until the `#property` method is called. This lazy dereferencing allows for working
|
42
|
+
If the `author` relationship was a regular link (that is, not embedded) in the above example the HTTP GET to retrieve Bob's representation from the server does not happen until the `#property` method is called. This lazy dereferencing allows for working more efficiently with larger relationship sets.
|
43
43
|
|
44
44
|
#### CURIEs
|
45
45
|
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'hal_client/representation'
|
2
|
+
|
3
|
+
class HalClient
|
4
|
+
|
5
|
+
# HAL representation of a single link. Provides access to an embedded representation.
|
6
|
+
class Link
|
7
|
+
|
8
|
+
# Create a new Link
|
9
|
+
#
|
10
|
+
# options - name parameters
|
11
|
+
# :rel - This Link's rel property
|
12
|
+
# :target - An instance of Representation
|
13
|
+
# :template - A URI template ( https://www.rfc-editor.org/rfc/rfc6570.txt )
|
14
|
+
# :curie_resolver - An instance of CurieResolver (used to resolve curied rels)
|
15
|
+
def initialize(options)
|
16
|
+
@literal_rel = options[:rel]
|
17
|
+
@target = options[:target]
|
18
|
+
@template = options[:template]
|
19
|
+
@curie_resolver = options[:curie_resolver] || CurieResolver.new([])
|
20
|
+
|
21
|
+
(fail ArgumentError, "A rel must be provided") if @literal_rel.nil?
|
22
|
+
|
23
|
+
if @target.nil? && @template.nil?
|
24
|
+
(fail ArgumentError, "A target or template must be provided")
|
25
|
+
end
|
26
|
+
|
27
|
+
if @target && @template
|
28
|
+
(fail ArgumentError, "Cannot provide both a target and a template")
|
29
|
+
end
|
30
|
+
|
31
|
+
if @target && !@target.kind_of?(Representation)
|
32
|
+
(fail ArgumentError, "Invalid HAL representation: #{target.inspect}")
|
33
|
+
end
|
34
|
+
|
35
|
+
if @template && !@template.kind_of?(Addressable::Template)
|
36
|
+
(fail ArgumentError, "Invalid Addressable::Template: #{template.inspect}")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
attr_accessor :literal_rel, :target, :template, :curie_resolver
|
41
|
+
|
42
|
+
|
43
|
+
# Create a new Link using an entry from the '_links' section of a HAL document
|
44
|
+
#
|
45
|
+
# options - name parameters
|
46
|
+
# :hash_entry - a hash containing keys :rel (string) and :data (hash from a '_links' entry)
|
47
|
+
# :hal_client - an instance of HalClient
|
48
|
+
# :curie_resolver - An instance of CurieResolver (used to resolve curied rels)
|
49
|
+
# :base_url - Base url for resolving relative links in hash_entry (probably the parent
|
50
|
+
# document's "self" link)
|
51
|
+
def self.new_from_link_entry(options)
|
52
|
+
hash_entry = options[:hash_entry]
|
53
|
+
hal_client = options[:hal_client]
|
54
|
+
curie_resolver = options[:curie_resolver]
|
55
|
+
base_url = options[:base_url]
|
56
|
+
|
57
|
+
rel = hash_entry[:rel]
|
58
|
+
hash_data = hash_entry[:data]
|
59
|
+
href = (base_url + hash_data['href']).to_s
|
60
|
+
|
61
|
+
if hash_data['templated']
|
62
|
+
Link.new(rel: rel,
|
63
|
+
template: Addressable::Template.new(href),
|
64
|
+
curie_resolver: curie_resolver)
|
65
|
+
else
|
66
|
+
Link.new(rel: rel,
|
67
|
+
target: Representation.new(hal_client: hal_client, href: href),
|
68
|
+
curie_resolver: curie_resolver)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
# Create a new Link using an entry from the '_embedded' section of a HAL document
|
74
|
+
#
|
75
|
+
# options - name parameters
|
76
|
+
# :hash_entry - a hash containing keys :rel (string) and :data (hash from a '_embedded' entry)
|
77
|
+
# :hal_client - an instance of HalClient
|
78
|
+
# :curie_resolver - An instance of CurieResolver (used to resolve curied rels)
|
79
|
+
# :base_url - Base url for resolving relative links in hash_entry (probably the parent
|
80
|
+
# document's "self" link)
|
81
|
+
def self.new_from_embedded_entry(options)
|
82
|
+
hash_entry = options[:hash_entry]
|
83
|
+
hal_client = options[:hal_client]
|
84
|
+
curie_resolver = options[:curie_resolver]
|
85
|
+
base_url = options[:base_url]
|
86
|
+
|
87
|
+
rel = hash_entry[:rel]
|
88
|
+
hash_data = hash_entry[:data]
|
89
|
+
|
90
|
+
absolute_href = (base_url + hash_data['_links']['self']['href']).to_s
|
91
|
+
hash_data['_links']['self']['href'] = absolute_href
|
92
|
+
|
93
|
+
Link.new(rel: rel,
|
94
|
+
target: Representation.new(hal_client: hal_client, parsed_json: hash_data),
|
95
|
+
curie_resolver: curie_resolver)
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
# Returns the URL of the resource this link references.
|
100
|
+
# In the case of a templated link, this is the unresolved url template pattern.
|
101
|
+
def raw_href
|
102
|
+
templated? ? template.pattern : target.href
|
103
|
+
end
|
104
|
+
|
105
|
+
def fully_qualified_rel
|
106
|
+
curie_resolver.resolve(literal_rel)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns true for a templated link, false for an ordinary (non-templated) link
|
110
|
+
def templated?
|
111
|
+
!template.nil?
|
112
|
+
end
|
113
|
+
|
114
|
+
# Links with the same href, same rel value, and the same 'templated' value are considered equal
|
115
|
+
# Otherwise, they are considered unequal
|
116
|
+
def ==(other)
|
117
|
+
if other.respond_to?(:raw_href) &&
|
118
|
+
other.respond_to?(:fully_qualified_rel) &&
|
119
|
+
other.respond_to?(:templated?)
|
120
|
+
(raw_href == other.raw_href) &&
|
121
|
+
(fully_qualified_rel == other.fully_qualified_rel) &&
|
122
|
+
(templated? == other.templated?)
|
123
|
+
else
|
124
|
+
false
|
125
|
+
end
|
126
|
+
end
|
127
|
+
alias :eql? :==
|
128
|
+
|
129
|
+
|
130
|
+
# Differing Representations or Addressable::Templates with matching hrefs will get matching hash
|
131
|
+
# values, since we are using raw_href and not the objects themselves when computing hash
|
132
|
+
def hash
|
133
|
+
[fully_qualified_rel, raw_href, templated?].hash
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
@@ -177,6 +177,28 @@ class HalClient
|
|
177
177
|
RepresentationSet.new (Array(embedded) + Array(linked))
|
178
178
|
end
|
179
179
|
|
180
|
+
def all_links
|
181
|
+
result = Set.new
|
182
|
+
base_url = Addressable::URI.parse(href || "")
|
183
|
+
|
184
|
+
embedded_entries = flatten_section(raw.fetch("_embedded", {}))
|
185
|
+
result.merge(embedded_entries.map do |entry|
|
186
|
+
Link.new_from_embedded_entry(hash_entry: entry,
|
187
|
+
hal_client: hal_client,
|
188
|
+
curie_resolver: namespaces,
|
189
|
+
base_url: base_url)
|
190
|
+
end)
|
191
|
+
|
192
|
+
link_entries = flatten_section(raw.fetch("_links", {}))
|
193
|
+
result.merge(link_entries.map { |entry|
|
194
|
+
Link.new_from_link_entry(hash_entry: entry,
|
195
|
+
hal_client: hal_client,
|
196
|
+
curie_resolver: namespaces,
|
197
|
+
base_url: base_url) })
|
198
|
+
|
199
|
+
result
|
200
|
+
end
|
201
|
+
|
180
202
|
# Returns urls of resources related via the specified
|
181
203
|
# link rel or the specified default value.
|
182
204
|
#
|
@@ -280,7 +302,16 @@ class HalClient
|
|
280
302
|
def raw
|
281
303
|
if @raw.nil? && @href
|
282
304
|
(fail "unable to make requests due to missing hal client") unless hal_client
|
283
|
-
|
305
|
+
|
306
|
+
response = hal_client.get(@href)
|
307
|
+
|
308
|
+
unless response.is_a?(Representation)
|
309
|
+
error_message = "Response body wasn't a valid HAL document:\n\n"
|
310
|
+
error_message += response.body
|
311
|
+
raise InvalidRepresentationError.new(error_message)
|
312
|
+
end
|
313
|
+
|
314
|
+
@raw ||= response.raw
|
284
315
|
end
|
285
316
|
|
286
317
|
@raw
|
@@ -294,6 +325,14 @@ class HalClient
|
|
294
325
|
|
295
326
|
MISSING = Object.new
|
296
327
|
|
328
|
+
def flatten_section(section_hash)
|
329
|
+
section_hash
|
330
|
+
.each_pair
|
331
|
+
.flat_map { |rel, some_link_info|
|
332
|
+
[some_link_info].flatten
|
333
|
+
.map { |a_link_info| { rel: rel, data: a_link_info } }
|
334
|
+
}
|
335
|
+
end
|
297
336
|
|
298
337
|
def links
|
299
338
|
@links ||= LinksSection.new((raw.fetch("_links"){{}}),
|
@@ -318,7 +357,7 @@ class HalClient
|
|
318
357
|
|
319
358
|
rescue InvalidRepresentationError => err
|
320
359
|
fail InvalidRepresentationError, "/_embedded/#{jpointer_esc(link_rel)} is not a valid representation"
|
321
|
-
end
|
360
|
+
end
|
322
361
|
|
323
362
|
def linked(link_rel, options, &default_proc)
|
324
363
|
default_proc ||= ->(link_rel,_options) {
|
data/lib/hal_client/version.rb
CHANGED
data/lib/hal_client.rb
CHANGED
@@ -7,6 +7,7 @@ class HalClient
|
|
7
7
|
autoload :Representation, 'hal_client/representation'
|
8
8
|
autoload :RepresentationSet, 'hal_client/representation_set'
|
9
9
|
autoload :CurieResolver, 'hal_client/curie_resolver'
|
10
|
+
autoload :Link, 'hal_client/link'
|
10
11
|
autoload :LinksSection, 'hal_client/links_section'
|
11
12
|
autoload :Collection, 'hal_client/collection'
|
12
13
|
autoload :InvalidRepresentationError, 'hal_client/errors'
|
@@ -0,0 +1,276 @@
|
|
1
|
+
require 'hal-client'
|
2
|
+
|
3
|
+
require_relative "../spec_helper"
|
4
|
+
|
5
|
+
require "hal_client/link"
|
6
|
+
|
7
|
+
describe HalClient::Link do
|
8
|
+
|
9
|
+
subject(:link) { described_class.new(rel: rel_1, target: repr_1) }
|
10
|
+
|
11
|
+
describe "#initialize" do
|
12
|
+
it "requires a target" do
|
13
|
+
expect { HalClient::Link.new(rel: rel_1) }.to raise_error(ArgumentError)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "doesn't allow both target and template" do
|
17
|
+
expect {
|
18
|
+
HalClient::Link.new(rel: rel_1, target: repr_1, template: template_1)
|
19
|
+
}.to raise_error(ArgumentError)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "requires target to be a Representation" do
|
23
|
+
expect {
|
24
|
+
HalClient::Link.new(rel: rel_1, target: template_1)
|
25
|
+
}.to raise_error(ArgumentError)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "requires template to be an Addressable::Template" do
|
29
|
+
expect {
|
30
|
+
HalClient::Link.new(rel: rel_1, template: repr_1)
|
31
|
+
}.to raise_error(ArgumentError)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe ".new_from_link_entry" do
|
36
|
+
it "creates an instance of Link" do
|
37
|
+
my_link = described_class.new_from_link_entry(hash_entry: link_entry_hash(href: href_1),
|
38
|
+
hal_client: a_client,
|
39
|
+
curie_resolver: curie_resolver,
|
40
|
+
base_url: href_1)
|
41
|
+
expect(my_link).to be_a(HalClient::Link)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "handles relative hrefs" do
|
45
|
+
input_hash = link_entry_hash(href: relative_href_1)
|
46
|
+
base_url = Addressable::URI.parse(href_1)
|
47
|
+
|
48
|
+
my_link = described_class.new_from_link_entry(hash_entry: input_hash,
|
49
|
+
hal_client: a_client,
|
50
|
+
curie_resolver: curie_resolver,
|
51
|
+
base_url: base_url)
|
52
|
+
expect(my_link.raw_href).to eq((base_url + relative_href_1).to_s)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe ".new_from_embedded_entry" do
|
57
|
+
it "creates an instance of Link" do
|
58
|
+
my_link = described_class.new_from_embedded_entry(hash_entry: embedded_entry_hash,
|
59
|
+
hal_client: a_client,
|
60
|
+
curie_resolver: curie_resolver,
|
61
|
+
base_url: href_1)
|
62
|
+
expect(my_link).to be_a(HalClient::Link)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "handles relative hrefs" do
|
66
|
+
input_hash = embedded_entry_hash(href: relative_href_1)
|
67
|
+
base_url = Addressable::URI.parse(href_1)
|
68
|
+
|
69
|
+
my_link = described_class.new_from_embedded_entry(hash_entry: input_hash,
|
70
|
+
hal_client: a_client,
|
71
|
+
curie_resolver: curie_resolver,
|
72
|
+
base_url: base_url)
|
73
|
+
expect(my_link.raw_href).to eq((base_url + relative_href_1).to_s)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#href" do
|
78
|
+
specify { expect(link.raw_href).to eq('http://example.com/href_1') }
|
79
|
+
specify { expect(templated_link1.raw_href).to eq('http://example.com/people{?name}') }
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "#templated?" do
|
83
|
+
specify { expect(link.templated?).to be false }
|
84
|
+
specify { expect(templated_link1.templated?).to be true }
|
85
|
+
end
|
86
|
+
|
87
|
+
context "equality and hash" do
|
88
|
+
let(:link_same_target_same_rel) { HalClient::Link.new(target: repr_1, rel: rel_1) }
|
89
|
+
|
90
|
+
let(:link_same_target_diff_rel) { HalClient::Link.new(target: repr_1, rel: rel_2) }
|
91
|
+
let(:link_diff_target_same_rel) { HalClient::Link.new(target: repr_2, rel: rel_1) }
|
92
|
+
let(:link_diff_target_diff_rel) { HalClient::Link.new(target: repr_2, rel: rel_2) }
|
93
|
+
|
94
|
+
let(:link_same_non_fetched) { HalClient::Link.new(target: href_only_repr, rel: rel_1) }
|
95
|
+
|
96
|
+
let(:same_as_templated_link1) do
|
97
|
+
HalClient::Link.new(rel: 'templated_link',
|
98
|
+
template: Addressable::Template.new('http://example.com/people{?name}'))
|
99
|
+
end
|
100
|
+
|
101
|
+
let(:templated_link2) do
|
102
|
+
HalClient::Link.new(rel: 'templated_link',
|
103
|
+
template: Addressable::Template.new('http://example.com/places{?name}'))
|
104
|
+
end
|
105
|
+
|
106
|
+
let(:template_but_not_a_template) do
|
107
|
+
HalClient::Link.new(rel: 'rel_1',
|
108
|
+
template: Addressable::Template.new('http://example.com/href_1'))
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "#==" do
|
112
|
+
specify { expect(link == link_same_target_same_rel).to eq true }
|
113
|
+
specify { expect(link == link_same_non_fetched).to eq true }
|
114
|
+
|
115
|
+
specify { expect(link == link_same_target_diff_rel).to eq false }
|
116
|
+
specify { expect(link == link_diff_target_same_rel).to eq false }
|
117
|
+
specify { expect(link == link_diff_target_diff_rel).to eq false }
|
118
|
+
|
119
|
+
specify { expect(templated_link1 == same_as_templated_link1).to eq true }
|
120
|
+
specify { expect(templated_link1 == templated_link2).to eq false }
|
121
|
+
|
122
|
+
specify { expect(link == template_but_not_a_template).to eq false }
|
123
|
+
|
124
|
+
specify { expect(full_uri_link_1 == curied_link_1).to eq true }
|
125
|
+
|
126
|
+
specify { expect(link == Object.new).to eq false }
|
127
|
+
end
|
128
|
+
|
129
|
+
describe ".eql?" do
|
130
|
+
specify { expect(link.eql? link_same_target_same_rel).to eq true }
|
131
|
+
specify { expect(link.eql? link_same_non_fetched).to eq true }
|
132
|
+
|
133
|
+
specify { expect(link.eql? link_same_target_diff_rel).to eq false }
|
134
|
+
specify { expect(link.eql? link_diff_target_same_rel).to eq false }
|
135
|
+
specify { expect(link.eql? link_diff_target_diff_rel).to eq false }
|
136
|
+
|
137
|
+
specify { expect(templated_link1.eql? same_as_templated_link1).to eq true }
|
138
|
+
specify { expect(templated_link1.eql? templated_link2).to eq false }
|
139
|
+
|
140
|
+
specify { expect(link.eql? template_but_not_a_template).to eq false }
|
141
|
+
|
142
|
+
specify { expect(full_uri_link_1.eql? curied_link_1).to eq true }
|
143
|
+
|
144
|
+
specify { expect(link.eql? Object.new).to eq false }
|
145
|
+
end
|
146
|
+
|
147
|
+
describe "hash" do
|
148
|
+
specify{ expect(link.hash).to eq link_same_target_same_rel.hash }
|
149
|
+
specify{ expect(link.hash).to eq link_same_non_fetched.hash }
|
150
|
+
|
151
|
+
specify{ expect(link.hash).to_not eq link_same_target_diff_rel.hash }
|
152
|
+
specify{ expect(link.hash).to_not eq link_diff_target_same_rel.hash }
|
153
|
+
specify{ expect(link.hash).to_not eq link_diff_target_diff_rel.hash }
|
154
|
+
|
155
|
+
specify { expect(templated_link1.hash).to eq(same_as_templated_link1.hash) }
|
156
|
+
specify { expect(templated_link1.hash).to_not eq(templated_link2.hash) }
|
157
|
+
|
158
|
+
specify { expect(link.hash).to_not eq(template_but_not_a_template.hash)}
|
159
|
+
|
160
|
+
specify { expect(full_uri_link_1.hash).to eq(curied_link_1.hash) }
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
# Background
|
167
|
+
|
168
|
+
let(:a_client) { HalClient.new }
|
169
|
+
|
170
|
+
let(:rel_1) { 'rel_1' }
|
171
|
+
let(:rel_2) { 'rel_2' }
|
172
|
+
|
173
|
+
let(:full_uri_rel_1) { 'http://example.com/rels/rel_1' }
|
174
|
+
let(:full_uri_link_1) { HalClient::Link.new(rel: full_uri_rel_1, target: repr_1) }
|
175
|
+
|
176
|
+
let(:curie_resolver) do
|
177
|
+
HalClient::CurieResolver.new({
|
178
|
+
'name' => 'ex',
|
179
|
+
'href' => 'http://example.com/rels/{rel}',
|
180
|
+
'templated' => true
|
181
|
+
})
|
182
|
+
end
|
183
|
+
|
184
|
+
let(:curied_rel_1) { 'ex:rel_1' }
|
185
|
+
|
186
|
+
let(:curied_link_1) do
|
187
|
+
HalClient::Link.new(rel: curied_rel_1, target: repr_1, curie_resolver: curie_resolver)
|
188
|
+
end
|
189
|
+
|
190
|
+
let(:href_1) { 'http://example.com/href_1' }
|
191
|
+
let(:href_2) { 'http://example.com/href_2' }
|
192
|
+
|
193
|
+
let(:relative_href_1) { 'path/to/href_1' }
|
194
|
+
|
195
|
+
def link_entry_hash(options = {})
|
196
|
+
href = options[:href] || href_1
|
197
|
+
rel = options[:rel] || rel_1
|
198
|
+
templated = options[:templated] || nil
|
199
|
+
|
200
|
+
hash_data = { 'href' => href }
|
201
|
+
hash_data['templated'] = templated if templated
|
202
|
+
|
203
|
+
{
|
204
|
+
rel: rel,
|
205
|
+
data: hash_data
|
206
|
+
}
|
207
|
+
end
|
208
|
+
|
209
|
+
def embedded_entry_hash(options = {})
|
210
|
+
href = options[:href] || href_1
|
211
|
+
rel = options[:rel] || rel_1
|
212
|
+
|
213
|
+
{
|
214
|
+
rel: rel,
|
215
|
+
data: { '_links' => { 'self' => { 'href' => href } } }
|
216
|
+
}
|
217
|
+
end
|
218
|
+
|
219
|
+
def raw_repr(options = {})
|
220
|
+
href = options[:href] || href_1
|
221
|
+
|
222
|
+
<<-HAL
|
223
|
+
{
|
224
|
+
"prop1": 1
|
225
|
+
,"prop2": 2
|
226
|
+
,"_links": {
|
227
|
+
"self": { "href": "#{href}" }
|
228
|
+
,"link1": { "href": "http://example.com/bar" }
|
229
|
+
,"templated_link": { "href": "http://example.com/people{?name}"
|
230
|
+
,"templated": true }
|
231
|
+
,"link3": [{ "href": "http://example.com/link3-a" }
|
232
|
+
,{ "href": "http://example.com/link3-b" }]
|
233
|
+
}
|
234
|
+
,"_embedded": {
|
235
|
+
"embed1": {
|
236
|
+
"_links": { "self": { "href": "http://example.com/baz" }}
|
237
|
+
}
|
238
|
+
}
|
239
|
+
}
|
240
|
+
HAL
|
241
|
+
end
|
242
|
+
|
243
|
+
def href_only_raw_repr(options = {})
|
244
|
+
href = options[:href] || href_1
|
245
|
+
|
246
|
+
<<-HAL
|
247
|
+
{
|
248
|
+
"_links": {
|
249
|
+
"self": { "href": "#{href}" }
|
250
|
+
}
|
251
|
+
}
|
252
|
+
HAL
|
253
|
+
end
|
254
|
+
|
255
|
+
def href_only_repr(options = {})
|
256
|
+
href = options[:href] || href_1
|
257
|
+
|
258
|
+
HalClient::Representation.new(hal_client: a_client,
|
259
|
+
parsed_json: MultiJson.load(href_only_raw_repr(href: href)))
|
260
|
+
end
|
261
|
+
|
262
|
+
let(:repr_1) do
|
263
|
+
HalClient::Representation.new(hal_client: a_client,
|
264
|
+
parsed_json: MultiJson.load(raw_repr))
|
265
|
+
end
|
266
|
+
|
267
|
+
let(:repr_2) do
|
268
|
+
HalClient::Representation.new(hal_client: a_client,
|
269
|
+
parsed_json: MultiJson.load(raw_repr(href: href_2)))
|
270
|
+
end
|
271
|
+
|
272
|
+
let(:template_1) { Addressable::Template.new('http://example.com/people{?name}') }
|
273
|
+
|
274
|
+
let(:templated_link1) { HalClient::Link.new(rel: 'templated_link', template: template_1) }
|
275
|
+
|
276
|
+
end
|
@@ -9,15 +9,20 @@ describe HalClient::Representation do
|
|
9
9
|
,"_links": {
|
10
10
|
"self": { "href": "http://example.com/foo" }
|
11
11
|
,"link1": { "href": "http://example.com/bar" }
|
12
|
-
,"
|
12
|
+
,"templated": { "href": "http://example.com/people{?name}"
|
13
13
|
,"templated": true }
|
14
14
|
,"link3": [{ "href": "http://example.com/link3-a" }
|
15
15
|
,{ "href": "http://example.com/link3-b" }]
|
16
|
+
,"dup": { "href": "http://example.com/dup" }
|
16
17
|
}
|
17
18
|
,"_embedded": {
|
18
19
|
"embed1": {
|
19
20
|
"_links": { "self": { "href": "http://example.com/baz" }}
|
20
21
|
}
|
22
|
+
,"dup": {
|
23
|
+
"dupProperty": "foo"
|
24
|
+
,"_links": { "self": { "href": "http://example.com/dup" }}
|
25
|
+
}
|
21
26
|
}
|
22
27
|
}
|
23
28
|
HAL
|
@@ -246,7 +251,7 @@ HAL
|
|
246
251
|
end
|
247
252
|
|
248
253
|
context "for existent templated link" do
|
249
|
-
subject { repr.related "
|
254
|
+
subject { repr.related "templated", name: "bob" }
|
250
255
|
it { should have(1).item }
|
251
256
|
it { should include_representation_of "http://example.com/people?name=bob" }
|
252
257
|
end
|
@@ -264,13 +269,27 @@ HAL
|
|
264
269
|
end
|
265
270
|
end
|
266
271
|
|
272
|
+
describe "#all_links" do
|
273
|
+
subject { repr.all_links }
|
274
|
+
|
275
|
+
specify { expect(subject).to include(link1_link) }
|
276
|
+
specify { expect(subject).to_not include(link2_link) }
|
277
|
+
|
278
|
+
specify { expect(subject).to include(templated_link) }
|
279
|
+
|
280
|
+
specify { expect(subject).to include(link3a_link) }
|
281
|
+
specify { expect(subject).to include(link3b_link) }
|
282
|
+
|
283
|
+
specify { expect(subject.any? { |item| item.target['dupProperty'] == 'foo' }).to be true }
|
284
|
+
end
|
285
|
+
|
267
286
|
specify { expect(repr.related_hrefs "link1")
|
268
287
|
.to contain_exactly "http://example.com/bar" }
|
269
288
|
specify { expect(repr.related_hrefs "embed1")
|
270
289
|
.to contain_exactly "http://example.com/baz" }
|
271
290
|
specify { expect { repr.related_hrefs 'wat' }.to raise_exception KeyError }
|
272
291
|
|
273
|
-
specify { expect(repr.raw_related_hrefs("
|
292
|
+
specify { expect(repr.raw_related_hrefs("templated").map(&:pattern))
|
274
293
|
.to contain_exactly "http://example.com/people{?name}" }
|
275
294
|
specify { expect(repr.raw_related_hrefs("link1"))
|
276
295
|
.to contain_exactly "http://example.com/bar" }
|
@@ -396,6 +415,41 @@ HAL
|
|
396
415
|
|
397
416
|
# Background
|
398
417
|
|
418
|
+
let(:link1_repr) do
|
419
|
+
HalClient::Representation.new(hal_client: a_client, href: "http://example.com/bar")
|
420
|
+
end
|
421
|
+
|
422
|
+
let(:link3a_repr) do
|
423
|
+
HalClient::Representation.new(hal_client: a_client, href: "http://example.com/link3-a")
|
424
|
+
end
|
425
|
+
|
426
|
+
let(:link3b_repr) do
|
427
|
+
HalClient::Representation.new(hal_client: a_client, href: "http://example.com/link3-b")
|
428
|
+
end
|
429
|
+
|
430
|
+
|
431
|
+
let(:link1_link) do
|
432
|
+
HalClient::Link.new(rel: 'link1', target: link1_repr)
|
433
|
+
end
|
434
|
+
|
435
|
+
let(:link2_link) do
|
436
|
+
HalClient::Link.new(rel: 'link2', target: link1_repr)
|
437
|
+
end
|
438
|
+
|
439
|
+
let(:templated_link) do
|
440
|
+
HalClient::Link.new(rel: 'templated',
|
441
|
+
template: Addressable::Template.new('http://example.com/people{?name}'))
|
442
|
+
end
|
443
|
+
|
444
|
+
let(:link3a_link) do
|
445
|
+
HalClient::Link.new(rel: 'link3', target: link3a_repr)
|
446
|
+
end
|
447
|
+
|
448
|
+
let(:link3b_link) do
|
449
|
+
HalClient::Link.new(rel: 'link3', target: link3b_repr)
|
450
|
+
end
|
451
|
+
|
452
|
+
|
399
453
|
let(:a_client) { HalClient.new }
|
400
454
|
let!(:bar_request) { stub_identity_request("http://example.com/bar") }
|
401
455
|
let!(:baz_request) { stub_identity_request "http://example.com/baz" }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hal-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-01-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: http
|
@@ -147,6 +147,7 @@ files:
|
|
147
147
|
- lib/hal_client/collection.rb
|
148
148
|
- lib/hal_client/curie_resolver.rb
|
149
149
|
- lib/hal_client/errors.rb
|
150
|
+
- lib/hal_client/link.rb
|
150
151
|
- lib/hal_client/links_section.rb
|
151
152
|
- lib/hal_client/representation.rb
|
152
153
|
- lib/hal_client/representation_editor.rb
|
@@ -154,6 +155,7 @@ files:
|
|
154
155
|
- lib/hal_client/version.rb
|
155
156
|
- spec/hal_client/collection_spec.rb
|
156
157
|
- spec/hal_client/curie_resolver_spec.rb
|
158
|
+
- spec/hal_client/link_spec.rb
|
157
159
|
- spec/hal_client/links_section_spec.rb
|
158
160
|
- spec/hal_client/representation_editor_spec.rb
|
159
161
|
- spec/hal_client/representation_set_spec.rb
|
@@ -188,6 +190,7 @@ summary: Use HAL APIs easily
|
|
188
190
|
test_files:
|
189
191
|
- spec/hal_client/collection_spec.rb
|
190
192
|
- spec/hal_client/curie_resolver_spec.rb
|
193
|
+
- spec/hal_client/link_spec.rb
|
191
194
|
- spec/hal_client/links_section_spec.rb
|
192
195
|
- spec/hal_client/representation_editor_spec.rb
|
193
196
|
- spec/hal_client/representation_set_spec.rb
|