remap 2.0.3 → 2.1.0
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 +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
|