27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
# File 'lib/yarrow/schema/value.rb', line 27
def self.factory(*slots, **fields, &block)
if slots.empty? && fields.empty?
raise ArgumentError.new("missing attribute definition")
end
slots_spec, fields_spec = if fields.any?
raise ArgumentError.new("cannot use slots when field map is supplied") if slots.any?
[fields.keys, fields]
else
[slots, Hash[slots.map { |s| [s, :any]}]]
end
validator = Dictionary.new(fields_spec)
struct = ValueType.new(*slots_spec, keyword_init: true, &block)
struct.define_method :initialize do |*args, **kwargs|
attr_values = if args.any?
raise ArgumentError.new("cannot mix slots and kwargs") if kwargs.any?
if args.first.instance_of?(Hash) and args.count == 1
args.first
else
Hash[slots.zip(args)]
end
else
kwargs
end
converted_values = validator.cast(attr_values)
super(**converted_values)
freeze
end
struct.define_method(:merge) do |other|
unless other.is_a?(self.class)
raise ArgumentError.new("cannot merge incompatible values")
end
kwargs = validator.attr_names.reduce({}) do |data, attr_name|
current_val = self.send(attr_name)
other_val = other.send(attr_name)
data[attr_name] = if current_val.nil?
other_val
elsif other_val.nil?
current_val
else
other_val
end
data
end
struct.new(**kwargs)
end
struct
end
|