4

Is a singleton method necessarily public? If not, when would a private/protected singleton method be useful?

sawa
  • 165,429
  • 45
  • 277
  • 381

4 Answers4

5

Singleton methods do not necessarily need to be public. Private/protected singleton methods are useful in the same situations as regular private/protected methods - for example as a helper method that you do not intend to be called outside of the class.

class Foo
end

f = Foo.new

class << f
  def foo
    helper
    # other stuff
  end

  private
  def helper
  end
end
Max
  • 21,123
  • 5
  • 49
  • 71
1

You can make a singleton method private if you want:

class Foo
end

f = Foo.new
def f.bar
  "baz"
end

f.singleton_class.send :private, :bar
f.bar # => NoMethodError: private method `bar' called for #<Foo:0x007f8674152a00>
f.send :bar # => "baz"

whether or not this is actually useful depends on what you're doing.

hjing
  • 4,922
  • 1
  • 26
  • 29
1

You can make a singleton method private if you want:

class Foo

  def self.bar
    # ...
  end
  private_class_method :bar

end
spickermann
  • 100,941
  • 9
  • 101
  • 131
  • BTW: in recent versions of Ruby, method definition expressions (`def`) evaluate to a `Symbol` corresponding to the name of the method being defined, so that you can do `private_class_method def self.bar; end` or `protected def foo; end` or something like that. – Jörg W Mittag Jan 29 '15 at 00:59
  • This doesn't really seem to answer the question of 'Are all singleton methods public?' – hichris123 Jan 29 '15 at 01:07
  • @hichris123, do we need more than one singleton method that is not public to disprove the assertion that they are all public? – Cary Swoveland Feb 02 '15 at 04:20
0

Could they be useful? Hmmm. For Ruby 2.2

ObjectSpace.each_object(Module).flat_map { |m|
  m.singleton_class.private_methods(false) }.size
  #=> 900

Most are private methods of classes' singleton classes:

ObjectSpace.each_object(Class).flat_map { |c|
  c.singleton_class.private_methods(false) }.size
  #=> 838

[Edit: The following is an edit of my original post, to present more useful information.)

I am puzzled by one thing. Let:

a = ObjectSpace.each_object(Class).map { |c|
      [c, c.singleton_class.private_methods(false)] }.to_h

b = ObjectSpace.each_object(Class).map { |c|
      [c, c.private_methods(false)] }.to_h

def diff(a,b) 
  a.map {|k,v| b.key?(k) ? [k,v-b[k]] : [k,v] }.reject { |_,a| a.empty?}.to_h
end

I expected diff(a,b) == diff(b,a) == {}. Let's see:

diff(a,b)
  #=> {} 

diff(b,a)
  #=> {Gem::Specification=>[:skip_during, :deprecate],
  #    Complex=>[:convert],
  #    Rational=>[:convert],
  #    Random=>[:state, :left],
  #    Time=>[:_load]} 

Hmmmm.

Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100