yaks 0.3.1 → 0.4.0.rc1
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 +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
|