CSC/ECE 517 Fall 2007/wiki1 4 ar
Private Method Invocation in Ruby
Ruby's private methods can be called only on "self" (the calling object). They can't be applied to other objects of the same class, even from within the class definition. In class today, I opined that it is rarely if ever necessary to call a private method on another object of the same class. Try to find Java or C++ examples on the Web where a private method is called on another object. Decide if this is good design, or if it would be better performed by using accessor functions, from the standpoint of elegance or maintainability. If you cannot find any such examples (or even if you can), try to find arguments about why one should not invoke private methods on other objects. Analyze these arguments; can you find weaknesses?
Ruby’s Access Control Mechanisms
Ruby has three levels of access controls – Public, Protected and Private.
Public methods – can be accessed by anyone. In Ruby, public is default access control, except for initialize method which is private by default.
Protected methods – can be accessed only by the defining class and its subclasses. Access is kept within the family.
Private methods – can be accessed only by the objects of the defining class. The explicit receiver is always self (the current object), which means that it is impossible to invoke another object’s private methods and variables directly even if the object has the same class type as the caller.
Example -1:
class Base
private
def foo
puts "foo"
end
public
def test
foo
end
def test1
self.foo
end
end
Output : irb(main):170:0> Base.new.test foo irb(main):171:0> Base.new.test1 NoMethodError: private method `foo' called for #<Base:0x695e9d4>
Example -1 shows that private methods can be called only with an implicit receiver. Even calling a private method using self throws NoMethodError.
Private methods cannot be invoked using an explicit receiver. But we use "self" to invoke writer methods else the method will be interpreted as an assignment to a local variable.
Comparing the private method invocation in C++ and Ruby
The private methods in C++ can be invoked by another object of the same class whereas Ruby does not allow explicit receiver for private methods.
Example -2:
class Base {
private:
void foo();
public:
void spam(Base *otherObject) {
foo(); // this is allowed
otherObject->foo(); // and so is this
}
};
Example -2 shows that a private method can be called by any instance of the same class.
Example -3:
class Base
private
def foo
puts "foo"
end
public
def test
foo
self.foo
end
end
b = Base.new
b.test
Output:
foo
NoMethodError: private method `foo' called for #<Base:0x4ef2690>
Example -3 shows that when calling a private method a receiver cannot be specified in Ruby.
So it can be concluded that in C++, “private” defines “private to the class”, while in Ruby it means “private to this instance”.
Bypassing private method invocation in Ruby
Example -4:
class Test
private
def print_hello
puts "Hello everyone!"
end
end
t = Test.new
t.send( "print_hello" )
Output:
Hello everyone!
Example -4 shows that it is possible to access private methods in Ruby through “send” function. In Ruby, declaring as private is only a guideline and not a strict rule. There are workarounds to access those private methods. The advantage of such workarounds allows testing of private methods but it is a bad practice since private methods are internals of a class.
Since private methods have a loophole in security, an alternative good practice would be to use accessor functions. In accessor functions, we implement the getter and setter methods for the private attributes, thereby providing the required security.
There are three accessor functions in Ruby –
attr_reader – creates a getter method
attr_writer – creates a setter method
attr_accessor – creates getter and setter methods
Example -5 shows the implementation of all methods
class Vehicle
attr_reader :model
attr_writer :color
attr_accessor :price
def initialize(model,color,price)
@model = model
@color = color
@price = price
end
end
v = Vehicle.new("Camry","black",20000)
puts v.model
puts v.color #throws NoMethodError
puts v.price
v.price = 30000
puts v.price
Output:
Camry
NoMethodError: undefined method `color' for #<Vehicle:0x66604d0 @model="Camry", @price=40000, @color="black">
20000
30000
The advantages of using accessor functions are – Flexibility and Maintainability
In general, it is a good programming practice to use accessor functions instead of invoking private methods in Ruby.
References
1. Programming Ruby: The Pragmatic Programmers' Guide
2. http://www.rubycentral.com/pickaxe/win32.html
3. http://lylejohnson.name/blog/?p=5
4. http://www.ruby-forum.com/topic/120257#new