include_once("common_lab_header.php");
Excerpt for Visibility of Methods in Ruby by , available in its entirety at Smashwords





Visibility of methods in ruby

(Alex's Adventures In Rubyland Series)

Published by Alexey Sologub


The latest version of the contents of this book can be found at https://www.alexindev.com/


All rights reserved.


This version of the book is completely free.


Any requests and comments you can send to alexindevservice@gmail.com. I will be glad to get acquainted with any of your thoughts about this book.



Thank you!



Table of Contents


  1. Introduction


  1. An introduction to public, private and protected methods in Ruby (access control)

  1. Ruby supports 3 levels of access to methods

  2. How do access control methods work?

  3. Why do we have these methods in ruby?

  4. public, private, protected - methods, not modifiers

  5. public, private and protected works only with methods



  1. Public methods

    1. What are public methods in Ruby?

    2. By default, all methods have public access level

    3. Exception 1. The initialize method is always private and doesn't require the private method call

    4. Exception 2. Methods that we define in the global Scope are private methods of the Object class

    5. Exception 3. Private methods that are defined in the Kernel module

    6. When to use the method public?

    7. Using the public method with explicit arguments

    8. Using the public method ("wrapper" syntax)

    9. All public methods can be called from outside the class

    1. Public methods are available for calling in all other methods

    1. Public methods are inherited by derived classes

    2. We can explicitly specify the receiver of the method

    3. Recommendations when to use public methods

    4. Style recommendations

    5. Conclusions



  1. Private methods

  1. What are private methods in Ruby?

  2. Using the private method

  3. Using the private method with explicit arguments

  4. Using the private method ("wrapper" syntax)

  5. Private methods can't be called outside the class

  6. Private methods can be called inside a class inside other methods

  7. Private methods can't be called using an explicit receiver

  8. Private methods are inherited by derived classes

  9. Recommendations when to use public methods

  10. Style recommendations

  11. Conclusions



  1. Protected methods

  1. What are protected methods in Ruby?

  2. Using the protected method

  3. Using the protected method with explicit arguments

  4. Using the protected method ("wrapper" syntax)

  5. Protected methods can't be called outside the class

  6. Protected methods can be called inside a class inside other methods

  7. Protected methods are inherited by derived classes

  8. Protected methods can be called using an explicit receiver

  9. Why do we need an explicit receiver?

  10. Example: Creation of comparison methods for instances of the same class

  11. Example: Definition of the equality comparison operator

  12. Style recommendations

  13. Conclusions



  1. Common notes

  1. Private and protected methods are accessible via the send method

  2. When is the send method usually used?

  3. Private and protected methods are not accessible through the public_send method

  4. The send method and whether there is the access level in ruby

  5. The visibility of the method can be redefined just like the method itself

  6. We can change the visibility of the method in derived classes too

  7. How to list private, public, protected methods of the class

  8. Dinosaurs and Excalibur (example of using public, private, and protected methods together

  9. Conclusions





Introduction

In this book, you will find information on how to use the visibility of methods in ruby. The book will be useful for beginner programmers and those who are familiar with the ruby for a long time.

The task of the book is to help to learn ruby more quickly. You can also use this book as a reference. I tried to divide this book on theses, which are accompanied by examples for a better understanding of the material.



An introduction to public, private and protected methods in Ruby (access control)

In this chapter, we'll look at the introductory theses on how we can control the visibility of methods in ruby classes.

Ruby supports 3 levels of access to methods

Ruby supports 3 levels of access to methods: public, protected, private .

An example of how they can be located in a class:

class SomeClass



def public_method



puts "Hi, I'm public method!"



end



private



def private_method



puts "Hi! I'm private method!"



end



protected



def protected_method



puts "Hi, I'm protected method!"



end



end

 

How do access control methods work?

Based on the names of methods you can understand that they are protecting and hiding something :). How does this affect the operation of the program? Let's try to use these methods.

Let's use SomeClass class s and try to call a couple of methods:

instance = SomeClass.new



instance.public_method



instance.private_method



instance.protected_method

result:

Hi, I'm public method!



private method `private_method' called for #<SomeClass:0x0055a1e7d114b8>



Did you mean? private_methods



(repl):24:in `<main>'

As we see, ruby didn't let us call private_method method. Thus,  ruby limits its use. This is the whole function of these delimiters. If access to the method is limited - the ruby will not allow it to be called, what motivates us to use other approaches to solve the problem.

Let's extend SomeClass class with other methods:

class SomeClass



def method_which_uses_private_method

private_method

end



def method_which_uses_protected_method

protected_method

end



def public_method

puts "Hi, I'm public method!"

end



private



def private_method

puts "Hi! I'm private method!"

end



protected



def protected_method

puts "Hi, I'm protected method!"

end



end

Let's try to use new methods:

instance = SomeClass.new



instance.public_method



instance.method_which_uses_private_method



instance.method_which_uses_protected_method

result:

Hi, I'm public method!



Hi! I'm private method!



Hi, I'm protected method!



=> nil

As you can see, because methods have a certain visibility, we have to create 2 additional methods and now we can use them.

Why do we have these methods in ruby?

Why do we have these methods in ruby? Would not it be better if we had no restrictions? Why should a programmer create them?

The answer to this question - OOP. When we work with code, our task is to write a solution that can be easily maintained and extended by other programmers. Through the separation of methods in the zones of visibility we can determine what function is performed by the appropriate methods in the classes.

When a programmer sees methods that have a different level of visibility, he seems to be wearing special glasses.

Public methods are seen by the programmer as methods that define a class interface - i.e. these are the methods through which the class is working from outside.

Private methods are considered as methods that define the internal logic of a class, these are the methods in which a programmer should not dig after you to understand how to use your class.

Protected methods have several cases of accepting and usually their usage is debatable, but usuallyif a private method can not solve the problem, then the protected method works with the internal logic. We'll talk about all these types of methods in more detail in the next articles. The main thing, for now, is to understand that the method can have a role in the class and, depending on it, we use one or another access level. 

public, private, protected - methods, not modifiers

Another thing you should understand from the start - levels of access in ruby are provided by methods. public, private, and protected defined inside class called Module as instance methods  (see here: https://ruby-doc.org/core-2.3.1/Module.html). It's all about methods, not modifiers. And if it's methods - then with them you can do practically the same thing as with other methods.

public, private and protected works only with methods

You can't use them for constants or other kinds of ruby variables.

For example, we'll try to make the local variable private:

class ExampleClass



local_variable_one = "something"



private :local_variable_one



end

result: 

NameError: undefined method `local_variable_one' for class `ExampleClass'



Did you mean? local_variables



from (irb):5:in `private'



from (irb):5:in `<class:ExampleClass>'



from (irb):1



from /usr/bin/irb:11:in `<main>'

As you can see, all we've got is the error.



Public methods in ruby

In this chapter, we'll look at the first part of the specifics of how public methods work and how they are used in ruby classes.

What are public methods in Ruby?

Public methods are methods that allow objects to interact with each other. The set of public methods of the object creates its interface. The more expressively defined such methods the easier it's to use the object. Public methods haven't restrictions when we using them (they can be used both inside the class and outside the class).

Now let's look at public methods in more detail.

By default, all methods have public access level.

In order for a method to have the public access level, you do not need to use special methods. By default, all methods are public.

Example:

class SimpleClass



def method_one

puts "Hi from method_one"

end



def method_two

puts "Hi from method_two"

end



def method_three

puts "Hi from method_three"

end



end



new_instance = SimpleClass.new

new_instance.method_one

new_instance.method_two

new_instance.method_three

Description:

  • We defined the class SimpleClass;

  • The class defines 3 instance methods;

  • Then, we run each of the methods on the new instance of the class.

result:

Hi from method_one

Hi from method_two

Hi from method_three

If any of these methods had a different level of visibility, then we would see an error.

Exception 1. The initialize method is always private and doesn't require the private method call.

The second exception is that the initialize method in any class is private and it doesn't require any syntactic constructs for this. Is it defined implicitly or explicitly, doesn't matter.

For instance, we will define a class and try to call the initialize method on an instance of this class:

class SomeClass

end



SomeClass.new.initialize

result:

private method `initialize' called for #<SomeClass:0x0055858f19f158>

(repl):5:in `<main>'

What if I try to make it a public method? We can explicitly define it:

class SomeClass



public



def initialize

puts "Hello! Now I'm public! Yey!"

end



end



new_instance = SomeClass.new

new_instance.initialize

result:

Hello! Now I'm public! Yey!

private method `initialize' called for #<SomeClass:0x00562faf42c1f8>

(repl):12:in `<main>'

What happened? The first time the method was called because we used the method new which creates a new instance of the class and runs the initialize method for this.

But when we tried to call the method a second time (new_instance.initialize), we got an error that says that the private method was called from outside the class.

Therefore, you can safely say that you can not override the visibility of the initialize method.

Exception 2. Methods that we define in the global Scope are private methods of the Object class.

There are several exceptions to public methods. The first exception is that the methods defined in the global scope are not public methods. They are defined as private methods in the Object class, after which they are available everywhere (since all new classes inherit from it).

For instance, we will use irb:

def some_method

puts "Hi! I'm private method stored inside Object class"

end



some_method

result:

Hi! I'm private method stored inside Object class

=> nil

Let's see if the method is added to the Object class. To do this, we add the public method method_with_some_method_inside to the Object class (because outside of the class  we can't use private methods directly):

def some_method

puts "Hi! I'm private method stored inside Object class"

end



class Object



def method_with_some_method_inside

puts "Hello from method_with_some_method_inside!"

some_method

end



end



Object.new.method_with_some_method_inside

result:

Hello from method_with_some_method_inside!

Hi! I'm private method stored inside Object class

=> nil

And if we will try to call some_method using an explicit receiver, we get an error:

Object.new.some_method

result:

private method `some_method' called for #<Object:0x0055fb9d2a2d00>

(repl):14:in `<main>'

As we can see, the method some_method was added to the Object class and it's definitely private method.

 private_methods.include?(:method_name)

Another way we can check this is to use the private_methods method which will return an array of private methods and then use the include?(method_name) method to check if the name of this method is in the array.

puts Object.private_methods.include?(:some_method)



def some_method

puts "Hi! I'm private method stored inside Object class"

end



puts Object.private_methods.include?(:some_method)

result: 

false

true

=> nil

Good. But why if this is the private method, we can call it directly in the console? We can call private methods only within the objects of the class where these methods are defined and in their subclasses, right? That's right, and this is a slightly in-depth question about how ruby works with context.

When we work in ruby without defining any modules or classes, for example like this:

puts "1111"

["aaa", "bbb", "ccc"]

Ruby by default creates an instance of the Object class in which this code serves.

This can be easily verified, let's enlarge one of our previous examples:

puts "self: #{self}"

puts "self class: #{self.class}"


puts "Is the method 'some_method' included in the class Object?"

puts Object.private_methods.include?(:some_method)


def some_method

puts "Hi! I'm private method stored inside Object class"

end


puts "after definition of method 'some_method'?"

puts Object.private_methods.include?(:some_method)


some_method

result:

self: main

self class: Object

Is the method 'some_method' included in the class Object?

false

after definition of method 'some_method'?

true

Hi! I'm private method stored inside Object class

=> nil

Thus, when we don't explicitly define the class in which we define methods, ruby does it for us. The same thing happens with another code if you try :).

Exception 3. Private methods that are defined in the Kernel module.

This feature is rarely mentioned, but the Kernel module has many interesting methods that we use throughout the program and they are all private.  

Let's output them using the private_instance_methods method with false flag.

Kernel.private_instance_methods(false)

=> [:sprintf, :format, :Integer, :Float, :String, :Array, :Hash, :catch, :throw, :loop,

:block_given?, :trace_var, :untrace_var, :Rational, :at_exit, :JSON, :j, :Complex,

:set_trace_func, :select, :caller, :caller_locations, :test, :`, :fork, :exit, :sleep,

:jj, :respond_to_missing?, :load, :gem_original_require, :syscall, :printf, :open, :putc,

:print, :readline, :puts, :p, :exit!, :system, :readlines, :gets, :proc, :lambda, :exec,

:rand, :srand, :initialize_copy, :initialize_clone, :initialize_dup, :spawn, :abort,

:trap, :gem, :require, :require_relative, :autoload, :autoload?, :binding,

:local_variables, :warn, :raise, :fail, :global_variables, :__method__, :__callee__,

:__dir__, :eval, :iterator?]

As you can see, there are such popular methods as print, puts, rand, raise, etc.

Why is this happening? 

It's connected with the work of the Kernel module that is included by class Object from which all objects are inherited in ruby, hence these methods are available to all objects in ruby. And since these methods are private, we can not call them on an explicit receiver. As we see, this exception has much in common with the previous one. '

Let's practice a little and add some method to the Kernel module and then try to call it:

module Kernel



private



def double_puts(something)

puts something

puts something

end



end

Now let's run the double_puts method without initializing an instance of the class and pass "OneOne" string as an argument:

double_puts("OneOne")

result:

OneOne

OneOne

=> nil

As you can see, we don't need a special method to call double_puts method.

When to use the method public?

In normal situations, this method will be helpful only for readability. Keep in mind that most style guides don't recommend using this method, it is assumed that everyone knows that methods have public access level by default. If you worked with a language with strict requirements for the presence of a pointer - change your habit. You can be forgiven for using this method, but be aware that this will not last forever :).

Let's use one of the previous examples and add the call of the public method inside of the class before all methods:

class SimpleClass



public



def method_one

puts "Hi from method_one"

end



def method_two

puts "Hi from method_two"

end



def method_three

puts "Hi from method_three"

end



end



new_instance = SimpleClass.new

new_instance.method_one

new_instance.method_two

new_instance.method_three

result:

Hi from method_one

Hi from method_two

Hi from method_three

=> nil

As we see, nothing has changed. Therefore, the public method is not usually used, why spend time on it?

Using the public method with explicit arguments.

Another way to define public methods is to use the public method, with explicitly passed method names to it as arguments.

For instance:

class SomeClass



def some_method_one

puts "111"

end



def some_method_two

puts "222"

end



public :some_method_one, :some_method_two



end



new_instance = SomeClass.new

new_instance.some_method_one

new_instance.some_method_two

result:

111

222

=> nil

It's difficult to say when such syntax is needed, but since it exists - welcome! :)

Using the public method ("wrapper" syntax).

Another option to determine the visibility of the method:

class ExampleClass



public def test_method_one

puts "Hi from test_method_one!"

end

public def test_metohd_two

puts "Hi from test_method_two!"

end



end

This approach has an important advantage. If you have a lot of methods in the class and you see a lot of public, private and protected methods, it will inspire uncertainty when placing the method in a file: "What access level will the method have if I add it here? Do I need to run through the entire file and analyze this?".  And in this case, you see how the access level is immediately specified: "If there is something there it will be rewritten, I can move on."

All public methods can be called from outside the class.

It's worthwhile to pay attention to this. When public methods are defined in the class, we can create an instance of the class and call this method outside the class (for comparison, you can't do this with private methods because you will see the error).

For instance:

class SimpleClass



def method_one

puts "Hi from method_one"

end



end



new_instance = SimpleClass.new

new_instance.method_one

result:

Hi from method_one

=> nil

As you can see, the method was called and returned us the message.

Public methods are available for calling in all other methods.

In addition to the fact that they can be called from outside the class, we can also call them inside the class in other methods (inside private, protected, and other public methods). 

For instance, we will create a class with 3 public methods and make the methods call each other. Typically, if a method is used inside a public method, it implements the internal logic of the object, but sometimes it doesn't (like in our example).

Example:

class Creature



def chaotic_movements

rand(1..4).times { move_left }

rand(1..4).times { move_right }

rand(1..4).times { move_back }

rand(1..4).times { move_forward }

end



def move_left

puts "Left!"

end



def move_right

puts "Right!"

end



def move_back

puts "Back!"

end



def move_forward

puts "Back!"

end



end



new_creature = Creature.new

new_creature.move_left

new_creature.move_back

new_creature.move_forward

new_creature.move_right

new_creature.chaotic_movements



Description of the code:

  • We have class Creature;

  • The class defines 5 public methods;

  • In the chaotic_movements method, we use the rand method whose task is to output the arbitrary number so that we can use the times method to call the code which is in the block { } a certain number of times.

result:

Left!

Back!

Forward!

Right!

Left!

Left!

Right!

Right!

Right!

Back!

Forward!

Forward!

Forward!

Forward!

=> 4

You can play a little with this example, each time at the end of the script there will be different actions.

Let's add to our example 2 methods, one private and one protected:

class Creature



def chaotic_movements

rand(1..4).times { move_left }

rand(1..4).times { move_right }

rand(1..4).times { move_back }

rand(1..4).times { move_forward }

end



def move_left

puts "Left!"

end



def move_right

puts "Right!"

end



def move_back

puts "Back!"

end



def move_forward

puts "Forward!"

end



def slip_away

jump

hide

end



private



def jump

move_left

move_right

puts "Jump! Jump! Jump!"

end



protected



def hide

move_left

move_back

puts "Hide!"

end



end



new_creature = Creature.new

new_creature.slip_away

result:

Left!

Right!

Jump! Jump! Jump!

Left!

Back!

Hide!

=> nil

As you can see, public methods have been successfully called.

Public methods are inherited by derived classes.

For instance:

class GuessWhoOne



def say_something

puts "Meow!"

end



end



class GuessWhoTwo < GuessWhoOne

end



class GuessWhoThree < GuessWhoTwo

end





new_instance = GuessWhoOne.new

new_instance.say_something



second_new_instance = GuessWhoTwo.new

second_new_instance.say_something



third_new_instance = GuessWhoThree.new

third_new_instance.say_something

Description:

  • We defined several classes, in one of the classes the public method say_set is defined.

  • Classes are inherited from each other.

  • Then we call the say_something method on the instances of the corresponding classes.

result:

Meow!

Meow!

Meow!

=> nil

As you can see, we defined the method once and it is available in all child classes.

We can explicitly specify the receiver of the method.

For this example, we'll look at a fairly large class, but after that, you will see a description :).

Example:

class Flag



attr_accessor :number, :position



def initialize(number, position)

@number = number

@position = position

puts "Initial position of flag number #{number}: #{position}"

end


def swap_with(other_flag)

first_flag_position = self.position

second_flag_position = other_flag.position


self.position = second_flag_position

other_flag.position = first_flag_position


puts "Positions of flags number #{self.number} and #{other_flag.number} changed successfully"

end


def self.current_position_of_flags(*flags)

flags.each do |flag|

puts "Position of flag nubmer #{flag.number}: #{flag.position}"

end

end


end


new_flag_one = Flag.new(1, "first")

new_flag_one.position = 1


new_flag_two = Flag.new(2, "second")

new_flag_two.position = 2


# swap them

new_flag_one.swap_with(new_flag_two)

Flag.current_position_of_flags(new_flag_one, new_flag_two)


# swap them back

new_flag_one.swap_with(new_flag_two)

Flag.current_position_of_flags(new_flag_one, new_flag_two)

 

Code description:

We defined class Flag. Main purpose of this class is to control the logic of the flags (suppose that flags are on some sporting event).
The class defines getters and setters for the number and position properties using the attr_accessor method;

initialize method:

  • The initialize method is defined (when we call Flag.new we can pass 2 arguments, number and position, which will display the state of the object);

  • The last line in the initialize method uses the puts method to print a string to the console;

  • Inside the string we use interpolation # {...}  whose task is to display the contents of the variables number and position;

swap_with method:

  • The swap_with method has 1 parameter - other_flag;

  • The purpose of the method is to swap flags positions;

  • Inside the method, the first thing we do is assign the current positions of the two flags to the corresponding local variables, then we redefine the `position` property of the objects themselves;

  • The first object is assigned the position of the second, the second is assigned the position of the first, then we display the message which indicate that the exchange was successful;

  • Inside the method, I added empty lines to make it easier to understand: the first part - assign variables, the second part - change places, the third part - output the line to the console.

current_position_of_flags method:

  • In contrast to other methods, this method is the class method;

  • The purpose of this method is to output messages about where each flag is located (the method is added to the example for better understanding of what is happening);

  • Why class method? It serves a group of instances of the class;

  • In the parameters was specified splat operator which indicates that the method can accept an undefined number of arguments.
    Inside the method, the iterator is used to run through each object and display the message about the number of the flag and its position.

 

result:

Initial position of flag number 1: first

Initial position of flag number 2: second

Positions of flags number 1 and 2 changed successfully

Position of flag nubmer 1: 2

Position of flag nubmer 2: 1

Positions of flags number 1 and 2 changed successfully

Position of flag nubmer 1: 1

Position of flag nubmer 2: 2

=> [#<Flag:0x005631960e58d0 @number=1, @position=1>, #<Flag:0x005631960e5380 @number=2, @position=2>]

As you can see, the code works :).

What does this example illustrate? There are many situations when we use public methods with an explicit receiver. The most common methods that we use in this way are getters and setters. We actively used them inside the swap_with method and if they had private visibility we wouldn't be able to use them that way.

Recommendations when to use public methods

Public methods are used to represent the class interface. The general recommendation is that the less unnecessary methods there are, the better. The more readable the public methods will look, the easier it is to use this class and understand what kind of entity we have. Having completed the work on the class, imagine that you see it the first time, how many possible mistakes will you make when you use it? Will this class be understandable for a beginner programmer?

Style recommendations

Because public methods play an important role - they define the interface of a class, then it is logical to assume that these are the methods that the programmer must see first. I suppose that's why most style guides recommend placing public methods at the beginning of the class (and not only in the ruby). Without understanding the interface of the class, you rarely want to look for something in private and protected methods.

For instance, we have a class:

class TaskManager



def do_tasks(a,b,c)

sub_process_one(a)

sub_process_two(b)

sub_process_three(c)

end



private

# some private methods



end

Until I have questions about how the do_tasks method works, I most likely will not look for private methods. And for usage, it should be enough to know the name of the method and the list of its parameters.

Conclusions

public methods:
- created specifically for creating a class interface;
- by default, all methods are public, but there are a few exceptions;
- you can use the public method to declare, but only if you really need it;
- public methods can be called from outside the class as well as inside the class;
- you can explicitly specify receiver of the method;
- inherited to the class derivatives through the inheritance, they don't lose their 'abilities' in the derived classes;
- the better you define public methods, the more number of programmers will be happy to use your classes to solve their problems;
- usually placed first in the class, before private and protected methods.







Private methods in ruby

In this chapter, we'll look at what private methods are and how they are used in ruby classes.

What are private methods in Ruby?

Methods that have private visibility implement the internal logic of the object. They can be called inside the same class in which they are defined, or inside derived classes. Unlike protected methods, you can call them only for a current instance of the class (you can't explicitly specify the method receiver). They are mainly used to conceal the implementation of classes, which positively affects the support of such classes.

Now let's look at private methods in more detail.

Using the private method.

We can declare private methods using the private method without using explicit arguments:

For instance:

class ExampleClass



def print_something

something

end



private



def something

puts "Hi from private method! "

end



end



new_instance = ExampleClass.new

new_instance.print_something

produces:

Hi from private method!

=> nil

 

Using the private method with explicit arguments

Another variant of the syntax of private methods is when we can explicitly pass method names as arguments.

Example:

class ExampleClass



def print_something

something

some_method

end



def something

puts "Hi from private method! "

end



def some_method

puts "Yes, I'm private too!"

end



private :something, :some_method



end



new_instance = ExampleClass.new

new_instance.print_something

produces:

Hi from private method!

Yes, I'm private too!

=> nil

If we try to run these methods outside the class, we will see errors:

new_instance.something

produces:

NoMethodError: private method `something' called for #<ExampleClass:0x00000002715320>

from (irb):85

from /usr/bin/irb:11:in `<main>'

and

new_instance.some_method

produces:

NoMethodError: private method `some_method' called for #<ExampleClass:0x00000002639ff0>

from (irb):112

from /usr/bin/irb:11:in `<main>'

Using the private method ("wrapper" syntax)

An example of how we can "wrap" a method:

class SomeClass



def method_one

method_two

end



private def method_two

puts "Hello from private method!"

end



end



new_instnace = SomeClass.new

new_instnace.method_one

produces:

Hello from private method!

=> nil

 

Private methods can't be called outside the class

This feature separates them from public methods, if we want to call a private method from outside the class, we'll see an error.

Let's create a class with a private method and try to call it from outside the class:

class Planet



private



def weight

"some value"

end



end



new_instance = Planet.new

new_instance.weight

produces:

NoMethodError: private method `weight' called for #<Planet:0x000000021e24c0>

from (irb):30

from /usr/bin/irb:11:in `<main>'

 

Private methods can be called inside a class inside other methods

Example:

class SomeClass



def method_one

method_two

end



private



def method_two

puts "I'm private method!"

end



end



new_instance = SomeClass.new

new_instance.method_one

produces:

I'm private method!

=> nil

Usually, private methods are created just for this use. We have public methods in which a certain work needs to be done and we create one (or a lot) of private methods that this work does.

Private methods can also use each other:

class SomeClass



def method_one

method_two

end



private



def method_two

puts "I'm private method!"

method_three

end



def method_three

puts "Hello from method_three!"

end



end



new_instance = SomeClass.new

new_instance.method_one

produces:

I'm private method!

Hello from method_three!

=> nil

Private methods can't be called using an explicit receiver

For instance, let's add self to our example and check:

class Article



def data

self.amout_of_pages

end



private



def amout_of_pages

puts "Some amount of pages in some #{self.class}"

end


end


class Book < Article


end

First, let's try to call the public method called data using an instance of the Article class:

new_instance = Article.new

new_instance.data

produces:

NoMethodError: private method `amout_of_pages' called for #<Article:0x00000002053000>

from (irb):34:in `data'

from (irb):50

from /usr/bin/irb:11:in `<main>'

Can something be changed if we use an instance of the Book class :) ?

new_instance = Book.new

new_instance.data

produces:

NoMethodError: private method `amout_of_pages' called for #<Book:0x000000020443e8>

from (irb):34:in `data'

from (irb):53

from /usr/bin/irb:11:in `<main>'

As you can see, nothing has changed. We have the same error in two cases that tell us that we can't use the private method this way.

Private methods are inherited by derived classes

In many programming languages, private methods are not inherited, but not in ruby

Example:

class Article



def data

amout_of_pages

end



private



def amout_of_pages

puts "Some amount of pages in some #{self.class}"

end


end


class Book < Article


end


new_instance = Article.new

new_instance.data


new_instance = Book.new

new_instance.data

produces:

Some amount of pages in some Article

Some amount of pages in some Book

=> nil



Description:

  1. We defined 2 classes: Article and Book;

  2. The Book class is inherited from the Article class;

  3. The class Article defines the method with private access amout_of_pages which uses #{self.class} to output as string a name of the class of the object on which the method is invoked;

  4. Also in the class I defined the data method that has a public access level that internally calls the amout_of_pages method.

What happened? Both methods were inherited, so we could call the data method which uses the private method amout_of_pages.

Recommendations when to use private methods

Private methods are used when we want to hide part of the implementation of a class.

For instance, we have a class with a set of public methods that we wrote. We know every method and its role. For us, the code looks readable and understandable.

Example:

class Car



def start

start_system_one

start_system_two

start_system_three

start_system_four

puts "Piiip! Car is ready to go sir, be careful on the road."

end



def stop

stop_system_one

stop_system_two

stop_system_three

stop_system_four

puts "Piiip! All functions are stopped. Hope you enjoyed the road"

end



def start_system_one

puts "System one started"

end



def start_system_two

puts "System two started"

end



def start_system_three

puts "System three started"

end



def start_system_four

puts "System four started"

end



def stop_system_one

puts "System one stopped"

end



def stop_system_two

puts "System two stopped"

end



def stop_system_three

puts "System three stopped"

end



def stop_system_four

puts "System four stopped"

end



end





new_instance = Car.new

new_instance.start

new_instance.stop

produces:

System one started

System two started

System three started

System four started

Piiip! Car is ready to go sir, be careful on the road.

System one stopped

System two stopped

System three stopped

System four stopped

Piiip! All functions are stopped. Hope you enjoyed the road

=> nil

As far as we can see, the situation is quite simple. We have 2 methods: start and stop (which contain many calls to other methods). Based on the methods name, we understand that these methods are responsible for starting and stopping a machine. Typically this is how the class sees the programmer who made it. Let's add a bit of reality to our example. We will mix the methods in places and add many other functions that will do something.

Changed example:

class Car



def start_system_three

run_sub_system_one

run_sub_system_two

run_sub_system_three

puts "System three started"

end



def stop

stop_system_one

stop_system_two

stop_system_three

stop_system_four

puts "Piiip! All functions are stopped. Hope you enjoyed the road"

end



def start_system_one

run_sub_system_six

run_sub_system_seven

run_sub_system_eight

stop_sub_system_two

puts "System one started"

end



def start_system_four

run_sub_system_three

stop_sub_system_one

puts "System four started"

end





def start_system_two

run_sub_system_four

stop_sub_system_one

puts "System two started"

end



def stop_system_three

run_sub_system_one

stop_sub_system_five

stop_sub_system_four

puts "System three stopped"

end



def start

start_system_one

start_system_two

start_system_three

start_system_four

puts "Piiip! Car is ready to go sir, be careful on the road."

end



def stop_system_one

stop_sub_system_five

stop_sub_system_two

puts "System one stopped"

end



def stop_system_two

stop_sub_system_three

stop_sub_system_four

puts "System two stopped"

end



def stop_system_four

stop_sub_system_six

stop_sub_system_seven

puts "System four stopped"

end



end

What was changed:
The place of methods in the class has been changed, and the methods run_sub_system and stop_sub_system have been added to cause a little panic for a reader :) Suppose that these methods are defined somewhere and do something with the subsystems of a car.

Now, let's imagine what kind of ideas a programmer will have who sees this class for the first time: "What does start mean?", "What does stop mean?", "What systems?", "In what order should they be run?". The situation turns into a try to manually start each machine system to start a drive. It will be nice to see that someone has already used this class. But you will not be looking at the other driver who starts the ride with a push every time if you can sit down and go at once? Moreover, suppose that some of the methods are named incorrectly and do not lead to any thoughts, the confusion will be even greater. Now, that we are decided not allow such situation to arise again, we will rewrite our example using the private method:

Our new example:

class Car



def start

start_system_one

start_system_two

start_system_three

start_system_four

puts "Piiip! Car is ready to go sir, be careful on the road."

end



def stop

stop_system_one

stop_system_two

stop_system_three

stop_system_four

puts "Piiip! All functions are stopped. Hope you enjoyed the road"

end



private



def start_system_three

run_sub_system_one

run_sub_system_two

run_sub_system_three

puts "System three started"

end



def start_system_one

run_sub_system_six

run_sub_system_seven

run_sub_system_eight

stop_sub_system_two

puts "System one started"

end



def start_system_four

run_sub_system_three

stop_sub_system_one

puts "System four started"

end





def start_system_two

run_sub_system_four

stop_sub_system_one

puts "System two started"

end



def stop_system_three

run_sub_system_one

stop_sub_system_five

stop_sub_system_four

puts "System three stopped"

end



def stop_system_one

stop_sub_system_five

stop_sub_system_two

puts "System one stopped"

end



def stop_system_two

stop_sub_system_three

stop_sub_system_four

puts "System two stopped"

end



def stop_system_four

stop_sub_system_six

stop_sub_system_seven

puts "System four stopped"

end



end

We made a very small change (plus changed the methods in some places in a more logical order), but as we can see, from the outside of the class we can call only 2 methods: start and stop. And the chance that we will try to cause other methods for some reason is reduced. Choose from two methods is much easier than out of ten. Dividing methods by the level of access, we say: "All you need to know to use this class is 2 methods - start and stop".

In addition to problems with the use of classes, there is a common problem with the extension of classes. Imagine that we have many classes and all of them suddenly began to expand. If we regularly modify public methods, then we will have to search for them by the application (in the places where they are called) and replace them with other methods. But if we created a good interface from public methods and everything that can be extended we put in private methods, in that case, we will need to make changes only in the private methods of the corresponding classes and it saves the energy and attention of the programmer.

Hence, implementation is the important thing in classes. As soon as some of the methods come out from our field of vision, they cease to mislead us. All methods that are not used externally from the class, methods that perform work for public methods are best hidden in the private methods.

Style recommendations

Typically, private methods are placed at the end of the file after public methods. Because other developers will primarily be interested in public methods and therefore there is no need of private methods at the beginning of the file.

Example:

class ExampleClass

def public_method_one

end

def public_method_two

end

private

def private_method_one

end

def private_method_two

end

end

 

Conclusions

private methods:
- created specifically for storing the implementation of class logic;
- you can declare private methods using method 'private';
- inherited to the class derivatives through the inheritance;
- you can not explicitly specify the recipient of the private method;
- can not be called outside the class directly, only through other methods;
- it is recommended to place after public methods in the class;









Protected methods in ruby

In this chapter, we'll look at what protection methods are and how they are used in ruby classes.

What are protected methods in Ruby?

Protect methods are another kind of methods that exist in ruby. In many languages, this level of access doesn't exist. They are similar to private methods: they can't be called from outside the class but can be called inside the class. The main difference between protected and private methods is that we can specify an explicit receiver for protected methods, which makes it possible for objects of the same type to interact with each other - that is why they are usually used.

Now let's look at protected methods in more detail.

Using the protected method

The easiest way to define protected methods is to place the protected method before all methods that we want to do protected.

For instance:

class SimpleClass



def use_three_methods

method_one

method_two

method_three

end



protected



def method_one

puts "Hello from method_one"

end



def method_two

puts "Hello from method_two"

end



def method_three

puts "Hello from method_three"

end



end



new_instance = SimpleClass.new

new_instance.use_three_methods

produces:

Hello from method_one

Hello from method_two

Hello from method_three

=> nil

Using the protected method with explicit arguments

We can also use another option for declaring:

class Example



def some_metohd_one

some_method_two

end



def some_method_two

puts "Hi from some_method_two"

end



protected :some_method_two



end





new_instance = Example.new

new_instance.some_metohd_one

produces:

Hi from some_method_two

=> nil

Using the protected method ("wrapper" syntax)

Example:

class Example



def some_metohd_one

some_method_two

end



protected def some_method_two

puts "Hi from some_method_two"

end



end



new_instance = Example.new

new_instance.some_metohd_one

produces:

Hi from some_method_two

=> nil

Protected methods can't be called outside the class

For instance, let's try to call a protected method directly:

class Example



def some_metohd_one

some_method_two

end



def some_method_two

puts "Hi from some_method_two"

end



protected :some_method_two



end





new_instance = Example.new

new_instance.some_method_two

produces:

NoMethodError: protected method `some_method_two' called for #<Example:0x00000000c414b8>

Did you mean? some_metohd_one

from (irb):17

from /usr/bin/irb:11:in `<main>'

As you can see, the error tells us that the method has protected access level, so ruby can't call it.

Protected methods can be called inside a class inside other methods

The protected methods are similar to the private and public methods so that they can be called inside the methods of the class in which they are defined.

For instance:

class SomeClass



def some_public_method

some_method

end



protected



def some_method

puts "message from proteted method "

end



end



new_instance = SomeClass.new

new_instance.some_public_method

produces:

message from proteted method

=> nil

Protected methods are inherited by derived classes

For instance:

class FirstClass



protected



def some_protected_method

puts "Message from protected method!"

end



end



class SecondClass < FirstClass



def some_method

some_protected_method

end



end



new_instance = SecondClass.new

new_instance.some_method

produces:

Message from protected method!

=> nil

Protected methods can be called using an explicit receiver

Example:

class FirstClass



protected



def some_protected_method

puts "Message from protected method!"

end



end



class SecondClass < FirstClass



def some_method

self.some_protected_method

end



end



new_instance = SecondClass.new

new_instance.some_method

produces:

Message from protected method!

=> nil

Here you should notice that the protected method called on the self object which is explicitly specified. It is this small difference that essentially distinguishes private methods and protected methods.

Why do we need an explicit receiver?

Some tasks that can't be solved using private methods can be solved using protected methods.

For example, let's create a class in which different instances of the class can exchange money (well, almost exchange :)):

class Bank



def initialize(amount_of_money)

@money = amount_of_money

end



def current_balance

"Bank has #{self.money} amount of money."

end


def grab(another_bank, amount)

if another_bank.money > amount

self.money = self.money + amount

another_bank.money = another_bank.money - amount

else

puts "They don't have such amount of money"

end

end


private


#getter

def money

@money

end


#setter

def money(money)

@money = money

end


end


bank_one = Bank.new(1000)

bank_two = Bank.new(1500)

bank_one.grab(bank_two, 1400)

puts "First bank balance: #{bank_one.current_balance}"

puts "Second bank balance #{bank_two.current_balance}"



Description of the code:

  • The Bank class is defined;

  • Inside the class, we defined an initialize method that has the amount_of_money parameter (that is, we can specify the amount of money the bank will have when creating a new instance of the class);

  • The class also defines the current_balance method that is needed to better illustrate the example (used at the end of the example). Note that it doesn't have the puts method, it returns the string that uses interpolation;



method 'grab':

  • The method that is most interesting to us is the grab method. It has two parameters: another_bank and amount;

  • The logic of the method - if a bank from which we want to take money doesn't have a certain amount, we will see output about it, the bank will take a certain amount of money and add it to another bank;

  • Since the method does not define any logic that involves negotiations it's called 'grab' :).

As private methods, we specified the getter and setter methods for the money property. Typically, these methods have the public access level, but for our example, they need to be private.

Let's run the code and see what happens:

NoMethodError: private method `money' called for #<Bank:0x00000002275ce8 @m1000>

from (irb):68:in `take_money'

from (irb):83

from /usr/bin/irb:11:in `<main>'

In this example, we used the private method inside the public method. And everything would work - but we used the explicit receiver 'another_bank' in the line 'if another_bank.money > amount'.

Now let's rewrite our example and use protected methods instead of private:

class Bank



def initialize(amount_of_money)

# same code

end



def current_balance

# same code

end



def grab(another_bank, amount)

# same code

end



protected



def money

@money

end



def m(money)

@money = money

end



end



bank_one = Bank.new(1000)

bank_two = Bank.new(1500)

bank_one.grab(bank_two, 1400)

puts "First bank balance: #{bank_one.current_balance}"

puts "Second bank balance: #{bank_two.current_balance}"

produces:

First bank balance: Bank has 2400 amount of money.

Second bank balance: Bank has 100 amount of money.

=> nil

As we see, a very small difference can significantly influence the decision. Thus, the protected level of visibility should be used for methods whose logic is associated with manipulating the states of objects of the same type. Let's look at a few more examples of using the protected methods.

Example: Creation of comparison methods for instances of the same class

Typically, protected methods are used to create comparison methods between several objects of the same class.

For instace:

class Thing



attr_accessor :size



def bigger_then(thing)

puts self.compare_with(thing) ? "Yes, you right buddy!" : "No, you wrong."

end



protected



def compare_with(other_thing)

self.size > other_thing.size

end



end

Description of the code:

  • The Thing class is defined;

  • Using the attr_accessor method for class instances, getter and setter methods for the 'size' property are defined;

  • The bigger_then method is defined which has 'thing' parameter.

  • The logic of the method: it takes an object and then makes a comparison between the instance of the class on which the method was called and the instance of the class that was passed as the argument.

  • In the same line, we use the ternary operator. If the expression will return true, then we will see "Yes, you right buddy!" otherwise "No, you wrong.".

Note the use of the puts method, it works with the result of what will return the ternary operator.

Let's create several instances and compare them:

first_thing = Thing.new

first_thing.size = 4

puts "First thing size: #{first_thing.size}"


second_thing = Thing.new

second_thing.size = 5

puts "Second thing size: #{second_thing.size}"


first_thing.bigger_then(second_thing)

produces:

First thing size: 4

Second thing size: 5

No, you wrong.

=> nil

What happens if I will swap instances?

second_thing.bigger_then(first_thing)

produces:

First thing size: 4

Second thing size: 5

"Yes, you right buddy!"

=> nil

As we see our class successfully did the task.

Example: Definition of the equality comparison operator

Another very common example of using protected methods is to use it with comparison operators. Let's look at the example where we a little extend the behavior of the standard equality comparison operator.

Example:

class Hero



attr_reader :amount_of_wins



def initialize(wins)

@amount_of_wins = wins

end



def ==(another_hero)

puts self.lvl == another_hero.lvl ? "Hmm... Looks like you right, it's true!" : "Hmmmmm... how you can be soooo wrong?"

end



protected



def lvl

if self.amount_of_wins < 10

0

elsif self.amount_of_wins < 20

1

elsif self.amount_of_wins < 1000

2

end

end



end

Now let's try to compare a few instances of the class:

first_hero = Hero.new(15)

second_hero = Hero.new(25)

first_hero == second_hero

produces:

Hmmmmm... how you can be soooo wrong?

=> nil

Let's try to use another values for instances of the class:

first_hero = Hero.new(8)

second_hero = Hero.new(9)

first_hero == second_hero

produces:

Hmm... Looks like you right, it's true!

=> nil

Style recommendations

Usually, protected methods are located after the public and private methods of the class.

Example:

class ExampleClass



def method_one

end



private



def method_two

end



protected



def method_three

end



end

Or if we don't have any private methods:

class ExampleClass



def method one

end



protected



def method_two

end



end

Conclusions

protected methods:
- defined using the protected method;
- inherited to the class derivatives through the inheritance;
- can not be called outside the class directly, only through other methods;
- distinctive feature - we can specify the receiver of the method;
- before using ask yourself: "Will this problem be solved by a private method?"
- it is recommended to place after public and private methods in the class.







Common notes about public, private and protected methods in ruby

In this chapter I decided to post everything that in my opinion did not fit with other things from other chapters.

Private and protected methods are accessible via the send method

Protected and private methods can be called from anywhere by using the send method. This method ignores the access level. This is why it is usually forbidden for use in projects.

The syntax for using the send method is:

object.send(:method_name)

or

object.send("method_name")

For instance, let's define several methods and use send to call them:

class SomeClass



def public_method

puts "Hello from public method!"

end



private



def private_method

puts "Hello from private method!"

end



protected



def protected_method

puts "Hello from protected method!"

end



end





new_instance = SomeClass.new

new_instance.send(:private_method)

new_instance.send(:protected_method)

produces:

Hello from private method!

Hello from protected method!

=> nil

As we can see, the send method overcame the visibility of the methods and called them. 

For comparison, if you call these methods without using the send method, then we will see the corresponding errors:

new_instance = SomeClass.new

new_instance.private_method

produces:

NoMethodError: private method `private_method' called for #<SomeClass:0x0000000170b448>

Did you mean? private_methods

from (irb):22

from /usr/bin/irb:11:in `<main

and

new_instance = SomeClass.new

new_instance.protected_method

produces:

NoMethodError: protected method `protected_method' called for #<SomeClass:0x00000000a94390>

Did you mean? protected_methods

from (irb):21

from /usr/bin/irb:11:in `<main>'

When is the send method usually used?

Usually it is very useful for testing. Imagine that your private or protected method is used in some method and you can't run it directly. The send method gives you the ability to use the method in isolation and make sure that it performs its function. But there are no cases when you should use the send method when solving ordinary tasks, the whole idea of setting access to methods (interface, implementation) disappears if you use the send method on the regular basis. If you can't solve a problem without using the send method, then possibly you are dealing with a code that is poorly written and maybe it should be rewritten.

Private and protected methods are not accessible through the public_send method

It should be clarified that in ruby there is a similar method public_send which works a little differently then the send method. The public_send method knows that OOP is good and If you will use it with private or protected methods then an error will be thrown. Let's use the class from the previous example and call the same methods with public_send:

new_instance = SomeClass.new

new_instance.public_send(:private_method)

produces:

NoMethodError: private method `private_method' called for #<SomeClass:0x0000000244e1a0>

Did you mean? private_methods

from (irb):182:in `public_send'

from (irb):182

from /usr/bin/irb:11:in `<main>'

Let's try to call the protected method:

new_instance.public_send(:protected_method)

produces:

NoMethodError: protected method `protected_method' called for #<SomeClass:0x0000000244e1a0>

Did you mean? protected_methods

from (irb):183:in `public_send'

from (irb):183

from /usr/bin/irb:11:in `<main>'

The send method and whether there is the access level in ruby

A bit of my opinion :) 

Since in the language the opportunity to overcome the visibility limitations is realized, there is an opinion that in fact there are no private and protected methods in ruby. I think this statement is partly correct since we have such ability. But do we use the send method on regular basis? That's the whole question. And I guess that it is necessary to have unlimited possibilities in the programming language so that we can use them in critical situations.

For example, imagine that a bug was discovered that is related to the visibility of methods, the application is used by a lot of people and it should perform its functions (or more critical - some commercial functions). The fastest way to solve this problem is to use the send method in a piece of code where a mistake was made during programming. Which is better, to have 'iron' access identifiers or to be able to overcome these restrictions? Since the task of any technology is to be useful, languages with strong constraints may require more time to solve problems, which may have an effect in the form of lost profits. Therefore, I see this method as an opportunity to solve the situation if it's worth it.

The visibility of the method can be redefined just like the method itself

If you use the same method with different access levels, then the last method with the last defined access level will work. Why is this important? In ruby on rails projects, you can find large models (the model is a class with methods which has different access levels) that can reach several hundred lines and sometimes it's difficult to understand what is happening, especially if some methods have been rewritten and the old ones have not been deleted.

Knowing that the level of access can be overwritten will help you save your time. In addition, this is the excellent case when 'public: some_method' can provide effective help in problem search.

For instance:

class ExampleClass



def some_method

puts "Hello!"

end



private :some_method

protected :some_method

public :some_method



end

Description of the code:

  • The ExampleClass class is defined;

  • One method has been defined 'some_method' which prints the string "Hello!";

  • Next, we define the access level for the method and do this 3 times (which is't necessary for normal situations, but we assume that the first 2 visibility definitions were skipped by an inattentive programmer)

Let's test what level of access this method has:

Is the method available from the outside?

new_instance = ExampleClass.new

new_instance.some_method

produces:

Hello!

=> nil

Of all 3 types of access, only methods with a public access level can be called from outside.

But what if we will define the same method 3 times and give all 3 methods a different level?

The answer - the result will be the same.

For instance:

class ExampleClass



def some_method

puts "Hello 1!"

end



private



def some_method

puts "Hello 2!"

end



protected



def some_method

puts "Hello 3!"

end



end

Let's try to call as a public method:

new_instance = ExampleClass.new

new_instance.some_method

produces:

protected method `some_method' called for #<ExampleClass:0x005611b50d4ce8>

(repl):23:in `<main>'

As we see from the error text, the method has the protected access level. We tried to call the protected method from outside the class, which can't be done with protected methods.

Let's see what happened step by step:

We defined the first method 'some_method' which had public access (because by default methods have the public access level);

Then the interpreter saw the private method which, as arguments, implicitly accepts all the methods that are written below it in the class (until a next method that determines the access level of the other methods);

Then the interpreter saw another method, 'some_method' (in the situation when the interpreter sees 2 methods with the same name, he recognizes the last of the methods, that is, the method that was last determined when the program was read by the interpreter);

Then the interpreter saw the protected method and the some_method method, the last variant of the some_method method became the latest version of this method, i.e. it became relevant and interpolator used it when we called this method in 'new_instance.some_method' and since its name was accepted as an argument for the protected method, this method has a protected access level. Accordingly, we received an error.

Let's add a child class and some method for testing.

Now our example looks like this:

class ExampleClass



def some_method

puts "Hello 1!"

end



private



def some_method

puts "Hello 2!"

end



protected



def some_method

puts "Hello 3!"

end



end



class ChildClass < ExampleClass



def use_protected_method

some_method

end



end



new_instance = ChildClass.new

new_instance.use_protected_method

produces:

Hello 3!

=> nil

As we see, the method was called, everything is correct.

We can change the visibility of the method in derived classes too

Most likely, you will not use this approach to solve problems, but when you will look for errors knowing this thing can be very useful.

Example:

class SomeClass



def method_one

puts "Hi!, I'm method one"

end



def method_two

puts "Hi, I'm method two"

end



end



class ChildClass < SomeClass



private :method_one

private :method_two



end

If we will call methods on the SomeClass instance, everything work:

new_instace = SomeClass.new

new_instace.method_one

new_instace.method_two

produces:

Hi!, I'm method one

Hi, I'm method two

=> nil

But if we will call the same methods on an instance of the ChildClass class, we will see NoMethodError error:

new_child_class_instance = ChildClass.new

new_child_class_instance.method_one

new_child_class_instance.method_two

produces:

private method `method_one' called for #<ChildClass:0x00558efbf489d8>

Did you mean? method

(repl):22:in `<main>'

What happened? The visibility of the methods was rearranged, now in the derived class they have private visibility level.

How to list private, public, protected methods of the class

The syntax of ruby is rich in methods of reflection that helps in understanding of written code. The Module class defines several methods that can help us in estimating how many and which methods are in the class. We are talking about methods such: private_instance_methods, protected_instance_methods, public_instance_methods. Calling them we get an array of methods names that have public, private or protected visibility in the class. These methods are implemented as class methods, i.e. we don't need to initialize an instance of the class to use them, we can call them directly on the class.

Let's create a class and call each of these methods. For our example, we will also pass `false` argument to each method, which according to the documentation means that the method will only work with the current class, without taking into account the methods that it inherits from the other classes. We also use the puts method and interpolation.

Example:

class ExampleClass



def public_method_one

end



def public_method_two

end



private



def private_method_one

end



protected



def protected_method_one

end



end



puts "Public methods: #{ExampleClass.public_instance_methods(false)}"

puts "Private methods: #{ExampleClass.private_instance_methods(false)}"

puts "Protected methods: #{ExampleClass.protected_instance_methods(false)}"

produces:

Public methods: [:public_method_one, :public_method_two]

Private methods: [:private_method_one]

Protected methods: [:protected_method_one]

=> nil


As you can see, now we can in a convenient form get a picture of what is the interface of the class, and what is its implementation.

The use of this method can be especially helpful if methods with different access levels are not structured in the class.

Dinosaurs and Excalibur (example of using public, private, and protected methods together)

Sometimes it's difficult to come up with a suitable example for certain features of the language. The purpose of this example is to show how all three types of methods are related to each other.

Description of the example:

Imagine that we have dinosaurs, every dinosaur has a house, short paws and a ball for football (not every day you can see such dinosaurs, but those who saw them know that it's all true :)). One of the dinosaurs has a sword, and it's not just a sword, its Excalibur!. Because one wizard decided to give it to a dinosaur for storage.

Dinosaurs are cute creatures and they take great care of those things that are given to them. Moreover, dinosaurs have a notable side -  modesty. They don't tell other kinds of creatures about themselves, so as not to embarrass the other kinds of creatures.

Let's write a class that will correspond to our unusual story:

class Dinosaur



DIGNITIES = ["I know 20 ways how to cook people!",

"I know how to growl!",

"I know how to jump!"]



attr_accessor :name, :has_ecscalibur



def initialize(name, has_ecscalibur = false)

@name = name

@has_ecscalibur = has_ecscalibur

puts "Dinosaur #{name}: Yiiiii"

end


def tell_about_your_friend(other_dinosaur)

puts "#{self.name}: His name is #{other_dinosaur.name} and some time ago he told me: '#{other_dinosaur.tell_your_dignities}'. What a great buddy!"

end


def check_if_ecscalibur_here

puts has_ecscalibur? ? "#{self.name}: Yes! It's here!" : "#{self.name}: Hm.. looks like I havent such thing.."

end


protected


def tell_your_dignities

DIGNITIES.sample

end


private


def has_ecscalibur?

@has_ecscalibur

end


end



Description of the code:

1) The example defines the class Dinosaur;

2) For objects, getters and setters methods are defined for properties 'name' and 'has_ecscalibur';

3) The initialize method is defined, which takes 2 parameters, one of which has a default value of false (because Excalibur exists in one instance);

4) Defined private method  'has_ecscalibur?' which should return true or false if the dinosaur has the sword; 

5) Defined protected method 'tell_your_dignities' whose task is to return a string in which the dignity of a certain dinosaur will be described;

6) To output a string we use the constant DIGNITIES, inside which we have 3 values. Maybe dinosaurs have more dignities, but for our purposes, 3 is pretty enough.  In order to get a random object from an array, we use 'sample' method;

7) Also in the example, we have the tell_about_your_friend method, which takes the parameter other_dinosaur and then inside the method we use an instance of the class to call the name method (other_dinosaur.name), and call the protected method tell_your_dignities;

8) The check_if_ecscalibur_here method is defined which, depending on what the has_ecscalibur? method returns will output one string (we use the ternary operator for this).

Let's create a few dinosaurs:

first_dinosaur = Dinosaur.new("Anthony")

second_dinosaur = Dinosaur.new("Rory")

produces:

Dinosaur Anthony: Yiiiii

Dinosaur Rory: Yiiiii

=> #<Dinosaur:0x00560b7ff0ed40 @name="Rory", @has_ecscalibur=false>

Let the wizard hand the sword to one of the dinosaurs in our example.

To do this, we will define the Wizard class with the give_sword method which will use the sample method which will emulate the wizard's selection and the has_ecscalibur setter.

Note that this method has a parameter that has a splat operator, which means that we can pass an undefined number of arguments to the method and inside the method they will all be available in one array.

Our new class:

class Wizard



def give_sword(*creatures)

creatures.sample.has_ecscalibur = true

puts "Wind: 'Wizard decided to give magick sword to one of the dinosaurs!!'"

end



end



Wizard.new.give_sword(first_dinosaur,second_dinosaur)

produces:

Wind: 'Wizard decided to give magick sword to one of the dinosaurs!!'

=> nil

Now let's check which dinosaurs have the sword:

first_dinosaur.check_if_ecscalibur_here

second_dinosaur.check_if_ecscalibur_here

produces:

Anthony: Yes! It's here!

Rory: Hm.. looks like I havent such thing..

=> nil

Let's ask the dinosaurs to tell us about themselves:

first_dinosaur.tell_about_your_friend(second_dinosaur)

second_dinosaur.tell_about_your_friend(first_dinosaur)

produces:

Anthony: His name is Rory and some time ago he told me: 'I know how to jump!'. What a great buddy!

Rory: His name is Anthony and some time ago he told me: 'I know 20 ways how to cook people!'. What a great buddy!

=> nil

As you can see, in our example there are public, private and protected methods. Every method has its own area of responsibility and they are perfectly combined (from my point of view).

The complete example:

class Dinosaur



DIGNITIES = ["I know 20 ways how to cook people!",

"I know how to growl!",

"I know how to jump!"]


Continue reading this ebook at Smashwords.
Download this book for your ebook reader.
(Pages 1-162 show above.)