representors 0.0.5
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 +7 -0
- data/CHANGELOG.md +18 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +126 -0
- data/LICENSE.md +19 -0
- data/README.md +28 -0
- data/Rakefile +10 -0
- data/lib/representor_support/utilities.rb +39 -0
- data/lib/representors.rb +5 -0
- data/lib/representors/errors.rb +7 -0
- data/lib/representors/field.rb +108 -0
- data/lib/representors/options.rb +67 -0
- data/lib/representors/representor.rb +161 -0
- data/lib/representors/representor_builder.rb +64 -0
- data/lib/representors/representor_hash.rb +59 -0
- data/lib/representors/serialization.rb +4 -0
- data/lib/representors/serialization/deserializer_base.rb +29 -0
- data/lib/representors/serialization/deserializer_factory.rb +13 -0
- data/lib/representors/serialization/hal_deserializer.rb +44 -0
- data/lib/representors/serialization/hal_serializer.rb +91 -0
- data/lib/representors/serialization/hale_deserializer.rb +162 -0
- data/lib/representors/serialization/hale_serializer.rb +110 -0
- data/lib/representors/serialization/serialization_base.rb +27 -0
- data/lib/representors/serialization/serialization_factory_base.rb +54 -0
- data/lib/representors/serialization/serializer_base.rb +20 -0
- data/lib/representors/serialization/serializer_factory.rb +17 -0
- data/lib/representors/transition.rb +130 -0
- data/lib/representors/version.rb +4 -0
- data/spec/fixtures/complex_hal.json +92 -0
- data/spec/fixtures/complex_hale_document.json +81 -0
- data/spec/fixtures/drds_hash.rb +120 -0
- data/spec/fixtures/hale_spec_examples/basic.json +77 -0
- data/spec/fixtures/hale_spec_examples/complex_reference_objects.json +157 -0
- data/spec/fixtures/hale_spec_examples/data.json +17 -0
- data/spec/fixtures/hale_spec_examples/data_objects.json +96 -0
- data/spec/fixtures/hale_spec_examples/link_objects.json +18 -0
- data/spec/fixtures/hale_spec_examples/nested_ref.json +43 -0
- data/spec/fixtures/hale_spec_examples/reference_objects.json +89 -0
- data/spec/fixtures/hale_tutorial_examples/basic_links.json +85 -0
- data/spec/fixtures/hale_tutorial_examples/basic_links_with_orders.json +96 -0
- data/spec/fixtures/hale_tutorial_examples/basic_links_with_references.json +108 -0
- data/spec/fixtures/hale_tutorial_examples/embedded.json +182 -0
- data/spec/fixtures/hale_tutorial_examples/empty.json +1 -0
- data/spec/fixtures/hale_tutorial_examples/enctype.json +14 -0
- data/spec/fixtures/hale_tutorial_examples/final.json +141 -0
- data/spec/fixtures/hale_tutorial_examples/get_link.json +17 -0
- data/spec/fixtures/hale_tutorial_examples/get_link_with_data.json +29 -0
- data/spec/fixtures/hale_tutorial_examples/links.json +11 -0
- data/spec/fixtures/hale_tutorial_examples/links_only.json +3 -0
- data/spec/fixtures/hale_tutorial_examples/meta.json +208 -0
- data/spec/fixtures/hale_tutorial_examples/self_link.json +7 -0
- data/spec/fixtures/single_drd.rb +266 -0
- data/spec/lib/representors/complex_representor_spec.rb +288 -0
- data/spec/lib/representors/field_spec.rb +141 -0
- data/spec/lib/representors/representor_builder_spec.rb +223 -0
- data/spec/lib/representors/representor_spec.rb +285 -0
- data/spec/lib/representors/serialization/deserializer_factory_spec.rb +118 -0
- data/spec/lib/representors/serialization/hal_deserializer_spec.rb +34 -0
- data/spec/lib/representors/serialization/hal_serializer_spec.rb +171 -0
- data/spec/lib/representors/serialization/hale_deserializer_spec.rb +59 -0
- data/spec/lib/representors/serialization/hale_roundtrip_spec.rb +34 -0
- data/spec/lib/representors/serialization/hale_serializer_spec.rb +659 -0
- data/spec/lib/representors/serialization/serializer_factory_spec.rb +108 -0
- data/spec/lib/representors/transition_spec.rb +349 -0
- data/spec/spec_helper.rb +32 -0
- data/spec/support/basic-hale.json +12 -0
- data/spec/support/hal_representor_shared.rb +206 -0
- data/spec/support/helpers.rb +8 -0
- data/tasks/benchmark.rake +75 -0
- data/tasks/complex_hal_document.json +98 -0
- data/tasks/test_specs.rake +37 -0
- data/tasks/yard.rake +22 -0
- metadata +232 -0
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'enumerable/lazy' if RUBY_VERSION < '2.0'
|
3
|
+
require 'representors/field'
|
4
|
+
require 'representors/transition'
|
5
|
+
require 'representors/serialization/serializer_factory'
|
6
|
+
|
7
|
+
module Representors
|
8
|
+
##
|
9
|
+
# Manages the respresentation of hypermedia messages for different media-types.
|
10
|
+
class Representor
|
11
|
+
DEFAULT_PROTOCOL = 'http'
|
12
|
+
PROTOCOL_TEMPLATE = "%s://%s"
|
13
|
+
UNKNOWN_PROTOCOL = 'ruby_id'
|
14
|
+
VALUE_KEY = :value
|
15
|
+
META_LINK_FIELDS = ['profile', 'help', 'type', 'self']
|
16
|
+
|
17
|
+
# @example
|
18
|
+
# representor = Representors::Representor.new do |builder|
|
19
|
+
# builder.add_attribute({ name: => 'Bob' })
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# @param [Hash] hash the abstract representor hash defining a resource
|
23
|
+
def initialize(hash = {}, builder = nil)
|
24
|
+
builder ||= RepresentorBuilder.new(hash)
|
25
|
+
builder = yield builder if block_given?
|
26
|
+
@representor_hash = builder.to_representor_hash
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param format to convert this representor to
|
30
|
+
# @return the representor serialized to a particular media-type like application/hal+json
|
31
|
+
def to_media_type(format, options={})
|
32
|
+
SerializerFactory.build(format, self).to_media_type(options)
|
33
|
+
end
|
34
|
+
|
35
|
+
def empty?
|
36
|
+
@representor_hash.empty?
|
37
|
+
end
|
38
|
+
|
39
|
+
def ==(other)
|
40
|
+
other.is_a?(Hash) ? to_hash == other : to_hash == other.to_hash
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns the document for the representor
|
44
|
+
#
|
45
|
+
# @return [String] the document for the representor
|
46
|
+
def doc
|
47
|
+
@doc ||= @representor_hash.doc || ''
|
48
|
+
end
|
49
|
+
|
50
|
+
# The URI for the object
|
51
|
+
#
|
52
|
+
# @note If the URI can't be made from the provided information it constructs one from the Ruby ID
|
53
|
+
# @return [String]
|
54
|
+
def identifier
|
55
|
+
@identifier ||= begin
|
56
|
+
uri = @representor_hash.href || self.object_id
|
57
|
+
protocol = @representor_hash.protocol || (uri == self.object_id ? UNKNOWN_PROTOCOL : DEFAULT_PROTOCOL)
|
58
|
+
PROTOCOL_TEMPLATE % [protocol, uri]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# @return [Hash] The hash representation of the object
|
63
|
+
def to_hash
|
64
|
+
@to_hash ||= @representor_hash.to_h
|
65
|
+
end
|
66
|
+
|
67
|
+
# @return [String] the yaml representation of the object
|
68
|
+
def to_yaml
|
69
|
+
@to_yaml ||= YAML.dump(to_hash)
|
70
|
+
end
|
71
|
+
|
72
|
+
# @return [String] so the user can 'puts' this object
|
73
|
+
def to_s
|
74
|
+
to_hash.inspect
|
75
|
+
end
|
76
|
+
|
77
|
+
# @return [Hash] the resource attributes inferred from representor[:semantics]
|
78
|
+
def properties
|
79
|
+
@properties ||= Hash[@representor_hash.attributes.map { |k, v| [ k, v[VALUE_KEY]] }]
|
80
|
+
end
|
81
|
+
|
82
|
+
# @return [Enumerable] who's elements are all <Representors:Representor> objects
|
83
|
+
def embedded
|
84
|
+
@embedded ||= begin
|
85
|
+
embedded_representors = @representor_hash.embedded.map do |name, values|
|
86
|
+
if values.is_a?(Array)
|
87
|
+
several_representors = values.map do |value|
|
88
|
+
Representor.new(value)
|
89
|
+
end
|
90
|
+
[name, several_representors]
|
91
|
+
else
|
92
|
+
[name, Representor.new(values)]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
Hash[embedded_representors]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# @return [Array] who's elements are all <Representors:Transition> objects
|
100
|
+
def meta_links
|
101
|
+
@meta_links ||= begin
|
102
|
+
links_from_transitions = {}
|
103
|
+
|
104
|
+
transitions.each do |transition|
|
105
|
+
if META_LINK_FIELDS.include?(transition.rel)
|
106
|
+
links_from_transitions[transition.rel.to_sym] = transition.uri
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
@representor_hash.links.merge(links_from_transitions).map do |k, v|
|
111
|
+
Representors::Transition.new({rel: k, href: v})
|
112
|
+
end.uniq { |transition| [transition.rel, transition.uri] }
|
113
|
+
end
|
114
|
+
end
|
115
|
+
# @return [Array] who's elements are all <Representors:Transition> objects
|
116
|
+
def transitions
|
117
|
+
@transitions ||= begin
|
118
|
+
transition_hashes = (@representor_hash.transitions + embedded_transitions_hashes).uniq do |hash|
|
119
|
+
[hash[:rel], hash[:href]]
|
120
|
+
end
|
121
|
+
transition_hashes.map { |hash| Transition.new(hash) }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# @return [Array] who's elements are all <Representors:Transition> objects from the self links of
|
126
|
+
# embedded items, updating the rel to reflect the embedded items key
|
127
|
+
def embedded_transitions
|
128
|
+
embedded_transitions_hashes.map { |hash| Transition.new(hash) }
|
129
|
+
end
|
130
|
+
|
131
|
+
# @return [Array] who's elements are all <Representors:Option> objects
|
132
|
+
def datalists
|
133
|
+
@datalists ||= begin
|
134
|
+
attributes = transitions.map { |transition| transition.attributes }
|
135
|
+
parameters = transitions.map { |transition| transition.parameters }
|
136
|
+
fields = [attributes, parameters].flatten
|
137
|
+
options = fields.map { |field| field.options }
|
138
|
+
options.select { |o| o.datalist? }
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
def embedded_transitions_hashes
|
145
|
+
@representor_hash.embedded.flat_map do |k,*v|
|
146
|
+
v.flatten.map do |item|
|
147
|
+
trans_hash = item[:transitions].find { |t| t[:rel] == "self" }
|
148
|
+
if trans_hash
|
149
|
+
profile_href = item[:links]["profile"] if item[:links]
|
150
|
+
trans_hash = trans_hash.merge(profile: profile_href) if profile_href
|
151
|
+
trans_hash.merge(rel: k)
|
152
|
+
else
|
153
|
+
{}
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'representor_support/utilities'
|
2
|
+
require 'representors/representor_hash'
|
3
|
+
|
4
|
+
module Representors
|
5
|
+
|
6
|
+
##
|
7
|
+
# Builder has methods to abstract the construction of Representor objects
|
8
|
+
# In the present implementation it will create a hash of a specific format to
|
9
|
+
# Initialize the Representor with, this will create classess with it.
|
10
|
+
class RepresentorBuilder
|
11
|
+
include RepresentorSupport::Utilities
|
12
|
+
|
13
|
+
HREF_KEY = :href
|
14
|
+
DATA_KEY = :data
|
15
|
+
|
16
|
+
def initialize(representor_hash = {})
|
17
|
+
@representor_hash = RepresentorHash.new(representor_hash)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns a hash usable by the representor class
|
21
|
+
def to_representor_hash
|
22
|
+
@representor_hash
|
23
|
+
end
|
24
|
+
|
25
|
+
# Adds an attribute to the Representor. We are creating a hash where the keys are the
|
26
|
+
# names of the attributes
|
27
|
+
def add_attribute(name, value, options={})
|
28
|
+
new_representor_hash = RepresentorHash.new(deep_dup(@representor_hash.to_h))
|
29
|
+
new_representor_hash.attributes[name] = options.merge({value: value})
|
30
|
+
RepresentorBuilder.new(new_representor_hash)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Adds a transition to the Representor, each transition is a hash of values
|
34
|
+
# The transition collection is an Array
|
35
|
+
def add_transition(rel, href, options={})
|
36
|
+
new_representor_hash = RepresentorHash.new(deep_dup(@representor_hash.to_h))
|
37
|
+
options = symbolize_keys(options)
|
38
|
+
options.delete(:method) if options[:method] == Transition::DEFAULT_METHOD
|
39
|
+
link_values = options.merge({href: href, rel: rel})
|
40
|
+
|
41
|
+
if options[DATA_KEY]
|
42
|
+
link_values[Transition::DESCRIPTORS_KEY] = link_values.delete(DATA_KEY)
|
43
|
+
end
|
44
|
+
|
45
|
+
new_representor_hash.transitions.push(link_values)
|
46
|
+
RepresentorBuilder.new(new_representor_hash)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Adds directly an array to our array of transitions
|
50
|
+
def add_transition_array(rel, array_of_hashes)
|
51
|
+
array_of_hashes.reduce(RepresentorBuilder.new(@representor_hash)) do |memo, transition|
|
52
|
+
transition = symbolize_keys(transition)
|
53
|
+
href = transition.delete(:href)
|
54
|
+
memo = memo.add_transition(rel, href, transition)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def add_embedded(name, embedded_resource)
|
59
|
+
new_representor_hash = RepresentorHash.new(deep_dup(@representor_hash.to_h))
|
60
|
+
new_representor_hash.embedded[name] = embedded_resource
|
61
|
+
RepresentorBuilder.new(new_representor_hash)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'representor_support/utilities'
|
2
|
+
|
3
|
+
module Representors
|
4
|
+
# This is the structure shared between the builder and the representor.
|
5
|
+
# This class allows to pass all the data to the representor without polluting it with methods
|
6
|
+
# It is supposed to be a fast class (Struct is implemented in C)
|
7
|
+
# The structure looks like this:
|
8
|
+
# id: [string]
|
9
|
+
# doc: [string]
|
10
|
+
# href: [string]
|
11
|
+
# protocol: [string]
|
12
|
+
# attributes: [hash] { key => value }
|
13
|
+
# links: [array of hashes]
|
14
|
+
# transitions: [array of hashes]
|
15
|
+
# embedded: [hash] where each value can be recursively defined by this same structure
|
16
|
+
RepresentorHash = Struct.new(:id, :doc, :href, :protocol, :attributes, :embedded, :links, :transitions) do
|
17
|
+
include RepresentorSupport::Utilities
|
18
|
+
|
19
|
+
DEFAULT_HASH_VALUES = {
|
20
|
+
id: nil,
|
21
|
+
doc: nil,
|
22
|
+
href: nil,
|
23
|
+
#TODO protocol doesnt belong in representors, remove it
|
24
|
+
protocol: nil,
|
25
|
+
#TODO fix this, make it the same interface as transitions
|
26
|
+
links: {},
|
27
|
+
attributes: {},
|
28
|
+
transitions: [],
|
29
|
+
embedded: {}
|
30
|
+
}.each_value(&:freeze).freeze
|
31
|
+
|
32
|
+
|
33
|
+
# be able to create from a hash
|
34
|
+
def initialize(hash = {})
|
35
|
+
DEFAULT_HASH_VALUES.each { |k, v| self[k] = dup_or_self(hash[k] || v) }
|
36
|
+
end
|
37
|
+
|
38
|
+
# Be able to generate a new structure with myself and a hash
|
39
|
+
def merge(hash)
|
40
|
+
new_representor_hash = RepresentorHash.new(to_h)
|
41
|
+
hash.each_pair { |k, v| new_representor_hash[k] = v }
|
42
|
+
new_representor_hash
|
43
|
+
end
|
44
|
+
|
45
|
+
# to_h does not exists in Ruby < 2.0
|
46
|
+
def to_h
|
47
|
+
members.each_with_object({}) { |member, hash| hash[member] = self[member] }
|
48
|
+
end
|
49
|
+
|
50
|
+
def empty?
|
51
|
+
members.all? { |k| self[k].nil? || self[k].empty? }
|
52
|
+
end
|
53
|
+
|
54
|
+
def ==(other)
|
55
|
+
members.all? { |k| self[k] == other[k] }
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'representors/serialization/serialization_base'
|
2
|
+
require 'representors/serialization/deserializer_factory'
|
3
|
+
|
4
|
+
module Representors
|
5
|
+
class DeserializerBase < SerializationBase
|
6
|
+
|
7
|
+
def initialize(target)
|
8
|
+
@target = target.empty? ? {} : target
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.inherited(subclass)
|
12
|
+
DeserializerFactory.register_deserializers(subclass)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns back a class with all the information of the document and with convenience methods
|
16
|
+
# to access it.
|
17
|
+
# TODO: Yield builder to factor out builder dependency.
|
18
|
+
def to_representor
|
19
|
+
Representor.new(to_representor_hash)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns a hash representation of the data. This is useful to merge with new data which may
|
23
|
+
# be built by different builders. In this class we use it to support embedded resources.
|
24
|
+
def to_representor_hash(options = {})
|
25
|
+
raise "Abstract method #to_representor_hash not implemented in #{self.name} deserializer class."
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'representors/serialization/serialization_factory_base'
|
2
|
+
|
3
|
+
module Representors
|
4
|
+
class DeserializerFactory < SerializationFactoryBase
|
5
|
+
def self.register_deserializers(*serializers)
|
6
|
+
register_serialization_classes(*serializers)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.registered_deserializers
|
10
|
+
registered_serialization_classes
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
require 'representors/serialization/hale_deserializer'
|
3
|
+
|
4
|
+
module Representors
|
5
|
+
##
|
6
|
+
# Deserializes the HAL format as specified in http://stateless.co/hal_specification.html
|
7
|
+
# For examples of how this format looks like check the files under spec/fixtures/hal
|
8
|
+
# TODO: support Curies http://www.w3.org/TR/2010/NOTE-curie-20101216/
|
9
|
+
class HalDeserializer < HaleDeserializer
|
10
|
+
media_symbol :hal
|
11
|
+
media_type 'application/hal+json', 'application/json'
|
12
|
+
|
13
|
+
HAL_LINK_KEYS = %w(href templated type deprecation name profile title hreflang)
|
14
|
+
@reserved_keys = [LINKS_KEY, EMBEDDED_KEY]
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def builder_add_from_deserialized(builder, media)
|
19
|
+
builder = deserialize_properties(builder, media)
|
20
|
+
builder = deserialize_links(builder, media)
|
21
|
+
builder = deserialize_embedded(builder, media)
|
22
|
+
end
|
23
|
+
|
24
|
+
def deserialize_links(builder, media)
|
25
|
+
(media[LINKS_KEY] || {}).each do |link_rel, link_values|
|
26
|
+
link_values = [link_values] unless link_values.is_a?(Array)
|
27
|
+
link_values = link_values.map do |hash|
|
28
|
+
hash.select { |k,_| HAL_LINK_KEYS.include?(k) }
|
29
|
+
end
|
30
|
+
ensure_valid_links!(link_rel, link_values)
|
31
|
+
builder = builder.add_transition_array(link_rel, link_values)
|
32
|
+
end
|
33
|
+
builder
|
34
|
+
end
|
35
|
+
|
36
|
+
def deserialize_properties(builder, media)
|
37
|
+
media.each do |k,v|
|
38
|
+
builder = builder.add_attribute(k, v) unless (self.class.reserved_keys.include?(k))
|
39
|
+
end
|
40
|
+
builder
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'representors/serialization/serializer_base'
|
2
|
+
|
3
|
+
module Representors
|
4
|
+
module Serialization
|
5
|
+
class HalSerializer < SerializerBase
|
6
|
+
LINKS_KEY = "_links"
|
7
|
+
EMBEDDED_KEY = "_embedded"
|
8
|
+
LINKS_ONLY_OPTION = :embed_links_only
|
9
|
+
|
10
|
+
media_symbol :hal_json
|
11
|
+
media_type 'application/hal+json'
|
12
|
+
|
13
|
+
# This is public and returning a hash to be able to implement embedded resources
|
14
|
+
# serialization
|
15
|
+
# TODO: make this private and merge with to_media_type
|
16
|
+
# The name is quite misleading,
|
17
|
+
def to_hash(options = {})
|
18
|
+
base_hash, links, embedded_hals = common_serialization(@target)
|
19
|
+
base_hash.merge!(links.merge(embedded_hals.(options)))
|
20
|
+
base_hash
|
21
|
+
end
|
22
|
+
|
23
|
+
# This is the main entry of this class. It returns a serialization of the data
|
24
|
+
# in a given media type.
|
25
|
+
def to_media_type(options = {})
|
26
|
+
to_hash.to_json
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def common_serialization(representor)
|
32
|
+
base_hash = get_semantics(representor)
|
33
|
+
embedded_hals = ->(options) { get_embedded_objects(representor, options) }
|
34
|
+
# we want to group by rel name because it is possible to have several transitions with the same
|
35
|
+
# rel name. This will become an array in the output. For instance an items array
|
36
|
+
# with links to each item
|
37
|
+
grouped_transitions = (representor.transitions + representor.meta_links).group_by { |transition| transition[:rel] }
|
38
|
+
links = build_links(grouped_transitions)
|
39
|
+
links = links.empty? ? {} : { LINKS_KEY => links.reduce({}, :merge) }
|
40
|
+
[base_hash, links, embedded_hals]
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_semantics(representor)
|
44
|
+
representor.properties
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_embedded_objects(representor, options)
|
48
|
+
@get_embedded_objects ||= if representor.embedded.empty? || options.has_key?(LINKS_ONLY_OPTION)
|
49
|
+
{}
|
50
|
+
else
|
51
|
+
embedded_elements = representor.embedded.map { |k, v| build_embedded_objects(k, v) }
|
52
|
+
{ EMBEDDED_KEY => embedded_elements.reduce({}, :merge) }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Lambda used in this case to DRY code. Allows 'is array' functionality to be handled elsewhere
|
57
|
+
def build_embedded_objects(key, embedded)
|
58
|
+
make_media_type = ->(obj) { self.class.new(obj).to_hash }
|
59
|
+
embed = map_or_apply(make_media_type, embedded)
|
60
|
+
{ key => embed}
|
61
|
+
end
|
62
|
+
|
63
|
+
# @param [Hash] transitions. A hash on the shape "rel_name" => [Transition]
|
64
|
+
# The value for the rel_name usually will have only one transition but when we are building an
|
65
|
+
# array of transitions will have many.
|
66
|
+
# @return [Array] Array of hashes with the format [ { rel_name => {link_info1}}, {rel_name2 => ... }]
|
67
|
+
def build_links(transitions)
|
68
|
+
transitions.map do |rel_name, transition_array|
|
69
|
+
links = transition_array.map{|transition| build_links_for_this_media_type(transition)}
|
70
|
+
if links.size > 1
|
71
|
+
{rel_name => links}
|
72
|
+
else
|
73
|
+
{rel_name => links.first}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# This method can be overriden by other classes
|
79
|
+
# @param transition , a single tansition
|
80
|
+
def build_links_for_this_media_type(transition)
|
81
|
+
link = if transition.templated?
|
82
|
+
{ href: transition.templated_uri, templated: true }
|
83
|
+
else
|
84
|
+
{ href: transition.uri }
|
85
|
+
end
|
86
|
+
[:name, :profile].each { |key| link[key] = transition[key] if transition.has_key?(key) }
|
87
|
+
link
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|