By

Instance methods

When you define a method on a class:

class Dog
  def bark
    'woof'
  end
end

You can use the method like this:

dog = Dog.new
dog.bark
=> "woof"

But not like this:

Dog.bark
# NoMethodError: undefined method `bark` for Dog:Class

How come we can only define methods on the class but only use them in the instance and not the class?

Last week, I wrote about Singleton Classes, and I showed how class methods and instance methods are two sides of the same coin, and how you can make Dog.bark work.

But what ARE methods, and why are they defined on Classes, and not on other kinds of Objects?

First, lets look at some methods Ruby provides to find out about methods on a class:

Dog.methods
 => [:new, :allocate, :superclass, :meow, :<=>, :module_exec, :<=, :>=, :==, :===, :include?, :name, :ancestors, :included_modules, :instance_methods, :public_instance_methods, :protected_instance_methods, :constants, :const_get, :private_instance_methods, :const_set, :class_variables, :const_defined?, :remove_class_variable, :class_variable_set, :class_variable_defined?, :class_variable_get, :public_constant, :private_constant, :deprecate_constant, :singleton_class?, :class_eval, :freeze, :inspect, :const_missing, :public_method_defined?, :class_exec, :prepend, :method_defined?, :<, :protected_method_defined?, :>, :private_method_defined?, :public_class_method, :private_class_method, :to_s, :autoload, :autoload?, :module_eval, :instance_method, :public_instance_method, :include, :tap, :public_send, :instance_variables, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :private_methods, :kind_of?, :is_a?, :instance_variable_get, :instance_of?, :define_singleton_method, :method, :extend, :public_method, :singleton_method, :to_enum, :enum_for, :=~, :!~, :eql?, :respond_to?, :display, :object_id, :send, :nil?, :hash, :class, :singleton_class, :clone, :dup, :itself, :taint, :tainted?, :untaint, :untrust, :trust, :untrusted?, :methods, :protected_methods, :frozen?, :public_methods, :singleton_methods, :!, :!=, :__send__, :equal?, :instance_eval, :instance_exec, :__id__]

Oops that was a lot of output. It turns out #methods accepts a parameter that when set to false, only returns the methods on the object.

Dog.methods(false)
 => []

Uh, okay :bark is not there. Well that makes sense since Dog is just an object that is an instance of Class. If we call #methods on an instance of it:

Dog.new.methods.select{|x| x == :bark}
 => [:bark]

Excellent. So it is one of the Dog’s methods.

If you are wondering why I did not use methods(false), scroll down to the You were probably asking… section

I can extract this method by doing:

Dog.new.method(:bark)
 => #<Method: Dog#bark>

And I can even call it!

m = Dog.new.method(:bark)
m.call
 => "woof"

Excellent. There is another method called #instance_methods that will give us the instance methods of a class:

Dog.instance_methods(false)
 => [:bark]

Similarly, I can extract the method this way:

Dog.instance_method(:bark)
 => #<UnboundMethod: Dog#bark>
m = Dog.instance_method(:bark)
m.call
# NoMethodError: undefined method `call` for #<UnboundMethod: Dog#bark>

Wait, I can’t call an UnboundMethod?

Unbound methods

What is an UnboundMethod anyway?

Well it turns out that all methods in Ruby need to be ‘bound’ to an object. In other words, every method in Ruby actually has a hidden self parameter:

# Like this, but not really.
def look_at(self, thing_to_look_at)
end

# You can't do the above either, since self is a keyword, you can't use it as a parameter name

Without this parameter, methods cannot be called. This is why (as shown above), an UnboundMethod has no #call method.

And the way you pass an argument to this self parameter is by using the #bind method on an UnboundMethod:

um = Dog.instance_method(:bark)
um.bind(Dog.new)
 => #<Method: Dog#bark>

Now we can call it again!

um = Dog.instance_method(:bark)
m = um.bind(Dog.new)
m.call
 => "woof"

Similarly you can call #unbind to get the UnboundMethod of any method:

m = Dog.new.method(:bark)
um = m.unbind
 => #<UnboundMethod: Dog#bark>

Does this mean I can transfer methods to other objects?

Well, no not really. Say you had something like this:

class NamedDog < Dog
  def initialize(name)
    @name = name
  end

  def bark
    "#{@name}: woof"
  end
end

# Lets say there were two dogs:
lady = NamedDog.new('lady')
tramp = NamedDog.new('tramp')

You can then grab the unbound method #bark and bind it to any other NamedDog.

NamedDog.instance_method(:bark).bind(lady).call
 => "lady: woof"

But you can’t bind it to anything else:

NamedDog.instance_method(:bark).bind(Object.new)
# TypeError: bind argument must be an instance of NamedDog

You can however, bind it to any object that inherits from NamedDog:

class Dalmatian < NamedDog
end

pongo = Dalmatian.new('pongo')

NamedDog.instance_method(:bark).bind(pongo).call
 => "pongo: woof"

You can also bind the old #bark method from Dog:

Dog.instance_method(:bark).bind(tramp).call
 => "woof"

You were probably asking…

Why did I do that big #select on methods, why can’t I just go Dog.new.methods(false)?

Dog.new.methods(false)
 => []

What, why? Why don’t we see :bark which we defined on Dog? Why did it appear in the call to #methods but not here?

Well, if you have read my post about assigning methods to objects, you would be aware that you can do this:

paco = Dog.new

def paco.complain
  'definitely not stoked'
end

paco.methods(false)
 => [:complain]

#methods only returns methods available to an object that are defined on their Singleton Class.

If you check the singleton class for instance methods, you will find :complain there:

paco.singleton_class.instance_methods(false)
 => [:complain]

Methods defined on Singleton Classes can only be bound to their instance (which makes sense since Singleton Classes are unique to a particular instance). So this does not work:

paco.singleton_class.instance_method(:complain).bind(lady)
# TypeError: singleton method called for a different object

Even though lady is a Dog, it is not paco, so a method defined on paco’s singleton class is not going to bind to lady.

Class Methods

This same mechanism is how Class Methods can be defined on one class and not another. (again, see my post about assigning methods to objects for more detail).

def Dog.kingdom
  'animal'
end

Dog.methods(false)
 => [:kingdom]

Dog.singleton_class.instance_methods(false)
 => [:kingdom]

k = Dog.singleton_class.instance_method(:kingdom)
 => #<UnboundMethod: #<Class:Dog>#kingdom>

k.bind(Object)
# TypeError: singleton method called for a different object

BUT, You can bind a Dog’s instance method to NamedDog, since it is a child class of Dog.

k = Dog.singleton_class.instance_method(:kingdom)
 => #<UnboundMethod: #<Class:Dog>#kingdom>

m = k.bind(NamedDog)
 => #<Method: NamedDog(Dog).kingdom>

m.call
 => "animal"

This is possible because the singleton class of NamedDog is the child of the singleton class of Dog. So the same rules that apply to classes apply here.

NamedDog.singleton_class.ancestors
 => [
   #<Class:NamedDog>, 
   #<Class:Dog>, 
   #<Class:Object>, 
   #<Class:BasicObject>, 
   Class, 
   Module, 
   Object, 
   Kernel, 
   BasicObject]

So to answer the question

The reason methods are defined on classes in Ruby is because of how methods are generally expected to be used in Ruby. You would declare a class that has a bunch of methods, and instantiate a bunch of objects that use those methods.

class SomeClass
  def some_method()
    self.class.name
  end
end

The alternative to this would be to copy the routine to each instance when a class is instatiated.

Extravagance aside, this would remove the need for singleton classes, but it would be impossible to modify the behavior of all instances of a class at once by modifying the class, which is another great feature of Ruby.

# You can redefine methods right after you have defined them
# existing instances will change behaviour too
class Dog
  def bark
    "wewf"
  end
end

shiitake = Dog.new
shiitake.bark
 => "wewf"

class Dog
  def bark
    "wan"
  end
end

shiitake.bark
 => "wan"

Hopefully this post has gone some way to explain the nature of methods and explains Ruby singleton classes and classes even more clearly, and would help you understand better how metaprogramming in Ruby is done.

Its difficult to remember so many code snippets

Picture of the Ruby Cheatsheet cover

So we compiled a small booklet to hand out at RubyKaigi 2019, that contains lots of small examples of using Ruby and its less obvious features. We’re giving them out for free too because we are nice like that and hopefully everyone will find it useful!

We will be at the fifth floor of the conference so please come over and have a chat with us!

Looking for a Rails developer
Degica is looking for a Rails developer to work with our dev team. Checkout the link for more information.