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?
a source to share
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.
a source to share
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
a source to share