Skip to content

Instantly share code, notes, and snippets.

@avdi
Created June 28, 2011 19:28
Show Gist options
  • Select an option

  • Save avdi/1051967 to your computer and use it in GitHub Desktop.

Select an option

Save avdi/1051967 to your computer and use it in GitHub Desktop.

Revisions

  1. Avdi Grimm revised this gist Jun 28, 2011. 1 changed file with 9 additions and 2 deletions.
    11 changes: 9 additions & 2 deletions deep_fetch.rb
    Original file line number Diff line number Diff line change
    @@ -29,12 +29,19 @@ def [](*keys)

    end

    h = {:foo => {:bar => [:x, :y, :z]}}
    h = {
    :foo => {
    :bar => [:x, :y, :z],
    :baz => Hash.new{ "missing value in :bar" }
    }
    }
    h.extend(DeepFetch)
    h[:foo] # => {:bar=>[:x, :y, :z]}
    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
  2. Avdi Grimm revised this gist Jun 28, 2011. 1 changed file with 23 additions and 16 deletions.
    39 changes: 23 additions & 16 deletions deep_fetch.rb
    Original file line number Diff line number Diff line change
    @@ -1,33 +1,40 @@
    module DeepFetch
    def deep_fetch(*keys, &fetch_default)
    # If we need the default, we need to stop processing the loop immediately
    throw_fetch_default = fetch_default && lambda {|*args|
    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|
    value.fetch(key, &throw_fetch_default)
    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){_df_default_}
    else deep_fetch(*keys){|key, coll| coll[key]}
    end
    end

    # Don't want to step on any naming toes
    def _df_default_(&default_block)
    # Hashes have fancy defaulting logic
    value = if respond_to?(:default_proc) && default_proc
    default_proc.call
    elsif respond_to?(:default)
    default
    else
    nil
    end
    throw :df_value, value
    end
    end

    h = {:foo => {:bar => [:x, :y, :z]}}
    h.extend(DeepFetch)
    h[:foo] # => {:bar=>[:x, :y, :z]}
    h[:foo, :bar] # => [:x, :y, :z]
    h[:foo, :bar, 1] # => :y
    h[:foo, :bar, 5] # => nil
    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
  3. Avdi Grimm created this gist Jun 28, 2011.
    33 changes: 33 additions & 0 deletions deep_fetch.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,33 @@
    module DeepFetch
    def deep_fetch(*keys, &fetch_default)
    # If we need the default, we need to stop processing the loop immediately
    throw_fetch_default = fetch_default && lambda {|*args|
    throw :df_value, fetch_default.call(*args)
    }
    catch(:df_value){
    keys.inject(self){|value,key|
    value.fetch(key, &throw_fetch_default)
    }
    }
    end

    def [](*keys)
    case keys.size
    when 1 then super
    else deep_fetch(*keys){_df_default_}
    end
    end

    # Don't want to step on any naming toes
    def _df_default_(&default_block)
    # Hashes have fancy defaulting logic
    value = if respond_to?(:default_proc) && default_proc
    default_proc.call
    elsif respond_to?(:default)
    default
    else
    nil
    end
    throw :df_value, value
    end
    end