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