Understanding how Ruby initializes objects · Prathamesh Sonpatki
source link: https://www.tuicool.com/articles/RfmIbei
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Deep buried in Rails source code, there is a code snippet :
class DeprecationProxy def self.new(*args, &block) object = args.first return object unless object super end ... end
It was added 9 years ago and the commit message said:
Override new on proxy objects so that they never wrap nil or false.
The question is that how does this work? Because we know
that when an object is initialized using new
, Ruby executes the initialize
method.
class Greeting def initialize(message) puts "Hello there! #{message}" end end Greeting.new("Welcome to Ruby world.") => "Hello there! Welcome to Ruby world."
Notice that we have not actually defined new
method anywhere.
So how does this code from Rails work?
To see how things work under the hood, let’s introspect where the new
method is defined on the class.
Ruby provides a handy method which is named method
to see
where a method is defined.
>> Greeting.method(:new) => #<Method: Class#new>
We can see that the new
method actually comes from Class
.
But then how does the initialize
method gets called when we
call new
on a class?
Turns out, Ruby internally calls initialize
on the object
passing all the arguments that were passed to new
.
greeting = Greeting.new(message) => Calls Class.new(message) => Calls initialize on the object => which results in: => greeting.initialize(message)
As Ruby allows us to override any method, we can
override the new
class method as well.
class Greeting def self.new(args) puts args puts "New ends here." end def initialize(args) puts "I am getting initialized with #{args}" end end
Let’s see what is the output of creating an object of the Greeting class now.
Greeting.new({name: "Prathamesh", country: "India"}) >> Greeting.new({name: "Prathamesh", country: "India"}) {:name=>"Prathamesh", :country=>"India"} New ends here. => nil
As expected, Ruby happily calls the overridden new
method and no longer calls the initialize
method because
we did not call it from our definition of new
.
Now let’s look at the snippet from Rails that we discussed earlier:
class DeprecationProxy def self.new(*args, &block) object = args.first return object unless object super end ... end
It is clear that the new
method is overridden by the DeprecationProxy
class
and it returns same
object when the object has a falsey value.
return nil unless nil return false unless false
It should also be noted that this overridden new
method
calls super
on the last line which in turn calls Class#new
.
This makes sure that Ruby’s semantics of new
method are
preserved. This will make sure that DeprecationProxy.new
will call initialize
method of DeprecationProxy
after the
overridden new
method is executed.
Subscribe to mynewsletter to know more about how Ruby works by prying out Rails code.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK