1

I am struggling a bit with subclassing and class variables. I was expecting the class variables set by a class method call of the subclass to be specific to that subclass, but I see them being set in the super class.

For example, if I run the following code:

class Obj
    @@can_say = []
    def self.says word
        @@can_say.push(word)
    end

    def self.listens_to calling
        define_method calling do
            "I say #{@@can_say}"
        end
    end
end

class Dog < Obj
    says "woof"
    says "bark bark"
    listens_to "goodboy"
end

class Cat < Obj
    says "meaow"
    says "purr"
    listens_to "lord"
end

cat = Cat.new
dog = Dog.new

puts("Dog: #{dog.goodboy}")
puts("Cat: #{cat.lord}")

I get:

ruby/test$ ruby test.rb 
Dog: I say ["woof", "bark bark", "meaow", "purr"]
Cat: I say ["woof", "bark bark", "meaow", "purr"]

I was expecting:

ruby/test$ ruby test.rb 
Dog: I say ["woof", "bark bark"]
Cat: I say ["meaow", "purr"]

Is there a way to achieve that?

2
  • This is a super common misconception about @@ vars ... see stackoverflow.com/questions/1251352/… Commented Apr 16, 2018 at 2:44
  • From the docs: "Class variables are shared between a class, its subclasses and its instances." Commented Apr 16, 2018 at 8:04

1 Answer 1

0

I kind of found a way:

class Obj
    class << self
        attr_accessor :can_say
    end

    def self.says word
        if @can_say.nil?
            @can_say = Array.new
        end
        @can_say.push(word)
    end

    def self.listens_to calling
        define_method calling do
            "I say #{self.class.can_say}"
        end
    end
end

class Dog < Obj
    says "woof"
    says "bark bark"
    listens_to "goodboy"
end

class Cat < Obj
    says "meaow"
    says "purr"
    listens_to "lord"
end

cat = Cat.new
dog = Dog.new

puts("Dog: #{dog.goodboy}")
puts("Cat: #{cat.lord}")

Now if I can somehow get rid of:

if @can_say.nil?
    @can_say = Array.new
end
Sign up to request clarification or add additional context in comments.

3 Comments

you can use this (@can_say ||= []) << word to get rid of that, it is just saying the same thing but in a single line, it'll just create an array if not found or append to the existing one if found
Thanks @Subash, that's helpful to know! Is there any other way to somehow declare and initialize the @cas_say beforehand?
(@can_say ||= []) this line here without appending word is doing the initializing of the empty array

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.