One of my favorite Ruby snippets

This is one of my favorite Ruby snippets, which I wrote many years ago and tend to sneak into codebases:

class Object
  def if_apply(cond, &block)
    unless cond.is_a?(TrueClass) || cond.is_a?(FalseClass)
      cond = cond.present?
    end
    if cond
      block.call(self)
    else
      self
    end
  end
end

It's super-helpful for cleaning up long method chains when writing in a "pipelined" style. Imagine the following pseudo-Rails code:

class FooController < ApplicationController
  def index
    items = Item.all.order(id: :asc)
    if params[:only_squares]
      items = items.where(square: true)
    end
    if params[:owner].present?
      items = items.where(owner: params[:owner])
    end
    @items = items.paginate(page: params[:page], per_page: 25)
  end
end

This is really noisy. The word items is repeated everywhere. Can we make this look a little closer-to a point-free style? With the .if_apply helper, you can rewrite this as the following:

class FooController < ApplicationController
  def index
    @items = Item
      .all
      .if_apply(params[:only_squares]) { _1.where(square: true) }
      .if_apply(params[:owner]) { _1.where(owner: params[:owner]) }
      .order(id: :asc)
      .paginate(page: params[:page], per_page: 25)
  end
end

If you prefer to avoid the _1 everwhere, you can replace block.call(self) with instance_exec(&block) to avoid that argument... but then you can't reference class variables from the enclosing scope, and you run the risk of calling private methods in the subject, so I try to avoid instance_exec when possible.

Also, .if_apply is a terrible method name, but there are two hard problems in computer science: naming things, cache invalidation, and off-by-terminated by signal SIGSEGV (Address boundary error)...


Want to comment on this? How about we talk on Mastodon instead? mastodon logo Share on Mastodon