dry-auto_inject 1.0.1 → 1.2.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.
@@ -9,12 +9,14 @@ module Dry
9
9
 
10
10
  def define_new
11
11
  class_mod.class_exec(container, dependency_map) do |container, dependency_map|
12
+ deps_with_indices = dependency_map.to_h.values.map.with_index
13
+
12
14
  define_method :new do |*args|
13
- deps = dependency_map.to_h.values.map.with_index { |identifier, i|
15
+ deps = deps_with_indices.map do |identifier, i|
14
16
  args[i] || container[identifier]
15
- }
17
+ end
16
18
 
17
- super(*deps, *args[deps.size..])
19
+ super(*deps, *args.drop(deps.size))
18
20
  end
19
21
  end
20
22
  end
@@ -46,11 +48,12 @@ module Dry
46
48
  end
47
49
 
48
50
  def define_initialize_with_splat(super_parameters)
49
- super_pass = if super_parameters.splat?
50
- "*args"
51
- else
52
- "*args.take(#{super_parameters.length})"
53
- end
51
+ super_pass =
52
+ if super_parameters.splat?
53
+ "*args"
54
+ else
55
+ "*args.take(#{super_parameters.length})"
56
+ end
54
57
 
55
58
  assignments = dependency_map.names.map.with_index do |name, idx|
56
59
  "@#{name} = args[#{idx}]"
@@ -9,12 +9,14 @@ module Dry
9
9
 
10
10
  def define_new
11
11
  class_mod.class_exec(container, dependency_map) do |container, dependency_map|
12
+ deps_map = dependency_map.to_h
13
+
12
14
  define_method :new do |options = {}|
13
- deps = dependency_map.to_h.each_with_object({}) { |(name, identifier), obj|
14
- obj[name] = options[name] || container[identifier]
15
- }.merge(options)
15
+ deps = deps_map.transform_values do |identifier|
16
+ options[identifier] || container[identifier]
17
+ end
16
18
 
17
- super(deps)
19
+ super({**deps, **options})
18
20
  end
19
21
  end
20
22
  end
@@ -24,7 +26,7 @@ module Dry
24
26
  super_pass = super_params.empty? ? "" : "options"
25
27
  assignments = dependency_map.names.map do |name|
26
28
  <<~RUBY
27
- unless !options.key?(:#{name}) && instance_variable_defined?(:'@#{name}')
29
+ if options.key?(:#{name}) || !instance_variable_defined?(:'@#{name}')
28
30
  @#{name} = options[:#{name}]
29
31
  end
30
32
  RUBY
@@ -33,7 +35,7 @@ module Dry
33
35
 
34
36
  instance_mod.class_eval <<-RUBY, __FILE__, __LINE__ + 1
35
37
  def initialize(options) # def initialize(options)
36
- # unless !options.key?(:dep) && instance_variable_defined?(:@dep)
38
+ # if options.key?(:dep) || !instance_variable_defined?(:@dep)
37
39
  #{body} # @dep = options[:dep]
38
40
  # end
39
41
  super(#{super_pass}) # super(options)
@@ -23,8 +23,10 @@ module Dry
23
23
 
24
24
  def define_initialize(klass)
25
25
  super_parameters = MethodParameters.of(klass, :initialize).each do |ps|
26
- # Look upwards past `def foo(*)` and `def foo(...)` methods
27
- # until we get an explicit list of parameters
26
+ # Look upwards past methods that only forward arguments, stopping at the
27
+ # first instance of explicit parameters. So `foo(**kwargs)` is
28
+ # skipped, but `foo(bar:, **kwargs)` is not. `foo(...)` is used by ROM as
29
+ # a delegation pattern, so it is not skipped.
28
30
  break ps unless ps.pass_through?
29
31
  end
30
32
 
@@ -37,15 +39,18 @@ module Dry
37
39
  self
38
40
  end
39
41
 
42
+ # rubocop:disable Lint/UnderscorePrefixedVariableName
40
43
  def define_initialize_with_keywords(super_parameters)
41
44
  assign_dependencies = method(:assign_dependencies)
42
45
  slice_kwargs = method(:slice_kwargs)
43
46
 
44
47
  instance_mod.class_exec do
45
- define_method :initialize do |**kwargs, &block|
46
- assign_dependencies.(kwargs, self)
48
+ # The choice of `__auto_inject_kwargs__` here is intentional, and used
49
+ # by MethodParameters#pass_through? to detect an injected initialize.
50
+ define_method :initialize do |**__auto_inject_kwargs__, &block|
51
+ assign_dependencies.(__auto_inject_kwargs__, self)
47
52
 
48
- super_kwargs = slice_kwargs.(kwargs, super_parameters)
53
+ super_kwargs = slice_kwargs.(__auto_inject_kwargs__, super_parameters)
49
54
 
50
55
  if super_kwargs.any?
51
56
  super(**super_kwargs, &block)
@@ -61,13 +66,13 @@ module Dry
61
66
  slice_kwargs = method(:slice_kwargs)
62
67
 
63
68
  instance_mod.class_exec do
64
- define_method :initialize do |*args, **kwargs, &block|
65
- assign_dependencies.(kwargs, self)
69
+ define_method :initialize do |*args, **__auto_inject_kwargs__, &block|
70
+ assign_dependencies.(__auto_inject_kwargs__, self)
66
71
 
67
72
  if super_parameters.splat?
68
- super(*args, **kwargs, &block)
73
+ super(*args, **__auto_inject_kwargs__, &block)
69
74
  else
70
- super_kwargs = slice_kwargs.(kwargs, super_parameters)
75
+ super_kwargs = slice_kwargs.(__auto_inject_kwargs__, super_parameters)
71
76
 
72
77
  if super_kwargs.any?
73
78
  super(*args, **super_kwargs, &block)
@@ -78,6 +83,7 @@ module Dry
78
83
  end
79
84
  end
80
85
  end
86
+ # rubocop:enable Lint/UnderscorePrefixedVariableName
81
87
 
82
88
  def assign_dependencies(kwargs, destination)
83
89
  dependency_map.names.each do |name|
@@ -11,9 +11,9 @@ module Dry
11
11
  register :default, strategy
12
12
  end
13
13
 
14
- register :args, proc { Args }
14
+ register :args, proc { Strategies::Args }
15
15
  register :hash, proc { Strategies::Hash }
16
- register_default :kwargs, proc { Kwargs }
16
+ register_default :kwargs, proc { Strategies::Kwargs }
17
17
  end
18
18
  end
19
19
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Dry
4
4
  module AutoInject
5
- VERSION = "1.0.1"
5
+ VERSION = "1.2.0"
6
6
  end
7
7
  end
@@ -6,14 +6,15 @@ require "dry/core"
6
6
  module Dry
7
7
  module AutoInject
8
8
  def self.loader
9
- @loader ||= Zeitwerk::Loader.new.tap do |loader|
10
- root = File.expand_path("..", __dir__)
9
+ @loader ||= ::Zeitwerk::Loader.new.tap do |loader|
10
+ root = ::File.expand_path("..", __dir__)
11
11
  loader.tag = "dry-auto_inject"
12
- loader.inflector = Zeitwerk::GemInflector.new("#{root}/dry-auto_inject.rb")
12
+ loader.inflector = ::Zeitwerk::GemInflector.new("#{root}/dry-auto_inject.rb")
13
13
  loader.push_dir(root)
14
14
  loader.ignore(
15
15
  "#{root}/dry-auto_inject.rb",
16
- "#{root}/dry/auto_inject/version.rb"
16
+ "#{root}/dry/auto_inject/version.rb",
17
+ "#{root}/rubocop"
17
18
  )
18
19
  end
19
20
  end
@@ -1,3 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "dry/auto_inject"
4
+
5
+ module RuboCop
6
+ module DryAutoInject
7
+ autoload :Plugin, "rubocop/dry_auto_inject/plugin"
8
+ end
9
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "mixin"
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module DryAutoInject
8
+ # Enforces a configurable order for dry-auto_inject `Import[...]` deps.
9
+ #
10
+ # Non-aliased deps are emitted first, then aliased deps. Within each
11
+ # section, deps are grouped by matching the configured `Order` patterns
12
+ # and sorted alphabetically inside a group. Each pattern can be:
13
+ #
14
+ # - `'*'` — catch-all (at most one; implicitly appended)
15
+ # - `'prefix.*'` — prefix wildcard (matches `prefix` and `prefix.*`)
16
+ # - `'/regex/flags'` — regex matched against the dep path
17
+ # - otherwise — exact path match
18
+ #
19
+ # If `Order` is empty or omits `'*'`, a catch-all `'*'` group is appended
20
+ # implicitly so unmatched deps still have a home.
21
+ #
22
+ # Specific patterns take priority over the `*` catch-all regardless of
23
+ # their position in `Order`.
24
+ #
25
+ # @example Order: ['web.*', '*', 'core.*']
26
+ # # bad
27
+ # include Import[
28
+ # 'core.db',
29
+ # 'web.router',
30
+ # ]
31
+ #
32
+ # # good
33
+ # include Import[
34
+ # 'web.router',
35
+ # 'core.db',
36
+ # ]
37
+ #
38
+ # @example Order: ['/\A[^.]+\z/', '*'] — group dotless deps first
39
+ # include Import[
40
+ # 'logger',
41
+ # 'core.db',
42
+ # ]
43
+ class DependencyOrder < Base
44
+ extend AutoCorrector
45
+ include Mixin
46
+
47
+ MSG = "Dependencies are not in the configured order."
48
+
49
+ def initialize(config = nil, options = nil)
50
+ super
51
+ @parsed_order = build_parsed_order
52
+ end
53
+
54
+ def on_send(node)
55
+ return unless injector_call?(node)
56
+
57
+ deps = parse_injector_deps(node)
58
+ return if deps.nil?
59
+
60
+ order = parsed_order
61
+ current = deps[:non_aliased] + deps[:aliased]
62
+ sorted = [deps[:non_aliased], deps[:aliased]].flat_map { |group|
63
+ group_and_sort(group, order) { |d| d[:path] }
64
+ }
65
+
66
+ return if current == sorted
67
+
68
+ add_offense(node, message: MSG) do |corrector|
69
+ replace_injector_content(corrector, node, sorted)
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ attr_reader :parsed_order
76
+
77
+ def build_parsed_order
78
+ raw = Array(cop_config["Order"]).dup
79
+
80
+ if raw.count("*") > 1
81
+ raise "#{self.class.badge}: `Order` may contain at most one '*' entry " \
82
+ "(got #{raw.inspect})"
83
+ end
84
+
85
+ raw << "*" unless raw.include?("*")
86
+ raw.each { |pat| validate_regex_pattern!(pat) if pat.start_with?("/") }
87
+ raw
88
+ end
89
+
90
+ def validate_regex_pattern!(pat)
91
+ return if parse_regex_entry(pat)
92
+
93
+ raise "#{self.class.badge}: invalid regex in `Order` entry #{pat.inspect}"
94
+ end
95
+
96
+ def group_and_sort(deps, order, &)
97
+ groups = ::Hash.new { |h, k| h[k] = [] }
98
+
99
+ # Specific patterns take priority over the `*` catch-all regardless
100
+ # of their position in `Order`.
101
+ specific_patterns = order.reject { |p| p == "*" }
102
+
103
+ deps.each do |d|
104
+ pat = specific_patterns.find { |p| match_pattern?(d[:path], p) } || "*"
105
+ groups[pat] << d
106
+ end
107
+
108
+ order.flat_map { |p| groups[p].sort_by(&) }
109
+ end
110
+
111
+ def match_pattern?(path, pattern)
112
+ return true if pattern == "*"
113
+
114
+ if pattern.start_with?("/")
115
+ parse_regex_entry(pattern)&.match?(path)
116
+ elsif pattern.end_with?(".*")
117
+ prefix = pattern[...-2]
118
+ path == prefix || path.start_with?("#{prefix}.")
119
+ else
120
+ path == pattern
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/core"
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module DryAutoInject
8
+ # Shared helpers for cops that inspect `Import[...]`-style calls produced
9
+ # by dry-auto_inject.
10
+ module Mixin
11
+ include ::Dry::Core::Constants
12
+
13
+ private
14
+
15
+ def injector_call?(node)
16
+ return false unless node.send_type?
17
+ return false unless node.method?(:[])
18
+
19
+ receiver = node.receiver
20
+ return false unless receiver&.const_type?
21
+
22
+ match_injector_module?(receiver)
23
+ end
24
+
25
+ def match_injector_module?(const_node)
26
+ full = const_fullname(const_node)
27
+ clean = full.sub(/\A::/, "")
28
+
29
+ Array(cop_config["InjectorModules"]).any? do |entry|
30
+ match_injector_entry?(entry.to_s, full, clean)
31
+ end
32
+ end
33
+
34
+ # Entry formats:
35
+ # - `/pattern/flags` — regex literal, matched against `clean`
36
+ # - `*::Name` — last segment match with any prefix (incl. empty)
37
+ # - otherwise — exact match on the full or leading-`::`-stripped path
38
+ def match_injector_entry?(entry, full, clean)
39
+ if entry.start_with?("/")
40
+ regex = parse_regex_entry(entry)
41
+ return false unless regex
42
+
43
+ regex.match?(clean)
44
+ elsif entry.start_with?("*::")
45
+ suffix = entry[3..]
46
+ clean == suffix || clean.end_with?("::#{suffix}")
47
+ else
48
+ entry == full || entry == clean
49
+ end
50
+ end
51
+
52
+ # Returns a Regexp for a `/pattern/flags` literal, or nil if the
53
+ # string is not a regex literal or the pattern is invalid.
54
+ def parse_regex_entry(str)
55
+ m = str.match(%r{\A/(.*)/([imx]*)\z}m)
56
+ return nil unless m
57
+
58
+ opts = 0
59
+ opts |= ::Regexp::IGNORECASE if m[2].include?("i")
60
+ opts |= ::Regexp::MULTILINE if m[2].include?("m")
61
+ opts |= ::Regexp::EXTENDED if m[2].include?("x")
62
+ ::Regexp.new(m[1], opts)
63
+ rescue ::RegexpError
64
+ nil
65
+ end
66
+
67
+ def const_fullname(const_node)
68
+ parts = []
69
+ current = const_node
70
+
71
+ while current
72
+ if current.const_type?
73
+ parts.unshift(current.children[1].to_s)
74
+ current = current.children[0]
75
+ elsif current.cbase_type?
76
+ parts.unshift("")
77
+ current = nil
78
+ else
79
+ return ""
80
+ end
81
+ end
82
+
83
+ parts.join("::")
84
+ end
85
+
86
+ # Parses the arguments of an `Import[...]` call into a structured form.
87
+ # Returns nil if any argument is not a plain string or symbol-keyed/string-valued pair.
88
+ def parse_injector_deps(node)
89
+ args = node.arguments
90
+ return nil if args.empty?
91
+
92
+ hash_arg = args.last if args.last.hash_type?
93
+ string_nodes = hash_arg ? args[...-1] : args
94
+
95
+ non_aliased = parse_non_aliased_deps(string_nodes)
96
+ aliased = parse_aliased_deps(hash_arg)
97
+ return nil if non_aliased.nil? || aliased.nil?
98
+
99
+ {non_aliased:, aliased:}
100
+ end
101
+
102
+ def parse_non_aliased_deps(nodes)
103
+ return nil unless nodes.all?(&:str_type?)
104
+
105
+ nodes.map { |arg| {node: arg, alias: nil, path: arg.value} }
106
+ end
107
+
108
+ def parse_aliased_deps(hash_arg)
109
+ return EMPTY_ARRAY unless hash_arg
110
+ return nil unless hash_arg.pairs.all? { |p| p.key.sym_type? && p.value.str_type? }
111
+
112
+ hash_arg.pairs.map { |p| {node: p, alias: p.key.value.to_s, path: p.value.value} }
113
+ end
114
+
115
+ def format_dep(dep)
116
+ node = dep[:node]
117
+ if dep[:alias]
118
+ "#{dep[:alias]}: #{node.value.source}"
119
+ else
120
+ node.source
121
+ end
122
+ end
123
+
124
+ def line_indent_col(node)
125
+ line = node.source_range.source_line
126
+ line.index(/\S/) || node.source_range.column
127
+ end
128
+
129
+ def item_indent_spaces(node)
130
+ args = node.arguments
131
+
132
+ if args.any? && args.first.source_range.line > node.source_range.line
133
+ indent(args.first.source_range.column)
134
+ else
135
+ indent(line_indent_col(node) + 2)
136
+ end
137
+ end
138
+
139
+ def indent(width)
140
+ " " * width
141
+ end
142
+
143
+ def render_deps(node, deps)
144
+ if node.multiline?
145
+ inner_indent = item_indent_spaces(node)
146
+ close_indent = indent(line_indent_col(node))
147
+ lines = deps.map { |d| "#{inner_indent}#{format_dep(d)}," }
148
+ "\n#{lines.join("\n")}\n#{close_indent}"
149
+ else
150
+ deps.map { |d| format_dep(d) }.join(", ")
151
+ end
152
+ end
153
+
154
+ def replace_injector_content(corrector, node, deps)
155
+ selector = node.loc.selector
156
+ return unless selector&.source&.start_with?("[")
157
+
158
+ corrector.replace(selector.adjust(begin_pos: 1, end_pos: -1), render_deps(node, deps))
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "mixin"
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module DryAutoInject
8
+ # Flags dry-auto_inject imports whose hash key matches the last segment
9
+ # of the path, since dry-auto_inject already derives the dependency name
10
+ # from that segment.
11
+ #
12
+ # @example
13
+ # # bad
14
+ # include Import[foo: 'some.path.foo']
15
+ #
16
+ # # good
17
+ # include Import['some.path.foo']
18
+ class RedundantAlias < Base
19
+ extend AutoCorrector
20
+ include Mixin
21
+
22
+ MSG =
23
+ "Redundant alias `%<alias_name>s:` — dependency key is derived from " \
24
+ "the last segment of `'%<path>s'`."
25
+
26
+ def on_send(node)
27
+ return unless injector_call?(node)
28
+
29
+ deps = parse_injector_deps(node)
30
+ return if deps.nil?
31
+
32
+ redundant = deps[:aliased].select { |d| d[:alias] == d[:path].split(".").last }
33
+ return if redundant.empty?
34
+
35
+ canonical = promote_redundant_aliases(deps)
36
+
37
+ redundant.each_with_index do |dep, i|
38
+ add_offense(
39
+ dep[:node],
40
+ message: format(MSG, alias_name: dep[:alias], path: dep[:path])
41
+ ) do |corrector|
42
+ next unless i.zero?
43
+
44
+ replace_injector_content(corrector, node, canonical)
45
+ end
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def promote_redundant_aliases(deps)
52
+ non_aliased = deps[:non_aliased].dup
53
+ aliased = []
54
+
55
+ deps[:aliased].each do |d|
56
+ if d[:alias] == d[:path].split(".").last
57
+ non_aliased << {node: d[:node].value, alias: nil, path: d[:path]}
58
+ else
59
+ aliased << d
60
+ end
61
+ end
62
+
63
+ non_aliased + aliased
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "dry_auto_inject"
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lint_roller"
4
+
5
+ require "dry/auto_inject/version"
6
+
7
+ module RuboCop
8
+ module DryAutoInject
9
+ class Plugin < ::LintRoller::Plugin
10
+ def about
11
+ ::LintRoller::About.new(
12
+ name: "dry-auto_inject",
13
+ version: ::Dry::AutoInject::VERSION,
14
+ homepage: "https://hanakai.org/learn/dry/dry-auto_inject",
15
+ description: "RuboCop cops for enforcing dry-auto_inject conventions."
16
+ )
17
+ end
18
+
19
+ def supported?(context)
20
+ context.engine == :rubocop
21
+ end
22
+
23
+ def rules(_context)
24
+ project_root = ::Pathname.new(__dir__).join("../../..")
25
+
26
+ ::LintRoller::Rules.new(
27
+ type: :path,
28
+ config_format: :rubocop,
29
+ value: project_root.join("config", "default.yml")
30
+ )
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "dry_auto_inject/plugin"
4
+ require_relative "cop/dry_auto_inject/dependency_order"
5
+ require_relative "cop/dry_auto_inject/redundant_alias"
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry-auto_inject
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
- - Piotr Solnica
8
- autorequire:
9
- bindir: bin
7
+ - Hanakai team
8
+ bindir: exe
10
9
  cert_chain: []
11
- date: 2023-02-13 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: dry-core
@@ -16,14 +15,14 @@ dependencies:
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
- version: '1.0'
18
+ version: '1.1'
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - "~>"
25
24
  - !ruby/object:Gem::Version
26
- version: '1.0'
25
+ version: '1.1'
27
26
  - !ruby/object:Gem::Dependency
28
27
  name: zeitwerk
29
28
  requirement: !ruby/object:Gem::Requirement
@@ -82,14 +81,18 @@ dependencies:
82
81
  version: '0'
83
82
  description: Container-agnostic automatic constructor injection
84
83
  email:
85
- - piotr.solnica@gmail.com
84
+ - info@hanakai.org
86
85
  executables: []
87
86
  extensions: []
88
- extra_rdoc_files: []
87
+ extra_rdoc_files:
88
+ - CHANGELOG.md
89
+ - LICENSE
90
+ - README.md
89
91
  files:
90
92
  - CHANGELOG.md
91
93
  - LICENSE
92
94
  - README.md
95
+ - config/default.yml
93
96
  - dry-auto_inject.gemspec
94
97
  - lib/dry-auto_inject.rb
95
98
  - lib/dry/auto_inject.rb
@@ -103,15 +106,21 @@ files:
103
106
  - lib/dry/auto_inject/strategies/hash.rb
104
107
  - lib/dry/auto_inject/strategies/kwargs.rb
105
108
  - lib/dry/auto_inject/version.rb
109
+ - lib/rubocop/cop/dry_auto_inject/dependency_order.rb
110
+ - lib/rubocop/cop/dry_auto_inject/mixin.rb
111
+ - lib/rubocop/cop/dry_auto_inject/redundant_alias.rb
112
+ - lib/rubocop/dry-auto_inject.rb
113
+ - lib/rubocop/dry_auto_inject.rb
114
+ - lib/rubocop/dry_auto_inject/plugin.rb
106
115
  homepage: https://dry-rb.org/gems/dry-auto_inject
107
116
  licenses:
108
117
  - MIT
109
118
  metadata:
110
- allowed_push_host: https://rubygems.org
111
119
  changelog_uri: https://github.com/dry-rb/dry-auto_inject/blob/main/CHANGELOG.md
112
120
  source_code_uri: https://github.com/dry-rb/dry-auto_inject
113
121
  bug_tracker_uri: https://github.com/dry-rb/dry-auto_inject/issues
114
- post_install_message:
122
+ funding_uri: https://github.com/sponsors/hanami
123
+ default_lint_roller_plugin: RuboCop::DryAutoInject::Plugin
115
124
  rdoc_options: []
116
125
  require_paths:
117
126
  - lib
@@ -119,15 +128,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
119
128
  requirements:
120
129
  - - ">="
121
130
  - !ruby/object:Gem::Version
122
- version: 2.7.0
131
+ version: '3.3'
123
132
  required_rubygems_version: !ruby/object:Gem::Requirement
124
133
  requirements:
125
134
  - - ">="
126
135
  - !ruby/object:Gem::Version
127
136
  version: '0'
128
137
  requirements: []
129
- rubygems_version: 3.3.26
130
- signing_key:
138
+ rubygems_version: 3.6.9
131
139
  specification_version: 4
132
140
  summary: Container-agnostic automatic constructor injection
133
141
  test_files: []