Calling a parent instance variable from a sub class in ruby

I have to call an instance variable initialized in a parent Class from a sub class. I know that I can solve using "@@profile" instead of "@profile" but it needs to be thread-safe.

class ParentClass

    attr_reader :profile

    def initialize(profile:)
        @profile = profile
    end

    def call_sub
        SubClass.new().run()
    end

end

class SubClass < ParentClass

    def initialize()
        @retries = 0
    end

    def run()
        puts "PROFILE: #{@profile.inspect}"
    end

end

pc = ParentClass.new(profile: "Me")
pc.call_sub

p = ParentClass.new(profile: "Me")
puts "PROFILE: #{p.profile}"

ruby test.rb

PROFILE: nil <--- Why is nil ?
PROFILE: Me

You are overriding ParentClass#initialize.

Ruby does not magically invoke overridden methods. That would kind of defeat the point of overriding in the first place: for example, often, the reason why you override a method in a subclass, is that the subclass implementation can make use of its knowledge of the subclass to improve performance. It wouldn't make sense for Ruby to invoke the un-optimized version in addition to the optimized one.

However, another big purpose of overriding is differential code reuse: reusing code by only implementing the difference in behavior. In order to do that, Ruby has the super keyword, which allows you to retry the method lookup starting one level up in the ancestors chain.

Here's what that would look like in your case:

class SubClass < ParentClass
  def initialize(*)
    super
    @retries = 0
  end
end

This solves the problem with the un-initialized instance variable.

However, there are several other problems with your code. For example, here:

SubClass.new().run()

You are sending the message new to SubClass without any arguments. By default, Class#new is implemented roughly like this:

class Class
  def new(...)
    obj = allocate
    obj.initialize(...)
    obj
  end
end

[Technically, initialize is private, so we need to break encapsulation here by using something more like obj.__send__(:initialize, ...).]

In other words, new passes its arguments on to initialize. But you have defined ParentClass#initialize with one mandatory keyword parameter profile:, so theoretically, we need to pass an argument to super … but what should we pass? So, instead, we have defined SubClass#initialize to take arguments and pass them on to ParentClass#initialize.

However, you are passing no arguments to SubClass::new here, that we could pass on, and in fact, it isn't clear what arguments we could pass here.

I would like to help you here, but to be honest, the code simply does not make sense, so I don't see how to fix it. Superclasses must never know about their subclasses, in fact, they typically cannot know about their subclasses: many languages allow you to add subclasses after the fact, and Ruby is no exception. Therefore, a superclass knowing about its subclass is a sign that something in your design is very, very, very wrong.

Also note that the very idea of a "parent instance variable" does not make sense: "parent" refers to the class hierarchy, but instance variables belong to instances … that's why they are called "instance" variables, after all.