remap 2.2.43 → 2.2.47

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 88fc0978315944ad791a089536840916db814b0fc7dad18cda8c19d1c9901d9c
4
- data.tar.gz: 2b59e22a02ac486b04758c772082731b2e91cb0ba698e40e3502794236515ff5
3
+ metadata.gz: d745b7e030764a548c8cb8b2651e89cf22485154120bab89053743653d0ccd9a
4
+ data.tar.gz: '04408f1457593d3c2f3fb8fc5d52f275b74e9d3f0eb31a4088d073ee93ddeeb6'
5
5
  SHA512:
6
- metadata.gz: 41a5d60aa1fa66fb254162d1313611e441e239de54e59c4d7ebaa8a5334a6930317cb32703bd837e85212826b23286afa57e9d8662d8e9c78ab4fc3d569bcddd
7
- data.tar.gz: 3c8b89c3a3cb5a4fae156a0757b2130afc95ced6911b2707e239e7a3dd791ed5036b46711fb4262b94006b027bc0e98f0882e5a2e4131a12c7730542699e7068
6
+ metadata.gz: 2fb3e7f89ed0dea90a33a33dee5f5a5b358f22de24b24a64e531a7a84196da067e621060ab21045efb810c02cc8af0f53e7ca43d36ddad3124fb1a2212973a81
7
+ data.tar.gz: ad67a4ec14390748e9df5d77f8093c44143ae63df023a5ea84434ab79f7c5a894f1df0cdf7b3ce38b19fea44668b19ba269b3185d18d6deaa435a4620f1a733f
data/lib/remap/base.rb CHANGED
@@ -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
@@ -4,6 +4,8 @@ module Remap
4
4
  module Extensions
5
5
  module Array
6
6
  refine ::Array do
7
+ using Object
8
+
7
9
  # @return [Array<Hash>]
8
10
  def to_hash
9
11
  map(&:to_hash)
@@ -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
@@ -5,7 +5,7 @@ module Remap
5
5
  module Hash
6
6
  refine ::Hash do
7
7
  def formatted
8
- JSON.neat_generate(self, sort: true, wrap: 40, aligned: true, around_colon: 1)
8
+ JSON.neat_generate(compact_blank, sort: true, wrap: 40, aligned: true, around_colon: 1)
9
9
  end
10
10
  end
11
11
  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
@@ -11,27 +11,6 @@ module Remap
11
11
  delegate_missing_to :notice
12
12
  delegate :inspect, :to_s, to: :notice
13
13
  option :notice, type: Types.Instance(Notice)
14
-
15
- def inspect
16
- "#<%s %s>" % [self.class, to_hash.formatted]
17
- end
18
-
19
- def undefined(state)
20
- state.set(notice: notice).except(:value)
21
- end
22
-
23
- def failure(state)
24
- Failure.new(failures: [notice], notices: state.fetch(:notices))
25
- end
26
-
27
- def traced(backtrace)
28
- e = Traced.new(notice: notice)
29
- e.set_backtrace(backtrace)
30
- e
31
- end
32
- end
33
-
34
- class Traced < Error
35
14
  end
36
15
  end
37
16
  end
data/lib/remap/notice.rb CHANGED
@@ -20,19 +20,5 @@ module Remap
20
20
  def to_hash
21
21
  super.except(:backtrace).compact_blank
22
22
  end
23
-
24
- # Used by State to skip mapping rules
25
- #
26
- # @raise [Notice::Ignore]
27
- def ignore!
28
- raise Ignore.new(notice: self)
29
- end
30
-
31
- # Used by the state to halt mappers
32
- #
33
- # @raise [Notice::Fatal]
34
- def fatal!
35
- raise Fatal.new(notice: self)
36
- end
37
23
  end
38
24
  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
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
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,23 @@ 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
+ throw :done, context(value).instance_exec(value, &block)
296
+ rescue KeyError => e
297
+ [e.key]
298
+ rescue IndexError
299
+ []
300
+ end
301
+
302
+ set(path: path + tail_path).ignore!("Undefined path")
303
+ end
291
304
 
292
305
  if result.equal?(Dry::Core::Constants::Undefined)
293
306
  ignore!("Undefined returned, skipping!")
294
307
  end
295
308
 
296
309
  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
310
  end
304
311
  end
305
312
 
@@ -385,6 +392,8 @@ module Remap
385
392
  # @return [Failure]
386
393
  def failure(reason = Undefined)
387
394
  failures = case [path, reason]
395
+ in [_, Undefined]
396
+ return Failure.new(failures: notices)
388
397
  in [_, Notice => notice]
389
398
  [notice]
390
399
  in [path, Array => reasons]
@@ -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.43
4
+ version: 2.2.47
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