define_method fabrique une vraie méthode, à partir d'un nom.define_method forges a real method, from a name.
Le № 1 interceptait les messages inconnus ; voici la moitié lumineuse du volume. Plutôt que de détourner method_missing, define_method crée des méthodes réelles à l'exécution, à partir d'un nom calculé et d'un bloc. La différence avec method_missing est totale : ces méthodes existent vraiment. instance_methods les liste, respond_to? les voit, un grep les trouve, le débogueur s'y arrête. C'est la métaprog qui produit du code visible au lieu de le cacher — et le seul moyen de générer une famille de méthodes dont les noms sont des données, pas du texte écrit à la main.№ 1 intercepted unknown messages; here's the volume's bright half. Rather than hijacking method_missing, define_method creates real methods at runtime, from a computed name and a block. The difference from method_missing is total: these methods actually exist. instance_methods lists them, respond_to? sees them, a grep finds them, the debugger stops in them. It's the metaprogramming that produces visible code instead of hiding it — and the only way to generate a family of methods whose names are data, not hand-written text.
class Person [:name, :email].each do |attr| # une boucle sur des noms define_method(attr) do # crée une VRAIE méthode lecteur instance_variable_get("@#{attr}") end define_method("#{attr}=") do |value| # … et son écrivain instance_variable_set("@#{attr}", value) end end end p = Person.new p.name = "Ada" p.name # => "Ada" Person.instance_methods(false).sort # => [:email, :email=, :name, :name=] # de vraies méthodes : listées, grep-ables # (.sort : l'ordre de retour n'est pas garanti)
def exige un nom écrit et ouvre un scope neuf. define_method prend un nom calculé et un bloc — donc une closure (Vol 2) qui capture la variable de boucle. C'est exactement ce qui permet de générer une méthode par nom.def demands a written name and opens a fresh scope. define_method takes a computed name and a block — hence a closure (Vol 2) capturing the loop variable. That's precisely what lets you generate one method per name.
« Ai-je vraiment besoin de générer ce comportement, ou une méthode explicite suffisait-elle ? » define_method paie quand les noms sont des données (une liste de champs, une config) ou quand ils sont nombreux. Pour deux ou trois méthodes distinctes écrites une fois, def reste plus clair."Do I really need to generate this behavior, or would an explicit method have done?" define_method pays when names are data (a field list, a config) or when they're many. For two or three distinct methods written once, def stays clearer.
La raison technique pour laquelle define_method marche en boucle là où def échouerait : son bloc est une closure (Vol 2), il capture attr de l'itération courante. Un def attr dans la boucle, lui, n'aurait pas accès à la variable — def coupe le lien avec le scope englobant. La métaprog d'ici n'invente rien : elle réutilise le bloc-closure du Vol 2.The technical reason define_method works in a loop where def wouldn't: its block is a closure (Vol 2), it captures the current iteration's attr. A def attr in the loop would have no access to the variable — def cuts the link to the enclosing scope. The metaprogramming here invents nothing: it reuses Vol 2's closure block.