[dry-initializer] accessing rest key word arguments

I would like to migrate some of my View component to dry-initializer. A typical patter I have (largely inspired by Github primer view component is to declare a few named attributes and get a generic **kwargs that will collect all remaining arguments passed to the component

example :

def initialize(color: :primary, **kwargs)
    @color = color
    @kwargs = kwargs
end

is there a ways to do the same with dry intializer ? How can I access to extra kwargs provided ?

here is one solution that was suggested to me

require "dry-initializer"

class HelloWorld
  extend ::Dry::Initializer
    
  option :color, default: proc { :primary }
  
  attr_accessor :kwargs
      
  def initialize(*args, **kwargs)
    super(args, kwargs)
    
    @kwargs = kwargs.tap { self.class.dry_initializer.attributes(self).keys.each { |key| kwargs.delete(key)} } || {}
  end
  
  def print
    puts "@color: #{color}"
    puts "@kwargs: #{kwargs}"
  end
end

HelloWorld.new(color: :secondary, other: "yo").print

HelloWorld.new(kwarg: :yo).print
HelloWorld.new.print

This is not directly supported in the gem but you can accomplish it yourself

class Component
  extend Dry::Initializer

  option :foo
  option :bar
  option :kwargs

  def initialize(**options)
    defined = self.class.dry_initializer.options.map(&:target)
    kwargs  = options.except(*defined)
    options = options.except(*kwargs.keys)

    kwargs.merge!(options.delete(:kwargs)) if options.key?(:kwargs)

    super(kwargs:, **options)
  end
end
3 Likes