From: robb+ruby@... Date: 2020-02-17T01:05:08+00:00 Subject: [ruby-core:97175] [Ruby master Feature#15563] #dig that throws an exception if an key doesn't exist Issue #15563 has been updated by robb (Robb Shecter). amcaplan (Ariel Caplan) wrote in #note-13: > matz (Yukihiro Matsumoto) wrote: > > [...] 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. > > [...] implicitly validating that API formats have not changed, which says something about the need for this... > I use chained `#fetch`s all the same when importing JSON: My code needs the JSON to have the correct attributes and structure, and when it's not appropriate to explicitly check and handle every potential missing attribute. IMO Ruby makes it hard to do the safe thing. From my production app: ``` # @param from_json [Hash] # @return [nil] def import_objects(from_json:) import_chapter from_json.fetch('statuteTree').fetch('chapter0') import_titles from_json.fetch('statuteTree').fetch('titles') end # ... # 3. Decide: Are my children Sections or Sub-chapters? Then, # import them. child_nodes = chapter_json.fetch('content').fetch('contents') # ... # @param nrs_json [Hash] # @return [Boolean] def simple_subchapter_content?(nrs_json) nrs_json.fetch('children').fetch('tag') == 'SubChapterSections' end # @param nrs_json [Hash] # @return [Boolean] def simple_chapter_content?(nrs_json) nrs_json.dig!('content').fetch('tag') == 'SimpleChapterContent' end ``` I wrote this gem for chained `#fetch`s on Hashes and Arrays and has a very clean one-line implementation using `#reduce`: https://github.com/dogweather/digbang to make it easier me for the do the above. ---------------------------------------- Feature #15563: #dig that throws an exception if an key doesn't exist https://bugs.ruby-lang.org/issues/15563#change-84281 * Author: 3limin4t0r (Johan Wentholt) * Status: Open * Priority: Normal ---------------------------------------- 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: