The Complete Guide to Create a Copy of an Object in Ruby: Part II
In this article, we’re going to explore the following topics:
#deep_dupmethod in Rails
Have a quick look to the Part I (3 minutes read)
Ruby doesn’t handle deep copy by default.
Indeed, it only handles shallow copy through the
But what if we still need to make a deep copy of an object?
In this article, we’ll detail 3 ways to make a deep copy of an object in Ruby.
#initialize_copy hook can be used to interact with the freshly created copy of a given object.
We can tweak this hook to achieve a deep copy.
First, let’s recap how to achieve a shallow copy in Ruby
Here the problem is that
tony_fergusson share the same belt —
UFC Lightweight Champion.
This means that if
khabib changes the
title.name attribute then the change will be propagated to the
tony_fergusson.title.name one — as they share the same
So, let’s implement the
Fighter#initialize_copy to make a deep copy of
In the above example, we define a
Fighter#initialize_copy method that assign a shallow copy of the
So, if we compare the 2 titles
object_id we can see that it’s now 2 different instances of
It works! That’s cool !
But, the problem is that we “tweak” a method that is called in the context of a shallow copy (via
#clone) in order to process a deep copy.
Of course, it’s not a “serious” issue !
But let’s see what are the other alternatives.
Object Marshalling is the concept of formatting the memory representation of an object to make it suitable to storage & deserialisation.
Basically, for a given object:
- we serialise all its attribute values
- we store the serialisation (for example, in a file or a variable)
- we deserialise it at any time and get back a new instance of the serialised object with the same values.
In Ruby, the
Object Marshalling logic is mainly implemented in the
Let’s have a look to the following example to detail how this class works
In the above example, we can see that the
jd variable contains an instance of
Then we serialise this instance by using the
This method returns a string that is the serialisation of the memory representation of the
Finally, we deserialise this string by using the
This method returns a new instance of
User that contains the exact same values as
As we can see,
other_jd are two distinct instances of
Now, let’s see how to process a deep copy using
To do so, let’s refactor the
Title classes example
Here, we serialise the
khabib instance of
Fighter by using the
Then we deserialise it by using the
Marshal.load method and we store the result in the
As we can see, we achieve the same result as with the
In effect, the
title attribute of
tony_fergusson are two distinct instance of
There can be 2 main concerns to process a deep copy by using object marshalling:
- it can become a very slow operation at scale
- it’s not fully working with complex objects.
Otherwise, it’s a quite efficient way to achieve deep copy in Ruby.
Now let’s have a look to what Ruby on Rails proposes to handle deep copy.
Note that I will dig into
Object Marshallingin Ruby in another article.
#deep_dup method in Rails
The Ruby on Rails framework provides the
Object#deep_dup method that allows you to create a deep copy of a given object.
This solution is implemented in ~30 LOC
Object#duplicable? method returns
This means that an instance that contains the
Object class in its ancestor chain is eligible to deep copy.
There is only few classes that are not “duplicable”:
NilClass FalseClass TrueClass Symbol Numeric BigDecimal Method Complex Rational
Object#deep_dup method returns the return value of the
dup method call or
self if the object is not eligible to deep copy.
map through the calling array and calls
#deep_dup on each element of the array.
dup the calling hash and iterates through the calling hash.
Then, for each pair, it checks if the key is a frozen object.
If so, then only the
key and the
There is also a set of
#initialize_copy methods that are defined to handle complex objects (in
ONE MORE THING ⬇
Feel free to subscribe here: www.rubycademy.com
Thank you for taking the time to read this post :-)
Feel free to 👏 and share this article if it has been useful for you. 🚀
Here is a link to my last article: