Jeff DeanJeff Dean
Hash#fetch for fun and profit
edit Posted by Jeff Dean on Saturday May 08, 2010 at 10:18PM

I have an app where I set the alt tag of images with javascript. To reduce the size of the page, I want to emit img tags with no alt attribute, however that is not currently possible:

image_tag "foo.png", :alt => nil # => <img alt="Foo" src="/images/foo.png" />

I agree that images should have alt tags, and I agree that the Rails default is sensible. However, if I explicitly send :alt => nil, I expect it to omit the alt attribute altogether, to be consistent with :class and other html attributes.

This happens because under the hood the code looks like this:

options[:alt]     ||= File.basename(src, '.*').capitalize

See the issue? If options[:alt] is falsey, the default is used. Since nil is falsey, there's no way to get options[:alt] to be nil. My requirement is:

  • If I pass an :alt attribute (even if it's nil), use the passed in value
  • If I pass no :alt attribute, use the default

Making this happen is very simple with the marvelous Hash#fetch:

UPDATE: Thanks to Brennan Falkner and Mark-Andre Lafortune for pointing out the block syntax of fetch is the preferred way.

options[:alt] = options.fetch(:alt) { File.basename(src, '.*').capitalize }

Instead of evaluating the truthiness of the :alt value, fetch checks for the existance of the :alt key. For those of you who are interested in getting this into rails core, apply the patch and give a +1 to the related Lighthouse Ticket.

Comments

  1. Brennan Falkner Brennan Falkner on May 10, 2010 at 10:20AM

    One of the advantages of the first form is that the expression won't be evaluated if :alt is set. You may want to allow.. options[:alt] = options.fetch(:alt) {File.basename(src, '.*').capitalize)}

  2. Jeff Dean Jeff Dean on May 10, 2010 at 11:48AM

    Brennan - thank you. I've updated the post.