Question

I am doing the ruby koans and I am on the DiceSet project. I've made the DiceSet class but my instance variables don't seem to persist with the instance like I thought they would. My code is

class DiceSet
   attr_reader :values
   @values = []
   puts @values.class
   def roll(number_of_rolls)
     (1..number_of_rolls).each do |roll|
       puts @values.class
       @values << (1..6).to_a.sample
     end
     return @values
   end
end

The koan then uses my DiceSet class with

dice = DiceSet.new
dice.roll(5)
puts dice.values.class
assert dice.values.is?(Array)

I put the puts commands in there to follow whats happening with the @values instance variable and only the first puts @values.class says its an Array class. All the others are returning NilClass. Am I using instance variables incorrectly or is there something else I am missing? Do instance variables get deallocated after a method call?

EDIT: My class works correctly now that I have put @values = [] in the roll method as suggested below. My only question now, is why the roll method thinks that @values is a NilClass instead of an array when I put @values = [] in an initialize method

Was it helpful?

Solution

In Ruby everything are objects. The Ruby interpreter assumes that all instance variables belong to the current object self. This is also true in a class definition. The role of self belongs to the class itself, so the instance variable @values belongs to the class. Don’t get confused! Instance variables of the class are different from instance variables of that class’s objects. Also you don't need specify return keyword explicitly Try this:

class DiceSet
   attr_accessor :values       

   def roll(number_of_rolls)
     @values = []
     (1..number_of_rolls).each do |roll|
       @values << (1..6).to_a.sample
     end
     @values
   end
end

dice = DiceSet.new
dice.roll(5)
puts dice.values.class
assert dice.values.is_a?(Array)

OTHER TIPS

Each DiceSet instance has its own @values, and furthermore, the class DiceSet also has its own @values. They are all different from one another. If you want the instances and the class to share the same variable, you should use a class variable @@values.

Just put the declaration of @values = [] in the initialized method and your code should work as expected.

class DiceSet

  attr_reader :values
  def initialize()
     @values = []
  end

  def roll(number_of_rolls)
    (1..number_of_rolls).each do |roll|
      puts @values.class
      @values << (1..6).to_a.sample
    end
    return @values
  end
end

Try this:

class Cat
  attr_accessor :age
  def initialize
    @age = 12
  end
  @age = 6
  def meow
    puts "I'm #{@age}"
  end
  def self.meow
    puts "I'm #{@age}, going on #{@age+1}"
  end    
end
Cat.age = 4 # => NoMethodError: undefined method `age=' for Cat:Class
p Cat.age # =? NoMethodError: undefined method `age' for Cat:Class
Cat.meow # => I'm 6, going on 7
cat = Cat.new
p cat.age # => 12
cat.meow # => I'm 12
cat.age = 20 # => 20
cat.meow # => I'm 20

Were I to add

class << self
   attr_accessor :age
end

the first three lines of output would become:

Cat.age = 4 # => 4
p Cat.age # => 4
Cat.meow # => I'm 4, going on 5
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top