Reputation: 12070
given
module Foo
def bar
puts "foobar"
end
end
I can do
String.extend(Foo)
and as a consequence do
String.bar # => "foobar"
Why doesnt this work?:
a = String.new
a.bar # => NoMethodError: undefined method `bar' for "":String
Is it because 'a' is now and instance and .extend only works against class methods? Why does it lose the 'new' functionality I have given String via .extend?
Upvotes: 2
Views: 660
Reputation: 246
Ruby allows you to add methods from a Module to a Class in two ways: extend
and include
.
Using the module you gave:
module Foo
def bar
puts "foobar"
end
end
With extend
the methods are added as class methods, and can be called directly on the class object itself:
class Bar
extend Foo
end
Bar.bar # => "foobar"
Alternatively, you can call it outside of the class scope:
class Bar
end
Bar.extend Foo
Bar.bar # => "foobar"
include
is slightly different. It will add the methods as instance methods. They are only callable on an instance of the class:
class Bar
include Foo
end
Bar.bar # NoMethodError
a = Bar.new
a.bar # => "foobar"
The key difference was that we first had to make an instance a
, before we could call the instance method.
As sepp2k noted, extend
is can be called on any Object, not just Class objects. For example, you can go:
class Bar
end
a = Bar.new
a.extend Foo
a.bar # => "foobar"
However, bar
will only be added to the single instance a
. If we create a new instance, it you will not be able to call it.
b = Bar.new
b.bar # => MethodNotFoundError
Upvotes: 6
Reputation: 156444
The Object#extend
method adds to the receiver the methods defined by the argument module.
You have to remember that the String
class is itself an object (of type Class
) and so when you call "extend" with it as the receiver, that class object (String) itself gets the new method.
So in your first example, String.extend(Foo)
, adds to the String class instance the function "bar". That is, "bar" is now an instance method of the shadow class (aka Singleton class), so the object String
(which is a class) now has the method "bar". This has roughly the same effect as adding a class method to the String class (e.g. def String.bar; puts 'foobar'; end
), so it has no effect on instances of type String.
Upvotes: 0
Reputation: 11198
In Ruby, class String is an object, and by doing String.extend(Foo), you create a singleton class for the String class object and include the module Foo in it (that means that you are adding class methods to the String class object, and so can call String.bar
). Full stop.
a.bar
doesn't work because there's no instance method bar.
No, you haven't given new to String via extend.
Upvotes: 0