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. https://gist.github.com/tmimura39/1ab5303f86cba4682c411cc30755e10d#file-keyword_argument_composed_of-rb-L245-L263
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?