Kerozu
Kerozu

Reputation: 673

Prevent inheritance in Ruby

I am trying to figure out how to prevent inheritance of one method in all subclasses. Currently I am trying with such code:

class Mother

  def phone
    puts "#{self.class} phoned"
  end

  protected
  def phone_kids
    puts "phoned kids"
  end
end

class Kid < Mother
end

But without any result, as I am getting such error: <top (required)>': protected method 'phone_kids' called for #<Mother:0x604da0> (NoMethodError). Could you please explain me briefly how inheritance in Ruby works?

Thanks in advance!

// EDIT:

I am getting this error with this code:

kid = Kid.new
mom = Mother.new

mom.phone_kids

Upvotes: 3

Views: 1776

Answers (3)

Saurabh
Saurabh

Reputation: 73649

The difference between protected and private is subtle. If a method is protected, it may be called by any instance of the defining class or its subclasses. If a method is private, it may be called only within the context of the calling object---it is never possible to access another object instance's private methods directly, even if the object is of the same class as the caller. For protected methods, they are accessible from objects of the same class (or children).`

source: http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Classes#Declaring_Visibility

Upvotes: 1

D.Fux
D.Fux

Reputation: 376

In ruby, both private and protected methods can be use only in the class def. So you can't invoke a protected or private method outside:

class Mother

  def phone
    puts "#{self.class} phoned"
  end

  protected
  def phone_kids
    puts "phoned kids"
  end
  private
  def anoter_phnoe_kids
    puts "anoter phoned kids"
  end
end

class Kid < Mother
  def hey
    phone_kids
    anoter_phnoe_kids
  end
end

Kid.new.phone #OK
Kid.new.phone_kids #protected method (NoMethodError)
Kid.new.anoter_phnoe_kids #private method (NoMethodError)
Kid.new.hey #OK

And here shows the difference between protected and private.

Upvotes: 1

David Grayson
David Grayson

Reputation: 87486

If you find yourself needing to do this, it probably means that you are not using inheritance correctly. If class C inherits from D, it should be an "is a" relationship: every C is a D. Since not every kid is a mother, your use of inheritance in this case is bad.

Here is how you might represent parents and kids in Ruby just using a single class:

class Person
  attr_accessor :name
  attr_accessor :children

  def initialize(name)
    @name = name
    @children = []
  end

  def phone_kids
    @children.each { |c| c.phone }
  end

  def phone
    puts "#{@name} phoned"
  end
end

Now, to answer the original question. If you want to prevent the Kid class from inheriting the phone_kids method, you could do something like this:

class Mother

  def self.inherited(klass)
    klass.send(:define_method, :phone_kids) do
      raise NoMethodError
    end
  end 

  def phone_kids
    puts "phoned kids"
  end
end

class Kid < Mother
end

Mother.new.phone_kids  # => phoned kids
Kid.new.phone_kids     # => NoMethodError

Upvotes: 11

Related Questions