lazy_mapper 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|