6

Why would I want to add anything to a class using class << Class syntax?

class Fun
    def much_fun
        # some code here
    end
end

class << Fun # the difference is here!
    def much_more_fun
        # some code here
    end
end

instead of using the monkey patching/duck punching method:

class Fun
    def much_fun
        # some code here
    end
end

class Fun # the difference is here!
    def much_more_fun
        # some code here
    end
end

While reading Why's Poignant Guide to Ruby I came across:

Why defines a class LotteryDraw:

class LotteryDraw
    # some code here
    def LotteryDraw.buy( customer, *tickets )
        # some code here as well
    end
end

and after a while adds a method to the LotteryDraw class:

class << LotteryDraw
    def play
        # some code here
    end
end

saying that:

When you see class << obj, believe in your heart, I’m adding directly to the definition of obj.

What is the purpose of this syntax? Why did Why decide to do it this way instead of using the monkey patching method?


These are some related questions and websites:

Community
  • 1
  • 1
Mateusz Piotrowski
  • 8,029
  • 10
  • 53
  • 79

3 Answers3

4

A little more explanation is needed. In ruby almost every single object can create a weird thing called an instance class. This thing is just like a normal class, the main difference is that it has only one single instance and that that instance is created before that class.

In short, having class A, you can do:

a = A.new
b = A.new

Both, a and b are now the instances of class A and have an access to all the instance method defined within this class. Now let's say, we want to add an extra method which can only be accesses by a, but not by b (this might be useful in some cases, but rather avoid it if possible). All the methods are defined within the classes, so it seems we need to add it to class A, but this way it will be accessible by b as well. In this case we need to create an instance class for a object. To do that, we use class << <object> syntax:

class << a
  def foo
  end
end

a.foo #=> nil
b.foo #=> undefined method

In short, class << <object> opens an instance class for given object, allowing to define additional instance methods for given instance, which will not be available on any other objects.

Now, with that being said: in Ruby, classes are just instances of class Class. This:

class A
end

Is almost equivalent to (differences not important here)

A = Class.new

So if classes are objects, they can create their instance classes.

class << A
  def foo
  end
end

A.foo  #=> nil
Class.new.foo #=> undefined method

It is commonly used to add so-called class method to the given class, the point however is that in fact you are creating an instance method on class' instance class.

Jeff Widman
  • 22,014
  • 12
  • 72
  • 88
BroiSatse
  • 44,031
  • 8
  • 61
  • 86
  • 2
    BTW: both the ISO Ruby Language Specification and the core library call it "singleton class", e.g. there is the `Object#singleton_class` methods which returns the singleton class of an object. – Jörg W Mittag Oct 31 '15 at 19:59
3

The first defines an instance method:

class Fun
  def much_fun
    puts 'hello'
  end
end

fun = Fun.new
fun.much_fun
#=> "hello"

Fun.hello
#=> NoMethodError: undefined method `hello' for Fun:Class

Whereas the << version defines a class method:

class Fun
  class << self # or Fun
    def much_more_fun
      puts 'hello'
    end
  end
end

fun = Fun.new
fun.much_more_fun
#=> NoMethodError: undefined method `much_more_fun' for #<Fun:0x007fafdb9ff0a0>

Fun.much_more_fun
#=> "hello"

Note that the second version could also be written as:

class Fun
  def self.much_more_fun
    puts 'hello'
  end
end
spickermann
  • 100,941
  • 9
  • 101
  • 131
1

I think class << Class is used to class methods and not instance methods:

class Foo
  class << Foo 
   def bar
   end
  end
end

Foo.bar #Bar is class method. Ruby has various ways to define class methods,eg :

 class Foo
  class << self 
    def bar
      p 'hi'
    end
  end
  def Foo.age
    p '24'
  end
  def self.gender
    p 'male'
  end
end
Foo.instance_eval do
  def name
    p 'hello'
  end
end
Foo.define_singleton_method(:status) do
  p 'single'
end

Foo.bar  #> 'hi'
Foo.name  #> 'hello'
Foo.age   #> '24'
Foo.gender  #>male
Foo.status  #>single
owade
  • 306
  • 1
  • 3
  • 9