Module: ArchSpec::ValueObject
- Defined in:
- lib/archspec/value_object.rb
Overview
A frozen value type with positional or keyword construction and #with, mirroring Ruby 3.2's Data.define. Deliberately hand-rolled: the gem supports Ruby 3.1, where Data does not exist. Do not replace this with Data until the required Ruby version reaches 3.2.
Class Method Summary collapse
Class Method Details
.define(*members, &block) ⇒ Object
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/archspec/value_object.rb', line 9 def self.define(*members, &block) klass = Struct.new(*members) do members.each { |member| undef_method :"#{member}=" } def initialize(*values, **keywords) member_names = self.class.members if keywords.any? raise ArgumentError, 'expected positional arguments or keyword arguments, not both' unless values.empty? unknown = keywords.keys - member_names raise ArgumentError, "unknown keyword: #{unknown.first.inspect}" if unknown.any? missing = member_names - keywords.keys raise ArgumentError, "missing keyword: #{missing.first.inspect}" if missing.any? values = member_names.map { |member| keywords.fetch(member) } elsif values.length != member_names.length raise ArgumentError, "wrong number of arguments (given #{values.length}, expected #{member_names.length})" end super(*values) freeze end def with(**changes) unknown = changes.keys - self.class.members raise ArgumentError, "unknown keyword: #{unknown.first.inspect}" if unknown.any? self.class.new(**to_h.merge(changes)) end end klass.class_eval(&block) if block klass end |