ruby_dig Gem Adds Hash#dig and Array#dig from Ruby 2.3 to Earlier Versions

Engineering Team
Nov 29, 2015 · 2 min read

Introducing `ruby_dig`

It may take us some time to upgrade to Ruby 2.3. But we’d like to be able to start using `dig` right away. The `ruby_dig` gem solves this by adding the `dig` method to `Array` and `Hash` just like Ruby 2.3+ has natively.

The gem can be found on ruby_gems and on github.

Why Do We Need `dig`?

With the ever-growing popularity of JSON-based APIs, we all find ourselves writing code to “dig” through a parsed JSON response of nested hashes and arrays. This can be error-prone and tedious, so Ruby 2.3 added the `dig` instance method to Array and Hash to simplify the process.

For a simple example, let’s take some code that uses Github’s API to get the assignee of the first Pull Request for a given repo:

uri = Uri.parse("")
pulls_response = Net::HTTP.get_response(uri)
pulls_response.code == 200 or raise "Got non-200 response code: #{response.inspect}"
pulls = JSON.parse(pulls_response.body)first_assignee = pulls[0]['assignee']['login']
But what if the response doesn't come back in the expected format? Any of the `[]` operators above might return `nil` and then next `[] would raise the dreaded—and nearly useless—Ruby exception:
NoMethodError: undefined method `[]' for nil:NilClassHere is the last line rewritten to use `dig` and to raise a useful exception if the format is unexpected:# first_assignee = pulls[0]['assignee']['login']
first_assignee = pulls.dig(0, 'assignee', 'login') or raise "Got unexpected response #{pulls.inspect}"

Implementation Notes

The dig method is implemented by calling `self.[]` so it will work with classes that derive from `Array` or `Hash`. Most notably, `ActiveSupport::HashWithIndifferentAccess`. Therefore `dig` will work fine in a Rails application looking in `params`:

params.dig(:user, :emails, 0, :friendly_name)
params.dig("user", "emails", 0, "friendly_name") # equivalent to the above

Also negative array indexes will work fine:

params.dig(:user, :emails, -1, :friendly_name) # find the last email friendly name

However, neither the Ruby 2.3 documentation nor the tests in the commit make it clear what should happen in `Array#dig` if you pass non-numeric index. This can happen easily, if the result you got had a hash where you expected an array. It seems in keeping with the spirit of `dig` that it should return `nil` in this case rather than raising an exception:

TypeError: no implicit conversion of Symbol into Integer

Invoca Engineering Blog

Invoca is a SaaS company helping marketers optimize for the…

Invoca Engineering Blog

Invoca is a SaaS company helping marketers optimize for the most important step in the customer journey: the phone call.