minervadreaming
minervadreaming

Reputation: 35

Ruby: trouble calling class instance from method

I'm obviously new to Ruby, and programming in general, and greatly appreciate any help.

Here is my code snippet:

class Player
    attr_accessor :name, :hp

    def initialize(name, hp)
        @name = name
        @hp = hp
    end

    def name
        @name
    end

    def hp
        @hp
    end
end

def prompt
    print "> "
end

prompt; pname = gets.chomp
player = Player.new(pname, rand(20..30))

puts "#{player.name} \:\: #{player.hp} HP"

def test
    puts "#{player.name} \:\: #{player.hp} HP - IN METHOD"
end

test

When run, here are the results:

$ ruby wtf.rb

> Test Name

Test Name :: 20 HP

wtf.rb:24:in `test': undefined local variable or method `player' for main:Object (NameError) from wtf.rb:27:in `<main>'

Why does my call work in the first instance, but not the second? Is it because it is now looking for a new "player" variable within the "test" method? If so, how do I go about calling the one from the class instance created earlier?

Thanks!

Upvotes: 1

Views: 77

Answers (3)

dseminara
dseminara

Reputation: 11935

Ruby doesn't have lexical scope for methods like Javascript for functions, so this (ruby):


player = Player.new(pname, rand(20..30))
def test
    # error: undefined variable or method 'player', 
    # this happens because player was defined
    # in an unrelated scope outside this method
    puts "#{player.name} \:\: #{player.hp} HP - IN METHOD"
end
test

Is NOT equivalent to this (javascript)


var player = new Player(pname, ...);
var test = function() {
    # works as expected, player is available because it was defined
    # in the parent scope
    console.log(player.name + "\:\:" + player.hp + " HP - IN METHOD");
};
test();

If you want something similar to lexical scope on ruby, maybe you should use lambda


player = Player.new(pname, rand(20..30))
test = lambda do
    # works as expected, this block scope inherits from parent scope where the block
    # was created, so, it can access player local variable
    puts "#{player.name} \:\: #{player.hp} HP - IN METHOD"
end
test.call

Upvotes: 0

jtzero
jtzero

Reputation: 2254

player is defined as a local variable outside the scope of the method test,

change references of player to @player, making it an instance variable

Here is a quick reference on scope and variables, and also a similar question

As a side note you shouldn't use test as a method name, because it is already defined on Kernel

irb(main):001:0> method(:test)
=> #<Method: Object(Kernel)#test>

Upvotes: 1

David Weiser
David Weiser

Reputation: 5205

def test
  puts "#{player.name} \:\: #{player.hp} HP - IN METHOD"
end

You forgot to pass player into the method:

def test(player)
  puts "#{player.name} \:\: #{player.hp} HP - IN METHOD"
end

test(player) # => player = Player.new(pname, rand(20..30)) :: 22 HP - IN METHOD

Upvotes: 0

Related Questions