Exploring the Builder-to-Proc Pattern in Ruby
One of my favorite Ruby idioms is the Builder-to-Proc pattern. It takes advantage of Ruby’s ability to convert objects into callables through to_proc, letting you pass a class directly to map and have it act like a builder for each element.
Imagine you are working with raw data from a third-party API in JSON or CSV form. You want to transform each record into a proper domain object. Normally, you might reach for map with a block, but the Builder-to-Proc pattern gives you a clean alternative.
# JSON or CSV from a third party API
raw_users = [{email: 'x@x.com'}, {email: 'y@y.com'}, {email: 'z@z.com'}]
class BaseBuilder
attr_reader :params
def initialize(params)
@params = params
end
def build
raise(NotImplementedError)
end
def self.to_proc
proc { |params| new(params).build }
end
end
class UserBuilder < BaseBuilder
def build
User.new(params)
end
end
users = raw_users.map &UserBuilder
Here, BaseBuilder defines self.to_proc, which returns a Proc that knows how to take raw parameters, instantiate a builder, and call build. The UserBuilder subclass implements its own build method, returning a User.
When we call:
users = raw_users.map &UserBuilder
Ruby converts UserBuilder into a proc using to_proc, then applies it to each element in raw_users. The result is a clean and expressive way to transform raw data into proper objects.
This pattern really shines when you have multiple builders for different kinds of data, as it keeps your code compact, declarative, and easy to extend.