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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 553aacea1a5e60830f4444b701d714433dcb7fbc
4
- data.tar.gz: dacb874f8b57a68e908028dee737b7d48cae4ce0
3
+ metadata.gz: 19eeb7d4e250354bcc747d7c65e66540d9bd2461
4
+ data.tar.gz: 6623c0304f0bc356a99ce59e88f976e5564d8e43
5
5
  SHA512:
6
- metadata.gz: e58ada075076a0ebd8ea3aa8366ed4980d972d6813be97294c6eafde889ae03e5bfab120fe8ca797b1590f6fee8c27c670c7f141f8e444f88b080da2039443d8
7
- data.tar.gz: d746794430e8b5bc974b8917dc33c4ce0fe5afe08d0fdf4c51978cb1cb2bd8a76f949c0eeb0d15aae39bf03ca0e1266c9ce42f590f9d4a2fa5ba141212e521a4
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 point. The simplest way to do this is using the `get` class method of `HalClient`.
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 with efficiently with larger relationship sets.
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
 
@@ -2,8 +2,7 @@ require 'addressable/template'
2
2
 
3
3
  class HalClient
4
4
 
5
- # Expands CURIEs to fully qualified URLs using a set curie
6
- # definitions.
5
+ # Expands CURIEs to fully qualified URLs using a set of curie definitions.
7
6
  class CurieResolver
8
7
 
9
8
  # Initialize new CurieResolver
@@ -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
@@ -2,8 +2,6 @@ class HalClient
2
2
 
3
3
  # Encapsulates a "_links" section.
4
4
  class LinksSection
5
- UNSET = Object.new
6
-
7
5
  # section - json hash for the links section
8
6
  # base_url - base URL with which to resolve relative URLs
9
7
  def initialize(section, opts={} )
@@ -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
- @raw ||= hal_client.get(@href).raw
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) {
@@ -1,3 +1,3 @@
1
1
  class HalClient
2
- VERSION = "3.13.0"
2
+ VERSION = "3.14.0"
3
3
  end
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
- ,"templated_link": { "href": "http://example.com/people{?name}"
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 "templated_link", name: "bob" }
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("templated_link").map(&:pattern))
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.13.0
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: 2015-12-23 00:00:00.000000000 Z
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