remap 2.1.11 → 2.1.15

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: 3f0c60203b3136553a963df2e9f574155d59548fb8eae484f5f1c0cf20fdfe47
4
- data.tar.gz: e9a6b23ed253e56151fe7722846f7959bf769c7c37248f142194e83ec08ed0e8
3
+ metadata.gz: e3439c23b054b2470688ec55cedb4dc0d7c0f77544c45f8f47c82b5d8d9b954a
4
+ data.tar.gz: 515558fee642db02bd30e5194846a4486f6b59c67830c46581b9b3d3c529532e
5
5
  SHA512:
6
- metadata.gz: 3cf87719908f3b0b5d3c51fd3b2d30f79ac7409f920045510351083519d579cae8c111b26d859b4592dec7735033918f22ab61fe21c6543a55125d7f9408478a
7
- data.tar.gz: 22d6e07824458f296d81d7cc02fb3ca0c7394e7d22f524feb387cba22decd708313a837f156bdf6008ab4c5d542b5bb3c303118b1dc070dce5bb9736f8a3bef3
6
+ metadata.gz: 599b8f2de0d62e5d4ab8ded2d303243fe11292f0cc621b7cdf66bb9e70ac712bb3fe4811cd926e9929a06f110445e283b2b8c3134a68cec5c810c53f572f720c
7
+ data.tar.gz: 6cf4704750445ac8dfdac08cfe2beec2bff5ac34db4c61557f9239b49857c3a1423978d6cc38342917753d48b4edeeff397d012c8b91d32a3ee49ec96ab62fa4
data/lib/remap/base.rb CHANGED
@@ -143,7 +143,7 @@ module Remap
143
143
  #
144
144
  # @return [void]
145
145
  def self.contract(&context)
146
- self.contract = Dry::Schema.JSON(&context)
146
+ self.contract = Dry::Schema.define(&context)
147
147
  end
148
148
 
149
149
  # Defines a rule for the mapper
@@ -236,14 +236,12 @@ module Remap
236
236
  #
237
237
  # @return [void]
238
238
  def self.define(target = Nothing, method: :new, strategy: :argument, &context)
239
- unless context
240
- raise ArgumentError, "Missing block"
239
+ unless block_given?
240
+ raise ArgumentError, "#{self}.define requires a block"
241
241
  end
242
242
 
243
- self.context = Compiler.call(&context)
244
243
  self.constructor = Constructor.call(method: method, strategy: strategy, target: target)
245
- rescue Dry::Struct::Error => e
246
- raise ArgumentError, e.message
244
+ self.context = Compiler.call(&context)
247
245
  end
248
246
 
249
247
  # Similar to {::call}, but takes a state
@@ -269,7 +267,7 @@ module Remap
269
267
  #
270
268
  # @private
271
269
  def call(state, &error)
272
- unless error
270
+ unless block_given?
273
271
  raise ArgumentError, "Base#call(state, &error) requires block"
274
272
  end
275
273
 
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Remap
4
+ using State::Extension
5
+
4
6
  # Constructs a {Rule} from the block passed to {Remap::Base.define}
5
7
  class Compiler < Proxy
6
8
  # @return [Array<Rule>]
@@ -28,13 +30,15 @@ module Remap
28
30
  #
29
31
  # @return [Rule]
30
32
  def self.call(&block)
31
- unless block
32
- return Rule::Void.new
33
+ unless block_given?
34
+ return Rule::VOID
33
35
  end
34
36
 
35
- new([]).tap do |compiler|
37
+ rules = new([]).tap do |compiler|
36
38
  compiler.instance_exec(&block)
37
- end.rule
39
+ end.rules
40
+
41
+ Rule::Block.new(rules)
38
42
  end
39
43
 
40
44
  # Maps input path [input] to output path [to]
@@ -58,14 +62,8 @@ module Remap
58
62
  # output.fetch(:value) # => { nickname: "John" }
59
63
  #
60
64
  # @return [Rule::Map::Required]
61
- def map(*path, to: EMPTY_ARRAY, backtrace: Kernel.caller, &block)
62
- add Rule::Map::Required.call(
63
- path: {
64
- output: [to].flatten,
65
- input: path.flatten
66
- },
67
- backtrace: backtrace,
68
- rule: call(&block))
65
+ def map(*path, to: EMPTY_ARRAY, backtrace: caller, &block)
66
+ add rule(*path, to: to, backtrace: backtrace, &block)
69
67
  end
70
68
 
71
69
  # Optional version of {#map}
@@ -91,14 +89,8 @@ module Remap
91
89
  # @see #map
92
90
  #
93
91
  # @return [Rule::Map::Optional]
94
- def map?(*path, to: EMPTY_ARRAY, backtrace: Kernel.caller, &block)
95
- add Rule::Map::Optional.call(
96
- path: {
97
- output: [to].flatten,
98
- input: path.flatten
99
- },
100
- backtrace: backtrace,
101
- rule: call(&block))
92
+ def map?(*path, to: EMPTY_ARRAY, backtrace: caller, &block)
93
+ add rule?(*path, to: to, backtrace: backtrace, &block)
102
94
  end
103
95
 
104
96
  # Select a path and uses the same path as output
@@ -121,8 +113,8 @@ module Remap
121
113
  # output.fetch(:value) # => { name: "John" }
122
114
  #
123
115
  # @return [Rule::Map::Required]
124
- def get(*path, backtrace: Kernel.caller, &block)
125
- map(path, to: path, backtrace: backtrace, &block)
116
+ def get(*path, backtrace: caller, &block)
117
+ add rule(path, to: path, backtrace: backtrace, &block)
126
118
  end
127
119
 
128
120
  # Optional version of {#get}
@@ -146,8 +138,8 @@ module Remap
146
138
  # @see #get
147
139
  #
148
140
  # @return [Rule::Map::Optional]
149
- def get?(*path, backtrace: Kernel.caller, &block)
150
- map?(path, to: path, backtrace: backtrace, &block)
141
+ def get?(*path, backtrace: caller, &block)
142
+ add rule?(path, to: path, backtrace: backtrace, &block)
151
143
  end
152
144
 
153
145
  # Maps using mapper
@@ -184,14 +176,18 @@ module Remap
184
176
  # output.fetch(:value) # => { car: "Volvo" }
185
177
  #
186
178
  # @return [Rule::Embed]
187
- def embed(mapper, &block)
188
- if block
179
+ def embed(mapper)
180
+ if block_given?
189
181
  raise ArgumentError, "#embed does not take a block"
190
182
  end
191
183
 
192
- add Rule::Embed.new(mapper: mapper)
193
- rescue Dry::Struct::Error
194
- raise ArgumentError, "Embeded mapper must be [Remap::Mapper], got [#{mapper}]"
184
+ embeding = rule.add do |state, &error|
185
+ mapper.call!(state.set(mapper: mapper)) do |failure|
186
+ next error[failure]
187
+ end.except(:mapper, :scope)
188
+ end
189
+
190
+ add embeding
195
191
  end
196
192
 
197
193
  # Set a static value
@@ -229,14 +225,12 @@ module Remap
229
225
  # @raise [ArgumentError]
230
226
  # if no path given
231
227
  # if path is not a Symbol or Array<Symbol>
232
- def set(*path, to:, &block)
233
- if block
228
+ def set(*path, to:)
229
+ if block_given?
234
230
  raise ArgumentError, "#set does not take a block"
235
231
  end
236
232
 
237
- add Rule::Set.new(path: path.flatten, value: to)
238
- rescue Dry::Struct::Error => e
239
- raise ArgumentError, e.message
233
+ add rule(to: path).add { to.call(_1) }
240
234
  end
241
235
 
242
236
  # Maps to path from map with block in between
@@ -260,8 +254,8 @@ module Remap
260
254
  # output.fetch(:value) # => { nickname: "John" }
261
255
  #
262
256
  # @return [Rule::Map]
263
- def to(*path, map: EMPTY_ARRAY, backtrace: Kernel.caller, &block)
264
- map(*map, to: path, backtrace: backtrace, &block)
257
+ def to(*path, map: EMPTY_ARRAY, backtrace: caller, &block)
258
+ add rule(*map, to: path, backtrace: backtrace, &block)
265
259
  end
266
260
 
267
261
  # Optional version of {#to}
@@ -287,7 +281,7 @@ module Remap
287
281
  #
288
282
  # @return [Rule::Map::Optional]
289
283
  def to?(*path, map: EMPTY_ARRAY, &block)
290
- map?(*map, to: path, &block)
284
+ add rule?(*map, to: path, &block)
291
285
  end
292
286
 
293
287
  # Iterates over the input value, passes each value
@@ -315,11 +309,11 @@ module Remap
315
309
  # @return [Rule::Each]]
316
310
  # @raise [ArgumentError] if no block given
317
311
  def each(&block)
318
- unless block
312
+ unless block_given?
319
313
  raise ArgumentError, "#each requires a block"
320
314
  end
321
315
 
322
- add Rule::Each.new(rule: call(&block))
316
+ add rule(all, &block)
323
317
  end
324
318
 
325
319
  # Wraps output in type
@@ -348,13 +342,11 @@ module Remap
348
342
  # @return [Rule::Wrap]
349
343
  # @raise [ArgumentError] if type is not :array
350
344
  def wrap(type, &block)
351
- unless block
345
+ unless block_given?
352
346
  raise ArgumentError, "#wrap requires a block"
353
347
  end
354
348
 
355
- add Rule::Wrap.new(type: type, rule: call(&block))
356
- rescue Dry::Struct::Error => e
357
- raise ArgumentError, e.message
349
+ add rule(&block).then { Array.wrap(_1) }
358
350
  end
359
351
 
360
352
  # Selects all elements
@@ -376,8 +368,8 @@ module Remap
376
368
  # output.fetch(:value) # => { names: ["John", "Jane"] }
377
369
  #
378
370
  # @return [Rule::Path::Segment::Quantifier::All]
379
- def all(&block)
380
- if block
371
+ def all
372
+ if block_given?
381
373
  raise ArgumentError, "all selector does not take a block"
382
374
  end
383
375
 
@@ -402,8 +394,8 @@ module Remap
402
394
  # output.fetch(:value) # => { api_key: "<SECRET>" }
403
395
  #
404
396
  # @return [Rule::Static::Fixed]
405
- def value(value, &block)
406
- if block
397
+ def value(value)
398
+ if block_given?
407
399
  raise ArgumentError, "option selector does not take a block"
408
400
  end
409
401
 
@@ -428,8 +420,8 @@ module Remap
428
420
  # @param id [Symbol]
429
421
  #
430
422
  # @return [Rule::Static::Option]
431
- def option(id, backtrace: Kernel.caller, &block)
432
- if block
423
+ def option(id, backtrace: caller)
424
+ if block_given?
433
425
  raise ArgumentError, "option selector does not take a block"
434
426
  end
435
427
 
@@ -457,8 +449,8 @@ module Remap
457
449
  #
458
450
  # @return [Path::Segment::Key]
459
451
  # @raise [ArgumentError] if index is not an Integer
460
- def at(index, &block)
461
- if block
452
+ def at(index)
453
+ if block_given?
462
454
  raise ArgumentError, "first selector does not take a block"
463
455
  end
464
456
 
@@ -486,8 +478,8 @@ module Remap
486
478
  # output.fetch(:value) # => { name: "John" }
487
479
  #
488
480
  # @return [Path::Segment::Key]]
489
- def first(&block)
490
- if block
481
+ def first
482
+ if block_given?
491
483
  raise ArgumentError, "first selector does not take a block"
492
484
  end
493
485
 
@@ -513,27 +505,39 @@ module Remap
513
505
  # output.fetch(:value) # => { name: "Linus" }
514
506
  #
515
507
  # @return [Path::Segment::Key]
516
- def last(&block)
517
- if block
508
+ def last
509
+ if block_given?
518
510
  raise ArgumentError, "last selector does not take a block"
519
511
  end
520
512
 
521
513
  at(-1)
522
514
  end
523
515
 
524
- # The final rule
525
- #
526
- # @return [Rule]
527
- #
528
- # @private
529
- def rule
530
- Rule::Collection.call(rules: rules)
531
- end
532
-
533
516
  private
534
517
 
535
518
  def add(rule)
536
519
  rule.tap { rules << rule }
537
520
  end
521
+
522
+ def rule(*path, to: EMPTY_ARRAY, backtrace: caller, &block)
523
+ Rule::Map::Required.call({
524
+ path: {
525
+ output: [to].flatten,
526
+ input: path.flatten
527
+ },
528
+ backtrace: backtrace,
529
+ rule: call(&block)
530
+ })
531
+ end
532
+
533
+ def rule?(*path, to: EMPTY_ARRAY, backtrace: caller, &block)
534
+ Rule::Map::Optional.call({
535
+ path: {
536
+ output: [to].flatten,
537
+ input: path.flatten
538
+ },
539
+ rule: call(&block)
540
+ })
541
+ end
538
542
  end
539
543
  end
@@ -28,19 +28,20 @@ module Remap
28
28
  # @return [Any]
29
29
  #
30
30
  # @raise When path cannot be found
31
- def get(*path, &error)
32
- _, result = path.reduce([
33
- EMPTY_ARRAY,
34
- self
35
- ]) do |(current_path, element), key|
36
- value = element.fetch(key) do
37
- raise PathError, current_path + [key]
38
- end
31
+ def get(*path, trace: [], &fallback)
32
+ return self if path.empty?
33
+
34
+ key = path.first
39
35
 
40
- [current_path + [key], value]
36
+ unless block_given?
37
+ get(*path, trace: trace) do
38
+ raise PathError, trace + [key]
39
+ end
41
40
  end
42
41
 
43
- result
42
+ fetch(key, &fallback).get(*path[1..], trace: trace + [key], &fallback)
43
+ rescue TypeError
44
+ raise PathError, trace + [key]
44
45
  end
45
46
  end
46
47
  end
@@ -17,14 +17,29 @@ module Remap
17
17
  block["Expected a state, got [#{self}] (#{self.class})"]
18
18
  end
19
19
 
20
+ # @return [Array]
21
+ #
22
+ # @see Extension::Paths::Hash
23
+ def paths
24
+ []
25
+ end
26
+
20
27
  # Fallback method used when #get is called on an object that does not respond to #get
21
28
  #
22
29
  # Block is invoked, if provided
23
30
  # Otherwise a symbol is thrown
24
31
  #
25
32
  # @param path [Array<Key>]
26
- def get(*path, &block)
27
- raise PathError, []
33
+ def get(*path, trace: [], &fallback)
34
+ return self if path.empty?
35
+
36
+ unless block_given?
37
+ get(*path, trace: trace) do
38
+ raise PathError, trace
39
+ end
40
+ end
41
+
42
+ yield
28
43
  end
29
44
  alias_method :fetch, :get
30
45
 
data/lib/remap/failure.rb CHANGED
@@ -14,22 +14,21 @@ module Remap
14
14
  # @return [Failure]
15
15
  def merge(other)
16
16
  unless other.is_a?(self.class)
17
- raise ArgumentError, "can't merge #{self.class} with #{other.class}"
17
+ raise ArgumentError, "Cannot merge %s (%s) with %s (%s)" % [
18
+ self, self.class, other, other.class
19
+ ]
18
20
  end
19
21
 
20
- failure = attributes.deep_merge(other.attributes) do |_, value1, value2|
21
- case [value1, value2]
22
- in [Array, Array]
22
+ failure = attributes.deep_merge(other.attributes) do |key, value1, value2|
23
+ case [key, value1, value2]
24
+ in [:failures | :notices, Array, Array]
23
25
  value1 + value2
24
- else
25
- raise ArgumentError, "can't merge #{self.class} with #{other.class}"
26
26
  end
27
27
  end
28
28
 
29
29
  new(failure)
30
30
  end
31
31
 
32
- # @return [String]
33
32
  def exception
34
33
  Error.new(attributes.formated)
35
34
  end
data/lib/remap/proxy.rb CHANGED
@@ -8,6 +8,7 @@ module Remap
8
8
 
9
9
  include Dry::Core::Constants
10
10
  extend Dry::Initializer
11
+ include Kernel
11
12
 
12
13
  # See Object#tap
13
14
  def tap(&block)
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Remap
4
+ class Rule
5
+ using State::Extension
6
+
7
+ class Block < Unit
8
+ # @return [Array<Rule>]
9
+ attribute :rules, [Types::Rule]
10
+
11
+ # Represents a non-empty define block with one or more rules
12
+ # Calls every {#rules} with state and merges the output
13
+ #
14
+ # @param state [State]
15
+ #
16
+ # @return [State]
17
+ def call(state, &error)
18
+ unless block_given?
19
+ raise ArgumentError, "Block#call(state, &error) requires a block"
20
+ end
21
+
22
+ if rules.empty?
23
+ return state.except(:value)
24
+ end
25
+
26
+ rules.map do |rule|
27
+ rule.call(state) do |failure|
28
+ return error[failure]
29
+ end
30
+ end.reduce(&:combine)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Remap
4
+ class Rule
5
+ class Map
6
+ class Enum < Proxy
7
+ include Dry::Monads[:maybe]
8
+
9
+ # @return [Hash]
10
+ option :mappings, default: -> { Hash.new { default } }
11
+
12
+ # @return [Maybe]
13
+ option :default, default: -> { None() }
14
+
15
+ alias execute instance_eval
16
+
17
+ # Builds an enumeration using the block as context
18
+ #
19
+ # @example
20
+ # enum = Remap::Rule::Map::Enum.call do
21
+ # from "B", to: "C"
22
+ # value "A"
23
+ # otherwise "D"
24
+ # end
25
+ #
26
+ # enum.get("A") # => "A"
27
+ # enum.get("B") # => "C"
28
+ # enum.get("C") # => "C"
29
+ # enum.get("MISSING") # => "D"
30
+ #
31
+ # @return [Any]
32
+ def self.call(&block)
33
+ unless block
34
+ raise ArgumentError, "no block given"
35
+ end
36
+
37
+ new.tap { _1.execute(&block) }
38
+ end
39
+
40
+ # Translates key into a value using predefined mappings
41
+ #
42
+ # @param key [#hash]
43
+ #
44
+ # @yield [String]
45
+ # If the key is not found & no default value is set
46
+ #
47
+ # @return [Any]
48
+ def get(key, &error)
49
+ unless error
50
+ return get(key) { raise Error, _1 }
51
+ end
52
+
53
+ self[key].bind { return _1 }.or do
54
+ error["Enum key [#{key}] not found among [#{mappings.keys.inspect}]"]
55
+ end
56
+ end
57
+ alias call get
58
+
59
+ # @return [Maybe]
60
+ def [](key)
61
+ mappings[key]
62
+ end
63
+
64
+ # @return [void]
65
+ def from(*keys, to:)
66
+ value = Some(to)
67
+
68
+ keys.each do |key|
69
+ mappings[key] = value
70
+ mappings[to] = value
71
+ end
72
+ end
73
+
74
+ # @return [void]
75
+ def value(*ids)
76
+ ids.each do |id|
77
+ from(id, to: id)
78
+ end
79
+ end
80
+
81
+ # @return [void]
82
+ def otherwise(value)
83
+ mappings.default = Some(value)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -9,11 +9,29 @@ module Remap
9
9
  # Represents an optional mapping rule
10
10
  # When the mapping fails, the value is ignored
11
11
  #
12
+ # @example Map [:name] to [:nickname]
13
+ # map = Map::Optional.call({
14
+ # path: {
15
+ # input: [:name],
16
+ # output: [:nickname]
17
+ # }
18
+ # })
19
+ #
20
+ # state = Remap::State.call({
21
+ # name: "John"
22
+ # })
23
+ #
24
+ # output = map.call(state) do |failure|
25
+ # raise failure.exeception
26
+ # end
27
+ #
28
+ # output.fetch(:value) # => { nickname: "John" }
29
+ #
12
30
  # @param state [State]
13
31
  #
14
32
  # @return [State]
15
33
  def call(state, &error)
16
- unless error
34
+ unless block_given?
17
35
  raise ArgumentError, "map.call(state, &error) requires a block"
18
36
  end
19
37
 
@@ -11,6 +11,25 @@ module Remap
11
11
  # Represents a required mapping rule
12
12
  # When it fails, the entire mapping is marked as failed
13
13
  #
14
+ # @example Map [:name] to [:nickname]
15
+ # map = Map::Required.call({
16
+ # backtrace: caller,
17
+ # path: {
18
+ # input: [:name],
19
+ # output: [:nickname]
20
+ # }
21
+ # })
22
+ #
23
+ # state = Remap::State.call({
24
+ # name: "John"
25
+ # })
26
+ #
27
+ # output = map.call(state) do |failure|
28
+ # raise failure.exeception
29
+ # end
30
+ #
31
+ # output.fetch(:value) # => { nickname: "John" }
32
+ #
14
33
  # @param state [State]
15
34
  #
16
35
  # @return [State]
@@ -28,7 +28,7 @@ module Remap
28
28
  attribute? :path, Path.default { Path.call(EMPTY_HASH) }
29
29
 
30
30
  # @return [Rule]
31
- attribute :rule, Rule.default { Void.call(EMPTY_HASH) }
31
+ attribute? :rule, Rule.default { Void.call(EMPTY_HASH) }
32
32
 
33
33
  # @return [Array<String>]
34
34
  attribute? :backtrace, Types::Backtrace, default: EMPTY_ARRAY
@@ -43,15 +43,19 @@ module Remap
43
43
  #
44
44
  # @abstract
45
45
  def call(state, &error)
46
- unless error
46
+ unless block_given?
47
47
  raise ArgumentError, "Map#call(state, &error) requires error handler block"
48
48
  end
49
49
 
50
50
  notice = catch :fatal do
51
51
  return path.input.call(state) do |inner_state|
52
- rule.call(inner_state) do |failure|
52
+ other_state = rule.call(inner_state) do |failure|
53
53
  return error[failure]
54
- end.then(&callback)
54
+ end
55
+
56
+ callback(other_state) do |failure|
57
+ return error[failure]
58
+ end
55
59
  end.then(&path.output)
56
60
  end
57
61
 
@@ -181,23 +185,27 @@ module Remap
181
185
  end
182
186
  end
183
187
 
184
- private
185
-
186
188
  # @return [self]
187
189
  def add(&block)
188
190
  tap { fn << block }
189
191
  end
190
192
 
193
+ private
194
+
191
195
  # @return [Array<Proc>]
192
196
  def fn
193
197
  @fn ||= []
194
198
  end
195
199
 
196
200
  # @return [Proc]
197
- def callback
198
- -> state do
199
- fn.reduce(state) do |inner, fn|
200
- fn[inner]
201
+ def callback(state, &error)
202
+ unless block_given?
203
+ raise ArgumentError, "Map#callback(state, &error) requires error handler block"
204
+ end
205
+
206
+ fn.reduce(state) do |inner, fn|
207
+ fn[inner] do |failure|
208
+ return error[failure]
201
209
  end
202
210
  end
203
211
  end
data/lib/remap/rule.rb CHANGED
@@ -5,6 +5,8 @@ module Remap
5
5
  defines :requirement
6
6
  requirement Types::Any
7
7
 
8
+ VOID = Void.new(EMPTY_HASH)
9
+
8
10
  # @param state [State]
9
11
  #
10
12
  # @abstract
@@ -8,8 +8,14 @@ module Remap
8
8
  #
9
9
  # @example Select the value at index 1 from a array
10
10
  # state = Remap::State.call([:one, :two, :tree])
11
- # result = Remap::Selector::Index.new(1).call(state)
12
- # result.fetch(:value) # => :two
11
+ # index = Remap::Selector::Index.new(1)
12
+ #
13
+ # result = index.call(state) do |element|
14
+ # value = element.fetch(:value)
15
+ # element.merge(value: value.upcase)
16
+ # end
17
+ #
18
+ # result.fetch(:value) # => :TWO
13
19
  class Index < Unit
14
20
  # @return [Integer]
15
21
  attribute :index, Integer
@@ -25,7 +31,9 @@ module Remap
25
31
  #
26
32
  # @return [State<U>]
27
33
  def call(outer_state, &block)
28
- return call(outer_state, &:itself) unless block
34
+ unless block_given?
35
+ raise ArgumentError, "The index selector requires an iteration block"
36
+ end
29
37
 
30
38
  outer_state.bind(index: index) do |array, state|
31
39
  requirement[array] do
@@ -7,13 +7,6 @@ module Remap
7
7
  using Extensions::Object
8
8
  using Extensions::Hash
9
9
 
10
- refine Object do
11
- # @see Extension::Paths::Hash
12
- def paths
13
- EMPTY_ARRAY
14
- end
15
- end
16
-
17
10
  refine Hash do
18
11
  # Returns a list of all key paths
19
12
  #
@@ -23,7 +16,7 @@ module Remap
23
16
  # b: :c
24
17
  # },
25
18
  # d: :e
26
- # }.hur_paths # => [[:a, :b], [:d]]
19
+ # }.paths # => [[:a, :b], [:d]]
27
20
  #
28
21
  # @return [Array<Array<Symbol>>] a list of key paths
29
22
  def paths
@@ -47,7 +40,7 @@ module Remap
47
40
  # b: :c
48
41
  # },
49
42
  # d: :e
50
- # }.hur_only(:a, :b) # => { a: { b: :c } }
43
+ # }.only(:a, :b) # => { a: { b: :c } }
51
44
  #
52
45
  # @returns [Hash] a hash containing the given path
53
46
  # @raise Europace::Error when path doesn't exist
@@ -117,11 +110,7 @@ module Remap
117
110
  def map(&block)
118
111
  bind do |value, state|
119
112
  Iteration.call(state: state, value: value).call do |other, **options|
120
- state.set(other, **options).then do |inner_state|
121
- block[inner_state] do |failure|
122
- throw :failure, failure
123
- end
124
- end
113
+ state.set(other, **options).then(&block)
125
114
  end.except(:index, :element, :key)
126
115
  end
127
116
  end
@@ -275,10 +264,6 @@ module Remap
275
264
  end
276
265
 
277
266
  set(result)._
278
- rescue NoMethodError => e
279
- e.name == :fetch ? error["Fetch not defined on value: #{e}"] : raise
280
- rescue NameError => e
281
- e.name == :Undefined ? error["Undefined returned, skipping!: #{e}"] : raise
282
267
  rescue KeyError, IndexError => e
283
268
  error[e.message]
284
269
  rescue PathError => e
@@ -351,7 +336,7 @@ module Remap
351
336
  #
352
337
  # @return [Struct]
353
338
  def context(value, context: self, &error)
354
- ::Struct.new(*keys, *options.keys, keyword_init: true) do
339
+ ::Struct.new(*keys, *options.keys, :state, keyword_init: true) do
355
340
  define_method :method_missing do |name, *|
356
341
  error["Method [#{name}] not defined"]
357
342
  end
@@ -359,7 +344,7 @@ module Remap
359
344
  define_method :skip! do |message = "Manual skip!"|
360
345
  context.ignore!(message)
361
346
  end
362
- end.new(**to_hash, **options, value: value)
347
+ end.new(**to_hash, **options, value: value, state: self)
363
348
  end
364
349
  end
365
350
  end
data/lib/remap/types.rb CHANGED
@@ -15,7 +15,7 @@ module Remap
15
15
  Enumerable = Any.constrained(type: Enumerable)
16
16
  Nothing = Constant(Remap::Nothing)
17
17
  Mapper = Interface(:call!)
18
- Rule = Interface(:call)
18
+ Rule = Interface(:call) | Instance(Proc)
19
19
  Key = Interface(:hash)
20
20
 
21
21
  # Validates a state according to State::Schema
data/lib/remap.rb CHANGED
@@ -17,7 +17,6 @@ require "zeitwerk"
17
17
 
18
18
  module Remap
19
19
  loader = Zeitwerk::Loader.for_gem
20
- loader.collapse("#{__dir__}/remap/rule/support")
21
20
  loader.collapse("#{__dir__}/remap/mapper/support")
22
21
  loader.setup
23
22
  loader.eager_load
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: remap
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.11
4
+ version: 2.1.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Linus Oleander
@@ -279,18 +279,12 @@ files:
279
279
  - lib/remap/path_error.rb
280
280
  - lib/remap/proxy.rb
281
281
  - lib/remap/rule.rb
282
- - lib/remap/rule/each.rb
283
- - lib/remap/rule/embed.rb
282
+ - lib/remap/rule/block.rb
284
283
  - lib/remap/rule/map.rb
284
+ - lib/remap/rule/map/enum.rb
285
285
  - lib/remap/rule/map/optional.rb
286
286
  - lib/remap/rule/map/required.rb
287
- - lib/remap/rule/set.rb
288
- - lib/remap/rule/support/collection.rb
289
- - lib/remap/rule/support/collection/empty.rb
290
- - lib/remap/rule/support/collection/filled.rb
291
- - lib/remap/rule/support/enum.rb
292
287
  - lib/remap/rule/void.rb
293
- - lib/remap/rule/wrap.rb
294
288
  - lib/remap/selector.rb
295
289
  - lib/remap/selector/all.rb
296
290
  - lib/remap/selector/index.rb
@@ -307,6 +301,9 @@ homepage: https://github.com/oleander/remap
307
301
  licenses:
308
302
  - MIT
309
303
  metadata:
304
+ bug_tracker_uri: https://github.com/oleander/remap/issues
305
+ homepage_uri: https://github.com/oleander/remap
306
+ documentation_uri: http://oleander.io/remap/
310
307
  rubygems_mfa_required: 'true'
311
308
  post_install_message:
312
309
  rdoc_options: []
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Remap
4
- class Rule
5
- using State::Extension
6
-
7
- # Iterates over a rule, even if the rule is not a collection
8
- #
9
- # @example Upcase each value in an array
10
- # state = Remap::State.call(["John", "Jane"])
11
- # upcase = Remap::Rule::Map.call({}).then(&:upcase)
12
- # each = Remap::Rule::Each.call(rule: upcase)
13
- # error = -> failure { raise failure.exception }
14
- # each.call(state, &error).fetch(:value) # => ["JOHN", "JANE"]
15
- class Each < Unit
16
- # @return [Rule]
17
- attribute :rule, Types::Rule
18
-
19
- # Iterates over state and passes each value to rule
20
- # Restores element, key & index before returning state
21
- #
22
- # @param state [State<Enumerable>]
23
- #
24
- # @return [State<Enumerable>]
25
- def call(state, &error)
26
- unless error
27
- raise ArgumentError, "Each#call(state, &error) requires a block"
28
- end
29
-
30
- state.map do |inner_state|
31
- rule.call(inner_state) do |failure|
32
- return error[failure]
33
- end
34
- end
35
- end
36
- end
37
- end
38
- end
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Remap
4
- class Rule
5
- using State::Extension
6
-
7
- # Embed mappers into each other
8
- #
9
- # @example Embed Mapper A into B
10
- # class Car < Remap::Base
11
- # define do
12
- # map :name, to: :model
13
- # end
14
- # end
15
- #
16
- # class Person < Remap::Base
17
- # define do
18
- # to :person do
19
- # to :car do
20
- # embed Car
21
- # end
22
- # end
23
- # end
24
- # end
25
- #
26
- # Person.call({name: "Volvo"}) # => { person: { car: { model: "Volvo" } } }
27
- class Embed < Unit
28
- # @return [#call!]
29
- attribute :mapper, Types::Mapper
30
-
31
- # Evaluates input against mapper and returns the result
32
- #
33
- # @param state [State<T>]
34
- #
35
- # @return [State<U>]
36
- def call(state, &error)
37
- unless error
38
- raise ArgumentError, "A block is required to evaluate the embed"
39
- end
40
-
41
- mapper.call!(state.set(mapper: mapper)) do |failure|
42
- return error[failure]
43
- end.except(:mapper, :scope)
44
- end
45
- end
46
- end
47
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Remap
4
- class Rule
5
- using State::Extension
6
-
7
- # Set path to a static value
8
- #
9
- # @example Set path [:a, :b] to value "C"
10
- # value = Remap::Static::Fixed.new(value: "a value")
11
- # set = Remap::Rule::Set.new(value: value, path: [:a, :b])
12
- # state = Remap::State.call("ANY VALUE")
13
- # set.call(state).fetch(:value) # => { a: { b: "a value" } }
14
- #
15
- # @example Set path [:a, :b] to option :c
16
- # value = Remap::Static::Option.new(name: :c)
17
- # set = Remap::Rule::Set.new(value: value, path: [:a, :b])
18
- # state = Remap::State.call("ANY VALUE", options: { c: "C" })
19
- # set.call(state).fetch(:value) # => { a: { b: "C" } }
20
- class Set < Concrete
21
- # @return [Static]
22
- attribute :value, Static, alias: :rule
23
-
24
- # @return [Path::Output]
25
- attribute :path, Path::Output
26
-
27
- # @see Rule#call
28
- def call(...)
29
- rule.call(...).then(&path)
30
- end
31
- end
32
- end
33
- end
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Remap
4
- class Rule
5
- class Collection
6
- using State::Extension
7
-
8
- # Represents an empty rule block
9
- class Empty < Unit
10
- attribute? :rules, Value(EMPTY_ARRAY), default: EMPTY_ARRAY
11
-
12
- # Represents an empty define block, without any rules
13
- #
14
- # @param state [State<T>]
15
- #
16
- # @return [State<T>]
17
- def call(state)
18
- state.notice!("No rules, empty block")
19
- end
20
- end
21
- end
22
- end
23
- end
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Remap
4
- class Rule
5
- class Collection
6
- using State::Extension
7
-
8
- # Represents a non-empty rule block
9
- #
10
- # @example A collection containing a single rule
11
- # state = Remap::State.call("A")
12
- # void = Remap::Rule::Void.call({})
13
- # rule = Remap::Rule::Collection.call([void])
14
- # error = -> failure { raise failure.exception }
15
- # rule.call(state, &error).fetch(:value) # => "A"
16
- class Filled < Unit
17
- # @return [Array<Rule>]
18
- attribute :rules, [Types.Interface(:call)], min_size: 1
19
-
20
- # Represents a non-empty define block with one or more rules
21
- # Calls every {#rules} with state and merges the output
22
- #
23
- # @param state [State]
24
- #
25
- # @return [State]
26
- def call(state, &error)
27
- unless error
28
- raise ArgumentError, "Collection::Filled#call(state, &error) requires a block"
29
- end
30
-
31
- rules.map do |rule|
32
- rule.call(state) do |failure|
33
- return error[failure]
34
- end
35
- end.reduce(&:combine)
36
- end
37
- end
38
- end
39
- end
40
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Remap
4
- class Rule
5
- # Represents a block defined by a rule
6
- class Collection < Abstract
7
- attribute :rules, Array
8
-
9
- # @param state [State]
10
- #
11
- # @return [State]
12
- #
13
- # @abstract
14
- def call(state)
15
- raise NotImplementedError, "#{self.class}#call not implemented"
16
- end
17
- end
18
- end
19
- end
@@ -1,86 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Remap
4
- class Rule
5
- class Enum < Proxy
6
- include Dry::Monads[:maybe]
7
-
8
- # @return [Hash]
9
- option :mappings, default: -> { Hash.new { default } }
10
-
11
- # @return [Maybe]
12
- option :default, default: -> { None() }
13
-
14
- alias execute instance_eval
15
-
16
- # Builds an enumeration using the block as context
17
- #
18
- # @example
19
- # enum = Remap::Rule::Enum.call do
20
- # from "B", to: "C"
21
- # value "A"
22
- # otherwise "D"
23
- # end
24
- #
25
- # enum.get("A") # => "A"
26
- # enum.get("B") # => "C"
27
- # enum.get("C") # => "C"
28
- # enum.get("MISSING") # => "D"
29
- #
30
- # @return [Any]
31
- def self.call(&block)
32
- unless block
33
- raise ArgumentError, "no block given"
34
- end
35
-
36
- new.tap { _1.execute(&block) }
37
- end
38
-
39
- # Translates key into a value using predefined mappings
40
- #
41
- # @param key [#hash]
42
- #
43
- # @yield [String]
44
- # If the key is not found & no default value is set
45
- #
46
- # @return [Any]
47
- def get(key, &error)
48
- unless error
49
- return get(key) { raise Error, _1 }
50
- end
51
-
52
- self[key].bind { return _1 }.or do
53
- error["Enum key [#{key}] not found among [#{mappings.keys.inspect}]"]
54
- end
55
- end
56
- alias call get
57
-
58
- # @return [Maybe]
59
- def [](key)
60
- mappings[key]
61
- end
62
-
63
- # @return [void]
64
- def from(*keys, to:)
65
- value = Some(to)
66
-
67
- keys.each do |key|
68
- mappings[key] = value
69
- mappings[to] = value
70
- end
71
- end
72
-
73
- # @return [void]
74
- def value(*ids)
75
- ids.each do |id|
76
- from(id, to: id)
77
- end
78
- end
79
-
80
- # @return [void]
81
- def otherwise(value)
82
- mappings.default = Some(value)
83
- end
84
- end
85
- end
86
- end
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Remap
4
- class Rule
5
- using State::Extension
6
-
7
- # Wraps rule in a type
8
- #
9
- # @example Maps { name: "Ford" } to { cars: ["Ford"] }
10
- # class Mapper < Remap::Base
11
- # define do
12
- # to :cars do
13
- # wrap(:array) do
14
- # map :name
15
- # end
16
- # end
17
- # end
18
- # end
19
- #
20
- # Mapper.call({ name: "Ford" }) # => { cars: ["Ford"] }
21
- class Wrap < Concrete
22
- # @return [:array]
23
- attribute :type, Value(:array)
24
-
25
- # @return [Rule]
26
- attribute :rule, Types::Rule
27
-
28
- # Wraps the output from {#rule} in a {#type}
29
- #
30
- # @see Rule#call
31
- def call(...)
32
- rule.call(...).fmap do |value|
33
- Array.wrap(value)
34
- end
35
- end
36
- end
37
- end
38
- end