Ruby's method_missing()

Ruby's method_missing()

Ruby programming language is a very unique programming language with tonnes of features. It's like a Swiss army knife that lets you do a lot of things without much frustration.

It's been a while since I posted. So let's talk about a very unique yet powerful feature in Ruby: method_missing()

Let's say you have a class or module, when you call an invalid method on that, you get a NoMethodError.

But Ruby lets you control the behaviour of what happens when you don't have a method on a class or a module.

Let's assume we want to set and get any items to values only using a Class or Module. We can't hardcode it because our program's users can call anything they want. In this case, we can use the method_missing() method which can be tuned to exactly fit our needs. Here's an equivalent code for a module:

module X
    @h = {}

    def self.method_missing(method, *args, &block)
        m = method.to_s

        ##
        # If method_missing() is called with =, it must be a setter.
        # For example m=, hello=
        #
        # If method_missing is called without =, it must be a getter.
        # For example, m(), hello
        # There is no way setter could end with something other than =

        m[-1] == '=' ? @h.store(m.chop, args.length == 1 ? args[0] : args) : @h[m]

        # Note that Hash#store returns the value. So when setting item
        # @h.store(a, b) will return b. So we don't need to explicitly return anything
    end
end

X.hello = 500             # => 500
p X.hello                 # => 500

X.world = 500, 50         # => [500, 50]
p X.world                 # => [500, 50]

X.hello = 'hello'         # => "hello"
p X.hello                 # => "hello"

X.lam = ->n { n ** 2 }    # => #<Proc:0x000056298b8eb3d0 p.rb:27 (lambda)>
p X.lam === 5             # => 25

p X.none                  # => nil
p X.none!                 # => nil        

If you want to do this on classes instead, no problem!

class X
    def initialize
        @h = {}
    end

    def method_missing(method, *args, &block)
        m = method.to_s

        ##
        # If method_missing() is called with =, it must be a setter.
        # For example m=, hello=
        #
        # If method_missing is called without =, it must be a getter.
        # For example, m(), hello
        # There is no way setter could end with something other than =

        m[-1] == '=' ? @h.store(m.chop, args.length == 1 ? args[0] : args) : @h[m]

        # Note that Hash#store returns the value. So when setting item
        # @h.store(a, b) will return b. So we don't need to explicitly return anything
    end
end

x = X.new                 # => #<X:0x00005642d185deb8 @h={}>

x.hello = 500             # => 500
p x.hello                 # => 500

x.world = 500, 50         # => [500, 50]
p x.world                 # => [500, 50]

x.hello = 'hello'         # => "hello"
p x.hello                 # => "hello"

x.lam = ->n { n ** 2 }    # => #<Proc:0x000056259ea58bf0 p.rb:27 (lambda)>
p x.lam === 5             # => 25

p x.none                  # => nil
p x.none!                 # => nil        

So there you go! You now have a Class / Module which can dynamically set and get items!

Explanation

When you call the method_missing() it's called whenever a missing method is called on a class instance (e.g., X.new.hello()) or module or a class itself (in this case, use self.method_missing()).

The method_missing() accepts 3 values:

  1. The method name: The name of the missing method itself is sent.
  2. The method arguments: The arguments you sent to the missing method.
  3. The block: In our program, we didn't utilize the block at all. But in your case, you can also pass blocks directly to an undefined method.

So that's what method_missing() is. It's a simple concept that can be utilized in so many different ways. Rails use method_missing() often to build such dynamic methods.

To view or add a comment, sign in

More articles by Sourav Goswami

Others also viewed

Explore content categories