Plough => Ruby Journey through ruby

Ruby object model

Right, so you know Ruby’s object model. Well here’s a little code snippet by JRuby creator Ola Bini and it drives the point home really.

class Obj
  def something
    puts "calling simple"
    @abc = 3*42
    def something
      puts "calling memoized"
      @abc
    end
    something
  end
end

o = Obj.new
o.something
o.something
o.something

The output of the code is:

calling simple
calling memoized
calling memoized
calling memoized

Let us look at the code and anaylise it. ‘o’ is an instance of ‘Obj’ class. ‘something’ is an instance method of ‘O’, therefore it can access it. The first ‘o.something’ would call the instance method defined in ‘Obj’ class. So, it prints “calling simple” and then the second ‘something’ defines a method on instance ‘o’ because inside the first ‘something’ self is same as instance ‘o’.

Now, what do you get when you do something like this:

def o.something
     puts "I am o's singleton method."
end

Yes, you get a singleton method on the instance. In this case, that’s exactly what happens. The second ‘something’ defines a singleton method on instance ‘o’. We are still in the middle of executing the first something. The last line of the first something (or the outer something) calls the method ‘something’. What the hell? Cyclic messaging!!!! Never ending loop. Yeah, but the code runs and exits gracefully.

Well, the call to ‘something’ would actually go and look for ‘something’ method and before it finds a ‘something’ method defined in the class ‘Obj’ it would find the singleton-method ‘something’ and execute that. So, what would that print. Yeah, you are right it prints “calling memoized”. The second and third calls to something will again encounter the ‘something’ singleton method and execute that and it would not bother to go and look for the something defined as an instance method in class ‘Obj’.

I know you are confused. Why would it go and look for the singleton method and not the instance method. This is the magic behind Ruby’s object model. Please head over to Ola Bini’s blog and read the article on metaprogramming. He goes into a lot more detail.

define_method, instance_eval and class_eval

Right, so this is a tad confusing and the reason I am explaining is to ensure that I understand it properly.

instance_eval - It creates singleton methods on the object it is invoked on. If it is invoked on a Class object then these methods become singleton methods of the class. Some people would call them “class methods” but these are simply methods in an object’s singleton class. Here’s the code:

SomeObject.instance_eval (def singleton_method; "singleton_method";end)

class_eval - It creates an instance method on the object. Simple there is nothing confusing about this. Here’s an example:

SomeObject.class_eval(def instance_method;"instance_method";end)

Now, define method when used inside either class_eval or instance_eval s immune to the usual behaviour.

Why, you may ask?

Because the documentation says so. As per the documentation “define method - Defines an instance method in the receiver”. Therefore, it does not matter if you use define_method inside class_eval or instance_eval it would always create an instance method. Now, the question to ask is - “Is there no way to define a singleton method using defne_method?”. Well, I still need to investigate that. It would probably be in an update to this post. Hope it all makes sense. Ruby metaprogramming is awesome only if you know what you are doing and to get to that stage one needs to spend countless hours metaprogramming.

Ruby online courses

Anyone interested in learning ruby then head over to RubyLearning blog with plethora of online courses. Some of them are free and some charge a very nominal price. I am currently scheduled to take Ruby Shoes course but there are others like: Ruby Core, ruby metaprogramming. I am enrolled and currently studying the ruby meta-programming course and it is fabulous. I paid $5 for it but it is compared to what you will achieve by the time you get to the other side. So, do not delay any further.

Ruby idioms

You can write a complete ruby script as a string and run it. Ruby (rather MRI) would just evalutae the string by executing it. For example:

"#{def x(s); puts(s.reverse); end; ('1'..'3').each{|aStr| x(aStr)} }"

This is absolutely valid.