All original content is created in Ukrainian. Not all content has been translated yet. Some posts may only be available in Ukrainian.Learn more

module_function in Ruby: when module methods are available as module methods and as functions

Post cover: module_function in Ruby: when module methods are available as module methods and as functions
Table of contentsClick link to navigate to the desired location
This content has been automatically translated from Ukrainian.
In Ruby, modules allow you to organize code, but sometimes you need a method to be accessible both for module instances and directly from the module (like a function). This is achieved using module_function.

How module_function works

  • Makes the method private for module instances (read about private here).
  • Adds a "module copy" of the method that can be called without creating an object.
module MathHelper
  def square(x)
    x * x
  end

  module_function :square
end
Now you can call the method directly through the module:
MathHelper.square(5) # => 25
Or use it inside other module methods (as private):
module MathHelper
  def cube(x)
    square(x) * x
  end

  module_function :cube
end

MathHelper.cube(3) # => 27

Features

Private for module instances:
include MathHelper
square(5) # => NoMethodError (because square became private)
Suitable for utilities
module_function is often used in "static utilities" to avoid creating a module object to call functions.
In short:
  • module_function makes a method simultaneously:
    • Private for instances (through include)
    • Accessible as a function through the module
MathHelper.square(5)  # works
include MathHelper
square(5)             # error
This is convenient for modules that have a set of utilities, like in Ruby's standard libraries (Math, Enumerable for example).
But if you pay attention to the point - private for instances (through include), you might ask, what about extend and prepend (wrote about these methods here)?
  • The module method becomes private for instances and accessible through the module itself.
  • Calling through include does not work directly (the method is private).
  • Calling through extend works as a class method.
  • Calling through prepend does not work on the object (the method is private).
Still hard to understand? Let's take a closer look:
module_function does two things at once:
  1. Private for instances
    • If you include the module (include) or do prepend, the method becomes private for all objects, meaning you cannot call it like this: obj.method - there will be an error.
    • Ruby does this so you cannot accidentally call this method on an instance.
  2. Accessible through the module itself
    • Ruby creates a copy of the method at the module level.
    • Therefore, you can call: ModuleName.method - this works like a "static function".
A simple example:
module Utils
  def greet
    "Hello"
  end
  module_function :greet
end

# Through the module you can
Utils.greet  # => "Hello"

# Through include — cannot, because it's private
include Utils
greet        # => NoMethodError
  • Why does extend work?
    extend adds module methods to the singleton class of the class or object. There, the method is called through the class/object, not through an instance, so it works.
  • Why doesn't prepend work?
    prepend inserts the method into the ancestor chain, but the original method remains private -> the object cannot call it directly.

This post doesn't have any additions from the author yet.

28 Oct 14:42

How &:to_s works in Ruby and what Symbol#to_proc is

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
What are Proc and Lambda in Ruby?
28 Oct 15:57

What are Proc and Lambda in Ruby?

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
What happens if you call [1, 2, 3].map(&Person)
29 Oct 17:54

What happens if you call [1, 2, 3].map(&Person)

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Singleton class (eigenclass) in Ruby: what it is and why it is needed
29 Oct 18:29

Singleton class (eigenclass) in Ruby: what it is and why it is needed

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
==, equal?, eql?, === in Ruby: what they check and when to use them
29 Oct 20:47

==, equal?, eql?, === in Ruby: what they check and when to use them

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Include, Extend, Prepend in Ruby: how they work and what the difference is
29 Oct 21:20

Include, Extend, Prepend in Ruby: how they work and what the difference is

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
What is memoization in Ruby?
30 Oct 10:17

What is memoization in Ruby?

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
is_a?, kind_of?, instance_of? — how does Ruby check the type of an object?
30 Oct 19:55

is_a?, kind_of?, instance_of? — how does Ruby check the type of an object?

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
&& vs and — the difference in Ruby that can break your code
30 Oct 20:23

&& vs and — the difference in Ruby that can break your code

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Variables in Ruby: @, @@ and class instance variable
30 Oct 20:54

Variables in Ruby: @, @@ and class instance variable

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
The difference between blank?, present?, empty? and nil? in Ruby
30 Oct 21:06

The difference between blank?, present?, empty? and nil? in Ruby

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
What is Middleware in Ruby on Rails and when is it used
04 Nov 10:39

What is Middleware in Ruby on Rails and when is it used

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska