Understanding Ruby Instance Class Methods and Instances

I have the following code:

#!/usr/bin/ruby

class Person
  def self.speak
    p = self.new
    puts "Hello"
    p.chatter
  end

private

  def chatter
    puts "Chattering"
  end
end

p = Person.new
Person.speak

      

I want to make the chatter private, only accessible within p .. but I want p to be able to access it in a class method. Is there a better way to design this so that the chatter is not publicly available, but a "factory" method like self.speak can call the chatter?

+2


a source to share


2 answers


In Ruby 1.8.7, "send" bypasses the usual protection against calling private methods:

#!/usr/bin/ruby1.8

class Person

  def self.speak
    puts "Hello"
    new.send(:chatter)
  end

  def speak
    puts "Hello"
    puts chatter
  end

  private

  def chatter
    puts "Chattering"
  end

end

Person.speak        # => Hello
                    # => Chattering
Person.new.speak    # => Hello
                    # => Chattering

      

However, what you want can be achieved without any voodoo, just using a class method all the work and defer the instance method to the class method:

class Person

  def self.speak
    puts "Hello"
    puts "Chatter"
  end

  def speak
    self.class.speak
  end

end

      

If you had multiple forwarding methods like this, it would be convenient to create a helper method that does them for you:



module DelegateToClass

  def delegate_to_class(name)
    define_method(name) do |*args|
      self.class.send(name, *args)
    end
  end

end

class Person

  extend DelegateToClass

  def self.speak
    puts "Hello"
    puts "Chatter"
  end
  delegate_to_class :speak

end

      

An inline module Forwardable

can do this as well:

require 'forwardable'

class Person

  extend Forwardable

  def self.speak
    puts "Hello"
    puts "Chatter"
  end

  def_delegator self, :speak

end

      

def_delegator

also bypasses protection from private methods.

+8


a source


There are several ways to do this. One approach would be a class method that instantiates a new instance and calls a method #speak

on the instance.

class Person
  def self.speak
    new.speak
  end

  def speak
    puts "Hello"
    chatter
  end

  private

  def chatter
    puts "Chattering"
  end
end

      



Then you can call it in the context of a class or instance:

p = Person.new
p.speak
# ...or...
Person.speak

      

+1


a source







All Articles