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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/lib/remap/base.rb +229 -75
  3. data/lib/remap/compiler.rb +127 -37
  4. data/lib/remap/constructor/argument.rb +20 -6
  5. data/lib/remap/constructor/keyword.rb +20 -4
  6. data/lib/remap/constructor/none.rb +3 -4
  7. data/lib/remap/constructor.rb +12 -5
  8. data/lib/remap/contract.rb +27 -0
  9. data/lib/remap/extensions/enumerable.rb +48 -0
  10. data/lib/remap/extensions/hash.rb +13 -0
  11. data/lib/remap/extensions/object.rb +37 -0
  12. data/lib/remap/failure.rb +25 -15
  13. data/lib/remap/iteration/array.rb +20 -11
  14. data/lib/remap/iteration/hash.rb +21 -13
  15. data/lib/remap/iteration/other.rb +7 -7
  16. data/lib/remap/iteration.rb +8 -2
  17. data/lib/remap/mapper/and.rb +29 -7
  18. data/lib/remap/mapper/binary.rb +3 -6
  19. data/lib/remap/mapper/or.rb +29 -6
  20. data/lib/remap/mapper/support/operations.rb +40 -0
  21. data/lib/remap/mapper/xor.rb +29 -7
  22. data/lib/remap/mapper.rb +1 -48
  23. data/lib/remap/notice/traced.rb +19 -0
  24. data/lib/remap/notice/untraced.rb +11 -0
  25. data/lib/remap/notice.rb +34 -0
  26. data/lib/remap/operation.rb +26 -13
  27. data/lib/remap/path/input.rb +37 -0
  28. data/lib/remap/path/output.rb +26 -0
  29. data/lib/remap/path.rb +22 -0
  30. data/lib/remap/path_error.rb +13 -0
  31. data/lib/remap/proxy.rb +18 -0
  32. data/lib/remap/rule/each.rb +25 -24
  33. data/lib/remap/rule/embed.rb +33 -28
  34. data/lib/remap/rule/map/optional.rb +42 -0
  35. data/lib/remap/rule/map/required.rb +35 -0
  36. data/lib/remap/rule/map.rb +176 -55
  37. data/lib/remap/rule/set.rb +23 -33
  38. data/lib/remap/rule/support/collection/empty.rb +7 -7
  39. data/lib/remap/rule/support/collection/filled.rb +21 -8
  40. data/lib/remap/rule/support/collection.rb +11 -3
  41. data/lib/remap/rule/support/enum.rb +44 -21
  42. data/lib/remap/rule/void.rb +17 -18
  43. data/lib/remap/rule/wrap.rb +25 -17
  44. data/lib/remap/rule.rb +8 -1
  45. data/lib/remap/selector/all.rb +29 -7
  46. data/lib/remap/selector/index.rb +24 -16
  47. data/lib/remap/selector/key.rb +31 -16
  48. data/lib/remap/selector.rb +17 -0
  49. data/lib/remap/state/extension.rb +182 -208
  50. data/lib/remap/state/schema.rb +1 -1
  51. data/lib/remap/state.rb +30 -4
  52. data/lib/remap/static/fixed.rb +14 -3
  53. data/lib/remap/static/option.rb +21 -6
  54. data/lib/remap/static.rb +13 -0
  55. data/lib/remap/struct.rb +1 -0
  56. data/lib/remap/types.rb +13 -28
  57. data/lib/remap.rb +15 -19
  58. metadata +91 -89
  59. data/lib/remap/result.rb +0 -11
  60. data/lib/remap/rule/support/path.rb +0 -45
  61. data/lib/remap/state/types.rb +0 -11
  62. data/lib/remap/success.rb +0 -29
  63. data/lib/remap/version.rb +0 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: feae800c3a05859a5cf0cefd1c080f3afa92109856b504aba89f81975f21dfc4
4
- data.tar.gz: 54c5b5b7dd28b67b67aed40019867d6293141edca668ea8d4de9b5b670dedaa4
3
+ metadata.gz: b3b3344d7c0ab3825ba1bb0a80c5ddbd7a00d57dbf762b0e26cd3cc6216e0644
4
+ data.tar.gz: 30db4e775e16a7923c46d4f1ca41b0b207ab0bb21cf791179a8fd76cbad9d455
5
5
  SHA512:
6
- metadata.gz: ecabf17439228869a9e14a2686cc38d92cd495a529efa2fc6114ffb10abf40837fe3fccf858123134f10c6a54f314e2464d8d309f854183b69c2875733cd48f4
7
- data.tar.gz: cc27691d7b1710d9642368559a25b7da4009c5a4346b5b44aa6166b81a8ec3ea053276ebd20b5d976548bf65290e9d2b4395395c2932edaaea8cea08bbdd285f
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 "dry/monads/all"
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 Dry::Core::Memoizable
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 State
110
+ extend Operation
16
111
 
17
- CONTRACT = Dry::Schema.JSON do
18
- # NOP
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
- # Holds the current context
32
- # @private
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
- config.contract = Dry::Schema.JSON(&context)
146
+ self.contract = Dry::Schema.JSON(&context)
35
147
  end
36
148
 
37
- # @see Dry::Validation::Contract.rule
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
- config.rules << ->(*) { rule(...) }
178
+ self.rules = rules + [-> * { rule(...) }]
40
179
  end
41
180
 
42
- # Defines a a constructor argument for the mapper
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
- # @param name [Symbol]
45
- # @param type [#call]
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
- config.options << ->(*) { option(field, type: key) }
205
+ self.options = options + [-> * { option(field, type: key) }]
54
206
  end
55
207
 
56
- # Pretty print the mapper
208
+ # Defines a mapper rules and possible constructor
57
209
  #
58
- # @return [String]
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
- # @param constructor [#call]
212
+ # @option method (:new) [Symbol]
213
+ # @option strategy (:argument) [:argument, :keywords, :none]
66
214
  #
67
- # @example A mapper from path :a to path :b
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
- config.context = Compiler.call(&context)
81
- config.constructor = Constructor.call(method: method, strategy: strategy, target: target)
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
- # Creates a new mapper
249
+ # Similar to {::call}, but takes a state
87
250
  #
88
- # @param input [Any]
89
- # @param params [Hash]
90
-
91
- # @return [Context]
92
-
93
- extend Operation
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
- # Creates a mapper tree using {#context} and uses {#state} as argument
262
+ # Mappers state according to the mapper rules
102
263
  #
103
- # @return [State]
264
+ # @param state [State]
104
265
  #
105
- # @see .call!
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, "Missing block"
273
+ raise ArgumentError, "Base#call(state, &error) requires block"
111
274
  end
112
275
 
113
276
  state.tap do |input|
114
- contract.call(input, state.options).tap do |result|
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
- state.then(&config.context).then(&config.constructor)
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
- def contract(scope: self)
127
- Class.new(Dry::Validation::Contract) do |klass|
128
- config = scope.class.config
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
@@ -1,17 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Remap
4
- class Compiler
5
- include Dry::Core::Constants
6
- extend Dry::Initializer
7
- extend Forwardable
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: self
10
+ delegate :call, to: Compiler
13
11
 
14
- # Constructs a rule tree given {block}
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 { _1.instance_eval(&block) }.rule
35
+ new([]).tap do |compiler|
36
+ compiler.instance_exec(&block)
37
+ end.rule
23
38
  end
24
39
 
25
- # Maps {path} to {to} with {block} inbetween
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.new(
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
- map: path.flatten,
35
- to: [to].flatten
49
+ output: [to].flatten,
50
+ input: path.flatten
36
51
  },
37
- rule: call(&block)
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 {mapper}
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 *path ([]) [Symbol, Array<Symbol>]
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
- add Rule::Set.new(path: { to: path.flatten, map: EMPTY_ARRAY }, value: to)
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 {path} from {map} with {block} inbetween
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, "no block given"
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 {type}
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, "no block given"
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
- Static::Option.new(name: id)
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 {index} element in input
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, "Selector at(index) requires an integer argument, got [#{index}] (#{index.class})"
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