1

I have recently learned how to create classes, although I am not ENTIRELY sure where and why I should use them. I'd use them to create objects, which have similar methods/properties.

I tried making a gag code, but I stumbled upon a question I can't find an answer to.

class Person
  def initialize(name,health)
    @name = name
    @hp = health
  end

  def kick
    @hp -= 1
    if (@hp <= 0)
      puts "#{@name} got REKT!"
    end
  end

end

#Friends
michael = Person.new("Michael", 10)

10.times { michael.kick }

Even though this code works, I'm wondering if it is possible to use/call mihchael's hp outside the class? Perhaps sort of like a hash? michael[@hp]? But this doesn't work, even if i set hp to be a global variable. Should all if/else statements who check object's properties be inside the class?

Thank you very much

Charles Caldwell
  • 16,649
  • 4
  • 40
  • 47
Mc-Ac
  • 161
  • 1
  • 8

1 Answers1

5

The standard way to do this in Ruby is to create an accessor:

class Person
  attr_reader :name
  attr_reader :hp
end

Then outside the class you can call things like:

puts "#{michael.name} has only #{michael.hp} HP left"

The reason you create objects is to organize your code and data into logical contexts and containers.

tadman
  • 208,517
  • 23
  • 234
  • 262
  • ...but if there are no accessors, all is not lost: `puts "#{michael.instance_variable_get(:@name)} has only #{michael.instance_variable_get(:@hp)} HP left" #=> "Michael has only 10 HP left"`. See [Object#instance_variable_get](http://ruby-doc.org/core-2.2.0/Object.html#method-i-instance_variable_get). – Cary Swoveland May 06 '16 at 19:02
  • 4
    @CarySwoveland True, but that's the equivalent of reaching into someone's pants to grab their keys. It's not very polite. An accessor is the best way to ask for permission, plus it means the class is free to change how those values are stored. – tadman May 06 '16 at 19:03
  • @tadman if attr_reader :keys is used then we do not hold the \@keys attribute in the pants pocket, but advertise it as 'the \@keys are on the hook next to the door'. You do not understand how attr_reader :keys works. Basically it tells interpreter do define def keys; \@keys ;end . So when you remove attr_reader and define method keys with you can easily have your changes done. – ruby_object May 06 '16 at 20:48
  • @ruby_object I'm not sure what you're saying here. Providing an accessor method means that you can change how it works in the future, there's no requirement to stick to any particular implementation. I'm pretty sure I know how `attr_reader` works. – tadman May 06 '16 at 21:01
  • there is some confusion on the subject http://stackoverflow.com/questions/11731698/ruby-pickaxe-book-says-attr-accessor-is-class-method , the documentation http://ruby-doc.org/core-2.0.0/Module.html#method-i-attr_accessor says attr_accessor, attr_reader and similar create a method. – ruby_object May 06 '16 at 21:10
  • 1
    @ruby_object Now I'm even more confused what your objection here is. Yes, it's a class method and that's why I'm using it in the class context. This is textbook how it's done. If there was a mistake in a book I don't see how that's relevant. – tadman May 06 '16 at 21:19
  • @tadman, the initial question is about calling instance variables. to get to the bottom of it can you elaborate on "it's a class method" does the textbook based on another language tell how to handle this subtle problem? – ruby_object May 06 '16 at 21:26
  • 1
    @ruby_object If there's any misunderstanding here I think it's your understanding of the question. The question was "how do I access instance variables" and the most idiomatic answer is "use `attr_reader`". Yes, `attr_reader` is a class method, and obviously so since it's used to alter the properties of the class and introduce these new instance methods dynamically. – tadman May 06 '16 at 21:47
  • Thanks @tadman! So if i understood correctly, the initialize method can be ommited, like this: [link](http://puu.sh/oJ37f/1a8d7aa71f.png) Basicly, I use attr_reader if i want to display my variables, attr_writer if I want to update the variables, and attr_accessor if I want both:)? But this raises another question... If I want my if statement to simply write the name of my friend (not michael directly), who has hp <= 0, I would do that like if Person::hp < 10 ? Tried, but it doesn't work. Thank you for your answer! – Mc-Ac May 07 '16 at 03:42
  • The `initialize` method is important for establishing initial conditions. If you don't have one you'll have to do it manually, and `attr_accessor` gives you read/write access to your instance variables. The way you do it is `person.hp`, that's "object dot method", whereas `Person::hp` is the class, which doesn't make sense here. – tadman May 07 '16 at 03:50
  • @tadman Thank you for the explanation. But how would I write a code that will output a person(any person, not just michael.hp) who's hp got lower or equal to 0? Could I do a .each loop inside a class that will check all @name(s) for their@hp and then do a if statement inside? – Mc-Ac May 07 '16 at 04:55
  • As is the case with a lot of things in Ruby, [Enumerable](http://ruby-doc.org/core-2.3.1/Enumerable.html) is the answer: `players.select { |p| p.hp <= 0 }.each { |p| puts '%s is dead' % p.name }` – tadman May 07 '16 at 05:41
  • I see why you've been busy of late: closing in on the century mark! – Cary Swoveland May 10 '16 at 14:31