Classes and Modules

In last week's blog post I wrote a short bit on classes in Ruby and how they can be interacted with. This week I am taking a look at modules and how they differ from classes.

The easiest way to describe modules is by considering them to provide a piece of functionality which could be useful to other classes. Instead of repeating the same code in a different class or calling a seemingly unrelated class for a specific function, modules offer the option to separate this functionality from a class and making it available to other classes.

How this is done

Have a look at the linked blog post if you wish to see how classes are declared. Maybe I would like to extend my VideoGame class so I can load my saved games. I could achieve this by adding a new method:

class VideoGame
  def load_game(file_nr)
    puts "Loading #{game}_#{file_nr}"
  end
end

Say instead of printing out text this would actually be a lot longer, getting a file, checking it, loading it. Mission accomplished. But now I am writing a different class, maybe a music player, and I also want to load files into there. My DRY alert is going off and here is where modules come into play. They are declared much the same as classes:

module LoadFile
  def load_file(file)
    puts "Loading #{file}"
  end
end
Now I just need to use this module in my VideoGame class:
class VideoGame
  include LoadFile
end

But wait! Now we can use the Load_File#load_file method, but how are we going to give it the right parameter? I want to keep LoadFile as objective as possible, so instead of complicating that method I can make a simple alteration in VideoGame:

class VideoGame
  include LoadFile

  def load_game(save_nr)
    game_file = title + "_" + save_nr.to_s
    load_file(game_file)
  end
end

Last week I used Skyrim, let's stick with that. Say I want to load my last saved game, which is the 18th save of Skyrim. Instead of writing my 'extensive' file loading method again for the VideoGame class I use the existing one which I can use for music and video players as well. In this example I only make sure that I provide the correct file name, then call on the module.

This file on GitHub.

When I load it in IRB I can do the same as last time:

>; skyrim = VideoGame.new("Skyrim")
=>; #<;VideoGame:0x007f792baf8e20 @title="Skyrim">;
>; skyrim.load_game(18)
Loading Skyrim_18

Why not use a class?

Good question. The same can be accomplished with another class, but classes are designed to be instantiated. To load a file there is no reason to create a new instance for each file we load. It is a design choice and best practice to use a module in this way.

This was just a short introduction to modules. For more information I would recommend reading chapter 4 in David A. Black's The Well-Grounded Rubyist. For a concise overview I found this question on StackOverflow helpful.