remap 2.0.3 → 2.1.6
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 +229 -75
- data/lib/remap/compiler.rb +403 -37
- data/lib/remap/constructor/argument.rb +20 -6
- data/lib/remap/constructor/keyword.rb +20 -4
- data/lib/remap/constructor/none.rb +3 -4
- data/lib/remap/constructor.rb +12 -5
- data/lib/remap/contract.rb +27 -0
- data/lib/remap/extensions/enumerable.rb +48 -0
- data/lib/remap/extensions/hash.rb +13 -0
- data/lib/remap/extensions/object.rb +37 -0
- data/lib/remap/failure.rb +25 -15
- data/lib/remap/iteration/array.rb +20 -11
- data/lib/remap/iteration/hash.rb +21 -13
- data/lib/remap/iteration/other.rb +7 -7
- data/lib/remap/iteration.rb +8 -2
- data/lib/remap/mapper/and.rb +29 -7
- data/lib/remap/mapper/binary.rb +3 -6
- data/lib/remap/mapper/or.rb +29 -6
- data/lib/remap/mapper/support/operations.rb +40 -0
- data/lib/remap/mapper/xor.rb +29 -7
- data/lib/remap/mapper.rb +1 -48
- data/lib/remap/notice/traced.rb +19 -0
- data/lib/remap/notice/untraced.rb +11 -0
- data/lib/remap/notice.rb +34 -0
- data/lib/remap/operation.rb +26 -13
- data/lib/remap/path/input.rb +37 -0
- data/lib/remap/path/output.rb +26 -0
- data/lib/remap/path.rb +22 -0
- data/lib/remap/path_error.rb +13 -0
- data/lib/remap/proxy.rb +18 -0
- data/lib/remap/rule/each.rb +25 -24
- data/lib/remap/rule/embed.rb +33 -28
- data/lib/remap/rule/map/optional.rb +42 -0
- data/lib/remap/rule/map/required.rb +35 -0
- data/lib/remap/rule/map.rb +176 -55
- data/lib/remap/rule/set.rb +23 -33
- data/lib/remap/rule/support/collection/empty.rb +7 -7
- data/lib/remap/rule/support/collection/filled.rb +21 -8
- data/lib/remap/rule/support/collection.rb +11 -3
- data/lib/remap/rule/support/enum.rb +44 -21
- data/lib/remap/rule/void.rb +17 -18
- data/lib/remap/rule/wrap.rb +25 -17
- data/lib/remap/rule.rb +8 -1
- data/lib/remap/selector/all.rb +29 -7
- data/lib/remap/selector/index.rb +24 -16
- data/lib/remap/selector/key.rb +31 -16
- data/lib/remap/selector.rb +17 -0
- data/lib/remap/state/extension.rb +182 -208
- data/lib/remap/state/schema.rb +1 -1
- data/lib/remap/state.rb +30 -4
- data/lib/remap/static/fixed.rb +14 -3
- data/lib/remap/static/option.rb +21 -6
- data/lib/remap/static.rb +13 -0
- data/lib/remap/struct.rb +1 -0
- data/lib/remap/types.rb +13 -28
- data/lib/remap.rb +15 -19
- metadata +91 -89
- data/lib/remap/result.rb +0 -11
- data/lib/remap/rule/support/path.rb +0 -45
- data/lib/remap/state/types.rb +0 -11
- data/lib/remap/success.rb +0 -29
- data/lib/remap/version.rb +0 -5
data/lib/remap/operation.rb
CHANGED
@@ -1,26 +1,39 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Remap
|
4
|
-
|
5
|
-
using State::Extension
|
6
|
-
include State
|
4
|
+
using State::Extension
|
7
5
|
|
6
|
+
# Class interface for {Remap::Base} and instance interface for {Mapper}
|
7
|
+
module Operation
|
8
|
+
# Public interface for mappers
|
9
|
+
#
|
10
|
+
# @param input [Any] Data to be mapped
|
11
|
+
# @param options [Hash] Mapper arguments
|
12
|
+
#
|
13
|
+
# @yield [Failure] if mapper fails
|
14
|
+
#
|
15
|
+
# @return [Success] if mapper succeeds
|
8
16
|
def call(input, **options, &error)
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
17
|
+
unless error
|
18
|
+
return call(input, **options) do |failure|
|
19
|
+
raise failure.exception
|
20
|
+
end
|
13
21
|
end
|
14
22
|
|
15
|
-
|
16
|
-
|
23
|
+
other = State.call(input, options: options, mapper: self).then do |state|
|
24
|
+
call!(state) do |failure|
|
25
|
+
return error[failure]
|
26
|
+
end
|
17
27
|
end
|
18
28
|
|
19
|
-
|
20
|
-
|
29
|
+
case other
|
30
|
+
in { value: }
|
31
|
+
value
|
32
|
+
in { notices: [] }
|
33
|
+
error[other.failure("No return value")]
|
34
|
+
in { notices: }
|
35
|
+
error[Failure.call(failures: notices)]
|
21
36
|
end
|
22
|
-
|
23
|
-
Success.new(problems: new_state.problems, result: value)
|
24
37
|
end
|
25
38
|
end
|
26
39
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Remap
|
4
|
+
class Path
|
5
|
+
using State::Extension
|
6
|
+
|
7
|
+
# Returns the value at a given path
|
8
|
+
#
|
9
|
+
# @example Select "A" from { a: { b: { c: ["A"] } } }
|
10
|
+
# state = Remap::State.call({ a: { b: { c: ["A"] } } })
|
11
|
+
# first = Remap::Selector::Index.new(index: 0)
|
12
|
+
# path = Remap::Path::Input.new([:a, :b, :c, first])
|
13
|
+
#
|
14
|
+
# path.call(state) do |state|
|
15
|
+
# state.fetch(:value)
|
16
|
+
# end
|
17
|
+
class Input < Unit
|
18
|
+
# @return [Array<Selector>]
|
19
|
+
attribute :segments, [Selector]
|
20
|
+
|
21
|
+
# Selects the value at the path {#segments}
|
22
|
+
#
|
23
|
+
# @param state [State]
|
24
|
+
#
|
25
|
+
# @return [State]
|
26
|
+
def call(state, &iterator)
|
27
|
+
unless block_given?
|
28
|
+
raise ArgumentError, "Input path requires an iterator block"
|
29
|
+
end
|
30
|
+
|
31
|
+
segments.reverse.reduce(iterator) do |inner_iterator, selector|
|
32
|
+
-> inner_state { selector.call(inner_state, &inner_iterator) }
|
33
|
+
end.call(state)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Remap
|
4
|
+
class Path
|
5
|
+
using Extensions::Enumerable
|
6
|
+
using State::Extension
|
7
|
+
|
8
|
+
# Sets the value to a given path
|
9
|
+
#
|
10
|
+
# @example Maps "A" to { a: { b: { c: "A" } } }
|
11
|
+
# state = Remap::State.call("A")
|
12
|
+
# result = Remap::Path::Output.new([:a, :b, :c]).call(state)
|
13
|
+
#
|
14
|
+
# result.fetch(:value) # => { a: { b: { c: "A" } } }
|
15
|
+
class Output < Unit
|
16
|
+
attribute :segments, [Types::Key]
|
17
|
+
|
18
|
+
# @return [State]
|
19
|
+
def call(state)
|
20
|
+
state.fmap do |value|
|
21
|
+
segments.hide(value)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/remap/path.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Remap
|
4
|
+
# Represents a sequence of keys and selects or maps a value given a path
|
5
|
+
class Path < Dry::Interface
|
6
|
+
attribute :segments, Types::Array
|
7
|
+
|
8
|
+
delegate :>>, to: :to_proc
|
9
|
+
|
10
|
+
# @return [State]
|
11
|
+
#
|
12
|
+
# @abstract
|
13
|
+
def call(state)
|
14
|
+
raise NotImplementedError, "#{self.class}#call not implemented"
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [Proc]
|
18
|
+
def to_proc
|
19
|
+
method(:call).to_proc
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/remap/proxy.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Remap
|
4
|
+
class Proxy < ActiveSupport::ProxyObject
|
5
|
+
def self.const_missing(name)
|
6
|
+
::Object.const_get(name)
|
7
|
+
end
|
8
|
+
|
9
|
+
include Dry::Core::Constants
|
10
|
+
extend Dry::Initializer
|
11
|
+
|
12
|
+
# See Object#tap
|
13
|
+
def tap(&block)
|
14
|
+
block[self]
|
15
|
+
self
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/remap/rule/each.rb
CHANGED
@@ -2,34 +2,35 @@
|
|
2
2
|
|
3
3
|
module Remap
|
4
4
|
class Rule
|
5
|
-
|
6
|
-
using State::Extension
|
5
|
+
using State::Extension
|
7
6
|
|
8
|
-
|
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
|
9
18
|
|
10
|
-
# Iterates over
|
11
|
-
# Restores
|
19
|
+
# Iterates over state and passes each value to rule
|
20
|
+
# Restores element, key & index before returning state
|
12
21
|
#
|
22
|
+
# @param state [State<Enumerable>]
|
13
23
|
#
|
14
|
-
#
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
#
|
25
|
-
# Mapper.call(people: [{ name: "John" }, { name: "Jane" }]) # => { names: ["John", "Jane"] }
|
26
|
-
#
|
27
|
-
# @param state [State]
|
28
|
-
#
|
29
|
-
# @return [State]
|
30
|
-
def call(state)
|
31
|
-
state.map do |state|
|
32
|
-
rule.call(state)
|
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
|
33
34
|
end
|
34
35
|
end
|
35
36
|
end
|
data/lib/remap/rule/embed.rb
CHANGED
@@ -2,40 +2,45 @@
|
|
2
2
|
|
3
3
|
module Remap
|
4
4
|
class Rule
|
5
|
-
|
6
|
-
using State::Extension
|
5
|
+
using State::Extension
|
7
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!]
|
8
29
|
attribute :mapper, Types::Mapper
|
9
30
|
|
10
|
-
# Evaluates
|
31
|
+
# Evaluates input against mapper and returns the result
|
11
32
|
#
|
12
|
-
# @param state [State]
|
33
|
+
# @param state [State<T>]
|
13
34
|
#
|
14
|
-
# @
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
# end
|
19
|
-
# end
|
20
|
-
#
|
21
|
-
# class Person < Remap::Base
|
22
|
-
# define do
|
23
|
-
# to :person do
|
24
|
-
# to :car do
|
25
|
-
# embed Car
|
26
|
-
# end
|
27
|
-
# end
|
28
|
-
# end
|
29
|
-
# end
|
30
|
-
#
|
31
|
-
# Person.call(name: "Volvo") # => { person: { car: { name: "Volvo" } } }
|
32
|
-
#
|
33
|
-
#
|
34
|
-
# @return [State]
|
35
|
-
def call(state)
|
36
|
-
mapper.call!(state.set(mapper: mapper)) do |error|
|
37
|
-
return state.problem(error)
|
35
|
+
# @return [State<U>]
|
36
|
+
def call(state, &error)
|
37
|
+
unless error
|
38
|
+
raise ArgumentError, "A block is required to evaluate the embed"
|
38
39
|
end
|
40
|
+
|
41
|
+
mapper.call!(state.set(mapper: mapper)) do |failure|
|
42
|
+
return error[failure]
|
43
|
+
end.except(:mapper, :scope)
|
39
44
|
end
|
40
45
|
end
|
41
46
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Remap
|
4
|
+
class Rule
|
5
|
+
class Map
|
6
|
+
using State::Extension
|
7
|
+
|
8
|
+
class Optional < Concrete
|
9
|
+
# Represents an optional mapping rule
|
10
|
+
# When the mapping fails, the value is ignored
|
11
|
+
#
|
12
|
+
# @param state [State]
|
13
|
+
#
|
14
|
+
# @return [State]
|
15
|
+
def call(state, &error)
|
16
|
+
unless error
|
17
|
+
raise ArgumentError, "map.call(state, &error) requires a block"
|
18
|
+
end
|
19
|
+
|
20
|
+
fatal(state) do
|
21
|
+
return ignore(state) do
|
22
|
+
return notice(state) do
|
23
|
+
return super
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# Catches :ignore exceptions and re-package them as a state
|
32
|
+
#
|
33
|
+
# @param state [State]
|
34
|
+
#
|
35
|
+
# @return [State]
|
36
|
+
def ignore(state, &block)
|
37
|
+
state.set(notice: catch(:ignore, &block).traced(backtrace)).except(:value)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Remap
|
4
|
+
class Rule
|
5
|
+
class Map
|
6
|
+
using State::Extension
|
7
|
+
|
8
|
+
class Required < Concrete
|
9
|
+
attribute :backtrace, Types::Backtrace
|
10
|
+
|
11
|
+
# Represents a required mapping rule
|
12
|
+
# When it fails, the entire mapping is marked as failed
|
13
|
+
#
|
14
|
+
# @param state [State]
|
15
|
+
#
|
16
|
+
# @return [State]
|
17
|
+
def call(state, &error)
|
18
|
+
unless block_given?
|
19
|
+
raise ArgumentError, "Required.call(state, &error) requires a block"
|
20
|
+
end
|
21
|
+
|
22
|
+
notice = catch :ignore do
|
23
|
+
return fatal(state) do
|
24
|
+
return notice(state) do
|
25
|
+
return super
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
error[state.failure(notice)]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/remap/rule/map.rb
CHANGED
@@ -2,62 +2,72 @@
|
|
2
2
|
|
3
3
|
module Remap
|
4
4
|
class Rule
|
5
|
-
|
6
|
-
|
5
|
+
using Extensions::Enumerable
|
6
|
+
using State::Extension
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
# Maps an input path to an output path
|
9
|
+
#
|
10
|
+
# @example Map { name: "Ford" } to { person: { name: "Ford" } }
|
11
|
+
# class Mapper < Remap::Base
|
12
|
+
# define do
|
13
|
+
# map :name, to: [:person, :name]
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# Mapper.call({ name: "Ford" }) # => { person: { name: "Ford" } }
|
18
|
+
class Map < Abstract
|
19
|
+
class Path < Struct
|
20
|
+
Output = Remap::Path::Output
|
21
|
+
Input = Remap::Path::Input
|
10
22
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
# 2. For each yielded value
|
15
|
-
# 2.1. Map value using {#rule}
|
16
|
-
# 2.2. Map value using {#fn}
|
23
|
+
attribute :output, Output.default { Output.call(EMPTY_ARRAY) }
|
24
|
+
attribute :input, Input.default { Input.call(EMPTY_ARRAY) }
|
25
|
+
end
|
17
26
|
|
18
|
-
# @
|
19
|
-
|
20
|
-
|
21
|
-
#
|
22
|
-
|
23
|
-
|
24
|
-
#
|
25
|
-
|
26
|
-
|
27
|
+
# @return [Hash]
|
28
|
+
attribute? :path, Path.default { Path.call(EMPTY_HASH) }
|
29
|
+
|
30
|
+
# @return [Rule]
|
31
|
+
attribute :rule, Rule.default { Void.call(EMPTY_HASH) }
|
32
|
+
|
33
|
+
# @return [Array<String>]
|
34
|
+
attribute? :backtrace, Types::Backtrace, default: EMPTY_ARRAY
|
35
|
+
|
36
|
+
order :Optional, :Required
|
37
|
+
|
38
|
+
# Represents a required or optional mapping rule
|
27
39
|
#
|
28
|
-
#
|
40
|
+
# @param state [State]
|
29
41
|
#
|
30
|
-
# @
|
31
|
-
# @param state [State] Current state
|
42
|
+
# @return [State]
|
32
43
|
#
|
33
|
-
# @
|
34
|
-
def call(state)
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
44
|
+
# @abstract
|
45
|
+
def call(state, &error)
|
46
|
+
unless error
|
47
|
+
raise ArgumentError, "Map#call(state, &error) requires error handler block"
|
48
|
+
end
|
49
|
+
|
50
|
+
notice = catch :fatal do
|
51
|
+
return path.input.call(state) do |inner_state|
|
52
|
+
rule.call(inner_state) do |failure|
|
53
|
+
return error[failure]
|
54
|
+
end.then(&callback)
|
55
|
+
end.then(&path.output)
|
41
56
|
end
|
57
|
+
|
58
|
+
raise notice.traced(backtrace).exception
|
42
59
|
end
|
43
60
|
|
44
|
-
#
|
45
|
-
#
|
46
|
-
# @example Add 5 to mapped value
|
47
|
-
# class Mapper < Remap
|
48
|
-
# define do
|
49
|
-
# map.adjust do |value|
|
50
|
-
# value + 5
|
51
|
-
# end
|
52
|
-
# end
|
53
|
-
# end
|
54
|
-
#
|
55
|
-
# Mapper.call(10) # => 15
|
61
|
+
# A post-processor method
|
56
62
|
#
|
57
|
-
# @
|
58
|
-
#
|
63
|
+
# @example Upcase mapped value
|
64
|
+
# state = Remap::State.call("Hello World")
|
65
|
+
# map = Remap::Rule::Map.call({})
|
66
|
+
# upcase = map.adjust(&:upcase)
|
67
|
+
# error = -> failure { raise failure.exception }
|
68
|
+
# upcase.call(state, &error).fetch(:value) # => "HELLO WORLD"
|
59
69
|
#
|
60
|
-
# @return [
|
70
|
+
# @return [Map]
|
61
71
|
def adjust(&block)
|
62
72
|
add do |state|
|
63
73
|
state.execute(&block)
|
@@ -65,45 +75,156 @@ module Remap
|
|
65
75
|
end
|
66
76
|
alias then adjust
|
67
77
|
|
78
|
+
# A pending rule
|
79
|
+
#
|
80
|
+
# @param reason [String]
|
81
|
+
#
|
82
|
+
# @example Pending mapping
|
83
|
+
# state = Remap::State.call(:value)
|
84
|
+
# map = Remap::Rule::Map.call({})
|
85
|
+
# pending = map.pending("this is pending")
|
86
|
+
# error = -> failure { raise failure.exception }
|
87
|
+
# pending.call(state, &error).key?(:value) # => false
|
88
|
+
#
|
89
|
+
# @return [Map]
|
68
90
|
def pending(reason = "Pending mapping")
|
69
91
|
add do |state|
|
70
|
-
state.
|
92
|
+
state.notice!(reason)
|
71
93
|
end
|
72
94
|
end
|
73
95
|
|
96
|
+
# An enumeration processor
|
97
|
+
#
|
98
|
+
# @example A mapped enum
|
99
|
+
# enum = Remap::Rule::Map.call({}).enum do
|
100
|
+
# value "A", "B"
|
101
|
+
# otherwise "C"
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# error = -> failure { raise failure.exception }
|
105
|
+
#
|
106
|
+
# a = Remap::State.call("A")
|
107
|
+
# enum.call(a, &error).fetch(:value) # => "A"
|
108
|
+
#
|
109
|
+
# b = Remap::State.call("B")
|
110
|
+
# enum.call(b, &error).fetch(:value) # => "B"
|
111
|
+
#
|
112
|
+
# c = Remap::State.call("C")
|
113
|
+
# enum.call(c, &error).fetch(:value) # => "C"
|
114
|
+
#
|
115
|
+
# d = Remap::State.call("D")
|
116
|
+
# enum.call(d, &error).fetch(:value) # => "C"
|
117
|
+
#
|
118
|
+
# @return [Map]
|
74
119
|
def enum(&block)
|
75
|
-
add do |
|
76
|
-
|
77
|
-
Enum.call(&block).get(id
|
120
|
+
add do |outer_state|
|
121
|
+
outer_state.fmap do |id, state|
|
122
|
+
Enum.call(&block).get(id) do
|
123
|
+
state.ignore!("Enum value %p (%s) not defined", id, id.class)
|
124
|
+
end
|
78
125
|
end
|
79
126
|
end
|
80
127
|
end
|
81
128
|
|
129
|
+
# Keeps map, only if block is true
|
130
|
+
#
|
131
|
+
# @example Keep if value contains "A"
|
132
|
+
# map = Remap::Rule::Map.call({}).if do
|
133
|
+
# value.include?("A")
|
134
|
+
# end
|
135
|
+
#
|
136
|
+
# error = -> failure { raise failure.exception }
|
137
|
+
#
|
138
|
+
# a = Remap::State.call("A")
|
139
|
+
# map.call(a, &error).fetch(:value) # => "A"
|
140
|
+
#
|
141
|
+
# b = Remap::State.call("BA")
|
142
|
+
# map.call(b, &error).fetch(:value) # => "BA"
|
143
|
+
#
|
144
|
+
# c = Remap::State.call("C")
|
145
|
+
# map.call(c, &error).key?(:value) # => false
|
146
|
+
#
|
147
|
+
# @return [Map]
|
82
148
|
def if(&block)
|
83
|
-
add do |
|
84
|
-
|
85
|
-
bool ?
|
149
|
+
add do |outer_state|
|
150
|
+
outer_state.execute(&block).fmap do |bool, state|
|
151
|
+
bool ? outer_state.value : state.notice!("#if returned false")
|
86
152
|
end
|
87
153
|
end
|
88
154
|
end
|
89
155
|
|
156
|
+
# Keeps map, only if block is false
|
157
|
+
#
|
158
|
+
|
159
|
+
# @example Keep unless value contains "A"
|
160
|
+
# map = Remap::Rule::Map.call({}).if_not do
|
161
|
+
# value.include?("A")
|
162
|
+
# end
|
163
|
+
#
|
164
|
+
# error = -> failure { raise failure.exception }
|
165
|
+
#
|
166
|
+
# a = Remap::State.call("A")
|
167
|
+
# map.call(a, &error).key?(:value) # => false
|
168
|
+
#
|
169
|
+
# b = Remap::State.call("BA")
|
170
|
+
# map.call(b, &error).key?(:value) # => false
|
171
|
+
#
|
172
|
+
# c = Remap::State.call("C")
|
173
|
+
# map.call(c, &error).fetch(:value) # => "C"
|
174
|
+
#
|
175
|
+
# @return [Map]
|
90
176
|
def if_not(&block)
|
91
|
-
add do |
|
92
|
-
|
93
|
-
bool ?
|
177
|
+
add do |outer_state|
|
178
|
+
outer_state.execute(&block).fmap do |bool, state|
|
179
|
+
bool ? state.notice!("#if_not returned false") : outer_state.value
|
94
180
|
end
|
95
181
|
end
|
96
182
|
end
|
97
183
|
|
98
184
|
private
|
99
185
|
|
186
|
+
# @return [self]
|
100
187
|
def add(&block)
|
101
188
|
tap { fn << block }
|
102
189
|
end
|
103
190
|
|
191
|
+
# @return [Array<Proc>]
|
104
192
|
def fn
|
105
193
|
@fn ||= []
|
106
194
|
end
|
195
|
+
|
196
|
+
# @return [Proc]
|
197
|
+
def callback
|
198
|
+
-> state do
|
199
|
+
fn.reduce(state) do |inner, fn|
|
200
|
+
fn[inner]
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# Catches :fatal and raises {Notice::Error}
|
206
|
+
#
|
207
|
+
# @param state [State]
|
208
|
+
# @param id (:fatal) [:fatal, :notice, :ignore]
|
209
|
+
#
|
210
|
+
# raise [Notice::Error]
|
211
|
+
def fatal(state, id: :fatal, &block)
|
212
|
+
raise catch(id, &block).traced(backtrace).exception
|
213
|
+
end
|
214
|
+
|
215
|
+
# Catches :notice exceptions and repackages them as a state
|
216
|
+
#
|
217
|
+
# @param state [State]
|
218
|
+
#
|
219
|
+
# @return [State]
|
220
|
+
def notice(state, &block)
|
221
|
+
state.set(notice: catch(:notice, &block).traced(backtrace)).except(:value)
|
222
|
+
end
|
223
|
+
|
224
|
+
# @abstract
|
225
|
+
def ignore(...)
|
226
|
+
raise NotImplementedError, "#{self.class}#ignore"
|
227
|
+
end
|
107
228
|
end
|
108
229
|
end
|
109
230
|
end
|