3 Ruby Design Patterns We Use Every Day

By Tom Holford on December 15, 2017

When Yukihiro “Matz” Matsumoto created Ruby, his guiding philosophy was to design a language that makes the programmer productive while still being fun to use. At Returnly, our Engineering team strives to write Ruby on Rails code that follows through on Matz’ vision. Here are three design patterns we use every day that enable us to be productive while having some fun along the way.

1. Adapter

One use case of this pattern is to expose a common interface between components in your codebase while abstracting away implementation details such as environment-specific behavior.

Here’s a simple implementation of an OrderCreator that supports both local and production use cases:

class OrderCreator
  def initialize(order_lines)
    @order_lines = order_lines
  end

  def create
    adapter.create(@order_lines)
  end

  private

  def adapter
    return OrderCreators::Fake if Rails.env.development?

    OrderCreators::Shopify
  end
end

 

When developing locally, this adapter allows us to iterate quickly by stubbing out API calls in our Fake adapter. And even better, we can extend this adapter to easily add support for additional e-commerce platforms:

  def adapter
    return OrderCreators::Shopify if platform.shopify?
    return OrderCreators::Magento if platform.magento?

    OrderCreators::Default
  end

 

2. Memoization

The memoization pattern allows us to run expensive queries or calculations once per session, and then reuse the results as many times as needed afterward.

Suppose you need to access the current user’s information multiple times in a session, you can memoize the data like so:

  def current_user
    @current_user ||= User.find(params[:user_id])
  end

 

The ||= operator (explained well here) is key to the memoization. The first call to the #current_user method will result in a database lookup, but each subsequent call will use the in-memory value.

 

3. Policy

The policy pattern allows for encapsulation of complex business logic and helps keep your controllers skinny.

For example, suppose an e-commerce merchant would like to offer a special Black Friday promotion, but only to shoppers who meet certain qualifications (in this case, first-time shoppers spending over $200):

class BlackFridayPromotionPolicy
  def initialize(user, order)
    @user = user
    @order = order
  end

  def eligible?
    @user.first_time_shopper? && @order.total_amount > 200
  end
end

 

Then in our controller:

class OrdersController < ApplicationController
  def create
    OrderCreator.new(@order_lines).create

    redirect_to :special_offer if shopper_is_eligible_for_promotion?    
  end

  private

  def shopper_is_eligible_for_promotion?
    BlackFridayPromotionPolicy.new(current_user, @order).eligible?
  end
end

 

Thanks for reading! Let us know what Ruby patterns you use in the comments below.

And if you're into the exciting world of e-commerce and engineering, visit our job openings here!

By Tom Holford

Tom is a senior software engineer at Returnly, building SaaS and Fintech products for merchants and shoppers. He is passionate about data-driven design and buttery smooth UX.