Thursday, 9 October 2014

Access Control In Ruby

RUBY

What is access control?


Access control provide restriction to access sensible parts of your code from other. This feature enables you to hide the implementation details of your code, and specify a preferred way by which that code can be accessed and used.

Object is only thing with which we can make changes in application. The only way to change an object's state in Ruby is calling one of its methods.

Good rule of thumb in access control:
"never to expose methods that could leave an object in an invalid state"

In Ruby, the inheritance hierarchy or the package/module don't really enter into the equation in context of access control like any other language, it is rather all about which object is the receiver of a particular method call.

Ruby gives you three levels of protection:

Public methods : Public methods can be called by everyone - no access control is enforced. Methods are public by default, anyone can call them. Can be accessed with implicit & explicit receiver. But initialize method is always private.

Protected methods : Protected methods can be only called by objects of the defining class and its subclasses. Can be accessed with implicit & explicit receiver. However, usage of protected is limited.

Private methods : Private methods cannot be called with an explicit receiver - the receiver is always self. This means that private methods can be called only in the context of the current object. We cannot call another object's private methods.

So now implicit & explicit receiver comes in picture.

What is implicit & explicit calling ?
In explicit calling we need to provide reference of object, on which method should be call. We provide this using self or using class instance. But in implicit calling we do not need to provide any reference to object, it will automatically take self as referencing object.

Confused confused confused :(
If we take example from real world then it might be easy to understand. If I ask some one to purchase a book then without any specification that person will go to bookshop not to hospital or medical. This we can consider as implicit calling. Now if I ask that person to purchase book from specific vendor (ABC shop). Then he will go to that specific shop to purchase that book. This we call as explicit calling.

Lets see below code to understand what Implicit and Explicit calling is


class Test
  def method1
    # Implicit calling
    method2
  end

  def method2
  end
end

t = Test.new
# Explicit calling
t.method1



We will study further about private and protected access modifier.

How we can define a method private or protected in ruby?
There are two ways to define a method private or protected.


class MyClass

  def method1          # this method is public
  end

  protected

    def method2        # this method is protected
    end

  private

    def method3        # this method is private
    end

end


class MyClass

  def method1          # this method is public
  end

  def method2
  end

  def method3
  end

  protected :method2   # this method is protected

  private :method3     # this method is private
end

Lets run below example.


class MyClass
  def method1
    self.private1
  end

  private

  def private1
    puts "Hello I am Private"
  end
end

MyClass.new.method1

Running this produces the following noMethodError:


in `method1': private method `private1' called for #<XXXX> (NoMethodError)


Oh no! I missed private method receiver concept. Private method can be called only with implicit receiver. So I have changed my class like below.


class MyClass
  def method1
    private1
  end

  private

  def private1
    puts "Hello I am Private"
  end
end

MyClass.new.method1

Now run again.


Hello I am Private

I get desired output from my code. Now if I call it in subclass then.


class ClassA
  def main_method1
    method1
  end

  private
  def method1
    puts "hello from #{self.class}"
  end
end

class ClassB < ClassA
  def main_method1
    method1
  end
end

ClassA.new.main_method1
ClassB.new.main_method1

If I run this program.


hello from ClassA
hello from ClassB

Get desired result. "This means we can call a private method from within a class it is declared in as well as all subclasses of this class"

We can not call private method with explicit receiver not even with self. So changing previous example accordingly.


class ClassA
  def main_method1
    method1
  end

  private
  def method1
    puts "hello from #{self.class}"
  end
end

class ClassB < ClassA
  def main_method1
    self.method1
  end
end

ClassA.new.main_method1
ClassB.new.main_method1

Output :


hello from classA
`main_method1': private method `method1' called for #<XXXX> (NoMethodError)

Now Moving to protected method.
protected methods can be called by any instance of the defining class or its subclasses.

So changing examples for protected method.


class MyClass
  def method1
    protected1
  end

  def method2
    self.protected2
  end

  protected

  def protected1
    puts "Hello I am Protected1"
  end

  def protected2
    puts "Hello I am Protected2"
  end
end

MyClass.new.method1
MyClass.new.method2

Output:


Hello I am Protected1
Hello I am Protected2

Similarly for subclass:


class ClassA
  def main_method1
    method1
  end

  protected
  def method1
    puts "hello from #{self.class}"
  end
end

class ClassB < ClassA
  def main_method1
    self.method1
  end
end

ClassA.new.main_method1
ClassB.new.main_method1

Output:


hello from ClassA
hello from ClassB

So no change in output.

Now I added another class C


class ClassC < ClassA
  def main_method1
    ClassB.new.method1
  end
end

ClassC.new.main_method1

Run it and output is :


hello from ClassB


Why we need Protected ??

why we need protected if we can call a method explicitely why we need implicit call to a method.
If private method is so secure why we need one more level of access with protected in ruby.

I found my solution when I try to compaire two objects of same class. This can not achive with explicit reciever(Private Method).


class Person
  def initialize(number)
    @number = number
  end

  def number
    @number
  end

  def compare_number(c)
    if c.number > number
      "The second number is bigger."
    else
      "The second number is the same or smaller."
    end
  end

  protected :number

end

first = Person.new(25)
second = Person.new(34)

puts first.compare_number(second)

Output is:


The second number is bigger.

We cannot accomplish  this with private method only and public method are not secure for such methods. So It's clear protected methods are equally important in ruby as private.

No comments:

Post a comment