How to convert method to proc for &method usage in Ruby - ruby

For learning purposes, I'm exploring if I can convert these three method calls:
Foo::Bar.logger.debug(a)
Foo::Bar.logger.debug(b)
Foo::Bar.logger.debug(c)
into a single-line statement using the shorthand proc &method approach:
[a, b, c].each(&method(:'Foo::Bar.logger.debug'))
.debug does not respond to .to_proc so naturally:
NameError: undefined method `Foo::Bar.logger.debug' for class `#<Class:Foo>'
This does work; however, but isn't as succinct as the former:
logger = Proc.new { |x| Foo::Bar.logger.debug(x) }
[a, b, c].each(&logger)
Is it possible for the former approach to work?

You're using the method method incorrectly. From the fine manual:
method(sym) → method
Looks up the named method as a receiver in obj, returning a Method object (or raising NameError).
You'd usually say
m = some_obj.method(:some_method_name)
and then work with m. Saying:
method(:Foo::Bar.logger.debug)
should be giving you a TypeError because because :Foo is a symbol, not a class or module and trying to apply :: to a symbol makes no sense. I suspect that you're actually saying:
method(':Foo::Bar.logger.debug')
as that will produce the error you're seeing. Assuming that that's the case, then you're actually trying to get a reference to the method named ':Foo::Bar.logger.debug' in the object self.
If you want a reference to the debug method in Foo::Bar.logger then you'd say:
Foo::Bar.logger.method(:debug)
Combining that your to_proc call (via &):
[a, b, c].each(&Foo::Bar.logger.method(:debug))

Related

best way to invoke a class method dynamically in ruby

I have controller "MyController" which has a class method "my_controller_class_method" which accepts arguments a,b,c
is there a way to pass "MyController.my_controller_class_method" as an argument to another method so I could do something like that:
def use_another_method(other_method,a,b,c)
#invoke method inside
other_method(a,b,c)
end
and then I could run
use_another_method(MyController.my_controller_class_method,a1,b1,c1)
You can use .method to get an object representing that method:
method_object = MyController.method(:my_controller_class_method)
method_object.call(a, b, c)
method_object.(a, b, c) # shorter syntax
That object can then be passed around and used as a function argument, just like any other object.

How method name is converted into a symbol in Ruby?

I am trying to understand Ruby and it's not clear for me, how Ruby converts name of method into a Symbol?
In method definition we give it a name meth
module Mod
def meth
puts 'm'
end
end
But if we want to check if a method exists, we pass the symbol :meth as a parameter to method_defined
Mod.method_defined?(:meth)
=> true
Please, help me understand, how does this work?
This is due to ruby's method invocation syntax: You can call a method just by referencing its name, without any further syntax like brackets () needed.
Now, if the method_defined? method would take the method itself as an argument, there would be no way to do so without actually invoking the method and thereby producing an error if the method would not exist:
Mod.method_defined?(meth)
#=> NameError: undefined local variable or method `meth'
With symbols, there is no invocation taking place, it is just normally instantiated and not producing any error. Behind the curtains, method_defined? can then lookup if a method exists by the name the symbol references without producing any error.
it's not clear for me, how Ruby converts name of method into :symbol?
That's the way Method#name works, it returns the name of the method as a symbol:
m = "foo".method(:size) #=> #<Method: String#size>
m.name #=> :size
m.call #=> 3
All methods referencing other methods usually work this way. For example, Object#methods returns a array of method names:
"foo".methods
#=> [:<=>, :==, :===, :eql?, :hash, :casecmp, :+, :*, ...]
In method definition we give it name meth ... but if we want check, does any method exist, we give into method_defined symbol :meth
meth would be a reference to a variable or another method, whereas :meth is just a symbol:
meth = :foo
Mod.method_defined? meth #=> false, equivalent to Mod.method_defined? :foo
Mod.method_defined? :meth #=> true
Module#method_defined?
method_defined?(symbol) → true or false
method_defined?(string) → true or false
Returns true if the named method is defined by mod (or its included modules and, if mod is a class, its ancestors). Public and protected methods are matched. String arguments are converted to symbols.
That means- method_defined? method always expect symbol as its argument,but if you use string,then it is internally converted into a symbol using String#to_sym.
It isn't so much that a method is "turned into" a symbol, it is just that it can be referred to by symbol.
When we look at the collection of methods from ObjectSpace.methods` we see that the objects are referred to as symbols.
This makes sense as symbols in Ruby are highly efficient as there is only one object that is that symbol, they are immutable, and it can refer to what it needs to depending on its context, referring to the scope and what 'self' is at the moment.
In other words, a symbol called :to_s will be the same symbol no matter where it is referred to. It's object_id will always be the same, but it may refer to Array's to_s method or even a local variable called to_s.
Identifiers also need to be stored in memory. All languages store method names, class names in some form or the other. They just don't make these accessible like Ruby.
Class names are stored as constants. Method names are stored as symbols. There is nothing such as being converted to Symbol. It is stored as symbol always. This makes following possible:
1.send(:+, 3) # exactly same as 1+3
This also means that you can actually accept names from user and execute that particular method. You were always able to do that via switch/if-else but this really removes a layer from functionality.
For example
case option
when '+'
variable1 + variable2
when '-'
variable1 - variable2
when '*'
variable1 * variable2
when '/'
variable1 / variable2
end
compared to
variable1.send(option, variable2)
Do you see the genius of it?

Call of a method as a block

Beginner in ruby world, I would like to do something like:
[1,2.0,"a",2].select(&:is_a?(Integer))
but like this it definitely don't work...
Any ideas?
You can't do what you are asking for because when you use the & syntax you have to use a method that doesn't take parameters.
However, if you for some reason you really want to do something like that, you need to make a method that doesn't take parameters like so:
class Object
def is_an_integer?
is_a? Integer
end
end
You can then do:
[1,2.0,"a",2].select(&:is_an_integer)
&:method_name is syntactic sugar for &:method.to_proc. Enumerators like select and whatnot accept a block and yield each element of the enumerator to the passed block. That is:
[1,2,3].select &:even?
is equivalent to:
p = :even.to_proc
[1,2,3].select {|val| p.yield(val) }
Since only the parameters yielded by the enumerator are yielded to the proc, you would have to include them in the source list. That is, we might expect:
[[1, Integer]].select &:is_a?
to result in:
select {|*args|, p.yield(*args) }
However, remember that p isn't a method bound to any particular class! It's going to try to invoke the given method on the passed argument. So, it's going to try to invoke Array#is_a? with no arguments, rather than splatting the arguments out and invoking Integer#is_a?(Integer).
So, to accomplish this, we'll have to somehow create a proc that binds the passed arguments, and then calls the given method on the yielded receiver with the passed args. We can do this by adding a method to the Symbol class:
class Symbol
def with_args(*args)
proc {|receiver| receiver.send(self, *args) }
end
end
[1, "a"].select &:is_a?.with_args(Integer)
While it's perhaps not amazingly clean, it does work.

How does the "#map(&proc)" idiom work when introspecting module classes?

Presenting the Idiom
I found an interesting but unexplained alternative to an accepted answer. The code clearly works in the REPL. For example:
module Foo
class Bar
def baz
end
end
end
Foo.constants.map(&Foo.method(:const_get)).grep(Class)
=> [Foo::Bar]
However, I don't fully understand the idiom in use here. In particular, I don't understand the use of &Foo, which seems to be some sort of closure, or how this specific invocation of #grep operates on the result.
Parsing the Idiom
So far, I've been able to parse bits and pieces of this, but I'm not really seeing how it all fits together. Here's what I think I understand about the sample code.
Foo.constants returns an array of module constants as symbols.
method(:const_get) uses Object#method to perform a method lookup and return a closure.
Foo.method(:const_get).call :Bar is a closure that returns a qualified path to a constant within the class.
&Foo seems to be some sort of special lambda. The docs say:
The & argument preserves the tricks if a Proc object is given by & argument.
I'm not sure I fully understand what that means in this specific context, either. Why a Proc? What "tricks," and why are they necessary here?
grep(Class) is operating on the value of the #map method, but its features are not obvious.
Why is this #map construct returning a greppable Array instead of an Enumerator?
Foo.constants.map(&Foo.method(:const_get)).class
=> Array
How does grepping for a class named Class actually work, and why is that particular construction necessary here?
[Foo::Bar].grep Class
=> [Foo::Bar]
The Question, Restated
I'd really like to understand this idiom in its entirety. Can anyone fill in the gaps here, and explain how the pieces all fit together?
&Foo.method(:const_get) is the method const_get of the Foo object. Here's another example:
m = 1.method(:+)
#=> #<Method: Fixnum#+>
m.call(1)
#=> 2
(1..3).map(&m)
#=> [2, 3, 4]
So in the end this is just a pointfree way of saying Foo.constants.map { |c| Foo.const_get(c) }. grep uses === to select elements, so it would only get constants that refer to classes, not other values. This can be verified by adding another constant to Foo, e.g. Baz = 1, which will not get grepped.
If you have further questions please add them as comments and I'll try to clarify them.
Your parse of the idiom is pretty spot on, but I'll go through it and try to clear up any questions you mentioned.
1. Foo.constants
As you mentioned, this returns an array of module constant names as symbols.
2. Array#map
You obviously know what this does, but I want to include it for completeness. Map takes a block and calls that block with each element as an argument. It returns an Array of the results of these block calls.
3. Object#method
Also as you mentioned, this does a method lookup. This is important because a method without parentheses in Ruby is a method call of that method without any arguments.
4. &
This operator is for converting things to blocks. We need this because blocks are not first-class objects in Ruby. Because of this second-class status, we have no way to create blocks which stand alone, but we can convert Procs into blocks (but only when we are passing them to a function)! The & operator is our way of doing this conversion. Whenever we want to pass a Proc object as if it were a block, we can prepend it with the & operator and pass it as the last argument to our function. But & can actually convert more than just Proc objects, it can convert anything that has a to_proc method!
In our case, we have a Method object, which does have a to_proc method. The difference between a Proc object and a Method object lies in their context. A Method object is bound to a class instance and has access to the variables which belong to that class. A Proc is bound to the context in which it is created; that is, it has access to the scope in which it was created. Method#to_proc bundles up the context of the method so that the resulting Proc has access to the same variables. You can find more about the & operator here.
5. grep(Class)
The way Enumerable#grep works is that it runs argument === x for all x in the enumerable. The ordering of the arguments to === is very important in this case, since it's calling Class.=== rather than Foo::Bar.===. We can see the difference between these two by running:
irb(main):043:0> Class === Foo::Bar
=> true
irb(main):044:0> Foo::Bar === Class
=> false
Module#=== (Class inherits its === method from Method) returns True when the argument is an instance of Module or one of its descendants (like Class!), which will filter out constants which are not of type Module or Class.
You can find the documentation for Module#=== here.
The first thing to know is that:
& calls to_proc on the object succeeding it and uses the proc produced as the methods' block.
Now you have to drill down to how exactly the to_proc method is implemented in a specific class.
1. Symbol
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
Or something like this. From the above code you clearly see that the proc produced calls the method (with name == the symbol) on the object and passes the arguments to the method. For a quick example:
[1,2,3].reduce(&:+)
#=> 6
which does exactly that. It executes like this:
Calls :+.to_proc and gets a proc object back => #<Proc:0x007fea74028238>
It takes the proc and passes it as the block to the reduce method, thus instead of calling [1,2,3].reduce { |el1, el2| el1 + el2 } it calls
[1,2,3].reduce { |el1, el2| el1.send(:+, el2) }.
2. Method
class Method
def to_proc
Proc.new do |*args|
self.call(*args)
end
end
end
Which as you can see it has a different implementation of Symbol#to_proc. To illustrate this consider again the reduce example, but now let as see how it uses a method instead:
def add(x, y); x + y end
my_proc = method(:add)
[1,2,3].reduce(&my_proc)
#=> 6
In the above example is calling [1,2,3].reduce { |el1, el2| my_proc(el1, el2) }.
Now on why the map method returns an Array instead of an Enumerator is because you are passing it a block, try this instead:
[1,2,3].map.class
#=> Enumerator
Last but not least the grep on an Array is selecting the elements that are === to its argument. Hope this clarifies your concerns.
Your sequence is equivalent to:
c_names = Foo.constants #=> ["Bar"]
cs = c_names.map { |c_name| Foo.__send__(:const_get, c_name) } #=> [Foo::Bar]
cs.select{ |c| Class === c } #=> [Foo::Bar]
You can consider Object#method as (roughly):
class Object
def method(m)
lambda{ |*args| self.__send__(m, *args) }
end
end
grep is described here http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-grep
=== for Class (which is subclass of Module) is described here http://ruby-doc.org/core-1.9.3/Module.html#method-i-3D-3D-3D
UPDATE: And you need to grep because there can be other constants:
module Foo
PI = 3.14
...
end
and you probably don't need them.

Where does the variable "p" get it's value from in ruby if it's not defined explicitly?

Question: Where does p get it's value from below and why does it happen?
Consider this irb session:
me#somewhere:~$ irb
irb(main):001:0> a
NameError: undefined local variable or method `a' for main:Object
from (irb):1
irb(main):002:0> foo
NameError: undefined local variable or method `foo' for main:Object
from (irb):2
irb(main):003:0> p
=> nil
irb(main):004:0> p.class
=> NilClass
irb(main):005:0>
I never defined p - so why is it nil valued? Neither a nor foo were recognized so what's special about p? I also didn't find anything listed under Kernel#p
Context: I'm reading the so-called "28 bytes of ruby joy" and assumed p was a variable, as in: def p.method_missing *_ ...
(Don't worry: I'm not going to actually define method_missing on nil everywhere... just studying some ruby code...)
p is just a method on Kernel which calls inspect on its arguments, producing human-readable representations of those objects. If you give it no arguments, it prints nothing. Regardless of what you pass it, though, it returns nil. See Kernel#p and Object#inspect.
Power tip: In Ruby 1.9, when you have a method and you don't know where it came from, use the method method:
ruby-1.9.1-p378 > method(:p)
=> #<Method: Object(Kernel)#p>
Putting it together one step at a time, we read this as:
p # We found a method called p.
#p # It's an instance method.
Object ... #p # It's available on Object.
Object(Kernel)#p # It came from the Kernel module.
Update: The OP provided some context from this article, where the author claims that your life will be easier if you add a method_missing to nil, by doing the following:
def p.method_missing*_;p;end
This somewhat obfuscated code should be read as:
Define a new method (def), called method_missing. This overrides the default method_missing handler on Object, which simply raises a NoMethodError when it encounters a method it doesn't understand.
This method will live on something called p.
It accepts any number of arguments (*) and stores them in a variable called _.
The result of these arguments is something called p.
The second bullet is the tricky part here. def p.method_missing means one of two things, depending on context:
A previously defined object called p which is in scope here.
A method called p which is in scope, and which is passed no arguments.
With def p.method_missing, we mean, "this method is being defined on the object which is the result of calling p with no arguments". In this case, that is NilClass; if you call p with no arguments, you get nil. So this is just a short, hacky way to define a method on NilClass.
Note: I definitely recommend against defining a method_missing on nil. This is a silly and dangerous tactic to use for the sake of saving a few lines of code, because it changes the behavior of nil. Don't do it!
p is a method which prints the inspect value of its arguments and returns nil. Without arguments it simply does nothing.
Its documentation is under Kernel#p, not Kernel::p (because it's an instance method of Kernel).
p is a method of the Kernel module. It's good for debugging - it prints out the internal representation of whatever you give it, by calling inspect on it. If you don't pass it any arguments, it just prints out nil.
irb(main):001:0> p 11
11
=> 11
irb(main):002:0> Kernel::p [1,2] + [3,4]
[1, 2, 3, 4]
=> [1, 2, 3, 4]
The results are printed out twice here as it also returns the object, which irb then prints out too.

Resources