In Ruby, I understand the basic idea of extend
. However, what's happening in this segment of code? Specifically, what does extend
do? Is it just a convenient way o开发者_Python百科f making the instance methods into class methods? Why would you do it this way rather than specifying class methods from the beginning?
module Rake
include Test::Unit::Assertions
def run_tests # etc.
end
# what does the next line do?
extend self
end
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.
精彩评论