Monday, April 29, 2013

Ruby: Accessor

Instance variables in Ruby are private by default.
class Book
  def initialize
    @title = "Ender's Game"
    @author = "Orson Scott Card"
    @location = "Chicago"
  end
end

book = Book.new
book.title = "Programming Ruby"
puts (book.title)
If you run the program, you get a no method error.
accessor.rb:10:in `
': undefined method `title=' for # (NoMethodError)

In order to access to the variables, you need to specify what type of access you want to grant. There are three types of access:
  • attr_reader - for read access only
  • attr_writer - for write access only
  • attr_accessor - for read and write access
Let's say you're writing a book. It's the first draft. You are sending it your friends for review. Let's imagine it's pre-email day, so you're sending it through snail mail. You only have one copy (because you're a poor author), so you want to keep track of where it is. So you have a location that others can read or write to. You haven't decided on a title yet, therefore you don't want anyone else to see it. They can change it, but they can't see it. As for the author, because it's your book, this is something that can't be change so others can only read this field. Let's review.

You have a book with the following attributes:
  • Title - write only access
  • Author - read only access
  • Location - read and write
So your class looks something like this:
class Book
  attr_reader :author
  attr_writer :title
  attr_accessor :location
  
  def initialize
    @title = "Ender's Game"
    @author = "Orson Scott Card"
    @location = "Chicago"
  end
end
So what can you do?
book = Book.new

book.title = "Ender's Shadow"

puts(book.author)

puts(book.location) 
book.location = "Philly"
You can do all that, but if you try to read the title or change the author, you will get an error.
put(book.title)

accessor.rb:22:in `
': undefined method `title' for # (NoMethodError) book.author = "Me" accessor.rb:23:in `
': undefined method `author=' for # (NoMethodError)

The really cool thing about all this is that one day, if you decide to collect all the friends ideas for the book title, you can do it, without changing the caller.
class Book
  attr_reader :author, :suggested_titles
  attr_accessor :location
  
  def initialize
    @title = "Ender's Game"
    @author = "Orson Scott Card"
    @location = "Chicago"
    @suggested_titles = []
    @suggested_titles << @title
  end
  
  def title=(title)
    @title = title
    @suggested_titles << @title
  end
end

book = Book.new
book.title = "Ender's Shadow"
puts(book.suggested_titles)
Even though I added def title, I still set the title by typing book.title = "book name". One thing to note is that you can specify more than variables to the attribute by separating them with commas as I did with :author and :suggested_titles.

0 comments:

Post a Comment