Any way to determine which object called a method? - ruby

I'm hoping that Ruby's message-passing infrastructure means there might be some clever trick for this.
How do I determine the calling object -- which object called the method I'm currently in?

You can easily look at the line of code that called the function of interest through
caller.first
which will tell you the filename and line number which called the relevant function. You could then back-calculate which object it was.
However, it sounds like you're more after some object that called a certain function, perhaps within an instance method. I'm not aware of a method for figuring this out - but I wouldn't use it anyway, since it seems to violate encapsulation badly.

As an option, there is a binding_of_caller gem that allows you to execute code in context of any caller on the call stack (caller, caller's caller and so on). It's useful for inspecting (read do anything at any position on the call stack) call stack in development, as used in better_errors.
Objects of class Binding encapsulate the execution context at some particular place in the code and retain this context for future use.
– http://www.ruby-doc.org/core-2.1.4/Binding.html
Should I mention, this technique should only be used for debugging, fun or educational purposes, because it violates principles of OOP really badly.
Mostly because of eval.
Let's prepare stuff:
require 'binding_of_caller' # I assume, you installed this gem already?
Get the immediate (closest on stack, hence 0) caller instance:
binding.of_caller(0).eval('self')
...or even an immediate calling method:
binding.of_caller(0).eval('__method__')
If you need to get higher up the call stack, use numbers other than 0 for getting a caller's binding.
Awfully hacky. But if you really need this — there you go.

Technology at its finest:
1 # phone.rb
2 class Phone
3 def caller_id
4 caller
5 end
6 end
7
8 class RecklessDriver
9 def initialize
10 #phone = Phone.new
11 end
12 def dial
13 #phone.caller_id
14 end
15 end
16
17 p = Phone.new
18 p.caller_id.inspect # => ["phone.rb:18:in `<main>'"]
19
20 macek = RecklessDriver.new
22 macek.dial.inspect # => ["phone.rb:13:in `dial'", "phone.rb:22:in `<main>'"]
Note: Line number for demonstrative purposes. phone.rb:X refers to Line X of the script.
Look at phone.rb:13! This dial method is what sent the call! And phone.rb:22 refers to the reckless driver that used the dial method!

You mean like self?
irb> class Object
.. def test
.. self
.. end
.. end
=> nil
irb> o = Object.new
=> #<Object:0xb76c5b6c>
irb> o.test
=> #<Object:0xb76c5b6c>

Peter's answer used in production code example
In my company we were deprecating deleted flag in flavor of Paranoia gem deleted_at column. The code bellow is how we were ensuring all will go well before we remove column (deploying this code and then after 2 or 3 days of being live we deploy migration remoove_column :lessons, :deleted
class Lesson < ActiveRecord::Base
def deleted
if caller.select { |c| c.match /serialization\.rb/ }.any?
# this is Rails object mapping
!!deleted_at
else
raise 'deplicated - deleted was replaced by deleted_at'
end
end
end

Related

Is there a way using TracePoint in ruby 2.0 to get the time between call and return of methods?

I'm trying to figure out if I can get the time it takes for a method to execute using TracePoint in ruby 2.0. Any help is appreciated.
Update
I want to clarify this question. My goal is to get the time it takes for all methods to execute even if you don't know what those methods will be. I've found this to be quite tricky. Deivid's response below involves setting a t0 and t1 variables in a shared context, then setting the time values on call and return. While this works for a simple example, it becomes unmanageable when trying to log the time of all method calls in a more complex ruby program. Take for example the following program:
class C
def self.slow
C.fast
sleep 3
end
def self.fast
end
end
C.slow
In this program, it is only possible to monitor t0 and t1 times if you keep track of the method names being called. In an even more complex program such as a Rails app with many stack frames where you do not know in advance all of the methods that will be executed, it is not as obvious as to the best way to monitor and print all call and return times.
The solution 'may' involve keeping a hash of call times where the key is some combination of the Thread.current.object_id and the tp.method_id.. I have not found the correct key though that is unique enough to ensure that the return time can be matched to the caller, considering you may have recursive method calls that create non-standard call and return situations.
class C
def self.slow n
return C.fast if n == 0
sleep 1
slow n-1
end
def self.fast
end
end
#times = {}
traceCall = TracePoint.new(:call, :return) do |tp|
key = "#{tp.defined_class}_#{tp.method_id}_#{caller(0).size}"
if tp.event == :call
#times[key] = Time.now
else
#times[key] = Time.now - #times[key]
end
end.enable do
C.slow 3
end
p #times

Why ruby constants are changable? What is the difference between variable?

I was learning ruby, and i learnt that ruby constants must start with a Upper case letter (e.g. Myconstant). This will make it a constant, but its value is changeable!
If a constant's value is changeable then why do we need constant, what is the difference between variable then?
Constants have lexical scoping, whereas methods have dynamic scoping:
class Super
Constant = "Super::Constant"
def method
'Super#method'
end
def get_constant
Constant
end
def get_method
method
end
end
class Sub < Super
Constant = 'Sub::Constant'
def method
'Sub#method'
end
end
Super.new.get_constant # => "Super::Constant"
Sub.new.get_constant # => "Super::Constant"
Super.new.get_method # => "Super#method"
Sub.new.get_method # => "Sub#method"
And as far as variables, they are inaccessible from the outside. How would you intend to access these?
class Object
Constant = 'constant'
local_var = 'local var'
#instance_var = 'instance var'
##class_var = 'class var' # btw, never use these
end
Also, there's a lot of things you can do in Ruby, but for your own sanity, be wary. I'd recommend against changing constants around, it will likely frustrate your team.
Ruby lets you shoot yourself in the foot (if you really want to). But, at least in this case, it warns you about it.
ONE = 'one'
ONE = 'two' # !> already initialized constant ONE
Some reasons:
1) Convention. It's easy to see just from the name of an identifier that it's not supposed to change.
2) Technical. It (probably; someone more knowledgeable than I will probably answer) makes the interpreter simpler.
3) Dynamism is sometimes helpful; in testing, for example, it's possible to redefine things for testing purposes rather than having to stub/proxy everything…
I use this feature sometimes to test out code without otherwise necessary parameters, eg when i run the script from my editor where it is difficult to provide a parameter.
#ARGV[0] = "c:/test.txt" #in case of testing i remove the first remark sign

Rspec: access to instance inside Klass.any_instance.stub block

Feature: test randomness
In order to make some code testable
As a developer
I want Array#sample to become Array#first
It would be possible if one could access instance inside Klass.any_instance.stub block. Something like this:
Array.any_instance.stub(:sample) { instance.first }
But that afaik is not possible.
Anyway, scenarios wanted!
I found a hacky solution, which I've tested on rspec versions 2.13.1 and 2.14.4. You'll need the binding_of_caller gem.
Helper method - this should be callable by your rspec example:
# must be called inside stubbed implementation
def any_instance_receiver(search_limit = 20)
stack_file_str = 'lib/rspec/mocks/any_instance/recorder.rb:'
found_instance = nil
# binding_of_caller's notion of the stack is different from caller(), so we need to search
(1 .. search_limit).each do |cur_idx|
frame_self, stack_loc = binding.of_caller(cur_idx).eval('[self, caller(0)[0]]')
if stack_loc.include?(stack_file_str)
found_instance = frame_self
break
end
end
raise "instance not found" unless found_instance
return found_instance
end
Then in your example:
Array.any_instance.stub(:sample) do
instance = any_instance_receiver
instance.first
end
I've set a limit on the stack searching, to avoid searching a huge stack. I don't see why you'd need to increase it, since it should always be around cur_idx == 8.
Note that using binding_of_caller is probably not recommended in production.
For those stumbling across this now, Rspec 3 implements this functionality via the first argument in the block passed to stub:
RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks = true # I believe this is the default
Array.any_instance.stub(:sample) { |arr| arr.first }
I found this here.

Ruby case statement with multiple variables using an Array

I'd like to compare multiple variables for a case statement, and am currently thinking overriding the case equals operator (===) for Array is the best way to do it. Is this the best way?
Here is an example use case:
def deposit_apr deposit,apr
# deposit: can be nil or 2 length Array of [nil or Float, String]
# apr: can be nil or Float
case [deposit,apr]
when [[Float,String],Float]
puts "#{deposit[0]} #{deposit[1]}, #{apr*100.0}% APR"
when [[nil,String],Float]
puts "#{apr*100.0}% APR on deposits greater than 100 #{deposit[1]}"
when [[Float,String],nil]
puts "#{deposit[0]} #{deposit[1]}"
else
puts 'N/A'
end
end
The only problem is the Array case equals operator doesn't apply the case equal to the elements of the Array.
ruby-1.9.2-p0 > deposit_apr([656.00,'rupees'],0.065)
N/A
It will if I override, but am not sure what I'd be breaking if I did:
class Array
def ===(other)
result = true
self.zip(other) {|bp,ap| result &&= bp === ap}
result
end
end
Now, it all works:
ruby-1.9.2-p0 > deposit_apr([656.00,'rupees'],0.065)
656.0 rupees, 6.5% APR
Am I missing something?
I found this question because I was looking to run a case statement on multiple variables, but, going through the following, came to the conclusion that needing to compare multiple variables might suggest that a different approach is needed. (I went back to my own code with this conclusion, and found that even a Hash is helping me write code that is easier to understand.)
Gems today use "no monkey patching" as a selling point. Overriding an operator is probably not the right approach. Monkey patching is great for experimentation, but it's too easy for things to go awry.
Also, there's a lot of type-checking. In a language that is designed for Duck Typing, this clearly indicates the need for a different approach. For example, what happens if I pass in integer values instead of floats? We'd get an 'N/A', even though that's not likely what we're looking for.
You'll notice that the example given in the question is difficult to read. We should be able to find a way to represent this logic more clearly to the reader (and to the writer, when they revisit the code again in a few months and have to puzzle out what's going on).
And finally, since there are multiple numbers with associated logic, it seems like there's at least one value object-type class (Deposit) that wants to be written.
For cleanliness, I'm going to assume that a nil APR can be considered a 0.0% APR.
class Deposit
def initialize(amount, unit='USD', options={})
#amount = amount.to_f # `nil` => 0.0
#unit = unit.to_s # Example assumes unit is always present
#apr = options.fetch(:apr, 0.0).to_f # `apr: nil` => 0.0
end
end
Once we have our Deposit object, we can implement the print logic without needing case statements at all.
class Deposit
# ... lines omitted
def to_s
string = "#{#amount} #{#unit}"
string << ", #{#apr * 100.0}% APR" if #apr > 0.0
string
end
end
d = Deposit.new(656.00, 'rupees', apr: 0.065)
d.to_s
# => "656.0 rupees, 6.5% APR"
e = Deposit.new(100, 'USD', apr: nil)
e.to_s
# => "100.0 USD"
f = Deposit.new(100, 'USD')
f.to_s
# => "100.0 USD"
Conclusion: If you're comparing multiple variables in a case statement, use that as a smell to suggest a deeper design issue. Multiple-variable cases might indicate that there's an object that wants to be created.
If you are worried about breaking something by changing Array behavior, and certainly that's a reasonable worry, then just put your revised operator in a subclass of Array.
it's definitely not the best way. even more - you should not redefine methods of standart classes as core functionality may depend on it - have fun debugging then.
defensive style is nice(with lot of type checks and whatnot) but it usually hurts performance and readability.
if you know that you will not pass anything else than bunch of floats and strings to that method - why do you need all those checks for?
IMO use exception catching and fix the source of problem, don't try to fix the problem somewhere in the middle

How can I get source and variable values in ruby tracebacks?

Here's the last few frames of a typical Ruby on Rails traceback:
And here are the last few frames of a typical Nevow traceback in Python:
It's not just the web environment either, you can make similar comparisons between ipython and irb. How can I get more of these sorts of details in Ruby?
AFAIK, once an exception has been caught it's too late to grab the context in which it was raised. If you trap the exception's new call, you could use evil.rb's Binding.of_caller to grab the calling scope, and do
eval("local_variables.collect { |l| [l, eval(l)] }", Binding.of_caller)
But that's quite a big hack. The right answer is probably to extend Ruby to allow some inspection of the call stack. I'm not sure if some of the new Ruby implementations will allow this, but I do remember a backlash against Binding.of_caller because it will make optimizations much harder.
(To be honest, I don't understand this backlash: as long as the interpreter records enough information about the optimizations performed, Binding.of_caller should be able to work, although perhaps slowly.)
Update
Ok, I figured it out. Longish code follows:
class Foo < Exception
attr_reader :call_binding
def initialize
# Find the calling location
expected_file, expected_line = caller(1).first.split(':')[0,2]
expected_line = expected_line.to_i
return_count = 5 # If we see more than 5 returns, stop tracing
# Start tracing until we see our caller.
set_trace_func(proc do |event, file, line, id, binding, kls|
if file == expected_file && line == expected_line
# Found it: Save the binding and stop tracing
#call_binding = binding
set_trace_func(nil)
end
if event == :return
# Seen too many returns, give up. :-(
set_trace_func(nil) if (return_count -= 1) <= 0
end
end)
end
end
class Hello
def a
x = 10
y = 20
raise Foo
end
end
class World
def b
Hello.new.a
end
end
begin World.new.b
rescue Foo => e
b = e.call_binding
puts eval("local_variables.collect {|l| [l, eval(l)]}", b).inspect
end

Resources