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
Related
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!)
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
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
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