I'm currently reading "The Well-Grounded Rubyist", and on page 196 I see the following:
Suppose you define a method at the top level:
def talk puts "Hello" end
....
A method that you define at the top level is stored as a private instance method of the
Object
class. The previous code is equivalent to this:class Object private def talk puts "Hello" end end
...
To illustrate, let's extend the
talk
example. Here it is again, with some code that exercises it:puts "Trying 'talk' with no receiver..." talk puts "Trying 'talk' with an explicit receiver..." obj = Object.new obj.talk
The first call to
talk
succeeds; the second fails with a fatal error, because it tries to call a private method with an explicit receiver.
I wanted to reproduce this on my local, so I put the above code in a Ruby file I created. I did indeed get the results mentioned in the book:
$ ruby talk.rb
Trying 'talk' with no receiver...
Hello
Trying 'talk' with an explicit receiver...
Traceback (most recent call last):
talk.rb:22:in `<main>': private method `talk' called for #<Object:0x00007f9a8499c3e0> (NoMethodError)
I also tried the following, which produced the same error as running the code via the Ruby interpreter:
irb(main):008:0> load 'talk.rb'
Trying 'talk' with no receiver...
Hello
Trying 'talk' with an explicit receiver...
Traceback (most recent call last):
4: from /Users/richiethomas/.rbenv/versions/2.5.3/bin/irb:11:in `<main>'
3: from (irb):8
2: from (irb):8:in `load'
1: from talk.rb:22:in `<top (required)>'
NoMethodError (private method `talk' called for #<Object:0x00007ffb219c95e0>)
Next, I tried the same code in irb
, and this time I got the following strange results:
irb(main):001:0> def talk
irb(main):002:1> puts "Hello"
irb(main):003:1> end
=> :talk
irb(main):004:0> puts "Trying 'talk' with no receiver..."
Trying 'talk' with no receiver...
=> nil
irb(main):005:0> talk
Hello
=> nil
irb(main):006:0> puts "Trying 'talk' with an explicit receiver..."
Trying 'talk' with an explicit receiver...
=> nil
irb(main):007:0> Object.new.talk
Hello
=> nil
As you can see, in the last code example, I was able to call Object.new.talk
and have it print Hello
as if .talk
were a public method on the Object
instance.
My question is- why is the talk
method public on the Object class when I implement it directly in the REPL, but private when I implement it in a file and load it into the REPL (and also when I run that same file directly in my CLI via the Ruby interpreter)?
Both irb
and pry
(sidenote: I strongly encourage to use the latter) tweak their input to declare all methods as public (during E
stage of REP
loop):
▶ def foo; end
#⇒ :foo
▶ public_methods.grep /foo/
#⇒ [:foo]
That’s it, no magic.
That is done mostly to simplify playing with it in scenarios when one defines the method here and then wants it to be accessible from e.g. there. In REPL
it is worth it to make everything accessible everywhere.
def pretty_print; self.inspect; end
class A; ...; end
class B; ...; end
A.new.pretty_print
B.new.pretty_print
One should not pay too much attention to encapsulation, SRP, etc while playing the sandbox.
In general, it’s like declaring the module with generic helpers and including it everywhere for free.