Вопрос

I am really confused why the methods in modules B and C are mixed in to the class in module A when I use include C. Does include somehow recursively mix in with all classes below the namespace where it is called?

module A
  class AClass
    def a_method_
    end
  end
end

module B
  extend self
  def b_method_
  end
end

module C
  extend self
  include B
  def c_method_
    puts A::AClass.new.methods.grep /_method_/
  end
end

C.c_method_

puts "----"
include C
c_method_

With the result that before the include, the AClass instance correctly only has method a_method_, but after the include it has access to the other two methods as well.

a_method_
----
a_method_
c_method_
b_method_

Can someone please help explain this?

Это было полезно?

Решение

If you change the tail end of your code to this:

C.c_method_

puts self.inspect
puts "----"
include C
c_method_

you should help you see what's going on. That will give you this output:

a_method_
main
----
a_method_
c_method_
b_method_

So what is this main business? Chuck has a nice summary over in this answer:

Everything in Ruby occurs in the context of some object. The object at the top level is called "main". It's basically an instance of Object with the special property that any methods defined there are added as instance methods of Object (so they're available everywhere).

That means that saying this at the top level:

include C

is the same as saying:

Object.send(:include, C)

Adding things to Object pollutes everything.

Другие советы

You might find this illuminating:

puts Object.instance_methods.grep /_method_/
# c_method_
# b_method_

Your methods have been added to everything, not just AClass! And to really demonstrate the weirdness, try changing that last bit to:

class D
  include C
  puts Object.instance_methods.grep /_method_/
end

No more output! You've stumbled upon one of the weird, intentional behaviors of Ruby's main (the implicit receiver when nothing else is specified). The basic motivation is that if you define a method in main, you want to be able to call it anywhere:

def foo
end
# now all of these work:
class A
  foo
end
module B
  foo
end
A.new.instance_eval { foo }
A.singleton_class.class_eval { foo }

This isn't about scope resolution either; if you change that to foo = :bar you'll get NameErrors. This is for stuff like puts and gets where you generally don't care who the receiver is when you call them. But this behavior doesn't jive at all with Ruby's object model, so matz hacked it in: every method defined in main gets added to Object as an instance method. Since everything is an object, the rest works as expected.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top