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? Share on Mastodon