I'm familiar with dry-rb coding style and I think it is very difficult to reason about. Metaprogramming often feels like magic but it comes at a price - hard for newbies to grok and impossible for static tools to work with. I would only advise using it (if at all) in well-tested libraries that expose a well-documented public API.
I'm immediately put off by any system that doesn't let me figure out WTF something actually is until runtime. I should be able to learn most of what I need to know about a codebase with a basic text editor and grep. Some questions should be for documentation to answer, but "what the hell is this variable representing?" isn't one of them.
I prefer to let my code and my tools remember stuff for me. Rails and similarly-magical Ruby systems have too high a (human) memory overhead for my taste. Hard to jump in and out of if it's not the main thing you do every day.
I think Rails does a pretty good job of making well-documented sense. Lots of crappy gems abuse things like implicit global variables, rewriting core methods, and other monstrous applications of metaprogramming.
It is hard to reason about. I think if you understood it (meaning you are a core contributor) then you feel like it really helps organize your code and keep everything reasonable. But to a newbie it is the exact opposite.
I know in my projects I use some techniques which if you continued to work on the project you would really appreciate how many problems it solves but it seems crazy to my coworkers. Luckily it's a small gem and I'm the one who works on it but still.
The problem as I see it is when metaprogramming is used laterally or vertically. If code at a higher scope changes code at a lower scope, this makes a lot more sense than the arbitrary and profane directions of mutability that Ruby allows. So, like all things in Ruby, it's a matter of how the tool is used.
To metaprogramming! The cause of -- and solution to -- all of life's problems.
def my_method
super_result = super
...
(do something with super_result, or simply after super has been called)
...
super_result <== to make sure your method returns the result of super
end
you can just do this:
def my_method
super.tap{|result| (do something with/after result) } <== will still return the result of super
end
From my understanding, the anti-pattern is related to creating an API that requires a call to super when overriding a method. super.tap may be used when interacting with an API that employs the "call super" anti-pattern, but calling super is not itself an instance of the anti-pattern; it can be used in perfectly legitimate cases as well.
I would only advise using it (if at all) in well-tested libraries that expose a well-documented public API.
I sometimes fantasise about a world where what you describe is just... how we write software. I really wish i could use my colleagues libraries instead of digging into their source code.
Metaprogramming in Rails is for infrastructure code, not business logic.
As far as the metaprogramming is encapsulated in a module with a good API and serves to make business code easier to read, it can be extremely helpful.
This article demonstrates the mess you can get into when you mix OOP with metaprogramming and why Ruby has a bad reputation for encouraging it. Metaprogramming seems to compound the contortions of OOP in contrast to the elegance macros add to functional languages like Clojure and Elixir. Ruby's method_missing and instance_eval seem like ugly hacks compared with the simple quote/unquote of Lisp-based languages.
Author here. As a longtime fan of Scheme and Lisp-based languages in general, I take responses like this very seriously. I'm not a huge fan of method_missing, and in practice I don't use it terribly often, but it has its place.
That said, the article is about something quite minimal in terms of metaprogramming: subclassing a class (Module), which would itself not be defined as metaprogramming at all, and then defining some methods on it in an initializer. I don't find this very convoluted at all: you're creating a prototype for a module which can be configured to use in a variety of contexts.
I wrote this article after having used this pattern in a very practical context (designing Mobility) to solve a very concrete problem: abstracting storage solutions (backends) from the interface for translating content. I found it to be a very concise and elegant solution in this context. I think a project like Rails would benefit in several places by adopting this pattern since it would actually simplify a lot of the code which already uses anonymous modules (basically the same mechanism) but in a very hard-to-understand way.
Comparisons to other languages like Clojure and Elixir bring with them a whole slew of other questions, since the languages are entirely different. I'd prefer to focus on Ruby itself and what the alternatives are within this language, and for the use case discussed I strongly believe Module Builder is the optimal solution, not a "mess" at all.
i wish there were more of these (ruby) metaprogramming articles, since there is a lot of ingenuity going on under the covers.
it's just like jargon in specialized fields - so much is packed into so few symbols. it allows those who understand it to express more complex ideas more easily but also serves as a barrier to those unfamiliar with the jargon.