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, prepend
ed modules or include
d 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. class
es 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 interface
s 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
How to Wrap the Invocation of a Ruby Method by Including a Module
Rails 3. How to Add a Helper That Activeadmin Will Use
How to Set the Actionmailer Default_Url_Options's :Host Dynamically to the Request's Hostname
How to Remove a Substring After a Certain Character in a String Using Ruby
Ruby Modules and Classes Same Name in Structure
Difference Between Kernel#Yield_Self, Yield(Self), Kernel#Then and Object#Tap in Ruby
Replacing the 'Auto_Link' Method in Ruby on Rails 3.1
Rails Assets Pipeline "Cannot Allocate Memory - Nodejs"
How to Add a Virtual Attribute to a Model in Ruby on Rails
Any Way to Determine Which Object Called a Method
Difference Between '%{}', '%Q{}', '%Q{}' in Ruby String Delimiters
Suppressing the Output of a Command Run Using 'System' Method While Running It in a Ruby Script
Execute Ruby Code in Sublime Text 2
Rails: Render View from Outside Controller
How to Specify Output File Encoding in Ruby
Ruby/Rails - .Each Iterator Is Printing Entire Array at the End of the Loop