Mike Slinn

Ruby Modularity

Published 2023-06-03.
Time to read: 3 minutes.

This page is part of the ruby collection.

The Ruby language allows modules and classes to be redefined and/or enhanced at runtime. This may seem strange to programmers who work with other computer languages.

Modularity

Similar to the Python language, Ruby classes and modules are mutable. Class and module methods are just attributes of the enclosing class or module, and they can be changed. New methods can be added to a pre-existing module or class.

Modifying Ruby libraries in this way is called monkey patching, and this powerful feature can be dangerous.

However, for your own code, reopening your own modules and classes to add new methods and variables can be a terrific way to write in a modular fashion, and this code is generally quite maintainable.

Case Study: Git_tree

My git_tree Ruby gem takes advantage of mutable modules to provide a simple and flexible mechanism for organizing code in a manageable manner.

Git_tree mostly consists of one module, GitTree, that contains common code for all the git-tree subcommands (git-tree-evars, git-tree-exec, and git-tree-replicate).

lib/git_tree.rb
module GitTree
  def self.directories_to_process(root)
    # implementation
  end
end

In the above code, the self. prefix for the method name marks this method as being a module-level method. Module methods can be invoked without having to include or extend the containing module.

When a subcommand is launched, the GitTree module is re-opened, and methods are added that are specific to that subcommand, then the code is executed.

The above GitTree module is re-opened in the following 3 files. In each of them:

  1. The require_relative statement reads the original definition of the GitTree module, shown above.
  2. The module GitTree statement re-opens the module definition and adds new methods to it for the subcommand that the file implements. Two kinds of methods are common to each subcommand:
    1. command_xxx methods contain the top-level logic for the xxx subcommand.
    2. help_xxx methods display the help message for the xxx subcommand.

Notice that no Ruby classes are used for modularity purposes.

lib/git_tree_evars.rb
require_relative 'git_tree'

module GitTree
  def self.command_evars(root)
    # implementation
  end

  def self.help_evars(msg = nil)
    # implementation
  end

  # Other methods also
end
lib/git_tree_exec.rb
require_relative 'git_tree'

module GitTree
  def self.command_exec(root)
    # implementation
  end

  def self.help_exec(msg = nil)
    # implementation
  end

  # Other methods also
end
lib/git_tree_replicate.rb
require_relative 'git_tree'

module GitTree
  def self.command_replicate(root)
    # implementation
  end

  def self.help_replicate(msg = nil)
    # implementation
  end

  # Other methods also
end

Debug Stubs

Stubs are used for debugging command-line invocations because it is awkward to repeatedly compile-edit-debug installed gems. There is one debug stub for each of the three git-tree subcommands. The debug stubs are not included in the gem.

test/git_tree_evars.rb
require_relative '../lib/git_tree_evars'

GitTree.command_evars
test/git_tree_exec.rb
require_relative '../lib/git_tree_exec'

GitTree.command_exec
test/git_tree_replicate.rb
require_relative '../lib/git_tree_replicate'

GitTree.command_replicate

The Visual Studio Code launch.json entries for debugging are also simple. Each of the above three stubs can be debugged separately.

.vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "args": [ "$jekyll_img $jekyll_pre", "version" ],
      "name": "Debug git-tree-exec",
      "type": "Ruby",
      "request": "launch",
      "program": "${workspaceRoot}/test/git_tree_exec.rb"
    },
    {
      "args": [ "$work" ],
      "name": "Debug git-tree-evars",
      "type": "Ruby",
      "request": "launch",
      "program": "${workspaceRoot}/test/git_tree_evars.rb"
    },
    {
      "args": [ "$work" ],
      "name": "Debug git-tree-replicate",
      "type": "Ruby",
      "request": "launch",
      "program": "${workspaceRoot}/test/git_tree_replicate.rb"
    },
  }
}
😁

Simple, elegant, flexible and easy to understand modularity.

About the Author

I, Mike Slinn, have been working with Ruby for a long time now. Back in 2005, I was the product marketing manager at CodeGear (the company was formerly known as Borland) for their 3rd Rail IDE. 3rd Rail supported Ruby and Ruby on Rails at launch.

In 2006, I co-chaired the Silicon Valley Ruby Conference on behalf of the SD Forum in Silicon Valley. As you can see, I have the t-shirt. I was the sole chairman of the 2007 Silicon Valley Ruby Conference.

Several court cases have come my way over the years in my capacity as a software expert witness. The court cases featured questions about IP misappropriation for Ruby on Rails programs. You can read about my experience as a software expert if that interests you.

I currently enjoy writing Jekyll plugins in Ruby for this website and others, as well as Ruby utilities.

* indicates a required field.

Please select the following to receive Mike Slinn’s newsletter:

You can unsubscribe at any time by clicking the link in the footer of emails.

Mike Slinn uses Mailchimp as his marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp’s privacy practices.