DEV Community

Merdan Durdyyev
Merdan Durdyyev

Posted on

Using Modules in Ruby

Alt Text

Welcome back dear friends, dear readers, coders, and enthusiasts!

Welcome to our next article where we present the preciousness of our beloved Ruby PL. This time we are going to look through the “Modules” topic in Ruby. We will also strengthen our learning by showing some examples of how to use Modules in Ruby.

....................................................

What are the Modules?

In short, a Module is a way to gather classes, methods, and constants under one roof. To expand the topic a bit wider, this gives us two widely used methodologies in programming.

  1. Collectionotherwise named as a ‘namespace’, gives us an opportunity to gather classes, methods, and constants under a single roof.

  2. Mixina mechanism to include a ready module into another class and use its content (methods, values, constants) as its own. Thus we can expand the possibilities of that class.

After mentioning its advantages in general, let’s dive deeper into it and look at the above-mentioned items in detail. We will also provide explanations with code examples where possible.

....................................................

Alt Text

1. Collection

Imagine such a situation. You decided to collect together operators, functions, and all the related data into one place. Why should we not make it a module?

module Trig
   PI = 3.1416 # A Constant  

   Trig.sin(x)
      #...
   end   

   Trig.cos(x)
      #...
   end
   ...
   ...
end
Enter fullscreen mode Exit fullscreen mode

We can use the above-defined methods and constants somewhere else. Let’s use this module to perform trigonometric calculations. Here is an example:

radius = 6

# Here we call a constant named 'PI' from module 'Trig'
area = Trig.PI*radius**2 # PI*R^2 formula

# Let's calculate sine and cosine of the angle.
# Here we call 'sin' and 'cos' methods from
# 'Trig' module.

angle = 45
sine = Trig.sin(angle)
cosine = Trig.cos(angle)
Enter fullscreen mode Exit fullscreen mode

If these examples are a bit easy for you, we can take a look at more complex ones. Now, let’s take a look at a module that comprises several classes in it.

You decided to develop a ‘Customer’ class. It has a few properties and methods. Depending on the context, a customer can act differently. So let’s write a class that has a bit different methods and properties depending on the type of customer in a particular situation, and place them in different modules.

module Bank
   class Customer # A bank customer.
      attr_accessor :balance, first_name, last_name

      def put_cash(sum)
         puts "#{sum} amount of cash was put."
      end      

      def withdraw(sum)
         puts "#{sum} amount was withdrawn."
      end
   end
end

module Shop
   class Customer # A shop customer
      attr_accessor :products

      def pay_for_products
         products.each do |product|
            puts "#{product} bought."
         end
      end
      ...
      ...
   end
end
Enter fullscreen mode Exit fullscreen mode

I hope you paid attention to how we separated the issues and placed two different customers in two different modules. Both of the modules have ‘Customer’ class, but they behave differently. In other words, they have different methods and properties. If we have defined both classes in the same namespace, we would have had a conflict. And that would be a name conflict at least, not to mention the others. Ruby would not have allowed us to write the same-named class within the same namespace. So we just place them in different modules and no conflicts comes up.

Now we can easily call and use the ‘Customer’ class and its methods in the following examples:

# Example - 1
bank_customer = Bank::Customer.new
bank_customer.balance = 25
bank_customer.first_name = "John"
bank_customer.last_name = "Brown"
bank_customer.withdraw(50)

# Example - 2
shop_customer = Dukan::Customer.new
shop_customer.products = ["apple", "fig", "bread", "milk"]
shop_customer.pay_for_products
Enter fullscreen mode Exit fullscreen mode

As you see in the examples above, by placing classes, modules, and constants into different modules we can avoid name conflicts and have them being used within the right context.

Now let’s take a look at how we can use them as Mixins.

...................................................

Alt Text

2. Mixins

Example A : Class relationships

There is a bit complex relationship matter called ‘inheritance’ in the programming world. It is a case when a class is a child/subclass of another one, that we call as ‘parent’. This way a child class possesses the features and methods of a parent class. Here is an example of this:

# Define a class for animals.
class Animal
   attr_accessor :name, :category  

   def make_noise
      puts 'Making noise...'
   end   

   def move
      puts 'Moving...'
   end
end
Enter fullscreen mode Exit fullscreen mode

The next classes to be defined are somewhat part of, or subpart of an ‘Animal’ class. And they will possess the features and methods of an ‘Animal’ class. So, let’s define classes ‘Fish’ and ‘Bird’ and make them as child classes of an ‘Animal’ class. ‘Ruby’ PL makes it this way:

# 'Fish' class
class Fish < Animal
   ...
end

# 'Bird' class
class Bird < Animal
   ...
end
Enter fullscreen mode Exit fullscreen mode

Now we can call methods of an ‘Animal’ class and use its properties from within ‘Fish’ and ‘Bird’ classes.

a_fish = Fish.new
a_fish.name = 'Ariel'
a_fish.make_noise # 'Making noise...'

a_bird = Bird.new
a_bird.name = 'Dove'
a_bird.move # 'Moving...'
Enter fullscreen mode Exit fullscreen mode

Example B : Mixins

Now, getting inspired by the examples above, let’s write something a bit different. A film, an advertisement, a clip are all Videos. We can ‘play’ them and ‘pause’ them all.

Besides that, we can download them onto our PC or mobile device. Suppose we can download only video, only audio, or both at the same time. We have ‘Download’ module for this.

So let’s define ‘Video’ and ‘Download’ modules for this purpose.

# 'Video' module
module Video
   def play
      puts 'Playing ...'
   end   

   def pause
      puts 'Paused...'
   end
end

# 'Download' module
module Download
   def download_video
      puts 'Downloading only video...'
   end

   def download_audio
      puts 'Downloading only audio...'
   end   

   def download_both
      puts 'Download audio & video...'
   end
end
Enter fullscreen mode Exit fullscreen mode

Now let’s define other 3 classes and make methods of ‘Video’ and ‘Download’ modules available to them.

# 'Film' class
class Film
   attr_accessor :title
   include Video
   include Download
end

# 'Advertisement' class
class Advertisement
   include Video
   include Download
end

# 'Clip' class
class Clip
   include Video
   include Download
end
Enter fullscreen mode Exit fullscreen mode

Now we can use features of ‘Video’ and ‘Download’ modules after making them available within those lately defined classes. Here is an example:

# 'Film' class
silence = Film.new
silence.title= 'The Silence of Lambs'
silence.play  # 'Playing ...'
silence.download_both # Download audio and video

# 'Advertisement' class
cola_ad = Advertisement.new
cola_ad.play  # 'Playing ...'
cola_ad.download_audio # Download only audio

# 'Clip' class
drake_clip = Clip.new
drake_clip.play  # 'Playing ...'
drake_clip.download_video # Download only video
Enter fullscreen mode Exit fullscreen mode

So we defined ‘Video’ and ‘Download’ as modules, and not classes. Lately, we made their features available to the other three classes. We used ‘include’ keyword for that.

Differences between Example A and Example B

The relationship between a Parent and a Child class is very important in programming. We implement it with a mechanism called ‘inheritance’ and avoid writing excess code. Thus we support DRY (Don’t Repeat Yourself).

Both ‘A’ and ‘B’ examples are doing almost the same thing but there are few differences between them. A Ruby class can not inherit from multiple classes at the same time. In other words, a class can not have multiple parent classes at the same time. So we could not inherit from ‘Video’ and ‘Download’ classes at the same time. For this reason, we implemented ‘Video’ and ‘Download’ as modules and used them as Mixins in ‘Film’, ‘Advertisement’, and ‘Clip’ classes.

.................................................

Alt Text

Conclusion

And now, we introduced our beloved Ruby PL closer and learned how to implement Modules in it. Besides that, we learned in which cases we should implement something as a Class or as a Module.

One of the famous modules in Ruby PL are ‘Enumerable’ and ‘Comparable’ modules. By including these modules into your custom classes, you add comparison and numerating functionality to your own classes. This way you can compare two instances of your own class and iterate through its elements if it is a type of collection.

Hope to meet you soon in the next article, dear friends.
Stay healthy, wealthy, and wise!

Top comments (0)