yaks 0.4.4 → 0.5.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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/CHANGELOG.md +44 -3
  4. data/README.md +90 -33
  5. data/Rakefile +10 -0
  6. data/bench/bench.rb +0 -1
  7. data/bench/bench_1000.rb +60 -0
  8. data/lib/yaks/breaking_changes.rb +22 -0
  9. data/lib/yaks/config/dsl.rb +114 -27
  10. data/lib/yaks/config.rb +39 -54
  11. data/lib/yaks/default_policy.rb +32 -14
  12. data/lib/yaks/format/collection_json.rb +4 -4
  13. data/lib/yaks/format/hal.rb +20 -3
  14. data/lib/yaks/format/json_api.rb +3 -3
  15. data/lib/yaks/format.rb +54 -9
  16. data/lib/yaks/fp/callable.rb +9 -0
  17. data/lib/yaks/fp/hash_updatable.rb +2 -0
  18. data/lib/yaks/fp/updatable.rb +2 -0
  19. data/lib/yaks/fp.rb +8 -0
  20. data/lib/yaks/mapper/link.rb +2 -2
  21. data/lib/yaks/mapper.rb +6 -6
  22. data/lib/yaks/primitivize.rb +2 -2
  23. data/lib/yaks/resource/link.rb +0 -4
  24. data/lib/yaks/runner.rb +90 -0
  25. data/lib/yaks/util.rb +4 -0
  26. data/lib/yaks/version.rb +1 -1
  27. data/lib/yaks.rb +3 -0
  28. data/spec/acceptance/acceptance_spec.rb +6 -1
  29. data/spec/json/confucius.collection.json +5 -16
  30. data/spec/json/plant_collection.collection.json +32 -0
  31. data/spec/spec_helper.rb +2 -1
  32. data/spec/support/deep_eql.rb +14 -7
  33. data/spec/support/pet_mapper.rb +0 -2
  34. data/spec/unit/yaks/collection_mapper_spec.rb +24 -2
  35. data/spec/unit/yaks/config/dsl_spec.rb +6 -10
  36. data/spec/unit/yaks/config_spec.rb +40 -99
  37. data/spec/unit/yaks/default_policy_spec.rb +20 -0
  38. data/spec/unit/yaks/format/collection_json_spec.rb +41 -0
  39. data/spec/unit/yaks/format/hal_spec.rb +38 -3
  40. data/spec/unit/yaks/format/json_api_spec.rb +2 -2
  41. data/spec/unit/yaks/format_spec.rb +28 -3
  42. data/spec/unit/yaks/fp/callable_spec.rb +13 -0
  43. data/spec/unit/yaks/mapper_spec.rb +226 -126
  44. data/spec/unit/yaks/resource/link_spec.rb +2 -3
  45. data/spec/unit/yaks/resource_spec.rb +15 -0
  46. data/spec/unit/yaks/runner_spec.rb +260 -0
  47. data/spec/unit/yaks/util_spec.rb +7 -1
  48. data/yaks.gemspec +4 -1
  49. metadata +72 -15
  50. /data/spec/json/{hal_plant_collection.json → plant_collection.hal.json} +0 -0
@@ -0,0 +1,260 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Yaks::Runner do
4
+ subject(:runner) {
5
+ described_class.new(object: object, config: config, options: options)
6
+ }
7
+
8
+ let(:object) { Object.new }
9
+ let(:config) { Yaks::Config.new }
10
+ let(:options) { {} }
11
+
12
+ describe '#call' do
13
+ let(:runner) {
14
+ Class.new(described_class) do
15
+ def steps
16
+ [ [:step1, ->(x) { x + 35 }],
17
+ [:step2, ->(x) { "#{x} #{x}"}] ]
18
+ end
19
+ end.new(object: object, config: config, options: options)
20
+ }
21
+
22
+ let(:object) { 7 }
23
+
24
+ it 'should go through all the steps' do
25
+ expect(runner.call).to eql "42 42"
26
+ end
27
+ end
28
+
29
+ describe '#context' do
30
+ it 'should contain the policy, env, and an empty mapper_stack' do
31
+ expect(runner.context).to eql(policy: config.policy, env: {}, mapper_stack: [])
32
+ end
33
+
34
+ context 'with an item mapper' do
35
+ let(:options) { { item_mapper: :foo } }
36
+
37
+ it 'should contain the item_mapper' do
38
+ expect(runner.context).to eql(policy: config.policy, env: {}, mapper_stack: [], item_mapper: :foo)
39
+ end
40
+ end
41
+ end
42
+
43
+ describe '#format_class' do
44
+ let(:config) do
45
+ Yaks::Config.new do
46
+ default_format :collection_json
47
+ end
48
+ end
49
+
50
+ let(:rack_env) {
51
+ { 'HTTP_ACCEPT' => 'application/hal+json;q=0.8, application/vnd.api+json' }
52
+ }
53
+
54
+ it 'should fall back to the default when no HTTP_ACCEPT key is present' do
55
+ runner = described_class.new(object: nil, config: config, options: { env: {} })
56
+ expect(runner.format_class).to equal Yaks::Format::CollectionJson
57
+ end
58
+
59
+ it 'should detect format based on accept header' do
60
+ rack_env = { 'HTTP_ACCEPT' => 'application/hal+json;q=0.8, application/vnd.api+json' }
61
+ runner = described_class.new(object: nil, config: config, options: { env: rack_env })
62
+ expect(runner.format_class).to equal Yaks::Format::JsonAPI
63
+ end
64
+
65
+ it 'should know to pick the best match' do
66
+ rack_env = { 'HTTP_ACCEPT' => 'application/hal+json;q=0.8, application/vnd.api+json;q=0.7' }
67
+ runner = described_class.new(object: nil, config: config, options: { env: rack_env })
68
+ expect(runner.format_class).to equal Yaks::Format::Hal
69
+ end
70
+
71
+ it 'should pick the one given in the options if no header matches' do
72
+ rack_env = { 'HTTP_ACCEPT' => 'text/html, application/json' }
73
+ runner = described_class.new(object: nil, config: config, options: { format: :hal, env: rack_env })
74
+ expect(runner.format_class).to equal Yaks::Format::Hal
75
+ end
76
+
77
+ it 'should fall back to the default when no mime type is recognized' do
78
+ rack_env = { 'HTTP_ACCEPT' => 'text/html, application/json' }
79
+ runner = described_class.new(object: nil, config: config, options: { env: rack_env })
80
+ expect(runner.format_class).to equal Yaks::Format::CollectionJson
81
+ end
82
+ end
83
+
84
+ describe '#format_name' do
85
+ context 'with no format specified' do
86
+ it 'should default to :hal' do
87
+ expect(runner.format_name).to eql :hal
88
+ end
89
+ end
90
+
91
+ context 'with a default format specified' do
92
+ let(:config) { Yaks::Config.new { default_format :collection_json } }
93
+
94
+ context 'with a format in the options' do
95
+ let(:options) { { format: :json_api } }
96
+ it 'should give preference to that one' do
97
+ expect(runner.format_name).to eql :json_api
98
+ end
99
+ end
100
+
101
+ context 'without a format in the options' do
102
+ it 'should take the specified default' do
103
+ expect(runner.format_name).to eql :collection_json
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ describe '#formatter' do
110
+ let(:config) {
111
+ Yaks::Config.new do
112
+ default_format :json_api
113
+ format_options :json_api, {format_option: [:foo]}
114
+ end
115
+ }
116
+
117
+ let(:formatter) { runner.formatter }
118
+
119
+ it 'should create a formatter based on class and options' do
120
+ expect(formatter).to be_a Yaks::Format::JsonAPI
121
+ expect(formatter.send(:options)).to eql(format_option: [:foo])
122
+ end
123
+ end
124
+
125
+ describe '#insert_hooks' do
126
+ let(:options) { { mapper: Yaks::Mapper } }
127
+ let(:config) { Yaks::Config.new(&hooks) }
128
+
129
+ describe 'before' do
130
+ let(:hooks) { proc { before(:map) { :before_map_impl } } }
131
+
132
+ it 'should insert a hook before the step' do
133
+ expect(runner.steps.map(&:first)).to eql [
134
+ :before_map, :map, :format, :primitivize, :serialize
135
+ ]
136
+ expect(runner.steps.assoc(:before_map).last.call).to be :before_map_impl
137
+ end
138
+ end
139
+
140
+ describe 'after' do
141
+ let(:hooks) { proc { after(:format) { :after_format_impl } } }
142
+
143
+ it 'should insert a hook after the step' do
144
+ expect(runner.steps.map(&:first)).to eql [
145
+ :map, :format, :after_format, :primitivize, :serialize
146
+ ]
147
+ expect(runner.steps.assoc(:after_format).last.call).to be :after_format_impl
148
+ end
149
+ end
150
+
151
+ describe 'around' do
152
+ let(:hooks) { proc { around(:format) { :around_format_impl } } }
153
+
154
+ it 'should insert a hook around the step' do
155
+ expect(runner.steps.map(&:first)).to eql [
156
+ :map, :format, :primitivize, :serialize
157
+ ]
158
+ expect(runner.steps.assoc(:format).last.call(nil)).to be :around_format_impl
159
+ end
160
+ end
161
+
162
+ describe 'around' do
163
+ let(:hooks) { proc { skip(:serialize) } }
164
+
165
+ it 'should insert a hook before the step' do
166
+ expect(runner.steps.map(&:first)).to eql [
167
+ :map, :format, :primitivize
168
+ ]
169
+ end
170
+ end
171
+
172
+ describe 'multiple hooks' do
173
+ let(:hooks) {
174
+ proc {
175
+ after(:format) { :after_format_impl }
176
+ skip(:serialize)
177
+ }
178
+ }
179
+
180
+ it 'should insert the hooks' do
181
+ expect(runner.steps.map(&:first)).to eql [
182
+ :map, :format, :after_format, :primitivize
183
+ ]
184
+ end
185
+ end
186
+ end
187
+
188
+ describe '#mapper' do
189
+ context 'with an explicit mapper in the options' do
190
+ let(:mapper_class) { Class.new(Yaks::Mapper) }
191
+ let(:options) { { mapper: mapper_class } }
192
+
193
+ it 'should take the mapper from options' do
194
+ expect(runner.mapper).to be_a mapper_class
195
+ end
196
+ end
197
+
198
+ context 'without a mapper specified' do
199
+ let(:object) { Pet.new(id: 7, name: 'fifi', species: 'cat') }
200
+
201
+ it 'should infer one from the object to be mapped' do
202
+ expect(runner.mapper).to be_a PetMapper
203
+ end
204
+
205
+ it 'should pass the context to the mapper' do
206
+ expect(runner.mapper.context).to be runner.context
207
+ end
208
+ end
209
+ end
210
+
211
+ describe '#serializer' do
212
+ context 'with a serializer configured' do
213
+ let(:config) {
214
+ Yaks::Config.new do
215
+ json_serializer do |input|
216
+ "serialized #{input}"
217
+ end
218
+ end
219
+ }
220
+
221
+ it 'should try to find an explicitly configured serializer' do
222
+ expect(runner.serializer.call('42')).to eql 'serialized 42'
223
+ end
224
+ end
225
+
226
+ it 'should fall back to the policy' do
227
+ expect(runner.serializer.call([1,2,3])).to eql "[\n 1,\n 2,\n 3\n]"
228
+ end
229
+ end
230
+
231
+ describe '#steps' do
232
+ let(:options) {{ mapper: Yaks::Mapper }}
233
+
234
+ it 'should have all four steps' do
235
+ expect(runner.steps).to eql [
236
+ [ :map, runner.mapper ],
237
+ [ :format, runner.formatter ],
238
+ [ :primitivize, runner.primitivize],
239
+ [ :serialize, runner.serializer ]
240
+ ]
241
+ end
242
+
243
+ context 'with hooks' do
244
+ let(:config) {
245
+ Yaks::Config.new do
246
+ after(:format, :my_hook) { :foo }
247
+ end
248
+ }
249
+
250
+ it 'should insert hooks' do
251
+ expect(runner.steps.map(&:first)).to eql [:map, :format, :my_hook, :primitivize, :serialize]
252
+ end
253
+ end
254
+ end
255
+
256
+ it 'should memoize' do
257
+ expect(runner.formatter).to be runner.formatter
258
+ end
259
+
260
+ end
@@ -31,7 +31,7 @@ RSpec.describe Yaks::Util do
31
31
 
32
32
  describe '#camelize' do
33
33
  it 'should camelize' do
34
- expect(camelize('foo_bar/baz')).to eql 'FooBar::Baz'
34
+ expect(camelize('foo_bar_moo/baz/booz')).to eql 'FooBarMoo::Baz::Booz'
35
35
  end
36
36
  end
37
37
 
@@ -40,4 +40,10 @@ RSpec.describe Yaks::Util do
40
40
  expect(underscore('FooBar::Baz-Quz::Quux')).to eql 'foo_bar/baz__quz/quux'
41
41
  end
42
42
  end
43
+
44
+ describe '#slice_hash' do
45
+ it '#should retain the given keys from a hash' do
46
+ expect(slice_hash({a: 1, b:2, c:3}, :a, :c, :d)).to eql(a: 1, c:3)
47
+ end
48
+ end
43
49
  end
data/yaks.gemspec CHANGED
@@ -26,11 +26,14 @@ Gem::Specification.new do |gem|
26
26
  gem.add_runtime_dependency 'concord' , '~> 0.1.4'
27
27
  gem.add_runtime_dependency 'uri_template' , '~> 0.6.0'
28
28
  gem.add_runtime_dependency 'rack-accept' , '~> 0.4.5'
29
+ gem.add_runtime_dependency 'anima' , '~> 0.2.0'
30
+ gem.add_runtime_dependency 'adamantium' , '~> 0.2.0'
29
31
 
30
32
  gem.add_development_dependency 'virtus'
31
- gem.add_development_dependency 'rspec', '~> 2.99'
33
+ gem.add_development_dependency 'rspec', '~> 3.0'
32
34
  gem.add_development_dependency 'bogus'
33
35
  gem.add_development_dependency 'rake'
36
+ gem.add_development_dependency 'yard'
34
37
  gem.add_development_dependency 'mutant-rspec'
35
38
  gem.add_development_dependency 'mutant'
36
39
  gem.add_development_dependency 'rspec-its'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yaks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.4
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arne Brasseur
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-26 00:00:00.000000000 Z
11
+ date: 2014-09-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: inflection
@@ -66,6 +66,34 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: 0.4.5
69
+ - !ruby/object:Gem::Dependency
70
+ name: anima
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.2.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.2.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: adamantium
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.2.0
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.2.0
69
97
  - !ruby/object:Gem::Dependency
70
98
  name: virtus
71
99
  requirement: !ruby/object:Gem::Requirement
@@ -86,14 +114,14 @@ dependencies:
86
114
  requirements:
87
115
  - - "~>"
88
116
  - !ruby/object:Gem::Version
89
- version: '2.99'
117
+ version: '3.0'
90
118
  type: :development
91
119
  prerelease: false
92
120
  version_requirements: !ruby/object:Gem::Requirement
93
121
  requirements:
94
122
  - - "~>"
95
123
  - !ruby/object:Gem::Version
96
- version: '2.99'
124
+ version: '3.0'
97
125
  - !ruby/object:Gem::Dependency
98
126
  name: bogus
99
127
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +150,20 @@ dependencies:
122
150
  - - ">="
123
151
  - !ruby/object:Gem::Version
124
152
  version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: yard
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
125
167
  - !ruby/object:Gem::Dependency
126
168
  name: mutant-rspec
127
169
  requirement: !ruby/object:Gem::Requirement
@@ -196,6 +238,7 @@ files:
196
238
  - README.md
197
239
  - Rakefile
198
240
  - bench/bench.rb
241
+ - bench/bench_1000.rb
199
242
  - lib/yaks.rb
200
243
  - lib/yaks/breaking_changes.rb
201
244
  - lib/yaks/collection_mapper.rb
@@ -208,6 +251,7 @@ files:
208
251
  - lib/yaks/format/hal.rb
209
252
  - lib/yaks/format/json_api.rb
210
253
  - lib/yaks/fp.rb
254
+ - lib/yaks/fp/callable.rb
211
255
  - lib/yaks/fp/hash_updatable.rb
212
256
  - lib/yaks/fp/updatable.rb
213
257
  - lib/yaks/mapper.rb
@@ -223,6 +267,7 @@ files:
223
267
  - lib/yaks/primitivize.rb
224
268
  - lib/yaks/resource.rb
225
269
  - lib/yaks/resource/link.rb
270
+ - lib/yaks/runner.rb
226
271
  - lib/yaks/util.rb
227
272
  - lib/yaks/version.rb
228
273
  - notes.org
@@ -235,8 +280,9 @@ files:
235
280
  - spec/json/confucius.collection.json
236
281
  - spec/json/confucius.hal.json
237
282
  - spec/json/confucius.json_api.json
238
- - spec/json/hal_plant_collection.json
239
283
  - spec/json/john.hal.json
284
+ - spec/json/plant_collection.collection.json
285
+ - spec/json/plant_collection.hal.json
240
286
  - spec/json/youtypeitwepostit.collection.json
241
287
  - spec/spec_helper.rb
242
288
  - spec/support/classes_for_policy_testing.rb
@@ -254,9 +300,11 @@ files:
254
300
  - spec/unit/yaks/config_spec.rb
255
301
  - spec/unit/yaks/default_policy/derive_mapper_from_object_spec.rb
256
302
  - spec/unit/yaks/default_policy_spec.rb
303
+ - spec/unit/yaks/format/collection_json_spec.rb
257
304
  - spec/unit/yaks/format/hal_spec.rb
258
305
  - spec/unit/yaks/format/json_api_spec.rb
259
306
  - spec/unit/yaks/format_spec.rb
307
+ - spec/unit/yaks/fp/callable_spec.rb
260
308
  - spec/unit/yaks/fp/hash_updatable_spec.rb
261
309
  - spec/unit/yaks/fp/updatable_spec.rb
262
310
  - spec/unit/yaks/fp_spec.rb
@@ -273,6 +321,7 @@ files:
273
321
  - spec/unit/yaks/primitivize_spec.rb
274
322
  - spec/unit/yaks/resource/link_spec.rb
275
323
  - spec/unit/yaks/resource_spec.rb
324
+ - spec/unit/yaks/runner_spec.rb
276
325
  - spec/unit/yaks/util_spec.rb
277
326
  - spec/yaml/confucius.yaml
278
327
  - spec/yaml/youtypeitwepostit.yaml
@@ -284,18 +333,22 @@ metadata: {}
284
333
  post_install_message: |2+
285
334
 
286
335
 
287
- Breaking Changes in Yaks 0.4.3
336
+ Breaking Changes in Yaks 0.5.0
288
337
  ==============================
289
338
 
290
- Yaks::Mapper#filter was removed, if you override this method in your
291
- mappers to conditionally filter attributes or associations, you will
292
- have to override #attributes or #associations instead.
339
+ Yaks now serializes its output, you no longer have to convert to JSON
340
+ yourself. Use `skip :serialize' to get the old behavior, or
341
+ `json_serializer` to use a different JSON implementation.
342
+
343
+ The single `after' hook has been replaced with a set of `before',
344
+ `after', `around' and `skip' hooks.
293
345
 
294
- When specifying a rel_template, now a single {rel} placeholder is
295
- expected instead of {src} and {dest}.
346
+ If you've created your own subclass of `Yaks::Format' (previously:
347
+ `Yaks::Serializer'), then you need to update the call to
348
+ `Format.register'.
296
349
 
297
- There are other internal changes. See the CHANGELOG and README for full
298
- documentation.
350
+ These are potentially breaking changes. See the CHANGELOG and README
351
+ for full documentation.
299
352
 
300
353
  rdoc_options: []
301
354
  require_paths:
@@ -312,7 +365,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
312
365
  version: '0'
313
366
  requirements: []
314
367
  rubyforge_project:
315
- rubygems_version: 2.2.2
368
+ rubygems_version: 2.4.1
316
369
  signing_key:
317
370
  specification_version: 4
318
371
  summary: Serialize to hypermedia. HAL, JSON-API, etc.
@@ -325,8 +378,9 @@ test_files:
325
378
  - spec/json/confucius.collection.json
326
379
  - spec/json/confucius.hal.json
327
380
  - spec/json/confucius.json_api.json
328
- - spec/json/hal_plant_collection.json
329
381
  - spec/json/john.hal.json
382
+ - spec/json/plant_collection.collection.json
383
+ - spec/json/plant_collection.hal.json
330
384
  - spec/json/youtypeitwepostit.collection.json
331
385
  - spec/spec_helper.rb
332
386
  - spec/support/classes_for_policy_testing.rb
@@ -344,9 +398,11 @@ test_files:
344
398
  - spec/unit/yaks/config_spec.rb
345
399
  - spec/unit/yaks/default_policy/derive_mapper_from_object_spec.rb
346
400
  - spec/unit/yaks/default_policy_spec.rb
401
+ - spec/unit/yaks/format/collection_json_spec.rb
347
402
  - spec/unit/yaks/format/hal_spec.rb
348
403
  - spec/unit/yaks/format/json_api_spec.rb
349
404
  - spec/unit/yaks/format_spec.rb
405
+ - spec/unit/yaks/fp/callable_spec.rb
350
406
  - spec/unit/yaks/fp/hash_updatable_spec.rb
351
407
  - spec/unit/yaks/fp/updatable_spec.rb
352
408
  - spec/unit/yaks/fp_spec.rb
@@ -363,6 +419,7 @@ test_files:
363
419
  - spec/unit/yaks/primitivize_spec.rb
364
420
  - spec/unit/yaks/resource/link_spec.rb
365
421
  - spec/unit/yaks/resource_spec.rb
422
+ - spec/unit/yaks/runner_spec.rb
366
423
  - spec/unit/yaks/util_spec.rb
367
424
  - spec/yaml/confucius.yaml
368
425
  - spec/yaml/youtypeitwepostit.yaml