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 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