Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does `include` behave differently at the top level?

I used the following hook to check the module that is doing the including when I do include Foo:

module Foo
  def self.included(includer)
    puts includer
  end
end

Module#include behaves differently in a module (where it is usually used) vs. at the top level. Inside a module, self is the module, which is an instance of Module. When I call include, the module doing the including is what self is:

module Bar
  puts self   # => Bar
  include Foo # => includer: Bar
end

At the top level of a ruby script, self is main, which is an instance of Object. When I call include at the top level, the module doing including is Object, the class of what self is:

puts self    # => main
include Foo  # => includer: Object

Can someone explain why?

The top-level object must be special; If I call to_s or inspect on it, it just says main, but if I create another object with Object.new and call to_s or inspect on it, I get the usual object notation: #<Object:0x007fae0a87ac48>.

like image 881
odigity Avatar asked Oct 04 '15 19:10

odigity


1 Answers

main is special, and has its own definition of include. That is, its singleton_class has its own definition of include. To prove it:

irb(main):017:0> method(:include).owner
=> #<Class:#<Object:0x007fc0398c6468>>
irb(main):018:0> self.singleton_class
=> #<Class:#<Object:0x007fc0398c6468>>

The include you're thinking of is defined on Module:

MyClass.method(:include).owner
=> Module

So, include acts different at the "top level", aka on the object we're calling main for the simplest possible reason: it's just a completely different method than Module#include.

like image 88
Andrew Schwartz Avatar answered Oct 05 '22 12:10

Andrew Schwartz