2

I have a module and a class.

module Dog
  def speak
    "woof"
  end
end

class Daschund; end

I create two separate instances of the class.

sausage = Daschund.new
saveloy = Daschund.new

If I want to add Dog#woof as an instance method of my two new objects, I can do it in two ways:

class << sausage
  include Dog
end
> sausage.speak
=> "woof"
saveloy.extend Dog
> saveloy.speak
=> "woof"

Are the two methods equivalent? I know the first adds the module's method to the object's meta-class. Does object#extend do the same thing? Or is it doing something slightly different? Is there any way to prove this?

user3574603
  • 3,364
  • 3
  • 24
  • 59
  • `extend` is the more Ruby way, although arbitrarily extending individual objects is something you may want to avoid. Why can't you inherit a subclass and have `Dog` as a parent? Composition is a valid way of solving this, but a clear inheritance path is always good to have. – tadman Jan 25 '23 at 00:53
  • [Possibly related](https://stackoverflow.com/questions/156362/what-is-the-difference-between-include-and-extend-in-ruby), though it's from ten years ago and Ruby has evolved quite a bit, so I'm hesitant to mark as dup. – Silvio Mayolo Jan 25 '23 at 01:00
  • @SilvioMayolo definitely related and worth reading, but I think it's not a dup. This question here is specifically about extending an object vs. including into its singleton class, whereas the other question calls `include` and `extend` within a class, asking for their differences in general. – Stefan Jan 25 '23 at 10:57

1 Answers1

2

According to the docs for Object, extending an object with a module means including that module to the object's singleton class:

extend: Includes the given modules in the singleton class of self.

Inspecting the ancestors of both objects' singleton classes confirms this:

sausage.singleton_class.ancestors
#=> [#<Class:#<Daschund:0x00007fa6af92e868>>, Dog, Daschund, Object, Kernel, BasicObject]

saveloy.singleton_class.ancestors
#=> [#<Class:#<Daschund:0x00007fa6af92e778>>, Dog, Daschund, Object, Kernel, BasicObject]

The actual implementation details of course depend on the Ruby implementation and version you are using. For MRI/YARV, you have rb_extend_object defined as:

void
rb_extend_object(VALUE obj, VALUE module)
{
    rb_include_module(rb_singleton_class(obj), module);
}
Stefan
  • 109,145
  • 14
  • 143
  • 218
  • For me, `sausage.singleton_class.constants` returns `[]`. And `sausage.singleton_class.methods.include? :speak` returns `false`. – user3574603 Jan 25 '23 at 11:01
  • 2
    @user3574603 sorry, that was a copy-and-paste typo, it's `ancestors` of course, not `constants` :) – Stefan Jan 25 '23 at 11:06