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