Issue #15563 has been updated by amcaplan (Ariel Caplan).


matz (Yukihiro Matsumoto) wrote:
> I am against `dig!` for this purpose. When we have two versions of a method (`foo` and `foo!`), the bang version should be more dangerous than the non-bang version. `dig!` is not the case.
> 
> And with whatever name, we need the real-world use-case for a new method. "We don't have `fetch` counterpart of `dig`" is not good enough.
> 
> Matz.

I think that I, along with others, have gotten used to bang methods being subject to more errors rather than mutating state, due to how ActiveSupport uses them. Ruby is not Rails, though, so this is a good reminder :)

I like `#deep_fetch`, which also happens to be a gem that already does this (https://rubygems.org/gems/deep_fetch). It was designed for implicitly validating that API formats have not changed, which says something about the need for this. I think having an easy way to `dig` while doing that constant validation would encourage writing code that notifies us right away when our assumptions are wrong.

As an alternative, maybe `#fetch_strict` or (though I like this less) a named parameter passed to `dig` like `my_array.dig(:foo, :bar, :baz, strict: true)`. The latter option currently isn't possible since it's unclear whether `strict: true` is a key or a named parameter, but that problem will go away in Ruby 3.

----------------------------------------
Feature #15563: #dig that throws an exception if an key doesn't exist
https://bugs.ruby-lang.org/issues/15563#change-82894

* Author: 3limin4t0r (Johan Wentholt)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 
----------------------------------------
Ruby 2.3.0 introduced `#dig` for *Array*, *Hash* and *Struct*. Both *Array* and *Hash* have `#fetch` which does the same as `#[]`, but instead of returning the default value an exception is raised (unless a second argument or block is given). *Hash* also has `#fetch_values` which does the same as `#values_at`, raising an exception if an key is missing. For `#dig` there is no such option.

My proposal is to add a method which does the same as `#dig`, but instead of using the `#[]` accessor it uses `#fetch`.

This method would look something like this:

```Ruby
module Traversable
  def traverse(key, *others)
    value = fetch(key)
    return value if others.empty?

    if value.respond_to?(__method__, true)
      value.send(__method__, *others)
    else
      raise TypeError, "#{value.class} does not have ##{__method__} method"
    end
  end
end

Array.include(Traversable)
Hash.include(Traversable)
```

The exception raised is taken from `#dig` (`[1].dig(0, 1) #=> TypeError: Integer does not have #dig method`).

```Ruby
yaml = YAML.load_file('some_file.yml')

# this change is meant to make chaining #fetch calls more easy
yaml.fetch('foo').fetch('bar').fetch('baz')

# would be replaced with
yaml.traverse('foo', 'bar', 'baz')
```

I'm curious to hear what you guys think about the idea as a whole and the method name.
 



-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>