Define protected class methods from module - ruby - ruby

I'm trying to give a class a protected method via modules which can only be called by itself and other classes inheriting them. I'd like to hide the method from being used by Class.method and only be accessible to the Class class internally or subclasses that inherit Class. How would I go about doing this? This is what I have so far.
#! /usr/bin/env ruby
module ProtectedClassMethods
#class_instance_var = 1234567
def included(base); base.extend(ProtectedClassMethods); end
protected
def protected_class_method; puts "A PROTECTED CLASS METHOD => #{#class_instance_var}"; end
end
class Class1
def value; "Class1"; end
end
class Class2 < Class1
extend ProtectedClassMethods
class << self
protected_class_method #crashes here
end
def value; "Class2"; end
end
class Class3 < Class2
class << self
#class_instance_var = 24681012
protected_class_method
end
def value; "Class3"; end
end
puts "Initializing Class1"
class1 = Class1.new
puts "Done (Class1)"
puts "Initializing Class2"
class2 = Class2.new
puts "Done (Class2)"
puts "Initializing Class3"
class3 = Class3.new
puts "Done (Class3)"
puts class1.value
puts class2.value
puts class3.value
gives this error
./protected_class_test.rb:20:in `singletonclass': undefined local variable or method `protected_class_method' for #<Class:Class2> (NameError)
from ./protected_class_test.rb:18:in `<class:Class2>'
from ./protected_class_test.rb:16:in `<main>'

Here is a working thing.
module ProtectedClassMethods
#class_instance_var = 1234567
def included(base); base.extend(ProtectedClassMethods); end
protected
def protected_class_method; puts "A PROTECTED CLASS METHOD => #{#class_instance_var}"; end
end
class Class1
def value; "Class1"; end
end
class Class2 < Class1
extend ProtectedClassMethods
protected_class_method #crashes here
def value; "Class2"; end
end
class Class3 < Class2
#class_instance_var = 24681012
protected_class_method
def value; "Class3"; end
end
puts "Initializing Class1"
class1 = Class1.new
puts "Done (Class1)"
Doing
class << self
protected_class_method #crashes here
end
You were actually calling the method on the meta class (the class of the class) and the method was defined on the class itself.

Related

Why does ruby allow child classes access parent's private methods?

class Main
def say_hello
puts "Hello"
end
private
def say_hi
puts "hi"
end
end
class SubMain < Main
def say_hello
puts "Testing #{say_hi}"
end
end
test = SubMain.new
test.say_hello()
OUTPUT:
hi
Testing
The difference is that in ruby you can call private methods in subclasses implicitly but not explicitly. Protected can be called both ways. As for why? I guess you would have to ask Matz.
Example:
class TestMain
protected
def say_hola
puts "hola"
end
def say_ni_hao
puts "ni hao"
end
private
def say_hi
puts "hi"
end
def say_bonjour
puts "bonjour"
end
end
class SubMain < TestMain
def say_hellos
# works - protected/implicit
say_hola
# works - protected/explicit
self.say_ni_hao
# works - private/implicit
say_hi
# fails - private/explicit
self.say_bonjour
end
end
test = SubMain.new
test.say_hellos()

Why isn't Class.new called?

I am trying to override Class.new so that I can see what classes are being created e.g.
class Class
class << self
alias new_orig new
def new(*args)
obj = new_orig *args
print "created",obj,"\n"
obj
end
end
end
class X
end
It doesn't print anything, but if I create class using Class.new it works
puts Class.new
created#<Class:0x8b75888>
#<Class:0x8b75888>
So why there is a difference between class X and Class.new ?
To get the wanted effect, you could use Class#inherited:
class Class
def inherited(subclass)
puts "Created #{subclass}"
end
end
class XX #writes "Created XX"
end

Unable to override base class method in derived class

Here are the Ruby classes I have:
class MyBase
class << self
def static_method1
##method1_var ||= "I'm a base static method1"
end
def static_method1=(value)
##method1_var = value
end
def static_method2
##method2_var ||= "I'm a base static method2"
end
def static_method2=(value)
##method2_var = value
end
end
def method3
MyBase::static_method1
end
end
class MyChild1 < MyBase
end
class MyChild2 < MyBase
class << self
def static_method1
##method1_var ||= "I'm a child static method1"
end
end
end
c1 = MyChild1.new
puts c1.method3 #"I'm a base static method1" - correct
c2 = MyChild2.new
puts c2.method3 # "I'm a base static method1" - incorrect. I want to get "I'm a child static method1"
I'm aware of attr_accessor and modules, but I can't use use them here because I want them to give default values in MyBase class. I want to override MyBase.static_method1 in MyChild2.
The problem is that method3 is always explicitly calling the method on the base class. Change it to this:
def method3
self.class.static_method1
end
After that, consider not using ##.
## in ruby is extremely counterintuitive and rarely means what you think it means.
The problem with ## is that it is shared across the all of the inherited classes and the base class. See this blog post for an explanation.

How to call super class method within class

How can I call super class method within class like:
class A
def foo
puts "lol"
end
end
class B < A
foo
end
You're trying to call an instance method from within the context of a class. This is not valid.
What would work:
class A
def self.foo
puts "lol"
end
end
class B < A
foo
end

Private module methods in Ruby

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

Resources