jdee
jdee

Reputation: 12070

ruby .extend help needed

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

Answers (3)

rrjamie
rrjamie

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

maerics
maerics

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

Marek Příhoda
Marek Příhoda

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

Related Questions