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
Related
I was trying to create a class that has a private class method. I want this private class method available to be used inside an instance method.
The following was my first attempt:
class Animal
class << self
def public_class_greeter(name)
private_class_greeter(name)
end
private
def private_class_greeter(name)
puts "#{name} greets private class method"
end
end
def public_instance_greeter(name)
self.class.private_class_greeter(name)
end
end
Animal.public_class_greeter('John') works fine, printing John greets private class method.
However, Animal.new.public_instance_greeter("John") throws an error: NoMethodError: private method 'private_class_greeter' called for Animal:Class.
That is expected, as the invocation self.class.private_class_greeter is same as Animal.private_class_greeter, which obviously throws an error.
After searching on how this can be fixed, I came up with the following code, that does the job:
class Animal
class << self
def public_class_greeter(name)
private_class_greeter(name)
end
private
def private_class_greeter(name)
puts "#{name} greets private class method"
end
end
define_method :public_instance_greeter, &method(:private_class_greeter)
end
I don't exactly understand what is happening here: &method(:private_class_greeter).
Could you please explain what does this mean?
If I were to replace:
define_method :public_instance_greeter, &method(:private_class_greeter)
with:
def public_instance_greeter
XYZ
end
then, what should be the content in place of XYZ?
How does Ruby parse &method(:private_class_greeter)?
The expression &method(:private_class_greeter) is
the value of the method call method(:private_class_greeter)
prefixed with the & operator.
What does the method method do?
The method method looks up the specified method name in the current context and returns a Method object that represents it. Example in irb:
def foo
"bar"
end
my_method = method(:foo)
#=> #<Method: Object#foo>
Once you have this method, you can do various things with it:
my_method.call
#=> "bar"
my_method.source_location # gives you the file and line the method was defined on
#=> ["(irb)", 5]
# etc.
What is the & operator for?
The & operator is used to pass a Proc as a block to a method that expects a block to be passed to it. It also implicitly calls the to_proc method on the value you pass in, in order to convert values that are not Proc into a Proc.
The Method class implements to_proc — it returns the contents of the method as a Proc. Therefore, you can prefix a Method instance with & and pass it as a block to another method:
def call_block
yield
end
call_block &my_method # same as `call_block &my_method.to_proc`
#=> "bar"
The define_method method just happens to take a block with the contents of the new method that is being defined. In your example, &method(:private_class_greeter) passes in the existing private_class_greeter method as a block.
Is this how &:symbol works?
Yes. Symbol implements to_proc so that you can simplify your code like this:
["foo", "bar"].map(&:upcase)
#=> ["FOO", "BAR"]
# this is equivalent to:
["foo", "bar"].map { |item| item.upcase }
# because
:upcase.to_proc
# returns this proc:
Proc { |val| val.send(:upcase) }
How can I replicate &method(:private_class_greeter)?
You can pass in a block that calls the target method:
define_method :public_instance_greeter do |name|
self.class.send(:private_class_greeter, name)
end
Of course, then you don't need to use define_method anymore, which results in the same solution Eric mentioned in his answer:
def public_instance_greeter(name)
self.class.send(:private_class_greeter, name)
end
First, take good care with your indentation. private should be 2 spaces to the right: it gives the impression that public_instance_greeter is private otherwise.
If you don't care about encapsulation, you could simply use Kernel#send:
class Animal
class << self
def public_class_greeter(name)
private_class_greeter(name)
end
private
def private_class_greeter(name)
puts "#{name} greets private class method"
end
end
def public_instance_greeter(name)
self.class.send(:private_class_greeter, name)
end
end
Animal.public_class_greeter('John')
# John greets private class method
Animal.new.public_instance_greeter("John")
# John greets private class method
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.
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'
I'm learning Ruby, and have come up to a point where I am confused.
The book I am using is talking about private, public, and protected methods, but I am still a bit confused. What are the differences between each?
Public - can be called from anywhere
Private - The method cannot be called outside class scope. The object can only send the message to itself
ex: the baker has bake method as public but break_eggs is private
Protected - You can call an object's protected methods as long as the default object self is an instance of the same class as the object whose method you're calling
ex: with n protected method, c1 can ask c2 to execute c2.n, because c1 and c2 are both instances of the same class
And last but not least:
Inheritance: Subclasses inherit the method-access rules of their superclass
if "class D < C", then D will exhibit the same access behaviour as instances of C
reference: http://www.amazon.com/Ruby-Rails-Techniques-Developers/dp/1932394699
public methods are open to everyone. As for private versus protected, I refer to "Ruby Private Methods vs. Protected Methods":
What is the difference between 'private' and 'protected' methods in
Ruby? In Ruby, the primary difference between a 'private' and
'protected' method is that a private method cannot be called with an
explicit receiver, while a protected method can. What is an
'explicit receiver', you ask? An explicit receiver is the object that
is receiving a message. In the following example, we have a receiver
('parent') and a method ('get_name'). The 'parent' object is
receiving the instruction to perform the 'get_name' method.
Check out "Ruby Programming/Syntax/Classes" for a detailed example and explanation.
Put simply, the differences between private, public, and protected methods are visibility of that method in the program, kinda like read-only, read and write, and near invisible.
Unlike some of the other languages, you can't completely hide a Ruby private method, you can only access private methods for your instance of object and not for any other object instance of a class.
Public, of course, is total accessibility and methods are usually defaulted to public with some exceptions.
Protected methods are accessible from objects of the same class or even children, which is not the case for a private method.
Let me explain
Private and protected methods work a little differently in Ruby than in most other
programming languages. Suppose you have a class called Foo and a subclass SubFoo .
In languages like Java, SubFoo has no access to any private methods defined by Foo .
As seen in the Solution, Ruby provides no way to hide a class’s methods from its sub-
classes. In this way, Ruby’s private works like Java’s protected.
Suppose further that you have two instances of the Foo class, a and b. In languages
like Java, a and b can call each other’s private methods. In Ruby, you need to use a
protected method for that. This is the main difference between private and protected methods in Ruby.
class Foo
private
def pri
'hey I am private of Foo'
end
protected
def prot
'Hey I am protected of Foo'
end
end
Now subclass of Foo
class SubFoo < Foo
def call_pri_of_foo
pri
end
def call_prot_of_foo
prot
end
end
Now calling the accessors within SubFoo
> sub_foo = SubFoo.new
=> #<SubFoo:0x00000002b56ad8>
> sub_foo.call_pri_of_foo
=> "hey I am private of Foo"
> sub_foo.call_prot_of_foo
=> "Hey I am protected of Foo"
Up to here; there seem to be no difference
next_sub_foo = SubFoo.new
=> #<SubFoo:0x00000002b1a0b0>
def next_sub_foo.access_private(child_of_sub_foo)
child_of_sub_foo.pri
end
def next_sub_foo.access_protected(child_of_sub_foo)
child_of_sub_foo.prot
end
Now calling the accessor
> next_sub_foo.access_private(sub_foo)
# => NoMethodError: private method `pri' called for #<SubFoo:0x00000002b56ad8>
but it can access the protected methods of its siblings
> next_sub_foo.access_protected(sub_foo)
# => "Hey I am protected of Foo"
You can also see #tenderlove's blog for more clear picture http://tenderlovemaking.com/2012/09/07/protected-methods-and-ruby-2-0.html
The difference will be on Visibility and how they are affected by Inheritance :
Visibility :
|| Anywhere || Public can be accessed from inside and outside the class.
|| Inside the class || Both Private and Protected can only be accessed from inside the class.
The similarity between Protected and Private :
Both can be accessed from outside the class through a public method.
The differences between Protected and Private are :
Private method can not be called with a receiver (not even with #self). UNLESS ... calling a PRIVATE SETTER method. If you try to remove the receiver, Ruby will create a local variable. Self is a must in this case.
Protected may or may not use self.
Protected can access another object's protected method that comes from the same class, Private can't.
When it comes to Inheritance :
Private methods can only be called on subclasses implicitly (simply just the name of the method) but not explicitly (using #self).
Protected can be called both ways (with or without #self || implicitly or explicitly).
Example with code below :
class Dog
attr_accessor :name, :age
def initialize(n, a)
self.name = n
self.age = a
end
def accessing_private
"#{self.name} in human years is #{human_years}. This is secret!"
end
def accessing_protected
"Will this work? " + a_protected_method
end
def eat_more_than(other)
# accessing other instance's protected method from the same class
daily_diet < other.daily_diet
"#{name} eats more than #{other.name}"
end
def boy
gender_method("boy") # accessing private setter method
end
protected
def daily_diet
age * 2 # the younger, the more they have to eat
end
def a_protected_method
"Yes, I'm protected!"
end
private
attr_writer :gender
def gender_method(gender)
self.gender = gender # private setter method requires self
"#{name} is a #{gender}"
end
def human_years
age * 8
end
end
# Create the first object of Dog
blake = Dog.new("Blake", 5)
p blake.accessing_private # "Blake in human years is 16. This is secret!"
p blake.accessing_protected # "Will this work? Yes, I'm protected!"
# Create the second object of Dog
jackson = Dog.new("Jackson", 1)
# Below, protected methods from different objects of the same type/class
# are proven to share access
p jackson.eat_more_than(blake) # true -> "Jackson eats more than Blake"
# Below, accessing private setter method through a public method.
p blake.boy # Blake is a boy
I think breaking down an explicit receiver is what is important if your having trouble grasping the concept.
An explicit receiver is an object that is accepting a message.
person.get_name
person is the receiver and the method "get_name" is giving instructions to the object "person" to perform the method "get_name".
class Person
attr_accessor :first_name, :last_name
def initialize(first_name, last_name)
#first_name = first_name
#last_name = last_name
puts "And #{phone_number}" # Private method called when initialized
end
private
def phone_number
return "XXX-XXX-XXXX"
end
end
p p1 = Person.new("mike", "jones")
p p1.phone_number # Not within the context of the object instance.
When a method is private, it can only be used by other methods inside the object in whose class it is defined.
Studying the information I've taken from here, I extended explanations through errors, and for my opinion, helps to understand why and how to use protected and not private.
1) Protected:
The line num 12 crash because the parameter received is from another class, the error message is clear:
v.rb:12:in `==': undefined method `sku' for "Object of another class ==> crash":String (NoMethodError)
2) Private:
If remove self from line 8 and 12, and I change protected for private, crash because in line 12, other doesn't know what sku is:
v.rb:12:in `==': private method `sku' called for #<Product:0x00000001574e68 #name="Bread", #quantity=1> (NoMethodError)
The program:
class Product
attr_accessor :name, :quantity
def initialize(name)
#name = name
#quantity = 1
puts "The SKU is #{self.sku}"
end
def == (other)
self.sku == other.sku
end
protected
def sku
name.crypt("yo")
end
end
milk1 = Product.new("Milk")
milk2 = Product.new("Milk")
bread = Product.new("Bread")
puts milk1 == bread
puts milk1 == milk2
puts milk1 == "Object of another class ==> crash"
Ruby doesn't seem to have a facility for defining a protected/private block like so:
protected do
def method
end
end
This would be nice compared to
protected
def method
end
public
where you might forget to "public" after the protected methods.
It seems possible to implement this using metaprogramming. Any ideas how?
Since you want to group by functionality you can declare all your methods, and then declare which ones are protected and private by using protected followed by the symbols of the methods you want to be protected, and the same for private.
The following class shows what I mean. In this class all methods are public except bar_protected and bar_private which are declared protected and private at the end.
class Foo
def bar_public
print "This is public"
end
def bar_protected
print "This is protected"
end
def bar_private
print "This is private"
end
def call_protected
bar_protected
end
def call_private
bar_private
end
protected :bar_protected
private :bar_private
end
I actually endorse bodnarbm's solution and do not recommend doing this, but since I can't pass up a metaprogramming challenge, here's a hack that will accomplish this:
class Module
def with_protected
alias_if_needed = lambda do |first, second|
alias_method first, second if instance_methods.include? second
end
metaclass = class<<self; self end
metaclass.module_eval {|m| alias_if_needed[:__with_protected_old__, :method_added]}
def self.method_added(method)
protected method
send :__with_protected_old__ if respond_to? :__with_protected_old__
end
yield
metaclass.module_eval do |m|
remove_method :method_added
alias_if_needed[:method_added, :__with_protected_old__]
end
end
end
Old question, but I think this is a bit cleaner:
class Whatever
module ProtectedBits
protected
def foo()
...
end
end
include ProtectedBits
end