Clarification on Ruby class method

78 Views Asked by At

I found this class from Eloquent Ruby book.

class TextCompressor
  attr_reader :unique, :index
  def initialize( text )
    @unique = []
    @index = []
    words = text.split
    words.each do |word|
      i = @unique.index( word )
      if i
        @index << i
      else
        @unique << word
        @index << unique.size - 1
      end
    end 
  end
end

It works like this:

text = "This specification is the spec for a specification"
compressor = TextCompressor.new(text)
compressor.unique #=> ["This", "specification", "is", "the", "spec", "for", "a"]
compressor.index #=> [0, 1, 2, 3, 4, 5, 6, 1]
  1. What is unique in @index << unique.size - 1, and where did it get its value from?
  2. Are compressor.unique and compressor.index coming from attr_reader :unique, :index, or @unique and @index?
2

There are 2 best solutions below

2
max.underthesun On BEST ANSWER
  1. In the context of the initialize method the unique is the same as self.unique and the self here is the instantiated object (TextCompressor.new)

self.unique in this case will return you the value of @unique variable

  1. The reason you can call compressor.unique and compressor.index is coming from attr_reader :unique, :index. This line sets getters for you (getter is a method to query object for instance variable value).
1
Silvio Mayolo On

The @unique and @index are private instance variables. Inside the class, you always precede them with an @. Outside the class, they are completely unavailable[1]. attr_reader is a metaprogramming function in Ruby that constructs a getter. The following are equivalent.

attr_reader :unique, :index

and

def unique
  @unique
end

def index
  @index
end

The methods defined by attr_reader and co are public and thus can be accessed outside the class, using object_name.unique syntax. Like in many languages, they can also be accessed unqualified from within the class, so if you're inside the class scope, unique and self.unique will evaluate to the same thing (assuming there isn't a local variable called unique in scope). So your unique.size call is actually calling two methods. In Java, it would look like getUnique().getSize(), but Ruby lets us omit the parentheses in many cases.

An important thing to remember about Ruby is that, in general, if you do a.b or a.b = c, you are always calling a method. Ruby instance variables (the things starting with @) are always private, so there's no way to directly access them outside the class. There's no foo.bar syntax to access a public instance variable like in Java or C++. foo.bar is always a method; it just might happen to be a method which accesses and returns the value of @bar.

This is more in line with Smalltalk's view of OOP, where loosely coupled objects pass messages in order to exchange information. In such a model, it doesn't make sense to have direct access to someone else's data; all you can do is request it and wait for the response.


[1] Ignoring reflection features, which can bypass such rules.