Firstly, note that in place of
object = Test.new
object.class.instance_methods(false)
you could simply write
Test.instance_methods(false)
so let's simplify:
puts "before calling class method abc: #{Test.instance_methods(false)}"
# (prints) []
Test.abc
puts "after calling class method abc: #{Test.instance_methods(false)}"
# (prints) [:John, :John=]
There's a short explanation of this behaviour, but also a longer one.
Short Explanation
Test.method(:attr_accessor)
#=> #<Method: Class(Module)#attr_accessor>
This return value, together with the fact that
Test.class #=> Class
tells us that attr_accessor
is an instance_method of Class
, and therefore a method of Test
. Let's confirm.
Class.instance_methods.include?(:attr_accessor)
#=> false
Whoops! (You were expecting => true
?). That can only mean that attr_accessor
is a protected or private instance method:
Class.protected_instance_methods.include?(:attr_accessor)
#=> false
Class.private_instance_methods.include?(:attr_accessor)
#=> true
Therefore
Class.private_instance_methods.include?(:attr_accessor)
#=> true
So attr_accessor
is simply a private instance method of Test
's class, Class
, making it a private method of Test
. The expression (from earlier)
Test.method(:attr_accessor)
#=> #<Method: Class(Module)#attr_accessor>
also tells us that attr_accessor
is defined in Class
's superclass Module
.
Class.superclass
#=> Module
Class.ancestors
#=> [Class, Module, Object, Kernel, BasicObject]
Class.instance_method(:attr_accessor).owner
#=> Module
Test.method(:attr_accessor).owner
#=> Module
Longer Explanation
The method Module#attr_accessor can be made available to the class Test
in one of two ways. This is the essence of Ruby's object model. (I have sidestepped the place of singleton class methods.)
1. Test inherits the method from its superclass
Test.superclass
#=> Object
Test.ancestors
#=> [Test, Object, Kernel, BasicObject]
Let's see.
Object.method(:attr_accessor)
#=> #<Method: Class(Module)#attr_accessor>
Object.public_methods.include?(:attr_accessor)
#=> false
Object.private_methods.include?(:attr_accessor)
#=> true
2. attr_accessor
is an instance method of Test
's class
Test.class
#=> Class
Class.private_instance_methods.include?(:attr_accessor)
#=> true
So Test
both inherits attr_accessor
from its superclass and has it available as an instance method of its class. This duality is often explained with a diagram such as this one.
Ruby first checks Test
's class, Class
, for the instance method attr_accessor
. where it will find it. (Had she not found it there she'd then look for the method (not instance method) in the superclass.)