I'm new to Ruby, so excuse the likely noob question, but Google has been unhelpful on this so far.
I have a class constant defined in my parent class
Items = [[1, 3, 5], [2, 4, 6]]
And then I have a subclass where I want to add an array [1, 5, 9] into the Items array. Can you reference super / redefine the constant in such a way?
Something like
Items = super.Items.concat([1, 5, 9])
I would like to not need to copy the Items definition into my subclass with the additional item.
Constants are name spaced within the class or module they are defined. They are resolved through the usual ancestors path. In your subclass you can define a constant of the same name as one in the superclass, and the expression initializing it can reference the superclass's constant as the subclass's constant won't be defined until after the initial assignment. Like this:
$ pry
[1] pry(main)> class A; Items = [[1, 3, 5], [2, 4, 6]]; end
=> [[1, 3, 5], [2, 4, 6]]
[2] pry(main)> class B < A; end
=> nil
[3] pry(main)> class B; Items; end
=> [[1, 3, 5], [2, 4, 6]]
[4] pry(main)> A::Items
=> [[1, 3, 5], [2, 4, 6]]
[5] pry(main)> B::Items
=> [[1, 3, 5], [2, 4, 6]]
[6] pry(main)> class B; Items = Items.dup << [7,8,9]; end
=> [[1, 3, 5], [2, 4, 6], [7, 8, 9]]
[7] pry(main)> A::Items
=> [[1, 3, 5], [2, 4, 6]]
[8] pry(main)> B::Items
=> [[1, 3, 5], [2, 4, 6], [7, 8, 9]]
When deriving the new constant, be careful to dup the original if you plan to modify it with a mutating method (like Array#<<). See the trap:
[9] pry(main)> class A; Foo = [[1,2],[3,4]]; end
=> [[1, 2], [3, 4]]
[10] pry(main)> A::Foo
=> [[1, 2], [3, 4]]
[11] pry(main)> class B; Foo = Foo << [5,6]; end
=> [[1, 2], [3, 4], [5, 6]]
[12] pry(main)> B::Foo
=> [[1, 2], [3, 4], [5, 6]]
[13] pry(main)> A::Foo
=> [[1, 2], [3, 4], [5, 6]]
[14] pry(main)> B::Foo.object_id == A::Foo.object_id
=> true
[15] pry(main)> B::Items.object_id == A::Items.object_id
=> false
You can explicitly reference the constant in the parent namespace without naming the superclass using Class#superclass
[16] pry(main)> class B; superclass::Items; end
=> [[1, 3, 5], [2, 4, 6]]
Constant lookup is not always super obvious in Ruby, in particular because it is usually not scoped (e.g. we write String, not ::String)
You can override it in subclasses. You can build on it by accessing the superclass:
class C < Base
CONST = build_on(superclass::CONST)
end
You have to be careful as to how you access the constant from instance and singleton methods though:
class Base
FOO = [42]
def self.naive
FOO
end
def naive_ins
FOO
end
def self.aware
self::FOO
end
def aware_ins
self.class::FOO
end
end
class C < Base
FOO = superclass::FOO + [:bar]
end
C.naive # => [42]
C.new.naive_ins # => [42]
C.aware # => [42, :bar]
C.new.aware_ins # => [42, :bar]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With