views:

116

answers:

2

This question has two parts.

In the Ruby Programming Language book, there is an example (section 8.1.1) of extending a string object and class with a module.

First question. Why is it that if you extend a class with a new method, and then create an object/instance of that class, you cannot access that method?

irb(main):001:0> module Greeter; def ciao; "Ciao!"; end; end
=> nil
irb(main):002:0> String.extend(Greeter)
=> String
irb(main):003:0> String.ciao
=> "Ciao!"
irb(main):004:0> x = "foo bar"
=> "foo bar"
irb(main):005:0> x.ciao
NoMethodError: undefined method `ciao' for "foo bar":String
        from (irb):5
        from :0
irb(main):006:0>

Second part, When I try to extend a Fixnum object, I get an undefined method error. Can someone explain why this works for a string but not a fixnum?

irb(main):045:0> module Greeter; def ciao; "Ciao!"; end; end
=> nil
irb(main):006:0> 3.extend(Greeter)
TypeError: can't define singleton
        from (irb):6:in `extend_object'
        from (irb):6:in `extend'
        from (irb):6
+1  A: 

First question. Why is it that if you extend a class with a new method, and then create an object/instance of that class, you cannot access that method?

Because you extended the String class, so #ciao is a class method and not an instance method.

String.send(:include, Greeter)
x = "foo bar"
x.ciao
# => "Ciao!"

Second part, When I try to extend a Fixnum object, I get an undefined method error. Can someone explain why this works for a string but not a fixnum?

Here's the short answer.

"Fixnums, Symbols, true, nil, and false are implemented as immediate values. With immediate values, variables hold the objects themselves, rather than references to them.

Singleton methods cannot be defined for such objects. Two Fixnums of the same value always represent the same object instance, so (for example) instance variables for the Fixnum with the value "one" are shared between all the "ones" is the system. This makes it impossible to define a singleton method for just one of these."

Off course, you can include/exten the Fixnum class and every Fixnum instance will expose the methods in the Mixin. This is exactly what Rails/ActiveSupport does in order to allow you to write

3.days.ago
1.hour.from_now
Simone Carletti
Your answer to the first part doesn't explain (to me) the why. In other languages, when you change the class, and create a new object of that class type, you get the new method. I am making an assumption that there is a connection from class to objects that is invalid. Can you explain why that connection is not valid here?
Researching the "include" you gave me helped. So, it looks like now that I understand there exists a difference between extend and include, and now know what to search for, there is a post here that explains those differences: http://stackoverflow.com/questions/156362/what-is-the-difference-between-include-and-extend-in-ruby
A: 

obj.extend(module) adds all the methods in module to obj. When called as String.extend(Greeter) you are adding the methods from Greeter to the instance of Class that represents String.

The easiest way to add additional instance methods to an existing class is to re-open the class. The following examples do the same thing:

class String
  include Greeter
end

class String
  def ciao
    "Ciao!"
  end
end

Fixnums (as well as Symbols, true, false and nil) are handled in a different way to normal instances. Two Fixnums with the same value will always be represented by the same object instance. As a result, Ruby doesn't allow you to extend them.

You can of course extend an instance of any other class, e.g.:

t = "Test"
t.extend(Greeter)
t.ciao
=> "Ciao!"
Phil Ross