[Proposal] `ActiveRecord composed_of` Support KeywordArgument Constructor

ValueObject whose constructor is a keyword argument.

class Address
 attr_reader :street, :city

 def initialize(street:, city:)
   @street, @city = street, city
 end

  # some methods...
end

To use this ValueObject with compsoed_of requires this redundant configuration.

class Customer < ActiveRecord::Base
  composed_of :address,
              mapping: [%w(address_street street), %w(address_city city)],
              constructor: Proc.new { |address_street, address_city| Address.new(street: address_street, city: address_city) }

For example, how about adding a new option keyword_init.

The option name is based on keyword_init (option) of Struct, but there is a possibility of mismatch.

class Customer < ActiveRecord::Base
  composed_of :address,
              mapping: [%w(address_street street), %w(address_city city)],
              keyword_init: true

The implementation will look like this.

def reader_method(name, class_name, mapping, allow_nil, constructor)
  define_method(name) do
    if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? { |key, _| !read_attribute(key).nil? })
      attrs = mapping.to_h { |entity_attr, value_object_attr| [value_object_attr.to_sym, read_attribute(entity_attr)] }
      object =
        if constructor.respond_to?(:call)
          constructor.call(*attrs.values)
        else
          if keyword_init
            class_name.constantize.send(constructor, **attrs)
          else
            class_name.constantize.send(constructor, *attrs.values)
          end
        end
      @aggregation_cache[name] = object
    end
    @aggregation_cache[name]
  end
end

What do you think?