yaks 0.4.2 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +65 -5
- data/README.md +38 -8
- data/Rakefile +33 -0
- data/lib/yaks/breaking_changes.rb +22 -0
- data/lib/yaks/collection_mapper.rb +18 -21
- data/lib/yaks/collection_resource.rb +19 -5
- data/lib/yaks/config/dsl.rb +78 -0
- data/lib/yaks/config.rb +37 -63
- data/lib/yaks/default_policy.rb +27 -9
- data/lib/yaks/{serializer → format}/collection_json.rb +7 -3
- data/lib/yaks/{serializer → format}/hal.rb +14 -4
- data/lib/yaks/{serializer → format}/json_api.rb +22 -4
- data/lib/yaks/{serializer.rb → format.rb} +5 -5
- data/lib/yaks/fp/hash_updatable.rb +17 -0
- data/lib/yaks/fp/updatable.rb +15 -0
- data/lib/yaks/mapper/association.rb +24 -21
- data/lib/yaks/mapper/association_mapper.rb +42 -0
- data/lib/yaks/mapper/attribute.rb +17 -0
- data/lib/yaks/mapper/class_methods.rb +0 -1
- data/lib/yaks/mapper/config.rb +8 -28
- data/lib/yaks/mapper/has_many.rb +8 -3
- data/lib/yaks/mapper/has_one.rb +1 -1
- data/lib/yaks/mapper/link.rb +13 -13
- data/lib/yaks/mapper.rb +28 -32
- data/lib/yaks/null_resource.rb +1 -0
- data/lib/yaks/resource.rb +15 -5
- data/lib/yaks/version.rb +1 -1
- data/lib/yaks.rb +16 -10
- data/spec/acceptance/acceptance_spec.rb +16 -17
- data/spec/acceptance/json_shared_examples.rb +8 -0
- data/spec/acceptance/models.rb +2 -2
- data/spec/integration/map_to_resource_spec.rb +3 -3
- data/spec/json/confucius.collection.json +39 -0
- data/spec/json/confucius.hal.json +7 -4
- data/spec/json/confucius.json_api.json +1 -1
- data/spec/spec_helper.rb +6 -0
- data/spec/support/classes_for_policy_testing.rb +36 -0
- data/spec/support/shared_contexts.rb +1 -1
- data/spec/unit/yaks/collection_mapper_spec.rb +34 -9
- data/spec/unit/yaks/collection_resource_spec.rb +4 -4
- data/spec/unit/yaks/config/dsl_spec.rb +91 -0
- data/spec/unit/yaks/config_spec.rb +10 -6
- data/spec/unit/yaks/default_policy/derive_mapper_from_object_spec.rb +80 -0
- data/spec/unit/yaks/default_policy_spec.rb +50 -0
- data/spec/unit/yaks/{serializer → format}/hal_spec.rb +1 -1
- data/spec/unit/yaks/format/json_api_spec.rb +42 -0
- data/spec/unit/yaks/format_spec.rb +12 -0
- data/spec/unit/yaks/fp/hash_updatable_spec.rb +22 -0
- data/spec/unit/yaks/fp/updatable_spec.rb +22 -0
- data/spec/unit/yaks/mapper/association_mapper_spec.rb +60 -0
- data/spec/unit/yaks/mapper/association_spec.rb +96 -41
- data/spec/unit/yaks/mapper/attribute_spec.rb +20 -0
- data/spec/unit/yaks/mapper/class_methods_spec.rb +49 -10
- data/spec/unit/yaks/mapper/config_spec.rb +25 -50
- data/spec/unit/yaks/mapper/has_many_spec.rb +33 -5
- data/spec/unit/yaks/mapper/has_one_spec.rb +32 -17
- data/spec/unit/yaks/mapper/link_spec.rb +44 -12
- data/spec/unit/yaks/mapper_spec.rb +45 -17
- data/spec/unit/yaks/resource_spec.rb +41 -7
- data/yaks.gemspec +7 -1
- metadata +72 -21
- data/examples/hal01.rb +0 -126
- data/examples/jsonapi01.rb +0 -68
- data/examples/jsonapi02.rb +0 -62
- data/examples/jsonapi03.rb +0 -86
- data/spec/support/serializers.rb +0 -14
- data/spec/unit/yaks/serializer_spec.rb +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f8ee4f04ef31bd3c1e944ee723c910bf37dd97b8
|
4
|
+
data.tar.gz: 93282ee4d7a30bdb826ccc81447d86db1de245f0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d09019c49799077e500badde0c259fa41d06f2fdbfe3ec3c7c7cf71625ce786d2eb682b2b449edc8a683b8a24a20234cd3ea16716697154a58fcd88c6a6773a9
|
7
|
+
data.tar.gz: b0107900f0d00a2b30ab7d6cd701b84a59e3155961a653847b863d5cc0a569c0ff24640769b78826afad31fa6b5667cf3cf294961de82f3b1c6fbd8301f8c460
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,66 @@
|
|
1
1
|
### Development
|
2
2
|
[full changelog](http://github.com/plexus/yaks/compare/v0.4.2...master)
|
3
3
|
|
4
|
+
* when specifying a rel_template, instead of allowing for {src} and {dest} fields, now a single {rel} field is expected, which corresponds more with typical usage.
|
5
|
+
|
6
|
+
```ruby
|
7
|
+
Yaks.new do
|
8
|
+
rel_template 'http://my-api/docs/relationships/{rel}'
|
9
|
+
end
|
10
|
+
```
|
11
|
+
|
12
|
+
* Yaks::Serializer has been renamed to Yaks::Format
|
13
|
+
|
14
|
+
* Yaks::Mapper#{map_attributes,map_links,map_subresource} signature has changed, they now are responsible for adding themselves to a resource instance.
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
class FooMapper < Yaks::Mapper
|
18
|
+
def map_attributes(resource)
|
19
|
+
resource.update_attributes(:example => 'attribute')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
```
|
23
|
+
|
24
|
+
* Conditionally turn associations into links
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
class ShowMapper < Yaks::Mapper
|
28
|
+
has_many :events, href: '/show/{id}/events', link_if: ->{ events.count > 50 }
|
29
|
+
end
|
30
|
+
```
|
31
|
+
|
32
|
+
* Reify `Yaks::Mapper::Attribute`
|
33
|
+
|
34
|
+
* Remove `Yaks::Mapper#filter`, instead override `#attributes` or `#associations` to filter things out, for example:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
class SongMapper
|
38
|
+
attributes :title, :duration, :lyrics
|
39
|
+
has_one :artist
|
40
|
+
has_one :album
|
41
|
+
|
42
|
+
def minimal?
|
43
|
+
env['HTTP_PREFER'] =~ /minimal/
|
44
|
+
end
|
45
|
+
|
46
|
+
def attributes
|
47
|
+
if minimal?
|
48
|
+
super.reject {|attr| attr.name.equal? :lyrics } # These are instances of Yaks::Mapper::Attribute
|
49
|
+
else
|
50
|
+
super
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def associations
|
55
|
+
return [] if minimal?
|
56
|
+
super
|
57
|
+
end
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
61
|
+
* Give Attribute, Link, Association a common interface : `add_to_resource(resource, mapper, context)`
|
62
|
+
* Add persistent update methods to `Yaks::Resource`
|
63
|
+
|
4
64
|
### v0.4.2
|
5
65
|
|
6
66
|
* JSON-API: render self links as href attributes
|
@@ -10,13 +70,13 @@
|
|
10
70
|
* Make Yaks::CollectionMapper#collection overridable for pagination
|
11
71
|
* Don't render links from custom link methods (link :foo, :method_that_generates_url) that return nil
|
12
72
|
|
13
|
-
|
73
|
+
### v0.4.1
|
14
74
|
|
15
75
|
* Change how env is passed to yaks.serialize to match docs
|
16
76
|
* Fix JSON-API bug (#18 reported by Nicolas Blanco)
|
17
77
|
* Don't pluralize has_one association names in JSON-API
|
18
78
|
|
19
|
-
|
79
|
+
## v0.4.0
|
20
80
|
|
21
81
|
* Introduce after {} post-processing hook
|
22
82
|
* Streamline interfaces and variable names, especially the use of `call`
|
@@ -25,7 +85,7 @@
|
|
25
85
|
* Switch back to using `src` and `dest` as the rel-template keys, instead of `association_name`
|
26
86
|
* deprecate `mapper_namespace` in favor of `namespace`
|
27
87
|
|
28
|
-
|
88
|
+
### v0.4.0.rc1
|
29
89
|
|
30
90
|
* Introduce Yaks.new as the main public interface
|
31
91
|
* Fix JsonApiSerializer and make it compliant with current spec
|
@@ -36,10 +96,10 @@
|
|
36
96
|
* Honor the HTTP Accept header if it is present in the rack env
|
37
97
|
* Add map_to_primitive configuration option
|
38
98
|
|
39
|
-
|
99
|
+
## v0.3.0
|
40
100
|
|
41
101
|
* Allow partial expansion of templates, expand certain fields, leave others as URI template in the result.
|
42
102
|
|
43
|
-
|
103
|
+
## v0.2.0
|
44
104
|
|
45
105
|
* links can now take a simple for a template to compute a link just like an attribute
|
data/README.md
CHANGED
@@ -62,7 +62,7 @@ or a bit more elaborate
|
|
62
62
|
```ruby
|
63
63
|
yaks = Yaks.new do
|
64
64
|
default_format :json_api
|
65
|
-
rel_template 'http://api.example.com/rels/{
|
65
|
+
rel_template 'http://api.example.com/rels/{rel}'
|
66
66
|
format_options(:hal, plural_links: [:copyright])
|
67
67
|
end
|
68
68
|
|
@@ -100,11 +100,30 @@ end
|
|
100
100
|
|
101
101
|
#### Filtering
|
102
102
|
|
103
|
-
|
103
|
+
Implementing a `#filter` method in your mappers is no longer supported. Instead you can override `#attributes`, or `#associations`.
|
104
104
|
|
105
105
|
```ruby
|
106
|
-
|
107
|
-
|
106
|
+
class SongMapper
|
107
|
+
attributes :title, :duration, :lyrics
|
108
|
+
|
109
|
+
has_one :artist
|
110
|
+
has_one :album
|
111
|
+
|
112
|
+
def minimal?
|
113
|
+
env['HTTP_PREFER'] =~ /minimal/
|
114
|
+
end
|
115
|
+
|
116
|
+
# @return Array<Yaks::Mapper::Attribute>
|
117
|
+
def attributes
|
118
|
+
return super.reject {|attr| attr.name.equal? :lyrics } if minimal?
|
119
|
+
super
|
120
|
+
end
|
121
|
+
|
122
|
+
# @return Array<Yaks::Mapper::Association>
|
123
|
+
def associations
|
124
|
+
return [] if minimal?
|
125
|
+
super
|
126
|
+
end
|
108
127
|
end
|
109
128
|
```
|
110
129
|
|
@@ -159,6 +178,13 @@ Options
|
|
159
178
|
* `:mapper` : Use a specific for each instance, will be derived from the class name if omitted (see Policy vs Configuration)
|
160
179
|
* `:collection_mapper` : For mapping the collection as a whole, this defaults to Yaks::CollectionMapper, but you can subclass it for example to add links or attributes on the collection itself
|
161
180
|
* `:rel` : Set the relation (symbol or URI) this association has with the object. Will be derived from the association name and the configured rel_template if ommitted
|
181
|
+
* `:link_if`: Conditionally render the association as a link. A `:href` option is required
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
class ShowMapper < Yaks::Mapper
|
185
|
+
has_many :events, href: '/show/{id}/events', link_if: ->{ events.count > 50 }
|
186
|
+
end
|
187
|
+
```
|
162
188
|
|
163
189
|
## Namespace
|
164
190
|
|
@@ -236,13 +262,13 @@ If `env` contains a `HTTP_ACCEPT` key (Rack's way of representing the `Accept` h
|
|
236
262
|
|
237
263
|
## Custom attribute/link/subresource handling
|
238
264
|
|
239
|
-
When inheriting from `Yaks::Mapper`, you can override `map_attributes`, `map_links` and `map_resources` to skip (or augment) above methods, and instead implement your own custom mechanism. For example
|
265
|
+
When inheriting from `Yaks::Mapper`, you can override `map_attributes`, `map_links` and `map_resources` to skip (or augment) above methods, and instead implement your own custom mechanism. These methods take a `Yaks::Resource` instance, and should return an updated resource. They should not alter the resource instance in-place. For example
|
240
266
|
|
241
267
|
```ruby
|
242
268
|
class ErrorMapper < Yaks::Mapper
|
243
269
|
link :profile, '/api/error'
|
244
270
|
|
245
|
-
def map_attributes
|
271
|
+
def map_attributes(resource)
|
246
272
|
attrs = {
|
247
273
|
http_code: 500,
|
248
274
|
message: object.to_s,
|
@@ -257,7 +283,7 @@ class ErrorMapper < Yaks::Mapper
|
|
257
283
|
attrs[:type] = "record_not_found"
|
258
284
|
end
|
259
285
|
|
260
|
-
attrs
|
286
|
+
resource.update_attributes(attrs)
|
261
287
|
end
|
262
288
|
end
|
263
289
|
```
|
@@ -400,6 +426,10 @@ Yaks is used in production by [Ticketsolve](http://www.ticketsolve.com/). You ca
|
|
400
426
|
|
401
427
|
Get in touch if you like to see your name and API here.
|
402
428
|
|
429
|
+
## Demo
|
430
|
+
|
431
|
+
You can find an example app at [Yakports](https://github.com/plexus/yakports), or browse the HAL api directly using the [HAL browser](http://yaks-airports.herokuapp.com/browser.html).
|
432
|
+
|
403
433
|
## Acknowledgment
|
404
434
|
|
405
435
|
The mapper syntax is largely borrowed from ActiveModel::Serializers, which in turn closely mimics the syntax of ActiveRecord models. It's a great concise syntax that still offers plenty of flexibility, so to not reinvent the wheel I've stuck to the existing syntax as far as practical, although there are several extensions and deviations.
|
@@ -416,7 +446,7 @@ If this approach sounds appealing, have a look at [microrb.com](http://microrb.c
|
|
416
446
|
|
417
447
|
## How to contribute
|
418
448
|
|
419
|
-
Run the tests, the examples, try it with your own stuff and leave your impressions in the issues. Or discuss on API-craft.
|
449
|
+
Run the tests, the examples, try it with your own stuff and leave your impressions in the issues. Or discuss on [API-craft](https://groups.google.com/d/forum/api-craft).
|
420
450
|
|
421
451
|
To fix a bug
|
422
452
|
|
data/Rakefile
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'rubygems/package_task'
|
2
|
+
require 'yaks'
|
2
3
|
|
3
4
|
spec = Gem::Specification.load(Pathname.glob('*.gemspec').first.to_s)
|
4
5
|
Gem::PackageTask.new(spec).define
|
@@ -19,3 +20,35 @@ task :mutant do
|
|
19
20
|
result = Mutant::CLI.run(%w[-Ilib -ryaks --use rspec --score 100] + opts + [pattern])
|
20
21
|
fail unless result == Mutant::CLI::EXIT_SUCCESS
|
21
22
|
end
|
23
|
+
|
24
|
+
task :mutant_chunked do
|
25
|
+
[
|
26
|
+
# Yaks::Util,
|
27
|
+
# Yaks::Primitivize,
|
28
|
+
Yaks::FP,
|
29
|
+
Yaks::Resource,
|
30
|
+
Yaks::NullResource,
|
31
|
+
Yaks::CollectionResource,
|
32
|
+
Yaks::Mapper::Association,
|
33
|
+
Yaks::Mapper::AssociationMapper,
|
34
|
+
Yaks::Mapper::HasMany,
|
35
|
+
Yaks::Mapper::HasOne,
|
36
|
+
Yaks::Mapper::Config,
|
37
|
+
Yaks::Mapper::ClassMethods,
|
38
|
+
Yaks::Mapper::Attribute,
|
39
|
+
Yaks::Format,
|
40
|
+
Yaks::Config::DSL,
|
41
|
+
Yaks::CollectionMapper,
|
42
|
+
Yaks::Mapper::Link,
|
43
|
+
Yaks::Format::JsonApi,
|
44
|
+
Yaks::DefaultPolicy, # 45/249 (81.93%)
|
45
|
+
Yaks::Format::CollectionJson, # 15/183 (91.80%)
|
46
|
+
Yaks::Format::Hal, # 17/209 (91.87%)
|
47
|
+
Yaks::Mapper, # 12/203 (94.09%)
|
48
|
+
Yaks::Config, # 12/263 (95.44%)
|
49
|
+
].each do |space|
|
50
|
+
puts space
|
51
|
+
ENV['PATTERN'] = "#{space}"
|
52
|
+
Rake::Task["mutant"].execute
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Yaks
|
2
|
+
|
3
|
+
BreakingChanges = {
|
4
|
+
'0.4.3' => %q~
|
5
|
+
|
6
|
+
Breaking Changes in Yaks 0.4.3
|
7
|
+
==============================
|
8
|
+
|
9
|
+
Yaks::Mapper#filter was removed, if you override this method in your
|
10
|
+
mappers to conditionally filter attributes or associations, you will
|
11
|
+
have to override #attributes or #associations instead.
|
12
|
+
|
13
|
+
When specifying a rel_template, now a single {rel} placeholder is
|
14
|
+
expected instead of {src} and {dest}.
|
15
|
+
|
16
|
+
There are other internal changes. See the CHANGELOG and README for full
|
17
|
+
documentation.
|
18
|
+
|
19
|
+
~
|
20
|
+
}
|
21
|
+
|
22
|
+
end
|
@@ -4,48 +4,45 @@ module Yaks
|
|
4
4
|
class CollectionMapper < Mapper
|
5
5
|
alias collection object
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
end
|
10
|
-
|
11
|
-
def member_mapper
|
12
|
-
context.fetch(:member_mapper) do
|
13
|
-
if collection.first
|
14
|
-
mapper_for_model(collection.first)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
7
|
+
# @param [Array] collection
|
8
|
+
# @return [Array]
|
19
9
|
def call(collection)
|
20
10
|
@object = collection
|
21
11
|
|
22
12
|
attrs = {
|
23
13
|
type: collection_type,
|
24
|
-
links: map_links,
|
25
|
-
attributes: map_attributes,
|
26
14
|
members: collection().map do |obj|
|
27
15
|
mapper_for_model(obj).new(context).call(obj)
|
28
16
|
end
|
29
17
|
}
|
30
18
|
|
31
|
-
attrs[ :
|
19
|
+
attrs[ :collection_rel ] = collection_rel
|
32
20
|
|
33
|
-
|
21
|
+
map_attributes(
|
22
|
+
map_links(
|
23
|
+
CollectionResource.new(attrs)
|
24
|
+
)
|
25
|
+
)
|
34
26
|
end
|
35
27
|
|
36
28
|
private
|
37
29
|
|
38
|
-
def
|
39
|
-
|
30
|
+
def collection_rel
|
31
|
+
if collection_type
|
32
|
+
policy.expand_rel( pluralize( collection_type ) )
|
33
|
+
else
|
34
|
+
'collection'
|
35
|
+
end
|
40
36
|
end
|
41
37
|
|
42
38
|
def collection_type
|
43
|
-
|
44
|
-
|
39
|
+
if item_mapper = context[:item_mapper]
|
40
|
+
item_mapper.config.type || policy.derive_type_from_mapper_class(item_mapper)
|
41
|
+
end
|
45
42
|
end
|
46
43
|
|
47
44
|
def mapper_for_model(model)
|
48
|
-
context.fetch(:
|
45
|
+
context.fetch(:item_mapper) do
|
49
46
|
policy.derive_mapper_from_object(model)
|
50
47
|
end
|
51
48
|
end
|
@@ -15,19 +15,30 @@ module Yaks
|
|
15
15
|
# In the second case a collection has a single "subresource", being its
|
16
16
|
# members.
|
17
17
|
class CollectionResource < Resource
|
18
|
-
include Equalizer.new(:type, :links, :attributes, :members, :
|
18
|
+
include Equalizer.new(:type, :links, :attributes, :members, :collection_rel)
|
19
|
+
include FP::HashUpdatable.new(:type, :links, :attributes, :members, :collection_rel)
|
19
20
|
include Enumerable
|
20
21
|
|
21
22
|
extend Forwardable
|
22
23
|
|
23
|
-
|
24
|
+
# @!attribute [r] type
|
25
|
+
# @return [String]
|
26
|
+
# @!attribute [r] links
|
27
|
+
# @return [Array]
|
28
|
+
# @!attribute [r] members
|
29
|
+
# @return [Array]
|
30
|
+
# @!attribute [r] collection_rel
|
31
|
+
# @return [String]
|
32
|
+
attr_reader :type, :links, :members, :collection_rel
|
24
33
|
|
25
34
|
def_delegators :members, :each
|
26
35
|
|
36
|
+
# @param [Hash] options
|
37
|
+
# @return [CollectionResource]
|
27
38
|
def initialize(options)
|
28
39
|
super
|
29
40
|
@members = options.fetch(:members, [])
|
30
|
-
@
|
41
|
+
@collection_rel = options.fetch(:collection_rel, 'members')
|
31
42
|
end
|
32
43
|
|
33
44
|
# Make a CollectionResource quack like a resource.
|
@@ -37,20 +48,23 @@ module Yaks
|
|
37
48
|
# whatever it gets as a single resource with links and subresources,
|
38
49
|
# we just push the collection down one level.
|
39
50
|
#
|
40
|
-
# Once inside subresources the HAL
|
51
|
+
# Once inside subresources the HAL format does check if a resource
|
41
52
|
# is a collection, since there it does make a distinction, and because
|
42
53
|
# in that case it will iterate with each/map rather than calling subresources,
|
43
54
|
# this doesn't cause infinite recursion. Not very pretty, needs looking at.
|
44
55
|
#
|
45
56
|
# :(
|
57
|
+
#
|
58
|
+
# @return [Hash]
|
46
59
|
def subresources
|
47
60
|
if any?
|
48
|
-
{
|
61
|
+
{ collection_rel => self }
|
49
62
|
else
|
50
63
|
{}
|
51
64
|
end
|
52
65
|
end
|
53
66
|
|
67
|
+
# @return [Boolean]
|
54
68
|
def collection?
|
55
69
|
true
|
56
70
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Yaks
|
2
|
+
class Config
|
3
|
+
class DSL
|
4
|
+
# @!attribute [r] config
|
5
|
+
# @return [Yaks::Config]
|
6
|
+
attr_reader :config
|
7
|
+
|
8
|
+
# @param [Yaks::Config] config
|
9
|
+
# @param [Proc] blk
|
10
|
+
# @return [Yaks::Config::DSL]
|
11
|
+
def initialize(config, &blk)
|
12
|
+
@config = config
|
13
|
+
@policy_class = Class.new(DefaultPolicy)
|
14
|
+
@policies = []
|
15
|
+
instance_eval(&blk) if blk
|
16
|
+
@policies.each do |policy_blk|
|
17
|
+
@policy_class.class_eval &policy_blk
|
18
|
+
end
|
19
|
+
config.policy_class = @policy_class
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param [Symbol] format
|
23
|
+
# @return [Symbol]
|
24
|
+
def format_options(format, options)
|
25
|
+
config.format_options[format] = options
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param [Symbol] format
|
29
|
+
# @return [Symbol]
|
30
|
+
def default_format(format)
|
31
|
+
config.default_format = format
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param [Object] klass
|
35
|
+
# @return [Object]
|
36
|
+
def policy(klass)
|
37
|
+
@policy_class = klass
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param [String] templ
|
41
|
+
# @return [String]
|
42
|
+
def rel_template(templ)
|
43
|
+
config.policy_options[:rel_template] = templ
|
44
|
+
end
|
45
|
+
|
46
|
+
# @param [Object] namespace
|
47
|
+
# @return [Object]
|
48
|
+
def mapper_namespace(namespace)
|
49
|
+
config.policy_options[:namespace] = namespace
|
50
|
+
end
|
51
|
+
alias namespace mapper_namespace
|
52
|
+
|
53
|
+
# @param [Array] args
|
54
|
+
# @param [Proc] blk
|
55
|
+
# @return [Array]
|
56
|
+
def map_to_primitive(*args, &blk)
|
57
|
+
config.primitivize.map(*args, &blk)
|
58
|
+
end
|
59
|
+
|
60
|
+
# @param [Proc] block
|
61
|
+
# @return [Array]
|
62
|
+
def after(&block)
|
63
|
+
config.steps << block
|
64
|
+
end
|
65
|
+
|
66
|
+
# Will define each method available in the DefaultPolicy upon the DSL
|
67
|
+
# and then make it available to apply to any Class taking on the
|
68
|
+
# `@policies` Array.
|
69
|
+
DefaultPolicy.public_instance_methods(false).each do |method|
|
70
|
+
define_method method do |&blk|
|
71
|
+
@policies << proc {
|
72
|
+
define_method method, &blk
|
73
|
+
}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/yaks/config.rb
CHANGED
@@ -1,85 +1,56 @@
|
|
1
1
|
module Yaks
|
2
2
|
class Config
|
3
|
-
class DSL
|
4
|
-
attr_reader :config
|
5
|
-
|
6
|
-
def initialize(config, &blk)
|
7
|
-
@config = config
|
8
|
-
@policy_class = Class.new(DefaultPolicy)
|
9
|
-
@policies = []
|
10
|
-
instance_eval(&blk) if blk
|
11
|
-
@policies.each do |policy_blk|
|
12
|
-
@policy_class.class_eval &policy_blk
|
13
|
-
end
|
14
|
-
config.policy_class = @policy_class
|
15
|
-
end
|
16
|
-
|
17
|
-
def format_options(format, options)
|
18
|
-
config.format_options[format] = options
|
19
|
-
end
|
20
|
-
|
21
|
-
def default_format(format)
|
22
|
-
config.default_format = format
|
23
|
-
end
|
24
|
-
|
25
|
-
def policy(klass)
|
26
|
-
@policy_class = klass
|
27
|
-
end
|
28
|
-
|
29
|
-
def rel_template(templ)
|
30
|
-
config.policy_options[:rel_template] = templ
|
31
|
-
end
|
32
|
-
|
33
|
-
def mapper_namespace(namespace)
|
34
|
-
config.policy_options[:namespace] = namespace
|
35
|
-
end
|
36
|
-
alias namespace mapper_namespace
|
37
|
-
|
38
|
-
def map_to_primitive(*args, &blk)
|
39
|
-
config.primitivize.map(*args, &blk)
|
40
|
-
end
|
41
|
-
|
42
|
-
def after(&block)
|
43
|
-
config.steps << block
|
44
|
-
end
|
45
|
-
|
46
|
-
DefaultPolicy.public_instance_methods(false).each do |method|
|
47
|
-
define_method method do |&blk|
|
48
|
-
@policies << proc {
|
49
|
-
define_method method, &blk
|
50
|
-
}
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
3
|
|
4
|
+
# @!attribute [rw] format_options
|
5
|
+
# @return [Hash]
|
6
|
+
# @!attribute [rw] default_format
|
7
|
+
# @return [Symbol]
|
8
|
+
# @!attribute [rw] policy_class
|
9
|
+
# @return [Constant]
|
10
|
+
# @!attribute [rw] policy_options
|
11
|
+
# @return [Hash]
|
12
|
+
# @!attribute [rw] primitivize
|
13
|
+
# @return [Boolean]
|
14
|
+
# @!attribute [rw] steps
|
15
|
+
# @return [Array]
|
55
16
|
attr_accessor :format_options, :default_format, :policy_class, :policy_options, :primitivize, :steps
|
56
17
|
|
18
|
+
# @param [Proc] blk
|
19
|
+
# @return [Yaks::Config]
|
57
20
|
def initialize(&blk)
|
58
21
|
@format_options = Hash.new({})
|
59
22
|
@default_format = :hal
|
60
23
|
@policy_options = {}
|
61
24
|
@primitivize = Primitivize.create
|
62
|
-
@steps = [
|
25
|
+
@steps = [
|
26
|
+
@primitivize
|
27
|
+
]
|
63
28
|
DSL.new(self, &blk)
|
64
29
|
end
|
65
30
|
|
31
|
+
# @return [Yaks::DefaultPolicy, Object]
|
66
32
|
def policy
|
67
33
|
@policy_class.new(@policy_options)
|
68
34
|
end
|
69
35
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
36
|
+
# @param [Hash] opts
|
37
|
+
# @param [Hash] env
|
38
|
+
# @return [Yaks::Format::CollectionJson, Yaks::Format::Hal, Yaks::Format::JsonApi]
|
39
|
+
def format_class(opts, env)
|
40
|
+
accept = Rack::Accept::Charset.new(env['HTTP_ACCEPT'])
|
41
|
+
mime_type = accept.best_of(Format.mime_types.values)
|
42
|
+
return Format.by_mime_type(mime_type) if mime_type
|
43
|
+
Format.by_name(opts.fetch(:format) { @default_format })
|
77
44
|
end
|
78
45
|
|
46
|
+
# @param [Hash] opts
|
47
|
+
# @return [String]
|
79
48
|
def format_name(opts)
|
80
49
|
opts.fetch(:format) { @default_format }
|
81
50
|
end
|
82
51
|
|
52
|
+
# @param [Symbol] format
|
53
|
+
# @return [Object]
|
83
54
|
def options_for_format(format)
|
84
55
|
format_options[format]
|
85
56
|
end
|
@@ -87,7 +58,10 @@ module Yaks
|
|
87
58
|
# model => Yaks::Resource
|
88
59
|
# Yaks::Resource => serialized structure
|
89
60
|
# serialized structure => serialized flat
|
90
|
-
|
61
|
+
#
|
62
|
+
# @param [Object] object
|
63
|
+
# @param [Hash] opts
|
64
|
+
# @return [Object]
|
91
65
|
def call(object, opts = {})
|
92
66
|
env = opts.fetch(:env, {})
|
93
67
|
context = {
|
@@ -96,10 +70,10 @@ module Yaks
|
|
96
70
|
mapper_stack: []
|
97
71
|
}
|
98
72
|
|
99
|
-
mapper
|
100
|
-
|
73
|
+
mapper = opts.fetch(:mapper) { policy.derive_mapper_from_object(object) }.new(context)
|
74
|
+
format = format_class(opts, env).new(format_options[format_name(opts)])
|
101
75
|
|
102
|
-
[ mapper,
|
76
|
+
[ mapper, format, *steps ].inject(object) {|memo, step| step.call(memo) }
|
103
77
|
end
|
104
78
|
alias serialize call
|
105
79
|
end
|