lazy_mapper 0.2.1 → 0.3.0
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 +4 -4
- data/.rubocop.yml +124 -0
- data/Gemfile +1 -1
- data/README.md +1 -1
- data/lazy_mapper.gemspec +4 -4
- data/lib/lazy_mapper.rb +54 -44
- data/spec/lazy_mapper_spec.rb +62 -52
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d96e0b06dde82ad35dad5af9b8df984d184350697d3b52bc13bca736d4fddc83
|
|
4
|
+
data.tar.gz: 3da907ac56b03e014ac47a0bcc21fdb9bd15727efe34ac72ceba9c80f2fe2687
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0ec71a118de6245dcf684f3a7380f2b83400305c9a62ac1734f88672eff5b97c9b253e10408b13bdde3b12b37dafbfd8f3b7f775737145a2ff42b8479875810d
|
|
7
|
+
data.tar.gz: 0da914f451230d8a9bbbe341aa47fb806e5465a5a0b60adb868b0484ed3a63b7895cea42788f077b606afeda188729e865c2a1f7559667f025dd098797bc8403
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
Metrics/CyclomaticComplexity:
|
|
2
|
+
Enabled: false
|
|
3
|
+
|
|
4
|
+
Metrics/LineLength:
|
|
5
|
+
Max: 120
|
|
6
|
+
# To make it possible to copy or click on URIs in the code, we allow lines
|
|
7
|
+
# containing a URI to be longer than Max.
|
|
8
|
+
AllowHeredoc: true
|
|
9
|
+
AllowURI: true
|
|
10
|
+
URISchemes:
|
|
11
|
+
- http
|
|
12
|
+
- https
|
|
13
|
+
|
|
14
|
+
Metrics/MethodLength:
|
|
15
|
+
CountComments: false # count full line comments?
|
|
16
|
+
Max: 15
|
|
17
|
+
|
|
18
|
+
Metrics/ClassLength:
|
|
19
|
+
CountComments: false # count full line comments?
|
|
20
|
+
Max: 150
|
|
21
|
+
|
|
22
|
+
Metrics/ModuleLength:
|
|
23
|
+
CountComments: false # count full line comments?
|
|
24
|
+
Max: 150
|
|
25
|
+
|
|
26
|
+
Metrics/BlockLength:
|
|
27
|
+
CountComments: false # count full line comments?
|
|
28
|
+
Max: 150 # Nothing wrong with e.g. big Structs
|
|
29
|
+
|
|
30
|
+
Layout/SpaceInsideStringInterpolation:
|
|
31
|
+
EnforcedStyle: space
|
|
32
|
+
|
|
33
|
+
Layout/SpaceInsideArrayLiteralBrackets:
|
|
34
|
+
Enabled: false
|
|
35
|
+
|
|
36
|
+
Style/RedundantSelf:
|
|
37
|
+
Enabled: false
|
|
38
|
+
|
|
39
|
+
Style/BlockDelimiters:
|
|
40
|
+
Enabled: false
|
|
41
|
+
|
|
42
|
+
Style/SignalException:
|
|
43
|
+
Enabled: false
|
|
44
|
+
|
|
45
|
+
Layout/EmptyLinesAroundBlockBody:
|
|
46
|
+
Enabled: false
|
|
47
|
+
|
|
48
|
+
Layout/EmptyLinesAroundBlockBody:
|
|
49
|
+
Description: "Keeps track of empty lines around block bodies."
|
|
50
|
+
Enabled: false
|
|
51
|
+
|
|
52
|
+
Layout/EmptyLinesAroundClassBody:
|
|
53
|
+
Description: "Keeps track of empty lines around class bodies."
|
|
54
|
+
Enabled: false
|
|
55
|
+
|
|
56
|
+
Layout/EmptyLinesAroundModuleBody:
|
|
57
|
+
Description: "Keeps track of empty lines around module bodies."
|
|
58
|
+
Enabled: false
|
|
59
|
+
|
|
60
|
+
Layout/EmptyLinesAroundMethodBody:
|
|
61
|
+
Description: "Keeps track of empty lines around method bodies."
|
|
62
|
+
Enabled: false
|
|
63
|
+
|
|
64
|
+
Style/DoubleNegation:
|
|
65
|
+
Enabled: false
|
|
66
|
+
|
|
67
|
+
Style/Documentation:
|
|
68
|
+
Enabled: false
|
|
69
|
+
|
|
70
|
+
Metrics/AbcSize:
|
|
71
|
+
Enabled: false
|
|
72
|
+
|
|
73
|
+
Style/RaiseArgs:
|
|
74
|
+
Enabled: false
|
|
75
|
+
|
|
76
|
+
Style/RegexpLiteral:
|
|
77
|
+
Enabled: false
|
|
78
|
+
|
|
79
|
+
Layout/AlignHash:
|
|
80
|
+
EnforcedColonStyle: table
|
|
81
|
+
EnforcedHashRocketStyle: table
|
|
82
|
+
|
|
83
|
+
Layout/AlignParameters:
|
|
84
|
+
EnforcedStyle: with_first_parameter
|
|
85
|
+
|
|
86
|
+
Layout/IndentArray:
|
|
87
|
+
EnforcedStyle: consistent
|
|
88
|
+
|
|
89
|
+
Layout/IndentHash:
|
|
90
|
+
EnforcedStyle: consistent
|
|
91
|
+
|
|
92
|
+
Style/AsciiComments:
|
|
93
|
+
Enabled: false
|
|
94
|
+
|
|
95
|
+
Style/FrozenStringLiteralComment:
|
|
96
|
+
Enabled: true
|
|
97
|
+
|
|
98
|
+
Style/MutableConstant:
|
|
99
|
+
Enabled: false
|
|
100
|
+
|
|
101
|
+
Style/MethodDefParentheses:
|
|
102
|
+
Enabled: false
|
|
103
|
+
EnforcedStyle: require_no_parentheses_except_multiline
|
|
104
|
+
|
|
105
|
+
Style/StabbyLambdaParentheses:
|
|
106
|
+
Enabled: false
|
|
107
|
+
|
|
108
|
+
Layout/SpaceInLambdaLiteral:
|
|
109
|
+
Enabled: false
|
|
110
|
+
|
|
111
|
+
Style/StructInheritance:
|
|
112
|
+
Enabled: false
|
|
113
|
+
|
|
114
|
+
Naming/PredicateName:
|
|
115
|
+
Enabled: false
|
|
116
|
+
|
|
117
|
+
Style/FormatStringToken:
|
|
118
|
+
Enabled: false
|
|
119
|
+
|
|
120
|
+
Rails/DynamicFindBy:
|
|
121
|
+
Enabled: false
|
|
122
|
+
|
|
123
|
+
Style/RescueStandardError:
|
|
124
|
+
EnforcedStyle: implicit
|
data/Gemfile
CHANGED
data/README.md
CHANGED
data/lazy_mapper.gemspec
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Gem::Specification.new do |spec|
|
|
2
2
|
spec.name = 'lazy_mapper'
|
|
3
|
-
spec.version = '0.
|
|
4
|
-
spec.summary =
|
|
5
|
-
spec.description =
|
|
6
|
-
spec.authors = [
|
|
3
|
+
spec.version = '0.3.0'
|
|
4
|
+
spec.summary = 'A lazy object mapper'
|
|
5
|
+
spec.description = 'Wraps primitive data in a semantically rich model'
|
|
6
|
+
spec.authors = ['Adam Lett']
|
|
7
7
|
spec.email = 'adam@bruun-rasmussen.dk'
|
|
8
8
|
spec.homepage = 'https://github.com/bruun-rasmussen/lazy_mapper'
|
|
9
9
|
spec.license = 'MIT'
|
data/lib/lazy_mapper.rb
CHANGED
|
@@ -3,11 +3,10 @@ require 'bigdecimal/util'
|
|
|
3
3
|
require 'time'
|
|
4
4
|
|
|
5
5
|
#
|
|
6
|
-
# Wraps a
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# The mapped values are memoized.
|
|
6
|
+
# Wraps a Hash or Hash-like data structure of primitive values and lazily maps
|
|
7
|
+
# its attributes to semantically rich domain objects using either a set of
|
|
8
|
+
# default mappers (for Ruby's built-in value types), or custom mappers which
|
|
9
|
+
# can be added either at the class level or at the instance level.
|
|
11
10
|
#
|
|
12
11
|
# Example:
|
|
13
12
|
# class Foo < LazyMapper
|
|
@@ -17,7 +16,6 @@ require 'time'
|
|
|
17
16
|
# many :users, User, map: ->(u) { User.new(u) }
|
|
18
17
|
# end
|
|
19
18
|
#
|
|
20
|
-
|
|
21
19
|
class LazyMapper
|
|
22
20
|
|
|
23
21
|
#
|
|
@@ -70,32 +68,40 @@ class LazyMapper
|
|
|
70
68
|
@mappers ||= DEFAULT_MAPPINGS
|
|
71
69
|
end
|
|
72
70
|
|
|
73
|
-
def self.
|
|
74
|
-
|
|
75
|
-
klass.instance_variable_set IVAR[:default_values], self.default_values.dup
|
|
71
|
+
def self.attributes
|
|
72
|
+
@attributes ||= {}
|
|
76
73
|
end
|
|
77
74
|
|
|
75
|
+
def self.inherited(klass)
|
|
76
|
+
# Make the subclass "inherit" the values of these class instance variables
|
|
77
|
+
%i[
|
|
78
|
+
mappers
|
|
79
|
+
default_values
|
|
80
|
+
attributes
|
|
81
|
+
].each do |s|
|
|
82
|
+
klass.instance_variable_set IVAR[s], self.send(s).dup
|
|
83
|
+
end
|
|
84
|
+
end
|
|
78
85
|
|
|
79
86
|
def mappers
|
|
80
87
|
@mappers ||= self.class.mappers
|
|
81
88
|
end
|
|
82
89
|
|
|
83
|
-
IVAR =
|
|
90
|
+
IVAR = lambda { |name| # :nodoc:
|
|
84
91
|
name_as_str = name.to_s
|
|
85
|
-
if name_as_str[-1] == '?'
|
|
86
|
-
name_as_str = name_as_str[0...-1]
|
|
87
|
-
end
|
|
92
|
+
name_as_str = name_as_str[0...-1] if name_as_str[-1] == '?'
|
|
88
93
|
|
|
89
94
|
('@' + name_as_str).freeze
|
|
90
95
|
}
|
|
91
96
|
|
|
92
|
-
WRITER = -> name { (name.to_s.
|
|
97
|
+
WRITER = -> name { (name.to_s.delete('?') + '=').to_sym }
|
|
93
98
|
|
|
94
99
|
#
|
|
95
100
|
# Creates a new instance by giving a Hash of attribues.
|
|
96
101
|
#
|
|
97
102
|
# Attribute values are type checked according to how they were defined.
|
|
98
|
-
#
|
|
103
|
+
#
|
|
104
|
+
# Fails with +TypeError+, if a value doesn't have the expected type.
|
|
99
105
|
#
|
|
100
106
|
# == Example
|
|
101
107
|
#
|
|
@@ -110,7 +116,6 @@ class LazyMapper
|
|
|
110
116
|
# ]
|
|
111
117
|
|
|
112
118
|
def initialize(values = {})
|
|
113
|
-
@json = {}
|
|
114
119
|
@mappers = {}
|
|
115
120
|
values.each do |name, value|
|
|
116
121
|
send(WRITER[name], value)
|
|
@@ -124,7 +129,7 @@ class LazyMapper
|
|
|
124
129
|
#
|
|
125
130
|
# == Arguments
|
|
126
131
|
#
|
|
127
|
-
# +
|
|
132
|
+
# +unmapped_data+ - The unmapped data as a Hash(-like object). Must respond to #to_h.
|
|
128
133
|
# Keys are assumed to be camelCased string
|
|
129
134
|
#
|
|
130
135
|
# +mappers:+ - Optional instance-level mappers.
|
|
@@ -133,7 +138,7 @@ class LazyMapper
|
|
|
133
138
|
#
|
|
134
139
|
# == Example
|
|
135
140
|
#
|
|
136
|
-
# Foo.
|
|
141
|
+
# Foo.from({
|
|
137
142
|
# "xmlId" => 42,
|
|
138
143
|
# "createdAt" => "2015-07-29 14:07:35 +0200",
|
|
139
144
|
# "amount" => "$2.00",
|
|
@@ -146,17 +151,18 @@ class LazyMapper
|
|
|
146
151
|
# :amount => -> x { Money.new(x) },
|
|
147
152
|
# User => User.method(:new) })
|
|
148
153
|
#
|
|
149
|
-
def self.
|
|
150
|
-
return nil if
|
|
151
|
-
fail TypeError, "#{
|
|
154
|
+
def self.from unmapped_data, mappers: {}
|
|
155
|
+
return nil if unmapped_data.nil?
|
|
156
|
+
fail TypeError, "#{ unmapped_data.inspect } is not a Hash" unless unmapped_data.respond_to? :to_h
|
|
152
157
|
instance = new
|
|
153
|
-
instance.send :
|
|
158
|
+
instance.send :unmapped_data=, unmapped_data.to_h
|
|
154
159
|
instance.send :mappers=, mappers
|
|
155
160
|
instance
|
|
156
161
|
end
|
|
157
162
|
|
|
158
|
-
def self.
|
|
159
|
-
|
|
163
|
+
def self.from_json *args, &block
|
|
164
|
+
warn "#{ self }.from_json is deprecated. Use #{ self }.from instead."
|
|
165
|
+
from *args, &block
|
|
160
166
|
end
|
|
161
167
|
|
|
162
168
|
#
|
|
@@ -201,7 +207,7 @@ class LazyMapper
|
|
|
201
207
|
# Define reader
|
|
202
208
|
define_method(name) {
|
|
203
209
|
memoize(name, ivar) {
|
|
204
|
-
unmapped_value =
|
|
210
|
+
unmapped_value = unmapped_data[from]
|
|
205
211
|
mapped_value(name, unmapped_value, type, **args)
|
|
206
212
|
}
|
|
207
213
|
}
|
|
@@ -210,7 +216,7 @@ class LazyMapper
|
|
|
210
216
|
end
|
|
211
217
|
|
|
212
218
|
#
|
|
213
|
-
# Converts a value to true or false according to its truthyness
|
|
219
|
+
# Converts a value to +true+ or +false+ according to its truthyness
|
|
214
220
|
#
|
|
215
221
|
TO_BOOL = -> b { !!b }
|
|
216
222
|
|
|
@@ -251,15 +257,14 @@ class LazyMapper
|
|
|
251
257
|
#
|
|
252
258
|
# +type+ - The type of the elements in the collection.
|
|
253
259
|
#
|
|
254
|
-
# +from:+ - Specifies the name of the wrapped array in the
|
|
260
|
+
# +from:+ - Specifies the name of the wrapped array in the unmapped data.
|
|
255
261
|
# Defaults to camelCased version of +name+.
|
|
256
262
|
#
|
|
257
263
|
# +map:+ - Specifies a custom mapper to apply to each elements in the wrapped collection.
|
|
258
264
|
# If unspecified, it defaults to the default mapper for the specified +type+ or simply the identity mapper
|
|
259
265
|
# if no default mapper exists.
|
|
260
266
|
#
|
|
261
|
-
# +default:+ - The default value to use, if the
|
|
262
|
-
# in the wrapped JSON object.
|
|
267
|
+
# +default:+ - The default value to use, if the unmapped value is missing.
|
|
263
268
|
#
|
|
264
269
|
# == Example
|
|
265
270
|
#
|
|
@@ -279,7 +284,7 @@ class LazyMapper
|
|
|
279
284
|
# Define getter
|
|
280
285
|
define_method(name) {
|
|
281
286
|
memoize(name) {
|
|
282
|
-
unmapped_value =
|
|
287
|
+
unmapped_value = unmapped_data[from]
|
|
283
288
|
if unmapped_value.is_a? Array
|
|
284
289
|
unmapped_value.map { |v| mapped_value(name, v, type, **args) }
|
|
285
290
|
else
|
|
@@ -296,30 +301,28 @@ class LazyMapper
|
|
|
296
301
|
mappers[type] = block
|
|
297
302
|
end
|
|
298
303
|
|
|
304
|
+
def to_h
|
|
305
|
+
attributes.each_with_object({}) {|(key, _value), h|
|
|
306
|
+
h[key] = self.send key
|
|
307
|
+
}
|
|
308
|
+
end
|
|
309
|
+
|
|
299
310
|
def inspect
|
|
300
311
|
@__under_inspection__ ||= 0
|
|
301
312
|
return "<#{ self.class.name } ... >" if @__under_inspection__ > 0
|
|
302
313
|
@__under_inspection__ += 1
|
|
303
|
-
|
|
304
|
-
if self.class.superclass.respond_to? :attributes
|
|
305
|
-
attributes = self.class.superclass.attributes.merge attributes
|
|
306
|
-
end
|
|
307
|
-
present_attributes = attributes.keys.each_with_object({}) {|name, memo|
|
|
314
|
+
present_attributes = attributes.keys.each_with_object({}) { |name, memo|
|
|
308
315
|
value = self.send name
|
|
309
316
|
memo[name] = value unless value.nil?
|
|
310
317
|
}
|
|
311
|
-
"<#{ self.class.name } #{ present_attributes.map {|k,v| k.to_s + ': ' + v.inspect }.join(', ') } >"
|
|
312
|
-
res = "<#{ self.class.name } #{ present_attributes.map {|k,v| k.to_s + ': ' + v.inspect }.join(', ') } >"
|
|
318
|
+
"<#{ self.class.name } #{ present_attributes.map { |k, v| k.to_s + ': ' + v.inspect }.join(', ') } >"
|
|
319
|
+
res = "<#{ self.class.name } #{ present_attributes.map { |k, v| k.to_s + ': ' + v.inspect }.join(', ') } >"
|
|
313
320
|
@__under_inspection__ -= 1
|
|
314
321
|
res
|
|
315
322
|
end
|
|
316
323
|
|
|
317
324
|
protected
|
|
318
325
|
|
|
319
|
-
def json
|
|
320
|
-
@json ||= {}
|
|
321
|
-
end
|
|
322
|
-
|
|
323
326
|
#
|
|
324
327
|
# Defines how to map an attribute name
|
|
325
328
|
# to the corresponding name in the unmapped
|
|
@@ -333,9 +336,13 @@ class LazyMapper
|
|
|
333
336
|
|
|
334
337
|
private
|
|
335
338
|
|
|
336
|
-
attr_writer :
|
|
339
|
+
attr_writer :unmapped_data
|
|
337
340
|
attr_writer :mappers
|
|
338
341
|
|
|
342
|
+
def unmapped_data
|
|
343
|
+
@unmapped_data ||= {}
|
|
344
|
+
end
|
|
345
|
+
|
|
339
346
|
def mapping_for(name, type)
|
|
340
347
|
mappers[name] || mappers[type] || self.class.mappers[type]
|
|
341
348
|
end
|
|
@@ -344,6 +351,10 @@ class LazyMapper
|
|
|
344
351
|
self.class.default_values[type]
|
|
345
352
|
end
|
|
346
353
|
|
|
354
|
+
def attributes
|
|
355
|
+
self.class.attributes
|
|
356
|
+
end
|
|
357
|
+
|
|
347
358
|
def mapped_value(name, unmapped_value, type, map: mapping_for(name, type), default: default_value(type))
|
|
348
359
|
if unmapped_value.nil?
|
|
349
360
|
# Duplicate to prevent accidental sharing between instances
|
|
@@ -369,12 +380,11 @@ class LazyMapper
|
|
|
369
380
|
[ all_but_last.join(separator), last ].join conjunction
|
|
370
381
|
end
|
|
371
382
|
|
|
372
|
-
|
|
373
383
|
def memoize name, ivar = IVAR[name]
|
|
374
384
|
send WRITER[name], yield unless instance_variable_defined?(ivar)
|
|
375
385
|
instance_variable_get(ivar)
|
|
376
386
|
end
|
|
377
387
|
|
|
378
388
|
SNAKE_CASE_PATTERN = /(_[a-z])/ # :nodoc:
|
|
379
|
-
CAMELIZE = -> name { name.to_s.gsub(SNAKE_CASE_PATTERN) { |x| x[1].upcase }.
|
|
389
|
+
CAMELIZE = -> name { name.to_s.gsub(SNAKE_CASE_PATTERN) { |x| x[1].upcase }.delete('?') }
|
|
380
390
|
end
|
data/spec/lazy_mapper_spec.rb
CHANGED
|
@@ -5,11 +5,18 @@ require 'lazy_mapper'
|
|
|
5
5
|
|
|
6
6
|
describe LazyMapper do
|
|
7
7
|
|
|
8
|
-
describe 'when constructed
|
|
8
|
+
describe 'when constructed from unmapped data' do
|
|
9
9
|
|
|
10
|
-
subject(:instance) { klass.
|
|
10
|
+
subject(:instance) { klass.from unmapped_data }
|
|
11
11
|
|
|
12
|
-
let(:
|
|
12
|
+
let(:unmapped_data) {
|
|
13
|
+
{
|
|
14
|
+
'createdAt' => '2015-07-27',
|
|
15
|
+
'updatedAt' => ['2015-01-01', '2015-01-02'],
|
|
16
|
+
'foo' => '42',
|
|
17
|
+
'blue' => true
|
|
18
|
+
}
|
|
19
|
+
}
|
|
13
20
|
let(:klass) {
|
|
14
21
|
t = type
|
|
15
22
|
m = map
|
|
@@ -25,11 +32,13 @@ describe LazyMapper do
|
|
|
25
32
|
let(:type) { Integer }
|
|
26
33
|
|
|
27
34
|
context 'if the supplied data is nil' do
|
|
35
|
+
let(:unmapped_data) { nil }
|
|
36
|
+
|
|
28
37
|
it { is_expected.to be_nil }
|
|
29
38
|
end
|
|
30
39
|
|
|
31
40
|
context 'when invalid data is supplied' do
|
|
32
|
-
let(:
|
|
41
|
+
let(:unmapped_data) { 'not a hash' }
|
|
33
42
|
|
|
34
43
|
it 'fails with a TypeError' do
|
|
35
44
|
expect { instance }.to raise_error(TypeError)
|
|
@@ -38,20 +47,11 @@ describe LazyMapper do
|
|
|
38
47
|
|
|
39
48
|
context 'when valid data is supplied' do
|
|
40
49
|
|
|
41
|
-
|
|
42
|
-
{
|
|
43
|
-
'createdAt' => '2015-07-27',
|
|
44
|
-
'updatedAt' => ['2015-01-01', '2015-01-02'],
|
|
45
|
-
'foo' => '42',
|
|
46
|
-
'blue' => true
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
it 'maps JSON attributes to domain objects' do
|
|
50
|
+
it 'maps primitives to domain objects' do
|
|
51
51
|
expect(instance.created_at).to eq(Date.new(2015, 7, 27))
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
it 'maps arrays of
|
|
54
|
+
it 'maps arrays of primitives to arrays of domain objects' do
|
|
55
55
|
expect(instance.updated_at).to be_a(Array)
|
|
56
56
|
expect(instance.updated_at.first).to be_a(Date)
|
|
57
57
|
expect(instance).to be_blue
|
|
@@ -79,8 +79,8 @@ describe LazyMapper do
|
|
|
79
79
|
|
|
80
80
|
subject(:instance) { foo }
|
|
81
81
|
|
|
82
|
-
let(:foo) { klass_foo.
|
|
83
|
-
let(:bar) { klass_bar.
|
|
82
|
+
let(:foo) { klass_foo.from 'bar' => 'bar' }
|
|
83
|
+
let(:bar) { klass_bar.from 'foo' => 'foo' }
|
|
84
84
|
let(:foo_builder) { proc { foo } }
|
|
85
85
|
let(:bar_builder) { proc { bar } }
|
|
86
86
|
|
|
@@ -116,9 +116,9 @@ describe LazyMapper do
|
|
|
116
116
|
end
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
let(:
|
|
119
|
+
let(:unmapped_data) { { 'BAZ' => 999, 'hairy' => true } }
|
|
120
120
|
|
|
121
|
-
it 'specifies
|
|
121
|
+
it 'specifies the name of the attribute in the unmapped data' do
|
|
122
122
|
expect(instance.baz).to eq(999)
|
|
123
123
|
end
|
|
124
124
|
|
|
@@ -135,7 +135,7 @@ describe LazyMapper do
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
it 'fails with a TypeError when an attribute is accessed' do
|
|
138
|
-
instance = klass.
|
|
138
|
+
instance = klass.from 'bar' => 42
|
|
139
139
|
expect { instance.bar }.to raise_error(TypeError)
|
|
140
140
|
end
|
|
141
141
|
end
|
|
@@ -146,7 +146,7 @@ describe LazyMapper do
|
|
|
146
146
|
one :composite, type
|
|
147
147
|
end
|
|
148
148
|
|
|
149
|
-
instance = klass.
|
|
149
|
+
instance = klass.from 'composite' => '123 456'
|
|
150
150
|
instance.add_mapper_for(type) { |unmapped_value| type.new(*unmapped_value.split(' ')) }
|
|
151
151
|
|
|
152
152
|
expect(instance.composite).to eq type.new('123', '456')
|
|
@@ -155,35 +155,6 @@ describe LazyMapper do
|
|
|
155
155
|
expect(instance.composite).to eq type.new('abc', 'cde')
|
|
156
156
|
end
|
|
157
157
|
|
|
158
|
-
it 'supports adding inheritable default mappers to derived classes' do
|
|
159
|
-
type = Struct.new(:val1, :val2)
|
|
160
|
-
|
|
161
|
-
klass = Class.new LazyMapper do
|
|
162
|
-
mapper_for type, ->(unmapped_value) { type.new(*unmapped_value.split(' ')) }
|
|
163
|
-
one :composite, type
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
klass2 = Class.new(klass)
|
|
167
|
-
instance = klass.from_json 'composite' => '123 456'
|
|
168
|
-
expect(instance.composite).to eq type.new('123', '456')
|
|
169
|
-
|
|
170
|
-
instance2 = klass2.from_json 'composite' => '456 789'
|
|
171
|
-
expect(instance2.composite).to eq type.new('456', '789')
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
it 'supports adding or overriding inheritable default values for types to derived classes' do
|
|
175
|
-
type = Struct.new(:val1, :val2)
|
|
176
|
-
|
|
177
|
-
klass = Class.new LazyMapper do
|
|
178
|
-
default_value_for type, type.new('321', '123')
|
|
179
|
-
one :composite, type
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
klass2 = Class.new(klass)
|
|
183
|
-
instance = klass2.from_json({})
|
|
184
|
-
expect(instance.composite).to eq type.new('321', '123')
|
|
185
|
-
end
|
|
186
|
-
|
|
187
158
|
it 'supports injection of customer mappers during instantiation' do
|
|
188
159
|
type = Struct.new(:val1, :val2)
|
|
189
160
|
klass = Class.new LazyMapper do
|
|
@@ -191,7 +162,7 @@ describe LazyMapper do
|
|
|
191
162
|
one :bar, type
|
|
192
163
|
end
|
|
193
164
|
|
|
194
|
-
instance = klass.
|
|
165
|
+
instance = klass.from({ 'foo' => '123 456', 'bar' => 'abc def' },
|
|
195
166
|
mappers: {
|
|
196
167
|
foo: ->(f) { type.new(*f.split(' ').reverse) },
|
|
197
168
|
type => ->(t) { type.new(*t.split(' ')) }
|
|
@@ -207,11 +178,37 @@ describe LazyMapper do
|
|
|
207
178
|
many :bars, String, map: ->(v) { return v }
|
|
208
179
|
end
|
|
209
180
|
|
|
210
|
-
instance = klass.
|
|
181
|
+
instance = klass.from 'foos' => 'abc', 'bars' => 'abc'
|
|
211
182
|
|
|
212
183
|
expect(instance.foos).to eq %w[a b c]
|
|
213
184
|
expect { instance.bars }.to raise_error(TypeError)
|
|
214
185
|
end
|
|
186
|
+
|
|
187
|
+
context 'when it is derived from another LazyMapper' do
|
|
188
|
+
let(:klass) { Class.new(base) }
|
|
189
|
+
let(:composite_type) { Struct.new(:val1, :val2) }
|
|
190
|
+
let(:base) {
|
|
191
|
+
type = composite_type
|
|
192
|
+
Class.new(LazyMapper) do
|
|
193
|
+
default_value_for type, type.new('321', '123')
|
|
194
|
+
mapper_for type, ->(unmapped_value) { type.new(*unmapped_value.split(' ')) }
|
|
195
|
+
one :composite, type
|
|
196
|
+
end
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
it 'inherits attributes' do
|
|
200
|
+
expect(klass.attributes.keys).to eq [:composite]
|
|
201
|
+
expect(instance).to respond_to(:composite)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
it 'inherits default values' do
|
|
205
|
+
expect(instance.composite).to eq composite_type.new('321', '123')
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
it 'inherits default mappers' do
|
|
209
|
+
expect(klass.from('composite' => 'abc def').composite).to eq composite_type.new('abc', 'def')
|
|
210
|
+
end
|
|
211
|
+
end
|
|
215
212
|
end
|
|
216
213
|
|
|
217
214
|
context 'when constructed with .new' do
|
|
@@ -289,6 +286,19 @@ describe LazyMapper do
|
|
|
289
286
|
expect(instance2.tags).to be_empty
|
|
290
287
|
expect(instance2.things).to_not be_empty
|
|
291
288
|
end
|
|
289
|
+
|
|
290
|
+
it 'still includes every attribute when converted to Hash' do
|
|
291
|
+
expect(instance.to_h).to eq(
|
|
292
|
+
title: '',
|
|
293
|
+
count: 0,
|
|
294
|
+
rate: 0.0,
|
|
295
|
+
tags: [],
|
|
296
|
+
widget: nil,
|
|
297
|
+
things: ['something'],
|
|
298
|
+
green?: false,
|
|
299
|
+
flowers?: false
|
|
300
|
+
)
|
|
301
|
+
end
|
|
292
302
|
end
|
|
293
303
|
end
|
|
294
304
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lazy_mapper
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Adam Lett
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2018-10-
|
|
11
|
+
date: 2018-10-19 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -45,6 +45,7 @@ extensions: []
|
|
|
45
45
|
extra_rdoc_files: []
|
|
46
46
|
files:
|
|
47
47
|
- ".gitignore"
|
|
48
|
+
- ".rubocop.yml"
|
|
48
49
|
- Gemfile
|
|
49
50
|
- LICENCE
|
|
50
51
|
- README.md
|