remap 2.1.11 → 2.1.15
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/lib/remap/base.rb +5 -7
- data/lib/remap/compiler.rb +68 -64
- data/lib/remap/extensions/enumerable.rb +11 -10
- data/lib/remap/extensions/object.rb +17 -2
- data/lib/remap/failure.rb +6 -7
- data/lib/remap/proxy.rb +1 -0
- data/lib/remap/rule/block.rb +34 -0
- data/lib/remap/rule/map/enum.rb +88 -0
- data/lib/remap/rule/map/optional.rb +19 -1
- data/lib/remap/rule/map/required.rb +19 -0
- data/lib/remap/rule/map.rb +18 -10
- data/lib/remap/rule.rb +2 -0
- data/lib/remap/selector/index.rb +11 -3
- data/lib/remap/state/extension.rb +5 -20
- data/lib/remap/types.rb +1 -1
- data/lib/remap.rb +0 -1
- metadata +6 -9
- data/lib/remap/rule/each.rb +0 -38
- data/lib/remap/rule/embed.rb +0 -47
- data/lib/remap/rule/set.rb +0 -33
- data/lib/remap/rule/support/collection/empty.rb +0 -23
- data/lib/remap/rule/support/collection/filled.rb +0 -40
- data/lib/remap/rule/support/collection.rb +0 -19
- data/lib/remap/rule/support/enum.rb +0 -86
- data/lib/remap/rule/wrap.rb +0 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e3439c23b054b2470688ec55cedb4dc0d7c0f77544c45f8f47c82b5d8d9b954a
|
4
|
+
data.tar.gz: 515558fee642db02bd30e5194846a4486f6b59c67830c46581b9b3d3c529532e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 599b8f2de0d62e5d4ab8ded2d303243fe11292f0cc621b7cdf66bb9e70ac712bb3fe4811cd926e9929a06f110445e283b2b8c3134a68cec5c810c53f572f720c
|
7
|
+
data.tar.gz: 6cf4704750445ac8dfdac08cfe2beec2bff5ac34db4c61557f9239b49857c3a1423978d6cc38342917753d48b4edeeff397d012c8b91d32a3ee49ec96ab62fa4
|
data/lib/remap/base.rb
CHANGED
@@ -143,7 +143,7 @@ module Remap
|
|
143
143
|
#
|
144
144
|
# @return [void]
|
145
145
|
def self.contract(&context)
|
146
|
-
self.contract = Dry::Schema.
|
146
|
+
self.contract = Dry::Schema.define(&context)
|
147
147
|
end
|
148
148
|
|
149
149
|
# Defines a rule for the mapper
|
@@ -236,14 +236,12 @@ module Remap
|
|
236
236
|
#
|
237
237
|
# @return [void]
|
238
238
|
def self.define(target = Nothing, method: :new, strategy: :argument, &context)
|
239
|
-
unless
|
240
|
-
raise ArgumentError, "
|
239
|
+
unless block_given?
|
240
|
+
raise ArgumentError, "#{self}.define requires a block"
|
241
241
|
end
|
242
242
|
|
243
|
-
self.context = Compiler.call(&context)
|
244
243
|
self.constructor = Constructor.call(method: method, strategy: strategy, target: target)
|
245
|
-
|
246
|
-
raise ArgumentError, e.message
|
244
|
+
self.context = Compiler.call(&context)
|
247
245
|
end
|
248
246
|
|
249
247
|
# Similar to {::call}, but takes a state
|
@@ -269,7 +267,7 @@ module Remap
|
|
269
267
|
#
|
270
268
|
# @private
|
271
269
|
def call(state, &error)
|
272
|
-
unless
|
270
|
+
unless block_given?
|
273
271
|
raise ArgumentError, "Base#call(state, &error) requires block"
|
274
272
|
end
|
275
273
|
|
data/lib/remap/compiler.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Remap
|
4
|
+
using State::Extension
|
5
|
+
|
4
6
|
# Constructs a {Rule} from the block passed to {Remap::Base.define}
|
5
7
|
class Compiler < Proxy
|
6
8
|
# @return [Array<Rule>]
|
@@ -28,13 +30,15 @@ module Remap
|
|
28
30
|
#
|
29
31
|
# @return [Rule]
|
30
32
|
def self.call(&block)
|
31
|
-
unless
|
32
|
-
return Rule::
|
33
|
+
unless block_given?
|
34
|
+
return Rule::VOID
|
33
35
|
end
|
34
36
|
|
35
|
-
new([]).tap do |compiler|
|
37
|
+
rules = new([]).tap do |compiler|
|
36
38
|
compiler.instance_exec(&block)
|
37
|
-
end.
|
39
|
+
end.rules
|
40
|
+
|
41
|
+
Rule::Block.new(rules)
|
38
42
|
end
|
39
43
|
|
40
44
|
# Maps input path [input] to output path [to]
|
@@ -58,14 +62,8 @@ module Remap
|
|
58
62
|
# output.fetch(:value) # => { nickname: "John" }
|
59
63
|
#
|
60
64
|
# @return [Rule::Map::Required]
|
61
|
-
def map(*path, to: EMPTY_ARRAY, backtrace:
|
62
|
-
add
|
63
|
-
path: {
|
64
|
-
output: [to].flatten,
|
65
|
-
input: path.flatten
|
66
|
-
},
|
67
|
-
backtrace: backtrace,
|
68
|
-
rule: call(&block))
|
65
|
+
def map(*path, to: EMPTY_ARRAY, backtrace: caller, &block)
|
66
|
+
add rule(*path, to: to, backtrace: backtrace, &block)
|
69
67
|
end
|
70
68
|
|
71
69
|
# Optional version of {#map}
|
@@ -91,14 +89,8 @@ module Remap
|
|
91
89
|
# @see #map
|
92
90
|
#
|
93
91
|
# @return [Rule::Map::Optional]
|
94
|
-
def map?(*path, to: EMPTY_ARRAY, backtrace:
|
95
|
-
add
|
96
|
-
path: {
|
97
|
-
output: [to].flatten,
|
98
|
-
input: path.flatten
|
99
|
-
},
|
100
|
-
backtrace: backtrace,
|
101
|
-
rule: call(&block))
|
92
|
+
def map?(*path, to: EMPTY_ARRAY, backtrace: caller, &block)
|
93
|
+
add rule?(*path, to: to, backtrace: backtrace, &block)
|
102
94
|
end
|
103
95
|
|
104
96
|
# Select a path and uses the same path as output
|
@@ -121,8 +113,8 @@ module Remap
|
|
121
113
|
# output.fetch(:value) # => { name: "John" }
|
122
114
|
#
|
123
115
|
# @return [Rule::Map::Required]
|
124
|
-
def get(*path, backtrace:
|
125
|
-
|
116
|
+
def get(*path, backtrace: caller, &block)
|
117
|
+
add rule(path, to: path, backtrace: backtrace, &block)
|
126
118
|
end
|
127
119
|
|
128
120
|
# Optional version of {#get}
|
@@ -146,8 +138,8 @@ module Remap
|
|
146
138
|
# @see #get
|
147
139
|
#
|
148
140
|
# @return [Rule::Map::Optional]
|
149
|
-
def get?(*path, backtrace:
|
150
|
-
|
141
|
+
def get?(*path, backtrace: caller, &block)
|
142
|
+
add rule?(path, to: path, backtrace: backtrace, &block)
|
151
143
|
end
|
152
144
|
|
153
145
|
# Maps using mapper
|
@@ -184,14 +176,18 @@ module Remap
|
|
184
176
|
# output.fetch(:value) # => { car: "Volvo" }
|
185
177
|
#
|
186
178
|
# @return [Rule::Embed]
|
187
|
-
def embed(mapper
|
188
|
-
if
|
179
|
+
def embed(mapper)
|
180
|
+
if block_given?
|
189
181
|
raise ArgumentError, "#embed does not take a block"
|
190
182
|
end
|
191
183
|
|
192
|
-
|
193
|
-
|
194
|
-
|
184
|
+
embeding = rule.add do |state, &error|
|
185
|
+
mapper.call!(state.set(mapper: mapper)) do |failure|
|
186
|
+
next error[failure]
|
187
|
+
end.except(:mapper, :scope)
|
188
|
+
end
|
189
|
+
|
190
|
+
add embeding
|
195
191
|
end
|
196
192
|
|
197
193
|
# Set a static value
|
@@ -229,14 +225,12 @@ module Remap
|
|
229
225
|
# @raise [ArgumentError]
|
230
226
|
# if no path given
|
231
227
|
# if path is not a Symbol or Array<Symbol>
|
232
|
-
def set(*path, to
|
233
|
-
if
|
228
|
+
def set(*path, to:)
|
229
|
+
if block_given?
|
234
230
|
raise ArgumentError, "#set does not take a block"
|
235
231
|
end
|
236
232
|
|
237
|
-
add
|
238
|
-
rescue Dry::Struct::Error => e
|
239
|
-
raise ArgumentError, e.message
|
233
|
+
add rule(to: path).add { to.call(_1) }
|
240
234
|
end
|
241
235
|
|
242
236
|
# Maps to path from map with block in between
|
@@ -260,8 +254,8 @@ module Remap
|
|
260
254
|
# output.fetch(:value) # => { nickname: "John" }
|
261
255
|
#
|
262
256
|
# @return [Rule::Map]
|
263
|
-
def to(*path, map: EMPTY_ARRAY, backtrace:
|
264
|
-
|
257
|
+
def to(*path, map: EMPTY_ARRAY, backtrace: caller, &block)
|
258
|
+
add rule(*map, to: path, backtrace: backtrace, &block)
|
265
259
|
end
|
266
260
|
|
267
261
|
# Optional version of {#to}
|
@@ -287,7 +281,7 @@ module Remap
|
|
287
281
|
#
|
288
282
|
# @return [Rule::Map::Optional]
|
289
283
|
def to?(*path, map: EMPTY_ARRAY, &block)
|
290
|
-
|
284
|
+
add rule?(*map, to: path, &block)
|
291
285
|
end
|
292
286
|
|
293
287
|
# Iterates over the input value, passes each value
|
@@ -315,11 +309,11 @@ module Remap
|
|
315
309
|
# @return [Rule::Each]]
|
316
310
|
# @raise [ArgumentError] if no block given
|
317
311
|
def each(&block)
|
318
|
-
unless
|
312
|
+
unless block_given?
|
319
313
|
raise ArgumentError, "#each requires a block"
|
320
314
|
end
|
321
315
|
|
322
|
-
add
|
316
|
+
add rule(all, &block)
|
323
317
|
end
|
324
318
|
|
325
319
|
# Wraps output in type
|
@@ -348,13 +342,11 @@ module Remap
|
|
348
342
|
# @return [Rule::Wrap]
|
349
343
|
# @raise [ArgumentError] if type is not :array
|
350
344
|
def wrap(type, &block)
|
351
|
-
unless
|
345
|
+
unless block_given?
|
352
346
|
raise ArgumentError, "#wrap requires a block"
|
353
347
|
end
|
354
348
|
|
355
|
-
add
|
356
|
-
rescue Dry::Struct::Error => e
|
357
|
-
raise ArgumentError, e.message
|
349
|
+
add rule(&block).then { Array.wrap(_1) }
|
358
350
|
end
|
359
351
|
|
360
352
|
# Selects all elements
|
@@ -376,8 +368,8 @@ module Remap
|
|
376
368
|
# output.fetch(:value) # => { names: ["John", "Jane"] }
|
377
369
|
#
|
378
370
|
# @return [Rule::Path::Segment::Quantifier::All]
|
379
|
-
def all
|
380
|
-
if
|
371
|
+
def all
|
372
|
+
if block_given?
|
381
373
|
raise ArgumentError, "all selector does not take a block"
|
382
374
|
end
|
383
375
|
|
@@ -402,8 +394,8 @@ module Remap
|
|
402
394
|
# output.fetch(:value) # => { api_key: "<SECRET>" }
|
403
395
|
#
|
404
396
|
# @return [Rule::Static::Fixed]
|
405
|
-
def value(value
|
406
|
-
if
|
397
|
+
def value(value)
|
398
|
+
if block_given?
|
407
399
|
raise ArgumentError, "option selector does not take a block"
|
408
400
|
end
|
409
401
|
|
@@ -428,8 +420,8 @@ module Remap
|
|
428
420
|
# @param id [Symbol]
|
429
421
|
#
|
430
422
|
# @return [Rule::Static::Option]
|
431
|
-
def option(id, backtrace:
|
432
|
-
if
|
423
|
+
def option(id, backtrace: caller)
|
424
|
+
if block_given?
|
433
425
|
raise ArgumentError, "option selector does not take a block"
|
434
426
|
end
|
435
427
|
|
@@ -457,8 +449,8 @@ module Remap
|
|
457
449
|
#
|
458
450
|
# @return [Path::Segment::Key]
|
459
451
|
# @raise [ArgumentError] if index is not an Integer
|
460
|
-
def at(index
|
461
|
-
if
|
452
|
+
def at(index)
|
453
|
+
if block_given?
|
462
454
|
raise ArgumentError, "first selector does not take a block"
|
463
455
|
end
|
464
456
|
|
@@ -486,8 +478,8 @@ module Remap
|
|
486
478
|
# output.fetch(:value) # => { name: "John" }
|
487
479
|
#
|
488
480
|
# @return [Path::Segment::Key]]
|
489
|
-
def first
|
490
|
-
if
|
481
|
+
def first
|
482
|
+
if block_given?
|
491
483
|
raise ArgumentError, "first selector does not take a block"
|
492
484
|
end
|
493
485
|
|
@@ -513,27 +505,39 @@ module Remap
|
|
513
505
|
# output.fetch(:value) # => { name: "Linus" }
|
514
506
|
#
|
515
507
|
# @return [Path::Segment::Key]
|
516
|
-
def last
|
517
|
-
if
|
508
|
+
def last
|
509
|
+
if block_given?
|
518
510
|
raise ArgumentError, "last selector does not take a block"
|
519
511
|
end
|
520
512
|
|
521
513
|
at(-1)
|
522
514
|
end
|
523
515
|
|
524
|
-
# The final rule
|
525
|
-
#
|
526
|
-
# @return [Rule]
|
527
|
-
#
|
528
|
-
# @private
|
529
|
-
def rule
|
530
|
-
Rule::Collection.call(rules: rules)
|
531
|
-
end
|
532
|
-
|
533
516
|
private
|
534
517
|
|
535
518
|
def add(rule)
|
536
519
|
rule.tap { rules << rule }
|
537
520
|
end
|
521
|
+
|
522
|
+
def rule(*path, to: EMPTY_ARRAY, backtrace: caller, &block)
|
523
|
+
Rule::Map::Required.call({
|
524
|
+
path: {
|
525
|
+
output: [to].flatten,
|
526
|
+
input: path.flatten
|
527
|
+
},
|
528
|
+
backtrace: backtrace,
|
529
|
+
rule: call(&block)
|
530
|
+
})
|
531
|
+
end
|
532
|
+
|
533
|
+
def rule?(*path, to: EMPTY_ARRAY, backtrace: caller, &block)
|
534
|
+
Rule::Map::Optional.call({
|
535
|
+
path: {
|
536
|
+
output: [to].flatten,
|
537
|
+
input: path.flatten
|
538
|
+
},
|
539
|
+
rule: call(&block)
|
540
|
+
})
|
541
|
+
end
|
538
542
|
end
|
539
543
|
end
|
@@ -28,19 +28,20 @@ module Remap
|
|
28
28
|
# @return [Any]
|
29
29
|
#
|
30
30
|
# @raise When path cannot be found
|
31
|
-
def get(*path, &
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
]) do |(current_path, element), key|
|
36
|
-
value = element.fetch(key) do
|
37
|
-
raise PathError, current_path + [key]
|
38
|
-
end
|
31
|
+
def get(*path, trace: [], &fallback)
|
32
|
+
return self if path.empty?
|
33
|
+
|
34
|
+
key = path.first
|
39
35
|
|
40
|
-
|
36
|
+
unless block_given?
|
37
|
+
get(*path, trace: trace) do
|
38
|
+
raise PathError, trace + [key]
|
39
|
+
end
|
41
40
|
end
|
42
41
|
|
43
|
-
|
42
|
+
fetch(key, &fallback).get(*path[1..], trace: trace + [key], &fallback)
|
43
|
+
rescue TypeError
|
44
|
+
raise PathError, trace + [key]
|
44
45
|
end
|
45
46
|
end
|
46
47
|
end
|
@@ -17,14 +17,29 @@ module Remap
|
|
17
17
|
block["Expected a state, got [#{self}] (#{self.class})"]
|
18
18
|
end
|
19
19
|
|
20
|
+
# @return [Array]
|
21
|
+
#
|
22
|
+
# @see Extension::Paths::Hash
|
23
|
+
def paths
|
24
|
+
[]
|
25
|
+
end
|
26
|
+
|
20
27
|
# Fallback method used when #get is called on an object that does not respond to #get
|
21
28
|
#
|
22
29
|
# Block is invoked, if provided
|
23
30
|
# Otherwise a symbol is thrown
|
24
31
|
#
|
25
32
|
# @param path [Array<Key>]
|
26
|
-
def get(*path, &
|
27
|
-
|
33
|
+
def get(*path, trace: [], &fallback)
|
34
|
+
return self if path.empty?
|
35
|
+
|
36
|
+
unless block_given?
|
37
|
+
get(*path, trace: trace) do
|
38
|
+
raise PathError, trace
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
yield
|
28
43
|
end
|
29
44
|
alias_method :fetch, :get
|
30
45
|
|
data/lib/remap/failure.rb
CHANGED
@@ -14,22 +14,21 @@ module Remap
|
|
14
14
|
# @return [Failure]
|
15
15
|
def merge(other)
|
16
16
|
unless other.is_a?(self.class)
|
17
|
-
raise ArgumentError, "
|
17
|
+
raise ArgumentError, "Cannot merge %s (%s) with %s (%s)" % [
|
18
|
+
self, self.class, other, other.class
|
19
|
+
]
|
18
20
|
end
|
19
21
|
|
20
|
-
failure = attributes.deep_merge(other.attributes) do |
|
21
|
-
case [value1, value2]
|
22
|
-
in [Array, Array]
|
22
|
+
failure = attributes.deep_merge(other.attributes) do |key, value1, value2|
|
23
|
+
case [key, value1, value2]
|
24
|
+
in [:failures | :notices, Array, Array]
|
23
25
|
value1 + value2
|
24
|
-
else
|
25
|
-
raise ArgumentError, "can't merge #{self.class} with #{other.class}"
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
29
|
new(failure)
|
30
30
|
end
|
31
31
|
|
32
|
-
# @return [String]
|
33
32
|
def exception
|
34
33
|
Error.new(attributes.formated)
|
35
34
|
end
|
data/lib/remap/proxy.rb
CHANGED
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Remap
|
4
|
+
class Rule
|
5
|
+
using State::Extension
|
6
|
+
|
7
|
+
class Block < Unit
|
8
|
+
# @return [Array<Rule>]
|
9
|
+
attribute :rules, [Types::Rule]
|
10
|
+
|
11
|
+
# Represents a non-empty define block with one or more rules
|
12
|
+
# Calls every {#rules} with state and merges the output
|
13
|
+
#
|
14
|
+
# @param state [State]
|
15
|
+
#
|
16
|
+
# @return [State]
|
17
|
+
def call(state, &error)
|
18
|
+
unless block_given?
|
19
|
+
raise ArgumentError, "Block#call(state, &error) requires a block"
|
20
|
+
end
|
21
|
+
|
22
|
+
if rules.empty?
|
23
|
+
return state.except(:value)
|
24
|
+
end
|
25
|
+
|
26
|
+
rules.map do |rule|
|
27
|
+
rule.call(state) do |failure|
|
28
|
+
return error[failure]
|
29
|
+
end
|
30
|
+
end.reduce(&:combine)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Remap
|
4
|
+
class Rule
|
5
|
+
class Map
|
6
|
+
class Enum < Proxy
|
7
|
+
include Dry::Monads[:maybe]
|
8
|
+
|
9
|
+
# @return [Hash]
|
10
|
+
option :mappings, default: -> { Hash.new { default } }
|
11
|
+
|
12
|
+
# @return [Maybe]
|
13
|
+
option :default, default: -> { None() }
|
14
|
+
|
15
|
+
alias execute instance_eval
|
16
|
+
|
17
|
+
# Builds an enumeration using the block as context
|
18
|
+
#
|
19
|
+
# @example
|
20
|
+
# enum = Remap::Rule::Map::Enum.call do
|
21
|
+
# from "B", to: "C"
|
22
|
+
# value "A"
|
23
|
+
# otherwise "D"
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# enum.get("A") # => "A"
|
27
|
+
# enum.get("B") # => "C"
|
28
|
+
# enum.get("C") # => "C"
|
29
|
+
# enum.get("MISSING") # => "D"
|
30
|
+
#
|
31
|
+
# @return [Any]
|
32
|
+
def self.call(&block)
|
33
|
+
unless block
|
34
|
+
raise ArgumentError, "no block given"
|
35
|
+
end
|
36
|
+
|
37
|
+
new.tap { _1.execute(&block) }
|
38
|
+
end
|
39
|
+
|
40
|
+
# Translates key into a value using predefined mappings
|
41
|
+
#
|
42
|
+
# @param key [#hash]
|
43
|
+
#
|
44
|
+
# @yield [String]
|
45
|
+
# If the key is not found & no default value is set
|
46
|
+
#
|
47
|
+
# @return [Any]
|
48
|
+
def get(key, &error)
|
49
|
+
unless error
|
50
|
+
return get(key) { raise Error, _1 }
|
51
|
+
end
|
52
|
+
|
53
|
+
self[key].bind { return _1 }.or do
|
54
|
+
error["Enum key [#{key}] not found among [#{mappings.keys.inspect}]"]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
alias call get
|
58
|
+
|
59
|
+
# @return [Maybe]
|
60
|
+
def [](key)
|
61
|
+
mappings[key]
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [void]
|
65
|
+
def from(*keys, to:)
|
66
|
+
value = Some(to)
|
67
|
+
|
68
|
+
keys.each do |key|
|
69
|
+
mappings[key] = value
|
70
|
+
mappings[to] = value
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# @return [void]
|
75
|
+
def value(*ids)
|
76
|
+
ids.each do |id|
|
77
|
+
from(id, to: id)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# @return [void]
|
82
|
+
def otherwise(value)
|
83
|
+
mappings.default = Some(value)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -9,11 +9,29 @@ module Remap
|
|
9
9
|
# Represents an optional mapping rule
|
10
10
|
# When the mapping fails, the value is ignored
|
11
11
|
#
|
12
|
+
# @example Map [:name] to [:nickname]
|
13
|
+
# map = Map::Optional.call({
|
14
|
+
# path: {
|
15
|
+
# input: [:name],
|
16
|
+
# output: [:nickname]
|
17
|
+
# }
|
18
|
+
# })
|
19
|
+
#
|
20
|
+
# state = Remap::State.call({
|
21
|
+
# name: "John"
|
22
|
+
# })
|
23
|
+
#
|
24
|
+
# output = map.call(state) do |failure|
|
25
|
+
# raise failure.exeception
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# output.fetch(:value) # => { nickname: "John" }
|
29
|
+
#
|
12
30
|
# @param state [State]
|
13
31
|
#
|
14
32
|
# @return [State]
|
15
33
|
def call(state, &error)
|
16
|
-
unless
|
34
|
+
unless block_given?
|
17
35
|
raise ArgumentError, "map.call(state, &error) requires a block"
|
18
36
|
end
|
19
37
|
|
@@ -11,6 +11,25 @@ module Remap
|
|
11
11
|
# Represents a required mapping rule
|
12
12
|
# When it fails, the entire mapping is marked as failed
|
13
13
|
#
|
14
|
+
# @example Map [:name] to [:nickname]
|
15
|
+
# map = Map::Required.call({
|
16
|
+
# backtrace: caller,
|
17
|
+
# path: {
|
18
|
+
# input: [:name],
|
19
|
+
# output: [:nickname]
|
20
|
+
# }
|
21
|
+
# })
|
22
|
+
#
|
23
|
+
# state = Remap::State.call({
|
24
|
+
# name: "John"
|
25
|
+
# })
|
26
|
+
#
|
27
|
+
# output = map.call(state) do |failure|
|
28
|
+
# raise failure.exeception
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# output.fetch(:value) # => { nickname: "John" }
|
32
|
+
#
|
14
33
|
# @param state [State]
|
15
34
|
#
|
16
35
|
# @return [State]
|
data/lib/remap/rule/map.rb
CHANGED
@@ -28,7 +28,7 @@ module Remap
|
|
28
28
|
attribute? :path, Path.default { Path.call(EMPTY_HASH) }
|
29
29
|
|
30
30
|
# @return [Rule]
|
31
|
-
attribute :rule, Rule.default { Void.call(EMPTY_HASH) }
|
31
|
+
attribute? :rule, Rule.default { Void.call(EMPTY_HASH) }
|
32
32
|
|
33
33
|
# @return [Array<String>]
|
34
34
|
attribute? :backtrace, Types::Backtrace, default: EMPTY_ARRAY
|
@@ -43,15 +43,19 @@ module Remap
|
|
43
43
|
#
|
44
44
|
# @abstract
|
45
45
|
def call(state, &error)
|
46
|
-
unless
|
46
|
+
unless block_given?
|
47
47
|
raise ArgumentError, "Map#call(state, &error) requires error handler block"
|
48
48
|
end
|
49
49
|
|
50
50
|
notice = catch :fatal do
|
51
51
|
return path.input.call(state) do |inner_state|
|
52
|
-
rule.call(inner_state) do |failure|
|
52
|
+
other_state = rule.call(inner_state) do |failure|
|
53
53
|
return error[failure]
|
54
|
-
end
|
54
|
+
end
|
55
|
+
|
56
|
+
callback(other_state) do |failure|
|
57
|
+
return error[failure]
|
58
|
+
end
|
55
59
|
end.then(&path.output)
|
56
60
|
end
|
57
61
|
|
@@ -181,23 +185,27 @@ module Remap
|
|
181
185
|
end
|
182
186
|
end
|
183
187
|
|
184
|
-
private
|
185
|
-
|
186
188
|
# @return [self]
|
187
189
|
def add(&block)
|
188
190
|
tap { fn << block }
|
189
191
|
end
|
190
192
|
|
193
|
+
private
|
194
|
+
|
191
195
|
# @return [Array<Proc>]
|
192
196
|
def fn
|
193
197
|
@fn ||= []
|
194
198
|
end
|
195
199
|
|
196
200
|
# @return [Proc]
|
197
|
-
def callback
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
+
def callback(state, &error)
|
202
|
+
unless block_given?
|
203
|
+
raise ArgumentError, "Map#callback(state, &error) requires error handler block"
|
204
|
+
end
|
205
|
+
|
206
|
+
fn.reduce(state) do |inner, fn|
|
207
|
+
fn[inner] do |failure|
|
208
|
+
return error[failure]
|
201
209
|
end
|
202
210
|
end
|
203
211
|
end
|
data/lib/remap/rule.rb
CHANGED
data/lib/remap/selector/index.rb
CHANGED
@@ -8,8 +8,14 @@ module Remap
|
|
8
8
|
#
|
9
9
|
# @example Select the value at index 1 from a array
|
10
10
|
# state = Remap::State.call([:one, :two, :tree])
|
11
|
-
#
|
12
|
-
#
|
11
|
+
# index = Remap::Selector::Index.new(1)
|
12
|
+
#
|
13
|
+
# result = index.call(state) do |element|
|
14
|
+
# value = element.fetch(:value)
|
15
|
+
# element.merge(value: value.upcase)
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# result.fetch(:value) # => :TWO
|
13
19
|
class Index < Unit
|
14
20
|
# @return [Integer]
|
15
21
|
attribute :index, Integer
|
@@ -25,7 +31,9 @@ module Remap
|
|
25
31
|
#
|
26
32
|
# @return [State<U>]
|
27
33
|
def call(outer_state, &block)
|
28
|
-
|
34
|
+
unless block_given?
|
35
|
+
raise ArgumentError, "The index selector requires an iteration block"
|
36
|
+
end
|
29
37
|
|
30
38
|
outer_state.bind(index: index) do |array, state|
|
31
39
|
requirement[array] do
|
@@ -7,13 +7,6 @@ module Remap
|
|
7
7
|
using Extensions::Object
|
8
8
|
using Extensions::Hash
|
9
9
|
|
10
|
-
refine Object do
|
11
|
-
# @see Extension::Paths::Hash
|
12
|
-
def paths
|
13
|
-
EMPTY_ARRAY
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
10
|
refine Hash do
|
18
11
|
# Returns a list of all key paths
|
19
12
|
#
|
@@ -23,7 +16,7 @@ module Remap
|
|
23
16
|
# b: :c
|
24
17
|
# },
|
25
18
|
# d: :e
|
26
|
-
# }.
|
19
|
+
# }.paths # => [[:a, :b], [:d]]
|
27
20
|
#
|
28
21
|
# @return [Array<Array<Symbol>>] a list of key paths
|
29
22
|
def paths
|
@@ -47,7 +40,7 @@ module Remap
|
|
47
40
|
# b: :c
|
48
41
|
# },
|
49
42
|
# d: :e
|
50
|
-
# }.
|
43
|
+
# }.only(:a, :b) # => { a: { b: :c } }
|
51
44
|
#
|
52
45
|
# @returns [Hash] a hash containing the given path
|
53
46
|
# @raise Europace::Error when path doesn't exist
|
@@ -117,11 +110,7 @@ module Remap
|
|
117
110
|
def map(&block)
|
118
111
|
bind do |value, state|
|
119
112
|
Iteration.call(state: state, value: value).call do |other, **options|
|
120
|
-
state.set(other, **options).then
|
121
|
-
block[inner_state] do |failure|
|
122
|
-
throw :failure, failure
|
123
|
-
end
|
124
|
-
end
|
113
|
+
state.set(other, **options).then(&block)
|
125
114
|
end.except(:index, :element, :key)
|
126
115
|
end
|
127
116
|
end
|
@@ -275,10 +264,6 @@ module Remap
|
|
275
264
|
end
|
276
265
|
|
277
266
|
set(result)._
|
278
|
-
rescue NoMethodError => e
|
279
|
-
e.name == :fetch ? error["Fetch not defined on value: #{e}"] : raise
|
280
|
-
rescue NameError => e
|
281
|
-
e.name == :Undefined ? error["Undefined returned, skipping!: #{e}"] : raise
|
282
267
|
rescue KeyError, IndexError => e
|
283
268
|
error[e.message]
|
284
269
|
rescue PathError => e
|
@@ -351,7 +336,7 @@ module Remap
|
|
351
336
|
#
|
352
337
|
# @return [Struct]
|
353
338
|
def context(value, context: self, &error)
|
354
|
-
::Struct.new(*keys, *options.keys, keyword_init: true) do
|
339
|
+
::Struct.new(*keys, *options.keys, :state, keyword_init: true) do
|
355
340
|
define_method :method_missing do |name, *|
|
356
341
|
error["Method [#{name}] not defined"]
|
357
342
|
end
|
@@ -359,7 +344,7 @@ module Remap
|
|
359
344
|
define_method :skip! do |message = "Manual skip!"|
|
360
345
|
context.ignore!(message)
|
361
346
|
end
|
362
|
-
end.new(**to_hash, **options, value: value)
|
347
|
+
end.new(**to_hash, **options, value: value, state: self)
|
363
348
|
end
|
364
349
|
end
|
365
350
|
end
|
data/lib/remap/types.rb
CHANGED
@@ -15,7 +15,7 @@ module Remap
|
|
15
15
|
Enumerable = Any.constrained(type: Enumerable)
|
16
16
|
Nothing = Constant(Remap::Nothing)
|
17
17
|
Mapper = Interface(:call!)
|
18
|
-
Rule = Interface(:call)
|
18
|
+
Rule = Interface(:call) | Instance(Proc)
|
19
19
|
Key = Interface(:hash)
|
20
20
|
|
21
21
|
# Validates a state according to State::Schema
|
data/lib/remap.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: remap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.
|
4
|
+
version: 2.1.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Linus Oleander
|
@@ -279,18 +279,12 @@ files:
|
|
279
279
|
- lib/remap/path_error.rb
|
280
280
|
- lib/remap/proxy.rb
|
281
281
|
- lib/remap/rule.rb
|
282
|
-
- lib/remap/rule/
|
283
|
-
- lib/remap/rule/embed.rb
|
282
|
+
- lib/remap/rule/block.rb
|
284
283
|
- lib/remap/rule/map.rb
|
284
|
+
- lib/remap/rule/map/enum.rb
|
285
285
|
- lib/remap/rule/map/optional.rb
|
286
286
|
- lib/remap/rule/map/required.rb
|
287
|
-
- lib/remap/rule/set.rb
|
288
|
-
- lib/remap/rule/support/collection.rb
|
289
|
-
- lib/remap/rule/support/collection/empty.rb
|
290
|
-
- lib/remap/rule/support/collection/filled.rb
|
291
|
-
- lib/remap/rule/support/enum.rb
|
292
287
|
- lib/remap/rule/void.rb
|
293
|
-
- lib/remap/rule/wrap.rb
|
294
288
|
- lib/remap/selector.rb
|
295
289
|
- lib/remap/selector/all.rb
|
296
290
|
- lib/remap/selector/index.rb
|
@@ -307,6 +301,9 @@ homepage: https://github.com/oleander/remap
|
|
307
301
|
licenses:
|
308
302
|
- MIT
|
309
303
|
metadata:
|
304
|
+
bug_tracker_uri: https://github.com/oleander/remap/issues
|
305
|
+
homepage_uri: https://github.com/oleander/remap
|
306
|
+
documentation_uri: http://oleander.io/remap/
|
310
307
|
rubygems_mfa_required: 'true'
|
311
308
|
post_install_message:
|
312
309
|
rdoc_options: []
|
data/lib/remap/rule/each.rb
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Remap
|
4
|
-
class Rule
|
5
|
-
using State::Extension
|
6
|
-
|
7
|
-
# Iterates over a rule, even if the rule is not a collection
|
8
|
-
#
|
9
|
-
# @example Upcase each value in an array
|
10
|
-
# state = Remap::State.call(["John", "Jane"])
|
11
|
-
# upcase = Remap::Rule::Map.call({}).then(&:upcase)
|
12
|
-
# each = Remap::Rule::Each.call(rule: upcase)
|
13
|
-
# error = -> failure { raise failure.exception }
|
14
|
-
# each.call(state, &error).fetch(:value) # => ["JOHN", "JANE"]
|
15
|
-
class Each < Unit
|
16
|
-
# @return [Rule]
|
17
|
-
attribute :rule, Types::Rule
|
18
|
-
|
19
|
-
# Iterates over state and passes each value to rule
|
20
|
-
# Restores element, key & index before returning state
|
21
|
-
#
|
22
|
-
# @param state [State<Enumerable>]
|
23
|
-
#
|
24
|
-
# @return [State<Enumerable>]
|
25
|
-
def call(state, &error)
|
26
|
-
unless error
|
27
|
-
raise ArgumentError, "Each#call(state, &error) requires a block"
|
28
|
-
end
|
29
|
-
|
30
|
-
state.map do |inner_state|
|
31
|
-
rule.call(inner_state) do |failure|
|
32
|
-
return error[failure]
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
data/lib/remap/rule/embed.rb
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Remap
|
4
|
-
class Rule
|
5
|
-
using State::Extension
|
6
|
-
|
7
|
-
# Embed mappers into each other
|
8
|
-
#
|
9
|
-
# @example Embed Mapper A into B
|
10
|
-
# class Car < Remap::Base
|
11
|
-
# define do
|
12
|
-
# map :name, to: :model
|
13
|
-
# end
|
14
|
-
# end
|
15
|
-
#
|
16
|
-
# class Person < Remap::Base
|
17
|
-
# define do
|
18
|
-
# to :person do
|
19
|
-
# to :car do
|
20
|
-
# embed Car
|
21
|
-
# end
|
22
|
-
# end
|
23
|
-
# end
|
24
|
-
# end
|
25
|
-
#
|
26
|
-
# Person.call({name: "Volvo"}) # => { person: { car: { model: "Volvo" } } }
|
27
|
-
class Embed < Unit
|
28
|
-
# @return [#call!]
|
29
|
-
attribute :mapper, Types::Mapper
|
30
|
-
|
31
|
-
# Evaluates input against mapper and returns the result
|
32
|
-
#
|
33
|
-
# @param state [State<T>]
|
34
|
-
#
|
35
|
-
# @return [State<U>]
|
36
|
-
def call(state, &error)
|
37
|
-
unless error
|
38
|
-
raise ArgumentError, "A block is required to evaluate the embed"
|
39
|
-
end
|
40
|
-
|
41
|
-
mapper.call!(state.set(mapper: mapper)) do |failure|
|
42
|
-
return error[failure]
|
43
|
-
end.except(:mapper, :scope)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
data/lib/remap/rule/set.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Remap
|
4
|
-
class Rule
|
5
|
-
using State::Extension
|
6
|
-
|
7
|
-
# Set path to a static value
|
8
|
-
#
|
9
|
-
# @example Set path [:a, :b] to value "C"
|
10
|
-
# value = Remap::Static::Fixed.new(value: "a value")
|
11
|
-
# set = Remap::Rule::Set.new(value: value, path: [:a, :b])
|
12
|
-
# state = Remap::State.call("ANY VALUE")
|
13
|
-
# set.call(state).fetch(:value) # => { a: { b: "a value" } }
|
14
|
-
#
|
15
|
-
# @example Set path [:a, :b] to option :c
|
16
|
-
# value = Remap::Static::Option.new(name: :c)
|
17
|
-
# set = Remap::Rule::Set.new(value: value, path: [:a, :b])
|
18
|
-
# state = Remap::State.call("ANY VALUE", options: { c: "C" })
|
19
|
-
# set.call(state).fetch(:value) # => { a: { b: "C" } }
|
20
|
-
class Set < Concrete
|
21
|
-
# @return [Static]
|
22
|
-
attribute :value, Static, alias: :rule
|
23
|
-
|
24
|
-
# @return [Path::Output]
|
25
|
-
attribute :path, Path::Output
|
26
|
-
|
27
|
-
# @see Rule#call
|
28
|
-
def call(...)
|
29
|
-
rule.call(...).then(&path)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Remap
|
4
|
-
class Rule
|
5
|
-
class Collection
|
6
|
-
using State::Extension
|
7
|
-
|
8
|
-
# Represents an empty rule block
|
9
|
-
class Empty < Unit
|
10
|
-
attribute? :rules, Value(EMPTY_ARRAY), default: EMPTY_ARRAY
|
11
|
-
|
12
|
-
# Represents an empty define block, without any rules
|
13
|
-
#
|
14
|
-
# @param state [State<T>]
|
15
|
-
#
|
16
|
-
# @return [State<T>]
|
17
|
-
def call(state)
|
18
|
-
state.notice!("No rules, empty block")
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Remap
|
4
|
-
class Rule
|
5
|
-
class Collection
|
6
|
-
using State::Extension
|
7
|
-
|
8
|
-
# Represents a non-empty rule block
|
9
|
-
#
|
10
|
-
# @example A collection containing a single rule
|
11
|
-
# state = Remap::State.call("A")
|
12
|
-
# void = Remap::Rule::Void.call({})
|
13
|
-
# rule = Remap::Rule::Collection.call([void])
|
14
|
-
# error = -> failure { raise failure.exception }
|
15
|
-
# rule.call(state, &error).fetch(:value) # => "A"
|
16
|
-
class Filled < Unit
|
17
|
-
# @return [Array<Rule>]
|
18
|
-
attribute :rules, [Types.Interface(:call)], min_size: 1
|
19
|
-
|
20
|
-
# Represents a non-empty define block with one or more rules
|
21
|
-
# Calls every {#rules} with state and merges the output
|
22
|
-
#
|
23
|
-
# @param state [State]
|
24
|
-
#
|
25
|
-
# @return [State]
|
26
|
-
def call(state, &error)
|
27
|
-
unless error
|
28
|
-
raise ArgumentError, "Collection::Filled#call(state, &error) requires a block"
|
29
|
-
end
|
30
|
-
|
31
|
-
rules.map do |rule|
|
32
|
-
rule.call(state) do |failure|
|
33
|
-
return error[failure]
|
34
|
-
end
|
35
|
-
end.reduce(&:combine)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Remap
|
4
|
-
class Rule
|
5
|
-
# Represents a block defined by a rule
|
6
|
-
class Collection < Abstract
|
7
|
-
attribute :rules, Array
|
8
|
-
|
9
|
-
# @param state [State]
|
10
|
-
#
|
11
|
-
# @return [State]
|
12
|
-
#
|
13
|
-
# @abstract
|
14
|
-
def call(state)
|
15
|
-
raise NotImplementedError, "#{self.class}#call not implemented"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,86 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Remap
|
4
|
-
class Rule
|
5
|
-
class Enum < Proxy
|
6
|
-
include Dry::Monads[:maybe]
|
7
|
-
|
8
|
-
# @return [Hash]
|
9
|
-
option :mappings, default: -> { Hash.new { default } }
|
10
|
-
|
11
|
-
# @return [Maybe]
|
12
|
-
option :default, default: -> { None() }
|
13
|
-
|
14
|
-
alias execute instance_eval
|
15
|
-
|
16
|
-
# Builds an enumeration using the block as context
|
17
|
-
#
|
18
|
-
# @example
|
19
|
-
# enum = Remap::Rule::Enum.call do
|
20
|
-
# from "B", to: "C"
|
21
|
-
# value "A"
|
22
|
-
# otherwise "D"
|
23
|
-
# end
|
24
|
-
#
|
25
|
-
# enum.get("A") # => "A"
|
26
|
-
# enum.get("B") # => "C"
|
27
|
-
# enum.get("C") # => "C"
|
28
|
-
# enum.get("MISSING") # => "D"
|
29
|
-
#
|
30
|
-
# @return [Any]
|
31
|
-
def self.call(&block)
|
32
|
-
unless block
|
33
|
-
raise ArgumentError, "no block given"
|
34
|
-
end
|
35
|
-
|
36
|
-
new.tap { _1.execute(&block) }
|
37
|
-
end
|
38
|
-
|
39
|
-
# Translates key into a value using predefined mappings
|
40
|
-
#
|
41
|
-
# @param key [#hash]
|
42
|
-
#
|
43
|
-
# @yield [String]
|
44
|
-
# If the key is not found & no default value is set
|
45
|
-
#
|
46
|
-
# @return [Any]
|
47
|
-
def get(key, &error)
|
48
|
-
unless error
|
49
|
-
return get(key) { raise Error, _1 }
|
50
|
-
end
|
51
|
-
|
52
|
-
self[key].bind { return _1 }.or do
|
53
|
-
error["Enum key [#{key}] not found among [#{mappings.keys.inspect}]"]
|
54
|
-
end
|
55
|
-
end
|
56
|
-
alias call get
|
57
|
-
|
58
|
-
# @return [Maybe]
|
59
|
-
def [](key)
|
60
|
-
mappings[key]
|
61
|
-
end
|
62
|
-
|
63
|
-
# @return [void]
|
64
|
-
def from(*keys, to:)
|
65
|
-
value = Some(to)
|
66
|
-
|
67
|
-
keys.each do |key|
|
68
|
-
mappings[key] = value
|
69
|
-
mappings[to] = value
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
# @return [void]
|
74
|
-
def value(*ids)
|
75
|
-
ids.each do |id|
|
76
|
-
from(id, to: id)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
# @return [void]
|
81
|
-
def otherwise(value)
|
82
|
-
mappings.default = Some(value)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
data/lib/remap/rule/wrap.rb
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Remap
|
4
|
-
class Rule
|
5
|
-
using State::Extension
|
6
|
-
|
7
|
-
# Wraps rule in a type
|
8
|
-
#
|
9
|
-
# @example Maps { name: "Ford" } to { cars: ["Ford"] }
|
10
|
-
# class Mapper < Remap::Base
|
11
|
-
# define do
|
12
|
-
# to :cars do
|
13
|
-
# wrap(:array) do
|
14
|
-
# map :name
|
15
|
-
# end
|
16
|
-
# end
|
17
|
-
# end
|
18
|
-
# end
|
19
|
-
#
|
20
|
-
# Mapper.call({ name: "Ford" }) # => { cars: ["Ford"] }
|
21
|
-
class Wrap < Concrete
|
22
|
-
# @return [:array]
|
23
|
-
attribute :type, Value(:array)
|
24
|
-
|
25
|
-
# @return [Rule]
|
26
|
-
attribute :rule, Types::Rule
|
27
|
-
|
28
|
-
# Wraps the output from {#rule} in a {#type}
|
29
|
-
#
|
30
|
-
# @see Rule#call
|
31
|
-
def call(...)
|
32
|
-
rule.call(...).fmap do |value|
|
33
|
-
Array.wrap(value)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|