In the last column I mentioned Ruby modules without going into much detail. This month I'm going to talk about the way Ruby code is organised and some of the tools that you can use to explore this organisation.

The first thing we need to look at is access control in Ruby.

Ruby methods are either public, protected or private. Methods that are public, which is the default, are accessible pretty much as you would expect, but the Ruby description is that the receiver of a public method can be either explicit (some variable with an instance of the class), self (a pseudo variable that refers to the current object), or implicit (no receiver specified).

You might wonder why you'd ever want to use an implicit receiver, but we've already seen examples of this when we've written Ruby scripts -- whenever we say:

puts 'some string'

#puts is a method that is being invoked with an implicit receiver. We'll come back to this shortly.

Protected methods are more constrained -- the receiver of a protected method must be the same class as self, either explicitly or implicitly.

Private methods are more even more tightly constrained -- the receiver of a private method must be implicitly self, you can't use an explicit self to invoke a private method.

Unlike other object oriented languages, such as Java, inheritance doesn't play a major part in determining access, provided the access complies with the rules I've described. Let's look at some examples.

We'll begin with a class that simply captures a person's first and last names, and uses protected and private methods to build up a full name:


class Person
  
  def first_name
    'Steve'
  end
  
  def last_name
    'Hayes'
  end

  protected
  
  def full_name
    built_name
  end
    
  private
  
  def built_name
    first_name + ' ' + last_name
  end

end

The protected and private keywords can be used in any order and as many times as you like. The keywords apply until another access modifier is encountered.

Now we can create a new instance of Person and see what happens when we try to access each of these methods.


person = Person.new

person.first_name # => "Steve"
person.last_name # => "Hayes"
person.full_name # => protected method `full_name' called for #...
person.built_name # => private method `built_name' called for #...

In this case, #first_name and #last_name have the default access modifier, public, so we can access them directly from our script. We can't access the protected method #full_name because the received of a protected method must have the same class as self (here self is Object and the receiver is Person), and we can't access the private method #built_name because the receiver of a private name must implicitly be self.

Let's give ourselves complete public access to these methods through a subclass:


class OpenPerson < Person
  
  def public_full_name
    self.full_name
  end

  def public_built_name
    built_name
  end
  
end

In OpenPerson, we can access #full_name because the receiver is explicitly self. We can also access #built_name, because the received is implicitly self. Here's the result:


person = OpenPerson.new

person.public_full_name # => "Steve Hayes"
person.public_built_name # => "Steve Hayes"

Since #full_name is protected, we're free to remove the explicit receiver in #public_full_name, but we can't legally add an explicit receiver in #public_built_name because #built_name is private. If we try this implementation:


class OpenPerson < Person
  
  def public_full_name
    full_name
  end

  def public_built_name
    self.built_name
  end
  
end

we get these results:


person = OpenPerson.new

person.public_full_name # => "Steve Hayes"
person.public_built_name # => :in `public_built_name': private method `built_name' called for #

So far it looks like protected is quite similar to private -- why do we need both?

A protected method can be called on an object passed as a method parameter, so it can be very useful for comparisons or copies, amongst other uses of course. Here's an example of a comparison method that uses the protected Person#full_name method:


class Person
   #...
  def same_as?(other_person)
    self.full_name.eql?(other_person.full_name)
  end
   #...
end

person = Person.new
person.same_as?(Person.new) # => true

Since #full_name is protected it's not publicly available, but we're still able to send it to both self and to other_person, which is the same class as self. If #full_name was private this wouldn't be possible; protected provides us with a useful, if little used, middle ground.

Do you need help with Ruby? Gain advice from Builder AU forums

Comments

1

nona - 14/09/07

Articles and content in this section of the website are really amazing.

» Report offensive content

2

Shallu - 15/11/07

I really enjoyed my visit to this website.

» Report offensive content

2

Shallu - 15/11/07

I really enjoyed my visit to this website. ... more

1

nona - 14/09/07

Articles and content in this section of the website are really amazing. ... more

Log in


Sign up | Forgot your password?

What's on?

  • Optus Deal

    Broadband + home phone + PlayStation®3 in a single package price!