Just started to learn Ruby. I'm confused with Ruby's private keyword.
Let's say I have code like this
private
def greeting
random_response :greeting
end
def farewell
random_response :farewell
end
Does private only applied to the #greeting or both - #greeting and #farewell?
It's fairly standard to put private/protected methods at the bottom of the file. Everything after private will become a private method.
class MyClass
def a_public_method
end
private
def a_private_method
end
def another_private_method
end
protected
def a_protected_method
end
public
def another_public_method
end
end
As you can see in this example, if you really need to you can go back to declaring public methods by using the public keyword.
It can also be easier to see where the scope changes by indenting your private/public methods another level, to see visually that they are grouped under the private section etc.
You also have the option to only declare one-off private methods like this:
class MyClass
def a_public_method
end
def a_private_method
end
def another_private_method
end
private :a_private_method, :another_private_method
end
Using the private module method to declare only single methods as private, but frankly unless you're always doing it right after each method declaration it can be a bit confusing that way to find the private methods. I just prefer to stick them at the bottom :)
In Ruby 2.1 method definitions return their name, so you can call private on the class passing the function definition. You can also pass the method name to private. Anything defined after private without any arguments will be a private method.
This leaves you with three different methods of declaring a private method:
class MyClass
def public_method
end
private def private_method
end
def other_private_method
end
private :other_private_method
private
def third_private_method
end
end
It applies to everything under private i.e greeting and farewell
To make either of them private you can make greeting alone private as below:
def greeting
random_response :greeting
end
private :greeting
def farewell
radnom_response :farewell
end
Documentation is available at Module#private
In Ruby 2.1 you can mark single methods as private also like this (space is optional):
class MyClass
private \
def private_method
'private'
end
private\
def private_method2
'private2'
end
def public_method
p private_method
'public'
end
end
t = MyClass.new
puts t.public_method # will work
puts t.private_method # Error: private method `private_method'
# called for #<MyClass:0x2d57228> (NoMethodError)
Related
Getting the following error when trying to call a private method inside the same class:
undefined local variable or method a_private_method' for AClass (NameError)`
Here the class:
class AClass
def self.public_method
file = a_private_method
end
private
def a_private_method
do something
end
end
You are trying to call an instance method from a class method, this does of course not work.
Try this
class AClass
class << self
def public_method
file = a_private_method
end
private
def a_private_method
# do something
end
end
end
You can also use self as the receiver similar to what you already did but pleases be aware that moving the method to the private part of your class does not work in this case. You could use private_class_method though.
class AClass
def self.public_method
file = a_private_method
end
def self.a_private_method
# do something
end
private_class_method :a_private_method
end
end
See https://jakeyesbeck.com/2016/01/24/ruby-private-class-methods and https://dev.to/adamlombard/ruby-class-methods-vs-instance-methods-4aje.
I have a PaymentManager service:
/payment_manager/
- online_payment_creator.rb
- manual_payment_creator.rb
- payment_splitter.rb
Code for 3 services:
module PaymentManager
class ManualPaymentCreator < ApplicationService
def call
# do stuff in transaction
PaymentSplitter.call(a, b, c)
end
end
end
module PaymentManager
class OnlinePaymentCreator < ApplicationService
# do stuff in transaction
PaymentSplitter.call(a2, b2, c2)
end
end
module PaymentManager
class PaymentSplitter < ApplicationService
def call(x, y, z)
# do very dangerous stuff
end
end
end
So, the PaymentSplitter essentially splits a payment across multiple invoices, and must be done only inside either of the other 2 services, but should never be called on its own (from a controller or in the console, etc).
I normally would make a private method inside ManualPaymentCreator but because it's needed in BOTH services, I don't know how to do that, as I can't use a simple private instance method without duplication.
Perhaps you are looking for inheritance and protected methods
class Foo < ApplicationService
def public_method
end
protected
#everything after here is private to this class and its descendants
def some_method_to_be_shared_with_descendants
#I am only accessible from this class and from descendants of this class
...
end
private
#Everything after here is private to this class only
def some_private_methods_that_are_only_accessible_from_this_class
end
...
end
Then descendant classes like so
class Bar < Foo
def do_something
# can access protected methods from Foo class here as native methods
some_res = some_method_to_be_shared_here
# do something with some_res
end
end
So you should descend your other 2 classes from PaymentSplitter and set the shared methods as protected
Perhaps a private class contained in a module might work better, eg.
module PaymentSplitting
def self.included(klass)
raise 'Error' unless klass.in?([ManualPaymentCreator, OnlinePaymentCreator])
end
class PaymentSplitter
end
private_constant :PaymentSplitter
end
This should allow you to freely reference PaymentSplitter in any class which includes PaymentSplitting, and you can only include PaymentSplitting in ManualPaymentCreator or OnlinePaymentCreator. Referencing PaymentSplitting::PaymentSplitter will throw a NameError: private constant exception.
Given a Class in Ruby:
class MyClass
def self.my_class_method
puts "class method"
end
private
def my_method
puts "regular method"
end
private_class_method :my_class_method
end
To access private methods I can call .send(:my_method) on the Class Object, but how does that work for class methods?
You should do:
class MyClass
def self.my_class_method
puts "class method"
end
private
def my_method
puts "regular method"
end
private_class_method :my_class_method
end
# to call class method
MyClass.send :my_class_method # => class method
# to call instance method
MyClass.new.send :my_method # => regular method
In Ruby, class(s) are also objects, so you can call the #send method on the class also.
In Ruby, you can define private class methods as
class MyClass
class << self
private
def my_class_method
puts "class method"
end
end
end
Or using thhis macro like method: private_class_method
First off, MyClass.send(:my_method) would not work. You have to send it to an instance: MyClass.new.send(:my_method).
Then, your my_class_method is not really private.
Ruby's semantic of private are somewhat different from what you might be used to in other languages. Since Ruby allows you to bypass encapsulation if you choose to, private just means that a method can only be called implicitly, without sending a message to an actual object.
For example:
class Example
def test
'foobar'
end
def hello
puts test # implicit receiver
puts self.test # explicit receiver
end
end
This is all good, but why is this important for your question?
Because you're declaring my_class_method explicitly on self. Doing so bypasses the private modifier, and the method is public. This means that you can just call it with:
MyClass.my_class_method
If you really need private class methods, then you can define them on the metaclass:
class MyClass
class << self
private
def my_class_method
puts "class method"
end
end
private
def my_method
puts "regular method"
end
end
This will make my_class_method actually private, and force you to invoke it with any of these:
MyClass.send :my_class_method
MyClass.class_exec { my_class_method }
MyClass.class_eval { my_class_method }
MyClass.class_eval "my_class_method"
Just for reference, that's not how you create a private class method.
class A
private
def self.foo
"foo"
end
end
A.foo # => "foo"
To create a private class method, you need to use private_class_method.
class A
def self.foo
"foo"
end
private_class_method :foo
end
A.foo # => private method `foo' called for A:Class
There are no such things as class methods. Class methods are just singleton methods of the class. But there are no such things as singleton methods either. Singleton methods are just instance methods of the singleton class. So, class methods are just instance methods of the class's singleton class.
Since there is no such thing as a class method, only instance methods, you already know what to do:
To access private methods I can call .send(:my_method) on the Class Object, but how does that work for class methods?
If I'm writing a private method, does rails think that every method under the word private is going to be private? or is it supposed to be only private for the first method?
private
def signed_in_user
redirect_to signin_url, notice: "Please sign in." unless signed_in?
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_path) unless current_user?(#user)
end
does that mean signed_in_user and correct_user is private? or just signed_in_user? Does that mean whenever I need to write private methods, it should be at the end of my file now?
Yes, each method after the private keyword will be private. If you want to change back to defining non-private methods, you can use a different keyword, like public or protected.
See Where to place private methods in Ruby?
Yes all the methods under private are private. Usually you will, indeed, find those methods at the bottom of your file.
But you can "stop" this by writing another keyword like protected and then all the methods following would be protected.
Or you can even define your access control in this way too, listing your methods as arguments to the access control functions (public, protected, private):
class SomeClass
def method1
...
end
def method2
...
end
def method3
...
end
# ... more methods def
public :method1, method4
protected :method3
private :method2
end
As others have written, Every method that follows the private keyword immediately is private in Ruby. This is plain Ruby syntax and has nothing to do with rails.
private
.....
def pvt_meth_1
.....
end
def pvt_meth_2
.....
end
public
def pub_meth_1
......
end
It works the same way as c++ private, public tags, so yes both of them will be private
If I have a class in Ruby:
class Person
def get_person
end
protected
def check_person_1
end
def check_person_2
end
private
def auth_person_1
end
def auth_person_2
end
end
is auth_person_2 a private function or public or protected function? I mean I do not have the "private" keyword above the function name, but it is under the auth_person_1 function which is however directly under "private", what function type auth_person_2 is in this case? and how about function check_person_2 ?
in this case auth_person1/2 would be private, check_person1/2 would be protected and get_person would be public.
Functions look for the last keyword and that's what they use.
You can also do it this way :
class Person
def method1
end
def method2
end
def method3
end
def method4
end
public :method1, :method4
protected :method2
private :method3
end
Doing something like this would also work :
class Person
def method
end
private
def method1
end
public
def method2
end
end
You can have them in any order and use the same keyword more than once.
Ruby is not like C# where you have to define the encapsulation for each method.
Everything bellow the method "private" is private until you call one of the other methods (private, public or protected). For example, you can use "public" to define a public method after a block of private ones.
By default your class is public.
class Hello
...
end
is the same as
class Hello
public
...
end