This article was originally published on Build a SaaS with Rails from Rails Designer
Let's be honest: comparison operators like <, >, <=, and >= work perfectly fine, but they're not so readable (even more so when you're scanning through code quickly or working with complex conditionals).
What if instead of writing seats < 10 you could write seats.below? 10? Or usage.at_least? limit instead of usage >= limit? Much clearer, right?
This is where refinements come in handy (in short: add methods to existing classes without the risks of monkey patching).
Here's a simple refinement module that I copy between my apps to make integer comparisons more expressive:
# lib/comparable_integer.rb (reusable modules and classes are stored in `lib/`)
module ComparableInteger
refine Integer do
def below?(other)
self < other
end
def above?(other)
self > other
end
def at_most?(other)
self <= other
end
def at_least?(other)
self >= other
end
end
end
Now imagine you're building subscription logic that checks seat limits:
class SubscriptionsController < ApplicationController
using ComparableInteger
def upgrade
if current_seats.below? requested_seats
charge_additional_seats
update_subscription
redirect_to subscription_path, notice: "Upgraded successfully"
else
redirect_to subscription_path, alert: "Cannot downgrade seats"
end
end
private
def current_seats = @subscription.seats
def requested_seats = params[:seats].to_i
end
The code reads almost like plain English. current_seats.below? requested_seats is immediately clear, while current_seats < requested_seats requires a mental translation.
Or another example when dealing with pricing tiers or feature limits:
class FeatureAccess
using ComparableInteger
def initialize(subscription)
@subscription = subscription
end
def can_access_advanced_analytics?
@subscription.amount_in_cents.at_least? 4999
end
def needs_team_plan?
@subscription.seats.above? 1
end
def qualifies_for_discount?
@subscription.months_active.at_least? 12
end
end
Much more readable than a sea of >= and > symbols, don't you think? Got more ideas for making Ruby code more expressive? Let me know in the comments below.
Top comments (0)