ruby get caller full namespace with its parameter name and argument - ruby

in debugging ruby applications, i am interested in printing in each function the:
- full method namespace
- the method parameters and their arguments (values)
for instance, given the following code block
module Foo
module Bar
def self.baz(p, q)
end
end
end
when invoking baz with arguments, such as Foo::Bar.baz 'val1', 'val2', a message should be printed with the following output Foo::Bar.baz(p=val1, q=val2) (or any similar output)
i am familiar with ruby reflection, so i can introduce to each function some additional code to print it. though, single point of modification is a bliss, thus having the each method call another method that will produce such output will be the great.
my questions are:
is there anyway to introduce a method, which each other method invoke that will yield an output similar to the above?
is there anyway to achieve the same result by binding the printing method (function) to the beginning of any other method?

What you are looking for is #set_trace_func combined with binding introspection:
module Foo
module Bar
def self.baz(p, q)
end
end
end
intercept_event = proc do |event, _, _, method_name, method_binding, _|
if event == 'call'
self_in_method = method_binding.eval('self')
parameter_names = self_in_method.method(method_name).parameters.map(&:last)
parameter_list = parameter_names.map do |name|
"#{name}=#{method_binding.local_variable_get(name)}"
end
puts "#{self_in_method}.#{method_name}(#{parameter_list.join ', '})"
end
end
set_trace_func intercept_event
Foo::Bar.baz 'val1', 'val2' # Foo::Bar.baz(p=val1, q=val2)
Note that this will not work for C methods. To handle them, you can check for 'c-call' instead of 'call'. You will have to move the method parameters in the string eval however, as self in those methods is not the same as in Ruby methods.

Related

Trying to get the name of the currently running function in ruby

I have a function that I would like to call and have it return the name of the function it was called from. Here is the function:
def get_pos
func = __method__.to_s
puts "You are in #{func}"
end
I understand that __method__ returns the name of the method it is currently being executed in.
I am trying to call get_pos() from test and this is the output I want to get:
def test
get_pos
end
You are in test
Instead I get the following
You are in get_pos
I understand why this is happening. Since __method__ is located inside the getpos function it returns the name of that function.
I know that if i make the following change and pass __method__ as an argument to the function, I'll get the expected result. Which is:
def get_pos(method)
puts "You are in #{method}"
end
def test
get_pos(__method__.to_s)
end
You are in test
The code has been simplified but is part of functionality in a logger where I want to be able to dump data about the current location in the code to a log and know exactly what module,class,function I am in.
Is there a better/cleaner way to do this than passing __method__ as a parameter to the function each time?
Why don't you use __callee__ from Kernel object?
I refactored your code:
def current
puts __callee__
end
def test_caller
current
end
test_caller
Which outputs current in this case.
There are all sorts of interesting methods in the Kernel Object. I recommend to take a look to the API here.
You can use caller_locations which returns an array of Thread::Backtrace::Location instances: (starting at index 1 by default, excluding the current method)
def foo
caller_locations.map(&:base_label)
end
def bar
foo
end
def baz
bar
end
baz
#=> ["bar", "baz", "<main>"]
So foo was called from bar which was called from baz which was called in <main>.

Intercepting def in a block

I have a method that needs to do a bit of sorcery on the attached block. A sample of such a block might be
myMethod do
somemethod x
someother y
def name(a,b)
a+b
end
end
the first two method calls (somemethod x and someother y) should just be executed as normal. However I'd like to intercept the method definition (as S-expression) without actually defining a new method. I can do this if I transform the entire block to S-expressions and then search through the AST. However then I need to figure out how to call the methods. A solution to either will do. That is either
intercepting the definition, transform to S-expression (that Ruby2Ruby can understand)
transform the block to S-expressions and find the method calls and execute these
EDIT
The AST I'm looking for is something similar to
[:defn,
:name,
[:args,:a,:b],
[:call,
[lvar,:a],
:+
[lvar,:b]]]
If I understand correctly, you want to be able to define a method within a code block passed to another method, but intercept that inner method definition so that it doesn't actually get defined but is instead converted to an S-expression? So you want it to behave almost as if it were commented out and some external process had come through the source code and parsed out the implementation into an S-expression?
Something like:
myMethod do
somemethod x
someother y
# def name(a,b)
# a+b
# end
end
Where somemethod and someother still execute, the implementation of name is defined, but is ignored by the Ruby interpreter. Then you somehow want to capture this implementation as an S-expression. It obviously wouldn't be done like this, by commenting it out, but its a nice way for me to picture the behavior.
Well, the RubyParser gem might do what you want. It takes advantage of the ability to pass in a 'here document' as a string to a method, as in this example:
def x (str)
str
end
x( <<-EOF )
this is
a here
document
EOF
# => "this is\n a here\ndocument\n"
Using RubyParser, you can do something like the following:
require 'ruby_parser'
require 'pp'
pp RubyParser.new.parse( <<-EOF )
def plus(x,y)
x+y
end
EOF
# => s(:defn, :name, s(:args, :a, :b), s(:call, s(:lvar, :a), :+, s(:lvar, :b)))
It would be a trivial matter, then, to merge it with your desired code, like so:
require 'ruby_parser'
def myMethod
x = yield
end
def somemethod( x )
puts x
end
def someother( y )
puts y
end
x = 'magic'
y = 'output'
sexp = myMethod do
somemethod x
someother y
RubyParser.new.parse( <<-EOF )
def name(a,b)
a+b
end
EOF
end
pp sexp
name(1,2)
# magic
# output
# s(:defn, :name, s(:args, :a, :b), s(:call, s(:lvar, :a), :+, s(:lvar, :b)))
# NoMethodError: undefined local variable or method 'name' for main:Object
As you can see, the method definition is essentially just a string, and can be manipulated as such.

Getting the name of the defined method

I know that I can capture the moment of a method definition by using set_trace_func.
set_trace_func ->event, file, line, method, binding, klass{
if event == "c-call" and method == :method_added
# The moment of method definition
end
}
Is it possible to capture the name of the method being defined at such moment? I know that the class can be captured by eval("self", binding). What code can I put inside the block shown above to capture the method name?
Is it further possible to get the format of the arguments for the method being defined (the required arguments, the rest of the arguments, and their names as is in the source)?
Outside of set_trace_func, you could use Module.method_added:
class Test
def self.method_added(method_name)
puts "#{method_name} added to #{self}"
end
def foo
"foo"
end
end
$ ruby test.rb
# => foo added to Test
Check the documentation.
The Kernel.set_trace_func proc allows you catch an id parameter. This—most times—is the function name.
However, learning from your example, you can also get the current running method using eval("__method__", binding) …but I think this only gets the methods you have defined in your classes.

Ruby any way to catch messages before method_missing?

I understand that method_missing is something of a last resort when Ruby is processing messages. My understanding is that it goes up the Object hierarchy looking for a declared method matching the symbol, then back down looking for the lowest declared method_missing. This is much slower than a standard method call.
Is it possible to intercept sent messages before this point? I tried overriding send, and this works when the call to send is explicit, but not when it is implicit.
Not that I know of.
The most performant bet is usually to use method_missing to dynamically add the method being to a called to the class so that the overhead is only ever incurred once. From then on it calls the method like any other method.
Such as:
class Foo
def method_missing(name, str)
# log something out when we call method_missing so we know it only happens once
puts "Defining method named: #{name}"
# Define the new instance method
self.class.class_eval <<-CODE
def #{name}(arg1)
puts 'you passed in: ' + arg1.to_s
end
CODE
# Run the instance method we just created to return the value on this first run
send name, str
end
end
# See if it works
f = Foo.new
f.echo_string 'wtf'
f.echo_string 'hello'
f.echo_string 'yay!'
Which spits out this when run:
Defining method named: echo_string
you passed in: wtf
you passed in: hello
you passed in: yay!

What's the equivalent to "method reference" in Ruby

for instance in python it is possible to assign a method to a variable:
class MyClass
def myMethod(self):
return "Hi"
x = MyClass()
method = x.myMethod
print method() # prints Hi
I know this should be possible in Ruby, but I don't know what's the syntax.
You need to grab the method by using method with the method’s name as an argument. This will return you an instance of type Method, which can be called with call().
class MyClass
def myMethod
"Hi"
end
end
x = MyClass.new
m = x.method(:myMethod)
# => #<Method: MyClass#myMethod>
puts m.call
# You can also do m[] instead of m.call()
Note that any arguments would need to be added to the call method.
In many practical cases, however, there is no need to have the method itself saved to a variable in Ruby; if you just want to dynamically call a method (i.e. send a message to an object) and there is no need to save the method, you could also use the send (or __send__ method in case of name clashes).
x = MyClass.new
puts x.send :myMethod # also possible with a string: m.send "myMethod"
# "Hi"
Any arguments should follow the method name:
puts x.send(:myMethod, arg1, arg2)
To use it like this is probably more Ruby-like, as the concept of Method classes is not as prominent as it is in Python. In Python, you can always think of a two step mechanism when doing something like a_string.split(); first you grab the method with a_string.split and then you call it (either implicitly with () or explicitly with __call__()). So, cutting that two-step mechanism is rather natural to do.
Ruby is more based on message passing and to actually get a method class in Ruby, you’ll have to do some more work, because in some way, the method object will have to be constructed for you at that point. So, unless you really need some Methods object in Ruby, you should rather stick to the message passing abstraction and simply use send.
I think you are looking for Proc or lambda block
x = Proc.new { return "Hello World" }
puts x.call
x = lambda { return "Hello World" }
puts x.call
I would read this short post - there is a slight but significant difference in the way the methods behave
http://samdanielson.com/2007/3/19/proc-new-vs-lambda-in-ruby

Resources