remap 2.2.44 → 2.2.48

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f9c58b85124adc855131d9efee5ce1ff8653cf4828882a5ad1b60ebb0d1ea99b
4
- data.tar.gz: ace57dfce51c7b820d2db5c2f742e5e44828bc998cc719a817bb040e4d08f139
3
+ metadata.gz: a564dcf130adbf8c40eb089d14f498f993362b74823728ec8ad44b98014034e6
4
+ data.tar.gz: 3dcac97cd1e4c630ec55ccdbdb6c43922f2ce5367d770e471ebaa77f5ae3fc8c
5
5
  SHA512:
6
- metadata.gz: d868e87397958d9c68f2048a7888e306c3764dbac799ff5c34c2d8d00e08463fe5abacc708dcc232605c33f319b1cca0b34252108cfb8f4ca1f7831718956d9e
7
- data.tar.gz: 3880cde868fbfa68702db58d6db914825f34614b140c3284c264f6b4303338dfda22f1a0f151419d610225c4cd029a776c149b4901607915988acf3c6598386a
6
+ metadata.gz: 237c0305757a4a12e5d3c24358fd9ed6295d0159baf0bb5786a9b019e3b50c98bd163334b35b04bab99ef1f19adeb232aec234a2aed76ad16ade778c3950606e
7
+ data.tar.gz: 1c6637383207baf0bd32fd6398f34dc935a5237e2fa67eafa2fccaa1db0d1f9d3f569c6f0695f9c148e737b1c4de15a974a09ba193d0af55ffaf3bef21bad07d
data/lib/remap/base.rb CHANGED
@@ -34,33 +34,33 @@ module Remap
34
34
  # Mapper.call({}) # => { api_key: "ABC-123" }
35
35
  #
36
36
  # @example Maps ["A", "B", "C"] to ["A", "C"]
37
- # class Mapper < Remap::Base
37
+ # class IfNotMapper < Remap::Base
38
38
  # define do
39
39
  # each do
40
- # map?.if_not do
40
+ # map?.if_not do |value|
41
41
  # value.include?("B")
42
42
  # end
43
43
  # end
44
44
  # end
45
45
  # end
46
46
  #
47
- # Mapper.call(["A", "B", "C"]) # => ["A", "C"]
47
+ # IfNotMapper.call(["A", "B", "C"]) # => ["A", "C"]
48
48
  #
49
49
  # @example Maps ["A", "B", "C"] to ["B"]
50
- # class Mapper < Remap::Base
50
+ # class IfMapper < Remap::Base
51
51
  # define do
52
52
  # each do
53
- # map?.if do
53
+ # map?.if do |value|
54
54
  # value.include?("B")
55
55
  # end
56
56
  # end
57
57
  # end
58
58
  # end
59
59
  #
60
- # Mapper.call(["A", "B", "C"]) # => ["B"]
60
+ # IfMapper.call(["A", "B", "C"]) # => ["B"]
61
61
  #
62
62
  # @example Maps { a: { b: "A" } } to "A"
63
- # class Mapper < Remap::Base
63
+ # class EnumMapper < Remap::Base
64
64
  # define do
65
65
  # map(:a, :b).enum do
66
66
  # value "A", "B"
@@ -68,11 +68,11 @@ module Remap
68
68
  # end
69
69
  # end
70
70
  #
71
- # Mapper.call({ a: { b: "A" } }) # => "A"
72
- # Mapper.call({ a: { b: "B" } }) # => "B"
71
+ # EnumMapper.call({ a: { b: "A" } }) # => "A"
72
+ # EnumMapper.call({ a: { b: "B" } }) # => "B"
73
73
  #
74
74
  # @example Map { people: [{ name: "John" }] } to { names: ["John"] }
75
- # class Mapper < Remap::Base
75
+ # class PeopleMapper < Remap::Base
76
76
  # define do
77
77
  # map :people, to: :names do
78
78
  # each do
@@ -82,12 +82,12 @@ module Remap
82
82
  # end
83
83
  # end
84
84
  #
85
- # Mapper.call({ people: [{ name: "John" }] }) # => { names: ["John"] }
85
+ # PeopleMapper.call({ people: [{ name: "John" }] }) # => { names: ["John"] }
86
86
  #
87
87
  # @example Map "Hello" to "Hello!"
88
88
  # class HelloMapper < Remap::Base
89
89
  # define do
90
- # map.adjust do
90
+ # map.adjust do |value|
91
91
  # "#{value}!"
92
92
  # end
93
93
  # end
@@ -105,6 +105,7 @@ module Remap
105
105
  # Mapper.call([1, 2, 3]) # => 2
106
106
  class Base < Mapper
107
107
  include ActiveSupport::Configurable
108
+ include Dry::Core::Memoizable
108
109
  include Dry::Core::Constants
109
110
  include Catchable
110
111
  extend Mapper::API
@@ -239,7 +240,7 @@ module Remap
239
240
  # @return [void]
240
241
  # rubocop:disable Layout/LineLength
241
242
  def self.define(target = Nothing, method: :new, strategy: :argument, backtrace: caller, &context)
242
- unless block_given?
243
+ unless context
243
244
  raise ArgumentError, "#{self}.define requires a block"
244
245
  end
245
246
 
@@ -305,11 +306,11 @@ module Remap
305
306
  end
306
307
  end
307
308
 
308
- s1 = catch_ignored do |id|
309
- return context.call(state.set(id: id)).then(&constructor).remove_id
309
+ s1 = catch_ignored(state) do |s0|
310
+ return context.call(s0).then(&constructor).remove_id
310
311
  end
311
312
 
312
- Failure.new(failures: s1.notices).then(&error)
313
+ error[s1.failure]
313
314
  end
314
315
 
315
316
  private
@@ -318,5 +319,7 @@ module Remap
318
319
  def validation
319
320
  Contract.call(attributes: attributes, contract: contract, options: options, rules: rules)
320
321
  end
322
+
323
+ memoize :validation
321
324
  end
322
325
  end
@@ -1,21 +1,42 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Remap
4
+ # @api private
4
5
  module Catchable
5
- # @yieldparam id [Symbol]
6
- # @yieldreturn [T]
6
+ using State::Extension
7
+
8
+ # @param state [State]
9
+ #
10
+ # @yieldparam state [State]
11
+ # @yieldparam id [Symbol, String]
12
+ # @yieldreturn [State<T>]
7
13
  #
8
- # @return [T]
9
- def catch_ignored(&block)
10
- catch(to_id(:ignored), &block)
14
+ # @return [State<T>]
15
+ def catch_ignored(state, &block)
16
+ id = to_id(:ignored)
17
+
18
+ catch(id) do
19
+ block[state.set(id: id), id: id].remove_id
20
+ end
11
21
  end
12
22
 
13
- # @yieldparam id [Symbol]
14
- # @yieldreturn [T]
23
+ # @param state [State]
24
+ # @param backtrace [Array<String>]
15
25
  #
16
- # @return [T]
17
- def catch_fatal(&block)
18
- catch(to_id(:fatal), &block)
26
+ # @yieldparam state [State]
27
+ # @yieldparam id [Symbol, String]
28
+ # @yieldreturn [State<T>]
29
+ #
30
+ # @return [State<T>]
31
+ # @raise [Failure::Error]
32
+ def catch_fatal(state, backtrace, &block)
33
+ id = to_id(:fatal)
34
+
35
+ failure = catch(id) do
36
+ return block[state.set(fatal_id: id), fatal_id: id].remove_fatal_id
37
+ end
38
+
39
+ raise failure.exception(backtrace)
19
40
  end
20
41
 
21
42
  private
@@ -32,7 +32,7 @@ module Remap
32
32
  #
33
33
  # @return [Rule]
34
34
  def self.call(backtrace: caller, &block)
35
- unless block_given?
35
+ unless block
36
36
  return Rule::VOID
37
37
  end
38
38
 
@@ -317,7 +317,7 @@ module Remap
317
317
  # @return [Rule::Each]]
318
318
  # @raise [ArgumentError] if no block given
319
319
  def each(backtrace: caller, &block)
320
- unless block_given?
320
+ unless block
321
321
  raise ArgumentError, "#each requires a block"
322
322
  end
323
323
 
@@ -350,7 +350,7 @@ module Remap
350
350
  # @return [Rule::Wrap]
351
351
  # @raise [ArgumentError] if type is not :array
352
352
  def wrap(type, backtrace: caller, &block)
353
- unless block_given?
353
+ unless block
354
354
  raise ArgumentError, "#wrap requires a block"
355
355
  end
356
356
 
@@ -548,13 +548,13 @@ module Remap
548
548
  output: [to].flatten,
549
549
  input: path.flatten
550
550
  },
551
+ backtrace: backtrace,
551
552
  rule: call(backtrace: backtrace, &block)
552
553
  })
553
554
  end
554
555
 
555
556
  def build_embed(s0, mapper, backtrace)
556
- f0 = catch_fatal do |fatal_id|
557
- s1 = s0.set(fatal_id: fatal_id)
557
+ catch_fatal(s0, backtrace) do |s1|
558
558
  s2 = s1.set(mapper: mapper)
559
559
  old_mapper = s0.fetch(:mapper)
560
560
 
@@ -563,8 +563,6 @@ module Remap
563
563
  s3.return!
564
564
  end.except(:scope).merge(mapper: old_mapper)
565
565
  end
566
-
567
- raise f0.exception(backtrace)
568
566
  end
569
567
  end
570
568
  end
@@ -33,15 +33,13 @@ module Remap
33
33
 
34
34
  key = path.first
35
35
 
36
- unless block_given?
36
+ unless fallback
37
37
  return get(*path, trace: trace) do
38
- raise PathError, trace + [key]
38
+ throw :ignore, trace + [key]
39
39
  end
40
40
  end
41
41
 
42
42
  fetch(key, &fallback).get(*path[1..], trace: trace + [key], &fallback)
43
- rescue TypeError
44
- raise PathError, trace + [key]
45
43
  end
46
44
  end
47
45
  end
@@ -38,9 +38,9 @@ module Remap
38
38
  def get(*path, trace: [], &fallback)
39
39
  return self if path.empty?
40
40
 
41
- unless block_given?
41
+ unless fallback
42
42
  return get(*path, trace: trace) do
43
- raise PathError, trace
43
+ throw :ignore, trace + path
44
44
  end
45
45
  end
46
46
 
data/lib/remap/failure.rb CHANGED
@@ -17,7 +17,7 @@ module Remap
17
17
  ]
18
18
  end
19
19
 
20
- failure = attributes.deep_merge(other.attributes) do |key, value1, value2|
20
+ failure = attributes.merge(other.attributes) do |key, value1, value2|
21
21
  case [key, value1, value2]
22
22
  in [:failures | :notices, Array, Array]
23
23
  value1 + value2
@@ -22,7 +22,7 @@ module Remap
22
22
  #
23
23
  # @return [Any, T]
24
24
  def call(input, backtrace: caller, **options, &error)
25
- unless block_given?
25
+ unless error
26
26
  return call(input, **options) do |failure|
27
27
  raise failure.exception(backtrace)
28
28
  end
@@ -25,7 +25,7 @@ module Remap
25
25
  #
26
26
  # @return [State]
27
27
  def call(state, &iterator)
28
- unless block_given?
28
+ unless iterator
29
29
  raise ArgumentError, "Input path requires an iterator block"
30
30
  end
31
31
 
@@ -16,29 +16,26 @@ module Remap
16
16
  #
17
17
  # @return [State]
18
18
  def call(state)
19
- s0 = state.except(:value)
19
+ init = state.except(:value)
20
20
 
21
21
  if rules.empty?
22
- return s0
22
+ return init
23
23
  end
24
24
 
25
- failure = catch_fatal do |fatal_id|
26
- s1 = s0.set(fatal_id: fatal_id)
27
- s4 = state.set(fatal_id: fatal_id)
25
+ catch_fatal(init, backtrace) do |s1, fatal_id:|
26
+ s2 = state.set(fatal_id: fatal_id)
28
27
 
29
- return catch_ignored do |id|
30
- s2 = s1.set(id: id)
28
+ catch_ignored(s1) do |s3, id:|
29
+ states = rules.map do |rule|
30
+ rule.call(s2)
31
+ end
31
32
 
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)
33
+ states.reduce(s3) do |s4, s5|
34
+ s6 = s5.set(id: id)
35
+ s4.combine(s6)
37
36
  end
38
- end.remove_id.remove_fatal_id
37
+ end
39
38
  end
40
-
41
- raise failure.exception(backtrace)
42
39
  end
43
40
  end
44
41
  end
@@ -4,13 +4,9 @@ module Remap
4
4
  class Rule
5
5
  class Map
6
6
  class Enum < Proxy
7
- include Dry::Monads[:maybe]
8
-
9
7
  # @return [Hash]
10
- option :mappings, default: -> { Hash.new { default } }
11
-
12
- # @return [Maybe]
13
- option :default, default: -> { None() }
8
+ option :table, default: -> { {} }
9
+ option :default, default: -> { Undefined }
14
10
 
15
11
  alias execute instance_eval
16
12
 
@@ -37,7 +33,7 @@ module Remap
37
33
  new.tap { _1.execute(&block) }
38
34
  end
39
35
 
40
- # Translates key into a value using predefined mappings
36
+ # Translates key into a value using predefined table
41
37
  #
42
38
  # @param key [#hash]
43
39
  #
@@ -50,24 +46,21 @@ module Remap
50
46
  return get(key) { raise Error, _1 }
51
47
  end
52
48
 
53
- self[key].bind { return _1 }.or do
54
- error["Enum key [#{key}] not found among [#{mappings.keys.inspect}]"]
49
+ table.fetch(key) do
50
+ unless default == Undefined
51
+ return default
52
+ end
53
+
54
+ error["Enum key [#{key}] not found among [#{table.keys.inspect}]"]
55
55
  end
56
56
  end
57
57
  alias call get
58
58
 
59
- # @return [Maybe]
60
- def [](key)
61
- mappings[key]
62
- end
63
-
64
59
  # @return [void]
65
60
  def from(*keys, to:)
66
- value = Some(to)
67
-
68
61
  keys.each do |key|
69
- mappings[key] = value
70
- mappings[to] = value
62
+ table[key] = to
63
+ table[to] = to
71
64
  end
72
65
  end
73
66
 
@@ -80,7 +73,7 @@ module Remap
80
73
 
81
74
  # @return [void]
82
75
  def otherwise(value)
83
- mappings.default = Some(value)
76
+ @default = value
84
77
  end
85
78
  end
86
79
  end
@@ -5,12 +5,14 @@ module Remap
5
5
  class Map
6
6
  using State::Extension
7
7
 
8
- class Optional < Concrete
8
+ # @api private
9
+ class Optional < Required
9
10
  # Represents an optional mapping rule
10
11
  # When the mapping fails, the value is ignored
11
12
  #
12
13
  # @example Map [:name] to [:nickname]
13
14
  # map = Map::Optional.call({
15
+ # backtrace: caller,
14
16
  # path: {
15
17
  # input: [:name],
16
18
  # output: [:nickname]
@@ -22,16 +24,14 @@ module Remap
22
24
  # })
23
25
  #
24
26
  # output = map.call(state) do |failure|
25
- # raise failure.exeception
27
+ # raise failure.exception(caller)
26
28
  # end
27
29
  #
28
30
  # output.fetch(:value) # => { nickname: "John" }
29
31
  #
30
- # @param state [State]
31
- #
32
- # @return [State]
32
+ # @see Map#call
33
33
  def call(state)
34
- catch { super(state.set(id: _1)).except(:id) }
34
+ catch_ignored(state) { super(_1) }
35
35
  end
36
36
  end
37
37
  end
@@ -8,7 +8,19 @@ module Remap
8
8
  class Required < Concrete
9
9
  attribute :backtrace, Types::Backtrace
10
10
 
11
- # TODO: Remove
11
+ # @see Map#call
12
+ def call(state)
13
+ catch_fatal(state, backtrace) do |s0|
14
+ s2 = path.input.call(s0) do |s1|
15
+ callback(rule.call(s1))
16
+ end
17
+
18
+ s3 = s2.then(&path.output)
19
+ s4 = s3.set(path: state.path)
20
+
21
+ s4.except(:key)
22
+ end
23
+ end
12
24
  end
13
25
  end
14
26
  end
@@ -43,29 +43,14 @@ module Remap
43
43
  #
44
44
  # @abstract
45
45
  def call(state)
46
- failure = catch_fatal do |fatal_id|
47
- s0 = state.set(fatal_id: fatal_id)
48
-
49
- s2 = path.input.call(s0) do |s1|
50
- s2 = rule.call(s1)
51
- callback(s2)
52
- end
53
-
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
60
-
61
- raise failure.exception(backtrace)
46
+ raise NotImplementedError, "#{self.class}#call not implemented"
62
47
  end
63
48
 
64
49
  # A post-processor method
65
50
  #
66
51
  # @example Up-case mapped value
67
52
  # state = Remap::State.call("Hello World")
68
- # map = Remap::Rule::Map.call({})
53
+ # map = Remap::Rule::Map.call(backtrace: caller)
69
54
  # upcase = map.adjust(&:upcase)
70
55
  # error = -> failure { raise failure.exception }
71
56
  # upcase.call(state, &error).fetch(:value) # => "HELLO WORLD"
@@ -84,7 +69,7 @@ module Remap
84
69
  #
85
70
  # @example Pending mapping
86
71
  # state = Remap::State.call(:value)
87
- # map = Remap::Rule::Map.call({})
72
+ # map = Remap::Rule::Map::Optional.call(backtrace: caller)
88
73
  # pending = map.pending("this is pending")
89
74
  # error = -> failure { raise failure.exception }
90
75
  # pending.call(state, &error).key?(:value) # => false
@@ -99,7 +84,7 @@ module Remap
99
84
  # An enumeration processor
100
85
  #
101
86
  # @example A mapped enum
102
- # enum = Remap::Rule::Map.call({}).enum do
87
+ # enum = Remap::Rule::Map.call(backtrace: caller).enum do
103
88
  # value "A", "B"
104
89
  # otherwise "C"
105
90
  # end
@@ -132,7 +117,7 @@ module Remap
132
117
  # Keeps map, only if block is true
133
118
  #
134
119
  # @example Keep if value contains "A"
135
- # map = Remap::Rule::Map.call({}).if do
120
+ # map = Remap::Rule::Map::Optional.call(backtrace: caller).if do |value|
136
121
  # value.include?("A")
137
122
  # end
138
123
  #
@@ -160,11 +145,11 @@ module Remap
160
145
  #
161
146
 
162
147
  # @example Keep unless value contains "A"
163
- # map = Remap::Rule::Map.call({}).if_not do
148
+ # map = Remap::Rule::Map::Optional.new(backtrace: caller).if_not do |value|
164
149
  # value.include?("A")
165
150
  # end
166
151
  #
167
- # error = -> failure { raise failure.exception }
152
+ # error = -> failure { raise failure.exception(caller) }
168
153
  #
169
154
  # a = Remap::State.call("A")
170
155
  # map.call(a, &error).key?(:value) # => false
@@ -26,7 +26,7 @@ module Remap
26
26
  #
27
27
  # @return [State<U>]
28
28
  def call(outer_state, &block)
29
- unless block_given?
29
+ unless block
30
30
  raise ArgumentError, "All selector requires an iteration block"
31
31
  end
32
32
 
@@ -31,7 +31,7 @@ module Remap
31
31
  #
32
32
  # @return [State<U>]
33
33
  def call(state, &block)
34
- unless block_given?
34
+ unless block
35
35
  raise ArgumentError, "The index selector requires an iteration block"
36
36
  end
37
37
 
@@ -28,7 +28,7 @@ module Remap
28
28
  #
29
29
  # @return [State<U>]
30
30
  def call(state, &block)
31
- unless block_given?
31
+ unless block
32
32
  raise ArgumentError, "The key selector requires an iteration block"
33
33
  end
34
34
 
@@ -46,14 +46,10 @@ module Remap
46
46
  # @returns [Hash] a hash containing the given path
47
47
  # @raise Europace::Error when path doesn't exist
48
48
  def only(*path)
49
- path.reduce(EMPTY_HASH) do |hash, key|
50
- next hash unless key?(key)
51
-
52
- hash.deep_merge(key => fetch(key))
53
- end
49
+ dup.extract!(*path)
54
50
  end
55
51
 
56
- # Throws :fatal containing a Notice
52
+ # @see #notice
57
53
  def fatal!(...)
58
54
  fatal_id = fetch(:fatal_id) do
59
55
  raise ArgumentError, "Missing :fatal_id in %s" % formatted
@@ -62,12 +58,7 @@ module Remap
62
58
  throw fatal_id, Failure.new(failures: [notice(...)], notices: notices)
63
59
  end
64
60
 
65
- # Throws :warn containing a Notice
66
- def notice!(...)
67
- raise NotImplementedError, "Not implemented"
68
- end
69
-
70
- # Throws :ignore containing a Notice
61
+ # @see #notice
71
62
  def ignore!(...)
72
63
  set(notice: notice(...)).except(:value).return!
73
64
  end
@@ -117,11 +108,30 @@ module Remap
117
108
  #
118
109
  # @return [State]
119
110
  def map(&block)
120
- bind do |value, state|
121
- Iteration.call(state: state, value: value).call do |other, **options|
122
- state.set(other, **options).then(&block)
123
- end.except(:index, :element, :key)
111
+ result = case self
112
+ in { value: Array => array }
113
+ array.each_with_index.each_with_object([]) do |(value, index), array|
114
+ s1 = block[set(value, index: index)]
115
+
116
+ if s1.key?(:value)
117
+ array << s1[:value]
118
+ end
119
+ end
120
+ in { value: Hash => hash }
121
+ hash.each_with_object({}) do |(key, value), acc|
122
+ s1 = block[set(value, key: key)]
123
+
124
+ if s1.key?(:value)
125
+ acc[key] = s1[:value]
126
+ end
127
+ end
128
+ in { value: }
129
+ fatal!("Expected an enumerable got %s", value.class)
130
+ else
131
+ return self
124
132
  end
133
+
134
+ set(result)
125
135
  end
126
136
 
127
137
  # @return [String]
@@ -135,10 +145,14 @@ module Remap
135
145
  #
136
146
  # @return [State]
137
147
  def combine(other)
138
- deep_merge(other) do |key, value1, value2|
148
+ merge(other) do |key, value1, value2|
139
149
  case [key, value1, value2]
140
- in [:value, Array => list1, Array => list2]
141
- list1 + list2
150
+ in [_, Hash => left, Hash => right]
151
+ left.merge(right)
152
+ in [:ids | :fatal_ids, _, right]
153
+ right
154
+ in [_, Array => left, Array => right]
155
+ left + right
142
156
  in [:value, left, right]
143
157
  other.fatal!(
144
158
  "Could not merge [%s] (%s) with [%s] (%s)",
@@ -147,15 +161,6 @@ module Remap
147
161
  right.formatted,
148
162
  right.class
149
163
  )
150
- in [:notices, Array => n1, Array => n2]
151
- n1 + n2
152
- in [:ids, i1, i2] if i1.all? { i2.include?(_1) }
153
- i2
154
- in [:ids, i1, i2] if i2.all? { i1.include?(_1) }
155
- i1
156
- in [:ids, i1, i2]
157
- other.fatal!("Could not merge #ids [%s] (%s) with [%s] (%s)", i1, i1.class, i2,
158
- i2.class)
159
164
  in [Symbol, _, value]
160
165
  value
161
166
  end
@@ -165,31 +170,39 @@ module Remap
165
170
  # @todo Merge with {#remove_fatal_id}
166
171
  # @return [State]
167
172
  def remove_id
168
- case self
173
+ state = dup
174
+
175
+ case state
169
176
  in { ids: [], id: }
170
- except(:id)
177
+ state.except!(:id)
171
178
  in { ids:, id: }
172
- merge(ids: ids[1...], id: ids[0])
179
+ state.merge!(ids: ids[1...], id: ids[0])
173
180
  in { ids: [] }
174
- self
181
+ state
175
182
  in { ids: }
176
183
  raise ArgumentError, "[BUG] #ids for state are set, but not #id: %s" % formatted
177
- end._
184
+ end
185
+
186
+ state
178
187
  end
179
188
 
180
189
  # @todo Merge with {#remove_id}
181
190
  # @return [State]
182
191
  def remove_fatal_id
183
- case self
192
+ state = dup
193
+
194
+ case state
184
195
  in { fatal_ids: [], fatal_id: }
185
- except(:fatal_id)
186
- in { fatal_ids: ids, fatal_id: id }
187
- merge(fatal_ids: ids[1...], fatal_id: ids[0])
196
+ state.except!(:fatal_id)
197
+ in { fatal_ids: ids, fatal_id: }
198
+ state.merge!(fatal_ids: ids[1...], fatal_id: ids[0])
188
199
  in { fatal_ids: [] }
189
- self
200
+ state
190
201
  in { fatal_ids: }
191
202
  raise ArgumentError, "[BUG] #ids for state are set, but not #id: %s" % formatted
192
- end._
203
+ end
204
+
205
+ state
193
206
  end
194
207
 
195
208
  # Creates a new state with params
@@ -203,24 +216,30 @@ module Remap
203
216
  return set(**options, value: value)
204
217
  end
205
218
 
206
- case [self, options]
207
- in [{notices:}, {notice: notice, **rest}]
208
- merge(notices: notices + [notice]).set(**rest)
209
- in [{value:}, {mapper:, **rest}]
210
- merge(scope: value, mapper: mapper).set(**rest)
211
- in [{path:}, {key:, **rest}]
212
- merge(path: path + [key], key: key).set(**rest)
213
- in [{path:}, {index:, value:, **rest}]
214
- merge(path: path + [index], element: value, index: index, value: value).set(**rest)
215
- in [{path:}, {index:, **rest}]
216
- merge(path: path + [index], index: index).set(**rest)
217
- in [{ids:, id: old_id}, {id: new_id, **rest}]
218
- merge(ids: [old_id] + ids, id: new_id).set(**rest)
219
- in [{fatal_ids:, fatal_id: old_id}, {fatal_id: new_id, **rest}]
220
- merge(fatal_ids: [old_id] + fatal_ids, fatal_id: new_id).set(**rest)
219
+ state = dup
220
+
221
+ case [state, options]
222
+ in [{notices:}, {notice: notice}]
223
+ state.merge!(notices: notices + [notice])
224
+ in [{value:}, {mapper:}]
225
+ state.merge!(scope: value, mapper: mapper)
226
+ in [{path:}, {key:, value:}]
227
+ state.merge!(path: path + [key], key: key, value: value)
228
+ in [{path:}, {key:}]
229
+ state.merge!(path: path + [key], key: key)
230
+ in [{path:}, {index:, value:}]
231
+ state.merge!(path: path + [index], element: value, index: index, value: value)
232
+ in [{path:}, {index:}]
233
+ state.merge!(path: path + [index], index: index)
234
+ in [{ids:, id: old_id}, {id: new_id}]
235
+ state.merge!(ids: [old_id] + ids, id: new_id)
236
+ in [{fatal_ids:, fatal_id: old_id}, {fatal_id: new_id}]
237
+ state.merge!(fatal_ids: [old_id] + fatal_ids, fatal_id: new_id)
221
238
  else
222
- merge(options)
239
+ state.merge!(options)
223
240
  end
241
+
242
+ state
224
243
  end
225
244
 
226
245
  # Passes {#value} to block, if defined
@@ -240,22 +259,6 @@ module Remap
240
259
  end
241
260
  end
242
261
 
243
- # Creates a failure to be used in {Remap::Base} & {Remap::Mapper}
244
- #
245
- # @param reason [#to_s]
246
- #
247
- # @see State::Schema
248
- #
249
- # @return [Failure]
250
-
251
- # class Failure < Dry::Interface
252
- # attribute :notices, [Notice], min_size: 1
253
- # end
254
-
255
- def failure(reason = Undefined)
256
- raise NotImplementedError, "Not implemented"
257
- end
258
-
259
262
  # Passes {#value} to block, if defined
260
263
  # {options} are combine into the final state
261
264
  #
@@ -267,7 +270,7 @@ module Remap
267
270
  #
268
271
  # @return [Y]
269
272
  def bind(**options, &block)
270
- unless block_given?
273
+ unless block
271
274
  raise ArgumentError, "State#bind requires a block"
272
275
  end
273
276
 
@@ -287,19 +290,37 @@ module Remap
287
290
  # @return [State<U>]
288
291
  def execute(&block)
289
292
  bind do |value|
290
- result = context(value).instance_exec(value, &block)
293
+ result = catch :done do
294
+ tail_path = catch :ignore do
295
+ names = block.parameters.reduce([]) do |acc, (type, name)|
296
+ case type
297
+ in :keyreq
298
+ acc + [name]
299
+ else
300
+ acc
301
+ end
302
+ end
303
+
304
+ r = Proc.new(&block).call(value, **only(*names), **options.only(*names)) do |reason|
305
+ ignore!(reason)
306
+ end
307
+ throw :done, r
308
+ rescue NameError => e
309
+ fatal!(e.message)
310
+ rescue KeyError => e
311
+ [e.key]
312
+ rescue IndexError
313
+ []
314
+ end
315
+
316
+ set(path: path + tail_path).ignore!("Undefined path")
317
+ end
291
318
 
292
319
  if result.equal?(Dry::Core::Constants::Undefined)
293
320
  ignore!("Undefined returned, skipping!")
294
321
  end
295
322
 
296
323
  set(result)
297
- rescue KeyError => e
298
- set(path: path + [e.key]).ignore!(e.message)
299
- rescue IndexError => e
300
- ignore!(e.message)
301
- rescue PathError => e
302
- set(path: path + e.path).ignore!("Undefined path")
303
324
  end
304
325
  end
305
326
 
@@ -385,6 +406,8 @@ module Remap
385
406
  # @return [Failure]
386
407
  def failure(reason = Undefined)
387
408
  failures = case [path, reason]
409
+ in [_, Undefined]
410
+ return Failure.new(failures: notices)
388
411
  in [_, Notice => notice]
389
412
  [notice]
390
413
  in [path, Array => reasons]
@@ -419,27 +442,6 @@ module Remap
419
442
 
420
443
  throw id, remove_id
421
444
  end
422
-
423
- private
424
-
425
- # Creates a context containing {options} and {self}
426
- #
427
- # @param value [Any]
428
- #
429
- # @yieldparam reason [T]
430
- #
431
- # @return [Struct]
432
- def context(value, context: self)
433
- ::Struct.new(*except(:id).keys, *options.keys, :state, keyword_init: true) do
434
- define_method :method_missing do |name, *|
435
- context.fatal!("Method [%s] not defined", name)
436
- end
437
-
438
- define_method(:skip!) do |message = "Manual skip!"|
439
- context.ignore!(message)
440
- end
441
- end.new(**to_hash, **options, value: value, state: self)
442
- end
443
445
  end
444
446
  end
445
447
  end
@@ -9,8 +9,12 @@ 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
+
13
+ required(:ids).array(Types::ID)
14
+ optional(:id).filled(Types::ID)
15
+
16
+ required(:fatal_ids).array(Types::ID)
17
+ optional(:fatal_id).filled(Types::ID)
14
18
 
15
19
  optional(:index).filled(:integer)
16
20
  optional(:element).filled
data/lib/remap/state.rb CHANGED
@@ -31,11 +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,
35
- notices: EMPTY_ARRAY,
36
- path: EMPTY_ARRAY,
34
+ fatal_ids: [],
35
+ notices: [],
36
+ path: [],
37
37
  options: options,
38
- ids: EMPTY_ARRAY,
38
+ ids: [],
39
39
  mapper: mapper,
40
40
  values: value,
41
41
  value: value,
data/lib/remap/types.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/monads/maybe"
4
3
  require "dry/logic/operations/negation"
5
4
  require "dry/logic"
6
5
 
@@ -18,6 +17,7 @@ module Remap
18
17
  Rule = Interface(:call) | Instance(Proc)
19
18
  Key = Interface(:hash)
20
19
  Notice = Instance(Remap::Notice)
20
+ ID = String | Symbol
21
21
 
22
22
  # Validates a state according to State::Schema
23
23
  State = Hash.constructor do |input, type, &error|
data/lib/remap.rb CHANGED
@@ -5,11 +5,11 @@ require "active_support/core_ext/enumerable"
5
5
  require "active_support/core_ext/array/wrap"
6
6
  require "active_support/proxy_object"
7
7
 
8
+ require "dry/core/memoizable"
8
9
  require "dry/validation"
9
10
  require "dry/interface"
10
11
  require "dry/schema"
11
12
  require "dry/struct"
12
- require "dry/monads"
13
13
  require "dry/types"
14
14
 
15
15
  require "neatjson"
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.2.44
4
+ version: 2.2.48
5
5
  platform: ruby
6
6
  authors:
7
7
  - Linus Oleander
@@ -66,20 +66,6 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: 1.0.3
69
- - !ruby/object:Gem::Dependency
70
- name: dry-monads
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: 1.4.0
76
- type: :runtime
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: 1.4.0
83
69
  - !ruby/object:Gem::Dependency
84
70
  name: dry-schema
85
71
  requirement: !ruby/object:Gem::Requirement
@@ -264,10 +250,6 @@ files:
264
250
  - lib/remap/extensions/object.rb
265
251
  - lib/remap/failure.rb
266
252
  - lib/remap/failure/error.rb
267
- - lib/remap/iteration.rb
268
- - lib/remap/iteration/array.rb
269
- - lib/remap/iteration/hash.rb
270
- - lib/remap/iteration/other.rb
271
253
  - lib/remap/mapper.rb
272
254
  - lib/remap/mapper/and.rb
273
255
  - lib/remap/mapper/binary.rb
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Remap
4
- class Iteration
5
- using State::Extension
6
-
7
- # Implements an array iterator which defines index in state
8
- class Array < Concrete
9
- # @return [Array<T>]
10
- attribute :value, Types::Array, alias: :array
11
-
12
- # @return [State<Array<T>>]
13
- attribute :state, Types::State
14
-
15
- # @see Iteration#map
16
- def call(&block)
17
- array.each_with_index.reduce(init) do |state, (value, index)|
18
- reduce(state, value, index, &block)
19
- end
20
- end
21
-
22
- private
23
-
24
- def init
25
- state.set(EMPTY_ARRAY)
26
- end
27
-
28
- def reduce(state, value, index, &block)
29
- s0 = block[value, index: index]
30
- s1 = s0.set(**state.only(:ids, :fatal_id))
31
- state.combine(s1.fmap { [_1] })
32
- end
33
- end
34
- end
35
- end
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Remap
4
- class Iteration
5
- using State::Extension
6
-
7
- # Implements a hash iterator which defines key in state
8
- class Hash < Concrete
9
- # @return [Hash]
10
- attribute :value, Types::Hash, alias: :hash
11
-
12
- # @return [State<Hash>]
13
- attribute :state, Types::State
14
-
15
- # @see Iteration#map
16
- def call(&block)
17
- hash.reduce(init) do |state, (key, value)|
18
- reduce(state, key, value, &block)
19
- end
20
- end
21
-
22
- private
23
-
24
- def reduce(state, key, value, &block)
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 } })
28
- end
29
-
30
- def init
31
- state.set(EMPTY_HASH)
32
- end
33
- end
34
- end
35
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Remap
4
- class Iteration
5
- using State::Extension
6
-
7
- # Default iterator which doesn't do anything
8
- class Other < Concrete
9
- attribute :value, Types::Any, alias: :other
10
- attribute :state, Types::State
11
-
12
- # @see Iteration#map
13
- def call(&block)
14
- state.fatal!("Expected an enumerable")
15
- end
16
- end
17
- end
18
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Remap
4
- class Iteration < Dry::Interface
5
- # @return [State<T>]
6
- attribute :state, Types::State
7
-
8
- # @return [T]
9
- attribute :value, Types::Any
10
-
11
- # Maps every element in {#value}
12
- #
13
- # @abstract
14
- #
15
- # @yieldparam element [V]
16
- # @yieldparam key [K, Integer]
17
- # @yieldreturn [Array<V>, Hash<V, K>]
18
- #
19
- # @return [Array<V>, Hash<V, K>]
20
- def call(state)
21
- raise NotImplementedError, "#{self.class}#call not implemented"
22
- end
23
-
24
- order :Hash, :Array, :Other
25
- end
26
- end