Any way to determine which object called a method?
Solution 1
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.
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.
Solution 2
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.
Solution 3
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!
Solution 4
You mean like self
?
irb> class Object
.. def test
.. self
.. end
.. end
=> nil
irb> o = Object.new
=> #<Object:0xb76c5b6c>
irb> o.test
=> #<Object:0xb76c5b6c>
Solution 5
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 videos on Youtube
Joseph Weissman
Hi! I'm Joseph. I am a computer programmer (my Github) and thinker/writer (my working group's blog.) I'm one of the friendly neighborhood moderators at Philosophy StackExchange.
Updated on October 22, 2020Comments
-
Joseph Weissman over 3 years
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?
-
Pierreten about 14 years++ Dude, that's totally awesome how that looks like a stylized Z
-
Joseph Weissman about 14 yearsThat's a really good point, I will just pass the calling object instead. The idea was to simplify some of the arguments to a method by automatically reflecting some information about the calling object.
-
Peter about 14 yearswell, ideally, if it's more than once, it should be a method in a superclass, and you can use
self
. -
lulalala over 8 yearsI read the question that inside your
test()
OP wants to obtain the the object that you get if just typeself
in irb.