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("https://github.com/repos/:owner/:repo/pulls")
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 most important step in the customer journey: the phone call.

Engineering Team

Written by

Invoca Engineering Blog

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

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade