Reputation: 45943
The meow
method is delegated to the Cat. Is it possible to access the Person?
class Person
attr_reader :cat
delegate :meow, to: :cat
def initialize
@cat = Cat.new
end
end
class Cat
def meow
# How to access Person who called meow?
end
end
Person.new.meow
Upvotes: 0
Views: 497
Reputation: 3811
my solution: i create a module that will override delegate
method and add some code that will setup cat.delegator
to the caller person
each time he delegate meo
to his cat
module ForFunDelegator
def delegate(*methods, to: nil, prefix: nil, allow_nil: nil)
super
location = caller_locations(1, 1).first
file, line = location.path, location.lineno
methods.map do |method|
definition = /[^\]]=$/.match?(method) ? "arg" : "*args, &block"
method_def = [
"alias old_delegate_#{method} #{method}",
"def #{method}(#{definition})",
"def #{to}.delegator=(delegator)",
"@delegator = delegator",
"end",
"def #{to}.delegator",
"@delegator",
"end",
"#{to}.delegator = self",
"old_delegate_#{method}(#{definition})",
"end"
].join ";"
module_eval(method_def, file, line)
end
end
end
demo
class Person
extend ForFunDelegator
attr_reader :cat
delegate :meow, to: :cat
def initialize
@cat = Cat.new
end
end
class Cat
def meow
puts delegator if respond_to?(:delegator) # Person:0x00007fa158868098
end
end
Person.new.meow
now the Cat
or anything else no need to concern about the self
which is the delegator, the delegator module will do on behalf of them.
Upvotes: 0
Reputation: 44685
I think this is not really possible, or at least not in a way it is requested. Haing an access to an object invoking a method would create an absolutely fascinating type of coupling where receiver is coupled to the the caller.
That being said, there are some ways to get around this. As @ChristianBruckMayes already answered, your Cat
class could accept extra owner
. But this might not work, if the generic concept of Cat is quite owner-less or when one Cat
might be an attribute of multiple Person
.
One common pattern I use is a inner proxy object:
class Person
attr_reader :cat
delegate :meow, to: :cat
def initialize(cat = Cat.new)
@cat = OwnedCat.new(cat, owner: self)
end
class OwnedCat
def initialize(cat, owner:)
@cat = cat
@owner = owner
end
def meow
# access to cat and owner here
end
# delegate everything else to cat object
def method_missing(name, *args, &block)
return super unless @cat.respond_to?(name)
@cat.send(name, *args, &block)
end
def respond_to_missing?(name, private = false)
@cat.respond_to?(name, private)
end
end
end
Alternatively you could just pass self
to the method invcation:
class Cat
def meow(owner: nil)
if owner
...
else
...
end
end
end
def Person
def initialize
@cat = Cat.new
end
def meow
@cat.meow(owner: self)
end
end
Upvotes: 1
Reputation: 2187
As BroiSatse already said, the example doesn't really make sense. However, you could e.g. pass in the person to the Cat.
class Person
attr_reader :cat
delegate :meow, to: :cat
def initialize
@cat = Cat.new(self)
end
end
class Cat
def initialize(owner)
@owner = owner
end
def meow
puts "#{@owner} - meow"
end
end
Person.new.meow
Another approach could be to use SimpleDelegator
instead.
class User
def born_on
Date.new(1989, 9, 10)
end
end
class UserDecorator < SimpleDelegator
def birth_year
born_on.year
end
end
And then you can access the receiver with super
or __getobj__()
https://ruby-doc.org/stdlib-2.5.1/libdoc/delegate/rdoc/SimpleDelegator.html
Upvotes: 1