Ruby 2 - Refinements
- 28 August 2016
- Ruby
- Ruby 2 - Refinements
Refinements has been added to Ruby to replace popular, but bad approach called Monkey Patching.
Let's check what Monkey Patching is and why it is bad.
For example, we have a class User
, which has method to_s
:
class User
attr_reader :name
def initialize(name)
@name = name
end
def to_s
"Name: #{name}"
end
end
u = User.new("John")
puts u # => Name: John
Everything works fine, but at some moment your teammate decides to make something like that in other part of your application:
class User
def to_s
"User name is #{name}"
end
end
Yes, Ruby is very flexible, so it allows you to change class at any place of your application. Since method to_s
has been overwritten, result of execution will be different:
u = User.new("John")
puts u # => User name is John
It's good if you have a small team and you can keep an eye on such changes. But it's getting even worse when such problem happens with gems you use. You use gem which expected to behave one way, but because of Monkey Patching it changes behaviour of classes unexpectedly.
I don't recommend using Monkey Patching. But if you have to change behaviour of method only in couple places of your app - it's much better to use Refinements.
Let's say that in all cases when we use User
as a string, we should get something like: "User name is John", but in couple cases we need: "Name: John".
Let's add main to_s
implementation to User
class:
class User
attr_reader :name
def initialize(name)
@name = name
end
def to_s
"User name is #{name}"
end
end
And for custom cases we just create a Module with refinement:
module RefinedUser
refine User do
def to_s
"Name: #{name}"
end
end
end
Having this refinement, we can use it in cases where we need to get different behaviour of to_s
:
class UserDecorator
using RefinedUser
def self.decorated_user
puts User.new("John").to_s
end
end
UserDecorator.decorated_user # => Name: John
User
class will have custom implementation of to_s
method just in scope of UserDecorator
class, because of using RefinedUser
refinement. In all other places main implementation of User#to_s
will be used.
This example is very basic, but shows the main idea of refinements.
All custom implementation of existing methods can be pulled out into Refinements. And you can use it in places where you need it. So you can get rid from Monkey Patching because Refinements is a much safer approach.
No more Monkey Patching!