representors 0.0.5

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.
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,108 @@
1
+ require 'spec_helper'
2
+ require 'representors/serialization/serializer_base'
3
+
4
+ module Representors
5
+
6
+ describe SerializerFactory do
7
+ let(:factory) { Class.new(SerializerFactory) } # prevent singleton pollution from spec
8
+
9
+ describe '.register_serializers' do
10
+ it 'adds classes to the registered serializers' do
11
+ serializer_classes = [create_serializer('serializer1'), create_serializer('serializer2')]
12
+ factory.register_serializers(*serializer_classes)
13
+
14
+ expect(factory.registered_serializers).to include(*serializer_classes)
15
+ end
16
+ end
17
+
18
+ describe '.registered_serializers' do
19
+ before do
20
+ @serializer_class = create_serializer('serializer3')
21
+ factory.register_serializers(@serializer_class)
22
+ end
23
+
24
+ it 'memoizes' do
25
+ registered_serializers = factory.registered_serializers.object_id
26
+
27
+ expect(registered_serializers).to eq(factory.registered_serializers.object_id)
28
+ end
29
+
30
+ it 'returns a frozen list of registered_serializers' do
31
+ expect(factory.registered_serializers).to be_frozen
32
+ end
33
+
34
+ it 'returns registered serializers' do
35
+ expect(factory.registered_serializers).to include(@serializer_class)
36
+ end
37
+ end
38
+ end
39
+
40
+ describe '#build' do
41
+ let(:representor) { Representor.new }
42
+ subject(:serializer) { SerializerFactory.build(media_type, representor) }
43
+
44
+ shared_examples_for 'a built serializer' do
45
+ it 'sets the correct target in the serializer' do
46
+ expect(serializer.target).to eq(representor)
47
+ end
48
+ end
49
+
50
+ shared_examples_for 'a hal serializer' do
51
+ it 'returns a Halserializer' do
52
+ expect(serializer).to be_instance_of(Serialization::HalSerializer)
53
+ end
54
+
55
+ it_behaves_like 'a built serializer'
56
+ end
57
+
58
+ context 'with hal+json media type as a string' do
59
+ let(:media_type) { 'application/hal+json' }
60
+
61
+ it_behaves_like 'a hal serializer'
62
+ end
63
+
64
+ context 'with hal+json media type as a symbol' do
65
+ let(:media_type) { :hal_json }
66
+
67
+ it_behaves_like 'a hal serializer'
68
+ end
69
+
70
+ shared_examples_for 'a hale serializer' do
71
+ it 'returns a Haleserializer' do
72
+ expect(serializer).to be_instance_of(Serialization::HaleSerializer)
73
+ end
74
+
75
+ it_behaves_like 'a built serializer'
76
+ end
77
+
78
+ context 'with hale+json media type as a string' do
79
+ let(:media_type) { 'application/vnd.hale+json' }
80
+
81
+ it_behaves_like 'a hale serializer'
82
+ end
83
+
84
+ context 'with hale+json media type as a symbol' do
85
+ let(:media_type) { :hale_json }
86
+
87
+ it_behaves_like 'a hale serializer'
88
+ end
89
+
90
+ shared_examples_for 'an unknown media type' do
91
+ it 'raises an unknown media type error' do
92
+ expect { serializer }.to raise_error(UnknownMediaTypeError, "Unknown media-type: #{media_type}.")
93
+ end
94
+ end
95
+
96
+ context 'unknown media type string' do
97
+ let(:media_type) { 'unknown' }
98
+
99
+ it_behaves_like 'an unknown media type'
100
+ end
101
+
102
+ context 'unknown media type symbol' do
103
+ let(:media_type) { :unknown }
104
+
105
+ it_behaves_like 'an unknown media type'
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,349 @@
1
+ require 'spec_helper'
2
+ require 'yaml'
3
+ require 'uri'
4
+
5
+ module Representors
6
+ describe Transition do
7
+ before do
8
+ @self_transition = {
9
+ doc: 'Returns a list of DRDs.',
10
+ rt: 'drds',
11
+ type: 'safe',
12
+ href: 'some.example.com/list',
13
+ rel: 'self'
14
+ }
15
+ @search_transition = {
16
+ doc: 'Returns a list of DRDs that satisfy the search term.',
17
+ rt: 'drds',
18
+ type: 'safe',
19
+ method: 'post',
20
+ href: '/{?name}',
21
+ rel: 'search',
22
+ links: {
23
+ self: 'DRDs#drds/create',
24
+ help: 'Forms/create'
25
+ },
26
+ descriptors: {
27
+ name: {
28
+ doc: 'Name to search',
29
+ profile: 'http://alps.io/schema.org/Text',
30
+ sample: 'drdname',
31
+ scope: 'href',
32
+ options: { list: ['one', 'two'] }
33
+ },
34
+ status: {
35
+ doc: 'How is the DRD.',
36
+ profile: 'http://alps.io/schema.org/Text',
37
+ sample: 'renegade',
38
+ options: { list: ['renegade', 'compliant'], id: 'status_list' }
39
+ }
40
+ }
41
+ }
42
+ end
43
+
44
+ let(:representor_hash) { @representor_hash || @self_transition }
45
+ let(:subject) { Transition.new(representor_hash) }
46
+
47
+ describe '#to_s' do
48
+ it 'retuns a string representation' do
49
+ expect(subject.to_s).to eq(representor_hash.to_s)
50
+ end
51
+ end
52
+
53
+ describe '#to_hash' do
54
+ let(:hash_with_symbol_keys) do
55
+ {
56
+ href: 'some.niceplace.com/list',
57
+ rel: 'self'
58
+ }
59
+ end
60
+ let(:hash_with_string_keys) do
61
+ {
62
+ 'href' => 'some.niceplace.com/list',
63
+ 'rel' => 'self'
64
+ }
65
+ end
66
+ context 'the keys are strings' do
67
+ let(:representor_hash) {hash_with_string_keys}
68
+ it 'returns a hash with the keys as strings' do
69
+ expect(subject.to_hash).to eq(hash_with_string_keys)
70
+ end
71
+ end
72
+ context 'the keys are symbols' do
73
+ let(:representor_hash) {hash_with_symbol_keys}
74
+ it 'returns a hash with the keys as strings' do
75
+ expect(subject.to_hash).to eq(hash_with_string_keys)
76
+ end
77
+ end
78
+ end
79
+
80
+ describe '#[]' do
81
+ let(:representor_hash) { {key => value}}
82
+ let(:value) { 'http://www.dontknow.com'}
83
+ let(:key) {'href'}
84
+
85
+ it 'retuns the value for the keys' do
86
+ expect(subject[key]).to eq(value)
87
+ end
88
+
89
+ it 'returns nil if the key is not in this transition' do
90
+ expect(subject['Ido not exists']).to be_nil
91
+ end
92
+
93
+ it 'has indiferent access to the hash' do
94
+ expect(subject[key.to_sym]).to eq(value)
95
+ expect(subject[key.to_s]).to eq(value)
96
+ end
97
+
98
+ end
99
+
100
+ describe '#has_key?' do
101
+ let(:representor_hash) { {key => value}}
102
+ let(:value) { 'http://www.dontknow.com'}
103
+ let(:key) {'href'}
104
+
105
+ it 'retuns the value for the keys' do
106
+ expect(subject.has_key?(key)).to eq(true)
107
+ end
108
+
109
+ it 'returns nil if the key is not in this transition' do
110
+ expect(subject.has_key?('Ido not exists')).to eq(false)
111
+ end
112
+ end
113
+
114
+ describe '.new' do
115
+ it 'returns a Representors::Transition instance' do
116
+ expect(subject).to be_an_instance_of(Transition)
117
+ end
118
+
119
+ describe '#rel' do
120
+ it 'returns the transition key' do
121
+ expect(subject.rel).to eq('self')
122
+ end
123
+ end
124
+
125
+ describe '#interface_method' do
126
+ it 'returns the uniform interface method' do
127
+ expect(subject.interface_method).to eq('GET')
128
+ end
129
+ it 'retuns the interface_method provided by the hash' do
130
+ @representor_hash = @search_transition
131
+ expect(subject.interface_method).to eq('post')
132
+ end
133
+ end
134
+
135
+ describe '#parameters' do
136
+ it 'returns a list of fields representing the link parameters' do
137
+ @representor_hash = @search_transition
138
+ expect(subject.parameters.size).to eq(1)
139
+ field = subject.parameters.first
140
+ expect(field).to be_an_instance_of(Field)
141
+ expect(field.scope).to eq('href')
142
+ expect(field.name).to eq(:name)
143
+ end
144
+ context 'there are no params' do
145
+ let(:transition) do
146
+ {
147
+ href: 'some.example.com',
148
+ rel: 'filter'
149
+ }
150
+ end
151
+ it 'returns an empty array' do
152
+ expect(Transition.new(transition).parameters).to eq([])
153
+ end
154
+ end
155
+ context 'the uri template has information we do not have in data' do
156
+ let(:transition) do
157
+ {
158
+ href: 'some.place.com{?name,localization}',
159
+ rel: 'filter',
160
+ descriptors: {
161
+ name: {
162
+ doc: name_doc,
163
+ type: 'Integer',
164
+ profile: name_profile,
165
+ scope: 'href'
166
+ },
167
+ localization: {
168
+ doc: 'wrong scope',
169
+ type: 'Something crazy',
170
+ profile: 'Because this key has no scope should not be used'
171
+ }
172
+ }
173
+ }
174
+ end
175
+ let(:name_doc) {'di place of Trusmis'}
176
+ let(:name_profile) {'http://alps.io/schema.org/Text'}
177
+
178
+ it 'returns all the variables in the uri template' do
179
+ expect(Transition.new(transition).parameters.size).to eq(2)
180
+ end
181
+ it 'returns the information about the variable described by the document' do
182
+ param = Transition.new(transition).parameters.find{|param| param.name == :name}
183
+ expect(param.scope).to eq('href')
184
+ expect(param.type).to eq('Integer')
185
+ end
186
+ it 'returns default information for the variable not described by the document' do
187
+ param = Transition.new(transition).parameters.find{|param| param.name == :localization}
188
+ expect(param.scope).to eq('href')
189
+ expect(param.type).to eq('string')
190
+ end
191
+ end
192
+ end
193
+
194
+ describe '#attributes' do
195
+ it 'returns a list of fields representing the link attributes' do
196
+ @representor_hash = @search_transition
197
+ expect(subject.attributes.size).to eq(1)
198
+ field = subject.attributes.first
199
+ expect(field).to be_an_instance_of(Field)
200
+ expect(field.scope).to eq('attribute')
201
+ expect(field.name).to eq(:status)
202
+ end
203
+ end
204
+
205
+ describe 'descriptors' do
206
+ it 'returns a list of fields representing the link attributes' do
207
+ @representor_hash = @search_transition
208
+ expect(subject.descriptors.size).to eq(2)
209
+ fields = subject.descriptors.all? { |item| item.instance_of?(Field) }
210
+ expect(fields).to eq(true)
211
+ end
212
+
213
+ it 'returns params as part of the descriptors' do
214
+ @representor_hash = @search_transition
215
+ field = subject.descriptors.first
216
+ expect(field).to be_an_instance_of(Field)
217
+ expect(field.scope).to eq('attribute')
218
+ end
219
+
220
+ it 'returns attributes as part of the descriptors' do
221
+ @representor_hash = @search_transition
222
+ field = subject.descriptors[1]
223
+ expect(field).to be_an_instance_of(Field)
224
+ expect(field.scope).to eq('href')
225
+ end
226
+ end
227
+
228
+ describe '#meta_links' do
229
+ context 'no metalinks' do
230
+ it 'returns an empty array' do
231
+ expect(subject.meta_links).to eq([])
232
+ end
233
+ end
234
+
235
+ it 'returns a list of Transitions' do
236
+ @representor_hash = @search_transition
237
+ links = subject.meta_links.all? { |item| item.instance_of?(Transition) }
238
+ expect(links).to eq(true)
239
+ end
240
+ it 'returns self as the first meta link' do
241
+ @representor_hash = @search_transition
242
+ link = subject.meta_links[0]
243
+ expect(link.rel).to eq(:self)
244
+ expect(link.uri).to eq('DRDs#drds/create')
245
+ end
246
+ it 'returns self as the first meta link' do
247
+ @representor_hash = @search_transition
248
+ link = subject.meta_links[1]
249
+ expect(link.rel).to eq(:help)
250
+ expect(link.uri).to eq("Forms/create")
251
+ end
252
+ end
253
+
254
+ describe '#uri' do
255
+ context 'the url does not have a template variable in the url' do
256
+ let(:transition) do
257
+ {
258
+ href: 'some.example.com',
259
+ rel: 'filter'
260
+ }
261
+ end
262
+ context 'params provided' do
263
+ it 'returns the url as it is' do
264
+ expect(Transition.new(transition).uri(uuid: 'uuid')).to eq(transition[:href])
265
+ end
266
+ end
267
+ context 'params not provided' do
268
+ it 'returns the url as it is' do
269
+ expect(Transition.new(transition).uri).to eq(transition[:href])
270
+ end
271
+ end
272
+ end
273
+
274
+ context 'the url has template variables in the base url and the query parameters' do
275
+ let(:transition) do
276
+ {
277
+ href: 'some.example.com/{uuid}?first_param=goodstuff{&filter}',
278
+ rel: 'filter'
279
+ }
280
+ end
281
+ let(:uuid) { SecureRandom.uuid}
282
+ let(:filter) {'cows'}
283
+ let(:full_url) {"some.example.com/#{uuid}?first_param=goodstuff&filter=cows"}
284
+ let(:not_templated_url) {'some.example.com/?first_param=goodstuff'}
285
+
286
+ it 'allows to create the url with the correct parameters' do
287
+ expect(Transition.new(transition).uri(uuid: uuid, filter: filter)).to eq(full_url)
288
+ end
289
+ it 'returns the url without template variables when there are no params' do
290
+ expect(Transition.new(transition).uri).to eq(not_templated_url)
291
+ end
292
+ it 'returns the url without template variables when the params does not match the template variable' do
293
+ expect(Transition.new(transition).uri(stuff: filter)).to eq(not_templated_url)
294
+ end
295
+ end
296
+ end
297
+
298
+ describe '#templated_uri' do
299
+ context 'the transition has a templated url' do
300
+ let(:transition) do
301
+ {
302
+ href: 'some.example.com/{uuid}?first_param=goodstuff{&filter}',
303
+ rel: 'filter'
304
+ }
305
+ end
306
+ it 'returns the templated url' do
307
+ expect(Transition.new(transition).templated_uri).to eq(transition[:href])
308
+ end
309
+ end
310
+ context 'the transition has a non-templated url' do
311
+ let(:transition) do
312
+ {
313
+ href: 'some.example.com',
314
+ rel: 'filter'
315
+ }
316
+ end
317
+ it 'returns the url' do
318
+ expect(Transition.new(transition).templated_uri).to eq(transition[:href])
319
+ end
320
+ end
321
+ end
322
+
323
+ describe '#templated?' do
324
+ context 'the transition has a templated url' do
325
+ let(:transition) do
326
+ {
327
+ href: 'some.example.com/{uuid}?first_param=goodstuff{&filter}',
328
+ rel: 'filter'
329
+ }
330
+ end
331
+ it 'returns true' do
332
+ expect(Transition.new(transition)).to be_templated
333
+ end
334
+ end
335
+ context 'the transition has a non-templated url' do
336
+ let(:transition) do
337
+ {
338
+ href: 'some.example.com',
339
+ rel: 'filter'
340
+ }
341
+ end
342
+ it 'returns false' do
343
+ expect(Transition.new(transition)).not_to be_templated
344
+ end
345
+ end
346
+ end
347
+ end
348
+ end
349
+ end