Ruby - Protected method - ruby

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.

Related

How can I extract code into its own class while still calling a protected method?

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).

Why can't protected methods be called with symbol to proc?

Given the following class:
class Foo
def a
dup.tap { |foo| foo.bar }
end
def b
dup.tap(&:bar)
end
protected
def bar
puts 'bar'
end
end
It seems like both Foo#a and Foo#b should be equivalent, but they're not:
> Foo.new.a
bar
=> #<Foo:0x007fe64a951ab8>
> Foo.new.b
NoMethodError: protected method `bar' called for #<Foo:0x007fe64a940a88>
Is there a reason for this? Is this a bug?
Tested on Ruby 2.2.3p173
Let's start by noting that in Ruby, as you probably know, in a method a declared on a class Foo, I can invoke protected methods on any instance of Foo.
How does Ruby determine whether we're in a method declared on class Foo though? To understand this, we'll have to dig into the internals of method invocation. I'll be using examples from version 2.2 of MRI, but presumably the behavior and implementation are the same in other versions (I'd love to see results from testing this on JRuby or Rubinious, though).
Ruby does this in rb_call0. As the comment suggests, self is used to determine whether we can call protected methods. self is retrieved in rb_call from the current thread's call frame info. Then, in rb_method_call_status, we check that this value of self is of the same class on which a protected method is defined.
Blocks confuse the issue somewhat. Remember that local variables in a Ruby method are captured by any block declared in that method. This means that in the block, self is the same self on which the method was invoked. Let's look at an example:
class Foo
def give_me_a_block!
puts "making a block, self is #{self}"
Proc.new do
puts "self in block0 is #{self}"
end
end
end
proc = Foo.new.give_me_a_block!
proc.call
Running this, we see the same instance of Foo is the same at all levels, even though we called the proc from a completely different object.
So, now we understand why it's possible to call a protected method on another instance of the same class from within a block in a method.
Now let's look at why a proc created with &:bar can't do this. When we place an & sign before a method argument we do two things: instruct ruby to pass this argument as a block, and instruct it to call to_proc on it.
This means invoking the method Symbol#to_proc. This method is implemented in C, but when we invoke a C method, the pointer to self on the current frame becomes the receiver of that C method -- in this case, it becomes the symbol :bar. So we're looking at the instance of foo we got as though we are in a method of class Symbol, and we can't invoke a protected method.
That's a mouthful, but hopefully it makes enough sense. Let me know if you have any suggestions as to how I might improve it!

How does Ruby make #initialize private?

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.

Ruby Protected Methods Issue

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

What's the best way to unit test protected & private methods in Ruby?

What's the best way to unit test protected and private methods in Ruby, using the standard Ruby Test::Unit framework?
I'm sure somebody will pipe up and dogmatically assert that "you should only unit test public methods; if it needs unit testing, it shouldn't be a protected or private method", but I'm not really interested in debating that. I've got several methods that are protected or private for good and valid reasons, these private/protected methods are moderately complex, and the public methods in the class depend upon these protected/private methods functioning correctly, therefore I need a way to test the protected/private methods.
One more thing... I generally put all the methods for a given class in one file, and the unit tests for that class in another file. Ideally, I'd like all the magic to implement this "unit test of protected and private methods" functionality into the unit test file, not the main source file, in order to keep the main source file as simple and straightforward as possible.
You can bypass encapsulation with the send method:
myobject.send(:method_name, args)
This is a 'feature' of Ruby. :)
There was internal debate during Ruby 1.9 development which considered having send respect privacy and send! ignore it, but in the end nothing changed in Ruby 1.9. Ignore the comments below discussing send! and breaking things.
Here's one easy way if you use RSpec:
before(:each) do
MyClass.send(:public, *MyClass.protected_instance_methods)
end
Just reopen the class in your test file, and redefine the method or methods as public. You don't have to redefine the guts of the method itself, just pass the symbol into the public call.
If you original class is defined like this:
class MyClass
private
def foo
true
end
end
In you test file, just do something like this:
class MyClass
public :foo
end
You can pass multiple symbols to public if you want to expose more private methods.
public :foo, :bar
instance_eval() might help:
--------------------------------------------------- Object#instance_eval
obj.instance_eval(string [, filename [, lineno]] ) => obj
obj.instance_eval {| | block } => obj
------------------------------------------------------------------------
Evaluates a string containing Ruby source code, or the given
block, within the context of the receiver (obj). In order to set
the context, the variable self is set to obj while the code is
executing, giving the code access to obj's instance variables. In
the version of instance_eval that takes a String, the optional
second and third parameters supply a filename and starting line
number that are used when reporting compilation errors.
class Klass
def initialize
#secret = 99
end
end
k = Klass.new
k.instance_eval { #secret } #=> 99
You can use it to access private methods and instance variables directly.
You could also consider using send(), which will also give you access to private and protected methods (like James Baker suggested)
Alternatively, you could modify the metaclass of your test object to make the private/protected methods public just for that object.
test_obj.a_private_method(...) #=> raises NoMethodError
test_obj.a_protected_method(...) #=> raises NoMethodError
class << test_obj
public :a_private_method, :a_protected_method
end
test_obj.a_private_method(...) # executes
test_obj.a_protected_method(...) # executes
other_test_obj = test.obj.class.new
other_test_obj.a_private_method(...) #=> raises NoMethodError
other_test_obj.a_protected_method(...) #=> raises NoMethodError
This will let you call these methods without affecting other objects of that class.
You could reopen the class within your test directory and make them public for all the
instances within your test code, but that might affect your test of the public interface.
One way I've done it in the past is:
class foo
def public_method
private_method
end
private unless 'test' == Rails.env
def private_method
'private'
end
end
I'm sure somebody will pipe up and
dogmatically assert that "you should
only unit test public methods; if it
needs unit testing, it shouldn't be a
protected or private method", but I'm
not really interested in debating
that.
You could also refactor those into a new object in which those methods are public, and delegate to them privately in the original class. This will allow you to test the methods without magic metaruby in your specs while yet keeping them private.
I've got several methods that are
protected or private for good and
valid reasons
What are those valid reasons? Other OOP languages can get away without private methods at all (smalltalk comes to mind - where private methods only exist as a convention).
Similar to #WillSargent's response, here's what I've used in a describe block for the special case of testing some protected validators without needing to go through the heavyweight process of creating/updating them with FactoryGirl (and you could use private_instance_methods similarly):
describe "protected custom `validates` methods" do
# Test these methods directly to avoid needing FactoryGirl.create
# to trigger before_create, etc.
before(:all) do
#protected_methods = MyClass.protected_instance_methods
MyClass.send(:public, *#protected_methods)
end
after(:all) do
MyClass.send(:protected, *#protected_methods)
#protected_methods = nil
end
# ...do some tests...
end
To make public all protected and private method for the described class, you can add the following to your spec_helper.rb and not having to touch any of your spec files.
RSpec.configure do |config|
config.before(:each) do
described_class.send(:public, *described_class.protected_instance_methods)
described_class.send(:public, *described_class.private_instance_methods)
end
end
You can "reopen" the class and provide a new method that delegates to the private one:
class Foo
private
def bar; puts "Oi! how did you reach me??"; end
end
# and then
class Foo
def ah_hah; bar; end
end
# then
Foo.new.ah_hah
I would probably lean toward using instance_eval(). Before I knew about instance_eval(), however, I would create a derived class in my unit test file. I would then set the private method(s) to be public.
In the example below, the build_year_range method is private in the PublicationSearch::ISIQuery class. Deriving a new class just for testing purposes allows me to set a method(s) to be public and, therefore, directly testable. Likewise, the derived class exposes an instance variable called 'result' that was previously not exposed.
# A derived class useful for testing.
class MockISIQuery < PublicationSearch::ISIQuery
attr_accessor :result
public :build_year_range
end
In my unit test I have a test case which instantiates the MockISIQuery class and directly tests the build_year_range() method.
In Test::Unit framework can write,
MyClass.send(:public, :method_name)
Here "method_name" is private method.
& while calling this method can write,
assert_equal expected, MyClass.instance.method_name(params)
Here is a general addition to Class which I use. It's a bit more shotgun than only making public the method you are testing, but in most cases it doesn't matter, and it's much more readable.
class Class
def publicize_methods
saved_private_instance_methods = self.private_instance_methods
self.class_eval { public *saved_private_instance_methods }
begin
yield
ensure
self.class_eval { private *saved_private_instance_methods }
end
end
end
MyClass.publicize_methods do
assert_equal 10, MyClass.new.secret_private_method
end
Using send to access protected/private methods is broken in 1.9, so is not a recommended solution.
To correct the top answer above: in Ruby 1.9.1, it's Object#send that sends all the messages, and Object#public_send that respects privacy.
Instead of obj.send you can use a singleton method. It’s 3 more lines of code in your
test class and requires no changes in the actual code to be tested.
def obj.my_private_method_publicly (*args)
my_private_method(*args)
end
In the test cases you then use my_private_method_publicly whenever you want to test my_private_method.
http://mathandprogramming.blogspot.com/2010/01/ruby-testing-private-methods.html
obj.send for private methods was replaced by send! in 1.9, but later send! was removed again. So obj.send works perfectly well.
In order to do this:
disrespect_privacy #object do |p|
assert p.private_method
end
You can implement this in your test_helper file:
class ActiveSupport::TestCase
def disrespect_privacy(object_or_class, &block) # access private methods in a block
raise ArgumentError, 'Block must be specified' unless block_given?
yield Disrespect.new(object_or_class)
end
class Disrespect
def initialize(object_or_class)
#object = object_or_class
end
def method_missing(method, *args)
#object.send(method, *args)
end
end
end
I know I'm late to the party, but don't test private methods....I can't think of a reason to do this. A publicly accessible method is using that private method somewhere, test the public method and the variety of scenarios that would cause that private method to be used. Something goes in, something comes out. Testing private methods is a big no-no, and it makes it much harder to refactor your code later. They are private for a reason.

Resources