Posted on • Originally published atrailsdesigner.com
Alternative to Ruby's Monkey Patching
This article was originally published onRails Designer's Build a SaaS with Ruby on Rails
One of the joys of working with Ruby is it allows you to write code almost however you want, one feature that allows you to this is “monkey patching”: modifying methods in any class at runtime. This is truly a sharp knife: you might know where and how you use it, but third-parties, like gems, are not aware of your patches.
In this article, I want to highlight a better way to it instead:refinements.
Refinements was introduced in Ruby 2.0. It allows you to refine a class or module locally. Let's see an example (from the Ruby docs):
classCdeffooputs"C#foo"endendmoduleMrefineCdodeffooputs"C#foo in M"endendend
Alright, you define a module (M
) and therefine the class you want (C
).
c=C.newc.foo# => prints "C#foo"usingMc=C.newc.foo# => prints "C#foo in M"
Let's look at some real life examples from my own apps. I put these refinements in thelib folder as that's where I add all classes/modules that can be copied from app to app without modifications.
# lib/hash_dot.rbmoduleHashDotrefineHashdodefto_dotJSON.parseto_json,object_class:OpenStructendendend
Then wherever you want to use it, addto_dot
to the hash:
classSubscriptionsController<ApplicationControllerusingHashDotdefcreateSubscription.create\name:data.plan_type,\# instead of data.dig(:plan_type)status:data.status# instead of data.dig(:status)endprivatedefdata{plan_type:"professional",status:"active",billing_cycle:"monthly",amount_in_cents:2999,features:["unlimited_storage","team_collaboration","api_access"],seats:5,next_billing_at:1740369856,trial_ends_at:nil,created_at:1732421070}.to_dotendend
Another example:
# lib/to_timestamp.rbmoduleToTimestamprefineIntegerdodefto_timestampTime.at(self)endendrefineNilClassdodefto_timestampnilendendend
The data in theSubscriptionsController uses UNIX timestamps (like Stripe does), let's use the refinement too, by callingto_timestamp
onnext_billing_at
:
classSubscriptionsController<ApplicationControllerusingHashDotusingToTimestampdefcreateSubscription.create\name:data.plan_type,\status:data.status,\next_billing_at:data.next_billing_at.to_timestampendprivatedefdata{plan_type:"professional",status:"active",billing_cycle:"monthly",amount_in_cents:2999,features:["unlimited_storage","team_collaboration","api_access"],seats:5,next_billing_at:1740369856,trial_ends_at:nil,created_at:1732421070}.to_dotendend
When callingto_timestamp
it will convert it usingTime.at
:
data.next_billing_at.to_timestamp# => 2025-02-24 12:00:00 +0000
And that are some examples of using refinements. Got more suggestions or use cases? Let me know below. 👇
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse