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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b3b3344d7c0ab3825ba1bb0a80c5ddbd7a00d57dbf762b0e26cd3cc6216e0644
|
4
|
+
data.tar.gz: 30db4e775e16a7923c46d4f1ca41b0b207ab0bb21cf791179a8fd76cbad9d455
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e590c6d048e6cbb822c6bc49428f05e0dd6ead0f0f8752d563b5660adf422e61f1f8e2dc899490fedbdeb34bb36e3dc047906b7fe7eacf9a2711021c6f4a1d5f
|
7
|
+
data.tar.gz: 58ebb654582cbcfbfc776fb90a8ff2ce88a840642f1c1669c755478cb582f254f7f41a844762f7a228ed8a812e320aab073836dba190a15a3f0a42406a8740a0
|
data/lib/remap/base.rb
CHANGED
@@ -1,146 +1,300 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "active_support/configurable"
|
4
|
+
require "active_support/core_ext/object/with_options"
|
4
5
|
|
5
6
|
module Remap
|
7
|
+
# @example Select all elements
|
8
|
+
# class Mapper < Remap::Base
|
9
|
+
# define do
|
10
|
+
# map [all, :name]
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# Mapper.call([{ name: "John" }, { name: "Jane" }]) # => ["John", "Jane"]
|
15
|
+
#
|
16
|
+
# @example Given an option
|
17
|
+
# class Mapper < Remap::Base
|
18
|
+
# option :name
|
19
|
+
#
|
20
|
+
# define do
|
21
|
+
# set [:person, :name], to: option(:name)
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# Mapper.call({}, name: "John") # => { person: { name: "John" } }
|
26
|
+
#
|
27
|
+
# @example Given a value
|
28
|
+
# class Mapper < Remap::Base
|
29
|
+
# define do
|
30
|
+
# set [:api_key], to: value("ABC-123")
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# Mapper.call({}) # => { api_key: "ABC-123" }
|
35
|
+
#
|
36
|
+
# @example Maps ["A", "B", "C"] to ["A", "C"]
|
37
|
+
# class Mapper < Remap::Base
|
38
|
+
# define do
|
39
|
+
# each do
|
40
|
+
# map.if_not do
|
41
|
+
# value.include?("B")
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# Mapper.call(["A", "B", "C"]) # => ["A", "C"]
|
48
|
+
#
|
49
|
+
# @example Maps ["A", "B", "C"] to ["B"]
|
50
|
+
# class Mapper < Remap::Base
|
51
|
+
# define do
|
52
|
+
# each do
|
53
|
+
# map.if do
|
54
|
+
# value.include?("B")
|
55
|
+
# end
|
56
|
+
# end
|
57
|
+
# end
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# Mapper.call(["A", "B", "C"]) # => ["B"]
|
61
|
+
#
|
62
|
+
# @example Maps { a: { b: "A" } } to "A"
|
63
|
+
# class Mapper < Remap::Base
|
64
|
+
# define do
|
65
|
+
# map(:a, :b).enum do
|
66
|
+
# value "A", "B"
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# Mapper.call({ a: { b: "A" } }) # => "A"
|
72
|
+
# Mapper.call({ a: { b: "B" } }) # => "B"
|
73
|
+
#
|
74
|
+
# @example Map { people: [{ name: "John" }] } to { names: ["John"] }
|
75
|
+
# class Mapper < Remap::Base
|
76
|
+
# define do
|
77
|
+
# map :people, to: :names do
|
78
|
+
# each do
|
79
|
+
# map :name
|
80
|
+
# end
|
81
|
+
# end
|
82
|
+
# end
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# Mapper.call({ people: [{ name: "John" }] }) # => { names: ["John"] }
|
86
|
+
#
|
87
|
+
# @example Map "Hello" to "Hello!"
|
88
|
+
# class Mapper < Remap::Base
|
89
|
+
# define do
|
90
|
+
# map.adjust do
|
91
|
+
# "#{value}!"
|
92
|
+
# end
|
93
|
+
# end
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
# Mapper.call("Hello") # => "Hello!"
|
97
|
+
#
|
98
|
+
# @example Select the second element from an array
|
99
|
+
# class Mapper < Remap::Base
|
100
|
+
# define do
|
101
|
+
# map [at(1)]
|
102
|
+
# end
|
103
|
+
# end
|
104
|
+
#
|
105
|
+
# Mapper.call([1, 2, 3]) # => 2
|
6
106
|
class Base < Mapper
|
7
|
-
include
|
107
|
+
include ActiveSupport::Configurable
|
8
108
|
include Dry::Core::Constants
|
9
|
-
extend Dry::Monads[:result]
|
10
|
-
|
11
|
-
extend Dry::Configurable
|
12
|
-
extend Forwardable
|
13
|
-
|
14
109
|
using State::Extension
|
15
|
-
extend
|
110
|
+
extend Operation
|
16
111
|
|
17
|
-
|
18
|
-
|
112
|
+
with_options instance_accessor: true do |scope|
|
113
|
+
scope.config_accessor(:contract) { Dry::Schema.JSON {} }
|
114
|
+
scope.config_accessor(:constructor) { IDENTITY }
|
115
|
+
scope.config_accessor(:options) { EMPTY_ARRAY }
|
116
|
+
scope.config_accessor(:option) { EMPTY_HASH }
|
117
|
+
scope.config_accessor(:rules) { EMPTY_ARRAY }
|
118
|
+
scope.config_accessor(:context) { IDENTITY }
|
19
119
|
end
|
20
120
|
|
21
|
-
setting :constructor, default: IDENTITY
|
22
|
-
setting :options, default: EMPTY_ARRAY
|
23
|
-
setting :rules, default: EMPTY_ARRAY
|
24
|
-
setting :contract, default: CONTRACT
|
25
|
-
setting :context, default: IDENTITY
|
26
|
-
|
27
|
-
delegate [:config] => self
|
28
|
-
|
29
121
|
schema schema.strict(false)
|
30
122
|
|
31
|
-
#
|
32
|
-
#
|
123
|
+
# Defines a schema for the mapper
|
124
|
+
# If the schema fail, the mapper will fail
|
125
|
+
#
|
126
|
+
# @example Guard against missing values
|
127
|
+
# class MapperWithAge < Remap::Base
|
128
|
+
# contract do
|
129
|
+
# required(:age).filled(:integer)
|
130
|
+
# end
|
131
|
+
#
|
132
|
+
# define do
|
133
|
+
# map :age, to: [:person, :age]
|
134
|
+
# end
|
135
|
+
# end
|
136
|
+
#
|
137
|
+
# MapperWithAge.call({age: 50}) # => { person: { age: 50 } }
|
138
|
+
# MapperWithAge.call({age: '10'}) do |failure|
|
139
|
+
# # ...
|
140
|
+
# end
|
141
|
+
#
|
142
|
+
# @see https://dry-rb.org/gems/dry-schema/1.5/
|
143
|
+
#
|
144
|
+
# @return [void]
|
33
145
|
def self.contract(&context)
|
34
|
-
|
146
|
+
self.contract = Dry::Schema.JSON(&context)
|
35
147
|
end
|
36
148
|
|
37
|
-
#
|
149
|
+
# Defines a rule for the mapper
|
150
|
+
# If the rule fail, the mapper will fail
|
151
|
+
#
|
152
|
+
# @example Guard against values
|
153
|
+
# class MapperWithRule < Remap::Base
|
154
|
+
# contract do
|
155
|
+
# required(:age)
|
156
|
+
# end
|
157
|
+
#
|
158
|
+
# rule(:age) do
|
159
|
+
# unless value >= 18
|
160
|
+
# key.failure("must be at least 18 years old")
|
161
|
+
# end
|
162
|
+
# end
|
163
|
+
#
|
164
|
+
# define do
|
165
|
+
# map :age, to: [:person, :age]
|
166
|
+
# end
|
167
|
+
# end
|
168
|
+
#
|
169
|
+
# MapperWithRule.call({age: 50}) # => { person: { age: 50 } }
|
170
|
+
# MapperWithRule.call({age: 10}) do |failure|
|
171
|
+
# # ...
|
172
|
+
# end
|
173
|
+
#
|
174
|
+
# @see https://dry-rb.org/gems/dry-validation/1.6/rules/
|
175
|
+
#
|
176
|
+
# @return [void]
|
38
177
|
def self.rule(...)
|
39
|
-
|
178
|
+
self.rules = rules + [-> * { rule(...) }]
|
40
179
|
end
|
41
180
|
|
42
|
-
# Defines a
|
181
|
+
# Defines a required option for the mapper
|
182
|
+
#
|
183
|
+
# @example A mapper that takes an argument name
|
184
|
+
# class MapperWithOption < Remap::Base
|
185
|
+
# option :name
|
186
|
+
#
|
187
|
+
# define do
|
188
|
+
# set :name, to: option(:name)
|
189
|
+
# end
|
190
|
+
# end
|
43
191
|
#
|
44
|
-
#
|
45
|
-
#
|
192
|
+
# MapperWithOption.call({}, name: "John") # => { name: "John" }
|
193
|
+
#
|
194
|
+
# @param field [Symbol]
|
195
|
+
# @option type (Types::Any) [#call]
|
196
|
+
#
|
197
|
+
# @return [void]
|
46
198
|
def self.option(field, type: Types::Any)
|
47
199
|
attribute(field, type)
|
48
200
|
|
49
201
|
unless (key = schema.keys.find { _1.name == field })
|
50
|
-
raise ArgumentError, "Could not locate [#{field}] in [#{self}]"
|
202
|
+
raise ArgumentError, "[BUG] Could not locate [#{field}] in [#{self}]"
|
51
203
|
end
|
52
204
|
|
53
|
-
|
205
|
+
self.options = options + [-> * { option(field, type: key) }]
|
54
206
|
end
|
55
207
|
|
56
|
-
#
|
208
|
+
# Defines a mapper rules and possible constructor
|
57
209
|
#
|
58
|
-
# @
|
59
|
-
def self.inspect
|
60
|
-
"<#{self.class} #{rule}, #{self}>"
|
61
|
-
end
|
62
|
-
|
63
|
-
# Defines a mapper with a constructor used to wrap the output
|
210
|
+
# @param target (Nothing) [#call]
|
64
211
|
#
|
65
|
-
# @
|
212
|
+
# @option method (:new) [Symbol]
|
213
|
+
# @option strategy (:argument) [:argument, :keywords, :none]
|
66
214
|
#
|
67
|
-
# @example A mapper
|
68
|
-
# class Mapper < Remap
|
215
|
+
# @example A mapper, which mapps a value at [:a] to [:b]
|
216
|
+
# class Mapper < Remap::Base
|
69
217
|
# define do
|
70
218
|
# map :a, to: :b
|
71
219
|
# end
|
72
220
|
# end
|
73
221
|
#
|
74
|
-
# Mapper.call(a: 1) # => { b: 1 }
|
222
|
+
# Mapper.call({a: 1}) # => { b: 1 }
|
223
|
+
#
|
224
|
+
# @example A mapper with an output constructor
|
225
|
+
# class Person < Dry::Struct
|
226
|
+
# attribute :first_name, Dry::Types['strict.string']
|
227
|
+
# end
|
228
|
+
#
|
229
|
+
# class Mapper < Remap::Base
|
230
|
+
# define(Person) do
|
231
|
+
# map :name, to: :first_name
|
232
|
+
# end
|
233
|
+
# end
|
234
|
+
#
|
235
|
+
# Mapper.call({name: "John"}).first_name # => "John"
|
236
|
+
#
|
237
|
+
# @return [void]
|
75
238
|
def self.define(target = Nothing, method: :new, strategy: :argument, &context)
|
76
239
|
unless context
|
77
240
|
raise ArgumentError, "Missing block"
|
78
241
|
end
|
79
242
|
|
80
|
-
|
81
|
-
|
243
|
+
self.context = Compiler.call(&context)
|
244
|
+
self.constructor = Constructor.call(method: method, strategy: strategy, target: target)
|
82
245
|
rescue Dry::Struct::Error => e
|
83
246
|
raise ArgumentError, e.message
|
84
247
|
end
|
85
248
|
|
86
|
-
#
|
249
|
+
# Similar to {::call}, but takes a state
|
87
250
|
#
|
88
|
-
# @param
|
89
|
-
#
|
90
|
-
|
91
|
-
#
|
92
|
-
|
93
|
-
|
94
|
-
|
251
|
+
# @param state [State]
|
252
|
+
#
|
253
|
+
# @yield [Failure] if mapper fails
|
254
|
+
#
|
255
|
+
# @return [Result] if mapper succeeds
|
256
|
+
#
|
257
|
+
# @private
|
95
258
|
def self.call!(state, &error)
|
96
259
|
new(state.options).call(state._.set(mapper: self), &error)
|
97
|
-
rescue Dry::Struct::Error => e
|
98
|
-
raise ArgumentError, "Option missing to mapper [#{self}]: #{e}"
|
99
260
|
end
|
100
261
|
|
101
|
-
#
|
262
|
+
# Mappers state according to the mapper rules
|
102
263
|
#
|
103
|
-
# @
|
264
|
+
# @param state [State]
|
104
265
|
#
|
105
|
-
# @
|
266
|
+
# @yield [Failure] if mapper fails
|
267
|
+
#
|
268
|
+
# @return [State]
|
106
269
|
#
|
107
270
|
# @private
|
108
271
|
def call(state, &error)
|
109
272
|
unless error
|
110
|
-
raise ArgumentError, "
|
273
|
+
raise ArgumentError, "Base#call(state, &error) requires block"
|
111
274
|
end
|
112
275
|
|
113
276
|
state.tap do |input|
|
114
|
-
|
277
|
+
validation.call(input, state.options).tap do |result|
|
115
278
|
unless result.success?
|
116
279
|
return error[state.failure(result.errors.to_h)]
|
117
280
|
end
|
118
281
|
end
|
119
282
|
end
|
120
283
|
|
121
|
-
|
284
|
+
notice = catch :fatal do
|
285
|
+
return context.call(state) do |failure|
|
286
|
+
return error[failure]
|
287
|
+
end.then(&constructor)
|
288
|
+
end
|
289
|
+
|
290
|
+
error[state.failure(notice)]
|
122
291
|
end
|
123
292
|
|
124
293
|
private
|
125
294
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
config.rules.each do |rule|
|
131
|
-
klass.class_eval(&rule)
|
132
|
-
end
|
133
|
-
|
134
|
-
config.options.each do |option|
|
135
|
-
klass.class_eval(&option)
|
136
|
-
end
|
137
|
-
|
138
|
-
schema(config.contract)
|
139
|
-
end.new(**attributes)
|
140
|
-
end
|
141
|
-
|
142
|
-
def config
|
143
|
-
self.class.config
|
295
|
+
# @return [Contract]
|
296
|
+
def validation
|
297
|
+
Contract.call(attributes: attributes, contract: contract, options: options, rules: rules)
|
144
298
|
end
|
145
299
|
end
|
146
300
|
end
|
data/lib/remap/compiler.rb
CHANGED
@@ -1,17 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Remap
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
param :rules, default: -> { EMPTY_ARRAY.dup }
|
4
|
+
# Constructs a {Rule} from the block passed to {Remap::Base.define}
|
5
|
+
class Compiler < Proxy
|
6
|
+
# @return [Array<Rule>]
|
7
|
+
param :rules, type: Types.Array(Rule)
|
10
8
|
|
11
9
|
# @return [Rule]
|
12
|
-
delegate call:
|
10
|
+
delegate :call, to: Compiler
|
13
11
|
|
14
|
-
# Constructs a rule tree given
|
12
|
+
# Constructs a rule tree given block
|
13
|
+
#
|
14
|
+
# @example Compiles two rules
|
15
|
+
# rule = Remap::Compiler.call do
|
16
|
+
# get :name
|
17
|
+
# get :age
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# state = Remap::State.call({
|
21
|
+
# name: "John",
|
22
|
+
# age: 50
|
23
|
+
# })
|
24
|
+
#
|
25
|
+
# error = -> failure { raise failure.exception }
|
26
|
+
#
|
27
|
+
# rule.call(state, &error).fetch(:value) # => { name: "John", age: 50 }
|
15
28
|
#
|
16
29
|
# @return [Rule]
|
17
30
|
def self.call(&block)
|
@@ -19,57 +32,109 @@ module Remap
|
|
19
32
|
return Rule::Void.new
|
20
33
|
end
|
21
34
|
|
22
|
-
new.tap
|
35
|
+
new([]).tap do |compiler|
|
36
|
+
compiler.instance_exec(&block)
|
37
|
+
end.rule
|
23
38
|
end
|
24
39
|
|
25
|
-
# Maps
|
40
|
+
# Maps input path [input] to output path [to]
|
26
41
|
#
|
27
42
|
# @param path ([]) [Array<Segment>, Segment]
|
28
43
|
# @param to ([]) [Array<Symbol>, Symbol]
|
29
44
|
#
|
30
|
-
# @return [Rule::Map]
|
31
|
-
def map(*path, to: EMPTY_ARRAY, &block)
|
32
|
-
add Rule::Map.
|
45
|
+
# @return [Rule::Map::Required]
|
46
|
+
def map(*path, to: EMPTY_ARRAY, backtrace: Kernel.caller, &block)
|
47
|
+
add Rule::Map::Required.call(
|
33
48
|
path: {
|
34
|
-
|
35
|
-
|
49
|
+
output: [to].flatten,
|
50
|
+
input: path.flatten
|
36
51
|
},
|
37
|
-
|
38
|
-
|
52
|
+
backtrace: backtrace,
|
53
|
+
rule: call(&block))
|
54
|
+
end
|
55
|
+
|
56
|
+
# Optional version of {#map}
|
57
|
+
#
|
58
|
+
# @see #map
|
59
|
+
#
|
60
|
+
# @return [Rule::Map::Optional]
|
61
|
+
def map?(*path, to: EMPTY_ARRAY, backtrace: Kernel.caller, &block)
|
62
|
+
add Rule::Map::Optional.call(
|
63
|
+
path: {
|
64
|
+
output: [to].flatten,
|
65
|
+
input: path.flatten
|
66
|
+
},
|
67
|
+
backtrace: backtrace,
|
68
|
+
rule: call(&block))
|
69
|
+
end
|
70
|
+
|
71
|
+
# Select a path and uses the same path as output
|
72
|
+
#
|
73
|
+
# @param path ([]) [Array<Segment>, Segment]
|
74
|
+
#
|
75
|
+
# @return [Rule::Map::Required]
|
76
|
+
def get(*path, backtrace: Kernel.caller, &block)
|
77
|
+
map(path, to: path, backtrace: backtrace, &block)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Optional version of {#get}
|
81
|
+
#
|
82
|
+
# @see #get
|
83
|
+
#
|
84
|
+
# @return [Rule::Map::Optional]
|
85
|
+
def get?(*path, backtrace: Kernel.caller, &block)
|
86
|
+
map?(path, to: path, backtrace: backtrace, &block)
|
39
87
|
end
|
40
88
|
|
41
|
-
# Maps using
|
89
|
+
# Maps using mapper
|
42
90
|
#
|
43
91
|
# @param mapper [Remap]
|
44
92
|
#
|
45
93
|
# @return [Rule::Embed]
|
46
|
-
def embed(mapper)
|
94
|
+
def embed(mapper, &block)
|
95
|
+
if block
|
96
|
+
raise ArgumentError, "#embed does not take a block"
|
97
|
+
end
|
98
|
+
|
47
99
|
add Rule::Embed.new(mapper: mapper)
|
48
100
|
rescue Dry::Struct::Error
|
49
101
|
raise ArgumentError, "Embeded mapper must be [Remap::Mapper], got [#{mapper}]"
|
50
102
|
end
|
51
103
|
|
52
|
-
# @param
|
104
|
+
# @param path ([]) [Symbol, Array<Symbol>]
|
53
105
|
# @option to [Remap::Static]
|
54
106
|
#
|
55
107
|
# @return [Rule::Set]
|
56
108
|
# @raise [ArgumentError]
|
57
109
|
# if no path given
|
58
110
|
# if path is not a Symbol or Array<Symbol>
|
59
|
-
def set(*path, to
|
60
|
-
|
111
|
+
def set(*path, to:, &block)
|
112
|
+
if block
|
113
|
+
raise ArgumentError, "#set does not take a block"
|
114
|
+
end
|
115
|
+
|
116
|
+
add Rule::Set.new(path: path.flatten, value: to)
|
61
117
|
rescue Dry::Struct::Error => e
|
62
118
|
raise ArgumentError, e.message
|
63
119
|
end
|
64
120
|
|
65
|
-
# Maps to
|
121
|
+
# Maps to path from map with block in between
|
66
122
|
#
|
67
123
|
# @param path [Array<Symbol>, Symbol]
|
68
124
|
# @param map [Array<Segment>, Segment]
|
69
125
|
#
|
70
126
|
# @return [Rule::Map]
|
71
|
-
def to(*path, map: EMPTY_ARRAY, &block)
|
72
|
-
map(*map, to: path, &block)
|
127
|
+
def to(*path, map: EMPTY_ARRAY, backtrace: Kernel.caller, &block)
|
128
|
+
map(*map, to: path, backtrace: backtrace, &block)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Optional version of {#to}
|
132
|
+
#
|
133
|
+
# @see #to
|
134
|
+
#
|
135
|
+
# @return [Rule::Map::Optional]
|
136
|
+
def to?(*path, map: EMPTY_ARRAY, &block)
|
137
|
+
map?(*map, to: path, &block)
|
73
138
|
end
|
74
139
|
|
75
140
|
# Iterates over the input value, passes each value
|
@@ -79,13 +144,13 @@ module Remap
|
|
79
144
|
# @raise [ArgumentError] if no block given
|
80
145
|
def each(&block)
|
81
146
|
unless block
|
82
|
-
raise ArgumentError, "
|
147
|
+
raise ArgumentError, "#each requires a block"
|
83
148
|
end
|
84
149
|
|
85
150
|
add Rule::Each.new(rule: call(&block))
|
86
151
|
end
|
87
152
|
|
88
|
-
# Wraps output in
|
153
|
+
# Wraps output in type
|
89
154
|
#
|
90
155
|
# @param type [:array]
|
91
156
|
#
|
@@ -95,7 +160,7 @@ module Remap
|
|
95
160
|
# @raise [ArgumentError] if type is not :array
|
96
161
|
def wrap(type, &block)
|
97
162
|
unless block
|
98
|
-
raise ArgumentError, "
|
163
|
+
raise ArgumentError, "#wrap requires a block"
|
99
164
|
end
|
100
165
|
|
101
166
|
add Rule::Wrap.new(type: type, rule: call(&block))
|
@@ -106,7 +171,11 @@ module Remap
|
|
106
171
|
# Selects all elements
|
107
172
|
#
|
108
173
|
# @return [Rule::Path::Segment::Quantifier::All]
|
109
|
-
def all
|
174
|
+
def all(&block)
|
175
|
+
if block
|
176
|
+
raise ArgumentError, "all selector does not take a block"
|
177
|
+
end
|
178
|
+
|
110
179
|
Selector::All.new(EMPTY_HASH)
|
111
180
|
end
|
112
181
|
|
@@ -115,7 +184,11 @@ module Remap
|
|
115
184
|
# @param value [Any]
|
116
185
|
#
|
117
186
|
# @return [Rule::Static::Fixed]
|
118
|
-
def value(value)
|
187
|
+
def value(value, &block)
|
188
|
+
if block
|
189
|
+
raise ArgumentError, "option selector does not take a block"
|
190
|
+
end
|
191
|
+
|
119
192
|
Static::Fixed.new(value: value)
|
120
193
|
end
|
121
194
|
|
@@ -124,26 +197,39 @@ module Remap
|
|
124
197
|
# @param id [Symbol]
|
125
198
|
#
|
126
199
|
# @return [Rule::Static::Option]
|
127
|
-
def option(id)
|
128
|
-
|
200
|
+
def option(id, backtrace: Kernel.caller, &block)
|
201
|
+
if block
|
202
|
+
raise ArgumentError, "option selector does not take a block"
|
203
|
+
end
|
204
|
+
|
205
|
+
Static::Option.new(name: id, backtrace: backtrace)
|
129
206
|
end
|
130
207
|
|
131
|
-
# Selects
|
208
|
+
# Selects index element in input
|
132
209
|
#
|
133
210
|
# @param index [Integer]
|
134
211
|
#
|
135
212
|
# @return [Path::Segment::Key]
|
136
213
|
# @raise [ArgumentError] if index is not an Integer
|
137
|
-
def at(index)
|
214
|
+
def at(index, &block)
|
215
|
+
if block
|
216
|
+
raise ArgumentError, "first selector does not take a block"
|
217
|
+
end
|
218
|
+
|
138
219
|
Selector::Index.new(index: index)
|
139
220
|
rescue Dry::Struct::Error
|
140
|
-
raise ArgumentError,
|
221
|
+
raise ArgumentError,
|
222
|
+
"Selector at(index) requires an integer argument, got [#{index}] (#{index.class})"
|
141
223
|
end
|
142
224
|
|
143
225
|
# Selects first element in input
|
144
226
|
#
|
145
227
|
# @return [Path::Segment::Key]]
|
146
|
-
def first
|
228
|
+
def first(&block)
|
229
|
+
if block
|
230
|
+
raise ArgumentError, "first selector does not take a block"
|
231
|
+
end
|
232
|
+
|
147
233
|
at(0)
|
148
234
|
end
|
149
235
|
alias any first
|
@@ -151,7 +237,11 @@ module Remap
|
|
151
237
|
# Selects last element in input
|
152
238
|
#
|
153
239
|
# @return [Path::Segment::Key]
|
154
|
-
def last
|
240
|
+
def last(&block)
|
241
|
+
if block
|
242
|
+
raise ArgumentError, "last selector does not take a block"
|
243
|
+
end
|
244
|
+
|
155
245
|
at(-1)
|
156
246
|
end
|
157
247
|
|