Private Class (Not Class Method) in a Ruby Module

Private class (not class method) in a Ruby module?

I haven't seen such concept so far in Ruby, but I guess you could simulate that by creating private method which would return a class created as a local variable (remember that in Ruby, a class is an object just like any other, and can be instantiated in a method and returned by it).

BTW, even private methods in Ruby aren't as private as in other languages - you can always access them using send method. But doing that implies you know what you are doing.

Private module method accessible by module classes

Modules in ruby are just containers for methods and constants. Classes know nothing about the modules that they form part of and don't inherit from them. so there is no way to make a method on ´Foo´ available for all the classes that may be inside it.

This approach may give you what you want.

module Foo
module CommandRunning
def execute(*args)
# ...
end
end

class Bar
include CommandRunning

def initialize
execute('initialize', 'bar')
end
end
end

Use or misuse of private class methods in Ruby

There is no such thing as a "class method" in Ruby. There are only instance methods. "Class methods" are actually singleton methods (on an object which just so happens to be an instance of Class), and "singleton methods" are just instance methods of the singleton class.

private methods can only be called with an implicit receiver (which is self), i.e. they can only be called by other methods on the same object (which in turn means they must be methods of the same class or one of its ancestors, i.e. superclasses, prepended modules or included modules).

This means that private "class methods" can only be called by other "class methods", i.e. methods defined in Example's singleton class, Class, Module, Object, Kernel, or BasicObject. You cannot call them from methods defined in Example.

Think about it: what's the purpose of private? Its purpose is encapsulation, we want to encapsulate the internal implementation and representation details from the external protocol. There are two kinds of encapsulation in wide use currently: Abstract Data Type-Oriented Encapsulation (where instances of different types are encapsulated from each other, but instances of the same type can access each other's internals, e.g. classes in Java) and Object-Oriented Encapsulation (where different objects are encapsulated from each other, even if they are instances of the same type, e.g. Ruby and interfaces in Java). Ruby is an object-oriented language, therefore it uses object-oriented encapsulation. (Java OTOH uses ADT-oriented encapsulation for classes, which is counter-intuitive, since it is usually claimed to be an object-oriented language).

In your case, we have two objects, Example and an instance of Example. They are two different objects, so object-oriented encapsulation simply forbids you from accessing one object's private parts from the other object. Even if Ruby did use ADT-oriented encapsulation, it still wouldn't work: ADT-oriented encapsulation allows two instances of the same type to access each other's privates, but the two objects aren't of the same type, one is an instance of Class and the other is an instance of Example.

Basically, you want to manipulate two object's internals at the same time and that is just not possible in OOP. It's a fundamental design principle of OOP that each object (and each object alone) is responsible for its private parts, and you can only interact with that object by sending it messages through its public protocol.

tl;dr: what you want goes directly against the basic encapsulation principles in OO. Either Example::g must be public or you need to change your design. (Resorting to reflective hacks to circumvent access protection in code you have no control over is a code smell at best. Resorting to reflective hacks to circumvent access protection in code you own is just plain wrong, because you control the access protection, you could just change it.)


One possible solution is to leave OOP behind altogether, and look to functional programming for help. We could try and use closures for encapsulation:

We start out with your original example:

class Example
private_class_method def self.g
puts 4711
end

def f
self.class.send(:g)
end
end

Example.new.f
# 4711

Now, we turn g into a local variable and assign a lambda to it, and in turn use that lambda to define f:

class Example
g = -> {
puts 4711
}

define_method(:f, &g)
end

Example.new.f
# 4711

Now, g is (in some sense) even more "private" than before, because it only exists within the lexical scope of the class body, not even class methods defined in a different class body can access it. However, the lambda being referenced by g is a proper object and can be passed around even into different scopes.

But, presumably you don't want f and g to be just identical (otherwise you could just have used module_function, after all), instead you want f to do something other than just delegate to g. That is also possible:

class Example
g = -> {
puts 4711
}

define_method(:f) do
puts 42
g.()
end
end

Example.new.f
# 42
# 4711

This works, because in some other sense g is less "private" than before: lexical scopes can nest, in particular the lexical scopes of blocks (and only blocks) nest, so that the nested block (the block passed to define_method in this case) can access the lexical environment of the outer lexical scope (the class body in this case) even after that lexical scope no longer exists (the class body has finished evaluating).

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

Testing a ruby module class method not working

Reading the reference in this article which explains include, extend, and prepend and I found out that extends works with the Singleton class already so the self is unnecesary.

I made a small test with this code, which removes the self in the definition

module SomeModule
module Account
def account_info
raise NotImplementedError
end
end
end

class MockClass
extend SomeModule::Account
end

MockClass.account_info

And that raises NotImplementedError

Why can't ruby classes inside modules have the same scope as regular classes?


I have a module with a class inside,

No, you don't. You have a module definition with a class definition inside, but that does not make the class a nested class. Ruby does not have nested classes.

Ruby is not Beta, Scala, or Newspeak, there are no nested classes in Ruby.

Nesting a module or class definition inside another module or class definition does not create nesting relationship between the two classes / modules. It only makes the constant which references the class / module part of the outer class' / module's namespace.

In other words, there is no difference between

module Foo
class Bar
end
end

and

class Quux
end

module Foo
Bar = Quux
end

Only the constant is nested, but not the object that is referenced by the constant.

but I find that the class inside can't reach any of the methods in the enclosing module without specifying the module path.

That is precisely because there is no "enclosing module". There is a lexically enclosing module definition but that does not create any form of relationship whatsoever between the Foo class and the MyMod module.

Another way to look at it is that the module_function doesn't seem to carry into the class.

I honestly don't understand what you mean by that, what it means for a method to "carry into a class", but Module#module_function is not magic. It does exactly what the documentation says it does: it takes an instance method of the module, copies it as an instance method of the singleton class of the module, and makes the original instance method private.

You can read its specification in the Ruby/Spec, it is fairly simple. Also, the Rubinius source code, both the basic version for booting the Rubinius kernel and the full version are fairly readable.

In the end, Module#module_function really does not do much more than

class Module
def module_function(*meths)
meths.each do |meth|
define_singleton_method(meth, &instance_method(meth).bind(self))
private meth
end

self
end
end

If you run this, you get an error on the "But I can't..." line of:

undefined local variable or method `meaning'

The reason is simple: neither the class Foo nor any of its superclasses has any method of that name, so of course you get an exception.

But if you remove the MyMod bits around the whole file, it has no problem accessing the outer method.

There is no "outer method". Ruby does not have Beta-like nested classes. That is really the fundamental cause of your misunderstanding. You expect Ruby to behave like Beta, but it just doesn't. Ruby takes inspiration from any languages, most notably (in rough order of importance) Smalltalk, Lisp, Perl, and Clu, but Beta is not among them.

This here works for a completely different reason:

def meaning
42
end

class Foo
def initialize
meaning
end
end

Methods that are defined at the top-level are implicitly defined as private instance methods of Object. This is because the default definee at the top-level is ::Object. Since Foo inherits from Object, method lookup will eventually find the meaning method defined in Object.

Is there an easy way to make these accessible without having to give the full path?

Inheritance. For example, Module#append_features, which is called by Module#include, makes the module the superclass of the including class, and thus all instance methods of the module become part of the method lookup ancestry chain.

An aside: if there is no nesting, then what does Module::nesting do? Well, yeah, that is an unfortunately named method. The term "nested class" or "nested module" has a well-defined meaning in OO going all the way back to Beta. But this method is about a completely different kind of nesting:

It refers to the lexical nesting of module definitions, and not to nesting of modules themselves.

For example, these module definitions all define the exact same module, but the definition text has different nesting:

module Foo
module Bar
module Baz
module Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux, Foo::Bar::Baz, Foo::Bar, Foo]
end
end
end
end

module Foo
module Bar
module Baz::Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux, Foo::Bar, Foo]
end
end
end

module Foo
module Bar::Baz
module Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux, Foo::Bar::Baz, Foo]
end
end
end

module Foo::Bar
module Baz
module Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux, Foo::Bar::Baz, Foo::Bar]
end
end
end

module Foo
module Bar::Baz::Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux, Foo]
end
end

module Foo::Bar::Baz
module Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux, Foo::Bar::Baz]
end
end

module Foo::Bar
module Baz::Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux, Foo::Bar]
end
end

module Foo::Bar::Baz::Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux]
end

Again, this is purely lexical nesting of the module definition. The module itself is not nested; the module itself is the same in all of these cases. This nesting only affects constant lookup.

Constants are looked up first lexically outwards in enclosing module definitions, then upwards the inheritance chain.

There is another instance where things can be nested: blocks create nested lexical scopes, whereas all other lexical scopes (script, module / class definition, and method definition) don't nest. In other words, blocks and only blocks have access to the local variables (and self) of their enclosing lexical scopes.

Ruby: Use module method inside a class method

By including the module, you make module_method is an instance method on TestClass, meaning you need to invoke it on an instance of the class, not the class itself.

If you want to make it a method on the class itself, you need to extend TestModule, not include it.

module TestModule
def module_method
"module"
end
end

class TestClass
extend TestModule # extend, not include

def self.testSelfMethod
str = module_method
puts str
end
TestClass.testSelfMethod # "method"
end

ruby private class method helper

You could define the methods in an anonymous module by passing the block to Module.new, make each instance method in the module private and extend your class with the module:

class Class
def define_private_class_methods(&block)
mod = Module.new(&block)
mod.instance_methods.each { |m| mod.send(:private, m) }
extend(mod)
end
end

This has the desired result:

class Person
define_private_class_methods do
def method_one
123
end
end
end

Person.send(:method_one)
#=> 123

Person.method_one
#=> private method `method_one' called for Person:Class (NoMethodError)

... and as a bonus, it also gives you a super method: (probably of little use)

class Person
def self.method_one
super * 2
end
end

Person.method_one
#=> 456

Of course, you don't have to use extend, you could just as well define the methods manually:

class Class
def define_private_class_methods(&block)
mod = Module.new(&block)
mod.instance_methods.each do |m|
define_singleton_method(m, mod.instance_method(m))
private_class_method(m)
end
end
end

The essential component is the anonymous module, so you have a (temporary) container to define the methods in.



Related Topics



Leave a reply



Submit