1

This short code is working when I'm using class variable @@points instead of @points. I wonder why it's happening like this? Someone can explain me? It looks like @points is always nil.

class Game

  @points = 0

  def start

    until @points == 10
      puts "Guess number between 0 and 10:" 
      num = gets.chomp.to_i
      break if @points == 0
      guess_number(num)
      puts "Your score is: #{@points}"

    end

  end

  def guess_number(num)
    @points += 1 if num == rand(0..10)
  end

end

game = Game.new
game.start
1
  • A minuscule point: gets.chomp.to_i is often written without chomp: gets.to_i. That's because "123".to_i, "123\n".to_i and "123X456abc".to_i all return 123. See String#to_i, which includes, "Extraneous characters past the end of a valid number are ignored. ". Commented Oct 24, 2016 at 22:11

2 Answers 2

2

Because @points is a class instance variable, and to access it from within the instance method's scope you'd have to either do

self.class.instance_variable_get(:@points)

or define an attr_accessor in Game's singleton_class

class Game; class << self; attr_accessor :points; end; end

and then you'd be able to do

self.class.points

But neither of these is what you really want.

code is working when I'm using class variable @@points instead of @points

It is working, because you do have access to class variable from within a scope of an instance methods.

It looks like @points is always nil

It is always nil, because you never defined an instance variable @points, but, as said, class instance variable.

So these three things are different (you could read up something about Ruby scoping - do not mix with AR scopes):

  • class variable
  • class instance variable
  • instance variable

To solve it there are many ways, but if you want to keep it on the instance level, wrap @points into a method:

def points
  @points ||= 0
end

And then use it as points - now it'll work as you are expecting.

Sign up to request clarification or add additional context in comments.

3 Comments

If OP didn't want to expose the points outside the class, wouldn't it just be easier to implement a constructor that sets @points = 0?
@pjs as i said, there's quit a few ways to do it, so it's up to OP to choose, but of course, it's one of them :)
It may be misleading to say that the value of a class instance variable cannot be obtained within an instance's scope, since that can be done with self.class.instance_variable_get(:@points) (as you well-know).
0

Thanks to answer by Andrey Deineko. I came up with such solution to use instance variable here.

class Game

  def initialize
    @points = 0
  end

  def start

    until points == 10
      puts "Guess number between 0 and 10:" 
      num = gets.chomp.to_i
      break if points == 10
      guess_number(num)
      puts "Your score is: #{points}"

    end

  end

  private

  def guess_number(num)
    if num == rand(0..10)
      increment_points
    end
  end

  def points
    @points
  end

  def increment_points
    @points += 1
  end

end

game = Game.new
game.start

4 Comments

The hazard of this approach is that a user who pokes into your class will find they can call increment_points directly without playing the game. If you want to use an instance variable, add an initialize method to the class where the @points get initialized to 0 when you create a Game.new instance.
It seems to be good idea. I edited my anwser and also I made some of methods private.
You're also subverting canonical Ruby standards by naming the getter something other than what attr_reader would name it. Just something to consider.
I changed it to just points method not get_points. Thanks for your advice.

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.