Create A Module Of Utility Functions in Ruby

The Module class offers a powerful tool called module_function. It serves a dual purpose: it creates singleton method copies (class methods) on the module itself, while simultaneously setting the originals as private instance methods for any class that includes the module.

Let's see this in action by starting with a module of markdown utility functions.

module MarkdownHelpers
  module_function

  def heading(text, level = 1)
    ("#" * level) + " #{text}"
  end

  def link(text, href)
    "[#{text}](#{href})"
  end

  def image(alt_text, href)
    "!#{link(alt_text, href)}"
  end
end

Similar to private and protected, module_function is a scope gate that applies to all method definitions that follow, modifying their visibility. This is what transforms them into these "dual-use" functions.

We can now call these utility functions directly with the module as the receiver:

$ ruby -r ./markdown_helpers.rb -e 'puts MarkdownHelpers.link("Click here", "https://example.com")'
[Click here](https://example.com)

Next, let's see how this behaves when we mix the module into a class, such as Post.

class Post
  include MarkdownHelpers

  def build(title, cover_image, content)
    <<~MD
      #{heading(title)}

      #{image(cover_image.name, cover_image.url)}

      #{content}

      To read more like this, #{link("subscribe to my newsletter", "https://...")}.
    MD
  end
end

Those markdown helpers are available to instances of the Post class, functioning just like any other private method declared in the class itself. Notice that each of them is used in turn in the build method.

Here are some lines I appended to the bottom of the file to demonstrate that this works:

require "ostruct"

cover_image = OpenStruct.new(name: "City Sunset", url: "https://unsplash.com/123_city_sunset")

full_post =
  Post.new.build(
    "Latest Update",
    cover_image,
    "This is the body of the post."
  )

puts full_post

When executing the file, we see the following output:

# Latest Update

![City Sunset](https://unsplash.com/123_city_sunset)

This is the body of the post.

To read more like this, [subscribe to my newsletter](https://...).

Notice, however, that these utility functions are private to the including class. While they can be called internally, they cannot be called with an explicit receiver (part of the public interface).

ruby -r ./post.rb -e 'puts Post.new.link("Click here", "https://example.com")'
-e:1:in '<main>': private method 'link' called for an instance of Post (NoMethodError)

This is the cleanest way to namespace a set of utility functions and make them available directly in a class without expanding the public interface of that class. While a similar effect is achievable with tricks like extend self, that approach leaves included methods public.

I'll also quickly emphasize the usefulness of the mix-in instance methods being distinct from the singleton copies. That property allows us to override the behavior of the included methods without modifying the module's utility functions.

Let's expand Post from earlier to enhance heading to link to itself as a behavior unique to this class.

class Post
  include MarkdownHelpers

  def build(title, cover_image, content)
    <<~MD
      #{heading(title)}

      #{image(cover_image.name, cover_image.url)}

      #{content}

      To read more like this, #{link("subscribe to my newsletter", "https://...")}.
    MD
  end

  private

  def heading(title, level = 1)
    linked_title = "[#{title}](##{slugify(title)})"
    super(linked_title, level)
  end

  def slugify(str)
    str.downcase.gsub(/\s+/, "-").gsub(/[^\w-]/, "")
  end
end

We continue to rely on the underlying behavior of heading as defined in MarkdownHelpers while overriding it to turn the title into an in-page link.

Now the heading at the top of the generated post output looks like this:

# [Latest Update](#latest-update)

module_function is the specific tool designed to create this strictly private utility pattern. I've found this pattern to be particularly helpful in Rails apps as a way to define reusable helpers and to share logic across controllers. A real-world example of this is sharing a common set of webhook handling logic across the different 3rd-party webhook handlers.

Tell us about your project

We build good software through good partnerships. Reach out and we can discuss your business, your goals, and how VisualMode can help.