Why is there a difference between the two types of calling method in the following code:
class Access
def method_missing name
print "Method is missing"
end
protected
def protected_method
print "Calling Protected Method"
end
end
access = Access.new
access.protected_method #Method is missing
access.send(:protected_method) #Calling Protected Method
The access.protected_method works as expected. But the send option tries to call the method even though it's protected. I would like to know what happens internally.
I get a string for the method to be called, so I would like to use send but I do not want to call the protected methods.
That's just how send works. Use public_send instead, if you only want to call public methods.
Using send allows you to bypass some rules, such as accessing protected or private methods of an object.
Another thing is that send allows you to call methods dynamically. Using send will you to decide which methods gets called without knowing it before the program runs, that is you can decide what message will be passed on to it during runtime.
Aside from that, as far as I know, they're the same.
Well, that's how send works. It allows you to call a method regardless of its visibility.
If you send a non-existing name, then method_missing will kick in. See:
class Access
def method_missing name
"Method is missing"
end
protected
def protected_method
"Calling Protected Method"
end
end
access = Access.new
access.protected_method # => "Method is missing"
access.send(:protected_method) # => "Calling Protected Method"
access.send(:non_existing_method) # => "Method is missing"
If you don't want your protected methods to be called this way, then, I guess, this is a good use case for the dreaded eval.
eval "Access.new.protected_method" # => "Method is missing"
eval "Access.new.non_existing_method" # => "Method is missing"
One of the uses of send is to bypass visibility, so this is a feature, not a bug, and in fact you'll probably break the expectations of other programmers using your class if you don't allow this behavior.
That said, if you really need it, you can override send and method_missing in your class to implement the behavior you want:
class Access
def foo; puts "foo"; end
def method_missing(message, *args)
puts "Method #{message} missing"
end
def send(message, *args)
if self.class.protected_instance_methods.include?(message)
method_missing(message, *args)
else
super
end
end
protected
def bar; puts "bar"; end
end
a = Access.new
a.foo #=> 'foo'
a.bar #=> 'Method bar missing'
a.send('bar') #=> 'Method bar missing'
Related
send can be used to call public as well as private methods.
Example:
class Demo
def public_method
p "public_method"
end
private
def private_method
p "private_method"
end
end
Demo.new.send(:private_method)
Demo.new.send(:public_method)
Then where and why to use public_send?
Use public_send when you want to dynamically infer a method name and call it, yet still don't want to have encapsulation issues.
In other words, public_send will only simulate the call of the method directly, no work arounds. It's good for mixing encapsulation and meta programming.
Example:
class MagicBox
def say_hi
puts "Hi"
end
def say_bye
puts "Bye"
end
private
def say_secret
puts "Secret leaked, OMG!!!"
end
protected
def method_missing(method_name)
puts "I didn't learn that word yet :\\"
end
end
print "What do you want met to say? "
word = gets.strip
box = MagicBox.new
box.send("say_#{word}") # => says the secret if word=secret
box.public_send("say_#{word}") # => does not say the secret, just pretends that it does not know about it and calls method_missing.
When the input is hi and secret this is the output:
What do you want met to say? hi
=> Hi
=> Hi
What do you want met to say? secret
=> Secret leaked, OMG!!!
=> I didn't learn that word yet :\\
As you can see, send will call the private method and hence a security/encapsulation issue occurs. Whereas public_send will only call the method if it is public, otherwise the normal behaviour occurs (calling method_missing if overridden, or raising a NoMethodError).
I am trying to work our for myself access modifiers in Ruby. I have:
class Person
def initialize (first_name, last_name, age)
#first_name=first_name
#last_name=last_name
#age=age
end
def show()
puts #first_name
puts #last_name
puts #age
end
protected
def compare(other)
self.instance_variable_get(:#age)<=>other.instance_variable_get(:#age)
end
end
p1=Person.new("Some", "Body", "99")
p1.show
puts "\n"
p2=Person.new("Who", "Ever", "21")
p2.show
puts "\n"
p1.compare(p2)
I am getting the error "protected method `compare' called for # (NoMethodError)"
I have tried calling from within the class and without. I pasted the without version here. I thought that protected methods could be called on other objects of the same class. What does this error mean and how would I properly use a protected method here? Thank you for your help.
You got the wrong view of the protected visibility. The Ruby doc says:
The second visibility is protected. When calling a protected method the sender must be a subclass of the receiver or the receiver must be a subclass of the sender. Otherwise a NoMethodError will be raised.
So the restriction of the visibility is applied to the sender, not the receiver as what you thought.
If you want to call compare outside of the instance methods, you need to use public visibility. You need to remove the protected modifier if you can. This is the recommended way.
If the code is fixed and you cannot modify that piece of code, you can use the Object#send method. Object#send will bypass the visibility constraint and can access even private methods.
p1.send(:compare, p2)
Or you can reopen the class and change the visibility of the compare class:
# you code here
# reopen and modify visibility
class Person
public :compare
end
p1.compare(p2)
You can call a protected method in a public method of the class...
class Person
def initialize (first_name, last_name, age)
#first_name=first_name
#last_name=last_name
#age=age
end
def same_age?(other)
age == other.age
end
def show
puts #first_name
puts #last_name
puts #age
end
protected
def age
#age
end
end
p1=Person.new("Some", "Body", "99")
p1.show
puts "\n"
p2=Person.new("Who", "Ever", "21")
p2.show
puts "\n"
# calls a method that calls a protected method
p1.same_age?(p2)
=> false
# but you can't call #age directly...
begin
p1.age
rescue NoMethodError
puts "no method error (protected)"
end
If I have this in my main program:
def hi
puts 'hi'
end
self.hi
it won't work because hi is private.
I've learned that all methods in Ruby are public by default, but this doesn't seem to be the case, why?
It is tricky to define a method in the main environment. A method is private by default when you define it in main. So you either have to use it as private:
def hi
puts 'hi'
end
hi
or explicitly make it public:
def hi
puts 'hi'
end
public :hi
self.hi
Methods are public by default, except for "top-level procedures". If you define something which looks like a top-level procedure, it will actually be defined as a private instance method of Object.
def main_method
p "This is from main_method"
end
public :main_method
class Klass
def initialize
Object.main_method # Or we can simply say main_method as we do in the second exaple
end
end
puts Klass.new
It is the way to create Object 'class methods' as shown. If it were private, then we could not specify the receiver.
When it is private, as it is by default, then we would call it like this:
def main_method
p "This is from main_method"
end
class Klass
def initialize
main_method # With it being private, implicit self only
end
end
puts Klass.new
So is there any advantage to either way? I guess if you make it private, you send some kind of communication to the developer that you have a preference and this should be used as a private method, rather than a public method.
In practice, I don't see an advantage.
I must be missing something about how people do this in Ruby.
If '#protected' is uncommented we get:
in 'what': protected method 'zoop' called for Foo:Class (NoMethodError)
Is there a better way to approach protected class methods?
class Foo
class << self
#protected
def zoop
"zoop"
end
end
public
def what
"it is '#{self.class.zoop}'"
end
protected
end
a = Foo.new
p a.what # => "it is 'zoop'"
I would like zoop to be protected or private (no calling 'Foo.zoop'), but so far, I can't seem to find an elegant way.
It barely matters to make methods private or protected in Ruby, since you can just call send() to get around them.
If you want zoop to stay protected, use send() like this:
def what
"it is '#{self.class.send(:zoop)}'"
end
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
The class
class A
private
def foo
puts :foo
end
public
def bar
puts :bar
end
private
def zim
puts :zim
end
protected
def dib
puts :dib
end
end
instance of A
a = A.new
test
a.foo rescue puts :fail
a.bar rescue puts :fail
a.zim rescue puts :fail
a.dib rescue puts :fail
a.gaz rescue puts :fail
test output
fail
bar
fail
fail
fail
.send test
[:foo, :bar, :zim, :dib, :gaz].each { |m| a.send(m) rescue puts :fail }
.send output
foo
bar
zim
dib
fail
The question
The section labeled "Test Output" is the expected result. So why can I access private/protected method by simply Object#send?
Perhaps more important:
What is the difference between public/private/protected in Ruby? When to use each? Can someone provide real world examples for private and protected usage?
Technically: Because send doesn't do anything to check method visibility. (It would be more work to do so.)
Philosophically: Ruby is a very permissive language. You can already just open up a class and make any method you want public. The language designers implemented send in a way that allows it to override the restrictions normally imposed by private. Ruby 1.9 was originally going to have two variants, a private-respecting send and an unsafe variant called send!, but this was apparently dropped for backwards compatibility.
As for what private, protected and public mean:
public methods can be called by any sender
protected methods cannot be called outside of an instance of the method's class or an instance of a subclass
private methods cannot be called with an explicit receiver (with a couple of exceptions, such as setter methods, which always have to have an explicit receiver, and so can be called within the class that way)