yaks 0.4.2 → 0.4.3

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 (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 = {})