hal-client 3.13.0 → 3.14.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 +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
|