I need to make some instance methods private after registering that object in another object.
I don't want to freeze the object because it must remain editable, only with less functionality. And I don't want to undef the methods since they are used internally.
What I need is something like:
class MyClass
def my_method
puts "Hello"
end
end
a = MyClass.new
b = MyClass.new
a.my_method #=> "Hello"
a.private_instance_method(:my_method)
a.my_method #=> NoMethodError
b.my_method #=> "Hello"
Any ideas?
You can call method private on the method name anytime to make it private:
>> class A
>> def m
>> puts 'hello'
>> end
>> end
=> nil
>> a = A.new
=> #<A:0x527e90>
>> a.m
hello
=> nil
>> class A
>> private :m
>> end
=> A
>> a.m
NoMethodError: private method `m' called for #<A:0x527e90>
from (irb):227
from /usr/local/bin/irb19:12:in `<main>'
or, from outside the class:
A.send :private, :m
class A
def test
puts "test"
end
def test2
test
end
end
a = A.new
class << a
private :test
end
a.test2 # works
a.test # error: private method
What's public and what's private is per class. But each object can have its own class:
class Foo
private
def private_except_to_bar
puts "foo"
end
end
class Bar
def initialize(foo)
#foo = foo.dup
class << #foo
public :private_except_to_bar
end
#foo.private_except_to_bar
end
end
foo = Foo.new
Bar.new(foo) # => "foo"
foo.private_except_to_bar
# => private method `private_except_to_bar' called for #<Foo:0xb7b7e550> (NoMethodError)
But yuck. Consider these alternatives:
Just make the method public.
Explore alternative designs.
Related
Here is an external class which has its class method overwritten.
class Foo
def class
"fooo"
end
end
class Boo < Foo
end
class Moo < Foo
end
Now I have an instance of the subclass. Is it possible to find out which class it belongs to?
foo.class # currently returns 'fooo', I want get Boo or Moo.
You could use instance_method to grab the class method from somewhere safe (such as Object) as an UnboundMethod, bind that unbound method to your instance, and then call it. For example:
class_method = Object.instance_method(:class)
# #<UnboundMethod: Object(Kernel)#class>
class_method.bind(Boo.new).call
# Boo
class_method.bind(Moo.new).call
# Moo
class_method.bind(Foo.new).call
# Foo
Of course, if you've also replaced Object#class (or Kernel#class) then all bets are off and you're in a whole new world of pain and confusion.
An object's class also happens to be the superclass of its singleton_class:
Boo.new.singleton_class.superclass
#=> Boo
Moo.new.singleton_class.superclass
#=> Moo
Foo.new.singleton_class.superclass
#=> Boo
I prefer #Stefan’s and #muistooshort's solutions, but here's an alternative.
class Foo
def class
"foo"
end
end
class Boo < Foo
end
class Who < Boo
def class
"who"
end
end
boo = Boo.new
boo.method(:class).super_method.call
#=> Boo
who = Who.new
who.method(:class).super_method.call
#=> "foo"
who.method(:class).super_method.super_method.call
#=> Who
More generally:
def my_class(obj)
m = obj.method(:class)
until m.owner == Kernel do
m = m.super_method
end
m.call
end
my_class(boo)
#=> Boo
my_class(who)
#=> Who
See Method#super_method.
This solution is a little hacky, but overriding class is pretty hacky in itself, so when in Rome, do as Romans do:
class Foo
def class
'fooo'
end
end
class Boo < Foo
end
boo = Boo.new
=> #<Boo:0x00007fd2361feba8>
boo.class
=> "fooo"
boo.inspect
=> "#<Boo:0x00007fd2361feba8>"
klass = boo.inspect.split(':').reject(&:empty?)[0..-2].join('::').sub('#<', '')
=> "Boo"
boo.is_a?(Kernel.const_get(klass))
=> true
This also works for classes in a module:
module Bar
class Foo
def class
'fooo'
end
end
class Boo < Foo
end
end
boo = Bar::Boo.new
=> #<Bar::Boo:0x00007fe5a20358b0>
boo.class
=> "fooo"
boo.inspect
=> "#<Bar::Boo:0x00007fe5a20358b0>"
klass = boo.inspect.split(':').reject(&:empty?)[0..-2].join('::').sub('#<', '')
=> "Bar::Boo"
boo.is_a?(Kernel.const_get(klass))
=> true
I have a class:
class Foo
def self.test
#test
end
def foo
#test = 1
bar
end
private
def bar
#test = 2
end
end
object = Foo.new.foo
Foo.test
# => nil
The only way I could get it to output '2' is by making #test a class variable. Is there any other way around using the instance variable and being able to display it with Foo.test?
It's not really clear to me what you want to achieve, and why. Here's an example with a "class instance variable". It might be what you're looking for:
class Foo
class << self
attr_accessor :test
end
attr_accessor :test
def foo
#test = 1
bar
end
private
def bar
Foo.test = 2
end
end
foo = Foo.new
foo.foo
p foo.test
#=> 1
p Foo.test
#=> 2
Is there a way to define a method foo on module/class A so that it will be visible only from within module/class B, or its descendants? The following illustrates this situation:
A.new.foo # => undefined
class B
A.new.foo # => defined
def bar
A.new.foo # => defined
end
def self.baz
A.new.foo # => defined
end
end
class C < B
A.new.foo # => defined
def bar
A.new.foo # => defined
end
def self.baz
A.new.foo # => defined
end
end
I intuitively felt refinement was close in spirit, but it does not seem to do what I want.
This works. ^_^
class A
private
def foo
"THE FOO !!!"
end
end
class B < A
public :foo
def initialize
#foo = self.foo
end
end
puts "A.new.foo #{ A.new.foo rescue '... sorry, no.' }"
=> A.new.foo ... sorry, no.
puts "B.new.foo #{ B.new.foo rescue '... sorry, no.' }"
=> B.new.foo THE FOO !!!
If you want to use the A.new.foo within all sub classes by still using the A class name then you should use the following.
class A
private
def foo
"THE FOO !!!"
end
end
class B
class A < A
public :foo
end
attr_reader :c, :d
def c
A.new.foo
end
def d
A.new.foo
end
end
puts "A.new.foo #{ A.new.foo rescue '... sorry, no.' }"
=> A.new.foo ... sorry, no.
puts B.new.c
=> THE FOO !!!
puts B.new.d
=> THE FOO !!!
I want to call a protected superclass class method from an instance method in the base class.
class A
class << self
protected
def foo
puts "In foo"
end
end
end
class B < A
def bar
puts "In bar"
# call A::foo
end
end
What's the best way to do this?
... 2.67 years later ...
A simpler way to solve this is with class_eval
class A
class << self
protected
def foo
puts "In foo"
end
end
end
class B < A
def bar
self.class.class_eval { foo }
end
end
B.new.bar # prints "In foo"
Override the method in B, calling super:
class A
class << self
protected
def foo
puts "In foo"
end
end
end
class B < A
def self.foo
super
end
def bar
puts "In bar"
# call A::foo
self.class.foo
end
end
>> B.foo
=> In foo
>> B.new.bar
=> In bar
=> In foo
So far, the only solution I've found is to define a class method in the subclass that calls the class method in the superclass. Then I can call this method in the subclass' instance method.
class A
class << self
protected
def foo
puts "In foo"
end
end
end
class B < A
def self.call_foo
puts "In call_foo"
A::foo
end
def bar
puts "In bar"
self.class.call_foo
end
end
Is this really necessary?
I'd probably just make A.foo public. Otherwise send will do it, since it bypasses access controls:
A.send(:foo)
I have a two part question
Best-Practice
I have an algorithm that performs some operation on a data structure using the public interface
It is currently a module with numerous static methods, all private except for the one public interface method.
There is one instance variable that needs to be shared among all the methods.
These are the options I can see, which is the best?:
Module with static ('module' in ruby) methods
Class with static methods
Mixin module for inclusion into the data structure
Refactor out the part of the algorithm that modifies that data structure (very small) and make that a mixin that calls the static methods of the algorithm module
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.
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
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
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)
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
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.
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)
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
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.
Make a private module or class
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.
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