yaks 0.3.1 → 0.4.0.rc1
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +0 -2
- data/LICENSE +7 -0
- data/README.md +160 -35
- data/Rakefile +2 -1
- data/lib/yaks/collection_mapper.rb +25 -18
- data/lib/yaks/collection_resource.rb +11 -17
- data/lib/yaks/config.rb +96 -0
- data/lib/yaks/default_policy.rb +34 -4
- data/lib/yaks/fp.rb +18 -0
- data/lib/yaks/mapper/association.rb +19 -27
- data/lib/yaks/mapper/class_methods.rb +4 -2
- data/lib/yaks/mapper/config.rb +24 -39
- data/lib/yaks/mapper/has_many.rb +7 -6
- data/lib/yaks/mapper/has_one.rb +4 -3
- data/lib/yaks/mapper/link.rb +52 -55
- data/lib/yaks/mapper.rb +38 -26
- data/lib/yaks/null_resource.rb +3 -3
- data/lib/yaks/primitivize.rb +29 -27
- data/lib/yaks/resource/link.rb +4 -0
- data/lib/yaks/resource.rb +18 -7
- data/lib/yaks/serializer/collection_json.rb +38 -0
- data/lib/yaks/serializer/hal.rb +55 -0
- data/lib/yaks/serializer/json_api.rb +61 -0
- data/lib/yaks/serializer.rb +25 -4
- data/lib/yaks/util.rb +2 -42
- data/lib/yaks/version.rb +1 -1
- data/lib/yaks.rb +10 -32
- data/notes.org +72 -0
- data/shaved_yak.gif +0 -0
- data/spec/acceptance/acceptance_spec.rb +46 -0
- data/spec/acceptance/models.rb +28 -0
- data/spec/integration/map_to_resource_spec.rb +11 -15
- data/spec/json/confucius.hal.json +23 -0
- data/spec/json/confucius.json_api.json +22 -0
- data/spec/json/john.hal.json +29 -0
- data/spec/json/youtypeitwepostit.collection.json +45 -0
- data/spec/spec_helper.rb +12 -1
- data/spec/support/shared_contexts.rb +7 -10
- data/spec/support/youtypeit_models_mappers.rb +20 -0
- data/spec/unit/yaks/collection_mapper_spec.rb +84 -0
- data/spec/unit/yaks/collection_resource_spec.rb +72 -0
- data/spec/unit/yaks/config_spec.rb +129 -0
- data/spec/unit/yaks/fp_spec.rb +31 -0
- data/spec/unit/yaks/mapper/association_spec.rb +80 -0
- data/spec/{yaks → unit/yaks}/mapper/class_methods_spec.rb +4 -4
- data/spec/unit/yaks/mapper/config_spec.rb +191 -0
- data/spec/unit/yaks/mapper/has_many_spec.rb +46 -0
- data/spec/unit/yaks/mapper/has_one_spec.rb +34 -0
- data/spec/unit/yaks/mapper/link_spec.rb +152 -0
- data/spec/unit/yaks/mapper_spec.rb +177 -0
- data/spec/unit/yaks/resource_spec.rb +40 -0
- data/spec/{yaks/hal_serializer_spec.rb → unit/yaks/serializer/hal_spec.rb} +2 -2
- data/spec/unit/yaks/serializer_spec.rb +12 -0
- data/spec/unit/yaks/util_spec.rb +43 -0
- data/spec/yaml/confucius.yaml +10 -0
- data/spec/yaml/youtypeitwepostit.yaml +9 -0
- data/yaks.gemspec +7 -8
- metadata +92 -53
- data/Gemfile.lock +0 -111
- data/lib/yaks/hal_serializer.rb +0 -59
- data/lib/yaks/json_api_serializer.rb +0 -59
- data/lib/yaks/link_lookup.rb +0 -23
- data/lib/yaks/mapper/lookup.rb +0 -19
- data/lib/yaks/mapper/map_links.rb +0 -17
- data/lib/yaks/profile_registry.rb +0 -60
- data/lib/yaks/rel_registry.rb +0 -20
- data/lib/yaks/shared_options.rb +0 -15
- data/spec/support/shorthands.rb +0 -22
- data/spec/yaks/collection_resource_spec.rb +0 -9
- data/spec/yaks/mapper/association_spec.rb +0 -21
- data/spec/yaks/mapper/config_spec.rb +0 -77
- data/spec/yaks/mapper/has_one_spec.rb +0 -16
- data/spec/yaks/mapper/link_spec.rb +0 -38
- data/spec/yaks/mapper/map_links_spec.rb +0 -46
- data/spec/yaks/mapper_spec.rb +0 -65
- data/spec/yaks/resource_spec.rb +0 -23
@@ -1,48 +1,40 @@
|
|
1
1
|
module Yaks
|
2
2
|
class Mapper
|
3
3
|
class Association
|
4
|
-
include Equalizer.new(:name, :
|
5
|
-
include SharedOptions
|
4
|
+
include Equalizer.new(:name, :mapper, :rel)
|
6
5
|
|
7
|
-
attr_reader :name, :
|
8
|
-
private :links, :options
|
6
|
+
attr_reader :name, :mapper, :rel, :collection_mapper
|
9
7
|
|
10
|
-
def initialize(name,
|
11
|
-
@name
|
12
|
-
|
13
|
-
@mapper = mapper
|
14
|
-
@links = links
|
15
|
-
@options = options
|
8
|
+
def initialize(name, mapper, rel, collection_mapper)
|
9
|
+
@name, @mapper, @rel, @collection_mapper =
|
10
|
+
name, mapper, rel, collection_mapper
|
16
11
|
end
|
17
12
|
|
18
|
-
|
19
|
-
links.detect {|link| link.rel? :self }
|
20
|
-
end
|
21
|
-
|
22
|
-
# @param [Symbol] src_type
|
23
|
-
# The profile type of the resource that contains the association
|
24
|
-
# @param [#call] loader
|
13
|
+
# @param [#call] lookup
|
25
14
|
# A callable that can retrieve an association by its name
|
26
|
-
# @param [Hash] options
|
27
15
|
# @return Array[rel, resource]
|
28
16
|
# Returns the rel (registered type or URI) + the associated, mapped resource
|
29
|
-
def
|
17
|
+
def create_subresource(parent_mapper, lookup, context)
|
30
18
|
[
|
31
|
-
|
32
|
-
map_resource(
|
19
|
+
map_rel(parent_mapper, context.fetch(:policy)),
|
20
|
+
map_resource(lookup[name], context)
|
33
21
|
]
|
34
22
|
end
|
35
23
|
|
36
|
-
|
24
|
+
def map_rel(parent_mapper, policy)
|
25
|
+
return @rel unless @rel.equal?(Undefined)
|
26
|
+
policy.derive_rel_from_association(parent_mapper, self)
|
27
|
+
end
|
37
28
|
|
38
|
-
def
|
39
|
-
return
|
40
|
-
|
29
|
+
def association_mapper(policy)
|
30
|
+
return @mapper unless @mapper.equal?(Undefined)
|
31
|
+
policy.derive_mapper_from_association(self)
|
41
32
|
end
|
42
33
|
|
43
|
-
|
44
|
-
|
34
|
+
# @abstract
|
35
|
+
def map_resource(object, context)
|
45
36
|
end
|
37
|
+
|
46
38
|
end
|
47
39
|
end
|
48
40
|
end
|
@@ -5,8 +5,10 @@ module Yaks
|
|
5
5
|
module ClassMethods
|
6
6
|
include Forwardable
|
7
7
|
include Util
|
8
|
+
include FP
|
8
9
|
|
9
10
|
CONFIG_METHODS = [
|
11
|
+
:type,
|
10
12
|
:attributes,
|
11
13
|
:link,
|
12
14
|
:profile,
|
@@ -16,7 +18,7 @@ module Yaks
|
|
16
18
|
]
|
17
19
|
|
18
20
|
def config
|
19
|
-
@config ||= Config.new
|
21
|
+
@config ||= Config.new nil, [], [], []
|
20
22
|
@config = yield(@config) if block_given?
|
21
23
|
@config
|
22
24
|
end
|
@@ -27,7 +29,7 @@ module Yaks
|
|
27
29
|
|
28
30
|
CONFIG_METHODS.each do |method_name|
|
29
31
|
define_method method_name do |*args|
|
30
|
-
config
|
32
|
+
config &send_with_args(method_name, *args)
|
31
33
|
end
|
32
34
|
end
|
33
35
|
|
data/lib/yaks/mapper/config.rb
CHANGED
@@ -1,50 +1,44 @@
|
|
1
1
|
module Yaks
|
2
2
|
class Mapper
|
3
3
|
class Config
|
4
|
-
include Equalizer.new(:attributes)
|
4
|
+
include Equalizer.new(:type, :attributes, :links, :associations)
|
5
5
|
|
6
|
-
|
6
|
+
attr_reader :links, :associations
|
7
|
+
|
8
|
+
def initialize(type, attributes, links, associations)
|
9
|
+
@type = type
|
7
10
|
@attributes = attributes
|
8
11
|
@links = links
|
9
12
|
@associations = associations
|
10
|
-
@profile = profile
|
11
|
-
freeze
|
12
13
|
end
|
13
14
|
|
14
|
-
def
|
15
|
+
def updated(updates)
|
15
16
|
self.class.new(
|
17
|
+
updates.fetch(:type) { type },
|
16
18
|
updates.fetch(:attributes) { attributes },
|
17
19
|
updates.fetch(:links) { links },
|
18
|
-
updates.fetch(:associations) { associations }
|
19
|
-
updates.fetch(:profile) { profile },
|
20
|
+
updates.fetch(:associations) { associations }
|
20
21
|
)
|
21
22
|
end
|
22
23
|
|
24
|
+
def type(type = Undefined)
|
25
|
+
return @type if type.equal?(Undefined)
|
26
|
+
updated(type: type)
|
27
|
+
end
|
28
|
+
|
23
29
|
def attributes(*attrs)
|
24
30
|
return @attributes if attrs.empty?
|
25
|
-
|
26
|
-
attributes: @attributes + attrs
|
31
|
+
updated(
|
32
|
+
attributes: @attributes + attrs
|
27
33
|
)
|
28
34
|
end
|
29
35
|
|
30
36
|
def link(rel, template, options = {})
|
31
|
-
|
32
|
-
links: @links
|
33
|
-
)
|
34
|
-
end
|
35
|
-
|
36
|
-
def profile(type = Undefined)
|
37
|
-
return @profile if type == Undefined
|
38
|
-
new(
|
39
|
-
profile: type
|
37
|
+
updated(
|
38
|
+
links: @links + [Link.new(rel, template, options)]
|
40
39
|
)
|
41
40
|
end
|
42
41
|
|
43
|
-
# key
|
44
|
-
# embed_style
|
45
|
-
# rel
|
46
|
-
# (profile)
|
47
|
-
|
48
42
|
def has_one(name, options = {})
|
49
43
|
add_association(HasOne, name, options)
|
50
44
|
end
|
@@ -53,27 +47,18 @@ module Yaks
|
|
53
47
|
add_association(HasMany, name, options)
|
54
48
|
end
|
55
49
|
|
56
|
-
def add_association(type, name, options
|
57
|
-
|
58
|
-
associations: @associations
|
50
|
+
def add_association(type, name, options)
|
51
|
+
updated(
|
52
|
+
associations: @associations + [
|
59
53
|
type.new(
|
60
54
|
name,
|
61
|
-
options.fetch(:
|
62
|
-
options.fetch(:
|
63
|
-
options.fetch(:
|
64
|
-
options.reject {|k,v| [:as, :mapper, :links].include?(k) }
|
55
|
+
options.fetch(:mapper) { Undefined },
|
56
|
+
options.fetch(:rel) { Undefined },
|
57
|
+
options.fetch(:collection_mapper) { Undefined },
|
65
58
|
)
|
66
|
-
|
59
|
+
]
|
67
60
|
)
|
68
61
|
end
|
69
|
-
|
70
|
-
def links
|
71
|
-
@links
|
72
|
-
end
|
73
|
-
|
74
|
-
def associations
|
75
|
-
@associations
|
76
|
-
end
|
77
62
|
end
|
78
63
|
end
|
79
64
|
end
|
data/lib/yaks/mapper/has_many.rb
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
module Yaks
|
2
2
|
class Mapper
|
3
3
|
class HasMany < Association
|
4
|
-
def map_resource(collection,
|
5
|
-
|
6
|
-
|
4
|
+
def map_resource(collection, context)
|
5
|
+
resource_mapper = association_mapper(context.fetch(:policy))
|
6
|
+
context = context.merge(resource_mapper: resource_mapper)
|
7
|
+
collection_mapper.new(collection, context).to_resource
|
7
8
|
end
|
8
9
|
|
9
|
-
def collection_mapper
|
10
|
-
|
11
|
-
|
10
|
+
def collection_mapper
|
11
|
+
return @collection_mapper unless @collection_mapper.equal? Undefined
|
12
|
+
CollectionMapper
|
12
13
|
end
|
13
14
|
end
|
14
15
|
end
|
data/lib/yaks/mapper/has_one.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
module Yaks
|
2
2
|
class Mapper
|
3
3
|
class HasOne < Association
|
4
|
-
def map_resource(instance,
|
5
|
-
|
6
|
-
|
4
|
+
def map_resource(instance, context)
|
5
|
+
association_mapper(context.fetch(:policy))
|
6
|
+
.new(instance, context)
|
7
|
+
.to_resource
|
7
8
|
end
|
8
9
|
end
|
9
10
|
end
|
data/lib/yaks/mapper/link.rb
CHANGED
@@ -1,95 +1,92 @@
|
|
1
1
|
module Yaks
|
2
2
|
class Mapper
|
3
|
+
# A Yaks::Mapper::Link is part of a mapper's configuration. It captures
|
4
|
+
# what is set through the mapper's class level `#link` function, and is
|
5
|
+
# capable of generating a `Yaks::Resource::Link` for a given mapper
|
6
|
+
# instance (and hence subject).
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# link :self, 'http://api.foo.org/users/{id}', title: ->{ "User #{object.name}" }
|
10
|
+
# link :profile, 'http://apidocs.foo.org/profiles/users'
|
11
|
+
# link 'http://apidocs.foo.org/rels/friends', 'http://api.foo.org/users/{id}/friends?page={page}', expand: [:id]
|
12
|
+
#
|
13
|
+
# It takes a relationship identifier, a URI template and an options hash.
|
14
|
+
#
|
15
|
+
# @param rel [Symbol|String] Either a registered relationship type (Symbol)
|
16
|
+
# or a relationship URI. See [RFC5988 Web Linking](http://tools.ietf.org/html/rfc5988)
|
17
|
+
# @param template [String] A [RFC6570](http://tools.ietf.org/html/rfc6570) URI template
|
18
|
+
# @param template [Symbol] A method name that generates the link. No more expansion is done afterwards
|
19
|
+
# @option expand [Boolean] pass false to pass on the URI template in the response,
|
20
|
+
# instead of expanding the variables
|
21
|
+
# @option expand [Array[Symbol]] pass a list of variable names to only expand those,
|
22
|
+
# and return a partially expanded URI template in the response
|
23
|
+
# @option title [String] Give the link a title
|
24
|
+
# @option title [#to_proc] Block that returns the title. If it takes an argument,
|
25
|
+
# it will receive the mapper instance as argument. Otherwise it is evaluated in the mapper context
|
3
26
|
class Link
|
4
27
|
extend Forwardable
|
5
28
|
include Concord.new(:rel, :template, :options)
|
6
29
|
include Util
|
7
30
|
|
8
|
-
def_delegators :uri_template, :
|
31
|
+
def_delegators :uri_template, :expand_partial
|
9
32
|
|
10
|
-
def initialize(rel, template, options
|
33
|
+
def initialize(rel, template, options)
|
11
34
|
@rel, @template, @options = rel, template, options
|
12
35
|
end
|
13
36
|
|
14
37
|
def rel?(rel)
|
15
|
-
|
38
|
+
rel().eql? rel
|
16
39
|
end
|
17
40
|
|
18
|
-
|
19
|
-
|
41
|
+
# A link is templated if it does not expand, or only partially
|
42
|
+
def templated?
|
43
|
+
!options.fetch(:expand) { true }.equal? true
|
20
44
|
end
|
21
|
-
alias expand? expand
|
22
45
|
|
23
|
-
# link
|
24
|
-
def
|
25
|
-
|
46
|
+
# Does this link need expansion, full or partially
|
47
|
+
def expand?
|
48
|
+
options.fetch(:expand) { true }
|
26
49
|
end
|
27
50
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
# link 'http://link/{template}', expand: false
|
33
|
-
# link 'http://link/{template}', expand: [:only, :some, :fields]
|
34
|
-
return template unless expand?
|
51
|
+
def template_variables
|
52
|
+
options.fetch(:expand) { uri_template.variables }.map(&:to_sym)
|
53
|
+
end
|
35
54
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
uri_template.expand(expansion_mapping(lookup))
|
55
|
+
def expansion_mapping(lookup)
|
56
|
+
template_variables.map.with_object({}) do |name, hsh|
|
57
|
+
hsh[name] = lookup[name]
|
40
58
|
end
|
41
59
|
end
|
42
60
|
|
61
|
+
def uri_template
|
62
|
+
URITemplate.new(template)
|
63
|
+
end
|
64
|
+
|
43
65
|
def map_to_resource_link(mapper)
|
44
|
-
|
66
|
+
Resource::Link.new(
|
45
67
|
rel,
|
46
68
|
expand_with(mapper.method(:load_attribute)),
|
47
69
|
resource_link_options(mapper)
|
48
70
|
)
|
49
71
|
end
|
50
72
|
|
51
|
-
def
|
52
|
-
|
53
|
-
end
|
73
|
+
def expand_with(lookup)
|
74
|
+
return lookup[template] if template.is_a? Symbol
|
54
75
|
|
55
|
-
|
56
|
-
|
57
|
-
uri_template.variables & expand.map(&:to_s)
|
76
|
+
if expand?
|
77
|
+
expand_partial(expansion_mapping(lookup)).to_s
|
58
78
|
else
|
59
|
-
|
79
|
+
template
|
60
80
|
end
|
61
81
|
end
|
62
82
|
|
63
|
-
def expansion_mapping(lookup)
|
64
|
-
template_variables.map.with_object({}) do |var, hsh|
|
65
|
-
hsh[var] = lookup.call(var)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
# Link properties defined in HAL
|
70
|
-
# href
|
71
|
-
# templated
|
72
|
-
# typed
|
73
|
-
# deprecation
|
74
|
-
# name
|
75
|
-
# profile
|
76
|
-
# title
|
77
|
-
# hreflang
|
78
|
-
|
79
83
|
def resource_link_options(mapper)
|
80
|
-
options =
|
81
|
-
options = options.merge(title:
|
82
|
-
options = options.merge(
|
83
|
-
options.reject{|
|
84
|
+
options = options()
|
85
|
+
options = options.merge(title: Resolve(options[:title], mapper)) if options.key?(:title)
|
86
|
+
options = options.merge(templated: true) if templated?
|
87
|
+
options.reject{|key| key.equal? :expand }
|
84
88
|
end
|
85
89
|
|
86
|
-
def resolve_title(title_proc, mapper)
|
87
|
-
Resolve(title_proc, mapper)
|
88
|
-
end
|
89
|
-
|
90
|
-
def make_resource_link(rel, uri, options)
|
91
|
-
Resource::Link.new(rel, uri, options)
|
92
|
-
end
|
93
90
|
end
|
94
91
|
end
|
95
92
|
end
|
data/lib/yaks/mapper.rb
CHANGED
@@ -3,61 +3,73 @@
|
|
3
3
|
module Yaks
|
4
4
|
class Mapper
|
5
5
|
extend ClassMethods, Forwardable
|
6
|
-
include Util,
|
6
|
+
include Util, FP
|
7
7
|
|
8
8
|
def_delegators 'self.class', :config
|
9
9
|
def_delegators :config, :attributes, :links, :associations
|
10
10
|
|
11
|
-
attr_reader :
|
12
|
-
private :subject, :options
|
13
|
-
alias object subject
|
11
|
+
attr_reader :object, :context
|
14
12
|
|
15
|
-
def initialize(
|
16
|
-
@
|
17
|
-
@
|
13
|
+
def initialize(object, context)
|
14
|
+
@object = object
|
15
|
+
@context = context
|
18
16
|
end
|
19
17
|
|
20
|
-
def
|
21
|
-
|
18
|
+
def policy
|
19
|
+
context.fetch(:policy)
|
20
|
+
end
|
21
|
+
|
22
|
+
def env
|
23
|
+
context.fetch(:env)
|
24
|
+
end
|
25
|
+
|
26
|
+
def call
|
27
|
+
return NullResource.new if object.nil?
|
22
28
|
|
23
29
|
Resource.new(
|
24
|
-
|
25
|
-
|
26
|
-
|
30
|
+
type: mapper_name,
|
31
|
+
attributes: map_attributes,
|
32
|
+
links: map_links,
|
33
|
+
subresources: map_subresources
|
27
34
|
)
|
28
35
|
end
|
36
|
+
alias to_resource call
|
29
37
|
|
30
|
-
def
|
31
|
-
|
38
|
+
def map_attributes
|
39
|
+
filter(attributes).each_with_object({}) do |attr, memo|
|
40
|
+
memo[attr] = load_attribute(attr)
|
41
|
+
end
|
32
42
|
end
|
33
43
|
|
34
|
-
def
|
35
|
-
|
44
|
+
def map_links
|
45
|
+
links.map &send_with_args(:map_to_resource_link, self)
|
36
46
|
end
|
37
47
|
|
38
48
|
def map_subresources
|
39
|
-
attributes
|
40
|
-
associations.select{|assoc| attributes.include? assoc.name}
|
41
|
-
|
42
|
-
|
49
|
+
attributes = filter(associations.map(&:name))
|
50
|
+
associations = associations().select{|assoc| attributes.include? assoc.name }
|
51
|
+
associations.each_with_object({}) do |association, memo|
|
52
|
+
rel, subresource = association.create_subresource(
|
53
|
+
self,
|
43
54
|
method(:load_association),
|
44
|
-
|
55
|
+
context
|
45
56
|
)
|
57
|
+
memo[rel] = subresource
|
46
58
|
end
|
47
59
|
end
|
48
60
|
|
49
61
|
def load_attribute(name)
|
50
|
-
respond_to?(name) ?
|
62
|
+
respond_to?(name) ? public_send(name) : object.public_send(name)
|
51
63
|
end
|
52
64
|
alias load_association load_attribute
|
53
65
|
|
54
|
-
def profile
|
55
|
-
config.profile || policy.derive_missing_profile_from_mapper(self)
|
56
|
-
end
|
57
|
-
|
58
66
|
def filter(attrs)
|
59
67
|
attrs
|
60
68
|
end
|
61
69
|
|
70
|
+
def mapper_name
|
71
|
+
config.type || policy.derive_type_from_mapper_class(self.class)
|
72
|
+
end
|
73
|
+
|
62
74
|
end
|
63
75
|
end
|
data/lib/yaks/null_resource.rb
CHANGED
data/lib/yaks/primitivize.rb
CHANGED
@@ -1,43 +1,45 @@
|
|
1
1
|
module Yaks
|
2
2
|
class Primitivize
|
3
|
-
include Concord.new(:object)
|
4
3
|
|
5
|
-
|
6
|
-
MAPPINGS = {}
|
4
|
+
attr_reader :mappings
|
7
5
|
|
8
|
-
def
|
9
|
-
|
6
|
+
def initialize
|
7
|
+
@mappings = {}
|
10
8
|
end
|
11
9
|
|
12
|
-
def
|
13
|
-
|
14
|
-
|
10
|
+
def call(object)
|
11
|
+
mappings.each do |pattern, block|
|
12
|
+
return instance_exec(object, &block) if pattern === object
|
15
13
|
end
|
14
|
+
raise "don't know how to turn #{object.class} (#{object.inspect}) into a primitive"
|
16
15
|
end
|
17
16
|
|
18
|
-
map
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
map Symbol do
|
23
|
-
object.to_s
|
24
|
-
end
|
25
|
-
|
26
|
-
map Hash, Hamster::Hash do
|
27
|
-
object.to_enum(:each).with_object({}) do |(key, value), output|
|
28
|
-
output[self.class.(key)] = self.class.(value)
|
17
|
+
def map(*types, &blk)
|
18
|
+
types.each do |type|
|
19
|
+
mappings[type] = blk
|
29
20
|
end
|
30
21
|
end
|
31
22
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
23
|
+
def self.create
|
24
|
+
new.tap do |p|
|
25
|
+
p.map String, TrueClass, FalseClass, NilClass, Numeric do |object|
|
26
|
+
object
|
27
|
+
end
|
28
|
+
|
29
|
+
p.map Symbol do |object|
|
30
|
+
object.to_s
|
31
|
+
end
|
32
|
+
|
33
|
+
p.map Hash do |object|
|
34
|
+
object.to_enum(:each).with_object({}) do |(key, value), output|
|
35
|
+
output[call(key)] = call(value)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
p.map Enumerable do |object|
|
40
|
+
object.map(&method(:call)).to_a
|
41
|
+
end
|
39
42
|
end
|
40
|
-
raise "don't know how to turn #{object.class} (#{object.inspect}) into a primitive"
|
41
43
|
end
|
42
44
|
end
|
43
45
|
end
|
data/lib/yaks/resource/link.rb
CHANGED
data/lib/yaks/resource.rb
CHANGED
@@ -1,20 +1,24 @@
|
|
1
1
|
module Yaks
|
2
2
|
class Resource
|
3
|
-
include Equalizer.new(:links, :attributes, :subresources)
|
4
|
-
include Enumerable
|
3
|
+
include Equalizer.new(:type, :links, :attributes, :subresources)
|
4
|
+
include Enumerable
|
5
5
|
|
6
|
-
attr_reader :attributes, :links, :subresources
|
6
|
+
attr_reader :type, :attributes, :links, :subresources
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
@
|
10
|
-
@
|
11
|
-
@
|
8
|
+
def initialize(options)
|
9
|
+
@type = options.fetch(:type, nil)
|
10
|
+
@attributes = options.fetch(:attributes, {})
|
11
|
+
@links = options.fetch(:links, [])
|
12
|
+
@subresources = options.fetch(:subresources, {})
|
12
13
|
end
|
13
14
|
|
14
15
|
def [](attr)
|
15
16
|
attributes[attr]
|
16
17
|
end
|
17
18
|
|
19
|
+
# def type
|
20
|
+
# end
|
21
|
+
|
18
22
|
def each
|
19
23
|
return to_enum unless block_given?
|
20
24
|
yield self
|
@@ -23,5 +27,12 @@ module Yaks
|
|
23
27
|
def collection?
|
24
28
|
false
|
25
29
|
end
|
30
|
+
|
31
|
+
def self_link
|
32
|
+
links.find do |link|
|
33
|
+
link.rel == :self
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
26
37
|
end
|
27
38
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Yaks
|
2
|
+
class Serializer
|
3
|
+
class CollectionJson < self
|
4
|
+
Serializer.register self, :collection_json, 'application/vnd.collection+json'
|
5
|
+
|
6
|
+
include FP
|
7
|
+
|
8
|
+
def serialize_resource(resource)
|
9
|
+
result = {
|
10
|
+
version: "1.0",
|
11
|
+
items: serialize_items(resource)
|
12
|
+
}
|
13
|
+
result[:href] = resource.self_link.uri if resource.self_link
|
14
|
+
{collection: result}
|
15
|
+
end
|
16
|
+
|
17
|
+
def serialize_items(resource)
|
18
|
+
resource.map do |item|
|
19
|
+
attrs = item.attributes.map do |name, value|
|
20
|
+
{
|
21
|
+
name: name,
|
22
|
+
value: value
|
23
|
+
}
|
24
|
+
end
|
25
|
+
result = { data: attrs }
|
26
|
+
result[:href] = item.self_link.uri if item.self_link
|
27
|
+
item.links.each do |link|
|
28
|
+
next if link.rel == :self
|
29
|
+
result[:links] ||= []
|
30
|
+
result[:links] << {rel: link.rel, href: link.uri}
|
31
|
+
result[:links].last[:name] = link.name if link.name
|
32
|
+
end
|
33
|
+
result
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|