Reputation: 20367
I have a two part question
Best-Practice
These are the options I can see, which is the best?:
Technical part
Is there any way to make a private Module method?
module Thing
def self.pub; puts "Public method"; end
private
def self.priv; puts "Private method"; end
end
The private
in there doesn't seem to have any effect, I can still call Thing.priv
without issue.
Upvotes: 123
Views: 107458
Reputation: 2852
I think the best way (and mostly how existing libs are written) to do this is by creating a class within the module that deals with all the logic, and the module just provides a convenient method, e.g.
module GTranslate
class Translator
def perform(text)
translate(text)
end
private
def translate(text)
# do some private stuff here
end
end
def self.translate(text)
t = Translator.new
t.perform(text)
end
end
Upvotes: 101
Reputation: 4114
Here's a solution for how you can have multiple classes nested within a single module, with the ability to call a private method on the module that's accessible from any of the nested classes, by making use of extend
:
module SomeModule
class ClassThatDoesNotExtendTheModule
class << self
def random_class_method
private_class_on_module
end
end
end
class ClassThatDoesExtendTheModule
extend SomeModule
class << self
def random_class_method
private_class_on_module
end
end
end
class AnotherClassThatDoesExtendTheModule
extend SomeModule
class << self
def random_class_method
private_class_on_module
end
end
end
private
def private_class_on_module
puts 'some private class was called'
end
end
Some output to show the solution in action:
> SomeModule::ClassThatDoesNotExtendTheModule.random_class_method
NameError: undefined local variable or method `private_class_on_module' for SomeModule::ClassThatDoesNotExtendTheModule:Class
> SomeModule::ClassThatDoesExtendTheModule.random_class_method
some private class was called
> SomeModule::ClassThatDoesExtendTheModule.private_class_on_module
NoMethodError: private method `private_class_on_module' called for SomeModule::ClassThatDoesExtendTheModule:Class
> SomeModule::AnotherClassThatDoesExtendTheModule.random_class_method
some private class was called
> SomeModule::AnotherClassThatDoesExtendTheModule.random_class_method
NoMethodError: private method `private_class_on_module' called for SomeModule::AnotherClassThatDoesExtendTheModule:Class
Upvotes: 0
Reputation: 9378
This method won't allow sharing data with the private methods unless you explicitly pass the data by method parameters.
module Thing
extend self
def pub
puts priv(123)
end
private
def priv(value)
puts "Private method with value #{value}"
end
end
Thing.pub
# "Private method with value 123"
Thing.priv
# NoMethodError (private method `priv' called for Thing:Module)
Upvotes: 5
Reputation: 35064
What's about storing methods as lambdas within class variables/constants?
module MyModule
@@my_secret_method = lambda {
# ...
}
# ...
end
For test:
UPD: huge update of this code after 6 years shows cleaner way to declare private method d
module A
@@L = lambda{ "@@L" }
def self.a ; @@L[] ; end
def self.b ; a ; end
class << self
def c ; @@L[] ; end
private
def d ; @@L[] ; end
end
def self.e ; c ; end
def self.f ; self.c ; end
def self.g ; d ; end
def self.h ; self.d ; end
private
def self.i ; @@L[] ; end
class << self
def j ; @@L[] ; end
end
public
def self.k ; i ; end
def self.l ; self.i ; end
def self.m ; j ; end
def self.n ; self.j ; end
end
for expr in %w{ A.a A.b A.c A.d A.e A.f A.g A.h A.i A.j A.k A.l A.m A.n }
puts "#{expr} => #{begin ; eval expr ; rescue => e ; e ; end}"
end
Here we see that:
A.a => @@L
A.b => @@L
A.c => @@L
A.d => private method `d' called for A:Module
A.e => @@L
A.f => @@L
A.g => @@L
A.h => private method `d' called for A:Module
A.i => @@L
A.j => @@L
A.k => @@L
A.l => @@L
A.m => @@L
A.n => @@L
1) @@L
can not be accesses from outside but is accessible from almost everywhere
2) class << self ; private ; def
successfully makes the method d
inaccessible from outside and from inside with self.
but not without it -- this is weird
3) private ; self.
and private ; class << self
do not make methods private -- they are accessible both with and without self.
Upvotes: 1
Reputation: 125902
Constants are never private. However, it's possible to create a module or class without assigning it to a constant.
So an alternative to :private_class_method
is to create a private module or class and define public methods on it.
module PublicModule
def self.do_stuff(input)
@private_implementation.do_stuff(input)
end
@private_implementation = Module.new do
def self.do_stuff(input)
input.upcase # or call other methods on module
end
end
end
Usage:
PublicModule.do_stuff("whatever") # => "WHATEVER"
See the docs for Module.new and Class.new.
Upvotes: 0
Reputation: 6024
module Writer
class << self
def output(s)
puts upcase(s)
end
private
def upcase(s)
s.upcase
end
end
end
Writer.output "Hello World"
# -> HELLO WORLD
Writer.upcase "Hello World"
# -> so.rb:16:in `<main>': private method `upcase' called for Writer:Module (NoMethodError)
Upvotes: 80
Reputation: 369
A nice way is like this
module MyModule
class << self
def public_method
# you may call the private method here
tmp = private_method
:public
end
private def private_method
:private
end
end
end
# calling from outside the module
puts MyModule::public_method
Upvotes: 3
Reputation:
There's also Module.private_class_method
, which arguably expresses more intent.
module Foo
def self.included(base)
base.instance_eval do
def method_name
# ...
end
private_class_method :method_name
end
end
end
For the code in the question:
module Thing
def self.pub; puts "Public method"; end
def self.priv; puts "Private method"; end
private_class_method :priv
end
Ruby 2.1 or newer:
module Thing
def self.pub; puts "Public method"; end
private_class_method def self.priv; puts "Private method"; end
end
Upvotes: 93
Reputation: 1185
You can use the "included" method to do fancy things when a module is mixed in. This does about what you want I think:
module Foo
def self.included(base)
class << base
def public_method
puts "public method"
end
def call_private
private_method
end
private
def private_method
puts "private"
end
end
end
end
class Bar
include Foo
end
Bar.public_method
begin
Bar.private_method
rescue
puts "couldn't call private method"
end
Bar.call_private
Upvotes: 29
Reputation: 17051
Unfortunately, private
only applies to instance methods. The general way to get private "static" methods in a class is to do something like:
class << self
private
def foo()
....
end
end
Admittedly I haven't played with doing this in modules.
Upvotes: 12