-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathdata_mappings.rb
117 lines (100 loc) · 3.57 KB
/
data_mappings.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
module KOSapiClient
module Entity
module DataMappings
def self.included(base)
base.extend(ClassMethods)
end
def to_hash
result = {}
self.class.attr_mappings.each_key { |k| result[k] = convert_value(send(k)) }
result
end
private
def convert_value(val)
if val.respond_to? :to_hash
val.to_hash
elsif val.is_a?(Array)
val.map { |it| convert_value(it) }
else
val
end
end
module ClassMethods
def map_data(name, type=String, opts = {})
attr_accessor name
opts[:type] = type
@data_mappings ||= {}
@data_mappings[name] = opts
end
def attr_mappings
if self.superclass.respond_to? :attr_mappings
parent_mappings = self.superclass.attr_mappings
end
@data_mappings.reverse_merge(parent_mappings || {})
end
# Parses composed domain type from hash response structure.
#
# @param [Hash] content hash structure from API response corresponding to single domain object
# @return [BaseEntity] parsed domain object
def parse(content, context = {})
instance = new()
set_mapped_attributes(instance, content)
instance
end
# Creates new domain object instance and sets values
# of mapped domain object attributes from source hash.
# Attributes are mapped by .map_data method.
def set_mapped_attributes(instance, source_hash)
if self.superclass.respond_to? :set_mapped_attributes
self.superclass.set_mapped_attributes(instance, source_hash)
end
raise "Missing data mappings for entity #{self}" unless @data_mappings
@data_mappings.each do |name, options|
set_mapped_attribute(instance, name, source_hash, options)
end
end
private
def set_mapped_attribute(instance, name, source_hash, mapping_options)
namespace = mapping_options[:namespace]
src_element = mapping_options[:element] || name
if namespace
key = "#{namespace}_#{src_element}".to_sym
else
key = src_element
end
value = source_hash[key]
value = value[mapping_options[:array_wrapper_element]] if mapping_options.key? :array_wrapper_element
if value.nil?
raise "Missing value for attribute #{name}" if mapping_options[:required]
if mapping_options[:type].is_a?(Array)
value = []
else
return
end
else
value = convert_type(value, mapping_options[:type])
end
instance.send("#{name}=".to_sym, value)
end
def convert_type(value, type)
return value.to_i if type == Integer
return value if type == String
return convert_array(value, type.first) if type.is_a?(Array)
return type.parse(value) if type.respond_to? :parse
raise "Unknown type #{type} to convert value #{value} to."
end
# Converts values of array type to proper domain objects.
# It checks whether the value is really an array, because
# when API returns a single value it does not get parsed
# into an array.
def convert_array(values, type)
if values.is_a?(Array)
values.map { |it| convert_type(it, type) }
else
[ convert_type(values, type) ]
end
end
end
end
end
end