How to I Make Private Class Constants in Ruby

How to I make private class constants in Ruby

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.

Why would you private encapsulate a private constant?

The reason for private_constant is to prevent other code from accessing the constant directly.

some_constant might return the constant today, but it's not obligated to do that. If programming-by-contract then it's obligated to return a hash in that form, the origin of which is irrelevant.

For example, you have that code today, but what about tomorrow it evolves to:

class Foo < Bar
SM_CONSTANT = {
a: { name: 'A', priority: 2 },
b: { name: 'B', priority: -1 }
}.freeze
private_constant :SM_CONSTANT

private
def some_constant
SM_CONSTANT.map { |k,o| [ k.to_s, o[:name] ] }.to_h
end
end

Where the internals have completely changed but to outside code nothing has, the same calls produce the same results. This is why encapsulation is important. It gives you the freedom to iterate and rework code within a particular context without concerning yourself about breaking other things.

If code accessed SM_CONSTANT directly it'd have to be re-written to accept this new structure.

Constants or class variables in ruby?

The main thing is that by using the CONSTANT notation, you're making it clear to the reader. the lower case, frozen string gives the impression is might be settable, forcing someone to go back and read the RDoc.

Accessing a class's constants

What you posted should work perfectly:

class Foo
CONSTANT_NAME = ["a", "b", "c"]
end

Foo::CONSTANT_NAME
# => ["a", "b", "c"]

How to implement private inner class in Ruby

why am I able to create Node objects outside the LinkedList class even though it is declared as private?

Because in ruby constants ignore "regular" visibility modifiers. They're always public, regardless of which section they're in. To make them private, use private_constant. Call this inelegant design or whatever, but that's how it is.

Also, be warned that even with private_constant, the privateness means very little. Basically, the only thing it does is hide the constant from lists (LinkedList.constants) and direct resolution (LinkedList::Node). If one knows the name, they will be able to access it.

class LinkedList
class Node
attr_accessor :val, :next
end

private_constant :Node
end

LinkedList.const_get('Node') # => LinkedList::Node

How to namespace constants inside anonymous class definitions?

When creating anonymous classes through Class.new, they don't seem to have their own namespace for constants

Sure, by the definition of the word “anonymous.” Compare two following snippets:

class C1; puts "|#{name}|"; end
#⇒ |C1|
C2 = Class.new { puts "|#{name}|" }
#⇒ ||

Unless assigned to the constant, the class has no name and hence all constants defined inside go to Object namespace. That said, the warning here is actually pointing out to error and Object::FOO = "bar" overrides Object::FOO = "foo" constant.

That said, one cannot use constants in this scenario. Use class-level instance variables instead, or construct unique constant names manually (I would advise avoiding polluting Object class with a bunch of unrelated constants, though.)

How to create a private class method?

private doesn't seem to work if you are defining a method on an explicit object (in your case self). You can use private_class_method to define class methods as private (or like you described).

class Person
def self.get_name
persons_name
end

def self.persons_name
"Sam"
end

private_class_method :persons_name
end

puts "Hey, " + Person.get_name
puts "Hey, " + Person.persons_name

Alternatively (in ruby 2.1+), since a method definition returns a symbol of the method name, you can also use this as follows:

class Person
def self.get_name
persons_name
end

private_class_method def self.persons_name
"Sam"
end
end

puts "Hey, " + Person.get_name
puts "Hey, " + Person.persons_name

Access a private constant from module

I'd suggest writing it like this so you won't have to change anything other than include B if you rename B:

module B
def access_private_here
puts self.class::MY_CONST
end
end

class A
include B
private
MY_CONST = "cat"
end

A.new.access_private_here #=> "cat"

How to make a module constant also visible within an eigenclass?

Solution

class << self
def eigen_scope
p [__method__, hello(self::NAME)]
#=> [:eigen_scope, "Hello Otto!"]
end
end

Why does self::NAME work?

  • A::NAME would be the easiest, hard-coded version.
  • B::NAME would also work, because B includes A
  • Inside eigen_scope, self is B, so self::NAME works as well
  • self::NAME would also work in self.class_scope
  • self::NAME wouldn't work in instance_scope : a B instance is not a class/module.

Why doesn't NAME work?

Here's a very good explanation.

constant lookup searches for constants that are defined in
Module.nesting, Module.nesting.first.ancestors, and
Object.ancestors if Module.nesting.first is nil or a module

self is the same in class_scope and eigen_scope.

Module.nesting is different though :

  • [B] for class_scope
  • [#<Class:B>, B] for eigen_scope

So Module.nesting.first.ancestors is :

  • [B, A, Object, Kernel, BasicObject] for class_scope
  • [#<Class:B>, A::ClassMethods, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject] for eigen_scope

A isn't searched, but A::ClassMethods!

So you could define :

module A
module ClassMethods
NAME = 'Bob'
end
end


Related Topics



Leave a reply



Submit