With a(n inherited) method, the receiver/class where it is defined can be achieved by doing:
class A
def foo; end
end
class B < A
end
B.instance_method(:foo).owner # => A
With a(n inherited) constant, there is no counterpart to instance_method or method method, so it is not straightforward. Is it possible to achieve the class where it is defined?
class A
Foo = true
end
class B < A
end
B.some_way_to_extract_the_owner_of_constant(:Foo) # => A
Like, the below code:
class A
Foo = true
end
class B < A
end
B.ancestors.find { |klass| klass.const_defined? :Foo, false }
# => A
Similar to #Arup's answer, but I've used Module#constants.
class Base
end
class A < Base
Foo = true
end
class B < A
Foo = false
end
class C < B
end
C.ancestors.find { |o| o.constants(false).include?(:Foo) }
#=> B
Related
Here is an external class which has its class method overwritten.
class Foo
def class
"fooo"
end
end
class Boo < Foo
end
class Moo < Foo
end
Now I have an instance of the subclass. Is it possible to find out which class it belongs to?
foo.class # currently returns 'fooo', I want get Boo or Moo.
You could use instance_method to grab the class method from somewhere safe (such as Object) as an UnboundMethod, bind that unbound method to your instance, and then call it. For example:
class_method = Object.instance_method(:class)
# #<UnboundMethod: Object(Kernel)#class>
class_method.bind(Boo.new).call
# Boo
class_method.bind(Moo.new).call
# Moo
class_method.bind(Foo.new).call
# Foo
Of course, if you've also replaced Object#class (or Kernel#class) then all bets are off and you're in a whole new world of pain and confusion.
An object's class also happens to be the superclass of its singleton_class:
Boo.new.singleton_class.superclass
#=> Boo
Moo.new.singleton_class.superclass
#=> Moo
Foo.new.singleton_class.superclass
#=> Boo
I prefer #Stefan’s and #muistooshort's solutions, but here's an alternative.
class Foo
def class
"foo"
end
end
class Boo < Foo
end
class Who < Boo
def class
"who"
end
end
boo = Boo.new
boo.method(:class).super_method.call
#=> Boo
who = Who.new
who.method(:class).super_method.call
#=> "foo"
who.method(:class).super_method.super_method.call
#=> Who
More generally:
def my_class(obj)
m = obj.method(:class)
until m.owner == Kernel do
m = m.super_method
end
m.call
end
my_class(boo)
#=> Boo
my_class(who)
#=> Who
See Method#super_method.
This solution is a little hacky, but overriding class is pretty hacky in itself, so when in Rome, do as Romans do:
class Foo
def class
'fooo'
end
end
class Boo < Foo
end
boo = Boo.new
=> #<Boo:0x00007fd2361feba8>
boo.class
=> "fooo"
boo.inspect
=> "#<Boo:0x00007fd2361feba8>"
klass = boo.inspect.split(':').reject(&:empty?)[0..-2].join('::').sub('#<', '')
=> "Boo"
boo.is_a?(Kernel.const_get(klass))
=> true
This also works for classes in a module:
module Bar
class Foo
def class
'fooo'
end
end
class Boo < Foo
end
end
boo = Bar::Boo.new
=> #<Bar::Boo:0x00007fe5a20358b0>
boo.class
=> "fooo"
boo.inspect
=> "#<Bar::Boo:0x00007fe5a20358b0>"
klass = boo.inspect.split(':').reject(&:empty?)[0..-2].join('::').sub('#<', '')
=> "Bar::Boo"
boo.is_a?(Kernel.const_get(klass))
=> true
If I have a class B that inherits from class A, can I instantiate class B through a class method defined in class A?
class A
def self.instantiate params
# ???
end
end
class B < A
end
b = B.instantiate 123
b.class # => B
Obviously, I don't want to call B.new from A. Any class that inherits from A should benefit from this.
class C < A; end
c = C.instantiate 123
c.class # => C
class D < A; end
d = D.instantiate 123
d.class # => D
Simply call self.new (self references the class itself in the class method):
class A
def self.instantiate params
self.new
# OR simply `new`
end
end
class B < A; end
b = B.instantiate 123
b.class
# => B
class C < A; end
c = C.instantiate 123
c.class
# => C
class D < A; end
d = D.instantiate 123
d.class
# => D
UPDATE
As Cary Swoveland commented, you can omit self.:
def self.instantiate params
new
end
class Foo
class << self
attr_accessor :var
end
end
class Bar < Foo
var = "bar"
p var # print "bar"
end
p Bar.var # print nil
Why Bar.var not return "bar"?
how can i add getter/setter for class variables?
class Bar < Foo
var = "bar" # this is assignment to local variable, not the accessor
end
Use self to tell ruby that you want to call the method, not create local variable.
class Foo
class << self
attr_accessor :var
end
end
class Bar < Foo
self.var = "bar"
var # => "bar"
end
Bar.var # => "bar"
Why doesn't c.f() below work as b.f()? I'd like to use the class A::C-way to declare a nested class for avoiding too many indentations.
class A
CONSTANT = 1
end
class A
class B
p self # => A::B
def f
print CONSTANT, "\n"
end
end
end
class A::C
p self # => A::C
def f
print CONSTANT, "\n"
end
end
b = A::B.new
b.f() # => 1
c = A::C.new
c.f() # => `f': uninitialized constant A::C::CONSTANT (NameError)
You can put it in one line using semicolon:
class A; class C
...
end end
You can work around this by prefixing the constant with A::
class A
CONSTANT = 1
end
class A
class B
def f
puts A::CONSTANT
end
end
end
class A::C
def f
puts A::CONSTANT
end
end
b = A::B.new
b.f() #=> 1
c = A::C.new
c.f() #=> 1
Is there a way to introduce some variable/constant with the the following three properties?
a) It inherits a superclass's value when it is not assigned in the own class.
b) It does not inherit other classes's value other than for superclasses's (even if they share the namespace).
c) It does not overwrite the superclasses's value when assigned in the own class.
Using a class instance variable, a) is not satisfied.
class A; #foo = :foo end
class B < A; #foo end # => nil (Does not satisfy (a))
class A; class C; #foo end end # => nil (Satisfies (b))
class B < A; #foo = :bar end
class A; #foo end # => :foo (Satisfies (c))
Using a class variable, c) is not satisfied.
class A; ##foo = :foo end
class B < A; ##foo end # => :foo (Satisfies (a))
class A; class C; #foo end end # => NameError (Satisfies (b))
class B < A; ##foo = :bar end
class A; #foo end # => :bar (Does not satisfy (c))
Using a constant, b) is not satisfied.
class A; Foo = :foo end
class B < A; Foo end # => :foo (Satisfies (a))
class A; class C; Foo end end # => :foo (Does not satisfy (b))
class B < A; Foo = :bar end
class A; Foo end # => :foo (Satisfies (c))
I want something that behaves like this:
class A; something = :foo end
class B < A; something end # => :foo (Satisfies (a))
class A; class C; something end end # => nil or Error (Satisfies (b))
class B < A; something = :bar end
class A; something end # => :foo (Satisfies (c))
If it cannot be done simply by assigning and referencing a variable/constant, then is there any way to implement accessor methods that would have this property?
You'll need to create your own kind of accessor with the specific properties that you want. For example,
module InheritableProperty
def property
#property || superclass.property
end
def property=(value)
#property = value
end
end
class A
extend InheritableProperty
end
class B < A
extend InheritableProperty
class C
extend InheritableProperty
end
end
A.property = 1
A.property # => 1
B.property # => 1
B::C.property # error
A.property = 1
B.property = 2
A.property # => 1
B.property # => 2
B::C.property # error
A.property = 1
B.property = 2
B::C.property = 3
A.property # => 1
B.property # => 2
B::C.property # => 3
Along the lines suggested by joshuanapoli, I decided to go with this:
class A
def self.foo; defined?(#foo) ? #foo : superclass.foo end
end
class A; #foo = :foo end
class B < A; foo end # => :foo
class A; class C; foo end end # => Error
class B < A; #foo = :bar end
class A; foo end # => :foo