remap 2.2.40 → 2.2.41
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 +45 -15
- data/lib/remap/catchable.rb +27 -0
- data/lib/remap/compiler.rb +36 -9
- data/lib/remap/constructor/keyword.rb +2 -9
- data/lib/remap/extensions/array.rb +14 -0
- data/lib/remap/extensions/object.rb +6 -0
- data/lib/remap/failure/error.rb +20 -0
- data/lib/remap/failure.rb +7 -5
- data/lib/remap/iteration/array.rb +3 -6
- data/lib/remap/iteration/hash.rb +3 -6
- data/lib/remap/operation.rb +20 -15
- data/lib/remap/rule/block.rb +19 -12
- data/lib/remap/rule/map/optional.rb +2 -8
- data/lib/remap/rule/map/required.rb +1 -34
- data/lib/remap/rule/map.rb +16 -25
- data/lib/remap/rule/void.rb +1 -3
- data/lib/remap/rule.rb +15 -0
- data/lib/remap/selector/index.rb +1 -1
- data/lib/remap/selector/key.rb +1 -1
- data/lib/remap/state/extension.rb +127 -45
- data/lib/remap/state/schema.rb +2 -0
- data/lib/remap/state.rb +2 -0
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5a8ee7751985c548b272cb12bb0b08b655f52de3143075afafd8578f9e604eba
|
|
4
|
+
data.tar.gz: f333d2922e7d5ef7a6bd050eb2fc0261446097fdd17f2745eef1a3bc35eb96ee
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 69b98a323c17562a06d4b83f3455c2b1c7e6a482280f5afe283f0d9570411391bb2d0de353d96250436d8aa51ad8443f1a3e25805dde0d6baebbc5286d019a9e
|
|
7
|
+
data.tar.gz: 86f03041d4cf3ef2ebd1c597269645ae83c55e5d3326077a9d55f51f3f0a47aea131523e9e314d635f34d60487dbba20350a28ffa3eca9b032f0aecd01bf006f
|
data/lib/remap/base.rb
CHANGED
|
@@ -106,8 +106,10 @@ module Remap
|
|
|
106
106
|
class Base < Mapper
|
|
107
107
|
include ActiveSupport::Configurable
|
|
108
108
|
include Dry::Core::Constants
|
|
109
|
+
|
|
110
|
+
include Catchable
|
|
111
|
+
|
|
109
112
|
using State::Extension
|
|
110
|
-
extend Operation
|
|
111
113
|
|
|
112
114
|
with_options instance_accessor: true do |scope|
|
|
113
115
|
scope.config_accessor(:contract) { Dry::Schema.define {} }
|
|
@@ -120,6 +122,31 @@ module Remap
|
|
|
120
122
|
|
|
121
123
|
schema schema.strict(false)
|
|
122
124
|
|
|
125
|
+
# extend Operation
|
|
126
|
+
|
|
127
|
+
def self.call(input, backtrace: caller, **options, &error)
|
|
128
|
+
unless block_given?
|
|
129
|
+
return call(input, **options) do |failure|
|
|
130
|
+
raise failure.exception(backtrace)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
s0 = State.call(input, options: options, mapper: self)._
|
|
135
|
+
|
|
136
|
+
s1 = call!(s0) do |failure|
|
|
137
|
+
return error[failure]
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
case s1
|
|
141
|
+
in { value: value }
|
|
142
|
+
value
|
|
143
|
+
in { notices: [] }
|
|
144
|
+
error[s1.failure("No data could be mapped")]
|
|
145
|
+
in { notices: }
|
|
146
|
+
error[Failure.new(failures: notices)]
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
123
150
|
# Defines a schema for the mapper
|
|
124
151
|
# If the schema fail, the mapper will fail
|
|
125
152
|
#
|
|
@@ -235,26 +262,31 @@ module Remap
|
|
|
235
262
|
# Mapper.call({name: "John"}).first_name # => "John"
|
|
236
263
|
#
|
|
237
264
|
# @return [void]
|
|
238
|
-
|
|
265
|
+
# rubocop:disable Layout/LineLength
|
|
266
|
+
def self.define(target = Nothing, method: :new, strategy: :argument, backtrace: caller, &context)
|
|
239
267
|
unless block_given?
|
|
240
268
|
raise ArgumentError, "#{self}.define requires a block"
|
|
241
269
|
end
|
|
242
270
|
|
|
243
271
|
self.constructor = Constructor.call(method: method, strategy: strategy, target: target)
|
|
244
|
-
self.context = Compiler.call(&context)
|
|
272
|
+
self.context = Compiler.call(backtrace: backtrace, &context)
|
|
245
273
|
end
|
|
274
|
+
# rubocop:enable Layout/LineLength
|
|
246
275
|
|
|
247
|
-
# Similar to {::call}, but takes a state
|
|
248
|
-
#
|
|
249
276
|
# @param state [State]
|
|
250
277
|
#
|
|
251
|
-
# @yield [Failure]
|
|
278
|
+
# @yield [Failure]
|
|
279
|
+
# when a non-critical error occurs
|
|
280
|
+
# @yieldreturn T
|
|
252
281
|
#
|
|
253
|
-
# @return [
|
|
282
|
+
# @return [State, T]
|
|
283
|
+
# when request is a success
|
|
284
|
+
# @raise [Remap::Error]
|
|
285
|
+
# when a fatal error occurs
|
|
254
286
|
#
|
|
255
287
|
# @private
|
|
256
288
|
def self.call!(state, &error)
|
|
257
|
-
new(state.options).call(state
|
|
289
|
+
new(state.options).call(state, &error)
|
|
258
290
|
end
|
|
259
291
|
|
|
260
292
|
# Mappers state according to the mapper rules
|
|
@@ -267,8 +299,8 @@ module Remap
|
|
|
267
299
|
#
|
|
268
300
|
# @private
|
|
269
301
|
def call(state, &error)
|
|
270
|
-
|
|
271
|
-
raise ArgumentError, "
|
|
302
|
+
state._ do |reason|
|
|
303
|
+
raise ArgumentError, "Invalid state due to #{reason.formatted}"
|
|
272
304
|
end
|
|
273
305
|
|
|
274
306
|
state.tap do |input|
|
|
@@ -279,13 +311,11 @@ module Remap
|
|
|
279
311
|
end
|
|
280
312
|
end
|
|
281
313
|
|
|
282
|
-
|
|
283
|
-
return context.call(state)
|
|
284
|
-
return error[failure]
|
|
285
|
-
end.then(&constructor)
|
|
314
|
+
s1 = catch_ignored do |id|
|
|
315
|
+
return context.call(state.set(id: id)).then(&constructor).remove_id
|
|
286
316
|
end
|
|
287
317
|
|
|
288
|
-
|
|
318
|
+
Failure.new(failures: s1.notices).then(&error)
|
|
289
319
|
end
|
|
290
320
|
|
|
291
321
|
private
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Remap
|
|
4
|
+
module Catchable
|
|
5
|
+
# @yieldparam id [Symbol]
|
|
6
|
+
# @yieldreturn [T]
|
|
7
|
+
#
|
|
8
|
+
# @return [T]
|
|
9
|
+
def catch_ignored(&block)
|
|
10
|
+
catch(to_id(:ignored), &block)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @yieldparam id [Symbol]
|
|
14
|
+
# @yieldreturn [T]
|
|
15
|
+
#
|
|
16
|
+
# @return [T]
|
|
17
|
+
def catch_fatal(&block)
|
|
18
|
+
catch(to_id(:fatal), &block)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def to_id(value)
|
|
24
|
+
[value, self.class.name&.downcase || :unknown].join("::")
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/remap/compiler.rb
CHANGED
|
@@ -5,6 +5,8 @@ module Remap
|
|
|
5
5
|
|
|
6
6
|
# Constructs a {Rule} from the block passed to {Remap::Base.define}
|
|
7
7
|
class Compiler < Proxy
|
|
8
|
+
extend Catchable
|
|
9
|
+
include Catchable
|
|
8
10
|
# @return [Array<Rule>]
|
|
9
11
|
param :rules, type: Types.Array(Rule)
|
|
10
12
|
|
|
@@ -29,7 +31,7 @@ module Remap
|
|
|
29
31
|
# rule.call(state, &error).fetch(:value) # => { name: "John", age: 50 }
|
|
30
32
|
#
|
|
31
33
|
# @return [Rule]
|
|
32
|
-
def self.call(&block)
|
|
34
|
+
def self.call(backtrace: caller, &block)
|
|
33
35
|
unless block_given?
|
|
34
36
|
return Rule::VOID
|
|
35
37
|
end
|
|
@@ -38,7 +40,7 @@ module Remap
|
|
|
38
40
|
compiler.instance_exec(&block)
|
|
39
41
|
end.rules
|
|
40
42
|
|
|
41
|
-
Rule::Block.new(rules)
|
|
43
|
+
Rule::Block.new(backtrace: backtrace, rules: rules)
|
|
42
44
|
end
|
|
43
45
|
|
|
44
46
|
# Maps input path [input] to output path [to]
|
|
@@ -181,13 +183,15 @@ module Remap
|
|
|
181
183
|
raise ArgumentError, "#embed does not take a block"
|
|
182
184
|
end
|
|
183
185
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
next error[failure]
|
|
187
|
-
end.except(:mapper, :scope)
|
|
186
|
+
Types::Mapper[mapper] do
|
|
187
|
+
raise ArgumentError, "Argument to #embed must be a mapper, got #{mapper.class}"
|
|
188
188
|
end
|
|
189
189
|
|
|
190
|
-
add
|
|
190
|
+
result = rule(backtrace: backtrace).add do |s0|
|
|
191
|
+
build_embed(s0, mapper, backtrace)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
add result
|
|
191
195
|
end
|
|
192
196
|
|
|
193
197
|
# Set a static value
|
|
@@ -230,6 +234,10 @@ module Remap
|
|
|
230
234
|
raise ArgumentError, "#set does not take a block"
|
|
231
235
|
end
|
|
232
236
|
|
|
237
|
+
unless to.is_a?(Static)
|
|
238
|
+
raise ArgumentError, "Argument to #set must be a static value, got #{to.class}"
|
|
239
|
+
end
|
|
240
|
+
|
|
233
241
|
add rule(to: path, backtrace: backtrace).add { to.call(_1) }
|
|
234
242
|
end
|
|
235
243
|
|
|
@@ -346,6 +354,10 @@ module Remap
|
|
|
346
354
|
raise ArgumentError, "#wrap requires a block"
|
|
347
355
|
end
|
|
348
356
|
|
|
357
|
+
unless type == :array
|
|
358
|
+
raise ArgumentError, "Argument to #wrap must equal :array, got [#{type}] (#{type.class})"
|
|
359
|
+
end
|
|
360
|
+
|
|
349
361
|
add rule(backtrace: backtrace, &block).then { Array.wrap(_1) }
|
|
350
362
|
end
|
|
351
363
|
|
|
@@ -526,7 +538,7 @@ module Remap
|
|
|
526
538
|
input: path.flatten
|
|
527
539
|
},
|
|
528
540
|
backtrace: backtrace,
|
|
529
|
-
rule: call(&block)
|
|
541
|
+
rule: call(backtrace: backtrace, &block)
|
|
530
542
|
})
|
|
531
543
|
end
|
|
532
544
|
|
|
@@ -536,8 +548,23 @@ module Remap
|
|
|
536
548
|
output: [to].flatten,
|
|
537
549
|
input: path.flatten
|
|
538
550
|
},
|
|
539
|
-
rule: call(&block)
|
|
551
|
+
rule: call(backtrace: backtrace, &block)
|
|
540
552
|
})
|
|
541
553
|
end
|
|
554
|
+
|
|
555
|
+
def build_embed(s0, mapper, backtrace)
|
|
556
|
+
f0 = catch_fatal do |fatal_id|
|
|
557
|
+
s1 = s0.set(fatal_id: fatal_id)
|
|
558
|
+
s2 = s1.set(mapper: mapper)
|
|
559
|
+
old_mapper = s0.fetch(:mapper)
|
|
560
|
+
|
|
561
|
+
return mapper.call!(s2) do |f1|
|
|
562
|
+
s3 = s2.set(notices: f1.notices + f1.failures)
|
|
563
|
+
s3.return!
|
|
564
|
+
end.except(:scope).merge(mapper: old_mapper)
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
raise f0.exception(backtrace)
|
|
568
|
+
end
|
|
542
569
|
end
|
|
543
570
|
end
|
|
@@ -27,19 +27,12 @@ module Remap
|
|
|
27
27
|
#
|
|
28
28
|
# @return [State]
|
|
29
29
|
def call(state)
|
|
30
|
-
super.fmap do |input
|
|
30
|
+
super.fmap do |input|
|
|
31
31
|
unless input.is_a?(Hash)
|
|
32
|
-
|
|
32
|
+
raise ArgumentError, "Expected Hash, got #{input.class}"
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
target.public_send(id, **input)
|
|
36
|
-
rescue ArgumentError => e
|
|
37
|
-
raise e.exception("Failed to create [%p] with input [%s] (%s}) using method %s" % [
|
|
38
|
-
target,
|
|
39
|
-
input,
|
|
40
|
-
input.class,
|
|
41
|
-
id
|
|
42
|
-
])
|
|
43
36
|
end
|
|
44
37
|
end
|
|
45
38
|
end
|
|
@@ -4,6 +4,11 @@ module Remap
|
|
|
4
4
|
module Extensions
|
|
5
5
|
module Object
|
|
6
6
|
refine ::Object do
|
|
7
|
+
# @return [Any]
|
|
8
|
+
def to_hash
|
|
9
|
+
self
|
|
10
|
+
end
|
|
11
|
+
|
|
7
12
|
# Fallback validation method
|
|
8
13
|
#
|
|
9
14
|
# @yield if block is provided
|
|
@@ -43,6 +48,7 @@ module Remap
|
|
|
43
48
|
end
|
|
44
49
|
alias_method :fetch, :get
|
|
45
50
|
|
|
51
|
+
# return [Any]
|
|
46
52
|
def formatted
|
|
47
53
|
self
|
|
48
54
|
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Remap
|
|
4
|
+
class Failure
|
|
5
|
+
using Extensions::Hash
|
|
6
|
+
|
|
7
|
+
class Error < Error
|
|
8
|
+
extend Dry::Initializer
|
|
9
|
+
|
|
10
|
+
option :failure, type: Types.Instance(Failure)
|
|
11
|
+
delegate_missing_to :failure
|
|
12
|
+
|
|
13
|
+
# @return [String]
|
|
14
|
+
def inspect
|
|
15
|
+
"#<%s %s>" % [self.class, to_hash.formatted]
|
|
16
|
+
end
|
|
17
|
+
alias to_s inspect
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
data/lib/remap/failure.rb
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Remap
|
|
4
|
-
using Extensions::Hash
|
|
5
|
-
|
|
6
4
|
class Failure < Dry::Concrete
|
|
7
5
|
attribute :failures, Types.Array(Types::Notice), min_size: 1
|
|
8
6
|
attribute? :notices, Types.Array(Types::Notice), default: EMPTY_ARRAY
|
|
@@ -29,9 +27,13 @@ module Remap
|
|
|
29
27
|
new(failure)
|
|
30
28
|
end
|
|
31
29
|
|
|
32
|
-
# @
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
# @param backtrace [Array<String>] Backtrace from Kernel.caller
|
|
31
|
+
#
|
|
32
|
+
# @return [Failure::Error]
|
|
33
|
+
def exception(backtrace)
|
|
34
|
+
e = Error.new(failure: self)
|
|
35
|
+
e.set_backtrace(backtrace)
|
|
36
|
+
e
|
|
35
37
|
end
|
|
36
38
|
end
|
|
37
39
|
end
|
|
@@ -26,12 +26,9 @@ module Remap
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def reduce(state, value, index, &block)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
state.set(notice: notice)
|
|
29
|
+
s0 = block[value, index: index]
|
|
30
|
+
s1 = s0.set(**state.only(:ids, :fatal_id))
|
|
31
|
+
state.combine(s1.fmap { [_1] })
|
|
35
32
|
end
|
|
36
33
|
end
|
|
37
34
|
end
|
data/lib/remap/iteration/hash.rb
CHANGED
|
@@ -22,12 +22,9 @@ module Remap
|
|
|
22
22
|
private
|
|
23
23
|
|
|
24
24
|
def reduce(state, key, value, &block)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
state.set(notice: notice)
|
|
25
|
+
s0 = block[value, key: key]
|
|
26
|
+
s1 = s0.set(fatal_id: state.fatal_id, ids: state.ids)
|
|
27
|
+
state.combine(s1.fmap { { key => _1 } })
|
|
31
28
|
end
|
|
32
29
|
|
|
33
30
|
def init
|
data/lib/remap/operation.rb
CHANGED
|
@@ -7,32 +7,37 @@ module Remap
|
|
|
7
7
|
module Operation
|
|
8
8
|
# Public interface for mappers
|
|
9
9
|
#
|
|
10
|
-
# @param input [Any]
|
|
11
|
-
# @param options [Hash]
|
|
10
|
+
# @param input [Any] data to be mapped
|
|
11
|
+
# @param options [Hash] mapper options
|
|
12
12
|
#
|
|
13
|
-
# @yield [Failure]
|
|
13
|
+
# @yield [Failure]
|
|
14
|
+
# when a non-critical error occurs
|
|
15
|
+
# @yieldreturn T
|
|
14
16
|
#
|
|
15
|
-
# @return [
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
# @return [Any, T]
|
|
18
|
+
# when request is a success
|
|
19
|
+
# @raise [Remap::Error]
|
|
20
|
+
# when a fatal error occurs
|
|
21
|
+
def call(input, backtrace: caller, **options, &error)
|
|
22
|
+
unless block_given?
|
|
18
23
|
return call(input, **options) do |failure|
|
|
19
|
-
raise failure.exception
|
|
24
|
+
raise failure.exception(backtrace)
|
|
20
25
|
end
|
|
21
26
|
end
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
s0 = State.call(input, options: options, mapper: self)._
|
|
29
|
+
|
|
30
|
+
s1 = call!(s0) do |failure|
|
|
31
|
+
return error[failure]
|
|
27
32
|
end
|
|
28
33
|
|
|
29
|
-
case
|
|
30
|
-
in { value: }
|
|
34
|
+
case s1
|
|
35
|
+
in { value: value }
|
|
31
36
|
value
|
|
32
37
|
in { notices: [] }
|
|
33
|
-
error[
|
|
38
|
+
error[s1.failure("No data could be mapped")]
|
|
34
39
|
in { notices: }
|
|
35
|
-
error[Failure.
|
|
40
|
+
error[Failure.new(failures: notices)]
|
|
36
41
|
end
|
|
37
42
|
end
|
|
38
43
|
end
|
data/lib/remap/rule/block.rb
CHANGED
|
@@ -6,6 +6,7 @@ module Remap
|
|
|
6
6
|
|
|
7
7
|
class Block < Unit
|
|
8
8
|
# @return [Array<Rule>]
|
|
9
|
+
attribute :backtrace, [String], min_size: 1
|
|
9
10
|
attribute :rules, [Types::Rule]
|
|
10
11
|
|
|
11
12
|
# Represents a non-empty define block with one or more rules
|
|
@@ -14,24 +15,30 @@ module Remap
|
|
|
14
15
|
# @param state [State]
|
|
15
16
|
#
|
|
16
17
|
# @return [State]
|
|
17
|
-
def call(state
|
|
18
|
-
|
|
19
|
-
raise ArgumentError, "Block#call(state, &error) requires a block"
|
|
20
|
-
end
|
|
18
|
+
def call(state)
|
|
19
|
+
s0 = state.except(:value)
|
|
21
20
|
|
|
22
21
|
if rules.empty?
|
|
23
|
-
return
|
|
22
|
+
return s0
|
|
24
23
|
end
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
failure = catch_fatal do |fatal_id|
|
|
26
|
+
s1 = s0.set(fatal_id: fatal_id)
|
|
27
|
+
s4 = state.set(fatal_id: fatal_id)
|
|
28
|
+
|
|
29
|
+
return catch_ignored do |id|
|
|
30
|
+
s2 = s1.set(id: id)
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
rules.reduce(s2) do |s3, rule|
|
|
33
|
+
s5 = s3
|
|
34
|
+
s6 = rule.call(s4)
|
|
35
|
+
s7 = s6.set(id: id)
|
|
36
|
+
s5.combine(s7)
|
|
37
|
+
end
|
|
38
|
+
end.remove_id.remove_fatal_id
|
|
34
39
|
end
|
|
40
|
+
|
|
41
|
+
raise failure.exception(backtrace)
|
|
35
42
|
end
|
|
36
43
|
end
|
|
37
44
|
end
|
|
@@ -30,14 +30,8 @@ module Remap
|
|
|
30
30
|
# @param state [State]
|
|
31
31
|
#
|
|
32
32
|
# @return [State]
|
|
33
|
-
def call(state
|
|
34
|
-
|
|
35
|
-
raise ArgumentError, "map.call(state, &error) requires a block"
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
super
|
|
39
|
-
rescue Notice::Ignore => e
|
|
40
|
-
e.undefined(state)
|
|
33
|
+
def call(state)
|
|
34
|
+
catch { super(state.set(id: _1)).except(:id) }
|
|
41
35
|
end
|
|
42
36
|
end
|
|
43
37
|
end
|
|
@@ -8,40 +8,7 @@ module Remap
|
|
|
8
8
|
class Required < Concrete
|
|
9
9
|
attribute :backtrace, Types::Backtrace
|
|
10
10
|
|
|
11
|
-
#
|
|
12
|
-
# When it fails, the entire mapping is marked as failed
|
|
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
|
-
# # ...
|
|
29
|
-
# end
|
|
30
|
-
#
|
|
31
|
-
# output.fetch(:value) # => { nickname: "John" }
|
|
32
|
-
#
|
|
33
|
-
# @param state [State]
|
|
34
|
-
#
|
|
35
|
-
# @return [State]
|
|
36
|
-
def call(state, &error)
|
|
37
|
-
unless block_given?
|
|
38
|
-
raise ArgumentError, "Required.call(state, &error) requires a block"
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
super
|
|
42
|
-
rescue Notice::Ignore => e
|
|
43
|
-
error[e.failure(state)]
|
|
44
|
-
end
|
|
11
|
+
# TODO: Remove
|
|
45
12
|
end
|
|
46
13
|
end
|
|
47
14
|
end
|
data/lib/remap/rule/map.rb
CHANGED
|
@@ -42,29 +42,28 @@ module Remap
|
|
|
42
42
|
# @return [State]
|
|
43
43
|
#
|
|
44
44
|
# @abstract
|
|
45
|
-
def call(state
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
end
|
|
45
|
+
def call(state)
|
|
46
|
+
failure = catch_fatal do |fatal_id|
|
|
47
|
+
s0 = state.set(fatal_id: fatal_id)
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
s2 = path.input.call(s0) do |s1|
|
|
50
|
+
s2 = rule.call(s1)
|
|
51
|
+
callback(s2)
|
|
53
52
|
end
|
|
54
53
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
s3 = s2.then(&path.output)
|
|
55
|
+
s4 = s3.set(path: state.path)
|
|
56
|
+
s5 = s4.except(:key)
|
|
57
|
+
|
|
58
|
+
return s5.remove_fatal_id
|
|
59
|
+
end
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
rescue Notice::Fatal => e
|
|
62
|
-
raise e.traced(backtrace)
|
|
61
|
+
raise failure.exception(backtrace)
|
|
63
62
|
end
|
|
64
63
|
|
|
65
64
|
# A post-processor method
|
|
66
65
|
#
|
|
67
|
-
# @example
|
|
66
|
+
# @example Up-case mapped value
|
|
68
67
|
# state = Remap::State.call("Hello World")
|
|
69
68
|
# map = Remap::Rule::Map.call({})
|
|
70
69
|
# upcase = map.adjust(&:upcase)
|
|
@@ -198,16 +197,8 @@ module Remap
|
|
|
198
197
|
end
|
|
199
198
|
|
|
200
199
|
# @return [Proc]
|
|
201
|
-
def callback(state
|
|
202
|
-
|
|
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]
|
|
209
|
-
end
|
|
210
|
-
end
|
|
200
|
+
def callback(state)
|
|
201
|
+
fn.reduce(state) { |s1, f1| f1[s1] }
|
|
211
202
|
end
|
|
212
203
|
end
|
|
213
204
|
end
|
data/lib/remap/rule/void.rb
CHANGED
data/lib/remap/rule.rb
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
module Remap
|
|
4
4
|
class Rule < Dry::Interface
|
|
5
|
+
using Extensions::Hash
|
|
6
|
+
using Extensions::Array
|
|
7
|
+
using Extensions::Object
|
|
8
|
+
include Catchable
|
|
5
9
|
defines :requirement
|
|
6
10
|
requirement Types::Any
|
|
7
11
|
|
|
@@ -13,5 +17,16 @@ module Remap
|
|
|
13
17
|
def call(state)
|
|
14
18
|
raise NotImplementedError, "#{self.class}#call not implemented"
|
|
15
19
|
end
|
|
20
|
+
|
|
21
|
+
# @return [String]
|
|
22
|
+
def inspect
|
|
23
|
+
"#<#{self.class} #{to_hash.formatted}>"
|
|
24
|
+
end
|
|
25
|
+
alias to_s inspect
|
|
26
|
+
|
|
27
|
+
# @return [Hash]
|
|
28
|
+
def to_hash
|
|
29
|
+
attributes.transform_values(&:to_hash).except(:backtrace)
|
|
30
|
+
end
|
|
16
31
|
end
|
|
17
32
|
end
|
data/lib/remap/selector/index.rb
CHANGED
data/lib/remap/selector/key.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Remap
|
|
4
4
|
module State
|
|
5
|
+
# @api private
|
|
5
6
|
module Extension
|
|
6
7
|
using Extensions::Enumerable
|
|
7
8
|
using Extensions::Object
|
|
@@ -54,7 +55,11 @@ module Remap
|
|
|
54
55
|
|
|
55
56
|
# Throws :fatal containing a Notice
|
|
56
57
|
def fatal!(...)
|
|
57
|
-
|
|
58
|
+
fatal_id = fetch(:fatal_id) do
|
|
59
|
+
raise ArgumentError, "Missing :fatal_id in %s" % formatted
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
throw fatal_id, Failure.new(failures: [notice(...)], notices: notices)
|
|
58
63
|
end
|
|
59
64
|
|
|
60
65
|
# Throws :warn containing a Notice
|
|
@@ -64,7 +69,7 @@ module Remap
|
|
|
64
69
|
|
|
65
70
|
# Throws :ignore containing a Notice
|
|
66
71
|
def ignore!(...)
|
|
67
|
-
notice(...).
|
|
72
|
+
set(notice: notice(...)).except(:value).return!
|
|
68
73
|
end
|
|
69
74
|
|
|
70
75
|
# Creates a notice containing the given message
|
|
@@ -88,7 +93,9 @@ module Remap
|
|
|
88
93
|
# @return [self]
|
|
89
94
|
def _(&block)
|
|
90
95
|
unless block
|
|
91
|
-
return _
|
|
96
|
+
return _ do |reason|
|
|
97
|
+
raise ArgumentError, "[BUG] State: #{formatted} reason: #{reason.formatted}"
|
|
98
|
+
end
|
|
92
99
|
end
|
|
93
100
|
|
|
94
101
|
unless (result = Schema.call(self)).success?
|
|
@@ -98,11 +105,11 @@ module Remap
|
|
|
98
105
|
self
|
|
99
106
|
end
|
|
100
107
|
|
|
101
|
-
#
|
|
108
|
+
# Iterates over {#value}
|
|
102
109
|
#
|
|
103
110
|
# @yieldparam value [Any]
|
|
104
|
-
# @
|
|
105
|
-
# @
|
|
111
|
+
# @yieldparam key [Symbol]
|
|
112
|
+
# @yieldparam index [Integer]
|
|
106
113
|
#
|
|
107
114
|
# @yieldreturn [State]
|
|
108
115
|
#
|
|
@@ -131,7 +138,7 @@ module Remap
|
|
|
131
138
|
in [:value, Array => list1, Array => list2]
|
|
132
139
|
list1 + list2
|
|
133
140
|
in [:value, left, right]
|
|
134
|
-
fatal!(
|
|
141
|
+
other.fatal!(
|
|
135
142
|
"Could not merge [%s] (%s) with [%s] (%s)",
|
|
136
143
|
left.formatted,
|
|
137
144
|
left.class,
|
|
@@ -140,10 +147,47 @@ module Remap
|
|
|
140
147
|
)
|
|
141
148
|
in [:notices, Array => n1, Array => n2]
|
|
142
149
|
n1 + n2
|
|
150
|
+
in [:ids, i1, i2] if i1.all? { i2.include?(_1) }
|
|
151
|
+
i2
|
|
152
|
+
in [:ids, i1, i2] if i2.all? { i1.include?(_1) }
|
|
153
|
+
i1
|
|
154
|
+
in [:ids, i1, i2]
|
|
155
|
+
other.fatal!("Could not merge #ids [%s] (%s) with [%s] (%s)", i1, i1.class, i2,
|
|
156
|
+
i2.class)
|
|
143
157
|
in [Symbol, _, value]
|
|
144
158
|
value
|
|
145
159
|
end
|
|
146
|
-
end
|
|
160
|
+
end._
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# @todo Merge with {#remove_fatal_id}
|
|
164
|
+
# @return [State]
|
|
165
|
+
def remove_id
|
|
166
|
+
case self
|
|
167
|
+
in { ids: [], id: }
|
|
168
|
+
except(:id)
|
|
169
|
+
in { ids:, id: }
|
|
170
|
+
merge(ids: ids[1...], id: ids[0])
|
|
171
|
+
in { ids: [] }
|
|
172
|
+
self
|
|
173
|
+
in { ids: }
|
|
174
|
+
raise ArgumentError, "[BUG] #ids for state are set, but not #id: %s" % formatted
|
|
175
|
+
end._
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# @todo Merge with {#remove_id}
|
|
179
|
+
# @return [State]
|
|
180
|
+
def remove_fatal_id
|
|
181
|
+
case self
|
|
182
|
+
in { fatal_ids: [], fatal_id: }
|
|
183
|
+
except(:fatal_id)
|
|
184
|
+
in { fatal_ids: ids, fatal_id: id }
|
|
185
|
+
merge(fatal_ids: ids[1...], fatal_id: ids[0])
|
|
186
|
+
in { fatal_ids: [] }
|
|
187
|
+
self
|
|
188
|
+
in { fatal_ids: }
|
|
189
|
+
raise ArgumentError, "[BUG] #ids for state are set, but not #id: %s" % formatted
|
|
190
|
+
end._
|
|
147
191
|
end
|
|
148
192
|
|
|
149
193
|
# Creates a new state with params
|
|
@@ -168,6 +212,10 @@ module Remap
|
|
|
168
212
|
merge(path: path + [index], element: value, index: index, value: value).set(**rest)
|
|
169
213
|
in [{path:}, {index:, **rest}]
|
|
170
214
|
merge(path: path + [index], index: index).set(**rest)
|
|
215
|
+
in [{ids:, id: old_id}, {id: new_id, **rest}]
|
|
216
|
+
merge(ids: [old_id] + ids, id: new_id).set(**rest)
|
|
217
|
+
in [{fatal_ids:, fatal_id: old_id}, {fatal_id: new_id, **rest}]
|
|
218
|
+
merge(fatal_ids: [old_id] + fatal_ids, fatal_id: new_id).set(**rest)
|
|
171
219
|
else
|
|
172
220
|
merge(options)
|
|
173
221
|
end
|
|
@@ -185,8 +233,8 @@ module Remap
|
|
|
185
233
|
#
|
|
186
234
|
# @return [State<Y>]
|
|
187
235
|
def fmap(**options, &block)
|
|
188
|
-
bind(**options) do |input, state
|
|
189
|
-
state.set(block[input, state,
|
|
236
|
+
bind(**options) do |input, state:|
|
|
237
|
+
state.set(block[input, state, state: state])
|
|
190
238
|
end
|
|
191
239
|
end
|
|
192
240
|
|
|
@@ -203,28 +251,7 @@ module Remap
|
|
|
203
251
|
# end
|
|
204
252
|
|
|
205
253
|
def failure(reason = Undefined)
|
|
206
|
-
|
|
207
|
-
in [_, Notice => notice]
|
|
208
|
-
[notice]
|
|
209
|
-
in [path, Array => reasons]
|
|
210
|
-
reasons.map do |inner_reason|
|
|
211
|
-
Notice.call(path: path, reason: inner_reason, **only(:value))
|
|
212
|
-
end
|
|
213
|
-
in [path, String => reason]
|
|
214
|
-
[Notice.call(path: path, reason: reason, **only(:value))]
|
|
215
|
-
in [path, Hash => errors]
|
|
216
|
-
errors.paths.flat_map do |sufix|
|
|
217
|
-
Array.wrap(errors.dig(*sufix)).map do |inner_reason|
|
|
218
|
-
Notice.call(
|
|
219
|
-
reason: inner_reason,
|
|
220
|
-
path: path + sufix,
|
|
221
|
-
**only(:value)
|
|
222
|
-
)
|
|
223
|
-
end
|
|
224
|
-
end
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
Failure.new(failures: failures, notices: notices)
|
|
254
|
+
raise NotImplementedError, "Not implemented"
|
|
228
255
|
end
|
|
229
256
|
|
|
230
257
|
# Passes {#value} to block, if defined
|
|
@@ -242,12 +269,10 @@ module Remap
|
|
|
242
269
|
raise ArgumentError, "State#bind requires a block"
|
|
243
270
|
end
|
|
244
271
|
|
|
245
|
-
|
|
272
|
+
s1 = set(**options)
|
|
246
273
|
|
|
247
|
-
fetch(:value) { return
|
|
248
|
-
block[value,
|
|
249
|
-
return s.set(**other).ignore!(reason)
|
|
250
|
-
end
|
|
274
|
+
fetch(:value) { return s1 }.then do |value|
|
|
275
|
+
block[value, s1, state: s1]
|
|
251
276
|
end
|
|
252
277
|
end
|
|
253
278
|
|
|
@@ -259,11 +284,11 @@ module Remap
|
|
|
259
284
|
#
|
|
260
285
|
# @return [State<U>]
|
|
261
286
|
def execute(&block)
|
|
262
|
-
bind do |value
|
|
263
|
-
result = context(value
|
|
287
|
+
bind do |value|
|
|
288
|
+
result = context(value).instance_exec(value, &block)
|
|
264
289
|
|
|
265
290
|
if result.equal?(Dry::Core::Constants::Undefined)
|
|
266
|
-
|
|
291
|
+
ignore!("Undefined returned, skipping!")
|
|
267
292
|
end
|
|
268
293
|
|
|
269
294
|
set(result)
|
|
@@ -290,6 +315,21 @@ module Remap
|
|
|
290
315
|
fetch(:path, EMPTY_ARRAY)
|
|
291
316
|
end
|
|
292
317
|
|
|
318
|
+
# @return [Symbol]
|
|
319
|
+
def id
|
|
320
|
+
fetch(:id)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# @return [Array<Symbol>]
|
|
324
|
+
def ids
|
|
325
|
+
fetch(:ids)
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# @return [Symbol]
|
|
329
|
+
def fatal_id
|
|
330
|
+
fetch(:fatal_id)
|
|
331
|
+
end
|
|
332
|
+
|
|
293
333
|
# Represents options to a mapper
|
|
294
334
|
#
|
|
295
335
|
# @see Rule::Embed
|
|
@@ -303,7 +343,7 @@ module Remap
|
|
|
303
343
|
#
|
|
304
344
|
# @return [Hash]
|
|
305
345
|
def to_hash
|
|
306
|
-
super.except(:options, :notices, :value)
|
|
346
|
+
super.except(:options, :notices, :value, :id, :ids, :fatal_id, :fatal_ids)
|
|
307
347
|
end
|
|
308
348
|
|
|
309
349
|
# @return [Any]
|
|
@@ -331,6 +371,48 @@ module Remap
|
|
|
331
371
|
fetch(:notices)
|
|
332
372
|
end
|
|
333
373
|
|
|
374
|
+
# Creates a failure from the current state
|
|
375
|
+
#
|
|
376
|
+
# @param reason [String, Hash, Undefined]
|
|
377
|
+
#
|
|
378
|
+
# @return [Failure]
|
|
379
|
+
def failure(reason = Undefined)
|
|
380
|
+
failures = case [path, reason]
|
|
381
|
+
in [_, Notice => notice]
|
|
382
|
+
[notice]
|
|
383
|
+
in [path, Array => reasons]
|
|
384
|
+
reasons.map do |inner_reason|
|
|
385
|
+
Notice.call(path: path, reason: inner_reason, **only(:value))
|
|
386
|
+
end
|
|
387
|
+
in [path, String => reason]
|
|
388
|
+
[Notice.call(path: path, reason: reason, **only(:value))]
|
|
389
|
+
in [path, Hash => errors]
|
|
390
|
+
errors.paths.flat_map do |sufix|
|
|
391
|
+
Array.wrap(errors.dig(*sufix)).map do |inner_reason|
|
|
392
|
+
Notice.call(
|
|
393
|
+
reason: inner_reason,
|
|
394
|
+
path: path + sufix,
|
|
395
|
+
**only(:value)
|
|
396
|
+
)
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
Failure.new(failures: failures, notices: notices)
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
# @raise [ArgumentError]
|
|
405
|
+
# when {#id} is not defined
|
|
406
|
+
#
|
|
407
|
+
# @private
|
|
408
|
+
def return!
|
|
409
|
+
id = fetch(:id) do
|
|
410
|
+
raise ArgumentError, "#id not defined for state [%s]" % [formatted]
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
throw id, remove_id
|
|
414
|
+
end
|
|
415
|
+
|
|
334
416
|
private
|
|
335
417
|
|
|
336
418
|
# Creates a context containing {options} and {self}
|
|
@@ -340,13 +422,13 @@ module Remap
|
|
|
340
422
|
# @yieldparam reason [T]
|
|
341
423
|
#
|
|
342
424
|
# @return [Struct]
|
|
343
|
-
def context(value, context: self
|
|
344
|
-
::Struct.new(*keys, *options.keys, :state, keyword_init: true) do
|
|
425
|
+
def context(value, context: self)
|
|
426
|
+
::Struct.new(*except(:id).keys, *options.keys, :state, keyword_init: true) do
|
|
345
427
|
define_method :method_missing do |name, *|
|
|
346
|
-
|
|
428
|
+
context.fatal!("Method [%s] not defined", name)
|
|
347
429
|
end
|
|
348
430
|
|
|
349
|
-
define_method
|
|
431
|
+
define_method(:skip!) do |message = "Manual skip!"|
|
|
350
432
|
context.ignore!(message)
|
|
351
433
|
end
|
|
352
434
|
end.new(**to_hash, **options, value: value, state: self)
|
data/lib/remap/state/schema.rb
CHANGED
|
@@ -9,6 +9,8 @@ module Remap
|
|
|
9
9
|
required(:notices).array(Types.Instance(Notice))
|
|
10
10
|
required(:options).value(:hash)
|
|
11
11
|
required(:path).array(Types::Key)
|
|
12
|
+
required(:ids).value(:array)
|
|
13
|
+
required(:fatal_ids).value(:array)
|
|
12
14
|
|
|
13
15
|
optional(:index).filled(:integer)
|
|
14
16
|
optional(:element).filled
|
data/lib/remap/state.rb
CHANGED
|
@@ -31,9 +31,11 @@ module Remap
|
|
|
31
31
|
# @return [Hash] A valid state
|
|
32
32
|
def self.call(value, mapper: Dummy, options: EMPTY_HASH)
|
|
33
33
|
{
|
|
34
|
+
fatal_ids: EMPTY_ARRAY,
|
|
34
35
|
notices: EMPTY_ARRAY,
|
|
35
36
|
path: EMPTY_ARRAY,
|
|
36
37
|
options: options,
|
|
38
|
+
ids: EMPTY_ARRAY,
|
|
37
39
|
mapper: mapper,
|
|
38
40
|
values: value,
|
|
39
41
|
value: value,
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: remap
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.2.
|
|
4
|
+
version: 2.2.41
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Linus Oleander
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2021-12-
|
|
11
|
+
date: 2021-12-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -248,6 +248,7 @@ extra_rdoc_files: []
|
|
|
248
248
|
files:
|
|
249
249
|
- lib/remap.rb
|
|
250
250
|
- lib/remap/base.rb
|
|
251
|
+
- lib/remap/catchable.rb
|
|
251
252
|
- lib/remap/class_interface.rb
|
|
252
253
|
- lib/remap/compiler.rb
|
|
253
254
|
- lib/remap/constructor.rb
|
|
@@ -256,10 +257,12 @@ files:
|
|
|
256
257
|
- lib/remap/constructor/none.rb
|
|
257
258
|
- lib/remap/contract.rb
|
|
258
259
|
- lib/remap/error.rb
|
|
260
|
+
- lib/remap/extensions/array.rb
|
|
259
261
|
- lib/remap/extensions/enumerable.rb
|
|
260
262
|
- lib/remap/extensions/hash.rb
|
|
261
263
|
- lib/remap/extensions/object.rb
|
|
262
264
|
- lib/remap/failure.rb
|
|
265
|
+
- lib/remap/failure/error.rb
|
|
263
266
|
- lib/remap/iteration.rb
|
|
264
267
|
- lib/remap/iteration/array.rb
|
|
265
268
|
- lib/remap/iteration/hash.rb
|