Including/Extending the Kernel Doesn't Add Those Methods on Main:Object

Including/Extending the Kernel doesn't add those methods on main:Object

I will try to explain it a little bit deeper:

when you include module into some class, Ruby creates special internal include class and adds it to the hierarchy (please note that basically you are not allowed to see include class from Ruby programm, it is hidden class):

Given A inherits B
And we have a module C
When A includes C
Then A inherits includeC inherits B

If included module has other included Modules, then includeModules will be created for these modules as well:

Given A inherits B
And we have a module C
And C includes module D
When A includes C
Then A inherits includeC inherits includeD inherits B

Include class C method table is a link to method table of original class C.

When you extend some object with a module, then this module is included into singleton class of this object, thus:

class << self; include C; end
# is the same as
extend C

Going to your example:

module Kernel
extend Talk
end

Here you include Talk module into singleton class of Kernel (Kernel is an object of class Module). This is why you can call hello method only on Kernel object: Kernel.hello.

If we write this:

module Kernel
include Talk
end

Then Kernel will internally inherit include class includeTalk (class with link to Talk methods).

But Kernel module is already included into Object - Object inherits its own includeKernel class and includeKernel class has link to method table of Kernel and does not see methods of new include classes of Kernel.

But now if you will re-include Kernel into Object, all Objects will see methods of Talk:

> module Talk
> def hi
> puts 'hi'
> end
> end
=> nil
> module Kernel
> include Talk
> end
=> Kernel
> hi
NameError: undefined local variable or method `hi` for main:Object
from (irb):9
from /usr/share/ruby-rvm/rubies/ruby-1.9.2-p290/bin/irb:16:in `<main>`
> class Object
> include Kernel
> end
=> Object
> hi
hi
=> nil

The solution for you probjem might be to extend main object with your new module:

extend Talk

Hope this is clarified a bit behaviour you observe :)

UPDATE

Will try to clarify your questions:

I'm still a bit confused why I have to re-include Kernel in Object. In
cases that don't involve the main Object, I can instantiate an object
based on a class and then later reopen that class and include a
module, and that object will see methods in my module. Is there
something different about how main Object includes the Kernel? I'm
also not sure what you mean by "Object inherits its own includeKernel
class and includeKernel class..." Why doesn't it see the new included
module in Kernel?

You tell about the case with direct inclusion of module into class of an object:

module M
def hi
puts 'hi'
end
end

class C
end

c = C.new
c.hi # => UndefinedMethod

class C
include M
end

c.hi # => hi

in this case you will have object c of class C. Class C inherits Object (because it is an instance of Class class. c looks for its instance methods in his singleton class -> then in his class C -> then in parents of class C (in this case Object instance methods). When we include module M into class C, then includeM will be a superclass of C and if c will not find his instance method in his singleton class and C class, it will search for instance methods in includeM. includeM has a link to method table of M class (instance of Module class). Thus when c search for instance method hi it finds it in the M module.

But this is different from case when you include module M into Kernel module.
At the program start Object class includes module Kernel: class Object; include Kernel; end. This is why I say that Object inherits from includeKernel. includeKernel has link to method table of Kernel and when you change method table of Kernel, includeKernel will also see these changes:

module Kernel
def hi # add hi method to method table of Kernel
puts 'hi'
end
end

hi # => hi # any Object now see method hi

But when you include module M into Kernel, then method table of Kernel is not changed. Instead Kernel will now inherit includeM include class. includeKernel don't see methods of includeM because it does not know about inheritance chain of Kernel and includeM, it only knows the method table of Kernel.

But when you re-include Kernel into Object, inclusion mechanism will see that Kernel includes M and will create includeM for Object as well. Now Object will inherit includeKernel will inherit includeM will inherit BasicObject.

undef methods from Kernel class still able to call them from Kernel.singleton_class

Though this is a very bad practice to monkey-patch an existing main module like this, esp Kernel which is ancestor of every object in Ruby. It may have very bad & severe repercussions in your or other project, it is included. Though for understanding what is going on above with your code, you can change:

module Kernel
undef rand
undef srand
end

to

module Kernel
class << self
undef rand
undef srand
end
end
# OR, much cleaner:
Kernel.instance_eval { undef :rand, :srand }

by class << self you are hijacking the singleton class/eigenclass of Kernel and undefining the singleton methods rand and srand there.
These are the singleton methods of kernel rather than the instance methods.

Orginally:

 Kernel.singleton_methods.grep(/rand/)
=> [:srand, :rand]

After:

 Kernel.singleton_methods.grep(/rand/)
=> []

I know the method is called on Kernel.singleton_class but the singleton
class includes Kernel module which has the methods undefined.

include in a class/module doesn't include the singleton methods in that class/module. rand and srand are effectively only defined in singleton_class of Kernel and can only be undefined from there.

UPDATE:

As noticed, rand and srand in top-level object(main) will still work, but Kernel.rand and Kernel.srand will give NoMethodError. Reason:

When a module is included in a class or other module, Ruby internally creates a special include class which holds the reference for the module defined at that time, and adds that class in ancestor chain of the class(though hidden).

Now, Object has already included Kernel earlier. The methods present in Kernel at that instance are stacked in hidden include class and is available for Object. That's why, main (instance of Object) still holds the reference of those methods.

This is the reason why rand works but Kernel.rand doesn't.

Ruby's Mix-in chaining not working properly

Yes, in this case you should extend Object directly. But if you're so inclined, you can extend Kernel, just don't forget to re-include it to Object again.

module Foo
def bar
puts "Method is in scope!!!"
end
end

Kernel.send :include, Foo
# bar at this point will generate error

Object.send :include, Kernel
bar
# >> Method is in scope!!!

Also, see this answer for a much more thorough explanation.

How to determine if an instance has been extended by a Ruby module?

Is there any reason why you are not just using is_a?:

o.is_a? Foobar
# => true

When should I use class Object , class Module , module Kernel and nothing?

Each of these examples fall into different cases.

If you are writing methods that apply to all objects, then you open the Object class so all objects can access it. If you are writing methods that apply to all modules, then you open Module. Whenever you open a class to add methods, the methods should apply to all instances of the class and nothing else.

Extending the Kernel module is different: people do this to add methods that should be available to every scope, but not really as methods to be explicitly called on an object, by making them private.

When you are outside of any class or module statement, you are in the scope of the main object, and methods you define default to being private methods of Object. This is fine for small or simple programs, but you will eventually want to use proper modules as namespaces to organize your methods.

As a final note on the subject, you always need to be sure that you really want methods you add to built-in classes and modules to be available to everything in your application, including external inclusions because they all share the built-ins.

Now to apply this to answer your question. Because you are defining a method that creates accessors for class variables, you should put it in the class Class as it applies to all classes and nothing else. Finally, you will likely only use it in class definitions (within a class statement), so we should make it private:

class Class
private

def c_attr_accessor(name)
# ...
end
end

class User
c_attr_accessor :class_variable_name

# ...
end

If you don't really need it in every class (maybe just a few), then create a "mixin module" to extend every class that needs this feature:

module ClassVariableAccessor
private

def c_attr_accessor(name)
# ...
end
end

class User
extend ClassVariableAccessor

c_attr_accessor :class_variable_name

# ...
end

Note that you are using Object#extend to add c_attr_accessor only to the object User (remember that classes are objects; you will hear that a lot if you are new to Ruby metaprogramming).

There is another way to implement the last example, which works by explicitly extending its base class through the Module#included(base_class) "hook method" called whenever the module included, and the base class is passed to base_class:

module ClassVariableAccessor
def included(base_class)
base_class.extend ClassMethods
end

module ClassMethods
def c_attr_accessor(name)
# ...
end
end
end

class User
include ClassVariableAccessor

c_attr_accessor :class_variable_name

# ...
end

I recommend this last solution because it is the most general and uses a simple interface that should not need to be updated. I hope this is not too much!

Why does the Ruby module Kernel exist?

Ideally,

  • Methods in spirit (that are applicable to any object), that is, methods that make use of the receiver, should be defined on the Object class, while
  • Procedures (provided globally), that is, methods that ignore the receiver, should be collected in the Kernel module.

Kernel#puts, for example doesn't do anything with its receiver; it doesn't call private methods on it, it doesn't access any instance variables of it, it only acts on its arguments.

Procedures in Ruby are faked by using Ruby's feature that a receiver that is equal to self can be omitted. They are also often made private to prevent them from being called with an explicit receiver and thus being even more confusing. E.g., "Hello".puts would print a newline and nothing else since puts only cares about its arguments, not its receiver. By making it private, it can only be called as puts "Hello".

In reality, due to the long history of Ruby, that separation hasn't always been strictly followed. It is also additionally complicated by the fact that some Kernel methods are documented in Object and vice versa, and even further by the fact that when you define something which looks like a global procedure, and which by the above reasoning should then end up in Kernel, it actually ends up as a private instance method in Object.

Why method created at top-level is mentioned in Object.private_methods?

But why zzz method name is returned in array when call Object.private_methods?

self at the toplevel is an object called 'main', and 'main' is an instance of class Object:

p self
p self.class

--output:--
main
Object

The private_instance_methods() of a class, e.g. Object, are the private_methods() of the instances of the class, e.g. 'main'.

And why Object and self return different instance methods names?

That can't be because self/main is not a Class, and therefore self/main does not respond to instance_methods():

p self.instance_methods

--output:--
1.rb:3:in `<main>': undefined method `instance_methods' for main:Object (NoMethodError)

As for this:

p Object.private_methods(false)

--output:--
[:inherited, :initialize]

Object, like all classes, is an instance of Class, so the private_instance_methods() of Class:

p Class.private_instance_methods(false)

--output:--
[:inherited, :initialize]

are the private_methods() of the instances of Class, e.g. Object:

p Object.private_methods(false)

--output:--
[:inherited, :initialize]

And this:

Temp.private_methods() # [:inherited, :initialize, :included, :extended,...., :zzz,..., :singleton_method_added, :singleton_method_removed, :singleton_method_undefined, :method_missing]  
#`zzz` is in list, but there is no `:hello`

zzz() is a method defined in Object, which means that ALL objects inherit that method. Temp is a class, but Temp is also an object(it is an instance of Class):

puts Temp.class

--output:--
Class

So the Temp object inherits the method zzz() from Object. In fact, ALL objects can call zzz() because all objects inherit from Object, but because zzz is a private instance method in Object, you cannot specify a receiver:

def zzz
puts 'zzz'
end

class Temp
puts self #=>Temp
zzz #When you don't specify a receiver for a method call
#ruby uses self, so that line is equivalent to
#self.zzz, which is equivalent to Temp.zzz

def hello
puts 'Hello!'

puts self #=>Temp_instance
zzz #Same as self.zz, which is equivalent to Temp_instance.zz
end
end

--output:--
Temp
zzz
Hello!
#<Temp:0x00000101136238>
zzz

The method hello() can only be called by instances of the class Temp, so Temp can call zzz(), but Temp cannot call hello():

class Temp
def hello
puts 'Hello!'
end
end

Temp.new.hello
Temp.hello

--output:--
Hello!
1.rb:8:in `<main>': undefined method `hello' for Temp:Class (NoMethodError)

Module instance methods and Module regular methods in Ruby

This might clarify things:

▶ module Test
▷ def im; end
▷ def mm; end
▷ module_function :mm
▷ end
▶ Test.methods(false) # false to not output inherited
#⇒ [
# [0] mm() Test
# ]
▶ Test.instance_methods(false) # false to not output inherited
#⇒ [
# [0] :im
# ]
▶ Test.im
#⇒ NoMethodError: undefined method `im' for Test:Module
# from (pry):99:in `__pry__'
▶ Test.mm
#⇒ nil # fine, called

Module methods might be called as is, as seen above. For instance methods, one needs an instance:

▶ "Hello world!".extend(Test).im
#⇒ nil # fine, called

Why doesn't Object.instance_methods(false) return expected methods in Ruby?

Most of the methods are not defined on Object itself, they are defined on Kernel (a module that gets included to Object). You can see this:

Object.instance_methods(true).map(&method(:method)).map(&:owner)

=> [Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, #>, Kernel, Kernel, Kernel, #>, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, Kernel, BasicObject, BasicObject, BasicObject, BasicObject, BasicObject, BasicObject, BasicObject, BasicObject]

If I open a new irb window and run

Object.instance_methods(true).select { |sym| method(sym).owner == Object }

I get no results. But if I require 'pry', then I get only [:pry, :__binding__]



Related Topics



Leave a reply



Submit