Can we make nil[:a][:b] simply return nil?

48 views
Skip to first unread message

Alessio Signorini

unread,
Jul 27, 2014, 12:14:24 AM7/27/14
to rubyonra...@googlegroups.com
Hi guys,

trying to do something like obj[:id] when obj is nil, returns an error.

Things are even more complicated when you try to work with nested hashes. To access something like hash[:data][:details][:id] you need to write

   if hash.has_key?(:data) && hash[:data].has_key?(:details)
      return hash[:data][:details][:id]
   end

In my code I monkey-patched the NilClass so that the nested accesses always return nil if they do not exists instead of throwing an error. It simplified a lot my code, which is now way cleaner. The patch was something like

   class NilClass
     def [](*keys)
       nil
     end
   end

I thought I would bring this up to your attention. It may be worth introducing it in Rails as default.

Steve Klabnik

unread,
Jul 27, 2014, 1:48:55 PM7/27/14
to rubyonra...@googlegroups.com
This has a very large potential to break a very, very large amount of code.

Daniel Evans

unread,
Jul 27, 2014, 1:52:28 PM7/27/14
to rubyonra...@googlegroups.com
You can use fetches or default block to clean up that code without doing a dangerous monkey-patch like you are suggesting.

Consider:
hash.fetch(:data, {}).fetch(:details, {})[:id]


On Sun, Jul 27, 2014 at 11:48 AM, Steve Klabnik <st...@steveklabnik.com> wrote:
This has a very large potential to break a very, very large amount of code.

--
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rubyonrails-co...@googlegroups.com.
To post to this group, send email to rubyonra...@googlegroups.com.
Visit this group at http://groups.google.com/group/rubyonrails-core.
For more options, visit https://groups.google.com/d/optout.



--
Daniel Evans

Sergio Campamá

unread,
Jul 27, 2014, 1:53:05 PM7/27/14
to rubyonra...@googlegroups.com
I think a better approach is

return hash && hash[:data] && hash[:data][:details] &&
hash[:data][:details][:id]

That will return the value or nil if the chain was broken at any
point. I know it's not the same, but much less code than the example.
--------------------------------------
Sergio Campamá
sergio...@gmail.com



On Sun, Jul 27, 2014 at 10:48 AM, Steve Klabnik <st...@steveklabnik.com> wrote:
> This has a very large potential to break a very, very large amount of code.
>

James Coleman

unread,
Jul 27, 2014, 4:02:41 PM7/27/14
to rubyonra...@googlegroups.com
The andand gem will allow you to write code like:

hash.andand[:key].andand[:key2]

where anything on a nil object returns a proxy that accepts any method and returns nil. This is far safer than what you propose as it won't affect anyone.

In general though, while this may seem convenient, it makes reasoning about code significantly more difficult. The more code you write, the more I believe that you find that we need *more* explicit handling of nil not less.

Rodrigo Rosenfeld Rosas

unread,
Jul 27, 2014, 4:54:15 PM7/27/14
to rubyonra...@googlegroups.com

Take a look at the Configatron gem.

Pasha Muravyev

unread,
Jul 27, 2014, 9:31:38 PM7/27/14
to rubyonra...@googlegroups.com

Try works pretty well for this.

hash.try(:[], :data).try(:[], :details).try(:[], :id)

Or

hash[:data][:details][:id] rescue nil

Jan Lelis

unread,
Jul 27, 2014, 9:31:41 PM7/27/14
to rubyonra...@googlegroups.com
I agree, fetch is the way to go. It is more verbose, but does not any magic/core extensions.

If you want two have more magic, consider using the andand gem <https://github.com/raganwald/andand> or the egonil refinement <http://rubyzucker.info/#egonil>


On 27.07.2014 19:52, Daniel Evans wrote:
Reply all
Reply to author
Forward
0 new messages