Why can't protected methods be called with symbol to proc? - ruby

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!

Related

Why am I able to use Kernel singleton methods like `puts`?

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]

Within a Ruby method, should I create a proc or a method?

Just want to enquire what the right practice is.
My preference is to use procs, simply because I think that defining methods inside of methhods is a bit untidy and should be done only when necessary. To get around it, I simply use procs.
What is the right / better way to do it and why? (apart from the proc's ability to access the main method's variables defined before itself)
def meth( params_prime )
calculations = do_something_with_whatever
def sub_meth( params_sub )
do_something_with_params_sub
end
sub_meth_params(calculations) # is this better?
proc1 = proc{ |params_sub| do_something_with_params_sub }
proc1.call(calculations) # or is this?
end
It is not clear what your specific use-case is, but I would definitely go for procs or lambdas. There is less overhead when defining a proc or lambda dynamically, they are passable, so if needed you could return them and they could be used outside the function.
Using "def" exposes the method as an instance method outside of the current method scope (so in the containing class, which could be Object in your case). This may or may not be with you want. If you want to use an anonymous function only available in the local scope, use a lambda.
Also Proc vs Lambda: I generally prefer to use lambdas since they behave a little more "predictable", meaning: as you would expect (check passed variables, and return just returns from the lambda, proc returns from the called scope). But from your example it is hard to deduce what would apply. I think the key-difference is: lambas are ment to be passed around, and thus behave a little more sanely. If this is not your use-case, use Proc :) (a write-up of the difference).
If you want to use sub_func to encapsulate it from call from other methods you can use a class to group function and sub_func together and make sub_func private. Otherwise if you want to pass this function as a parameter further you can declare it as lamda.
def func params_prime
sub_func = ->(params_sub){do_something_with_params}
sub_func.call(params_prime)
end
Defining methods inside methods is a feature of Ruby that may have its use. But something is telling me that you are asking a very advanced question while you are still a beginner level Rubyist. Do you know what default definee is? If not, check this article by Yugui.
Procs are very important in Ruby, but newbies tend to use them instead of defining methods in appropriate objects, which is the exact smell I'm getting from your question. The normal way of doing things in OO languages of Ruby family is to define methods on objects:
class Foo
def bar *params
# do something with params
end
end
Since you do not understand the meaning of defining methods inside methods, refrain from doing it for the next 6 months. Once you understand objects, you can start experimenting with this very advanced feature again.
APPENDIX:
Since you demonstrated intrest, let me show you that using def in def at the top level is a frownable-upon thing to do. Normally, when you define a method on some class without further adornment, it becomes a public instance method of that class:
class X
def foo; "foo" end
end
X.instance_methods.include? :foo
#=> true
When you use def in a def, the definee for the inner def is going to be X:
class X
def bar
def baz
"baz"
end
"bar"
end
end
When you execute the above code, instance method #bar becomes defined on X:
X.instance_methods.include? :bar
#=> true
But #baz not yet:
X.instance_methods.include? :baz
#=> false
Only after you call #bar at least once does the method become defined on X:
X.new.bar
#=> "bar"
X.instance_methods.include? :baz
#=> true
And now I would like to ask you to appreciate how terrible thing just happened: An instance just modified its mother class. That's a violation. A violation of such a basic principle of OO design, that I'm not even sure it has a name. This technique is great for obfuscated coding competitions, but in production, it's taboo. Ruby gives you the freedom to break that taboo, gives you the rope to hang yourself on, but you don't do it under any kind of normal circumstances.
So what can be worse than a def inside a def in a class definition? The answer is, a def inside a def at the top level. Let me show you why. Normally, when you define methods with def at the top level, the default definee is Object, but the top level defnitions become private instance methods of object. This is to prevent the unintended consequence of top level defs, because almost all Ruby objects inherit from Object. For example, if you define:
class Object
def foo; "foo" end
end
Now all your objects will respond to foo:
foo #=> "foo"
1.foo #=> "foo"
[].foo #=> "foo
When we define methods at the top level, we usually just intend to use the method at the top level, and don't want every single object to inherit it. For that reason, top level defs become private:
hello #=> NameError: undefined local variable or method `hello' for main:Object
1.hello #=> NoMethodError: undifined method 'hello' for 1:Fixnum
Now we use def at the top level:
def hello; "hello" end
We can see that method #hello is has not become an instance methods of Object:
Object.instance_methods.include? :hello
#=> false
Mysteriously, it became its private method:
Object.private_instance_methods.include? :hello
#=> true
This way, we avoid the unintended consequence of defining #hello method for every single object. But the inheritance is there. The error message has changed:
1.hello #=> NoMethodError: private method 'hello' called for 1:Fixnum
And we can forcibly call the method via #send:
1.send :hello
#=> "hello"
Mysteriously, at the top level, we are allowed to call this private method without #send:
hello
#=> "hello"
And now, what happens when you do def in def at the top level:
def bar
def baz; "baz" end
"bar"
end
You define a private instance method Object#bar in an expected way. But when you call it, alas, the top level magic no longer works and a public method Object#baz gets defined:
bar #=> "bar"
This way, not just the top level, but every single Ruby object got polluted with your #baz method:
1.baz #=> "baz"
Class.baz #=> "baz"
This is why I told you to refrain from using this idiom until you progress from the level of unconscious incompetence to the level of conscious incompetence. I recommend you to read more about top level methods in Ruby.

Dynamically defined setter method is not called from block

I've been writing a DSL, and I'm trying to get a dynamically defined method to be accessible from a lambda. This works fine unless you try to do a setter something= in which case the lambda invocation just sets a local variable instead.
A simplified example:
class Caller
attr_accessor :cmd
def callme
self.class.send(:define_method, "something") { puts "Retrieve Something" }
self.class.send(:define_method, "something=") {|val| puts "Set Something = #{val}" }
instance_exec &cmd
end
end
c = Caller.new
c.cmd = lambda { something = 1 }
c.callme
This also works fine if I use self.something=. However that's less than ideal in the case of a DSL.
Is it possible to get this to work without self. in front of the method?
This has absolutely nothing to do with blocks or dynamically defined methods. It's just simple basic Ruby syntax:
foo = bar
is local variable assignment. Always.
self.foo = bar
is a method call.
Is it possible to get this to work without self. in front of the method?
No.
This is just basic Ruby syntax. The define_method metaprogramming, the instance_exec, the blocks in your code sample are just a red herring, the problem can be demonstrated with a much simpler example:
def foo=(*)
puts 'I was called!'
end
foo=('bar') # even removing spaces and adding parentheses won't help!
self.foo = 'bar'
# I was called!
Note also that foo= is private but was actually called with an explicit receiver (which is illegal for private methods). That's a special exception for setter methods in the rule for private methods, because they otherwise couldn't be called at all, precisely because they would always be interpreted as a local variable assignment.

Metaprogramming: method_missing and __send__

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.

Is there a way to create methods just for the instance of a Ruby class from inside that instance?

Let there be class Example defined as:
class Example
def initialize(test='hey')
self.class.send(:define_method, :say_hello, lambda { test })
end
end
On calling Example.new; Example.new I get a warning: method redefined; discarding old say_hello. This, I conclude, must be because it defines a method in the actual class (which makes sense, from the syntax). And that, of course, would prove disastrous should there be multiple instances of Example with different values in their methods.
Is there a way to create methods just for the instance of a class from inside that instance?
You need to grab a reference to the instance's singleton class, the class that holds all the instance specific stuff, and define the method on it. In ruby 1.8, it looks a little messy. (if you find a cleaner solution let me know!)
Ruby 1.8
class Example
def initialize(test='hey')
singleton = class << self; self end
singleton.send :define_method, :say_hello, lambda { test }
end
end
Ruby 1.9 however, provides a much easier way in.
Ruby 1.9
class Example
def initialize(test='hey')
define_singleton_method :say_hello, lambda { test }
end
end
First off, a small style tip:
self.class.send(:define_method, :say_hello, lambda { test })
You can make this look a little bit nicer by using the new proc literal in Ruby 1.9:
self.class.send(:define_method, :say_hello, -> { test })
But you don't need that. Ruby has something called blocks, which are basically a piece of code that you can pass as an argument to a method. In fact, you already used blocks, since lambda is just a method which takes a block as an argument and returns a Proc. However, define_method already takes a block anyway, there is no need to pass a block to lambda which converts it to a Proc which it passes to define_method which then converts it back into a block:
self.class.send(:define_method, :say_hello) { test }
As you already noticed, you are defining the method on the wrong class. You are defining it on the Example class, since inside an instance method like initialize, self is the current object (i.e. ex1 or ex2 in #mikej's example), which means that self.class is ex1's class, which is Example. So, you are overwriting the same method over and over again.
This leads to the following unwanted behavior:
ex1 = Example.new('ex1')
ex2 = Example.new('ex2') # warning: method redefined; discarding old say_hello
ex1.say_hello # => ex2 # Huh?!?
Instead, if you want a singleton method, you need to define it on the singleton class:
(class << self; self end).send(:define_method, :say_hello) { test }
This works as intended:
ex1 = Example.new('ex1')
ex2 = Example.new('ex2')
ex1.say_hello # => ex1
ex2.say_hello # => ex2
In Ruby 1.9, there's a method that does that:
define_singleton_method(:say_hello) { test }
Now, this works the way you want it to, but there's a higher-level problem here: this is not Ruby code. It is Ruby syntax, but it's not Ruby code, it's Scheme.
Now, Scheme is a brilliant language and writing Scheme code in Ruby syntax is certainly not a bad thing to do. It beats the hell out of writing Java or PHP code in Ruby syntax, or, as was the case in a StackOverflow question yesterday, Fortran-57 code in Ruby syntax. But it's not as good as writing Ruby code in Ruby syntax.
Scheme is a functional language. Functional languages use functions (more precisely, function closures) for encapsulation and state. But Ruby is not a functional language, it is an object-oriented language and OO languages use objects for encapsulation and state.
So, function closures become objects and captured variables become instance variables.
We can also come at this from a completely different angle: what you are doing is that you are defining a singleton method, which is a method whose purpose it is to define behavior which is specific to one object. But you are defining that singleton method for every instance of the class, and you are defining the same singleton method for every instance of the class. We already have a mechanism for defining behavior for every instance of a class: instance methods.
Both of these arguments come from completely opposite directions, but they arrive at the same destination:
class Example
def initialize(test='hey')
#test = test
end
def say_hello
#test
end
end
I know it was asked two years back, but I would like to add one more answer. .instance_eval will help to add methods to instance object
string = "String"
string.instance_eval do
def new_method
self.reverse
end
end
Define instance method from outside:
example = Example.new
def example.say_hello
puts 'hello'
end
From inside:
class Example
def initialize(word='hey')
#word = word
def self.say_hello
puts "example: #{#word}"
end
end
end
Tested on ruby 2.5

Resources