I have a simple Ruby base class where all the methods need to have protected visibility. The problem arises when another class inherits the base class and calls its methods. The Ruby interpreter stops and tells me the first method it interprets is a protected method, and tells me the class can't call it. Here is my code:
class Base
protected
def methodOne
# method code
end
def methodTwo
# method code
end
end
The error occurs when the subclass calls a method from the base.
Subclass.new.methodOne
I'm obviously missing something crucial with Ruby's visibility/inheritance model. Any help is appreciated!
You can only call your own and inherited protected methods.
What you are doing is creating an other new object (with Base.new) and call methodOne on it. You need to do self.methodOne
Example:
class Extended < Base
def new_method
self.methodOne # calling method one defined in Base
end
end
Related
I was hoping to extract a lot of logic into a separate class in the following code, but I am having trouble with that since it ends up calling a protected method.
This is my current code:
class ExcelSheet
...
protected
def save_stuff
# do work
end
end
class CustomSheet < ExcelSheet
def custom_stuff
# lots of logic
save_stuff
# more logic
end
end
This was my attempted code:
class LogicManager
def logic_valid?
# lots of logic
save_stuff
end
end
class CustomSheet < ExcelSheet
def custom_stuff
manager = LogicManager.new(some_data)
if manager.logic_valid?
# more logic
end
end
end
Unfortunately in my LogicManager I can't call save_stuff because it's protected. I didn't write the original method, and I'm sure it's marked as protected for a reason, so I don't think I should change that.
What options do I have to still refactor nicely?
It seems that you have a bit of a misunderstanding of protected methods.
Protected methods can be invoked only by the class that defines it, or by it's subclasses. In your case, it means that if you would like to invoke the method save_stuff in the class LogicManager, that means that LogicManager must inherit from ExcelSheet.
Given the naming of the classes, I am not sure if you want to do that as it doesn't seem logical to me (no pun intended).
I understand that Ruby's #initialize method is private. However, what stumps me is how Ruby makes the method private.
We normally define classes like this:
class Cat
def initialize(name)
#name = name
end
def say_name
puts #name
end
end
where #initialize seems to be defined publicly alongside #say_name. How does Ruby manage to make #initialize private after the class definition?
Yukihiro Matsumoto (the inventor of Ruby) has said:
#initialize is, by its design, supposed to be called only from within #new to separate per object/class initialization from the #new, thus you don't have to redefine #new. When you need/want to redefine #new, it's a sign of a bad design, I believe.
One of the reason #initialize being private is to tell you bad design.
So in summary it's a built in feature of Ruby that #initialize is automatically private and it's so developers won't easily call it outside of the .new class method.
Very interesting question! I researched it and found some interesting things about Ruby, though I did not find the exact answer you're looking for.
initialize is a private instance method that is meant to be redefined on most objects. It comes from BasicObject, the Ruby class from which all objects and classes inherit.
Any new class you create in Ruby will have a private instance method called initialize:
class Q ; end
Q.private_instance_methods.include?(:initialize)
=> true
That instance method is inherited from BasicObject#initialize:
q = Q.new
q.method(:initialize)
=> #<Method: Q(BasicObject)#initialize>
And the method itself is not defined in Ruby, it comes from the C source of the language:
q.method(:initialize).source_location
=> nil
This is what that looks like in the Ruby source code (object.c file):
rb_define_private_method(rb_cBasicObject, "initialize", rb_obj_dummy, 0);
rb_obj_dummy is basically a no-op function. Which makes sense because the expectation is that you'll override initialize with your own implementation code in your class.
All that said, your original question was about why initialize doesn't become a public method when you define it in the public space in your class definition. I don't know. Normally if you do that for any other method it will become a public method:
class Q
private
def my_private_method() "private" end
end
Q.new.my_private_method
NoMethodError: private method `my_private_method' called for #<Q:0x007fc5ea39eab0>
class Q
def my_private_method() "now i'm a public method" end
end
Q.new.my_private_method
=> "now i'm a public method"
So I guess somewhere else deep in the source code defining a method named "initialize" is handled differently from other methods. I couldn't find it, but maybe someone else can.
I want to know which class method_missing is defined.
It is defined in Object.
How can I figure out which class along the hierarchy overrides it?
You can use UnboundMethod#owner method to check where the method is implemented:
class A
def method_missing(*args)
# do something
end
end
method = A.instance_method(:method_missing)
method.owner
# => A
Note: If the method is implemented in module (which is later mixed into the class hierarchy somewhere), owner will return this module.
Rails has these cool properties that seem to be actually methods. For example:
class SomeController < ApplicationController
before_filter :authenticate!
end
What are these actually called and how would you create your own? For example, in one of my models I want to be able to have a dynamic property that selects an internal method for processing some results:
class MyModel < ActiveRecord::Base
active_method :some_class_method
end
How would I set this up so I can set active_method like that and be able to access the active_method symbol as an instance var?
Edit for elaboration:
So give this starter below, I need to figure out how to define "selected_method" so that it defines a accessor or instance variable so "called_selected_method" calls "method_b".
class MyClass
selected_method :method_b
def call_selected_method
end
private
def method_a
puts 'method_a'
end
def method_b
puts 'method_b'
end
end
c = MyClass.new
c.call_selected_method # should put 'method_b'
It's actually just a method call to a method defined on the class. before_filter is provided by a ruby Module, which is mixed in to ActionController.
Creating your own methods similar to before_filter is as easy as:
Define a class method on your Class
Call that method in any concrete implementations of your class.
Some example code:
class MyClass
class << self
def some_function(*args)
# your code here
end
end
some_function "foo"
end
If you wanted to abstract it further, you can put the class method in to a Module, and then include that module in to your class(es).
UPDATE:
In relation to your asking of how to get a call of some_function to set an instance variable on your class, you can't, as class methods cannot affect specific instances of that class.
I have to wonder, though... you're writing a method that will just act as a proxy to your other method, and would be hard-coded in to the class definition. That offers no benefit to you, and would just make your code redundantly complicated.
I have the following Ruby program:
class Access
def retrieve_public
puts "This is me when public..."
end
private
def retrieve_private
puts "This is me when privtae..."
end
protected
def retrieve_protected
puts "This is me when protected..."
end
end
access = Access.new
access.retrieve_protected
When I run it, I get the following:
accessor.rb:23: protected method `retrieve_protected' called for #<Access:0x3925
758> (NoMethodError)
Why is that?
Thanks.
Because you can call protected methods directly only from within instance method of this object, or or another object of this class (or subclass)
class Access
def retrieve_public
puts "This is me when public..."
retrieve_protected
anotherAccess = Access.new
anotherAccess.retrieve_protected
end
end
#testing it
a = Access.new
a.retrieve_public
# Output:
#
# This is me when public...
# This is me when protected...
# This is me when protected...
This is what protected methods are all about in Ruby. They can only be called if the receiver is self or of the same class hierarchy as self. Protected methods are typically used internally in instance methods.
See http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Classes#Protected
You can always circumvent this behavior by sending the method, e.g.
access.send(:retrieve_protected)
Although this could be considered bad practice as it's deliberately circumventing the access restrictions imposed by the programmer.
The protected access control in Ruby can be confusing at first. The problem is that you'll often read protected methods in Ruby can only be called by an explicit receiver of "self" or an sub-instance of the "self" Class whatever that class maybe. And that is not exactly true.
The real deal with Ruby protected methods is that you may only call a protected methods with an explicit receiver in the "context" an instances of the class or sub-classes that you've defined those methods in. If you try to call a protected method with an explicit receiver with in context that is not the class or sub-classes where you defined the methods you'll get an error.