Metaprogramming is a special feature of some languages (including Ruby) used to define code dynamically, at runtime. It is code that generates other code. This is responsible for a lot of the “magic” in Rails, for example: some_route_path
helpers, and find_by_%attr_name%
in ActiveRecord.
It seems like an awesome feature at first, until it is misused, and unfortunately it is misused most of the time. The downsides of metaprogramming are:
- Difficult to locate method source code.
- Hidden intention in the codebase.
- IDEs can’t locate these methods for auto-complete.
There is a famous saying: “if you have a problem and think that metaprogramming could help you, then congratulations! Now you have two problems.” Many times, the coding challenges that you solve with metaprogramming could be solved in a simpler way, with better code quality, separation of concerns, and clearness.
Here is an example of unnecessary metaprogramming in the rest-client
gem:
POSSIBLE_VERBS = ['get', 'put', 'post', 'delete']
POSSIBLE_VERBS.each do |m|
define_method(m.to_sym) do |path, *args, &b|
r[path].public_send(m.to_sym, *args, &b)
end
end