Let's Get Classy

I learned how to use a new Ruby 'thing' this week: Classes. To be completely honest with you, I don't completely understand what's going on with the Ruby 'class' object, but I've been using it, and I think I can do a reasonably competent job explaining the basics. Also this week, I learned some new tricks for arrays and hashes. Check them out at the end of my post from a few weeks ago. On to classes! I'm going to jump right in with a simple example:

Really Simple Example

class SimpleClass 
  def initialize 
    # code
  end

  def example_method
    # code
  end
end

This is a class! Calling SimpleClass.new will create a new instance of SimpleClass. It won't do anything, but it will exist!

The first time I searched "simple Ruby class example" I was horribly dissapointed, and could not seem to find anything that just 'made sense'. I didn't know what instance varibles were, so I was frustrated everytime I saw an "@" symbol. You can't do much without varibles, so here's some information on how to use variables with classes:

Define

Instance Variables - Variables defined inside the class definition. By convention, the first character of an instance variable is always the "@" symbol. Instance variables 'exist' separately for each instance of the class, and do not exist outside the class instance.

Instance Methods - Methods inside class definitions are called 'instance methods'. They'll only work on the class they're defined in. It's possible to call methods from within a class definition (example below).

Attribute Methods - Use these to read or write to instance variables outside of the class definition. I don't have a whole lot of experience with them, but I'll provide a short demo below (after 'Better Example'). Currently, my favorite way to access instance variables outside class definitions is with class_instance.instance_variable_get(:@instance_variable)

Here's a better example to illustrate some of this:

Better Example

class ClassName # => Use CamelCase for class names
  def initialize(variable) # => ClassName.new calls this method
    @instance_variable = variable
  end

  def first_instance_method 
    second_instance_method # => Call instance method from inside class definition
  end

  def second_instance_method 
    puts @instance_variable # => prints random_integer
  end
end
random_integer = rand(0..100) # => Random integer from 0 - 100
new_instance = ClassName.new(random_integer) 
new_instance.first_instance_method # => prints random_integer

Demo (attribute methods)

class IllustrateAttributes
  attr_reader :read_me
  attr_writer :write_me
  attr_accessor :read_or_write

  def initialize(read,write,both)
    @read_me = read
    @write_me = write
    @read_or_write = both
  end
end
new_instance = IllustrateAttributes.new("Read me","Change me","Do both/either")

puts new_instance.read_me # => prints "Read me"
puts new_instance.write_me # => doesn't work
puts new_instance.read_or_write # => prints "Do both/either"

new_instance.write_me = "Kelly was here."
new_instance.read_or_write = "Edited!"

puts new_instance.read_or_write # => prints "Edited!"
new_instance.instance_variable_get(:@write_me) # => "Kelly was here."

The reason there are read and write options (and they're not all just 'accessors') is to prevent strange errors from happening. For example, if you were storing a credit card number, you would never want to change that number, and if you were storing a pin number or password, you would never want to read it (probably).