yaks 0.4.2 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +65 -5
  4. data/README.md +38 -8
  5. data/Rakefile +33 -0
  6. data/lib/yaks/breaking_changes.rb +22 -0
  7. data/lib/yaks/collection_mapper.rb +18 -21
  8. data/lib/yaks/collection_resource.rb +19 -5
  9. data/lib/yaks/config/dsl.rb +78 -0
  10. data/lib/yaks/config.rb +37 -63
  11. data/lib/yaks/default_policy.rb +27 -9
  12. data/lib/yaks/{serializer → format}/collection_json.rb +7 -3
  13. data/lib/yaks/{serializer → format}/hal.rb +14 -4
  14. data/lib/yaks/{serializer → format}/json_api.rb +22 -4
  15. data/lib/yaks/{serializer.rb → format.rb} +5 -5
  16. data/lib/yaks/fp/hash_updatable.rb +17 -0
  17. data/lib/yaks/fp/updatable.rb +15 -0
  18. data/lib/yaks/mapper/association.rb +24 -21
  19. data/lib/yaks/mapper/association_mapper.rb +42 -0
  20. data/lib/yaks/mapper/attribute.rb +17 -0
  21. data/lib/yaks/mapper/class_methods.rb +0 -1
  22. data/lib/yaks/mapper/config.rb +8 -28
  23. data/lib/yaks/mapper/has_many.rb +8 -3
  24. data/lib/yaks/mapper/has_one.rb +1 -1
  25. data/lib/yaks/mapper/link.rb +13 -13
  26. data/lib/yaks/mapper.rb +28 -32
  27. data/lib/yaks/null_resource.rb +1 -0
  28. data/lib/yaks/resource.rb +15 -5
  29. data/lib/yaks/version.rb +1 -1
  30. data/lib/yaks.rb +16 -10
  31. data/spec/acceptance/acceptance_spec.rb +16 -17
  32. data/spec/acceptance/json_shared_examples.rb +8 -0
  33. data/spec/acceptance/models.rb +2 -2
  34. data/spec/integration/map_to_resource_spec.rb +3 -3
  35. data/spec/json/confucius.collection.json +39 -0
  36. data/spec/json/confucius.hal.json +7 -4
  37. data/spec/json/confucius.json_api.json +1 -1
  38. data/spec/spec_helper.rb +6 -0
  39. data/spec/support/classes_for_policy_testing.rb +36 -0
  40. data/spec/support/shared_contexts.rb +1 -1
  41. data/spec/unit/yaks/collection_mapper_spec.rb +34 -9
  42. data/spec/unit/yaks/collection_resource_spec.rb +4 -4
  43. data/spec/unit/yaks/config/dsl_spec.rb +91 -0
  44. data/spec/unit/yaks/config_spec.rb +10 -6
  45. data/spec/unit/yaks/default_policy/derive_mapper_from_object_spec.rb +80 -0
  46. data/spec/unit/yaks/default_policy_spec.rb +50 -0
  47. data/spec/unit/yaks/{serializer → format}/hal_spec.rb +1 -1
  48. data/spec/unit/yaks/format/json_api_spec.rb +42 -0
  49. data/spec/unit/yaks/format_spec.rb +12 -0
  50. data/spec/unit/yaks/fp/hash_updatable_spec.rb +22 -0
  51. data/spec/unit/yaks/fp/updatable_spec.rb +22 -0
  52. data/spec/unit/yaks/mapper/association_mapper_spec.rb +60 -0
  53. data/spec/unit/yaks/mapper/association_spec.rb +96 -41
  54. data/spec/unit/yaks/mapper/attribute_spec.rb +20 -0
  55. data/spec/unit/yaks/mapper/class_methods_spec.rb +49 -10
  56. data/spec/unit/yaks/mapper/config_spec.rb +25 -50
  57. data/spec/unit/yaks/mapper/has_many_spec.rb +33 -5
  58. data/spec/unit/yaks/mapper/has_one_spec.rb +32 -17
  59. data/spec/unit/yaks/mapper/link_spec.rb +44 -12
  60. data/spec/unit/yaks/mapper_spec.rb +45 -17
  61. data/spec/unit/yaks/resource_spec.rb +41 -7
  62. data/yaks.gemspec +7 -1
  63. metadata +72 -21
  64. data/examples/hal01.rb +0 -126
  65. data/examples/jsonapi01.rb +0 -68
  66. data/examples/jsonapi02.rb +0 -62
  67. data/examples/jsonapi03.rb +0 -86
  68. data/spec/support/serializers.rb +0 -14
  69. data/spec/unit/yaks/serializer_spec.rb +0 -12
@@ -2,17 +2,29 @@ module Yaks
2
2
  class DefaultPolicy
3
3
  include Util
4
4
 
5
+ # Default policy options.
5
6
  DEFAULTS = {
6
- rel_template: "rel:src={src}&dest={dest}",
7
+ rel_template: "rel:{rel}",
7
8
  namespace: Kernel
8
9
  }
9
10
 
11
+ # @!attribute [r] options
12
+ # @return [Hash]
10
13
  attr_reader :options
11
14
 
15
+ # @param [Hash] options
16
+ # @return [Yaks::DefaultPolicy]
12
17
  def initialize(options = {})
13
18
  @options = DEFAULTS.merge(options)
14
19
  end
15
20
 
21
+ # @param [Object] model
22
+ # @return [Yaks::CollectionMapper, Yaks::Mapper]
23
+ # or a subclass of Yaks::Mapper of some sort.
24
+ #
25
+ # @raise [NameError] only occurs when the model
26
+ # is anything but a collection.
27
+ #
16
28
  def derive_mapper_from_object(model)
17
29
  if model.respond_to? :to_ary
18
30
  if m = model.first
@@ -26,30 +38,36 @@ module Yaks
26
38
  return @options[:namespace].const_get(:CollectionMapper)
27
39
  rescue NameError
28
40
  end
29
- Yaks::CollectionMapper
41
+ CollectionMapper
30
42
  else
31
43
  name = model.class.name.split('::').last
32
44
  @options[:namespace].const_get(name + 'Mapper')
33
45
  end
34
46
  end
35
47
 
48
+ # @param [Class] mapper_class
49
+ # @return [String]
36
50
  def derive_type_from_mapper_class(mapper_class)
37
51
  underscore(mapper_class.name.split('::').last.sub(/Mapper$/, ''))
38
52
  end
39
53
 
54
+ # @param [Yaks::Mapper::Association] association
55
+ # @return [Class] of subclass Yaks::Mapper
56
+ # @raise [NameError]
40
57
  def derive_mapper_from_association(association)
41
58
  @options[:namespace].const_get("#{camelize(association.singular_name)}Mapper")
42
59
  end
43
60
 
44
- def derive_rel_from_association(mapper, association)
45
- expand_rel( derive_type_from_mapper_class(mapper.class), association.name )
61
+ # @param [Yaks::Mapper::Association] association
62
+ # @return [String]
63
+ def derive_rel_from_association(association)
64
+ expand_rel( association.name )
46
65
  end
47
66
 
48
- def expand_rel(src_name, dest_name)
49
- URITemplate.new(@options[:rel_template]).expand(
50
- src: src_name,
51
- dest: dest_name
52
- )
67
+ # @param [String] relname
68
+ # @return [String]
69
+ def expand_rel(relname)
70
+ URITemplate.new(@options[:rel_template]).expand(rel: relname)
53
71
  end
54
72
  end
55
73
  end
@@ -1,10 +1,12 @@
1
1
  module Yaks
2
- class Serializer
2
+ class Format
3
3
  class CollectionJson < self
4
- Serializer.register self, :collection_json, 'application/vnd.collection+json'
4
+ Format.register self, :collection_json, 'application/vnd.collection+json'
5
5
 
6
6
  include FP
7
7
 
8
+ # @param [Yaks:Resource] resource
9
+ # @return [Hash]
8
10
  def serialize_resource(resource)
9
11
  result = {
10
12
  version: "1.0",
@@ -14,6 +16,8 @@ module Yaks
14
16
  {collection: result}
15
17
  end
16
18
 
19
+ # @param [Yaks::Resource] resource
20
+ # @return [Array]
17
21
  def serialize_items(resource)
18
22
  resource.map do |item|
19
23
  attrs = item.attributes.map do |name, value|
@@ -26,7 +30,7 @@ module Yaks
26
30
  result[:href] = item.self_link.uri if item.self_link
27
31
  item.links.each do |link|
28
32
  next if link.rel == :self
29
- result[:links] ||= []
33
+ result[:links] = [] unless result.key?(:links)
30
34
  result[:links] << {rel: link.rel, href: link.uri}
31
35
  result[:links].last[:name] = link.name if link.name
32
36
  end
@@ -1,12 +1,14 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  module Yaks
4
- class Serializer
4
+ class Format
5
5
  class Hal < self
6
- Serializer.register self, :hal, 'application/hal+json'
6
+ Format.register self, :hal, 'application/hal+json'
7
7
 
8
8
  protected
9
9
 
10
+ # @param [Yaks::Resource] resource
11
+ # @return [Hash]
10
12
  def serialize_resource(resource)
11
13
  # The HAL spec doesn't say explicitly how to deal missing values,
12
14
  # looking at client behavior (Hyperagent) it seems safer to return an empty
@@ -19,14 +21,18 @@ module Yaks
19
21
  result
20
22
  end
21
23
 
24
+ # @param [Array] links
25
+ # @return [Hash]
22
26
  def serialize_links(links)
23
27
  links.reduce({}, &method(:serialize_link))
24
28
  end
25
29
 
30
+ # @param [Hash] memo
31
+ # @param [Yaks::Resource::Link]
32
+ # @return [Hash]
26
33
  def serialize_link(memo, link)
27
34
  hal_link = {href: link.uri}
28
- hal_link.merge!(link.options.reject{|k,_| k==:templated})
29
- hal_link.merge!(templated: true) if link.templated?
35
+ hal_link.merge!(link.options)
30
36
 
31
37
  memo[link.rel] = if singular?(link.rel)
32
38
  hal_link
@@ -36,10 +42,14 @@ module Yaks
36
42
  memo
37
43
  end
38
44
 
45
+ # @param [String] rel
46
+ # @return [Boolean]
39
47
  def singular?(rel)
40
48
  !options.fetch(:plural_links) { [] }.include?(rel)
41
49
  end
42
50
 
51
+ # @param [Array] subresources
52
+ # @return [Hash]
43
53
  def serialize_embedded(subresources)
44
54
  subresources.each_with_object({}) do |(rel, resources), memo|
45
55
  memo[rel] = if resources.collection?
@@ -1,12 +1,14 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  module Yaks
4
- class Serializer
4
+ class Format
5
5
  class JsonApi < self
6
- Serializer.register self, :json_api, 'application/vnd.api+json'
6
+ Format.register self, :json_api, 'application/vnd.api+json'
7
7
 
8
8
  include FP
9
9
 
10
+ # @param [Yaks::Resource] resource
11
+ # @return [Hash]
10
12
  def call(resource)
11
13
  main_collection = resource.map(&method(:serialize_resource))
12
14
 
@@ -18,6 +20,8 @@ module Yaks
18
20
  end
19
21
  end
20
22
 
23
+ # @param [Yaks::Resource] resource
24
+ # @return [Hash]
21
25
  def serialize_resource(resource)
22
26
  result = resource.attributes
23
27
 
@@ -32,24 +36,34 @@ module Yaks
32
36
  result
33
37
  end
34
38
 
39
+ # @param [Yaks::Resource] subresource
40
+ # @return [Hash]
35
41
  def serialize_links(subresources)
36
- subresources.each_with_object({}) do |(name, resource), hsh|
42
+ subresources.each_with_object({}) do |(_name, resource), hsh|
37
43
  next if resource.is_a? NullResource
38
44
  key = resource.collection? ? pluralize(resource.type) : resource.type
39
45
  hsh[key] = serialize_link(resource)
40
46
  end
41
47
  end
42
48
 
49
+ # @param [Yaks::Resource] resource
50
+ # @return [Array, String]
43
51
  def serialize_link(resource)
44
52
  resource.collection? ? resource.map(&send_with_args(:[], :id)) : resource[:id]
45
53
  end
46
54
 
55
+ # @param [Hash] subresources
56
+ # @param [Hash] hsh
57
+ # @return [Hash]
47
58
  def serialize_linked_subresources(subresources, hsh)
48
- subresources.each_with_object(hsh) do |(name, resources), hsh|
59
+ subresources.values.each do |resources|
49
60
  serialize_linked_resources(resources, hsh)
50
61
  end
51
62
  end
52
63
 
64
+ # @param [Array] resources
65
+ # @param [Hash] linked
66
+ # @return [Hash]
53
67
  def serialize_linked_resources(resources, linked)
54
68
  resources.each_with_object(linked) do |resource, memo|
55
69
  serialize_subresource(resource, memo)
@@ -57,6 +71,10 @@ module Yaks
57
71
  end
58
72
 
59
73
  # {shows => [{id: 3, name: 'foo'}]}
74
+ #
75
+ # @param [Yaks::Resource] resource
76
+ # @param [Hash] linked
77
+ # @return [Hash]
60
78
  def serialize_subresource(resource, linked)
61
79
  key = pluralize(resource.type)
62
80
  set = linked.fetch(key) { Set.new }
@@ -1,5 +1,5 @@
1
1
  module Yaks
2
- class Serializer
2
+ class Format
3
3
  extend Forwardable
4
4
  include Util
5
5
 
@@ -9,7 +9,7 @@ module Yaks
9
9
  protected :links, :attributes, :subresources, :options
10
10
 
11
11
  def initialize(options = {})
12
- @options = YAKS_DEFAULT_OPTIONS.merge(options)
12
+ @options = options
13
13
  end
14
14
 
15
15
  def call(resource)
@@ -19,15 +19,15 @@ module Yaks
19
19
 
20
20
  class << self
21
21
  def register(klass, name, mime_type)
22
- @serializers ||= {}
23
- @serializers[name] = klass
22
+ @formats ||= {}
23
+ @formats[name] = klass
24
24
 
25
25
  @mime_types ||= {}
26
26
  @mime_types[mime_type] = [name, klass]
27
27
  end
28
28
 
29
29
  def by_name(name)
30
- @serializers.fetch(name)
30
+ @formats.fetch(name)
31
31
  end
32
32
 
33
33
  def by_mime_type(mime_type)
@@ -0,0 +1,17 @@
1
+ module Yaks
2
+ module FP
3
+
4
+ class HashUpdatable < Module
5
+ def initialize(*attributes)
6
+ define_method :update do |updates|
7
+ self.class.new(
8
+ attributes.each_with_object({}) {|attr, hsh|
9
+ hsh[attr] = updates.fetch(attr) { send(attr) }
10
+ }
11
+ )
12
+ end
13
+ end
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ module Yaks
2
+ module FP
3
+
4
+ class Updatable < Module
5
+ def initialize(*attributes)
6
+ define_method :update do |updates|
7
+ self.class.new(
8
+ *attributes.map {|attr| updates.fetch(attr) { send(attr) }}
9
+ )
10
+ end
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -1,38 +1,41 @@
1
1
  module Yaks
2
2
  class Mapper
3
3
  class Association
4
- include Equalizer.new(:name, :mapper, :rel)
4
+ include Equalizer.new(:name, :child_mapper, :rel, :href, :link_if)
5
+ include Util
5
6
 
6
- attr_reader :name, :mapper, :rel, :collection_mapper
7
+ attr_reader :name, :child_mapper, :rel, :href, :link_if
7
8
 
8
- def initialize(name, mapper, rel, collection_mapper)
9
- @name, @mapper, @rel, @collection_mapper =
10
- name, mapper, rel, collection_mapper
9
+ def initialize(options)
10
+ @name = options.fetch(:name)
11
+ @child_mapper = options.fetch(:mapper, Undefined)
12
+ @rel = options.fetch(:rel, Undefined)
13
+
14
+ @href = options.fetch(:href, Undefined)
15
+ @link_if = options.fetch(:link_if, Undefined)
11
16
  end
12
17
 
13
- # @param [#call] lookup
14
- # A callable that can retrieve an association by its name
15
- # @return Array[rel, resource]
16
- # Returns the rel (registered type or URI) + the associated, mapped resource
17
- def create_subresource(parent_mapper, lookup, context)
18
- [
19
- map_rel(parent_mapper, context.fetch(:policy)),
20
- map_resource(lookup[name], context)
21
- ]
18
+ def add_to_resource(resource, parent_mapper, context)
19
+ AssociationMapper.new(parent_mapper, self, context).call(resource)
22
20
  end
23
21
 
24
- def map_rel(parent_mapper, policy)
25
- return @rel unless @rel.equal?(Undefined)
26
- policy.derive_rel_from_association(parent_mapper, self)
22
+ def render_as_link?(parent_mapper)
23
+ href != Undefined && link_if != Undefined && Resolve(link_if, parent_mapper)
27
24
  end
28
25
 
29
- def association_mapper(policy)
30
- return @mapper unless @mapper.equal?(Undefined)
31
- policy.derive_mapper_from_association(self)
26
+ def map_rel(policy)
27
+ return rel unless rel.equal?(Undefined)
28
+ policy.derive_rel_from_association(self)
32
29
  end
33
30
 
34
31
  # @abstract
35
- def map_resource(object, context)
32
+ def map_resource(_object, _context)
33
+ end
34
+
35
+ # support for HasOne and HasMany
36
+ def resolve_association_mapper(policy)
37
+ return child_mapper unless child_mapper.equal?(Undefined)
38
+ policy.derive_mapper_from_association(self)
36
39
  end
37
40
 
38
41
  end
@@ -0,0 +1,42 @@
1
+ module Yaks
2
+ class Mapper
3
+ class AssociationMapper
4
+ attr_reader :parent_mapper, :context, :rel, :association
5
+
6
+ def initialize(parent_mapper, association, context)
7
+ @parent_mapper = parent_mapper
8
+ @association = association
9
+ @context = context.merge(
10
+ mapper_stack: context[:mapper_stack] + [parent_mapper]
11
+ )
12
+ @rel = association.map_rel(policy)
13
+ end
14
+
15
+ def policy
16
+ context.fetch(:policy)
17
+ end
18
+
19
+ def call(resource)
20
+ if association.render_as_link?(parent_mapper)
21
+ add_link(resource)
22
+ else
23
+ add_subresource(resource)
24
+ end
25
+ end
26
+
27
+ def add_link(resource)
28
+ Link.new(rel, association.href, {})
29
+ .add_to_resource(resource, parent_mapper, nil)
30
+ # Yaks::Mapper::Link doesn't do anything with the context, making it
31
+ # hard to test that we pass it a context. Passing nil for now, until
32
+ # this is actually needed and can be tested.
33
+ end
34
+
35
+ def add_subresource(resource)
36
+ object = parent_mapper.load_association(association.name)
37
+ subresource = association.map_resource(object, context)
38
+ resource.add_subresource(rel, subresource)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,17 @@
1
+ module Yaks
2
+ class Mapper
3
+ class Attribute
4
+ include Equalizer.new(:name)
5
+
6
+ attr_reader :name
7
+
8
+ def initialize(name)
9
+ @name = name
10
+ end
11
+
12
+ def add_to_resource(resource, mapper, _context)
13
+ resource.update_attributes(name => mapper.load_attribute(name))
14
+ end
15
+ end
16
+ end
17
+ end
@@ -12,7 +12,6 @@ module Yaks
12
12
  :attributes,
13
13
  :link,
14
14
  :profile,
15
- # :embed,
16
15
  :has_one,
17
16
  :has_many
18
17
  ]
@@ -2,6 +2,7 @@ module Yaks
2
2
  class Mapper
3
3
  class Config
4
4
  include Equalizer.new(:type, :attributes, :links, :associations)
5
+ include FP::Updatable.new(:type, :attributes, :links, :associations)
5
6
 
6
7
  attr_reader :links, :associations
7
8
 
@@ -12,31 +13,22 @@ module Yaks
12
13
  @associations = associations
13
14
  end
14
15
 
15
- def updated(updates)
16
- self.class.new(
17
- updates.fetch(:type) { type },
18
- updates.fetch(:attributes) { attributes },
19
- updates.fetch(:links) { links },
20
- updates.fetch(:associations) { associations }
21
- )
22
- end
23
-
24
16
  def type(type = Undefined)
25
17
  return @type if type.equal?(Undefined)
26
- updated(type: type)
18
+ update(type: type)
27
19
  end
28
20
 
29
21
  def attributes(*attrs)
30
22
  return @attributes if attrs.empty?
31
- updated(
32
- attributes: @attributes + attrs
33
- )
23
+ update(attributes: @attributes + attrs.map(&Attribute.method(:new)))
34
24
  end
35
25
 
36
26
  def link(rel, template, options = {})
37
- updated(
38
- links: @links + [Link.new(rel, template, options)]
39
- )
27
+ update(links: @links + [Link.new(rel, template, options)])
28
+ end
29
+
30
+ def add_association(type, name, options)
31
+ update(associations: @associations + [type.new(options.merge(name: name))])
40
32
  end
41
33
 
42
34
  def has_one(name, options = {})
@@ -47,18 +39,6 @@ module Yaks
47
39
  add_association(HasMany, name, options)
48
40
  end
49
41
 
50
- def add_association(type, name, options)
51
- updated(
52
- associations: @associations + [
53
- type.new(
54
- name,
55
- options.fetch(:mapper) { Undefined },
56
- options.fetch(:rel) { Undefined },
57
- options.fetch(:collection_mapper) { Undefined },
58
- )
59
- ]
60
- )
61
- end
62
42
  end
63
43
  end
64
44
  end
@@ -3,11 +3,16 @@ module Yaks
3
3
  class HasMany < Association
4
4
  include Util
5
5
 
6
+ def initialize(options)
7
+ super
8
+ @collection_mapper = options.fetch(:collection_mapper, Undefined)
9
+ end
10
+
6
11
  def map_resource(collection, context)
7
12
  return NullResource.new(collection: true) if collection.nil?
8
- policy = context.fetch(:policy)
9
- member_mapper = association_mapper(policy)
10
- context = context.merge(member_mapper: member_mapper)
13
+ policy = context.fetch(:policy)
14
+ item_mapper = resolve_association_mapper(policy)
15
+ context = context.merge(item_mapper: item_mapper)
11
16
  collection_mapper(collection, policy).new(context).call(collection)
12
17
  end
13
18
 
@@ -2,7 +2,7 @@ module Yaks
2
2
  class Mapper
3
3
  class HasOne < Association
4
4
  def map_resource(object, context)
5
- association_mapper(context.fetch(:policy))
5
+ resolve_association_mapper(context.fetch(:policy))
6
6
  .new(context)
7
7
  .call(object)
8
8
  end
@@ -30,8 +30,10 @@ module Yaks
30
30
 
31
31
  def_delegators :uri_template, :expand_partial
32
32
 
33
- def initialize(rel, template, options)
34
- @rel, @template, @options = rel, template, options
33
+ def add_to_resource(resource, mapper, _context)
34
+ resource_link = map_to_resource_link(mapper)
35
+ return resource unless resource_link
36
+ resource.add_link(resource_link)
35
37
  end
36
38
 
37
39
  def rel?(rel)
@@ -43,13 +45,15 @@ module Yaks
43
45
  !options.fetch(:expand) { true }.equal? true
44
46
  end
45
47
 
46
- # Does this link need expansion, full or partially
47
- def expand?
48
- options.fetch(:expand) { true }
49
- end
50
-
51
48
  def template_variables
52
- options.fetch(:expand) { uri_template.variables }.map(&:to_sym)
49
+ case options.fetch(:expand) { true }
50
+ when true
51
+ uri_template.variables
52
+ when false
53
+ []
54
+ else
55
+ options[:expand]
56
+ end.map(&:to_sym)
53
57
  end
54
58
 
55
59
  def expansion_mapping(lookup)
@@ -72,11 +76,7 @@ module Yaks
72
76
  def expand_with(lookup)
73
77
  return lookup[template] if template.is_a? Symbol
74
78
 
75
- if expand?
76
- expand_partial(expansion_mapping(lookup)).to_s
77
- else
78
- template
79
- end
79
+ expand_partial(expansion_mapping(lookup)).to_s
80
80
  end
81
81
 
82
82
  def resource_link_options(mapper)
data/lib/yaks/mapper.rb CHANGED
@@ -26,39 +26,24 @@ module Yaks
26
26
  context.fetch(:mapper_stack)
27
27
  end
28
28
 
29
- def call(object)
30
- @object = object
31
-
32
- return NullResource.new if object.nil?
33
-
34
- Resource.new(
35
- type: mapper_name,
36
- attributes: map_attributes,
37
- links: map_links,
38
- subresources: map_subresources
39
- )
29
+ def self.mapper_name(policy)
30
+ config.type || policy.derive_type_from_mapper_class(self)
40
31
  end
41
32
 
42
- def map_attributes
43
- filter(attributes).each_with_object({}) do |attr, memo|
44
- memo[attr] = load_attribute(attr)
45
- end
33
+ def mapper_name
34
+ self.class.mapper_name(policy)
46
35
  end
47
36
 
48
- def map_links
49
- links.map(&send_with_args(:map_to_resource_link, self)).compact
50
- end
37
+ def call(object)
38
+ @object = object
39
+
40
+ return NullResource.new if object.nil?
51
41
 
52
- def map_subresources
53
- attributes = filter(associations.map(&:name))
54
- associations = associations().select{|assoc| attributes.include? assoc.name }
55
- associations.each_with_object({}) do |association, memo|
56
- rel, subresource = association.create_subresource(
57
- self,
58
- method(:load_association),
59
- context.merge(mapper_stack: mapper_stack + [self])
60
- )
61
- memo[rel] = subresource
42
+ [ :map_attributes,
43
+ :map_links,
44
+ :map_subresources
45
+ ].inject(Resource.new(type: mapper_name)) do |resource, method|
46
+ send(method, resource)
62
47
  end
63
48
  end
64
49
 
@@ -67,13 +52,24 @@ module Yaks
67
52
  end
68
53
  alias load_association load_attribute
69
54
 
70
- def filter(attrs)
71
- attrs
55
+ private
56
+
57
+ def map_attributes(resource)
58
+ attributes.inject(resource) do |resource, attribute|
59
+ attribute.add_to_resource(resource, self, context)
60
+ end
72
61
  end
73
62
 
74
- def mapper_name
75
- config.type || policy.derive_type_from_mapper_class(self.class)
63
+ def map_links(resource)
64
+ links.inject(resource) do |resource, mapper_link|
65
+ mapper_link.add_to_resource(resource, self, context)
66
+ end
76
67
  end
77
68
 
69
+ def map_subresources(resource)
70
+ associations.inject(resource) do |resource, association|
71
+ association.add_to_resource( resource, self, context)
72
+ end
73
+ end
78
74
  end
79
75
  end
@@ -1,5 +1,6 @@
1
1
  module Yaks
2
2
  class NullResource
3
+ include Equalizer.new(:collection?)
3
4
  include Enumerable
4
5
 
5
6
  def initialize(opts = {})