Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Deep Fetch
module DeepFetch
def deep_fetch(*keys, &fetch_default)
throw_fetch_default = fetch_default && lambda {|key, coll|
args = [key, coll]
# only provide extra block args if requested
args = args.slice(0, fetch_default.arity) if fetch_default.arity >= 0
# If we need the default, we need to stop processing the loop immediately
throw :df_value, fetch_default.call(*args)
}
catch(:df_value){
keys.inject(self){|value,key|
block = throw_fetch_default && lambda{|*args|
# sneak the current collection in as an extra block arg
args << value
throw_fetch_default.call(*args)
}
value.fetch(key, &block)
}
}
end
# Overload [] to work with multiple keys
def [](*keys)
case keys.size
when 1 then super
else deep_fetch(*keys){|key, coll| coll[key]}
end
end
end
h = {
:foo => {
:bar => [:x, :y, :z],
:baz => Hash.new{ "missing value in :bar" }
}
}
h.extend(DeepFetch)
h[:foo] # => {:baz=>{}, :bar=>[:x, :y, :z]}
h[:foo, :bar] # => [:x, :y, :z]
h[:foo, :bar, 1] # => :y
h[:foo, :bar, 5] # => nil
h[:foo, :baz] # => {}
h[:foo, :baz, :fuz] # => "missing value in :bar"
h.deep_fetch(:foo, :bar, 0) # => :x
h.deep_fetch(:buz) { :default_value } # => :default_value
h.deep_fetch(:foo, :bar, 5) { :default_value } # => :default_value
@sysbot

This comment has been minimized.

Copy link

@sysbot sysbot commented Oct 28, 2015

I would like to include this in as an Hash extension for Chef ruby. Is it fine if I create an PR with this code at [1]. I would include a link back to here if I do. Otherwise, you can do it too if you like or care, I just like to see it being available in Chef.

thanks,

[1] https://github.com/sethvargo/chef-sugar/tree/master/lib/chef/sugar/core_extensions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment