Ruby Method Missing In Javascript

All code is editable with live results.

I love Ruby and I love Ruby metaprogramming capabilities. One of the first metaprogramming tricks I discovered was Ruby’s method_missing functionality. To sum it up, it allows you to intercept calls to any method not implemented—either directly inside your class or indirectly in the ancestor chain—by your Ruby class.

Thanks to es2015 Proxies, it is easy to do in JS.

Ruby version

Here is a simple Ruby example:


class Foo
  def fooMethod
   'Hello from Foo'
  end

  def method_missing(m, *args, &block)
    "There's no method called #{m} here."
  end
end

Nothing fancy here, if the method is available we call it


Foo.new.fooMethod

Otherwise we return a default message


Foo.new.bar

JavaScript version

First let’s take a look at a simple JS constructor. This constructor will just return a new object with a fooMethod.



function Foo() {
  this.fooMethod = function fooMethod() {
    return 'Hello from Foo';
  };
}




const f = new Foo();
f.fooMethod();


const f = new Foo();
f.bar();

Ohoh, this is what’s missing and what we want to implement to match the ruby implementation. Let’s do it now. We will define an handler for our object returned by the Foo constructor. This handler will specify how to handle gets on our object. We will then attach the handler to the object using a es2015 Proxy.

Finally instead of returning the object created by the Foo constructor we will return the Proxy.



function Foo() {
  this.fooMethod = function fooMethod() {
    return 'Hello from Foo';
  };

  const handlers = {
    get(target, key) {
      if(Reflect.has(target, key)) {
        return Reflect.get(target, key);
      }

      return function method_missing(...args) {
        return `There's no method called ${key} here.`
      }
    }
  };

  return new Proxy(this, handlers);
}

Calling a regular function on the object created by the Foo constructor is still possible.


const f = new Foo();
f.fooMethod();

But we can now try to call a non-available method and we will have something similar to Ruby’s method missing behaviour.


const f = new Foo();
f.bar();