You can work around missing key error by making an object that responds to deconstruct_keys
:
hash = {a: 1}
H(hash) => x:,y:,a:
x # => nil
y # => nil
a # => 1
H(hash, "default") => x:,y:,a:
x # => "default"
y # => "default"
a # => 1
H(hash, x: :z) => x:,y:,a:
x # => :z
y # => nil
a # => 1
H(hash, "default", x: :z) => x:,y:,a:
x # => :z
y # => "default"
a # => 1
deconstruct_keys
is called when hash pattern is used => x:,y:,a:
, these keys are passed as argument:
# DelegateClass to make H be like a hash, but pattern match differently.
# (it works just fine without DelegateClass)
#
# H.new({a: 1}) # => {:a=>1}
#
class H < DelegateClass(Hash)
def initialize(hash, default = nil, **defaults)
# since hash needs to be duped anyway
@hash = defaults.merge hash # defaults for specific key
@default = default # default for any missing keys
super(@hash) # get delegation going
end
def deconstruct_keys keys
missing = keys - @hash.keys # find what's missing
missing.each { |k| @hash[k] = @default } # add it to hash with default value
@hash # return for pattern matching
end
end
# H() method, like a Hash() method.
def H(...)
H.new(...)
end
https://docs.ruby-lang.org/en/3.2/syntax/pattern_matching_rdoc.html#label-Matching+non-primitive+objects-3A+deconstruct+and+deconstruct_keys