lib/dry/auto_inject/strategies/kwargs.rb in dry-auto_inject-0.5.0 vs lib/dry/auto_inject/strategies/kwargs.rb in dry-auto_inject-0.6.0
- old
+ new
@@ -1,19 +1,20 @@
# frozen_string_literal: true
require 'dry/auto_inject/strategies/constructor'
+require 'dry/auto_inject/method_parameters'
module Dry
module AutoInject
class Strategies
# @api private
class Kwargs < Constructor
private
def define_new
class_mod.class_exec(container, dependency_map) do |container, dependency_map|
- map = dependency_map.to_h.to_a
+ map = dependency_map.to_h
define_method :new do |*args, **kwargs|
map.each do |name, identifier|
kwargs[name] ||= container[identifier]
end
@@ -22,62 +23,80 @@
end
end
end
def define_initialize(klass)
- super_method = Dry::AutoInject.super_method(klass, :initialize)
+ super_parameters = MethodParameters.of(klass, :initialize).each do |ps|
+ # Look upwards past `def foo(*)` methods until we get an explicit list of parameters
+ break ps unless ps.pass_through?
+ end
- if super_method.nil? || super_method.parameters.empty?
- define_initialize_with_keywords
+ if super_parameters.splat? || super_parameters.sequential_arguments?
+ define_initialize_with_splat(super_parameters)
else
- define_initialize_with_splat(super_method)
+ define_initialize_with_keywords(super_parameters)
end
self
end
- def define_initialize_with_keywords
- initialize_params = dependency_map.names.map { |name| "#{name}: nil" }.join(", ")
+ def define_initialize_with_keywords(super_parameters)
+ assign_dependencies = method(:assign_dependencies)
+ slice_kwargs = method(:slice_kwargs)
- instance_mod.class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def initialize(#{initialize_params})
- #{dependency_map.names.map { |name| "@#{name} = #{name} unless #{name}.nil? && instance_variable_defined?(:'@#{name}')" }.join("\n")}
- super()
- end
- RUBY
+ instance_mod.class_exec do
+ define_method :initialize do |**kwargs|
+ assign_dependencies.(kwargs, self)
- self
+ super_kwargs = slice_kwargs.(kwargs, super_parameters)
+
+ if super_kwargs.any?
+ super(super_kwargs)
+ else
+ super()
+ end
+ end
+ end
end
- def define_initialize_with_splat(super_method)
- super_kwarg_names = super_method.parameters.each_with_object([]) { |(type, name), names|
- names << name if [:key, :keyreq].include?(type)
- }
+ def define_initialize_with_splat(super_parameters)
+ assign_dependencies = method(:assign_dependencies)
+ slice_kwargs = method(:slice_kwargs)
- instance_mod.class_exec(dependency_map) do |dependency_map|
+ instance_mod.class_exec do
define_method :initialize do |*args, **kwargs|
- dependency_map.names.each do |name|
- # Assign instance variables, but only if the ivar is not
- # previously defined (this improves compatibility with objects
- # initialized in unconventional ways)
- instance_variable_set :"@#{name}", kwargs[name] unless kwargs[name].nil? && instance_variable_defined?(:"@#{name}")
- end
+ assign_dependencies.(kwargs, self)
- super_kwargs = kwargs.each_with_object({}) { |(key, _), hsh|
- if !dependency_map.names.include?(key) || super_kwarg_names.include?(key)
- hsh[key] = kwargs[key]
- end
- }
-
- if super_kwargs.any?
- super(*args, **super_kwargs)
+ if super_parameters.splat?
+ super(*args, kwargs)
else
- super(*args)
+ super_kwargs = slice_kwargs.(kwargs, super_parameters)
+
+ if super_kwargs.any?
+ super(*args, super_kwargs)
+ else
+ super(*args)
+ end
end
end
end
+ end
- self
+ def assign_dependencies(kwargs, destination)
+ dependency_map.names.each do |name|
+ # Assign instance variables, but only if the ivar is not
+ # previously defined (this improves compatibility with objects
+ # initialized in unconventional ways)
+ if kwargs.key?(name) || !destination.instance_variable_defined?(:"@#{name}")
+ destination.instance_variable_set :"@#{name}", kwargs[name]
+ end
+ end
+ end
+
+ def slice_kwargs(kwargs, super_parameters)
+ kwargs.select do |key|
+ !dependency_map.names.include?(key) || super_parameters.keyword?(key)
+ end
end
end
register_default :kwargs, Kwargs
end