remap 2.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/remap/base.rb +146 -0
- data/lib/remap/compiler.rb +171 -0
- data/lib/remap/constructor/argument.rb +28 -0
- data/lib/remap/constructor/keyword.rb +31 -0
- data/lib/remap/constructor/none.rb +23 -0
- data/lib/remap/constructor.rb +30 -0
- data/lib/remap/error.rb +7 -0
- data/lib/remap/failure.rb +27 -0
- data/lib/remap/iteration/array.rb +29 -0
- data/lib/remap/iteration/hash.rb +30 -0
- data/lib/remap/iteration/other.rb +18 -0
- data/lib/remap/iteration.rb +20 -0
- data/lib/remap/mapper/and.rb +29 -0
- data/lib/remap/mapper/binary.rb +16 -0
- data/lib/remap/mapper/or.rb +21 -0
- data/lib/remap/mapper/xor.rb +27 -0
- data/lib/remap/mapper.rb +56 -0
- data/lib/remap/nothing.rb +7 -0
- data/lib/remap/operation.rb +26 -0
- data/lib/remap/result.rb +11 -0
- data/lib/remap/rule/each.rb +37 -0
- data/lib/remap/rule/embed.rb +42 -0
- data/lib/remap/rule/map.rb +109 -0
- data/lib/remap/rule/set.rb +43 -0
- data/lib/remap/rule/support/collection/empty.rb +23 -0
- data/lib/remap/rule/support/collection/filled.rb +27 -0
- data/lib/remap/rule/support/collection.rb +11 -0
- data/lib/remap/rule/support/enum.rb +63 -0
- data/lib/remap/rule/support/path.rb +45 -0
- data/lib/remap/rule/void.rb +29 -0
- data/lib/remap/rule/wrap.rb +30 -0
- data/lib/remap/rule.rb +8 -0
- data/lib/remap/selector/all.rb +21 -0
- data/lib/remap/selector/index.rb +39 -0
- data/lib/remap/selector/key.rb +38 -0
- data/lib/remap/selector.rb +14 -0
- data/lib/remap/state/extension.rb +393 -0
- data/lib/remap/state/schema.rb +21 -0
- data/lib/remap/state/types.rb +11 -0
- data/lib/remap/state.rb +22 -0
- data/lib/remap/static/fixed.rb +20 -0
- data/lib/remap/static/option.rb +22 -0
- data/lib/remap/static.rb +6 -0
- data/lib/remap/struct.rb +7 -0
- data/lib/remap/success.rb +29 -0
- data/lib/remap/types.rb +47 -0
- data/lib/remap/version.rb +5 -0
- data/lib/remap.rb +28 -0
- metadata +329 -0
@@ -0,0 +1,393 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/hash/deep_transform_values"
|
4
|
+
|
5
|
+
module Remap
|
6
|
+
module State
|
7
|
+
module Extension
|
8
|
+
refine Object do
|
9
|
+
def _(&block)
|
10
|
+
unless block
|
11
|
+
return _ { raise _1 }
|
12
|
+
end
|
13
|
+
|
14
|
+
block["Expected a state, got [#{self}] (#{self.class})"]
|
15
|
+
end
|
16
|
+
|
17
|
+
def paths
|
18
|
+
EMPTY_ARRAY
|
19
|
+
end
|
20
|
+
|
21
|
+
def get(*path)
|
22
|
+
throw :missing, path
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
refine Array do
|
27
|
+
def hide(value)
|
28
|
+
reverse.reduce(value) do |element, key|
|
29
|
+
{ key => element }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def get(*path, last)
|
34
|
+
if path.empty?
|
35
|
+
return fetch(last) do
|
36
|
+
throw :missing, path + [last]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
dig(*path).fetch(last) do
|
41
|
+
throw :missing, path + [last]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
refine Hash do
|
47
|
+
def _(&block)
|
48
|
+
unless block
|
49
|
+
return _ { raise "Input: #{self} output: #{JSON.pretty_generate(_1)}" }
|
50
|
+
end
|
51
|
+
|
52
|
+
unless (result = Schema.call(self)).success?
|
53
|
+
return block[result.errors.to_h]
|
54
|
+
end
|
55
|
+
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def paths
|
60
|
+
reduce(EMPTY_ARRAY) do |acc, (path, leaves)|
|
61
|
+
if (paths = leaves.paths).empty?
|
62
|
+
next acc + [[path]]
|
63
|
+
end
|
64
|
+
|
65
|
+
acc + paths.map { |inner| [path] + inner }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def paths_pair
|
70
|
+
paths.map do |path|
|
71
|
+
[dig(*path), path]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Makes the state iterable
|
76
|
+
#
|
77
|
+
# @yieldparam value [Any]
|
78
|
+
# @yieldoption key [Symbol]
|
79
|
+
# @yieldoption index [Integer]
|
80
|
+
#
|
81
|
+
# @yieldreturn [State]
|
82
|
+
#
|
83
|
+
# @return [State]
|
84
|
+
def map(&block)
|
85
|
+
bind do |value, state|
|
86
|
+
Iteration.call(state: state, value: value).call do |other, **options|
|
87
|
+
state.set(other, **options)._.then(&block)._
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# @return [String]
|
93
|
+
def inspect
|
94
|
+
reject { _2.blank? }.then do |cleaned|
|
95
|
+
format("#<State %<json>s>", json: JSON.pretty_generate(cleaned))
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Merges {self} with {other} and returns a new state
|
100
|
+
#
|
101
|
+
# @param other [State]
|
102
|
+
#
|
103
|
+
# @return [State]
|
104
|
+
def merged(other)
|
105
|
+
all_problems = problems.deep_merge(other.problems) do |key, a, b|
|
106
|
+
case [a, b]
|
107
|
+
in [Array, Array]
|
108
|
+
a + b
|
109
|
+
else
|
110
|
+
raise ArgumentError, "Can't merge #{a.inspect} with #{b.inspect} @ #{key}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
catch :undefined do
|
115
|
+
value = recursive_merge(other) do |reason|
|
116
|
+
return merge(problems: all_problems).problem(reason)
|
117
|
+
end
|
118
|
+
|
119
|
+
return set(value, problems: all_problems)
|
120
|
+
end
|
121
|
+
|
122
|
+
set(problems: all_problems)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Resolves conflicts unsovable by ActiveSupport#deep_merge
|
126
|
+
#
|
127
|
+
# @param key [Symbol] the key that cannot be merged
|
128
|
+
# @param left [Any] the left value that cannot be merged
|
129
|
+
# @param right [Any] the right value that cannot be merged
|
130
|
+
#
|
131
|
+
# @yieldparam reason [String] if {left} and {right} cannot be merged
|
132
|
+
# @yieldreturn [State]
|
133
|
+
#
|
134
|
+
# @return [Any]
|
135
|
+
def conflicts(key, left, right, &error)
|
136
|
+
case [left, right]
|
137
|
+
in [Array, Array]
|
138
|
+
left + right
|
139
|
+
in [value, ^value]
|
140
|
+
value
|
141
|
+
in [left, right]
|
142
|
+
reason(left, right) do |reason|
|
143
|
+
[reason, "[#{key}]"].join(" @ ").then(&error)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def get(*path, last)
|
149
|
+
if path.empty?
|
150
|
+
return fetch(last) do
|
151
|
+
throw :missing, path + [last]
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
dig(*path).fetch(last) do
|
156
|
+
throw :missing, path + [last]
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Recursively merges {self} with {other}
|
161
|
+
# Invokes {error} when a conflict is detected
|
162
|
+
#
|
163
|
+
# @param other [State]
|
164
|
+
#
|
165
|
+
# @yieldparam key [Symbol]
|
166
|
+
# @yieldparam left [Any]
|
167
|
+
# @yieldparam right [Any]
|
168
|
+
# @yieldparam error [Proc]
|
169
|
+
#
|
170
|
+
# @yieldreturn [Any]
|
171
|
+
#
|
172
|
+
# @return [Any] Merge result (not a state)
|
173
|
+
def recursive_merge(other, &error)
|
174
|
+
case [self, other]
|
175
|
+
in [{value: Hash => left}, {value: Hash => right}]
|
176
|
+
left.deep_merge(right) { |*args| conflicts(*args, &error) }
|
177
|
+
in [{value: Array => left}, {value: Array => right}]
|
178
|
+
left + right
|
179
|
+
in [{value: left}, {value: right}]
|
180
|
+
reason(left, right, &error)
|
181
|
+
in [{value: left}, _]
|
182
|
+
left
|
183
|
+
in [_, {value: right}]
|
184
|
+
right
|
185
|
+
in [_, _]
|
186
|
+
throw :undefined
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def reason(left, right, &error)
|
191
|
+
params = { left: left, cleft: left.class, right: right, cright: right.class }
|
192
|
+
message = format("Could not merge [%<left>p] (%<cleft>s) with [%<right>p] (%<cright>s)", params)
|
193
|
+
error[message]
|
194
|
+
end
|
195
|
+
|
196
|
+
# Creates a new state with params
|
197
|
+
#
|
198
|
+
# @param value [Any, Undefined] Used as {value:}
|
199
|
+
# @options [Hash] To be merged into {self}
|
200
|
+
#
|
201
|
+
# @return [State]
|
202
|
+
def set(value = Undefined, **options)
|
203
|
+
if value != Undefined
|
204
|
+
return set(**options, value: value)
|
205
|
+
end
|
206
|
+
|
207
|
+
case [self, options]
|
208
|
+
in [{path:}, {quantifier:, **rest}]
|
209
|
+
merge(path: path + [quantifier]).set(**rest)
|
210
|
+
in [_, {mapper:, value:, **rest}]
|
211
|
+
merge(scope: value, value: value, mapper: mapper).set(**rest)
|
212
|
+
in [{value:}, {mapper:, **rest}]
|
213
|
+
merge(scope: value, mapper: mapper).set(**rest)
|
214
|
+
in [{path:}, {key:, **rest}]
|
215
|
+
merge(path: path + [key], key: key).set(**rest)
|
216
|
+
in [{path:}, {index:, value:, **rest}]
|
217
|
+
merge(path: path + [index], element: value, index: index, value: value).set(**rest)
|
218
|
+
in [{path:}, {index:, **rest}]
|
219
|
+
merge(path: path + [index], index: index).set(**rest)
|
220
|
+
else
|
221
|
+
merge(options)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# Passes {#value} to block, if defined
|
226
|
+
# The return value is then wrapped in a state
|
227
|
+
# and returned with {options} merged into the state
|
228
|
+
#
|
229
|
+
# @yieldparam value [T]
|
230
|
+
# @yieldparam self [State]
|
231
|
+
# @yieldparam error [Proc]
|
232
|
+
#
|
233
|
+
# @yieldreturn [Y]
|
234
|
+
#
|
235
|
+
# @return [State<Y>]
|
236
|
+
def fmap(**options, &block)
|
237
|
+
bind(**options) do |input, state, &error|
|
238
|
+
state.set(block[input, state, &error])
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def failure(reason)
|
243
|
+
explaination(reason)
|
244
|
+
end
|
245
|
+
|
246
|
+
def explaination(reason, explainations = EMPTY_HASH)
|
247
|
+
Remap::Types::Report::Self[explainations]
|
248
|
+
|
249
|
+
report = ->(message) { [{}.merge(reason: message)] }
|
250
|
+
|
251
|
+
explaination = case [self, reason]
|
252
|
+
in [{path: []}, String]
|
253
|
+
{ base: report[reason] }
|
254
|
+
in [{path:}, String]
|
255
|
+
path.hide(report[reason])
|
256
|
+
in [{path:}, Hash]
|
257
|
+
reason.paths_pair.reduce(EMPTY_HASH) do |acc, (item, suffix)|
|
258
|
+
Array.wrap(item).map { (path + suffix).hide(report[_1]) }.reduce(acc, &:deep_merge)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
output = explainations.deep_merge(explaination) do |key, left, right|
|
263
|
+
case [left, right]
|
264
|
+
in [Array, Array]
|
265
|
+
left + right
|
266
|
+
else
|
267
|
+
raise ArgumentError, "Cannot merge #{left} with #{right} @ #{key}"
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
# Remap::Types::Report::Self[output] do
|
272
|
+
# binding.pry
|
273
|
+
# end
|
274
|
+
end
|
275
|
+
|
276
|
+
def no_of_problems
|
277
|
+
a = problems.except(:base).paths.count
|
278
|
+
b = problems.fetch(:base, []).count
|
279
|
+
a + b
|
280
|
+
end
|
281
|
+
|
282
|
+
# Passes {#value} to block, if defined
|
283
|
+
# {options} are merged into the final state
|
284
|
+
#
|
285
|
+
# @yieldparam value [T]
|
286
|
+
# @yieldparam self [State]
|
287
|
+
# @yieldparam error [Proc]
|
288
|
+
#
|
289
|
+
# @yieldreturn [Y]
|
290
|
+
#
|
291
|
+
# @return [Y]
|
292
|
+
def bind(**options, &block)
|
293
|
+
unless block
|
294
|
+
raise ArgumentError, "no block given"
|
295
|
+
end
|
296
|
+
|
297
|
+
fetch(:value) { return self }.then do |value|
|
298
|
+
block[value, self] do |reason, **other|
|
299
|
+
return set(**options, **other).problem(reason)._
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
# Execute {block} in the current context
|
305
|
+
# Only calls {block} if {#value} is defined
|
306
|
+
#
|
307
|
+
# @yieldparam value [T]
|
308
|
+
# @yieldreturn [U]
|
309
|
+
#
|
310
|
+
# @return [State<U>]
|
311
|
+
def execute(&block)
|
312
|
+
bind do |value, &error|
|
313
|
+
catch :success do
|
314
|
+
path = catch :missing do
|
315
|
+
throw :success, set(context(value, &error).instance_exec(value, &block))._
|
316
|
+
end
|
317
|
+
|
318
|
+
return error["Could not fetch value at", path: path]
|
319
|
+
end
|
320
|
+
rescue NoMethodError => e
|
321
|
+
e.name == :fetch ? error["Fetch not defined on value: #{e}"] : raise
|
322
|
+
rescue NameError => e
|
323
|
+
e.name == :Undefined ? error["Undefined returned, skipping!: #{e}"] : raise
|
324
|
+
rescue KeyError, IndexError => e
|
325
|
+
error[e.message]
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
# Passes {#value} to block and returns {self}
|
330
|
+
#
|
331
|
+
# @return [self]
|
332
|
+
def tap(&block)
|
333
|
+
super { fmap(&block) }
|
334
|
+
end
|
335
|
+
|
336
|
+
# Ensures {value:} is not a state
|
337
|
+
#
|
338
|
+
# @param options [Hash]
|
339
|
+
#
|
340
|
+
# @return [Hash]
|
341
|
+
def merge(options)
|
342
|
+
case options
|
343
|
+
in {value:}
|
344
|
+
value._ { return super }
|
345
|
+
else
|
346
|
+
return super
|
347
|
+
end
|
348
|
+
|
349
|
+
raise ArgumentError, "Expected State#value not to be a State [#{value}] (#{value.class})"
|
350
|
+
end
|
351
|
+
|
352
|
+
def to_hash
|
353
|
+
except(:options, :mapper, :problems, :value)
|
354
|
+
end
|
355
|
+
|
356
|
+
def value
|
357
|
+
fetch(:value)
|
358
|
+
end
|
359
|
+
|
360
|
+
def problem(message)
|
361
|
+
merge(problems: explaination(message, problems)).except(:value)
|
362
|
+
end
|
363
|
+
|
364
|
+
def problems
|
365
|
+
fetch(:problems)
|
366
|
+
end
|
367
|
+
|
368
|
+
def options
|
369
|
+
fetch(:options)
|
370
|
+
end
|
371
|
+
|
372
|
+
# Creates a context containing {options} and {self}
|
373
|
+
#
|
374
|
+
# @param value [Any]
|
375
|
+
#
|
376
|
+
# @yieldparam reason [T]
|
377
|
+
#
|
378
|
+
# @return [Struct]
|
379
|
+
def context(value, state: self, &error)
|
380
|
+
::Struct.new(*keys, *options.keys, keyword_init: true) do
|
381
|
+
define_method :method_missing do |name, *|
|
382
|
+
error["Method [#{name}] not defined"]
|
383
|
+
end
|
384
|
+
|
385
|
+
define_method :skip! do |message = "Manual skip!"|
|
386
|
+
error[message]
|
387
|
+
end
|
388
|
+
end.new(**to_hash, **options, value: value)
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|
393
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Remap
|
4
|
+
module State
|
5
|
+
Schema = Dry::Schema.define do
|
6
|
+
required(:input)
|
7
|
+
|
8
|
+
required(:mapper).filled(Remap::Types::Mapper)
|
9
|
+
required(:problems).hash
|
10
|
+
required(:options).value(:hash)
|
11
|
+
required(:path).array(Types::Key)
|
12
|
+
|
13
|
+
optional(:index).filled(:integer)
|
14
|
+
optional(:element).filled
|
15
|
+
optional(:key).filled
|
16
|
+
|
17
|
+
optional(:scope)
|
18
|
+
optional(:value)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/remap/state.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/schema"
|
4
|
+
require "dry/validation"
|
5
|
+
require "dry/core/constants"
|
6
|
+
require "factory_bot"
|
7
|
+
require "dry/logic"
|
8
|
+
require "dry/logic/operations"
|
9
|
+
require "dry/logic/predicates"
|
10
|
+
require "json"
|
11
|
+
require "pry"
|
12
|
+
|
13
|
+
module Remap
|
14
|
+
module State
|
15
|
+
include Dry::Core::Constants
|
16
|
+
using Extension
|
17
|
+
|
18
|
+
def state(value, mapper:, options: {})
|
19
|
+
{ value: value, input: value, mapper: mapper, problems: {}, path: [], options: options, values: value }._
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Remap
|
4
|
+
class Static
|
5
|
+
class Fixed < Concrete
|
6
|
+
using State::Extension
|
7
|
+
|
8
|
+
attribute :value, Types::Any
|
9
|
+
|
10
|
+
# Set {state#value} to {#value}
|
11
|
+
#
|
12
|
+
# @param state [State]
|
13
|
+
#
|
14
|
+
# @return [State]
|
15
|
+
def call(state)
|
16
|
+
state.set(value)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Remap
|
4
|
+
class Static
|
5
|
+
class Option < Concrete
|
6
|
+
using State::Extension
|
7
|
+
|
8
|
+
attribute :name, Symbol
|
9
|
+
|
10
|
+
# Selects {#value} from {state#params}
|
11
|
+
#
|
12
|
+
# @param state [State]
|
13
|
+
#
|
14
|
+
# @return [State]
|
15
|
+
def call(state)
|
16
|
+
state.set state.options.fetch(name) {
|
17
|
+
return state.problem("Option [#{name}] not found in [#{state.options.inspect}]")
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/remap/static.rb
ADDED
data/lib/remap/struct.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Remap
|
4
|
+
class Success < Result
|
5
|
+
attribute :result, Types::Any
|
6
|
+
|
7
|
+
def inspect
|
8
|
+
format("Success<[%<result>s]>", result: JSON.pretty_generate(to_h))
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_hash
|
12
|
+
{ success: result, problems: problems }
|
13
|
+
end
|
14
|
+
|
15
|
+
def failure?
|
16
|
+
false
|
17
|
+
end
|
18
|
+
|
19
|
+
def success?(value = Undefined)
|
20
|
+
return true if value.equal?(Undefined)
|
21
|
+
|
22
|
+
result == value
|
23
|
+
end
|
24
|
+
|
25
|
+
def fmap(&block)
|
26
|
+
block[result]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/remap/types.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/monads/maybe"
|
4
|
+
require "dry/logic/operations/negation"
|
5
|
+
require "dry/logic"
|
6
|
+
|
7
|
+
module Remap
|
8
|
+
module Types
|
9
|
+
include Dry::Types()
|
10
|
+
include Dry::Logic::Operations
|
11
|
+
|
12
|
+
using State::Extension
|
13
|
+
|
14
|
+
Enumerable = Any.constrained(type: Enumerable)
|
15
|
+
Mapper = Interface(:call!) # Class.constrained(lt: Remap::Mapper) | Instance(Remap::Mapper).constructor { |v, &e| Instance(Remap::Mapper::Binary).call(v, &e) }
|
16
|
+
Nothing = Constant(Remap::Nothing)
|
17
|
+
Maybe = Instance(Dry::Monads::Maybe).fallback(&Dry::Monads::Maybe)
|
18
|
+
# Remap = Class.constrained(lt: Remap)
|
19
|
+
Proc = Instance(Proc)
|
20
|
+
Key = Interface(:hash) | Integer
|
21
|
+
Value = Any # .constrained(not_eql: nil)
|
22
|
+
|
23
|
+
State = Hash.constructor do |value, type, &error|
|
24
|
+
type[value, &error]._(&error)
|
25
|
+
end
|
26
|
+
|
27
|
+
Selectors = Array.of(Remap::Selector)
|
28
|
+
|
29
|
+
Dry::Types.define_builder(:not) do |type, owner = Object|
|
30
|
+
type.constrained_type.new(Instance(owner), rule: Negation.new(type.rule))
|
31
|
+
end
|
32
|
+
|
33
|
+
module Report
|
34
|
+
include Dry.Types()
|
35
|
+
|
36
|
+
Problem = Hash.schema(value?: Any, reason: String)
|
37
|
+
|
38
|
+
Key = String | Symbol | Integer
|
39
|
+
|
40
|
+
Value = Any.constructor do |value, &error|
|
41
|
+
(Array(Problem) | Self).call(value, &error)
|
42
|
+
end
|
43
|
+
|
44
|
+
Self = Hash.map(Key, Value) | Hash.schema(base: Array(Problem))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/remap.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/struct"
|
4
|
+
require "dry/validation"
|
5
|
+
|
6
|
+
require "active_support/core_ext/module/delegation"
|
7
|
+
require "dry/core/class_builder"
|
8
|
+
require "dry/core/memoizable"
|
9
|
+
require "dry/core/deprecations"
|
10
|
+
require "dry/logic/builder"
|
11
|
+
require "dry-configurable"
|
12
|
+
require "dry/schema"
|
13
|
+
require "dry/types"
|
14
|
+
require "dry/monads"
|
15
|
+
require "dry/logic"
|
16
|
+
require "zeitwerk"
|
17
|
+
require "dry/interface"
|
18
|
+
|
19
|
+
Dry::Types.load_extensions(:maybe)
|
20
|
+
|
21
|
+
loader = Zeitwerk::Loader.for_gem
|
22
|
+
loader.collapse("#{__dir__}/remap/rule/support")
|
23
|
+
loader.setup
|
24
|
+
|
25
|
+
module Remap
|
26
|
+
end
|
27
|
+
|
28
|
+
loader.eager_load
|