Using the Ruby block shortcut with arguments

Ruby has this handy block shortcut map(&:to_i) for map { |x| x.to_i }. However, it is limited to argument-less method invocations.

To call a method with an argument, you usually need to use the full block form. A common and annoying case is retrieving values from a list of hashes (imagine using a JSON API):

users = [ { name: 'Dominik', color: 'blue' }, { name: 'Stefan', color: 'red'} ]
names = users.collect do |user|
  user[:name]
end

If you're using Rails 5+, this example is covered by Enumerable#pluck (users.pluck(:name)). But with a little extension, you can have this for any method:

Block shortcut with arguments

By defining Symbol#with, you can start passing arguments to the short block form. (Ruby 2.7+ syntax)

class Symbol

  def with(...)
    ->(caller, *rest) {
      caller.send(self, *rest, ...)
    }
  end
  
end

Using Array#dig Show archive.org snapshot and Hash#dig Show archive.org snapshot , we can now do this:

users = [ { name: 'Dominik', color: 'blue' }, { name: 'Stefan', color: 'red'} ]
names = users.collect &:dig.with(:name)

Note that this example is very well suited for working with JSON, where you're usually collecting values at a certain path through the JSON data structure. This is what #dig was designed for. Example: projects.collect &:dig.with(:company, :tags, 0, :name) will call project[:company][:tags][0][:name] for each project and collect the results.


Credits to the linked StackOverflow question.

Dominik Schöler Almost 6 years ago