marameters 0.6.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/README.adoc +189 -21
- data/lib/marameters/builder.rb +0 -1
- data/lib/marameters/categorizer.rb +57 -0
- data/lib/marameters/defaulter.rb +1 -1
- data/lib/marameters/probe.rb +23 -22
- data/lib/marameters/signature.rb +1 -1
- data/lib/marameters/splat.rb +13 -0
- data/lib/marameters.rb +7 -0
- data/marameters.gemspec +1 -1
- data.tar.gz.sig +0 -0
- metadata +5 -3
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b53c9a80f1845fabb688e43162c18519192414425ef6a5b2a4c16e2575d29f99
|
4
|
+
data.tar.gz: e49e58811bb7e32ec2c8bee2691b6d230be4dc0f18503b86f5cebdfa812033c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c4763d0f9492fce3673c1803884ae5cc6d49e64957f35649c7b10045a9146fbabf513a64d21346fd3642df9e481ba4f12cb59fcc361bae5fae0eb0cb3b7cc7c4
|
7
|
+
data.tar.gz: 4c6ffd0555c7d563759d4c25eb880f44ae8d4158d0399520bcb1301d64709c67a88b7aeedea69b6485c9598430b94d2a7d58529001435e6721fe704de742a023
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/README.adoc
CHANGED
@@ -2,30 +2,29 @@
|
|
2
2
|
:toclevels: 5
|
3
3
|
:figure-caption!:
|
4
4
|
|
5
|
+
:amazing_print_link: link:https://github.com/amazing-print/amazing_print[Amazing Print]
|
6
|
+
:article_link: link:https://www.alchemists.io/articles/ruby_method_parameters_and_arguments[method parameters and arguments]
|
7
|
+
|
5
8
|
= Marameters
|
6
9
|
|
7
|
-
Marameters is a portmanteau (i.e. `[m]ethod + p[arameters] = marameters`) which is designed to
|
8
|
-
provide additional insight and diagnostics to method parameters. For context, the difference between
|
9
|
-
a method's parameters and arguments is:
|
10
|
+
Marameters is a portmanteau (i.e. `[m]ethod + p[arameters] = marameters`) which is designed to provide additional insight and diagnostics for method parameters. For context, the difference between a method's parameters and arguments is:
|
10
11
|
|
11
|
-
* *Parameters*: Represents the _expected_ values to be passed to a method when
|
12
|
-
|
13
|
-
* *Arguments*: Represents the _actual_ values passed to the method when messaged.
|
14
|
-
Example: `demo 1, two: 2`.
|
12
|
+
* *Parameters*: Represents the _expected_ values to be passed to a method when messaged as defined when the method is implemented. Example: `def demo one, two: nil`.
|
13
|
+
* *Arguments*: Represents the _actual_ values passed to the method when messaged. Example: `demo 1, two: 2`.
|
15
14
|
|
16
|
-
This gem will help you debug methods or
|
17
|
-
metaprogramming
|
18
|
-
gem, when architecting more sophisticated applications.
|
15
|
+
This gem will help you debug methods or aid your workflow when
|
16
|
+
metaprogramming -- as used in the link:https://www.alchemists.io/projects/infusible[Infusible] gem -- when architecting more sophisticated applications.
|
19
17
|
|
20
18
|
toc::[]
|
21
19
|
|
22
20
|
== Features
|
23
21
|
|
24
|
-
* Provides specialized objects for keyword, positional, and
|
22
|
+
* Provides specialized objects for keyword, positional, and block parameters.
|
25
23
|
|
26
24
|
== Requirements
|
27
25
|
|
28
26
|
. link:https://www.ruby-lang.org[Ruby].
|
27
|
+
. A solid understanding of {article_link}.
|
29
28
|
|
30
29
|
== Setup
|
31
30
|
|
@@ -45,16 +44,42 @@ gem "marameters"
|
|
45
44
|
|
46
45
|
== Usage
|
47
46
|
|
48
|
-
|
47
|
+
At a high level, you can use `Marameters` as a single object interface for accessing all capabilities provided by this gem. Here's an overview:
|
48
|
+
|
49
|
+
[source,ruby]
|
50
|
+
----
|
51
|
+
# Setup
|
52
|
+
def demo(one, two = 2, three: 3) = puts "One: #{one}, Two: #{two}, Three: #{three}"
|
53
|
+
|
54
|
+
parameters = method(:demo).parameters
|
55
|
+
arguments = %w[one two]
|
56
|
+
|
57
|
+
# Marameters::Categorizer wrapper
|
58
|
+
|
59
|
+
Marameters.categorize parameters, arguments
|
60
|
+
# #<struct Marameters::Splat positionals=["one", "two"], keywords={}, block=nil>
|
49
61
|
|
50
|
-
|
51
|
-
* *Marameters::Signature*: Allows you to dynamically build a method signature from raw parameters.
|
62
|
+
# Marameters::Probe wrapper
|
52
63
|
|
53
|
-
|
64
|
+
Marameters.of self, :demo # []
|
65
|
+
|
66
|
+
probe = Marameters.probe parameters
|
67
|
+
probe.to_a # [[:req, :one], [:opt, :two], [:key, :three]]
|
68
|
+
probe.positionals # [:one, :two]
|
69
|
+
probe.keywords # [:three]
|
70
|
+
probe.block # nil
|
71
|
+
|
72
|
+
# Marameters::Signature wrapper
|
73
|
+
|
74
|
+
Marameters.signature({req: :one, opt: [:two, 2], key: [:three, 3]}).to_s
|
75
|
+
# one, two = 2, three: 3
|
76
|
+
----
|
77
|
+
|
78
|
+
Read on to learn more about the details on how each of these methods work and the objects they wrap.
|
54
79
|
|
55
80
|
=== Probe
|
56
81
|
|
57
|
-
|
82
|
+
The probe allows you to analyze a method's parameters. To understand how, consider the following demonstration class:
|
58
83
|
|
59
84
|
[source,ruby]
|
60
85
|
----
|
@@ -98,7 +123,6 @@ probe.positionals? # true
|
|
98
123
|
probe.splats # [:three, :six]
|
99
124
|
probe.splats? # true
|
100
125
|
probe.to_a # [[:req, :one], [:opt, :two], [:rest, :three], [:keyreq, :four], [:key, :five], [:keyrest, :six], [:block, :seven]]
|
101
|
-
probe.to_h # {:req=>:one, :opt=>:two, :rest=>:three, :keyreq=>:four, :key=>:five, :keyrest=>:six, :block=>:seven}
|
102
126
|
----
|
103
127
|
|
104
128
|
In contrast the above, we can also probe the `#none` method which has no parameters for a completely
|
@@ -125,14 +149,158 @@ probe.positionals? # false
|
|
125
149
|
probe.splats # []
|
126
150
|
probe.splats? # false
|
127
151
|
probe.to_a # []
|
128
|
-
probe.to_h # {}
|
129
152
|
----
|
130
153
|
|
154
|
+
=== Categorizer
|
155
|
+
|
156
|
+
The categorizer allows you to dynamically build positional, keyword, and block arguments for message passing. This is most valuable when you know the object and method while needing to align the arguments in the right order. Here's a demonstration where {amazing_print_link} (i.e. `ap`) is used to format the output:
|
157
|
+
|
158
|
+
[source,ruby]
|
159
|
+
----
|
160
|
+
function = proc { "test" }
|
161
|
+
|
162
|
+
module Demo
|
163
|
+
def self.test one, two = nil, *three, four:, five: nil, **six, &seven
|
164
|
+
puts "The .#{__method__} method received the following arguments:\n"
|
165
|
+
|
166
|
+
[one, two, three, four, five, six, seven].each.with_index 1 do |argument, index|
|
167
|
+
puts "#{index}. #{argument.inspect}"
|
168
|
+
end
|
169
|
+
|
170
|
+
puts
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
module Inspector
|
175
|
+
def self.call arguments
|
176
|
+
Marameters::Categorizer.new(Demo.method(:test).parameters)
|
177
|
+
.call(arguments).then do |splat|
|
178
|
+
ap splat
|
179
|
+
puts
|
180
|
+
Demo.test(*splat.positionals, **splat.keywords, &splat.block)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
Inspector.call [1, nil, nil, {four: 4}]
|
186
|
+
|
187
|
+
# #<Struct:Marameters::Splat:0x00021930
|
188
|
+
# block = nil,
|
189
|
+
# keywords = {
|
190
|
+
# :four => 4
|
191
|
+
# },
|
192
|
+
# positionals = [
|
193
|
+
# 1,
|
194
|
+
# nil
|
195
|
+
# ]
|
196
|
+
# >
|
197
|
+
#
|
198
|
+
# The .test method received the following arguments:
|
199
|
+
# 1. 1
|
200
|
+
# 2. nil
|
201
|
+
# 3. []
|
202
|
+
# 4. 4
|
203
|
+
# 5. nil
|
204
|
+
# 6. {}
|
205
|
+
# 7. nil
|
206
|
+
----
|
207
|
+
|
208
|
+
When we step through the above implementation and output, we see the following unfold:
|
209
|
+
|
210
|
+
. The `Demo` module allows us to define a maximum set of parameters and then print the arguments received for inspection purposes.
|
211
|
+
. The `Inspector` module provides a wrapper around the `Categorizer` so we can conveniently pass in different arguments for experimentation purposes.
|
212
|
+
. We pass in our arguments to `Inspector.call` where `nil` is used for optional arguments and hashes for keyword arguments.
|
213
|
+
. Once inside `Inspector.call`, the `Categorizer` is initialized with the `Demo.test` method parameters.
|
214
|
+
. Then the `splat` (i.e. Struct) is printed out so you can see the categorized positional, keyword, and block arguments.
|
215
|
+
. Finally, `Demo.test` method is called with the splatted arguments.
|
216
|
+
|
217
|
+
The above example satisfies the minimum required arguments but if we pass in the maximum arguments -- loosely speaking -- we see more detail:
|
218
|
+
|
219
|
+
[source,ruby]
|
220
|
+
----
|
221
|
+
Inspector.call [1, 2, [98, 99], {four: 4}, {five: 5}, {twenty: 20, thirty: 30}, function]
|
222
|
+
|
223
|
+
# Output
|
224
|
+
|
225
|
+
# #<Struct:Marameters::Splat:0x00029cc0
|
226
|
+
# block = #<Proc:0x000000010a88cec0 (irb):1>,
|
227
|
+
# keywords = {
|
228
|
+
# :four => 4,
|
229
|
+
# :five => 5,
|
230
|
+
# :twenty => 20,
|
231
|
+
# :thirty => 30
|
232
|
+
# },
|
233
|
+
# positionals = [
|
234
|
+
# 1,
|
235
|
+
# 2,
|
236
|
+
# 98,
|
237
|
+
# 99
|
238
|
+
# ]
|
239
|
+
# >
|
240
|
+
#
|
241
|
+
# The .test method received the following arguments:
|
242
|
+
# 1. 1
|
243
|
+
# 2. 2
|
244
|
+
# 3. [98, 99]
|
245
|
+
# 4. 4
|
246
|
+
# 5. 5
|
247
|
+
# 6. {:twenty=>20, :thirty=>30}
|
248
|
+
# 7. #<Proc:0x000000010a88cec0 (irb):1>
|
249
|
+
----
|
250
|
+
|
251
|
+
Once again, it is important to keep in mind that the argument positions _must_ align with the parameter positions since the parameters are an array of elements too. For illustration purposes -- using the above example -- we can compare the parameters to the arguments as follows:
|
252
|
+
|
253
|
+
[source,ruby]
|
254
|
+
----
|
255
|
+
parameters = Demo.method(:test).parameters
|
256
|
+
arguments = [1, 2, [98, 99], {four: 4}, {five: 5}, {twenty: 20, thirty: 30}, function]
|
257
|
+
----
|
258
|
+
|
259
|
+
With {amazing_print_link}, we can print out this information:
|
260
|
+
|
261
|
+
[source,ruby]
|
262
|
+
----
|
263
|
+
ap parameters
|
264
|
+
ap arguments
|
265
|
+
----
|
266
|
+
|
267
|
+
...which can be further illustrated by this comparison table:
|
268
|
+
|
269
|
+
[options="header"]
|
270
|
+
|===
|
271
|
+
| Parameter | Argument
|
272
|
+
| `%i[reg one]` | `1`
|
273
|
+
| `%i[opt two]` | `2`
|
274
|
+
| `%i[rest three]` | `[98, 99]`
|
275
|
+
| `%i[keyreq four]` | `{four: 4}`
|
276
|
+
| `%i[key five]` | `{five: 5}`
|
277
|
+
| `%i[keyrest six]` | `{twenty: 20, thirty: 30}`
|
278
|
+
| `%i[block seven]` | `#<Proc:0x0000000108edc778>`
|
279
|
+
|===
|
280
|
+
|
281
|
+
This also means that:
|
282
|
+
|
283
|
+
* All positions must be filled if you want to supply arguments beyond the first couple of positions because everything is positional due to the nature of how link:https://rubyapi.org/o/method#method-i-parameters[Method#parameters] works. Use `nil` to fill an optional argument when you don't need it.
|
284
|
+
* The `:rest` (single splat) argument must be an array or `nil` if not present because even though it is _optional_, it is still _positional_.
|
285
|
+
* The `:keyrest` (double splat) argument -- much like the `:rest` argument -- must be a hash or `nil` if not present.
|
286
|
+
|
287
|
+
Lastly, in all of the above examples, only an array of arguments has been used but you can pass in a single argument too (i.e. non-array). This is handy for method signatures which have only a single parameter or only use splats. Having to remember to wrap your argument in an array each time can get tedious so when _only_ a single argument is supplied, the categorizer will automatically cast the argument as an array. A good example of this use case is when using structs. Example:
|
288
|
+
|
289
|
+
[source,ruby]
|
290
|
+
----
|
291
|
+
url = Struct.new :label, :url, keyword_init: true
|
292
|
+
|
293
|
+
Marameters.categorize(url.method(:new).parameters, {label: "Eaxmple", url: "https://example.com"})
|
294
|
+
.then { |splat| url.new(*splat.positionals, **splat.keywords) }
|
295
|
+
|
296
|
+
# Yields: #<struct label="Eaxmple", url="https://example.com">
|
297
|
+
----
|
298
|
+
|
299
|
+
For further details, please refer back to my {article_link} article mentioned in the _Requirements_ section.
|
300
|
+
|
131
301
|
=== Signature
|
132
302
|
|
133
|
-
The signature class is the
|
134
|
-
into a method signature. This is useful when dynamically building method signatures or using the
|
135
|
-
same signature when metaprogramming multiple methods.
|
303
|
+
The signature class is the inverse of the probe class in that you want to feed it parameters for turning into a method signature. This is useful when dynamically building method signatures or using the same signature when metaprogramming multiple methods.
|
136
304
|
|
137
305
|
The following demonstrates how you might construct a method signature with all possible parameters:
|
138
306
|
|
data/lib/marameters/builder.rb
CHANGED
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "refinements/structs"
|
4
|
+
|
5
|
+
module Marameters
|
6
|
+
# Builds the primary argument categories based on method parameters and arguments.
|
7
|
+
class Categorizer
|
8
|
+
using Refinements::Structs
|
9
|
+
|
10
|
+
def initialize parameters, model: Splat
|
11
|
+
@parameters = parameters
|
12
|
+
@model = model
|
13
|
+
end
|
14
|
+
|
15
|
+
def call arguments
|
16
|
+
@record = model.new
|
17
|
+
map arguments.is_a?(Array) ? arguments : [arguments]
|
18
|
+
record
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :parameters, :model, :record
|
24
|
+
|
25
|
+
def map arguments
|
26
|
+
return record if arguments.empty?
|
27
|
+
|
28
|
+
size = arguments.size
|
29
|
+
|
30
|
+
parameters.each.with_index do |pair, index|
|
31
|
+
filter pair, arguments[index] if index < size
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def filter pair, value
|
36
|
+
case pair
|
37
|
+
in [:rest] | [:rest, :*] then splat_positional value
|
38
|
+
in [:keyrest] | [:keyrest, :**] then record.keywords = Hash value
|
39
|
+
in [:req, *] | [:opt, *] then record.positionals.append value
|
40
|
+
in [:rest, *] then record.positionals.append(*value)
|
41
|
+
in [:nokey] then nil
|
42
|
+
in [:keyreq, *] | [:key, *] then record.keywords.merge! value if value
|
43
|
+
in [:keyrest, *] then record.keywords.merge!(**value) if value
|
44
|
+
in [:block, *] then record.block = value
|
45
|
+
else fail ArgumentError, "Invalid parameter kind: #{pair.first.inspect}."
|
46
|
+
end
|
47
|
+
rescue TypeError
|
48
|
+
raise TypeError, "#{value.inspect} is an invalid #{pair.first.inspect} value."
|
49
|
+
end
|
50
|
+
|
51
|
+
def splat_positional value
|
52
|
+
return unless value
|
53
|
+
|
54
|
+
record.positionals = value.is_a?(Array) ? value : [value]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/marameters/defaulter.rb
CHANGED
data/lib/marameters/probe.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "refinements/arrays"
|
4
|
-
|
5
3
|
module Marameters
|
6
|
-
# Provides
|
4
|
+
# Provides information on a method's parameters.
|
7
5
|
class Probe
|
8
|
-
|
6
|
+
CATEGORIES = {
|
7
|
+
positionals: %i[req opt],
|
8
|
+
keywords: %i[keyreq key],
|
9
|
+
splats: %i[rest keyrest]
|
10
|
+
}.freeze
|
9
11
|
|
10
12
|
def self.of klass, name, collection: []
|
11
13
|
method = klass.instance_method name
|
@@ -16,32 +18,32 @@ module Marameters
|
|
16
18
|
collection
|
17
19
|
end
|
18
20
|
|
19
|
-
|
21
|
+
attr_reader :keywords, :positionals, :splats
|
22
|
+
|
23
|
+
def initialize parameters, categories: CATEGORIES
|
20
24
|
@parameters = parameters
|
21
|
-
|
25
|
+
categories.each { |category, kinds| define_variable category, kinds }
|
22
26
|
end
|
23
27
|
|
24
|
-
def block =
|
28
|
+
def block = parameters.find { |kind, name| break name if kind == :block }
|
25
29
|
|
26
|
-
def block? =
|
30
|
+
def block? = (parameters in [*, [:block, *]])
|
27
31
|
|
28
|
-
def empty? =
|
32
|
+
def empty? = parameters.empty?
|
29
33
|
|
30
34
|
def keyword_slice collection, keys:
|
31
35
|
collection.select { |key| !keys.include?(key) || keywords.include?(key) }
|
32
36
|
end
|
33
37
|
|
34
|
-
def keywords = items.values_at(:keyreq, :key).compress!
|
35
|
-
|
36
38
|
def keywords? = keywords.any?
|
37
39
|
|
38
|
-
def kind?(
|
40
|
+
def kind?(value) = parameters.any? { |kind, _name| kind == value }
|
39
41
|
|
40
|
-
def kinds =
|
42
|
+
def kinds = parameters.map { |kind, _name| kind }
|
41
43
|
|
42
|
-
def name?(
|
44
|
+
def name?(value) = parameters.any? { |_kind, name| name == value }
|
43
45
|
|
44
|
-
def names =
|
46
|
+
def names = parameters.map { |_kind, name| name }
|
45
47
|
|
46
48
|
def only_bare_splats? = (parameters in [[:rest]] | [[:keyrest]] | [[:rest], [:keyrest]])
|
47
49
|
|
@@ -49,20 +51,19 @@ module Marameters
|
|
49
51
|
|
50
52
|
def only_single_splats? = (parameters in [[:rest, *]])
|
51
53
|
|
52
|
-
def positionals = items.values_at(:req, :opt).compress!
|
53
|
-
|
54
54
|
def positionals? = positionals.any?
|
55
55
|
|
56
|
-
def splats = items.values_at(:rest, :keyrest).compress!
|
57
|
-
|
58
56
|
def splats? = splats.any?
|
59
57
|
|
60
58
|
def to_a = parameters
|
61
59
|
|
62
|
-
def to_h = items
|
63
|
-
|
64
60
|
private
|
65
61
|
|
66
|
-
attr_reader :parameters
|
62
|
+
attr_reader :parameters
|
63
|
+
|
64
|
+
def define_variable category, kinds
|
65
|
+
parameters.filter_map { |kind, name| next name if kinds.include? kind }
|
66
|
+
.then { |collection| instance_variable_set "@#{category}", collection }
|
67
|
+
end
|
67
68
|
end
|
68
69
|
end
|
data/lib/marameters/signature.rb
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Marameters
|
4
|
+
# Captures arguments, by category, for message splatting.
|
5
|
+
Splat = Struct.new :positionals, :keywords, :block, keyword_init: true do
|
6
|
+
def initialize *arguments
|
7
|
+
super
|
8
|
+
|
9
|
+
self[:positionals] ||= []
|
10
|
+
self[:keywords] ||= {}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/marameters.rb
CHANGED
@@ -6,4 +6,11 @@ Zeitwerk::Loader.for_gem.setup
|
|
6
6
|
|
7
7
|
# Main namespace.
|
8
8
|
module Marameters
|
9
|
+
def self.categorize(parameters, arguments) = Categorizer.new(parameters).call(arguments)
|
10
|
+
|
11
|
+
def self.of(...) = Probe.of(...)
|
12
|
+
|
13
|
+
def self.probe(...) = Probe.new(...)
|
14
|
+
|
15
|
+
def self.signature(...) = Signature.new(...)
|
9
16
|
end
|
data/marameters.gemspec
CHANGED
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: marameters
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brooke Kuhlmann
|
@@ -28,7 +28,7 @@ cert_chain:
|
|
28
28
|
CxDe2+VuChj4I1nvIHdu+E6XoEVlanUPKmSg6nddhkKn2gC45Kyzh6FZqnzH/CRp
|
29
29
|
RFE=
|
30
30
|
-----END CERTIFICATE-----
|
31
|
-
date: 2022-08
|
31
|
+
date: 2022-09-08 00:00:00.000000000 Z
|
32
32
|
dependencies:
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: refinements
|
@@ -71,9 +71,11 @@ files:
|
|
71
71
|
- README.adoc
|
72
72
|
- lib/marameters.rb
|
73
73
|
- lib/marameters/builder.rb
|
74
|
+
- lib/marameters/categorizer.rb
|
74
75
|
- lib/marameters/defaulter.rb
|
75
76
|
- lib/marameters/probe.rb
|
76
77
|
- lib/marameters/signature.rb
|
78
|
+
- lib/marameters/splat.rb
|
77
79
|
- marameters.gemspec
|
78
80
|
homepage: https://www.alchemists.io/projects/marameters
|
79
81
|
licenses:
|
@@ -101,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
101
103
|
- !ruby/object:Gem::Version
|
102
104
|
version: '0'
|
103
105
|
requirements: []
|
104
|
-
rubygems_version: 3.3.
|
106
|
+
rubygems_version: 3.3.21
|
105
107
|
signing_key:
|
106
108
|
specification_version: 4
|
107
109
|
summary: Provides dynamic method parameter construction and deconstruction.
|
metadata.gz.sig
CHANGED
Binary file
|