Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby: extend self

Tags:

ruby

It is a convenient way to make instance methods into class methods. But you can also use it as a more efficient singleton.


In a module, self is the module class itself. So for example

puts self

will return Rake so,

extend self

basically makes the instance methods defined in Rake available to it, so you can do

Rake.run_tests

For me it always helps to think of extend as include inside the singleton class (also known as meta or eigen class).

You probably know that methods defined inside the singleton class are basically class methods:

module A
  class << self
    def x
      puts 'x'
    end
  end
end

A.x #=> 'x'

Now that we know that, extend will include the methods in the module inside the singleton class and thus expose them as class methods:

module A
  class << self
    include A

    def x
      puts 'x'
    end
  end

  def y
    puts 'y'
  end
end

A.x #=> 'x'
A.y #=> 'y'

To avoid link rot, the blog post of Chris Wanstrath linked by user83510 is reposted below (with his permission). Still, nothing beats an original, so use his link for as long as it continues to work.


→ singin' singletons 18 November 2008 There’s stuff I just don’t understand. David Bowie, for instance. Or the Southern Hemisphere. But nothing quite boggles my mind like Ruby’s Singleton. Because really, it’s totally unnecessary.

Here’s what they want you to do with your code:

require 'net/http'

# first you setup your singleton
class Cheat
  include Singleton

  def initialize
    @host = 'http://cheat.errtheblog.com/'
    @http = Net::HTTP.start(URI.parse(@host).host)
  end


  def sheet(name)
    @http.get("/s/#{name}").body
  end
end

# then you use it
Cheat.instance.sheet 'migrations'
Cheat.instance.sheet 'yahoo_ceo'

But that’s crazy. Fight the power.

require 'net/http'

# here's how we roll
module Cheat
  extend self

  def host
    @host ||= 'http://cheat.errtheblog.com/'
  end

  def http
    @http ||= Net::HTTP.start(URI.parse(host).host)
  end

  def sheet(name)
    http.get("/s/#{name}").body
  end
end

# then you use it
Cheat.sheet 'migrations'
Cheat.sheet 'singletons'

Any why not? The API is more concise, the code is easier to test, mock, and stub, and it’s still dead simple to convert into a proper class should the need arise.

(( copyright ought ten chris wanstrath ))


extend self includes all the existing instance methods as module methods. This is equivalent to saying extend Rake. Also Rake is an object of class Module.

Another way to achieve equivalent behavior will be:

module Rake
  include Test::Unit::Assertions

  def run_tests # etc.
  end

end 

Rake.extend(Rake)

This can be used to define self contained modules with private methods.