Hash#dig is fun, but sometimes I want to be strict … so I use dig_fetch … and dig_set to set a value without running into nil / MethodMissing errors.
# before
hash.dig(:a, :b, :c) # any of these could be nil ...
hash.fetch(:a).fetch(:b).fetch(:a) # repetitive and only shows last key
# after
hash.dig_fetch(:a, :b, :a)
Hash.class_eval do
def dig_fetch(*keys, last, &block)
block ||= ->(*) { raise KeyError, "key not found: #{(keys << last).inspect}" }
before = (keys.any? ? dig(*keys) || {} : self)
before.fetch(last, &block)
end
def dig_set(keys, value)
raise ArgumentError, "No key given" if keys.empty?
keys = keys.dup
last = keys.pop
failed = ->(*) { raise KeyError, "key not found: #{(keys << last).inspect}" }
nested = keys.inject(self) { |h, k| h.fetch(k, &failed) }
nested[last] = value
end
end