Calling protected class method from instance method in Ruby
EDIT: Answering the updated question
It is forbidden to call private methods with explicit receiver. You either have to use implicit receiver (private_bang
, without self
) or use send
. Please see my another answer for more information.
By the way, the original question is about calling class instance methods from instance methods. Your clarification doesn't include that. But if that's still true, you have to use self.class.send
or make the method public (so that you can use explicit receiver).
Is there a way to call a private Class method from an instance in Ruby?
Here is a code snippet to go along with the question. Using "private" in a class definition does not apply to class methods. You need to use "private_class_method" as in the following example.
class Foo
def self.private_bar
# Complex logic goes here
puts "hi"
end
private_class_method :private_bar
class <<self
private
def another_private_bar
puts "bar"
end
end
public
def instance_bar
self.class.private_bar
end
def instance_bar2
self.class.another_private_bar
end
end
f=Foo.new
f=instance_bar # NoMethodError: private method `private_bar' called for Foo:Class
f=instance_bar2 # NoMethodError: private method `another_private_bar' called for Foo:Class
I don't see a way to get around this. The documentation says that you cannot specify the receive of a private method. Also you can only access a private method from the same instance. The class Foo is a different object than a given instance of Foo.
Don't take my answer as final. I'm certainly not an expert, but I wanted to provide a code snippet so that others who attempt to answer will have properly private class methods.
Calling a private instance method from a class method in Ruby
Using private or protected really don't do that much in Ruby. You can call send on any object and use any method it has.
class Foo
def Foo.bar(my_instance, n)
my_instance.send(:plus, n)
end
end
Ruby: Calling class method from instance
Rather than referring to the literal name of the class, inside an instance method you can just call self.class.whatever
.
class Foo
def self.some_class_method
puts self
end
def some_instance_method
self.class.some_class_method
end
end
print "Class method: "
Foo.some_class_method
print "Instance method: "
Foo.new.some_instance_method
Outputs:
Class method: Foo
Instance method: Foo
Calling an instance method from a class method
In order to call an instance method you need to create an instance of the object. In ruby calling 'new' creates an instance of a class.
You can change this line
puts "#{action}"
To this:
puts "#{new.action}"
.new
inside of the class will make a new instance of the class you are in. Which will also run your initialize
and decrease @@variable
by one. The way the class is written right now we never past if @@variable <9
unless you are making instances of the class outside of the code you shared.
Overall this is a pretty odd class. It isn't common to use class variables like this. It is much more common to create an instance of a class, use instance variables/methods to house your logic.
Calling inherited class method from instance method in Ruby
It won't work, because the instance of C
is not kind_of?(B.singleton_class)
.
In ruby, a protected method can be called within the context of an object which is kind_of?
the class which defines the method, with an explicit receiver which is also kind_of?
the class which defines the method.
You defined a protected method on the singleton class of B
, so that method can only be called within the objects which are kind_of?(B.singleton_class)
. The class C
inherits B
, so C
's singleton class inherits B
's singleton class, so C
is kind_of? B.singleton_class
. Thus in your first case, it works. But obviously, C.new
is not kind_of? B.singleton_class
, so it won't work.
How does one access protected class methods from instance methods in Ruby?
Upon further discussions with rue: and drbrain: in ruby-lang, it turns out that my impulse to save memory by placing utility functions at the class level was misplaced.
In Ruby, the instance methods hang off the class anyway, and the answer is to go ahead and place the utility functions at the instance level as private.
In summary, a utility function that is accessed only by instance methods:
class Foo
def what
"it is '#{zoop}'"
end
private
def zoop
"zoop"
end
end
p Foo.new.what # => "it is 'zoop'"
For a utility function that needs to be called from instance and class methods, a nested module seemed to be a popular approach:
class Foo
module Util
def self.zoop
"zoop"
end
end
def what
"it is '#{Util.zoop}'"
end
class << self
def class_what
"for all time it is '#{Util.zoop}'"
end
end
end
p Foo.new.what # => "it is 'zoop'"
p Foo.class_what # => "for all time it is 'zoop'"
p Foo::Util.zoop # visible, alas
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).
Related Topics
Don't the Ruby Methods Instance_Eval() and Send() Negate the Benefits of Private Visibility
How to Link to a Nested Route Path Inside a Loop
Prawn Gem: How to Create the .Pdf from an *Existing* File (.Xls)
Ruby on Rails Group_By (How to Group Events by Month)
Consolidating Duplicate Array Items
Browser-Based Uploads Using Post
Importing CSV as Test Data in Cucumber
Rails 3 Translations Within Models in Production
Devise 'Find_First_By_Auth_Conditions' Method Explanation
Nomethoderror on Section 5.7 of Rails Guide
How to Marshal a Hash with Arrays
Vps Apache Config - Invalid Command 'Passengerdefaultruby' After Adding Latest Passenger Gem
Instantiate Capybara Browser and Set a Proxy
How to Convert a Large Gem to Standalone Rails App
Selenium Can't Find Fields with Type Number
How to Make an Infowindow Automatically Display as Open with Google-Maps-For-Rails