Ruby 3.0. Ruby and lesser known privacy control methods
Tomasz Szkaradek
Development Architect
One of the most beloved features of Ruby is its very flexible syntax. Personally, I love Ruby for how many possibilities we have in defining classes and their properties, and this is what I will discuss in this article.
Basic solutions
Let’s assume that we are using class Foo that has one public method and one private method:
class Foo
def bar
:awesome
end
private
def baz
:something_private
end
end
Everything is great, we see such a solution in virtually every project. Running Foo.new.baz will cause the error NoMethodError (private method ‘baz’ called for # ) and that’s what we meant to do. What happens if we try to change the save format and add private as a prefix in the class definition?
class Foo
def bar
:awesome
end
private def baz
:something_private
end
end
As you can see after running the code, it actually works! Why can we enter the visibility of the method before doing it? Because when defining a method, def returns the name of the method as a symbol. This expression is not only a part of the syntax, but de facto a method derived from the Module class and treating this symbol as an argument. For more information, please see the documentation in this link. Since it started so easy with private, let’s try to change the visibility of the private method.
class Foo
def bar
:awesome
end
private def baz
:something_private
end
public :baz
end
Success! Our method of bases became public because we made it visible twice. Of course, the same operation applies to the modules. Great, but where does it get us? This functionality gives us a lot because we can freely change the visibility of a method while defining it, or even change the visibility of methods when inheriting them.
Now, let’s take a look at what Ruby 2.7 can do in terms of changing the visibility of aliases and accessors.
class Foo
private attr_accessor :awesome_variable
end
Unfortunately, we get an error as the private method expects symbols and attr_accessor. The code returns nil and thus this method is not compatible with the private use in Ruby 2.7. So what are our options?
We can use attr_accessor under the private keyword to make it work, i.e. we will get an error when we want to refer to the awesome_variableawesome_variable method.
class Foo
private
attr_accessor :awesome_variable
end
The second option is to execute the private method on methods generated by attr_attribute; in this case, we also have to remember to enter the setter there.
class Foo
attr_accessor :awesome_variable
private :awesome_variable, :awesome_variable=
end
Problems with the attr_ * methods are not the only obstacles. We can encounter the same difficulty when we want to create a private alias.
class Foo
private alias :bar, :awesome_bar
end
Ruby 3.0 and our business
Fortunately, Ruby 3.0 introduces a great change as the visibility methods can take array as an argument and methods alias, attr_ *, can reset the array with the names of the methods that have been defined. You can read more here.
Now, let’s see some examples in the latest euba and check if the changes have actually been made and how we can use them. In the first example, let’s use private before attr accessor:
class Foo
private attr_accessor :awesome_variable
end
Such a call will not cause errors in parsing the syntax and, what is important, the awesome_variable andawesome_variable =methods become private. The alias method will do the same as now it also returns a symbol as the name of the new method and makes it visible.
class Foo
private alias :bar, :awesome_bar
end
An interesting fact is that we can also delve into further methods, e.g. the awesomeprint module can be called between private and attrreader; it is important that such a method returns an array with the names of the methods that are on the right side of the expression.
class Module
def awesome_print(names)
puts names
names
end
end
class Foo
private awesome_print attr_reader :awesome_bar
end
Summary
Hope you will find this article useful! In case of more news about Ruby 3.0. read more here.