ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
From: djo.went@hotmail.com
To: ruby-core@ruby-lang.org
Subject: [ruby-core:91308] [Ruby trunk Feature#15563] #dig that throws an exception if an key doesn't exist
Date: Mon, 28 Jan 2019 17:06:05 +0000 (UTC)	[thread overview]
Message-ID: <redmine.journal-76554.20190128170603.003f52057fce27f2@ruby-lang.org> (raw)
In-Reply-To: redmine.issue-15563.20190125170732@ruby-lang.org

Issue #15563 has been updated by 3limin4t0r (Johan Wentholt).


My scenario would be the similar as described by k0kubun.

```Ruby
# The connection translates the request to JSON and parses the response
# from JSON into the correct objects. In this case a nested hash structure.
response = connection.send(request)

# assign shortcuts
report = response
         .fetch('Era.Common.NetworkMessage.ConsoleApi.Reports.RpcGenerateReportResponse')
         .fetch('report')

column_data   = report.fetch('data').fetch('columns')
column_labels = report.fetch('rendering').fetch('table').fetch('columns')

# build report
report_data = column_data.each_with_object({}) do |column, data|
  column_id       = column.fetch('header').fetch('column_id')
  data[column_id] = column.fetch('values')
end

report = column_labels.each_with_object({}) do |column, data|
  label       = column.fetch('label').fetch('literal')
  column_id   = column.fetch('column_id')
  data[label] = report_data.fetch(column_id)
end
```

From the above scenario you can see that having this new functionality would help out a lot.

The reason I use `#fetch` here is because the API to which I'm talking might change it's structure. Getting an error as soon as possible reduces debug time, since the code won't move on with a `nil` value (that probably raises an exception somewhere later).

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

* 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 DigWithException
  def dig_e(key, *others)
    value = fetch(key)
    return value if value.nil? || 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(DigWithException)
Hash.include(DigWithException)
```

The exception raised is also taken from `#dig` (`[1].dig(0, 1) #=> TypeError: Integer does not have #dig method`). I personally have my issues with the name `#dig_e`, but I haven't found a better name yet.

There are also a few other things that I haven't thought out yet.

 1. Should this method be able to accept a block which, will be passed to the `#fetch` call and recursive `#dig_e` calls?  

    ```Ruby
    module DigWithException
      def dig_e(key, *others, &block)
        value = fetch(key, &block)
        return value if value.nil? || others.empty?

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

    Array.include(DigWithException)
    Hash.include(DigWithException)
    ```

 2. I currently kept the code compatible with the `#dig` description.

    > Extracts the nested value specified by the sequence of *key* objects by calling `dig` at each step, returning `nil` if any intermediate step is `nil`.

    However, with this new version of the method one could consider dropping the *"returning `nil` if any intermediate step is `nil`"* part, since this would be the more strict version.

    ```Ruby
    module DigWithException
      def dig_e(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(DigWithException)
    Hash.include(DigWithException)
    ```

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



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

  parent reply	other threads:[~2019-01-28 17:06 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <redmine.issue-15563.20190125170732@ruby-lang.org>
2019-01-25 17:07 ` [ruby-core:91265] [Ruby trunk Feature#15563] #dig that throws an exception if an key doesn't exist djo.went
2019-01-25 17:48 ` [ruby-core:91266] " djo.went
2019-01-25 19:35 ` [ruby-core:91267] " shevegen
2019-01-26 13:14 ` [ruby-core:91284] " Ruby-Lang
2019-01-26 13:54 ` [ruby-core:91285] " matz
2019-01-26 14:35 ` [ruby-core:91286] " takashikkbn
2019-01-28 17:06 ` djo.went [this message]
2019-03-12 21:56 ` [ruby-core:91795] " walerian.sobczak
2019-06-13  8:08 ` [ruby-core:93096] " knu
2019-11-18 13:08 ` [ruby-core:95876] [Ruby master " djo.went
2019-11-18 13:16 ` [ruby-core:95877] " djo.went
2019-12-01  8:43 ` [ruby-core:96044] " ariel.caplan

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.ruby-lang.org/en/community/mailing-lists/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=redmine.journal-76554.20190128170603.003f52057fce27f2@ruby-lang.org \
    --to=ruby-core@ruby-lang.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).