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
Navigate to Ruby Function Definition in VS Code
How to Make a Gemset in Rvm the Default
Jruby: Import VS Include VS Java_Import VS Include_Class
How to Get the Unique Elements from an Array of Hashes in Ruby
How to Bypass Mass Assignment Protection
Why Does Openuri Treat Files Under 10Kb in Size as Stringio
How to Access Class Variables in Ruby 1.9
Ruby on Rails: How to Check If a File Is an Image
How to Validate the Presence of a Belongs to Association with Rails
How to Install Ruby 1.9.3 on Ubuntu Without Rvm
Rails 4 User Roles and Permissions
Nameerror: Uninitialized Constant Faker
Escape Double and Single Backslashes in a String in Ruby
Simple_Form's Collection_Radio_Button and Custom Label Class