Can someone help explain the post_initialize callback to create a class (Sandi Metz)

I read Sandi Metz POODR and came across a coding principle that I don't quite understand. Here is the code:

class Bicycle attr_reader :size, :chain, :tire_size def initialize(args = {}) @size = args[:size] || 1 @chain = args[:chain] || 2 @tire_size = args[:tire_size] || 3 post_initialize(args) end end class MountainBike < Bicycle attr_reader :front_shock, :rear_shock def post_initialize(args) @front_shock = args[:front_shock] @rear_shock = args[:rear_shock] end end mb = MountainBike.new(front_shock: 4, rear_shock: 5) puts mb.size puts mb.chain puts mb.tire_size puts mb.front_shock puts mb.rear_shock 

This code outputs 1,2,3,4,5 for the corresponding attributes. What I do not understand is the search method.

When a mountain bike is created because it does not have its initialize method, it will move the method search chain to its superclass ( Bicycle ). But now it seems from there that the Bike then goes back to the post_initialize MountainBike method. Instead of continuing the chain of methods, how can it come back? Is post_initialize a ruby โ€‹โ€‹keyword, such as initialize , in which it performs some special function? Are there any other ruby โ€‹โ€‹introspection methods that I can use to see what happens?

+5
source share
2 answers

It is important to understand that in this code:

 def initialize(args = {}) # ... post_initialize(args) end 

... post_initialize has an implicit self receiver. In other words, post_initialize(args) is equivalent to โ€  to self.post_initialize(args) , and self is an instance of MountainBike. The method search always starts with the receiver class, โ€ก therefore there is no problem finding MountainBike#post_initialize .

โ€  This is a lie; this is not equivalent when it comes to confidentiality; private methods cannot be called with an explicit receiver .
โ€ก This is also a lie; it actually starts with a single-level receiver class, but then it tries its own class.

+6
source

There is nothing special about the post_initialize method. This is just a simple instance method of a vanilla subclass.

In Ruby, an instance method of a superclass can invoke an instance method of a subclass, even in its constructor. Check out this irb session:

 2.3.0 :003 > class Base 2.3.0 :004?> def initialize 2.3.0 :005?> foo 2.3.0 :006?> end 2.3.0 :007?> end => :initialize 2.3.0 :015 > class Derived < Base 2.3.0 :016?> def foo 2.3.0 :017?> puts 'I am foo.' 2.3.0 :018?> end 2.3.0 :019?> end => :foo 2.3.0 :020 > Derived.new I am foo. 

This is usually done using the super subclass, but I think Sandi offers a post_initialize approach, requiring the subclass to provide its own initialization or formally refuse to do this by implementing an empty method. (In addition, subclass authors may forget to call super .) Here's how it would be done with super:

 2.3.0 :001 > class Base 2.3.0 :002?> def initialize 2.3.0 :003?> puts 'in base' 2.3.0 :004?> end 2.3.0 :005?> end => :initialize => #<Derived:0x007fda6ba291d8> 2.3.0 :012 > class Derived < Base 2.3.0 :013?> def initialize 2.3.0 :014?> super 2.3.0 :015?> puts 'in derived' 2.3.0 :016?> end 2.3.0 :017?> end => :initialize 2.3.0 :018 > Derived.new in base in derived => #<Derived:0x007fda6b104b98> 
+1
source

All Articles