54

In Ruby how does one create a private class constant? (i.e one that is visible inside the class but not outside)

class Person
  SECRET='xxx' # How to make class private??

  def show_secret
    puts "Secret: #{SECRET}"
  end
end

Person.new.show_secret
puts Person::SECRET # I'd like this to fail
Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
DMisener
  • 891
  • 1
  • 8
  • 15
  • 2
    Of course, "private" in Ruby doesn't mean what it means in most languages. Ruby is not that big on hiding, so if you want to access something, you always can (but maybe not with the most obvious syntax). Ruby's `private` only says it can't be preceded by a dot (`.`) (which I guess is also a hint why it won't work on `::`) – Amadan May 20 '10 at 13:50
  • 2
    Just in case. Ruby 1.9.3 has now private constants. – Catherine Aug 03 '11 at 13:32

5 Answers5

174

Starting on ruby 1.9.3, you have the Module#private_constant method, which seems to be exactly what you wanted:

class Person
  SECRET='xxx'.freeze
  private_constant :SECRET

  def show_secret
    puts "Secret: #{SECRET}"
  end
end

Person.new.show_secret
# => "Secret: xxx"

puts Person::SECRET
# NameError: private constant Person::SECRET referenced
leviathan
  • 11,080
  • 5
  • 42
  • 40
Renato Zannon
  • 28,805
  • 6
  • 38
  • 42
14

You can also change your constant into a class method:

def self.secret
  'xxx'
end

private_class_method :secret

This makes it accessible within all instances of the class, but not outside.

harald
  • 5,976
  • 1
  • 24
  • 41
  • Perhaps a little meta-programming would make this more palatable. – DMisener May 25 '10 at 15:20
  • I thought about it a little longer and then realized the above approach doesn't make the constant available to instance methods so I think I'll stick with @@var approach. – DMisener May 25 '10 at 15:51
  • @DMisener: Ruby has a method for missing constants, if anyone wants meta-programming magic. – Andrew Grimm Apr 11 '11 at 23:08
  • you can access this class method from your instances using `self.class.secret` in an instance method – brad May 25 '11 at 20:01
  • 1
    @brad Instances can't call private class methods, even if the instance belongs to the class. The only way is to use `send` which defeats the whole purpose. – Kelvin Sep 09 '14 at 19:41
  • You can use private constant as well. Just as private as you with your methods/calls. – Rubyrider Sep 19 '16 at 05:57
  • If constant is based on some values from database or http request, it's very bad idea to use a method which may be called in a loop. You can store it in the instance variable only once, but I'm not sure, constants are available for a reason. – ciemborowicz Dec 26 '22 at 21:11
12

Instead of a constant you can use a @@class_variable, which is always private.

class Person
  @@secret='xxx' # How to make class private??

  def show_secret
    puts "Secret: #{@@secret}"
  end
end
Person.new.show_secret
puts Person::@@secret
# doesn't work
puts Person.class_variable_get(:@@secret)
# This does work, but there's always a way to circumvent privateness in ruby

Of course then ruby will do nothing to enforce the constantness of @@secret, but ruby does very little to enforce constantness to begin with, so...

sepp2k
  • 363,768
  • 54
  • 674
  • 675
0

Well...

@@secret = 'xxx'.freeze

kind of works.

Anonymous
  • 9
  • 1
0

You can just literally make it a private method in the first place ‍♂️

class Person
  def SECRET
    'xxx'
  end

  def show_secret
    puts SECRET
  end
end

Person::SECRET    # Error: No such constant
Person.SECRET     # Error: No such method
Person.new.SECRET # Error: Call private method
person.new.show_secret # prints "xxx"
Iron Savior
  • 4,238
  • 3
  • 25
  • 30