In Ruby, the method puts is a singleton method of the Kernel module.
Normally, when a module is included or extended by another module, the module (but not its singleton class) is added to the inheritance tree. That effectively makes the instance methods of the module available to either the module or its singleton class (for include and extend, respectively)... but the singleton methods of a mixed-in module remain inaccessible, because the singleton class of a module isn't ever added to the inheritance tree.
So why can I use puts (and other Kernel singleton methods)?
Kernel.singleton_methods(false)
# => [:caller_locations, :local_variables, :require, :require_relative, :autoload, :sprintf, :format, :Integer, :Float, :String, :Array, :Hash, :test, :warn, :autoload?, :fork, :binding, :exit, :raise, :fail, :global_variables, :__method__, :__callee__, :__dir__, :URI, :eval, :iterator?, :block_given?, :catch, :throw, :loop, :gets, :sleep, :proc, :lambda, :trace_var, :untrace_var, :at_exit, :load, :Rational, :select, :Complex, :syscall, :open, :printf, :print, :putc, :puts, :readline, :readlines, :`, :p, :system, :spawn, :exec, :exit!, :abort, :set_trace_func, :rand, :srand, :trap, :caller]
Note that puts does not seem to be an instance method on Kernel:
Kernel.instance_methods.grep(/puts/)
# []
Although Object does include Kernel
Object.included_modules
# [Kernel]
as far as I can tell, Kernel's singleton class (#<Class:Kernel>) doesn't show up in the ancestors of any object. is_a? appears to agree with this:
Object.is_a? Class.singleton_class # false
Object.is_a? Kernel.singleton_class # false
Object.singleton_class.is_a? Class.singleton_class # true
Object.singleton_class.is_a? Kernel.singleton_class # false
Yet, for some reason, they show up as private methods for every object.
Object.puts "asdf"
# NoMethodError (private method `puts' called for Object:Class)
How does the method lookup find these methods at all if #<Class:Kernel> doesn't show up in the ancestor chain?
Related:
Ruby method lookup path for an object
Class, Module, their eigenclasses, and method lookup
Note: this is different from what I'm asking, because this is class inheritance, so #<Class:Class> inherits from #<Class:Module>
Why a module's singleton method is not visible in downstream eigenclasses where it gets mixed?
You are looking in the wrong place.
Methods like Kernel#Array, Kernel#Complex, Kernel#Float, Kernel#Hash, Kernel#Integer, Kernel#Rational, Kernel#String, Kernel#__callee__, Kernel#__dir__, Kernel#__method__, Kernel#`, Kernel#abort, Kernel#at_exit, Kernel#autoload, Kernel#autoload?, Kernel#binding, Kernel#block_given?, Kernel#callcc, Kernel#caller, Kernel#caller_locations, Kernel#catch, Kernel#eval, Kernel#exec, Kernel#exit, Kernel#exit!, Kernel#fail, Kernel#fork, Kernel#format, Kernel#gets, Kernel#global_variables, Kernel#initialize_clone, Kernel#initialize_copy, Kernel#initialize_dup, Kernel#iterator?, Kernel#lambda, Kernel#load, Kernel#local_variables, Kernel#loop, Kernel#open, Kernel#p, Kernel#pp, Kernel#print, Kernel#printf, Kernel#proc, Kernel#putc, Kernel#puts, Kernel#raise, Kernel#rand, Kernel#readline, Kernel#readlines, Kernel#require, Kernel#require_relative, Kernel#select, Kernel#set_trace_func, Kernel#sleep, Kernel#spawn, Kernel#sprintf, Kernel#srand, Kernel#syscall, Kernel#system, Kernel#test, Kernel#throw, Kernel#trace_var, Kernel#trap, Kernel#untrace_var, and Kernel#warn don't do anything useful with their receiver. They don't call private methods, they don't access instance variables, they in fact completely ignore what self is.
Therefore, it would be misleading if you call them like this:
foo.puts 'Hello, World!'
Because a reader would be mislead into thinking that puts does something with foo, when in fact, it completely ignores it. (This applies especially to the printing family of methods, because there also exist IO#puts and friends, which indeed do care about their receiver.)
So, in order to prevent you from misleadingly calling these methods with a receiver, they are made private, which means they can only be called without an explicit receiver. (Obviously, they will still be called on self, but at least that won't be so obvious visually.)
Technically, these aren't really methods at all, they behave more like procedures, but Ruby doesn't have procedures, so this is the best way to "fake" them.
The reason why they are also defined as singleton methods is so that you can still call them in contexts where Kernel is not in the inheritance hierarchy, e.g. something like this:
class Foo < BasicObject
def works
::Kernel.puts 'Hello, World!'
end
def doesnt
puts 'Hello, World!'
end
end
f = Foo.new
f.works
# Hello, World!
f.doesnt
# NoMethodError: undefined method `puts' for #<Foo:0x00007f97cf918ed0>
And the reason why they need to be defined separately at all is that the instance method versions are private. If they weren't, then you would simply be able to call Kernel.puts anyway, because Object includes Kernel and Kernel is an instance of Module which is a subclass of Object, thus Kernel is an indirect instance of itself. However, the methods are private and thus you would get a
NoMethodError: private method `puts' called for Kernel:Module
instead. Therefore, they need to be duplicated separately. There is actually a helper method that does that: Module#module_function. (This is also used for Math, where you can either call e.g. Math.sqrt(4) or include Math; sqrt(4). In this case, you have the choice of includeing Math or not, whereas Kernel is pre-included in Object always.)
So, in summary: the methods are duplicated as private instance methods of Kernel as well as public singleton methods (which is really just instance methods of Kernel's singleton class). The reason they are defined as private instance methods is so they cannot be called with an explicit receiver and are forced to look more like procedures. The reason they are duplicated as singleton methods of Kernel is so that they can be called with an explicit receiver as long as that explicit receiver is Kernel, in contexts where Kernel is not available in the inheritance hierarchy.
Check this out:
#ruby --disable-gems --disable-did_you_mean -e'puts Kernel.private_instance_methods(false).sort'
Array
Complex
Float
Hash
Integer
Rational
String
__callee__
__dir__
__method__
`
abort
at_exit
autoload
autoload?
binding
block_given?
caller
caller_locations
catch
eval
exec
exit
exit!
fail
fork
format
gets
global_variables
initialize_clone
initialize_copy
initialize_dup
iterator?
lambda
load
local_variables
loop
open
p
pp
print
printf
proc
putc
puts
raise
rand
readline
readlines
require
require_relative
respond_to_missing?
select
set_trace_func
sleep
spawn
sprintf
srand
syscall
system
test
throw
trace_var
trap
untrace_var
warn
"How does the method lookup find these methods at all if # doesn't show up in the ancestor chain?"
1.class.included_modules # => [Comparable, Kernel]
Quoting OP:
Kernel.instance_methods.grep(/puts/)
# []
As you have found yourself, private instance methods don't show up, they do with Kernel.private_instance_methods.
As it turns out, the answer is that I was asking the wrong question. Why am I able use Kernel singleton methods like puts? The answer is: you can't.
Kernel's singleton methods, like all other singleton methods on modules, are not inheritable. The trick is that they aren't singleton methods, per se... they're module functions.
Creating a module function in ruby creates two copies of the method: a singleton method and a private instance method. That's why Kernel.singleton_method(:puts) and Kernel.instance_method(:puts) both work.
So, because Object includes Kernel, it gets access to its instance methods, including puts.
I made a mistake by using #instance_methods, which only shows public instance methods. To see private ones, I need to use #private_instance_methods, e.g.:
Kernel.private_instance_methods(false).grep(/puts/)
# [:puts]
Given the following class:
class Foo
def a
dup.tap { |foo| foo.bar }
end
def b
dup.tap(&:bar)
end
protected
def bar
puts 'bar'
end
end
It seems like both Foo#a and Foo#b should be equivalent, but they're not:
> Foo.new.a
bar
=> #<Foo:0x007fe64a951ab8>
> Foo.new.b
NoMethodError: protected method `bar' called for #<Foo:0x007fe64a940a88>
Is there a reason for this? Is this a bug?
Tested on Ruby 2.2.3p173
Let's start by noting that in Ruby, as you probably know, in a method a declared on a class Foo, I can invoke protected methods on any instance of Foo.
How does Ruby determine whether we're in a method declared on class Foo though? To understand this, we'll have to dig into the internals of method invocation. I'll be using examples from version 2.2 of MRI, but presumably the behavior and implementation are the same in other versions (I'd love to see results from testing this on JRuby or Rubinious, though).
Ruby does this in rb_call0. As the comment suggests, self is used to determine whether we can call protected methods. self is retrieved in rb_call from the current thread's call frame info. Then, in rb_method_call_status, we check that this value of self is of the same class on which a protected method is defined.
Blocks confuse the issue somewhat. Remember that local variables in a Ruby method are captured by any block declared in that method. This means that in the block, self is the same self on which the method was invoked. Let's look at an example:
class Foo
def give_me_a_block!
puts "making a block, self is #{self}"
Proc.new do
puts "self in block0 is #{self}"
end
end
end
proc = Foo.new.give_me_a_block!
proc.call
Running this, we see the same instance of Foo is the same at all levels, even though we called the proc from a completely different object.
So, now we understand why it's possible to call a protected method on another instance of the same class from within a block in a method.
Now let's look at why a proc created with &:bar can't do this. When we place an & sign before a method argument we do two things: instruct ruby to pass this argument as a block, and instruct it to call to_proc on it.
This means invoking the method Symbol#to_proc. This method is implemented in C, but when we invoke a C method, the pointer to self on the current frame becomes the receiver of that C method -- in this case, it becomes the symbol :bar. So we're looking at the instance of foo we got as though we are in a method of class Symbol, and we can't invoke a protected method.
That's a mouthful, but hopefully it makes enough sense. Let me know if you have any suggestions as to how I might improve it!
Seems I am confused between the two methods though I have been using them for a while, I can't understand why the method passengers is not being added to the object in the following code:
class Bus
def number_of_seats
42
end
end
Bus.class_eval do
define_method :number_of_windows do
number_of_seats
end
def fuel_type
:diesel
end
end
Bus.instance_eval do
define_method :destination do
'Paris'
end
def passengers
12
end
end
bus = Bus.new
bus.number_of_windows # => 42
bus.fuel_type # => :diesel
bus.destination # => "Paris"
bus.passengers # => undefined method `passengers' (NoMethodError)
Notes:
Tried instance_eval first, just randomly used class_eval and then it too seemed to work!
My understanding of instance_eval's block: The code in the block is run with self set to the object calling instance_eval.
My understanding of class_eval's block: The code in the block is evaluated as if its placed by opening the class of the object calling it. Hence I am puzzled at the class_eval in the above case! I was expecting class_eval on Bus would mean evaluating the block in the class of Bus Class.
You can refer this awesome article on class and instance_eval as to why passengers is not being added to the object.
TL;DR:
Bus.class_eval will create instance methods and Bus.instance_eval will create class methods.
Now, regarding the behavior of destination(which could be called on instance).....define method when used inside either class_eval or instance_eval is immune to the usual behaviour. Why?.
Because the documentation says so. As per the documentation:
define method - Defines an instance method in the receiver.
Therefore, it does not matter if you use define_method inside class_eval or instance_eval it would always create an instance method.
Source for reference.
Hope this helped :-).
Basically you can't do an instance_eval on a class object when using def. Here's a general way you use instance_eval and class_eval. http://web.stanford.edu/~ouster/cgi-bin/cs142-winter15/classEval.php
I've found an oddness in Ruby. Using method_missing/respond_to_missing? to dynamically handle method calls initiated via __send__ fails when Kernel defines the same method:
class Testerize
def method_missing(method, *args, &block)
if method == :system
puts 'yay'
else
super
end
end
def respond_to_missing?(method, internal)
return true if method == :system
end
end
t = Testerize.new
puts t.respond_to?(:system)
# Prints true
t.system
# Prints 'yay'
t.__send__(:system)
# Exception: wrong number of arguments (ArgumentError)
Kernel.system is somehow getting in the mix. Does anyone know what's going on here? I would have expected the :system "message" to get posted to the Testerize instance, hit method_missing, and voila. Why isn't my method_missing getting called when using __send__ when it is with direct invocation?
I'm using Ruby 1.9.3, if that is relevant.
with '__send__' or 'send' we can even call private methods of the object.
In your script do:
t.private_methods.grep /system/
You will see system method, while with
t.methods.grep /system/
you will see an empty array.
__send__ tries to call the private method inherited from Kernel in the inheritance chain, hence instead of using __send__ use Ruby's public_send method.
If you look up the ancestor chain of your class (Testerize.ancestors), you will find:
[Testerize, Object, Kernel, BasicObject]
Since new classes inherit from Object by default, and Object inherits from Kernel, all of the instance methods available to Kernel are available in your instances.
Using dynamic method calls (#send or #method) the methods' visibility is ignored.
Is there a simple way to dynamically call a method that will fail calling a private method?
As I know - you need method public_send:
----------------------------------------------------- Object#public_send
obj.public_send(symbol [, args...]) => obj
From Ruby 1.9.1
------------------------------------------------------------------------
Invokes the method identified by _symbol_, passing it any arguments
specified. Unlike send, public_send calls public methods only.
1.public_send(:puts, "hello") # causes NoMethodError
Use public_send instead of send:
my_object.public_send :method, *args
It's new to Ruby 1.9, so for older Ruby, you can require 'backports/1.9.1/kernel/public_send'.
If you are using ruby-1.9, you can use Object#public_send which does what you want.
If you use ruby-1.8.7 or earlier you have to write your own Object#public_send
class Object
def public_send(name, *args)
unless public_methods.include?(name.to_s)
raise NoMethodError.new("undefined method `#{name}' for \"#{self.inspect}\":#{self.class}")
end
send(name, *args)
end
end
Or you could write your own Object#public_method which behaves like Object#method but only for public methods
class Object
def public_method(name)
unless public_methods.include?(name.to_s)
raise NameError.new("undefined method `#{name}' for class `#{self.class}'")
end
method(name)
end
end
Thought I don't understand why you want to do so, you can use eval.
class Klass
private
def private_method(arg)
end
end
k = Klass.new
m = "private_method"
eval "k.#{m}('an arg')"
NoMethodError: private method `private_method' called for #<Klass:0x128a7c0>
from (irb):15
from (irb):15
It's true though, eval really is I think the only way to actually do it pre-1.9. If you want to read more about visibility Jamis Buck wrote an awesome article about what method visibility actually means in Ruby.
Much like other things in Ruby visibility is ever so slightly different than other languages.
If you want to avoid eval, send or public_send, or you want better performance, use the public_method method:
obj.public_method('my_method_name').call
You can add arguments like this:
obj.public_method('my_method_name').call('some_argument_1', 'some_argument_2')