dry-core 0.5.0 → 0.7.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 455db6bceb946acab5dbe431c4ba7edf9a7344f09c2b2af9e38c0b8788e3d600
4
- data.tar.gz: c460402f0b99fbe8ad488463cebd828a6a814d0a45f5200fbf1c90302fa66649
3
+ metadata.gz: cb330574bba6050e9a3d0fd466fdbd276ce76749b2661b8091ebf6178a2ef6c9
4
+ data.tar.gz: c1319b83600515df992a91556000989aa092d4229f69669b8f544368d93d21a8
5
5
  SHA512:
6
- metadata.gz: c07482895edfb498acc414cc6672c2fa271cbd892f59393b4baa6b3b7ec4e115b29474be85fe42e40660fe06f926442608d693816594bdfcd910411185acad9c
7
- data.tar.gz: c3d0966d660f09a26007e226b57e1cc2c124a420606b4de5d104730c6f5d9c54492a89788fb601722eab10f42dac4cda5d6689c79fe6c53a3df9c6510569fa7e
6
+ metadata.gz: 2b1967bb0c991637ea01da5d267df78daa12e4801288932372eca2ecf8aff0c5933ef2cb9cdc06d978d6223da963a31dc5e362f1ad1052490e10ebbc2b39fae6
7
+ data.tar.gz: 22662f066d46742cfd12d79eddcfb1e76811ab00b0080be8bac8fef53a1229fa91f5501b9f0b34f09182f5843c7bf995332fc16d1fe192daeadff186cd543cb7
data/CHANGELOG.md CHANGED
@@ -1,6 +1,49 @@
1
1
  <!--- DO NOT EDIT THIS FILE - IT'S AUTOMATICALLY GENERATED VIA DEVTOOLS --->
2
2
 
3
- ## unreleased
3
+ ## 0.7.1 2021-07-10
4
+
5
+
6
+ ### Fixed
7
+
8
+ - [memoizable] memoizable correctly handles cases where a method
9
+ has unnamed params (e.g. happens when the new `...` syntax is used) (@flash-gordon)
10
+
11
+
12
+
13
+ [Compare v0.7.0...v0.7.1](https://github.com/dry-rb/dry-core/compare/v0.7.0...v0.7.1)
14
+
15
+ ## 0.7.0 2021-07-08
16
+
17
+
18
+ ### Fixed
19
+
20
+ - [memoizable] warnings when using keyword arguments (@flash-gordon)
21
+ - [deprecations] warnings show more relevant information about caller by default (@timriley)
22
+
23
+ ### Changed
24
+
25
+ - Minimal Ruby version is 2.6
26
+ - [memoizable] memoization of block-accepting methods is deprecated (@flash-gordon)
27
+
28
+ [Compare v0.6.0...v0.7.0](https://github.com/dry-rb/dry-core/compare/v0.6.0...v0.7.0)
29
+
30
+ ## 0.6.0 2021-06-03
31
+
32
+
33
+ ### Added
34
+
35
+ - [memoizable] support for `BasicObject` (@oleander)
36
+ - [memoizable] support for methods that accept blocks (@oleander)
37
+ - [deprecations] allow printing frame info on warn when setting up Deprecation module (via #52) (@waiting-for-dev)
38
+
39
+ ### Fixed
40
+
41
+ - [memoizable] works with MRI 2.7+ keyword arguments now (@oleander)
42
+
43
+
44
+ [Compare v0.5.0...v0.6.0](https://github.com/dry-rb/dry-core/compare/v0.5.0...v0.6.0)
45
+
46
+ ## 0.5.0 2012-12-12
4
47
 
5
48
 
6
49
  ### Added
@@ -8,7 +51,7 @@
8
51
  - dry-equalizer has been imported into dry-core as `Dry::Core::Equalizer` but the interface remains the same, which is `include Dry.Equalizer(...)` - we'll be porting all other gems that depend on dry-equalizer to the latest dry-core with equalizer included *gradually*. Eventually dry-equalizer usage will be gone completely in rom-rb/dry-rb/hanami projects (@solnic)
9
52
 
10
53
 
11
- [Compare v0.4.10...master](https://github.com/dry-rb/dry-core/compare/v0.4.10...master)
54
+ [Compare v0.4.10...v0.5.0](https://github.com/dry-rb/dry-core/compare/v0.4.10...v0.5.0)
12
55
 
13
56
  ## 0.4.10 2020-11-19
14
57
 
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2015-2020 dry-rb team
3
+ Copyright (c) 2015-2021 dry-rb team
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy of
6
6
  this software and associated documentation files (the "Software"), to deal in
data/README.md CHANGED
@@ -1,3 +1,4 @@
1
+ <!--- this file is synced from dry-rb/template-gem project -->
1
2
  [gem]: https://rubygems.org/gems/dry-core
2
3
  [actions]: https://github.com/dry-rb/dry-core/actions
3
4
  [codacy]: https://www.codacy.com/gh/dry-rb/dry-core
@@ -14,15 +15,15 @@
14
15
 
15
16
  ## Links
16
17
 
17
- * [User documentation](http://dry-rb.org/gems/dry-core)
18
+ * [User documentation](https://dry-rb.org/gems/dry-core)
18
19
  * [API documentation](http://rubydoc.info/gems/dry-core)
19
20
 
20
21
  ## Supported Ruby versions
21
22
 
22
23
  This library officially supports the following Ruby versions:
23
24
 
24
- * MRI >= `2.5`
25
- * jruby >= `9.2`
25
+ * MRI `>= 2.6.0`
26
+ * ~~jruby~~ `>= 9.3` (we are waiting for [2.6 support](https://github.com/jruby/jruby/issues/6161))
26
27
 
27
28
  ## License
28
29
 
data/dry-core.gemspec CHANGED
@@ -1,31 +1,32 @@
1
1
  # frozen_string_literal: true
2
- # this file is managed by dry-rb/devtools project
3
2
 
4
- lib = File.expand_path('lib', __dir__)
3
+ # this file is synced from dry-rb/template-gem project
4
+
5
+ lib = File.expand_path("lib", __dir__)
5
6
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
- require 'dry/core/version'
7
+ require "dry/core/version"
7
8
 
8
9
  Gem::Specification.new do |spec|
9
- spec.name = 'dry-core'
10
+ spec.name = "dry-core"
10
11
  spec.authors = ["Nikita Shilnikov"]
11
12
  spec.email = ["fg@flashgordon.ru"]
12
- spec.license = 'MIT'
13
+ spec.license = "MIT"
13
14
  spec.version = Dry::Core::VERSION.dup
14
15
 
15
16
  spec.summary = "A toolset of small support modules used throughout the dry-rb ecosystem"
16
17
  spec.description = spec.summary
17
- spec.homepage = 'https://dry-rb.org/gems/dry-core'
18
+ spec.homepage = "https://dry-rb.org/gems/dry-core"
18
19
  spec.files = Dir["CHANGELOG.md", "LICENSE", "README.md", "dry-core.gemspec", "lib/**/*"]
19
- spec.bindir = 'bin'
20
+ spec.bindir = "bin"
20
21
  spec.executables = []
21
- spec.require_paths = ['lib']
22
+ spec.require_paths = ["lib"]
22
23
 
23
- spec.metadata['allowed_push_host'] = 'https://rubygems.org'
24
- spec.metadata['changelog_uri'] = 'https://github.com/dry-rb/dry-core/blob/master/CHANGELOG.md'
25
- spec.metadata['source_code_uri'] = 'https://github.com/dry-rb/dry-core'
26
- spec.metadata['bug_tracker_uri'] = 'https://github.com/dry-rb/dry-core/issues'
24
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
25
+ spec.metadata["changelog_uri"] = "https://github.com/dry-rb/dry-core/blob/master/CHANGELOG.md"
26
+ spec.metadata["source_code_uri"] = "https://github.com/dry-rb/dry-core"
27
+ spec.metadata["bug_tracker_uri"] = "https://github.com/dry-rb/dry-core/issues"
27
28
 
28
- spec.required_ruby_version = ">= 2.5.0"
29
+ spec.required_ruby_version = ">= 2.6.0"
29
30
 
30
31
  # to update dependencies edit project.yml
31
32
  spec.add_runtime_dependency "concurrent-ruby", "~> 1.0"
@@ -46,7 +46,8 @@ module Dry
46
46
  # this means you shouldn't pass Procs in args unless you're sure
47
47
  # they are always the same instances, otherwise you introduce a memory leak
48
48
  #
49
- # @return [Object] block's return value (cached for subsequent calls with the same argument values)
49
+ # @return [Object] block's return value (cached for subsequent calls with
50
+ # the same argument values)
50
51
  def fetch_or_store(*args, &block)
51
52
  cache.fetch_or_store(args.hash, &block)
52
53
  end
@@ -67,7 +67,7 @@ module Dry
67
67
  # defines :one, coerce: Dry::Types['coercible.string']
68
68
  # end
69
69
  #
70
- def defines(*args, type: ::Object, coerce: IDENTITY)
70
+ def defines(*args, type: ::Object, coerce: IDENTITY) # rubocop:disable Metrics/PerceivedComplexity
71
71
  unless coerce.respond_to?(:call)
72
72
  raise ::ArgumentError, "Non-callable coerce option: #{coerce.inspect}"
73
73
  end
@@ -80,10 +80,10 @@ module Dry
80
80
  if Undefined.equal?(value)
81
81
  if instance_variable_defined?(ivar)
82
82
  instance_variable_get(ivar)
83
- else
83
+ else # rubocop:disable Style/EmptyElse
84
84
  nil
85
85
  end
86
- elsif type === value
86
+ elsif type === value # rubocop:disable Style/CaseEquality
87
87
  instance_variable_set(ivar, coerce.call(value))
88
88
  else
89
89
  raise InvalidClassAttributeValue.new(name, value)
@@ -6,9 +6,7 @@ module Dry
6
6
  class ClassBuilder
7
7
  ParentClassMismatch = Class.new(TypeError)
8
8
 
9
- attr_reader :name
10
- attr_reader :parent
11
- attr_reader :namespace
9
+ attr_reader :name, :parent, :namespace
12
10
 
13
11
  def initialize(name:, parent: nil, namespace: nil)
14
12
  @name = name
@@ -83,7 +81,7 @@ module Dry
83
81
  def create_base(namespace, name, parent)
84
82
  begin
85
83
  namespace.const_get(name)
86
- rescue NameError
84
+ rescue NameError # rubocop:disable Lint/SuppressedException
87
85
  end
88
86
 
89
87
  if namespace.const_defined?(name, false)
@@ -25,7 +25,7 @@ module Dry
25
25
  # An empty set
26
26
  EMPTY_SET = ::Set.new.freeze
27
27
  # An empty string
28
- EMPTY_STRING = "".freeze
28
+ EMPTY_STRING = ""
29
29
  # Identity function
30
30
  IDENTITY = (-> x { x }).freeze
31
31
 
@@ -42,7 +42,7 @@ module Dry
42
42
  # end
43
43
  Undefined = Object.new.tap do |undefined|
44
44
  # @api private
45
- Self = -> { Undefined }
45
+ Self = -> { Undefined } # rubocop:disable Lint/ConstantDefinitionInBlock
46
46
 
47
47
  # @api public
48
48
  def undefined.to_s
@@ -62,7 +62,7 @@ module Dry
62
62
  # 1 + Undefined.default(val, 2)
63
63
  # end
64
64
  #
65
- def undefined.default(x, y = self)
65
+ def undefined.default(x, y = self) # rubocop:disable Naming/MethodParameterName
66
66
  if equal?(x)
67
67
  if equal?(y)
68
68
  yield
@@ -33,17 +33,27 @@ module Dry
33
33
  # Prints a warning
34
34
  #
35
35
  # @param [String] msg Warning string
36
- def warn(msg, tag: nil)
37
- tagged = "[#{tag || 'deprecated'}] #{msg.gsub(/^\s+/, '')}"
38
- logger.warn(tagged)
36
+ # @param [String] tag Tag to help identify the source of the warning.
37
+ # Defaults to "deprecated"
38
+ # @param [Integer] Caller frame to add to the message
39
+ def warn(msg, tag: nil, uplevel: nil)
40
+ caller_info = uplevel.nil? ? nil : "#{caller_locations(uplevel + 2, 1)[0]} "
41
+ tag = "[#{tag || "deprecated"}] "
42
+ hint = msg.gsub(/^\s+/, "")
43
+
44
+ logger.warn("#{caller_info}#{tag}#{hint}")
39
45
  end
40
46
 
41
47
  # Wraps arguments with a standard message format and prints a warning
42
48
  #
43
49
  # @param [Object] name what is deprecated
44
50
  # @param [String] msg additional message usually containing upgrade instructions
45
- def announce(name, msg, tag: nil)
46
- warn(deprecation_message(name, msg), tag: tag)
51
+ def announce(name, msg, tag: nil, uplevel: nil)
52
+ # Bump the uplevel (if provided) by one to account for the uplevel calculation
53
+ # taking place one frame deeper in `.warn`
54
+ uplevel += 1 if uplevel
55
+
56
+ warn(deprecation_message(name, msg), tag: tag, uplevel: uplevel)
47
57
  end
48
58
 
49
59
  # @api private
@@ -92,7 +102,7 @@ module Dry
92
102
  # @param [#warn] logger
93
103
  #
94
104
  # @api public
95
- def set_logger!(output = $stderr)
105
+ def set_logger!(output = $stderr) # rubocop:disable Naming/AccessorMethodName
96
106
  if output.respond_to?(:warn)
97
107
  @logger = output
98
108
  else
@@ -108,8 +118,9 @@ module Dry
108
118
  end
109
119
 
110
120
  # @api private
111
- class Tagged < Module
121
+ class Tagged < ::Module
112
122
  def initialize(tag)
123
+ super()
113
124
  @tag = tag
114
125
  end
115
126
 
@@ -145,8 +156,8 @@ module Dry
145
156
  # @option [String] message optional deprecation message
146
157
  def deprecate(old_name, new_name = nil, message: nil)
147
158
  full_msg = Deprecations.deprecated_name_message(
148
- "#{self.name}##{old_name}",
149
- new_name ? "#{self.name}##{new_name}" : nil,
159
+ "#{name}##{old_name}",
160
+ new_name ? "#{name}##{new_name}" : nil,
150
161
  message
151
162
  )
152
163
  mod = self
@@ -178,8 +189,8 @@ module Dry
178
189
  # @option [String] message optional deprecation message
179
190
  def deprecate_class_method(old_name, new_name = nil, message: nil)
180
191
  full_msg = Deprecations.deprecated_name_message(
181
- "#{self.name}.#{old_name}",
182
- new_name ? "#{self.name}.#{new_name}" : nil,
192
+ "#{name}.#{old_name}",
193
+ new_name ? "#{name}.#{new_name}" : nil,
183
194
  message
184
195
  )
185
196
 
@@ -203,7 +214,7 @@ module Dry
203
214
  remove_const(constant_name)
204
215
 
205
216
  full_msg = Deprecations.deprecated_name_message(
206
- "#{self.name}::#{constant_name}",
217
+ "#{name}::#{constant_name}",
207
218
  message
208
219
  )
209
220
 
@@ -14,7 +14,7 @@ module Dry
14
14
 
15
15
  module Core
16
16
  # Define equality, equivalence and inspection methods
17
- class Equalizer < Module
17
+ class Equalizer < ::Module
18
18
  # Initialize an Equalizer with the given keys
19
19
  #
20
20
  # Will use the keys with which it is initialized to define #cmp?,
@@ -29,6 +29,7 @@ module Dry
29
29
  #
30
30
  # @api private
31
31
  def initialize(*keys, **options)
32
+ super()
32
33
  @keys = keys.uniq
33
34
  define_methods(**options)
34
35
  freeze
@@ -1,67 +1,200 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "dry/core/deprecations"
4
+
3
5
  module Dry
4
6
  module Core
5
7
  module Memoizable
6
8
  MEMOIZED_HASH = {}.freeze
9
+ PARAM_PLACEHOLDERS = %i[* ** &].freeze
7
10
 
8
11
  module ClassInterface
9
- def memoize(*names)
10
- prepend(Memoizer.new(self, names))
12
+ module Base
13
+ def memoize(*names)
14
+ prepend(Memoizer.new(self, names))
15
+ end
11
16
  end
12
17
 
13
- def new(*)
14
- obj = super
15
- obj.instance_variable_set(:'@__memoized__', MEMOIZED_HASH.dup)
16
- obj
18
+ module BasicObject
19
+ include Base
20
+
21
+ def new(*)
22
+ obj = super
23
+ obj.instance_eval { @__memoized__ = MEMOIZED_HASH.dup }
24
+ obj
25
+ end
26
+ end
27
+
28
+ module Object
29
+ include Base
30
+
31
+ def new(*)
32
+ obj = super
33
+ obj.instance_variable_set(:@__memoized__, MEMOIZED_HASH.dup)
34
+ obj
35
+ end
36
+
37
+ if respond_to?(:ruby2_keywords, true)
38
+ ruby2_keywords(:new)
39
+ end
17
40
  end
18
41
  end
19
42
 
20
43
  def self.included(klass)
21
44
  super
22
- klass.extend(ClassInterface)
23
- end
24
45
 
25
- attr_reader :__memoized__
46
+ if klass <= Object
47
+ klass.extend(ClassInterface::Object)
48
+ else
49
+ klass.extend(ClassInterface::BasicObject)
50
+ end
51
+ end
26
52
 
27
53
  # @api private
28
- class Memoizer < Module
29
- attr_reader :klass
30
- attr_reader :names
31
-
54
+ class Memoizer < ::Module
32
55
  # @api private
33
56
  def initialize(klass, names)
34
- @names = names
35
- @klass = klass
36
- define_memoizable_names!
57
+ super()
58
+ names.each do |name|
59
+ define_memoizable(
60
+ method: klass.instance_method(name)
61
+ )
62
+ end
37
63
  end
38
64
 
39
65
  private
40
66
 
41
67
  # @api private
42
- def define_memoizable_names!
43
- names.each do |name|
44
- meth = klass.instance_method(name)
45
-
46
- if meth.parameters.size > 0
47
- define_method(name) do |*args|
48
- name_with_args = :"#{name}_#{args.hash}"
49
-
50
- if __memoized__.key?(name_with_args)
51
- __memoized__[name_with_args]
52
- else
53
- __memoized__[name_with_args] = super(*args)
54
- end
55
- end
68
+ def define_memoizable(method:) # rubocop:disable Metrics/AbcSize
69
+ parameters = method.parameters
70
+
71
+ if parameters.empty?
72
+ key = method.name.hash
73
+ module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
74
+ def #{method.name} # def slow_fetch
75
+ if @__memoized__.key?(#{key}) # if @__memoized__.key?(12345678)
76
+ @__memoized__[#{key}] # @__memoized__[12345678]
77
+ else # else
78
+ @__memoized__[#{key}] = super # @__memoized__[12345678] = super
79
+ end # end
80
+ end # end
81
+ RUBY
82
+ else
83
+ mapping = parameters.to_h { |k, v = nil| [k, v] }
84
+ params, binds = declaration(parameters, mapping)
85
+ last_param = parameters.last
86
+
87
+ if last_param[0].eql?(:block) && !last_param[1].eql?(:&)
88
+ Deprecations.warn(<<~WARN)
89
+ Memoization for block-accepting methods isn't safe.
90
+ Every call creates a new block instance bloating cached results.
91
+ In the future, blocks will still be allowed but won't participate in
92
+ cache key calculation.
93
+ WARN
94
+ end
95
+
96
+ m = module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
97
+ def #{method.name}(#{params.join(", ")}) # def slow_calc(arg1, arg2, arg3)
98
+ key = [:"#{method.name}", #{binds.join(", ")}].hash # [:slow_calc, arg1, arg2, arg3].hash
99
+ #
100
+ if @__memoized__.key?(key) # if @__memoized__.key?(key)
101
+ @__memoized__[key] # @__memoized__[key]
102
+ else # else
103
+ @__memoized__[key] = super # @__memoized__[key] = super
104
+ end # end
105
+ end # end
106
+ RUBY
107
+
108
+ if respond_to?(:ruby2_keywords, true) && mapping.key?(:reyrest)
109
+ ruby2_keywords(method.name)
110
+ end
111
+
112
+ m
113
+ end
114
+ end
115
+
116
+ # @api private
117
+ def declaration(definition, lookup)
118
+ params = []
119
+ binds = []
120
+ defined = {}
121
+
122
+ definition.each do |type, name|
123
+ mapped_type = map_bind_type(type, name, lookup, defined) do
124
+ raise ::NotImplementedError, "type: #{type}, name: #{name}"
125
+ end
126
+
127
+ if mapped_type
128
+ defined[mapped_type] = true
129
+ bind = name_from_param(name) || make_bind_name(binds.size)
130
+
131
+ binds << bind
132
+ params << param(bind, mapped_type)
133
+ end
134
+ end
135
+
136
+ [params, binds]
137
+ end
138
+
139
+ # @api private
140
+ def name_from_param(name)
141
+ if PARAM_PLACEHOLDERS.include?(name)
142
+ nil
143
+ else
144
+ name
145
+ end
146
+ end
147
+
148
+ # @api private
149
+ def make_bind_name(idx)
150
+ :"__lv_#{idx}__"
151
+ end
152
+
153
+ # @api private
154
+ def map_bind_type(type, name, original_params, defined_types) # rubocop:disable Metrics/PerceivedComplexity
155
+ case type
156
+ when :req
157
+ :reqular
158
+ when :rest, :keyreq, :keyrest
159
+ type
160
+ when :block
161
+ if name.eql?(:&)
162
+ # most likely this is a case of delegation
163
+ # rather than actual block
164
+ nil
165
+ else
166
+ type
167
+ end
168
+ when :opt
169
+ if original_params.key?(:rest) || defined_types[:rest]
170
+ nil
56
171
  else
57
- define_method(name) do
58
- if __memoized__.key?(name)
59
- __memoized__[name]
60
- else
61
- __memoized__[name] = super()
62
- end
63
- end
172
+ :rest
64
173
  end
174
+ when :key
175
+ if original_params.key?(:keyrest) || defined_types[:keyrest]
176
+ nil
177
+ else
178
+ :keyrest
179
+ end
180
+ else
181
+ yield
182
+ end
183
+ end
184
+
185
+ # @api private
186
+ def param(name, type)
187
+ case type
188
+ when :reqular
189
+ name
190
+ when :rest
191
+ "*#{name}"
192
+ when :keyreq
193
+ "#{name}:"
194
+ when :keyrest
195
+ "**#{name}"
196
+ when :block
197
+ "&#{name}"
65
198
  end
66
199
  end
67
200
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Dry
4
4
  module Core
5
- VERSION = "0.5.0".freeze
5
+ VERSION = "0.7.1"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nikita Shilnikov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-12-12 00:00:00.000000000 Z
11
+ date: 2021-07-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -107,14 +107,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
- version: 2.5.0
110
+ version: 2.6.0
111
111
  required_rubygems_version: !ruby/object:Gem::Requirement
112
112
  requirements:
113
113
  - - ">="
114
114
  - !ruby/object:Gem::Version
115
115
  version: '0'
116
116
  requirements: []
117
- rubygems_version: 3.1.4
117
+ rubygems_version: 3.1.6
118
118
  signing_key:
119
119
  specification_version: 4
120
120
  summary: A toolset of small support modules used throughout the dry-rb ecosystem