Simple trick with Metaprogramming in Ruby

Today i will show you a little trick I’ve used in a recent project. Nothing so special, just a little bit of Metaprogramming with Ruby

I have a constant variable like this:


class Foo
    DEFAULT_VALUES = {
        foo: 1,
        bar: 2,
        baz: 3
    }
end

Ok, great. Now every time I want one value from this hash I need use a syntax like this: Foo::DEFAULT_VALUES[:foo]. That rare beauty!

However I’m lazy, I don’t want :: and [] spread around in my code and I don’t want to use CAPITAL_CASE if I can avoid it.

Hence, let’s define some methods!! (I am going to use class methods)


class Foo
    DEFAULT_VALUES = {
        foo: 1,
        bar: 2,
        baz: 3
    }

    class << self
        def default_foo
            DEFAULT_VALUES[:foo]
        end
        def default_bar
            DEFAULT_VALUES[:bar]
        end
        def default_baz
            DEFAULT_VALUES[:baz]
        end
    end
end

Much much better, but it can become even better. I can use a cleaner syntax to retrieve what I need writing Foo.default_foo but I have violated the DRY principle.

So, let’s fix this code with some refactoring:


class Foo
  DEFAULT_VALUES = {
    foo: 1,
    bar: 2,
    baz: 3
  }

  class << self
    DEFAULT_VALUES.each do |k, v|
      define_method "default_#{k}" do
        v
      end
    end
  end
end

In this way, using metaprogramming we can DRY up our code. Thanks to define_method we can declare methods at runtime based on the values of DEFAULT_VALUES.

In this way we can edit the DEFAULT_VALUES hash in just one place and the metaprogramming will do the rest. For example:


class Foo
    DEFAULT_VALUES = {
        new_value: "string",
        hello: "world"
    }

    …
end

Foo.default_new_value # string
Foo.default_hello # world

I hope this post can help you solve some boring problems.

Bye and see you soon!

Leave a Reply

wpDiscuz