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