How can I programmatically get the class on which the currently executing code is defined? I need to find the class when control flow runs through multiple method definitions due to super():
class A
def foo
puts(get_current_class)
end
end
class B < A
def foo
puts(get_current_class)
super
end
end
class C < B
def foo
puts(get_current_class)
super
end
end
C.new.foo
# => C
# => B
# => A
I know how to get the method name (using __callee__, caller_locations or __method__); but what about the class?
Since classes in ruby are also modules, this could be achieved with Module#nesting:
class A
def foo
puts(Module.nesting.first)
end
end
class B < A
def foo
puts(Module.nesting.first)
super
end
end
class C < B
def foo
puts(Module.nesting.first)
super
end
end
C.new.foo
# => C
# => B
# => A
Alternatively, if the goal is to construct a list of which methods may be called by the object's ancestor chain, then you could use Method#owner and Method#super_method (available since ruby version 2.2.0):
c = C.new
c.method(:foo).owner # => C
c.method(:foo).super_method.owner # => B
c.method(:foo).super_method.super_method.owner # => A
c.method(:foo).super_method.super_method.super_method # => nil
As a quick off-the-cuff implementation to programatically print all classes then, how about:
c = C.new
method = c.method(:foo)
while(method)
puts m.owner
method = method.super_method
end
# => C
# => B
# => A
(However, there is no guarantee that all of these methods will actually be invoked - as this is determined at runtime via super!)
Related
I'm trying to do something like this in Ruby:
class A
def b(c)
Class.new do
def d
c
end
end
end
end
class B < A.b('whoa'); end
#
# What I want:
#
B.new.d # => 'whoa'
#
# What I get:
#
# NameError: undefined local variable or method `d' for B:0xfdfyadayada
Is there any way to do this?
The context is I'm trying to get some code reuse by constructing classes that are mostly similar except for some minor config.
I'm looking for an API similar to Sequel::Model where you can do something like:
class Order < Sequel::Model(:order)
The contents of a method definition using the def keyword are not lexically scoped the way blocks are. In other words you can’t do something like this:
foo = 7
def bar
# Code here inside the method definition can't
# "see" foo, so this doesn't work.
foo
end
However you can use the define_method method to dynamically define a method using a block, and the block will be able to refer to local variables in its outer scope. So in your case you could do this (I’ve also changed def b to def self.b, which I think is what you meant):
class A
def self.b(c)
Class.new do
define_method(:d) do # N.B. the d here is a symbol
c
end
end
end
end
class B < A.b('whoa'); end
B.new.d # => 'whoa'
This would also work if you created other classes:
class C < A.b('dude'); end
C.new.d # => 'dude'
# B still works
B.new.d # => 'whoa'
Assuming that b is a class method (i.e def self.b instead of def b), you could store c in a class variable.
class A
def self.b(c)
Class.new do
##c = c
def d
##c
end
end
end
end
class B < A.b('whoa'); end
puts B.new.d # => whoa
There could be a better solution, I haven't looked at the source code of Sequel. This is just the first thing that came to my mind.
My understanding of instance_eval was that if I have module M then the following were equivalent:
module M
def foo
:foo
end
end
class C
class << self
include M
end
end
puts C.foo
equivalent to:
module M
def foo
:foo
end
end
class C
end
C.instance_eval do
include M
end
puts C.foo
However, the first example prints :foo and the second throws a NoMethodError? (Ruby 2.3.0)
In both cases above, if I had replaced:
include M
with:
def foo
:foo
end
ie directly defining the method rather than including a module then both cases would have resulted in a C.foo method being defined. Should I be surprised at this difference between include and defining the method directly?
Or does it ever even make sense to call include within the context of instance_eval? Should it only ever be called within a class_eval?
In each of these cases, what object are you calling include on? In your first example, you're calling include on C's singleton class:
class C
class << self
p self == C.singleton_class
include M
end
end
# => true
p C.foo
# => :foo
...so your include line is equivalent to C.singleton_class.include(M).
In your second example, however, you're calling include on C itself:
class C
end
C.instance_eval do
p self == C
include M
end
# => true
p C.foo
# => NoMethodError: undefined method `foo' for C:Class
p C.new.foo
# => :foo
...so you're doing the equivalent of C.include(M), which is the same as:
class C
p self == C
include M
end
# => true
p C.new.foo
# => :foo
What would work like you want would be to call instance_eval on C's singleton class:
class D
end
D.singleton_class.instance_eval do
p self == D.singleton_class
include M
end
# => true
p D.foo
# => :foo
Module#class_eval() is very different from Object#instance_eval(). The instance_eval() only changes self, while class_eval() changes both self and the current class.
Unlike in your example, you can alter class_instance vars using instance_eval though, because they are in the object scope as MyClass is a singleton instance of class Class.
class MyClass
#class_instance_var = 100
##class_var = 100
def self.disp
#class_instance_var
end
def self.class_var
##class_var
end
def some_inst_method
12
end
end
MyClass.instance_eval do
#class_instance_var = 500
def self.cls_method
##class_var = 200
'Class method added'
end
def inst_method
:inst
end
end
MyClass.disp
#=> 500
MyClass.cls_method
#=> 'Class method added'
MyClass.class_var
#=> 100
MyClass.new.inst_method
# undefined method `inst_method' for #<MyClass:0x0055d8e4baf320>
In simple language.
If you have a look in the upper class defn code as an interpreter, you notice that there are two scopes class scope and object scope. class vars and instance methods are accessible from object scope and does not fall under jurisdiction of instance_eval() so it skips such codes.
Why? because, as the name suggests, its supposed to alter the Class's instance(MyClass)'s properties not other object's properties like MyClass's any object's properties. Also, class variables don’t really belong to classes—they belong to class hierarchies.
If you want to open an object that is not a class, then you can
safely use instance_eval(). But, if you want to open a class definition and define methods with def or include some module, then class_eval() should be your pick.
By changing the current class, class_eval() effectively reopens the class, just like the class keyword does. And, this is what you are trying to achieve in this question.
MyClass.class_eval do
def inst_method
:inst
end
end
MyClass.new.inst_method
#=> :inst
I can define an anonymous class within an anonymous module:
c = nil
m = Module.new do
c = Class.new
end
m #=> #<Module:0x007fad3a055660>
c #=> #<Class:0x007fad3a0555e8>
Is the above equivalent to:
m = Module.new
c = Class.new
In other words: does the concept of "nesting" actually apply to anonymous modules?
It is not about being anonymous. Assigning a dynamically created class to a constant makes it named:
Foo = Class.new # => Foo
foo = Class.new # => #<Class:0x007fe5dd45d650>
Yet it still doesn't nest further:
module Bar
Baz = Module.new do
p Module.nesting # => [Bar]
end
end
Or even about being dynamic for that matter:
module Quz
eval 'module Qux; p Module.nesting; end' # => [Quz::Qux, Quz]
end
It's about scope gates.
As far as constants are concerned, there are only two scope gates - the keywords class and module.
Nesting is done purely syntactically. That is why you get the weird:
module Do
X = 42
end
module Do
module Re
p Module.nesting # => [Do::Re, Do]
p X # => 42
end
end
module Do::Mi
p Module.nesting # => [Do::Mi]
p X # => uninitialized constant
end
Do.module_eval { p X } # => uninitialized constant
Do.instance_eval { p X } # => uninitialized constant
So if Ruby sees the keywords class or module, it nests the "current node" further. When the closing end is found, it goes up the tree. When a new constant is being defined, it places it in the current node.
I have searched on here for an answer to this question and couldn't find what I was after (in exact terms) so I'm gonna be slightly greedy and ask for some of the communities time. I hope to make my question as applicable as possible.
So, some context, I've been struggling with the idea of classes, class variables and methods for the last week but over the last 2 days have made serious headways in my understanding. However now I am faced with inheritance and can't work out why super would be used when I can just inherit without using it.
For example:
class Animal
def initialize (type, breed, age)
#type = type
#breed = breed
#age = age
end
end
class Dog < Animal
end
class Cat < Animal
end
class Fish
end
woof = Dog.new("dog", "shitzu", 12)
meow = Cat.new("cat", "tabby", 5)
fish = Fish.new("fish", "gold", 2)
Output:
=> #<Dog:0x00000001447680 #type="dog", #breed="shitzu", #age=12>
=> #<Cat:0x0000000140c918 #type="cat", #breed="tabby", #age=5>
ArgumentError: wrong number of arguments (given 3, expected 0)
As you can see, I have been able to inherit from Animal on both my Dog and Cat classes, which I marked for inheritance, but on my Fish I have not been able to, because I didn't inherit.
If someone could explain why we use super, and point out flaws in my understanding, I'd be very grateful, I understand that I may be completely just misunderstanding usage here, but I'd like to just get it clarified. Thanks for your time, appreciate the help.
Using super lets a class override a method that it inherits from its parent and customize it.
For instance, in your example, Dog and Cat inherit #initialize from Animal - but what if we wanted some special logic for Dog?
class Dog < Animal
def initialize(type, breed, age)
raise "Sorry, dogs don't live that long!" if age > 100
# Everything looks good - let Animal#initialize run now
super
end
end
This lets Dog customize what its initialize method does, but still call through to the original inherited method.
Excerpted from Inheritance - What is inherited?. The original author was nus. Attribution details can be found on the contributor page. The source is licenced under CC BY-SA 3.0 and may be found in the Documentation archive. Reference topic ID: 625 and example ID: 14883.
Methods are inherited
class A
def boo; p 'boo' end
end
class B < A; end
b = B.new
b.boo # => 'boo'
Class methods are inherited
class A
def self.boo; p 'boo' end
end
class B < A; end
p B.boo # => 'boo'
Constants are inherited
class A
WOO = 1
end
class B < A; end
p B::WOO # => 1
But beware, they can be overridden:
class B
WOO = WOO + 1
end
p B::WOO # => 2
Instance variables are inherited:
class A
attr_accessor :ho
def initialize
#ho = 'haha'
end
end
class B < A; end
b = B.new
p b.ho # => 'haha'
Beware, if you override the methods that initialize instance variables without calling super, they will be nil. Continuing from above:
class C < A
def initialize; end
end
c = C.new
p c.ho # => nil
Class instance variables are not inherited:
class A
#foo = 'foo'
class << self
attr_accessor :foo
end
end
class B < A; end
p B.foo # => nil
# The accessor is inherited, since it is a class method
#
B.foo = 'fob' # possible
Class variables aren't really inherited
They are shared between the base class and all subclasses as 1 variable:
class A
##foo = 0
def initialize
##foo += 1
p ##foo
end
end
class B < A;end
a = A.new # => 1
b = B.new # => 2
So continuing from above:
class C < A
def initialize
##foo = -10
p ##foo
end
end
a = C.new # => -10
b = B.new # => -9
What are the differences between:
module Mod
def here
puts 'child'
end
end
class A
prepend Mod
def here
puts 'parent'
end
end
and
class A
def here
puts 'parent'
end
end
class B < A
def here
puts 'child'
end
end
Or another way to put it: is derivating a class the same as prepending a module of the child's code?
No, it is not. B can only inherit from one class, but Mod can be prepended to many classes. If you were to call super inside B#here, it would always refer to A#here, but inside of Mod#here, it will refer to the #here instance method of whatever class Mod was prepended to:
module Mod
def here
super + ' Mod'
end
end
class A
prepend Mod
def here
'A'
end
end
class B
prepend Mod
def here
'B'
end
end
A.new.here
# => 'A Mod'
B.new.here
# => 'B Mod'
and
class A
def here
'A'
end
end
class B
def here
'B'
end
end
class C < A
def here
super + ' C'
end
end
C.new.here
# => 'A C'
class C < B
def here
super + ' C'
end
end
# TypeError: superclass mismatch for class C
No, it's totally different.
One can prepend as many modules as he wants.
module A
def foo; "A" end
end
module B
def foo; "B" end
end
class C
prepend A, B # Prepending is done by this line
def foo; "C" end
end
### take a look at it!
C.ancestors # => [A, B, C, Object, Kernel, BasicObject]
C.new.foo # => "A"
Ruby implements prepend and inheritance using different ways.
prepend is internally achieved by including modules, which causes the surprising ancestors.
here is another question about prepend which may be helpful.