The following prints Bar twice:
class Foo
def foo
p self.class # => prints Bar
end
end
class Bar < Foo
def foo
p self.class # => prints Bar
super
end
end
b = Bar.new
b.foo
How do I get it to print
Bar
Foo
? i.e. I want to know what class each method is defined on.
To capture the context in which a method was originally defined, you can use define_method instead of def to get the appropriate closure. A simple example:
class Foo
klass = self
define_method(:foo){p klass}
end
class Bar < Foo
def foo
p self.class
super
end
end
b = Bar.new
b.foo
You could change Foo#foo like so (provided there is just one subclass level):
class Foo
def foo
if self.class == Foo
p self.class
else
p self.class.superclass
end
end
end
class Bar < Foo
def foo
p self.class
super
end
end
Foo.new.foo
Foo
Bar.new.foo
Bar
Foo
You can use
b.class.superclass <= "Foo"
The problem you are having there is that self is the instance of Bar, b.
b.class <= always going to be Bar
self.class <= always going to be Bar if you are invoking Bar.
You say that you are defining a method at runtime, and that you don't know the class name. I don't really know what you mean ... the way I would handle this would be something like
class Bar
def initialize
puts 'In BAR class'
end
def foo
p self.class.name # => prints Bar
end
end
and then
Bar.class_eval do
def brand_new_method
# do something new
p "Still in Bar, but this is dynamically added"
end
end
Maybe you are talking about dynamically adding methods to classes higher in the inheritance chain ... to "Foo" in your example ... based on some conditional happening in an instance of "Bar". If that is the case, then why don't you use a single module to define your inherited methods:
module Foo
def foo
p self.class
end
end
and then use module_eval the same way as class_eval?
Related
Suppose I have a setup like this:
class Foo
attr_accessor :bar
def initialize
#bar = Bar.new
end
end
class Bar
def bar_method
self.class # => Bar
whatever???.class # => Foo
end
end
foo = Foo.new
foo.bar.bar_method
I know that I can set up the method like this:
def bar_method(selfs_self)
selfs_self.class # => Foo
end
And call the method like this: foo.bar.bar_method(foo) to get what I want. But that seems pretty redundant. Is there any way, inside of bar_method, that I can get a reference to foo, without specifically passing in a reference to it?
No.
Usually this is done by passing a reference to the parent object when initializing child objects, like:
class Foo
attr_accessor :bar
def initialize
#bar = Bar.new(self)
end
end
class Bar
attr_reader :foo
def initialize(foo)
#foo = foo
end
def bar_method
self.class # => Bar
foo.class # => Foo
end
end
I have self-writed gem
module GemNamespace
class Foo; end
class Bar
def foo
#foo ||= Foo.new
end
end
end
Also I have application
module ApplicationNamespace
class Foo < GemNamespace::Foo; end
class Bar < GemNamespace::Bar; end
end
When I call foo method at my application it returned me instanceof GemNamespace object:
bar = ApplicationNamespace::Bar.new
puts bar.foo
=> #<GemNamespace::Foo:0x007f849d8169f0>
But I want get object of ApplicationNamespace how I can do this without redefine foo method
Your Problem is not, that you have several Namespaces, but that GemNamespace::Bar is tightly coupled to GemNamespace::Foo.
You could use something like this:
class Bar
def initialize(klass)
#klass = klass
end
def foo
#foo ||= #klass.new
end
end
So instead of only ever using GemNamespace::Foo within Bar, you could pass any class.
Your current version of the foo method will allways refer to GemNamespace::Foo because its context is set at definition (not at execution). Instead you could get the module of the current executing class dynamically. I don't think there is a build-in method that does this so you have to get it manually:
def foo
#foo ||= self.class.name.split("::")[0..-2].inject(Kernel) { |s, c| s.const_get c }.const_get("Foo").new
end
This will work for any number of nested modules.
In this example code where I have child classes (Bar, Baz) inheriting the class methods of the parent (Foo), how might I ensure #foo is only created once across all children?
class Foo
def self.foo
# only want #foo to be set once across any child classes
# that may call this inherited method.
#foo ||= expensive_operation
end
end
class Bar < Foo
def self.bar
self.foo + 'bar'
end
end
class Baz < Foo
def self.baz
self.foo + 'baz'
end
end
Instead of depending on a class-specific instance variable being present, just reference it directly:
class Baz < Foo
def self.baz
Foo.foo + 'baz'
end
end
This is exactly what class instance variable is intended for - language mechanism to have one variable shared between classes which inherit from a class. It's generally not recommended to use because it behaves like you want to - and that's confusing for people coming from other languages.
class Foo
def self.foo
# notice ##
##foo ||= expensive_operation
end
def self.expensive_operation
puts "Expensive operation"
"cached value "
end
end
class Bar < Foo
def self.bar
self.foo + 'bar'
end
end
class Baz < Foo
def self.baz
self.foo + 'baz'
end
end
Foo.foo
Bar.bar
Baz.baz
This prints Expensive operation only once.
How I can get all instance method names in the baz method call, which are only present in the Bar module (without other instance methods of this class) ?
class Foo
include Bar
def a
end
def b
end
def baz
#HERE
end
end
class Foo
include Bar
def a
end
def b()
end
def baz
Bar.instance_methods(false)
end
end
puts Foo.new.baz
I am trying to define some classes in Ruby that have an inheritance hierarchy, but I want to use one of the methods in the base class in the derived class. The twist is that I don't want to call the exact method I'm in, I want to call a different one. The following doesn't work, but it's what I want to do (basically).
class A
def foo
puts 'A::foo'
end
end
class B < A
def foo
puts 'B::foo'
end
def bar
super.foo
end
end
Probably, this is what you want?
class A
def foo
puts 'A::foo'
end
end
class B < A
alias bar :foo
def foo
puts 'B::foo'
end
end
B.new.foo # => B::foo
B.new.bar # => A::foo
A more general solution.
class A
def foo
puts "A::foo"
end
end
class B < A
def foo
puts "B::foo"
end
def bar
# slightly oddly ancestors includes the class itself
puts self.class.ancestors[1].instance_method(:foo).bind(self).call
end
end
B.new.foo # => B::foo
B.new.bar # => A::foo