representors 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +18 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +126 -0
  5. data/LICENSE.md +19 -0
  6. data/README.md +28 -0
  7. data/Rakefile +10 -0
  8. data/lib/representor_support/utilities.rb +39 -0
  9. data/lib/representors.rb +5 -0
  10. data/lib/representors/errors.rb +7 -0
  11. data/lib/representors/field.rb +108 -0
  12. data/lib/representors/options.rb +67 -0
  13. data/lib/representors/representor.rb +161 -0
  14. data/lib/representors/representor_builder.rb +64 -0
  15. data/lib/representors/representor_hash.rb +59 -0
  16. data/lib/representors/serialization.rb +4 -0
  17. data/lib/representors/serialization/deserializer_base.rb +29 -0
  18. data/lib/representors/serialization/deserializer_factory.rb +13 -0
  19. data/lib/representors/serialization/hal_deserializer.rb +44 -0
  20. data/lib/representors/serialization/hal_serializer.rb +91 -0
  21. data/lib/representors/serialization/hale_deserializer.rb +162 -0
  22. data/lib/representors/serialization/hale_serializer.rb +110 -0
  23. data/lib/representors/serialization/serialization_base.rb +27 -0
  24. data/lib/representors/serialization/serialization_factory_base.rb +54 -0
  25. data/lib/representors/serialization/serializer_base.rb +20 -0
  26. data/lib/representors/serialization/serializer_factory.rb +17 -0
  27. data/lib/representors/transition.rb +130 -0
  28. data/lib/representors/version.rb +4 -0
  29. data/spec/fixtures/complex_hal.json +92 -0
  30. data/spec/fixtures/complex_hale_document.json +81 -0
  31. data/spec/fixtures/drds_hash.rb +120 -0
  32. data/spec/fixtures/hale_spec_examples/basic.json +77 -0
  33. data/spec/fixtures/hale_spec_examples/complex_reference_objects.json +157 -0
  34. data/spec/fixtures/hale_spec_examples/data.json +17 -0
  35. data/spec/fixtures/hale_spec_examples/data_objects.json +96 -0
  36. data/spec/fixtures/hale_spec_examples/link_objects.json +18 -0
  37. data/spec/fixtures/hale_spec_examples/nested_ref.json +43 -0
  38. data/spec/fixtures/hale_spec_examples/reference_objects.json +89 -0
  39. data/spec/fixtures/hale_tutorial_examples/basic_links.json +85 -0
  40. data/spec/fixtures/hale_tutorial_examples/basic_links_with_orders.json +96 -0
  41. data/spec/fixtures/hale_tutorial_examples/basic_links_with_references.json +108 -0
  42. data/spec/fixtures/hale_tutorial_examples/embedded.json +182 -0
  43. data/spec/fixtures/hale_tutorial_examples/empty.json +1 -0
  44. data/spec/fixtures/hale_tutorial_examples/enctype.json +14 -0
  45. data/spec/fixtures/hale_tutorial_examples/final.json +141 -0
  46. data/spec/fixtures/hale_tutorial_examples/get_link.json +17 -0
  47. data/spec/fixtures/hale_tutorial_examples/get_link_with_data.json +29 -0
  48. data/spec/fixtures/hale_tutorial_examples/links.json +11 -0
  49. data/spec/fixtures/hale_tutorial_examples/links_only.json +3 -0
  50. data/spec/fixtures/hale_tutorial_examples/meta.json +208 -0
  51. data/spec/fixtures/hale_tutorial_examples/self_link.json +7 -0
  52. data/spec/fixtures/single_drd.rb +266 -0
  53. data/spec/lib/representors/complex_representor_spec.rb +288 -0
  54. data/spec/lib/representors/field_spec.rb +141 -0
  55. data/spec/lib/representors/representor_builder_spec.rb +223 -0
  56. data/spec/lib/representors/representor_spec.rb +285 -0
  57. data/spec/lib/representors/serialization/deserializer_factory_spec.rb +118 -0
  58. data/spec/lib/representors/serialization/hal_deserializer_spec.rb +34 -0
  59. data/spec/lib/representors/serialization/hal_serializer_spec.rb +171 -0
  60. data/spec/lib/representors/serialization/hale_deserializer_spec.rb +59 -0
  61. data/spec/lib/representors/serialization/hale_roundtrip_spec.rb +34 -0
  62. data/spec/lib/representors/serialization/hale_serializer_spec.rb +659 -0
  63. data/spec/lib/representors/serialization/serializer_factory_spec.rb +108 -0
  64. data/spec/lib/representors/transition_spec.rb +349 -0
  65. data/spec/spec_helper.rb +32 -0
  66. data/spec/support/basic-hale.json +12 -0
  67. data/spec/support/hal_representor_shared.rb +206 -0
  68. data/spec/support/helpers.rb +8 -0
  69. data/tasks/benchmark.rake +75 -0
  70. data/tasks/complex_hal_document.json +98 -0
  71. data/tasks/test_specs.rake +37 -0
  72. data/tasks/yard.rake +22 -0
  73. metadata +232 -0
@@ -0,0 +1,285 @@
1
+ require 'spec_helper'
2
+ require 'yaml'
3
+ require 'uri'
4
+
5
+ module Representors
6
+ describe Representor do
7
+ let(:base_doc) {'A list of DRDs.'}
8
+ let(:rel_first_transition) {'self'}
9
+ let(:rel_second_transition) {'search'}
10
+ before do
11
+ @base_representor = {
12
+ protocol: 'http',
13
+ href: 'www.example.com/drds',
14
+ id: 'drds',
15
+ doc: base_doc,
16
+ attributes: {},
17
+ embedded: {},
18
+ links: {},
19
+ transitions: []
20
+ }
21
+
22
+ @semantic_elements = {
23
+ attributes: {
24
+ total_count: {
25
+ doc: 'The total count of DRDs.',
26
+ type: 'semantic',
27
+ profile: 'http://alps.io/schema.org/Integer',
28
+ sample: 1,
29
+ value: 2
30
+ },
31
+ uptime: {
32
+ value: '76ms'
33
+ },
34
+ brackreference: {
35
+ value: 886396728
36
+ }
37
+ }
38
+ }
39
+
40
+ @transition_elements = {
41
+ transitions: [
42
+ {
43
+ doc: 'Returns a list of DRDs.',
44
+ rt: 'drds',
45
+ type: 'safe',
46
+ href: 'some.example.com/list',
47
+ rel: rel_first_transition
48
+ },
49
+ {
50
+ doc: 'Returns a list of DRDs that satisfy the search term.',
51
+ rt: 'drds',
52
+ type: 'safe',
53
+ href: '/',
54
+ rel: rel_second_transition,
55
+ descriptors: {
56
+ name: {
57
+ doc: "Name to search",
58
+ profile: "http://alps.io/schema.org/Text",
59
+ sample: "drdname",
60
+ options: {'list' => ['one', 'two']}
61
+ },
62
+ status: {
63
+ doc: "How is the DRD.",
64
+ profile: "http://alps.io/schema.org/Text",
65
+ sample: "renegade",
66
+ options: {'list' => ['renegade', 'compliant'], 'id' => 'status_list'}
67
+ }
68
+ }
69
+ }
70
+ ]
71
+ }
72
+ end
73
+ let(:representor_hash) { @representor_hash || @base_representor }
74
+ let(:subject) { Representor.new(representor_hash) }
75
+
76
+ describe '.new' do
77
+ it 'returns a Representors::Representor instance' do
78
+ expect(subject).to be_an_instance_of(Representor)
79
+ end
80
+
81
+ it 'yields a builder' do
82
+ Representor.new do |builder|
83
+ expect(builder).to be_an_instance_of(RepresentorBuilder)
84
+ builder
85
+ end
86
+ end
87
+
88
+ it 'returns a Representors::Representor instance with a nil argument' do
89
+ expect(Representor.new).to be_an_instance_of(Representor)
90
+ end
91
+
92
+ describe '#to_s' do
93
+ it 'retuns a string representation' do
94
+ expect(eval(subject.to_s)).to eq(representor_hash)
95
+ end
96
+ end
97
+
98
+ describe '#doc' do
99
+ context 'the hash has a doc' do
100
+ let(:doc) {base_doc}
101
+ it 'returns the same value specified under the doc element of the hash' do
102
+ @representor_hash = RepresentorHash.new
103
+ @representor_hash.doc = doc
104
+ expect(subject.doc).to eq(doc)
105
+ end
106
+ end
107
+ context 'the hash has non doc' do
108
+ it 'returns an empty string' do
109
+ @representor_hash = RepresentorHash.new
110
+ expect(subject.doc).to eq('')
111
+ end
112
+ end
113
+ end
114
+
115
+ describe '#identifier' do
116
+ context 'href and protocol are specified' do
117
+ let(:href) { 'www.example.com/drds'}
118
+ let(:protocol) {'http'}
119
+ it 'returns an url' do
120
+ @representor_hash = RepresentorHash.new(protocol: protocol, href: href)
121
+ expect(subject.identifier).to match(URI::regexp)
122
+ end
123
+ end
124
+
125
+ context 'href is not specified and protocol is specified' do
126
+ let(:protocol) {'http'}
127
+ it 'returns an url with object id' do
128
+ @representor_hash = RepresentorHash.new(protocol: protocol)
129
+ expect(subject.identifier).to eq('http://%s' % subject.object_id)
130
+ end
131
+ end
132
+
133
+ context 'href is specified and protocol is not specified' do
134
+ let(:href) { 'www.example.com/drds'}
135
+ it 'returns an url with default protocol' do
136
+ @representor_hash = RepresentorHash.new(href: href)
137
+ expect(subject.identifier).to eq('http://%s' % href)
138
+ end
139
+ end
140
+
141
+ context 'href is not specified and protocol is not specified' do
142
+ let(:href) { 'www.example.com/drds'}
143
+ it 'returns an unknown protocol object url' do
144
+ @representor_hash = RepresentorHash.new
145
+ expect(subject.identifier).to eq("ruby_id://%s" % subject.object_id)
146
+ end
147
+ end
148
+
149
+ end
150
+
151
+ describe '#to_hash' do
152
+ it 'returns a hash that it can be reconstructed with' do
153
+ expect(Representor.new(subject.to_hash).to_hash).to eq(@base_representor)
154
+ end
155
+ end
156
+
157
+ describe '#to_yaml' do
158
+ it 'returns a yaml file that represents its internal hash' do
159
+ expect(YAML.load(subject.to_yaml)).to eq(@base_representor)
160
+ end
161
+ end
162
+
163
+ describe '#properties' do
164
+ it 'returns a hash of attributes associated with the represented resource' do
165
+ @representor_hash = @base_representor.merge(@semantic_elements)
166
+ semantic_elements_present = %w(total_count uptime brackreference).all? do |key|
167
+ subject.properties[key.to_sym] == @semantic_elements[:attributes][key.to_sym][:value]
168
+ end
169
+ expect(semantic_elements_present).to eq(true)
170
+ end
171
+ end
172
+
173
+ describe '#embedded' do
174
+ let(:embedded_resource) {'embedded_resource'}
175
+ let(:profile_link) { {profile: "http://alps.io/schema.org/Thing"} }
176
+
177
+ before do
178
+ @count = 3
179
+ @representor_hash = RepresentorHash.new(@base_representor).merge(@semantic_elements)
180
+ embedded_resources = []
181
+
182
+ @transitions_hash = {
183
+ transitions: [
184
+ { doc: 'Returns a list of DRDs',
185
+ type: 'safe',
186
+ rel: 'self'
187
+ }
188
+ ]
189
+ }
190
+
191
+ @count.times do |i|
192
+ transitions_hash = deep_dup(@transitions_hash)
193
+ transitions_hash[:transitions][0][:href] = "some.example.com/list/#{i}"
194
+ embedded_item = deep_dup(@representor_hash).merge(transitions_hash)
195
+ embedded_item[:links] = profile_link if i == 0
196
+ embedded_resources << embedded_item
197
+ end
198
+
199
+
200
+ @representor_hash[:embedded] = { embedded_resource => embedded_resources }
201
+ end
202
+
203
+ it 'returns a set of Representor objects' do
204
+ expect(subject.embedded[embedded_resource].first).to be_an_instance_of(Representors::Representor)
205
+ end
206
+
207
+ it 'returns a Representor objects that has its data' do
208
+ embedded_objects_valid = subject.embedded[embedded_resource].all? { |embed| embed.doc == base_doc }
209
+ expect(embedded_objects_valid).to eq(true)
210
+ end
211
+
212
+ it 'returns the all the Representors' do
213
+ expect(subject.embedded[embedded_resource].count).to eq(@count)
214
+ end
215
+
216
+ it 'doesn\'t blow up even if nothing is embedded' do
217
+ @representor_hash = @base_representor
218
+ expect(subject.embedded.count).to eq(0)
219
+ end
220
+
221
+ it 'includes appropriate profile links if it exists' do
222
+ expect(subject.transitions.first[:profile]).to eq(profile_link["profile"])
223
+ expect(subject.transitions[1][:profile]).to be_nil
224
+ expect(subject.transitions.last[:profile]).to be_nil
225
+ end
226
+ end
227
+
228
+ describe '#transitions' do
229
+ it 'returns all transitions' do
230
+ @representor_hash = @base_representor.merge(@transition_elements)
231
+ expect(subject.transitions.size).to eq(2)
232
+ has_transitions = subject.transitions.all? { |trans| trans.instance_of?(Transition) }
233
+ expect(has_transitions).to eq(true)
234
+ expect(subject.transitions[0].rel).to eq(rel_first_transition)
235
+ expect(subject.transitions[1].rel).to eq(rel_second_transition)
236
+ end
237
+ context 'with transitions which have the same rel and href' do
238
+ it 'returns only one transition' do
239
+ transitions = { transitions: [
240
+ {
241
+ doc: 'Returns a list of DRDs.',
242
+ rt: 'drds',
243
+ type: 'safe',
244
+ href: 'some.example.com/list',
245
+ rel: rel_first_transition
246
+ },
247
+ {
248
+ doc: 'Returns a list of trusmis.',
249
+ rt: 'trusmis',
250
+ type: 'unsafe',
251
+ href: 'some.example.com/list',
252
+ rel: rel_first_transition
253
+ }
254
+ ]
255
+ }
256
+ @representor_hash = @base_representor.merge(transitions)
257
+ expect(subject.transitions.size).to eq(1)
258
+ has_transitions = subject.transitions.all? { |trans| trans.instance_of?(Transition) }
259
+ expect(has_transitions).to eq(true)
260
+ expect(subject.transitions[0].rel).to eq(rel_first_transition)
261
+ end
262
+ end
263
+ end
264
+
265
+ describe '#meta_links' do
266
+ it 'should return a list of transitions representing those links' do
267
+ @base_representor[:links] = {
268
+ self: 'DRDs#drds/create',
269
+ help: 'Forms/create'
270
+ }
271
+ expect(subject.meta_links.size).to eq(2)
272
+ has_meta_link = subject.meta_links.all? { |trans| trans.instance_of?(Transition) }
273
+ expect(has_meta_link).to eq(true)
274
+ end
275
+ end
276
+
277
+ describe '#datalists' do
278
+ it 'returns all paramters and attributes that are members of a datalist' do
279
+ @representor_hash = @base_representor.merge(@transition_elements)
280
+ has_data_list = expect(subject.datalists.first.to_hash).to eq({"renegade" => "renegade", "compliant" => "compliant"})
281
+ end
282
+ end
283
+ end
284
+ end
285
+ end
@@ -0,0 +1,118 @@
1
+ require 'spec_helper'
2
+ require 'representors/serialization/deserializer_factory'
3
+
4
+ module Representors
5
+ describe DeserializerFactory do
6
+ let(:factory) { Class.new(DeserializerFactory) } # prevent singleton pollution from spec
7
+
8
+ describe '.register_deserializers' do
9
+ it 'adds classes to the registered deserializers' do
10
+ serializer_classes = [double('deserializer1'), double('deserializer2')]
11
+ factory.register_deserializers(*serializer_classes)
12
+
13
+ expect(factory.registered_deserializers).to include(*serializer_classes)
14
+ end
15
+ end
16
+
17
+ describe '.registered_deserializers' do
18
+ before do
19
+ @deserializer_class = double('deserializer3')
20
+ factory.register_deserializers(@deserializer_class)
21
+ end
22
+
23
+ it 'memoizes' do
24
+ registered_deserializers = factory.registered_deserializers.object_id
25
+
26
+ expect(registered_deserializers).to eq(factory.registered_deserializers.object_id)
27
+ end
28
+
29
+ it 'returns a frozen list of registered_deserializers' do
30
+ expect(factory.registered_deserializers).to be_frozen
31
+ end
32
+
33
+ it 'returns registered serializers' do
34
+ expect(factory.registered_deserializers).to include(@deserializer_class)
35
+ end
36
+ end
37
+
38
+ describe '#build' do
39
+ let(:document) { {}.to_json }
40
+ subject(:deserializer) { DeserializerFactory.build(media_type, document) }
41
+
42
+ shared_examples_for 'a built deserializer' do
43
+ it 'sets the correct target in the deserializer' do
44
+ expect(deserializer.to_representor_hash).to eq(RepresentorHash.new)
45
+ end
46
+ end
47
+
48
+ shared_examples_for 'a hal deserializer' do
49
+ it 'returns a HalDeserializer' do
50
+ expect(deserializer).to be_instance_of HalDeserializer
51
+ end
52
+
53
+ it_behaves_like 'a built deserializer'
54
+ end
55
+
56
+ context 'with an empty response body' do
57
+ [nil, '', '{}', ' '].each do |response_body|
58
+ context "when given a #{response_body.inspect}" do
59
+ let(:media_type) { 'application/hal+json' }
60
+ let(:document) { '' }
61
+
62
+ it_behaves_like 'a hal deserializer'
63
+ end
64
+ end
65
+ end
66
+
67
+ context 'with hal+json media type as a string' do
68
+ let(:media_type) { 'application/hal+json' }
69
+
70
+ it_behaves_like 'a hal deserializer'
71
+ end
72
+
73
+ context 'with hal+json media type as a symbol' do
74
+ let(:media_type) { :hal }
75
+
76
+ it_behaves_like 'a hal deserializer'
77
+ end
78
+
79
+ shared_examples_for 'a hale deserializer' do
80
+ it 'returns a HaleDeserializer' do
81
+ expect(deserializer).to be_instance_of HaleDeserializer
82
+ end
83
+
84
+ it_behaves_like 'a built deserializer'
85
+ end
86
+
87
+ context 'with hale+json media type as a string' do
88
+ let(:media_type) { 'application/vnd.hale+json' }
89
+
90
+ it_behaves_like 'a hale deserializer'
91
+ end
92
+
93
+ context 'with hale+json media type as a symbol' do
94
+ let(:media_type) { :hale }
95
+
96
+ it_behaves_like 'a hale deserializer'
97
+ end
98
+
99
+ shared_examples_for 'an unknown media type' do
100
+ it 'raises an unknown media type error' do
101
+ expect { deserializer }.to raise_error(UnknownMediaTypeError, "Unknown media-type: #{media_type}.")
102
+ end
103
+ end
104
+
105
+ context 'unknown media type string' do
106
+ let(:media_type) { 'unknown' }
107
+
108
+ it_behaves_like 'an unknown media type'
109
+ end
110
+
111
+ context 'unknown media type symbol' do
112
+ let(:media_type) { :unknown }
113
+
114
+ it_behaves_like 'an unknown media type'
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe Representors::HalDeserializer do
4
+
5
+ it "initializes with a JSON document" do
6
+ expect(Representors::HalDeserializer.new({}.to_json)).to be_instance_of(Representors::HalDeserializer)
7
+ end
8
+
9
+ it 'provides the media-type application/vnd.hal+json' do
10
+ formats = Representors::HalDeserializer.media_types
11
+ expect(formats).to include('application/hal+json', 'application/json')
12
+ end
13
+
14
+ it 'provides the media symbol :hal' do
15
+ expect(Representors::HalDeserializer.media_symbols).to include(:hal)
16
+ end
17
+
18
+ describe "#to_representor" do
19
+ it_behaves_like 'can create a representor from a hal document' do
20
+ subject(:deserializer) {Representors::HalDeserializer.new(document)}
21
+ end
22
+
23
+ context "when the Hal deserializer recieves a Hale document" do
24
+ it "deserializes as a Hal document and not a Hale document" do
25
+ file = File.read("#{SPEC_DIR}/fixtures/hale_tutorial_examples/meta.json")
26
+ hal_rep = Representors::HalDeserializer.new(file).to_representor
27
+ place_order_link_data = hal_rep.transitions.find { |t| t[:rel] == "place_order" }
28
+ expect(hal_rep.properties.keys).to include('_meta')
29
+ expect(place_order_link_data.interface_method).to eq('GET')
30
+ end
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,171 @@
1
+ require 'spec_helper'
2
+ require 'yaml'
3
+ require 'uri'
4
+
5
+ module Representors
6
+ describe Serialization::HalSerializer do
7
+
8
+ subject(:serializer) { SerializerFactory.build(:hal_json, Representor.new(document)) }
9
+ let(:result) {JSON.parse(serializer.to_media_type)}
10
+ let(:base_representor) do
11
+ {
12
+ protocol: 'http',
13
+ href: 'www.example.com/drds',
14
+ id: 'drds',
15
+ doc: 'doc'
16
+ }
17
+ end
18
+
19
+ shared_examples 'a hal documents attributes' do |representor_hash|
20
+ let(:document) { representor_hash.merge(base_representor) }
21
+
22
+ representor_hash[:attributes].each do |k, v|
23
+ it "includes the document attribute #{k} and associated value" do
24
+ expect(result[k]).to eq(v[:value])
25
+ end
26
+ end
27
+ end
28
+
29
+ shared_examples 'a hal documents links' do |representor_hash|
30
+ let(:document) { representor_hash.merge(base_representor) }
31
+
32
+ representor_hash[:transitions].each do |item|
33
+ it "includes the document transition #{item}" do
34
+ links = result['_links']
35
+ link = links[item[:rel]]
36
+ expect(link['href']).to eq(item[:href])
37
+ end
38
+ end
39
+ end
40
+
41
+ shared_examples 'a hal documents embedded hal documents' do |representor_hash|
42
+ let(:document) { representor_hash.merge(base_representor) }
43
+
44
+ representor_hash[:embedded].each do |embed_name, embed|
45
+ embed[:attributes].each do |k, v|
46
+ it "includes the document attribute #{k} and associated value" do
47
+ expect(result['_embedded'][embed_name][k]).to eq(v[:value])
48
+ end
49
+ end
50
+ embed[:transitions].each do |item|
51
+ it "includes the document attribute #{item} and associated value" do
52
+ expect(result['_embedded'][embed_name]['_links'][item[:rel]]['href']).to eq(item[:href])
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ shared_examples 'a hal documents embedded collection' do |representor_hash|
59
+ let(:document) { representor_hash.merge(base_representor) }
60
+
61
+ representor_hash[:embedded].each do |embed_name, embeds|
62
+ embeds.each_with_index do |embed, index|
63
+ embed[:attributes].each do |k, v|
64
+ it "includes the document attribute #{k} and associated value" do
65
+ expect(result['_embedded'][embed_name][index][k]).to eq(v[:value])
66
+ end
67
+ end
68
+ embed[:transitions].each do |item|
69
+ it "includes the document attribute #{item} and associated value" do
70
+ expect(result['_embedded'][embed_name][index]['_links'][item[:rel]]['href']).to eq(item[:href])
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ describe '#as_media_type' do
78
+ context 'empty document' do
79
+ let(:document) { {} }
80
+
81
+ it 'returns a hash with no attributes, links or embedded resources' do
82
+ empty_json_document = '{}'
83
+ expect(serializer.to_media_type).to eq(empty_json_document)
84
+ end
85
+ end
86
+
87
+ context 'Document with only properties' do
88
+ representor_hash = begin
89
+ {
90
+ attributes: {
91
+ 'title' => {value: 'The Neverending Story'},
92
+ 'author' => {value: 'Michael Ende'},
93
+ 'pages' => {value: '396'}
94
+ }
95
+ }
96
+ end
97
+
98
+ it_behaves_like 'a hal documents attributes', representor_hash
99
+ end
100
+
101
+ context 'Document with properties and links' do
102
+ representor_hash = begin
103
+ {
104
+ attributes: {
105
+ 'title' => {value: 'The Neverending Story'},
106
+ },
107
+ transitions: [
108
+ {
109
+ href: '/mike',
110
+ rel: 'author',
111
+ }
112
+ ]
113
+ }
114
+ end
115
+
116
+ it_behaves_like 'a hal documents attributes', representor_hash
117
+ it_behaves_like 'a hal documents links', representor_hash
118
+ end
119
+
120
+ context 'Document with properties, links, and embedded' do
121
+ representor_hash = begin
122
+ {
123
+ attributes: {
124
+ 'title' => {value: 'The Neverending Story'},
125
+ },
126
+ transitions: [
127
+ {
128
+ href: '/mike',
129
+ rel: 'author',
130
+ }
131
+ ],
132
+ embedded: {
133
+ 'embedded_book' => {attributes: {'content' => { value: 'A...' } }, transitions: [{rel: 'self', href: '/foo'}]}
134
+ }
135
+ }
136
+ end
137
+
138
+ it_behaves_like 'a hal documents attributes', representor_hash
139
+ it_behaves_like 'a hal documents links', representor_hash
140
+ it_behaves_like 'a hal documents embedded hal documents', representor_hash
141
+ end
142
+
143
+ context 'Document with an embedded collection' do
144
+ representor_hash = begin
145
+ {
146
+ attributes: {
147
+ 'title' => {value: 'The Neverending Story'},
148
+ },
149
+ transitions: [
150
+ {
151
+ href: '/mike',
152
+ rel: 'author',
153
+ }
154
+ ],
155
+ embedded: {
156
+ 'embedded_book' => [
157
+ {attributes: {'content' => { value: 'A...' } }, transitions: [{rel: 'self', href: '/foo1'}]},
158
+ {attributes: {'content' => { value: 'B...' } }, transitions: [{rel: 'self', href: '/foo2'}]},
159
+ {attributes: {'content' => { value: 'C...' } }, transitions: [{rel: 'self', href: '/foo3'}]}
160
+ ]
161
+ }
162
+ }
163
+ end
164
+
165
+ it_behaves_like 'a hal documents attributes', representor_hash
166
+ it_behaves_like 'a hal documents links', representor_hash
167
+ it_behaves_like 'a hal documents embedded collection', representor_hash
168
+ end
169
+ end
170
+ end
171
+ end