Ruby: What I Would Have Loved To Know When I First Knew It

Motivation Every time I’m learning a new language, the first thing I try to learn is how this new language …

Motivation

Every time I’m learning a new language, the first thing I try to learn is how this new language
implements OOP, and now it’s Ruby’s turn. Before joining the company, I had been playing with Java for some
years; then I first heard about Ruby being a fully OOP language I got really excited, and
when I got my hands-on some existing Ruby code, things got confusing.

I would’ve liked to know all of this before looking into any code:

First things first: Objects

Things that we must know:

  • An object is formed by: State + Behavior = Object
  • Everything in Ruby is an object, even nil which is NilClass, false FalseClass, true TrueClass and modulesruby-1.8.7-p299 > “some”.class
    => String
    ruby-1.8.7-p299 > “some”.class.superclass
    => Object
    ruby-1.8.7-p299 > “some”.class.superclass.superclass
    => nil
    ruby-1.8.7-p299 > “some”.class.superclass.superclass.class
    => NilClass
    ruby-1.8.7-p299 > false.class
    => FalseClass
    ruby-1.8.7-p299 > true.class
    => TrueClass
    ruby-1.8.7-p299 > module B end
    => nil
    ruby-1.8.7-p299 > B.class
    => Module
    ruby-1.8.7-p299 > B.class.superclass
    => Object
  • Do you remember those so-called ‘static classes’ in other languages? They’re objects too,
    and when I say objects, I really mean it. Consider the following code stored in test.rb file:class A
    def self.outside
    self
    enddef inside
    self
    end
    end

Let’s play with it using irb:
ruby-1.8.7-p299 > require ‘./test’
ruby-1.8.7-p299 > A.outside
=> A
ruby-1.8.7-p299 > A.outside.class
=> Class
ruby-1.8.7-p299 > A.new.inside
=> #
ruby-1.8.7-p299 > A.new.inside.class
=> A

As you can see both are A objects whose type is Class

  • Those class names are constants containing references to objects:ruby-1.8.7-p299 > clazz = Class.new
    => #Class:0x7f8fdec83230
    ruby-1.8.7-p299 > clazz.class
    => Class
    ruby-1.8.7-p299 > Blabla = clazz
    => Blabla
    ruby-1.8.7-p299 > Blabla.class
    => Class
    ruby-1.8.7-p299 > Blabla.new
    => #Blabla:0x7f8fdec77a70

Self

When I first knew Ruby I thought “self” was the same as this (like in many other languages). Boy was I wrong. Let’s see what it really is:

Default receiver of method calls

Since everything in this type of code is an object, then all the function definitions we find established all around
and all those functions we call like, puts, def, etc are method calls. Some may not be exactly                                               defined inside a class but, in the end they will get either inherited or mixed in.

Look at the following example:
p “1. Current receiver is #{self}”
class A
p “2. Current receiver is #{self}”
def initialize
p “3. Current receiver is #{self}”
end
p “4. Current receiver is #{self}”
end
p “5. Current receiver is #{self}”

Running this script we see:
user@user-desktop:~$ ruby test.rb
“1. Current receiver is main”
“2. Current receiver is A”
“4. Current receiver is A”
“5. Current receiver is main”

Where instance variables are found

class A
  def initialize
    @a, @b = 2, 1
  end
end

p A.new.instance_variables

Running this script we see:
user@user-desktop:~$ ruby test.rb
[“@b”, “@a”]

Metaprogramming

This is why I fell in love with ruby, sadly it’s a huge topic which I won’t cover, even though it allows you
to write code that writes code lets see some examples:

Create a A Class, re-open it and see how it is the same object

class A
  def method1
    p "I'm method one"
  end
end

p A.instance_methods false
p A.object_id

class A
  def method2
    p "I'm method two"
  end
end

p A.instance_methods false
p A.object_id

Which throws:
user@user-desktop:~$ ruby test.rb
[“method1”]
70312385439840
[“method2”, “method1”]
70312385439840

As you can clearly see, the object id remains the same, after re-openning a class in order to add a second method, also,
we got all instance methods that one class of A would have had.

Call all instance methods of a certain class whenever you call a method whose name matches

class A
  def hello
    p "Hello"
  end 
  def bye 
    p "Bye"
  end 
  def welcome
    p 'Welcome'
  end 
  def method_missing(method_name, *args, &block)
    method_to_call = methods.find {|name| name =~ /#{method_name.to_s}/}
    send(method_to_call) if method_to_call
    p 'Unknown method' unless method_to_call
  end 
end

instance = A.new
instance.els
instance.hel
instance.by
instance.wel

Which throws:
user@user-desktop:~$ ruby test.rb
“Unknown method”
“Hello”
“Bye”
“Welcome”

That’s it. I hope this gives you a good overview and if you are ready for the whole package I
suggest you start with:

Know more about us!

Keep reading

More >