remap 2.0.2
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 +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
|