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'm trying to get a good Ruby coding style. To prevent accidently calling a local variable with the same name, I'm always using self. where appropriate. But now I stumbled over this:
class MyClass < ActiveRecord::Base
before_validation :sanitize_user_data
private
def sanitize_user_data
self.sanitize_name # with ".self" it's a problem, without it's not!
end
def sanitize_name
unless self.name.nil?
self.name.gsub!(/\s+/, ' ')
self.name.strip!
end
end
end
The above code results in an error
private method sanitize_name called
but when removing self. and just using sanitize_name, it works. Why's that?
This happens because private methods cannot be called with an explicit receiver, and saying self.sanitize_name is explicitly specifying the object (self) that should receive sanitize_name, rather than relying on the implicit receiver (which is also self).
You can't avoid this, you either need to call plain old sanitize_name without an explicit receiver, or do self.send(:sanitize_name). I'm not sure that always explicitly specifying self is really "good style", but that's subjective. If you want to ensure you're calling a method instead of a variable, add parenthesis:
def a; "method"; end
a = "variable"
a() #=> "method"
a #=> "variable"
Why's that?
By definition. Private methods can only be invoked by a receiverless message send, that's the definition of what private means.
Ruby as an Object Oriented Language. What that means is whatever message I send, I strictly send it on some object/instance of class.
Example:
class Test
def test1
puts "I am in test1. A public method"
self.test2
end
def test2
puts "I am in test2. A public Method"
end
end
makes sense I call method test2 on self object
But I cannot do this
class Test
def test1
puts "I am in test1. A public method"
self.test2 # Don't work
test2 # works. (where is the object that I am calling this method on?)
end
private
def test2
puts "I am in test2. A private Method"
end
end
When test2 is public method I can call it on self (fair enough, a method sent to self object). But when test2 is private method I cannot call it on self. So where is the object that I am sending method on?
The Problem
In Ruby, private methods can't be called directly with an explicit receiver; self doesn't get any special treatment here. By definition, when you call self.some_method you are specifying self as the explicit receiver, so Ruby says "No!"
The Solution
Ruby has rules for its method lookups. There may be a more canonical source for the rules (other than going to the Ruby source), but this blog post lays out the rules right at the top:
1) Methods defined in the object’s singleton class (i.e. the object itself)
2) Modules mixed into the singleton class in reverse order of inclusion
3) Methods defined by the object’s class
4) Modules included into the object’s class in reverse order of inclusion
5) Methods defined by the object’s superclass, i.e. inherited methods
In other words, private methods are first looked up in self without requiring (or allowing) an explicit receiver.
where is the object that I am sending method on
It's self. Whenenver you don't specify a receiver, the receiver is self.
The definition of private in Ruby is that private methods can only be called without a receiver, i.e. with an implicit receiver of self. Interestingly, it didn't bother you at all with the puts method which is also a private instance method ;-)
Note: there's an exception to this rule. Private setters can be called with an explicit receiver, as long as the receiver is self. In fact, they must be called with an explicit receiver, because otherwise there would be an ambiguity with local variable assignments:
foo = :fortytwo # local variable
self.foo = :fortytwo # setter
self means the current instance of the object you are in.
class Test
def test1
self
end
end
Calling Test.new.test1 will return something like #<Test:0x007fca9a8d7928>.
This is the instance of the Test object you are currently using.
Defining a method as private means it can only be used inside the current object.
When using self.test2, you are going outside of the current object (you get the instance) and you call the method.
So you are calling a private methods as if you were not in the object, which is why you can't.
When you don't specify self, you remain inside the current object.
So you can just call the method. Ruby is smart enough to know that test2 is a method and not a variable and to call it.
This has been changed in Ruby 2.7 (December 2019): self.foo() is now also valid for a private method foo.
References:
https://rubyreferences.github.io/rubychanges/2.7.html#selfprivate_method
https://bugs.ruby-lang.org/issues/11297
https://bugs.ruby-lang.org/issues/16123
Is it possible to attach my own methods to another class, in limited region ?
If so, could anyone show me a better practice, or is it supposed to use something like deligate to do this?
The situation is like this : In class A that receive, generate and pass out instance of class B,
I want to attach some method onto those bs, without leaving those new methods accessible outside class A.
These are called singleton methods. You can add methods to any object, only affecting that instance, not the entire class it was created from.
some_object = Whatever.new
other_object = Whatever.new
class << some_object
def another_method
...
end
end
some_object.another_method # works
other_object.another_method # error: no such method another_method
You can also use define_singleton_method:
some_object.define_singleton_method(:foo) do |arg1, arg2|
puts "Called with #{arg1} and #{arg2}"
end
some_object.foo(7, 8)
There's also instance_eval, but you get the idea ;)
You can write a private method in class B that takes an A object as an argument and uses instance_variable_get, instance_variable_set, and send to access whatever data from the object you want. This is pretty ugly though.
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.