remap 2.0.3 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/remap/base.rb +229 -75
- data/lib/remap/compiler.rb +127 -37
- data/lib/remap/constructor/argument.rb +20 -6
- data/lib/remap/constructor/keyword.rb +20 -4
- data/lib/remap/constructor/none.rb +3 -4
- data/lib/remap/constructor.rb +12 -5
- data/lib/remap/contract.rb +27 -0
- data/lib/remap/extensions/enumerable.rb +48 -0
- data/lib/remap/extensions/hash.rb +13 -0
- data/lib/remap/extensions/object.rb +37 -0
- data/lib/remap/failure.rb +25 -15
- data/lib/remap/iteration/array.rb +20 -11
- data/lib/remap/iteration/hash.rb +21 -13
- data/lib/remap/iteration/other.rb +7 -7
- data/lib/remap/iteration.rb +8 -2
- data/lib/remap/mapper/and.rb +29 -7
- data/lib/remap/mapper/binary.rb +3 -6
- data/lib/remap/mapper/or.rb +29 -6
- data/lib/remap/mapper/support/operations.rb +40 -0
- data/lib/remap/mapper/xor.rb +29 -7
- data/lib/remap/mapper.rb +1 -48
- data/lib/remap/notice/traced.rb +19 -0
- data/lib/remap/notice/untraced.rb +11 -0
- data/lib/remap/notice.rb +34 -0
- data/lib/remap/operation.rb +26 -13
- data/lib/remap/path/input.rb +37 -0
- data/lib/remap/path/output.rb +26 -0
- data/lib/remap/path.rb +22 -0
- data/lib/remap/path_error.rb +13 -0
- data/lib/remap/proxy.rb +18 -0
- data/lib/remap/rule/each.rb +25 -24
- data/lib/remap/rule/embed.rb +33 -28
- data/lib/remap/rule/map/optional.rb +42 -0
- data/lib/remap/rule/map/required.rb +35 -0
- data/lib/remap/rule/map.rb +176 -55
- data/lib/remap/rule/set.rb +23 -33
- data/lib/remap/rule/support/collection/empty.rb +7 -7
- data/lib/remap/rule/support/collection/filled.rb +21 -8
- data/lib/remap/rule/support/collection.rb +11 -3
- data/lib/remap/rule/support/enum.rb +44 -21
- data/lib/remap/rule/void.rb +17 -18
- data/lib/remap/rule/wrap.rb +25 -17
- data/lib/remap/rule.rb +8 -1
- data/lib/remap/selector/all.rb +29 -7
- data/lib/remap/selector/index.rb +24 -16
- data/lib/remap/selector/key.rb +31 -16
- data/lib/remap/selector.rb +17 -0
- data/lib/remap/state/extension.rb +182 -208
- data/lib/remap/state/schema.rb +1 -1
- data/lib/remap/state.rb +30 -4
- data/lib/remap/static/fixed.rb +14 -3
- data/lib/remap/static/option.rb +21 -6
- data/lib/remap/static.rb +13 -0
- data/lib/remap/struct.rb +1 -0
- data/lib/remap/types.rb +13 -28
- data/lib/remap.rb +15 -19
- metadata +91 -89
- data/lib/remap/result.rb +0 -11
- data/lib/remap/rule/support/path.rb +0 -45
- data/lib/remap/state/types.rb +0 -11
- data/lib/remap/success.rb +0 -29
- data/lib/remap/version.rb +0 -5
@@ -1,52 +1,101 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/hash/deep_transform_values"
|
4
|
-
|
5
3
|
module Remap
|
6
4
|
module State
|
7
5
|
module Extension
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
return _ { raise _1 }
|
12
|
-
end
|
13
|
-
|
14
|
-
block["Expected a state, got [#{self}] (#{self.class})"]
|
15
|
-
end
|
6
|
+
using Extensions::Enumerable
|
7
|
+
using Extensions::Object
|
8
|
+
using Extensions::Hash
|
16
9
|
|
10
|
+
refine Object do
|
11
|
+
# @see Extension::Paths::Hash
|
17
12
|
def paths
|
18
13
|
EMPTY_ARRAY
|
19
14
|
end
|
20
|
-
|
21
|
-
def get(*path)
|
22
|
-
throw :missing, path
|
23
|
-
end
|
24
15
|
end
|
25
16
|
|
26
|
-
refine
|
27
|
-
|
28
|
-
|
29
|
-
|
17
|
+
refine Hash do
|
18
|
+
# Returns a list of all key paths
|
19
|
+
#
|
20
|
+
# @example Get paths
|
21
|
+
# {
|
22
|
+
# a: {
|
23
|
+
# b: :c
|
24
|
+
# },
|
25
|
+
# d: :e
|
26
|
+
# }.hur_paths # => [[:a, :b], [:d]]
|
27
|
+
#
|
28
|
+
# @return [Array<Array<Symbol>>] a list of key paths
|
29
|
+
def paths
|
30
|
+
reduce(EMPTY_ARRAY) do |acc, (path, leaves)|
|
31
|
+
if (paths = leaves.paths).empty?
|
32
|
+
next acc + [[path]]
|
33
|
+
end
|
34
|
+
|
35
|
+
acc + paths.map { |inner| [path] + inner }
|
30
36
|
end
|
31
37
|
end
|
32
38
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
+
# Restrict hash to passed key path
|
40
|
+
#
|
41
|
+
# @param key [Symbol] to be kept
|
42
|
+
# @param rest [Array<Symbol>] of the key path
|
43
|
+
#
|
44
|
+
# @example Select key path
|
45
|
+
# {
|
46
|
+
# a: {
|
47
|
+
# b: :c
|
48
|
+
# },
|
49
|
+
# d: :e
|
50
|
+
# }.hur_only(:a, :b) # => { a: { b: :c } }
|
51
|
+
#
|
52
|
+
# @returns [Hash] a hash containing the given path
|
53
|
+
# @raise Europace::Error when path doesn't exist
|
54
|
+
def only(*path)
|
55
|
+
path.reduce(EMPTY_HASH) do |hash, key|
|
56
|
+
next hash unless key?(key)
|
39
57
|
|
40
|
-
|
41
|
-
throw :missing, path + [last]
|
58
|
+
hash.deep_merge(key => fetch(key))
|
42
59
|
end
|
43
60
|
end
|
44
|
-
end
|
45
61
|
|
46
|
-
|
62
|
+
# Throws :fatal containing a Notice
|
63
|
+
def fatal!(...)
|
64
|
+
throw :fatal, notice(...)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Throws :warn containing a Notice
|
68
|
+
def notice!(...)
|
69
|
+
throw :notice, notice(...)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Throws :ignore containing a Notice
|
73
|
+
def ignore!(...)
|
74
|
+
throw :ignore, notice(...)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Creates a notice containing the given message
|
78
|
+
#
|
79
|
+
# @param template [String]
|
80
|
+
# @param values [Array]
|
81
|
+
#
|
82
|
+
# @return [Notice]
|
83
|
+
def notice(template, *values)
|
84
|
+
Notice.call(only(:value, :path).merge(reason: template % values))
|
85
|
+
end
|
86
|
+
|
87
|
+
# Validates {self} against {Schema}
|
88
|
+
#
|
89
|
+
# Only used during development
|
90
|
+
#
|
91
|
+
# @yield [Hash] if schema fails
|
92
|
+
#
|
93
|
+
# @raise if schema fails and no block is given
|
94
|
+
#
|
95
|
+
# @return [self]
|
47
96
|
def _(&block)
|
48
97
|
unless block
|
49
|
-
return _ { raise "Input: #{self} output: #{
|
98
|
+
return _ { raise ArgumentError, "Input: #{self} output: #{_1.formated}" }
|
50
99
|
end
|
51
100
|
|
52
101
|
unless (result = Schema.call(self)).success?
|
@@ -56,22 +105,6 @@ module Remap
|
|
56
105
|
self
|
57
106
|
end
|
58
107
|
|
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
108
|
# Makes the state iterable
|
76
109
|
#
|
77
110
|
# @yieldparam value [Any]
|
@@ -84,16 +117,18 @@ module Remap
|
|
84
117
|
def map(&block)
|
85
118
|
bind do |value, state|
|
86
119
|
Iteration.call(state: state, value: value).call do |other, **options|
|
87
|
-
state.set(other, **options).
|
88
|
-
|
120
|
+
state.set(other, **options).then do |inner_state|
|
121
|
+
block[inner_state] do |failure|
|
122
|
+
throw :failure, failure
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end.except(:index, :element, :key)
|
89
126
|
end
|
90
127
|
end
|
91
128
|
|
92
129
|
# @return [String]
|
93
130
|
def inspect
|
94
|
-
|
95
|
-
format("#<State %<json>s>", json: JSON.pretty_generate(cleaned))
|
96
|
-
end
|
131
|
+
"#<State %s>" % compact_blank.formated
|
97
132
|
end
|
98
133
|
|
99
134
|
# Merges {self} with {other} and returns a new state
|
@@ -101,102 +136,31 @@ module Remap
|
|
101
136
|
# @param other [State]
|
102
137
|
#
|
103
138
|
# @return [State]
|
104
|
-
def
|
105
|
-
|
106
|
-
case [
|
107
|
-
in [Array, Array]
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
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)
|
139
|
+
def combine(other)
|
140
|
+
deep_merge(other) do |key, value1, value2|
|
141
|
+
case [key, value1, value2]
|
142
|
+
in [:value, Array => list1, Array => list2]
|
143
|
+
list1 + list2
|
144
|
+
in [:value, left, right]
|
145
|
+
fatal!(
|
146
|
+
"Could not merge [%p] (%s) with [%p] (%s) @ %s",
|
147
|
+
left,
|
148
|
+
left.class,
|
149
|
+
right,
|
150
|
+
right.class,
|
151
|
+
(path + [key]).join("."))
|
152
|
+
in [:notices, Array => n1, Array => n2]
|
153
|
+
n1 + n2
|
154
|
+
in [Symbol, _, value]
|
155
|
+
value
|
144
156
|
end
|
145
157
|
end
|
146
158
|
end
|
147
159
|
|
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
160
|
# Creates a new state with params
|
197
161
|
#
|
198
162
|
# @param value [Any, Undefined] Used as {value:}
|
199
|
-
# @options [Hash] To be
|
163
|
+
# @options [Hash] To be combine into {self}
|
200
164
|
#
|
201
165
|
# @return [State]
|
202
166
|
def set(value = Undefined, **options)
|
@@ -205,10 +169,8 @@ module Remap
|
|
205
169
|
end
|
206
170
|
|
207
171
|
case [self, options]
|
208
|
-
in [{
|
209
|
-
merge(
|
210
|
-
in [_, {mapper:, value:, **rest}]
|
211
|
-
merge(scope: value, value: value, mapper: mapper).set(**rest)
|
172
|
+
in [{notices:}, {notice: notice, **rest}]
|
173
|
+
merge(notices: notices + [notice]).set(**rest)
|
212
174
|
in [{value:}, {mapper:, **rest}]
|
213
175
|
merge(scope: value, mapper: mapper).set(**rest)
|
214
176
|
in [{path:}, {key:, **rest}]
|
@@ -224,7 +186,7 @@ module Remap
|
|
224
186
|
|
225
187
|
# Passes {#value} to block, if defined
|
226
188
|
# The return value is then wrapped in a state
|
227
|
-
# and returned with {options}
|
189
|
+
# and returned with {options} combine into the state
|
228
190
|
#
|
229
191
|
# @yieldparam value [T]
|
230
192
|
# @yieldparam self [State]
|
@@ -239,48 +201,44 @@ module Remap
|
|
239
201
|
end
|
240
202
|
end
|
241
203
|
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
in [
|
257
|
-
|
258
|
-
|
204
|
+
# Creates a failure to be used in {Remap::Base} & {Remap::Mapper}
|
205
|
+
#
|
206
|
+
# @param reason [#to_s]
|
207
|
+
#
|
208
|
+
# @see State::Schema
|
209
|
+
#
|
210
|
+
# @return [Failure]
|
211
|
+
|
212
|
+
# class Failure < Dry::Interface
|
213
|
+
# attribute :notices, [Notice], min_size: 1
|
214
|
+
# end
|
215
|
+
|
216
|
+
def failure(reason = Undefined)
|
217
|
+
failures = case [path, reason]
|
218
|
+
in [_, Notice => notice]
|
219
|
+
[notice]
|
220
|
+
in [path, Array => reasons]
|
221
|
+
reasons.map do |inner_reason|
|
222
|
+
Notice.call(path: path, reason: inner_reason, **only(:value))
|
259
223
|
end
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
224
|
+
in [path, String => reason]
|
225
|
+
[Notice.call(path: path, reason: reason, **only(:value))]
|
226
|
+
in [path, Hash => errors]
|
227
|
+
errors.paths.flat_map do |sufix|
|
228
|
+
Array.wrap(errors.dig(*sufix)).map do |inner_reason|
|
229
|
+
Notice.call(
|
230
|
+
reason: inner_reason,
|
231
|
+
path: path + sufix,
|
232
|
+
**only(:value))
|
233
|
+
end
|
268
234
|
end
|
269
235
|
end
|
270
236
|
|
271
|
-
|
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
|
237
|
+
Failure.new(failures: failures, notices: notices)
|
280
238
|
end
|
281
239
|
|
282
240
|
# Passes {#value} to block, if defined
|
283
|
-
# {options} are
|
241
|
+
# {options} are combine into the final state
|
284
242
|
#
|
285
243
|
# @yieldparam value [T]
|
286
244
|
# @yieldparam self [State]
|
@@ -290,13 +248,13 @@ module Remap
|
|
290
248
|
#
|
291
249
|
# @return [Y]
|
292
250
|
def bind(**options, &block)
|
293
|
-
unless
|
294
|
-
raise ArgumentError, "
|
251
|
+
unless block_given?
|
252
|
+
raise ArgumentError, "State#bind requires a block"
|
295
253
|
end
|
296
254
|
|
297
255
|
fetch(:value) { return self }.then do |value|
|
298
256
|
block[value, self] do |reason, **other|
|
299
|
-
return set(**options, **other).
|
257
|
+
return set(**options, **other).notice!(reason)
|
300
258
|
end
|
301
259
|
end
|
302
260
|
end
|
@@ -304,25 +262,27 @@ module Remap
|
|
304
262
|
# Execute {block} in the current context
|
305
263
|
# Only calls {block} if {#value} is defined
|
306
264
|
#
|
307
|
-
# @yieldparam
|
265
|
+
# @yieldparam [T]
|
308
266
|
# @yieldreturn [U]
|
309
267
|
#
|
310
268
|
# @return [State<U>]
|
311
269
|
def execute(&block)
|
312
270
|
bind do |value, &error|
|
313
|
-
|
314
|
-
path = catch :missing do
|
315
|
-
throw :success, set(context(value, &error).instance_exec(value, &block))._
|
316
|
-
end
|
271
|
+
result = context(value, &error).instance_exec(value, &block)
|
317
272
|
|
318
|
-
|
273
|
+
if result.equal?(Dry::Core::Constants::Undefined)
|
274
|
+
return error["Undefined returned, skipping!"]
|
319
275
|
end
|
276
|
+
|
277
|
+
set(result)._
|
320
278
|
rescue NoMethodError => e
|
321
279
|
e.name == :fetch ? error["Fetch not defined on value: #{e}"] : raise
|
322
280
|
rescue NameError => e
|
323
281
|
e.name == :Undefined ? error["Undefined returned, skipping!: #{e}"] : raise
|
324
282
|
rescue KeyError, IndexError => e
|
325
283
|
error[e.message]
|
284
|
+
rescue PathError => e
|
285
|
+
ignore!("Path %s not defined for %p (%s)", e.path.join("."), value, value.class)
|
326
286
|
end
|
327
287
|
end
|
328
288
|
|
@@ -333,42 +293,56 @@ module Remap
|
|
333
293
|
super { fmap(&block) }
|
334
294
|
end
|
335
295
|
|
336
|
-
#
|
296
|
+
# A list of keys representing the path to {#value}
|
297
|
+
#
|
298
|
+
# @return [Array<Symbol, Integer, String>]
|
299
|
+
def path
|
300
|
+
fetch(:path, EMPTY_ARRAY)
|
301
|
+
end
|
302
|
+
|
303
|
+
# Represents options to a mapper
|
337
304
|
#
|
338
|
-
# @
|
305
|
+
# @see Rule::Embed
|
339
306
|
#
|
340
307
|
# @return [Hash]
|
341
|
-
def
|
342
|
-
|
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})"
|
308
|
+
def options
|
309
|
+
fetch(:options)
|
350
310
|
end
|
351
311
|
|
312
|
+
# Used by {#context} to create a limited context
|
313
|
+
#
|
314
|
+
# @return [Hash]
|
352
315
|
def to_hash
|
353
|
-
except(:options, :
|
316
|
+
super.except(:options, :notices, :value)
|
354
317
|
end
|
355
318
|
|
319
|
+
# @return [Any]
|
356
320
|
def value
|
357
321
|
fetch(:value)
|
358
322
|
end
|
359
323
|
|
360
|
-
|
361
|
-
|
324
|
+
# @return [Integer]
|
325
|
+
def index
|
326
|
+
fetch(:index)
|
362
327
|
end
|
363
328
|
|
364
|
-
|
365
|
-
|
329
|
+
# @return [Any]
|
330
|
+
def element
|
331
|
+
fetch(:element)
|
366
332
|
end
|
367
333
|
|
368
|
-
|
369
|
-
|
334
|
+
# @return [Any]
|
335
|
+
def key
|
336
|
+
fetch(:key)
|
370
337
|
end
|
371
338
|
|
339
|
+
# @return [Array<Notice>]
|
340
|
+
def notices
|
341
|
+
fetch(:notices)
|
342
|
+
end
|
343
|
+
|
344
|
+
private
|
345
|
+
|
372
346
|
# Creates a context containing {options} and {self}
|
373
347
|
#
|
374
348
|
# @param value [Any]
|
@@ -376,14 +350,14 @@ module Remap
|
|
376
350
|
# @yieldparam reason [T]
|
377
351
|
#
|
378
352
|
# @return [Struct]
|
379
|
-
def context(value,
|
353
|
+
def context(value, context: self, &error)
|
380
354
|
::Struct.new(*keys, *options.keys, keyword_init: true) do
|
381
355
|
define_method :method_missing do |name, *|
|
382
356
|
error["Method [#{name}] not defined"]
|
383
357
|
end
|
384
358
|
|
385
359
|
define_method :skip! do |message = "Manual skip!"|
|
386
|
-
|
360
|
+
context.ignore!(message)
|
387
361
|
end
|
388
362
|
end.new(**to_hash, **options, value: value)
|
389
363
|
end
|
data/lib/remap/state/schema.rb
CHANGED
data/lib/remap/state.rb
CHANGED
@@ -8,15 +8,41 @@ require "dry/logic"
|
|
8
8
|
require "dry/logic/operations"
|
9
9
|
require "dry/logic/predicates"
|
10
10
|
require "json"
|
11
|
-
require "pry"
|
12
11
|
|
13
12
|
module Remap
|
13
|
+
# Represents the current state of a mapping
|
14
14
|
module State
|
15
|
-
|
15
|
+
using Extensions::Object
|
16
16
|
using Extension
|
17
17
|
|
18
|
-
|
19
|
-
|
18
|
+
include Dry::Core::Constants
|
19
|
+
|
20
|
+
class Dummy < Remap::Base
|
21
|
+
# NOP
|
22
|
+
end
|
23
|
+
|
24
|
+
# Creates a valid state
|
25
|
+
#
|
26
|
+
# @param value [Any] Internal state value
|
27
|
+
#
|
28
|
+
# @option mapper [Mapper::Class] Mapper class
|
29
|
+
# @option options [Hash] Mapper options / arguments
|
30
|
+
#
|
31
|
+
# @return [Hash] A valid state
|
32
|
+
def self.call(value, mapper: Dummy, options: EMPTY_HASH)
|
33
|
+
value._ do
|
34
|
+
return {
|
35
|
+
notices: EMPTY_ARRAY,
|
36
|
+
path: EMPTY_ARRAY,
|
37
|
+
options: options,
|
38
|
+
mapper: mapper,
|
39
|
+
values: value,
|
40
|
+
value: value,
|
41
|
+
input: value
|
42
|
+
}._
|
43
|
+
end
|
44
|
+
|
45
|
+
raise ArgumentError, "Input is a state: #{value}"
|
20
46
|
end
|
21
47
|
end
|
22
48
|
end
|
data/lib/remap/static/fixed.rb
CHANGED
@@ -2,12 +2,23 @@
|
|
2
2
|
|
3
3
|
module Remap
|
4
4
|
class Static
|
5
|
-
|
6
|
-
using State::Extension
|
5
|
+
using State::Extension
|
7
6
|
|
7
|
+
# Maps a fixed value to state
|
8
|
+
#
|
9
|
+
# @example Map a fixed value to path
|
10
|
+
# class Mapper < Remap::Base
|
11
|
+
# define do
|
12
|
+
# set :a, :b, to: value('a value')
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# Mapper.call({}) # => { a: { b: 'a value' } }
|
17
|
+
class Fixed < Concrete
|
18
|
+
# @return [Any]
|
8
19
|
attribute :value, Types::Any
|
9
20
|
|
10
|
-
# Set
|
21
|
+
# Set state to {#value}
|
11
22
|
#
|
12
23
|
# @param state [State]
|
13
24
|
#
|
data/lib/remap/static/option.rb
CHANGED
@@ -2,20 +2,35 @@
|
|
2
2
|
|
3
3
|
module Remap
|
4
4
|
class Static
|
5
|
-
|
6
|
-
using State::Extension
|
5
|
+
using State::Extension
|
7
6
|
|
7
|
+
# Maps a mapper argument to a path
|
8
|
+
#
|
9
|
+
# @example Maps a mapper argument to a path
|
10
|
+
# class Mapper < Remap::Base
|
11
|
+
# option :name
|
12
|
+
#
|
13
|
+
# define do
|
14
|
+
# set :nickname, to: option(:name)
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# Mapper.call({}, name: "John") # => { nickname: "John" }
|
19
|
+
class Option < Concrete
|
20
|
+
# @return [Symbol]
|
8
21
|
attribute :name, Symbol
|
9
22
|
|
10
|
-
# Selects {#
|
23
|
+
# Selects {#name} from state
|
11
24
|
#
|
12
25
|
# @param state [State]
|
13
26
|
#
|
14
27
|
# @return [State]
|
15
28
|
def call(state)
|
16
|
-
state.set
|
17
|
-
|
18
|
-
|
29
|
+
state.set(state.options.fetch(name))
|
30
|
+
rescue KeyError => e
|
31
|
+
raise ArgumentError, e.exception("Option [%s] not found in input [%p]" % [
|
32
|
+
name, state.options
|
33
|
+
])
|
19
34
|
end
|
20
35
|
end
|
21
36
|
end
|
data/lib/remap/static.rb
CHANGED
@@ -1,6 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Remap
|
4
|
+
# A static mapped value either represented by an option or a value
|
4
5
|
class Static < Dry::Interface
|
6
|
+
attribute? :backtrace, Types::Backtrace
|
7
|
+
|
8
|
+
# Maps a static value to state
|
9
|
+
#
|
10
|
+
# @param state [State<T>]
|
11
|
+
#
|
12
|
+
# @return [State<Y>]
|
13
|
+
#
|
14
|
+
# @abstract
|
15
|
+
def call(state)
|
16
|
+
raise NotImplementedError, "#{self.class}#call not implemented"
|
17
|
+
end
|
5
18
|
end
|
6
19
|
end
|