Si consideri la seguente estensione (il modello popolare da diversi plugin Rails nel corso degli anni):Come estendi un modulo Ruby con metodi di metaprogrammazione macro-come?
module Extension
def self.included(recipient)
recipient.extend ClassMethods
recipient.send :include, InstanceMethods
end
module ClassMethods
def macro_method
puts "Called macro_method within #{self.name}"
end
end
module InstanceMethods
def instance_method
puts "Called instance_method within #{self.object_id}"
end
end
end
Se si voleva esporre questo per ogni classe, è possibile effettuare le seguenti operazioni:
Object.send :include, Extension
Ora è possibile definire qualsiasi classe e utilizzare il metodo di macro:
class FooClass
macro_method
end
#=> Called macro_method within FooClass
e le istanze possono utilizzare i metodi di istanza:
FooClass.new.instance_method
#=> Called instance_method within 2148182320
Ma anche se Module.is_a?(Object)
, non è possibile utilizzare il metodo macro in un modulo.
module FooModule
macro_method
end
#=> undefined local variable or method `macro_method' for FooModule:Module (NameError)
Ciò è vero anche se si include in modo esplicito l'originale Extension
in Module
con Module.send(:include, Extension)
.
Per singoli moduli è possibile includere le estensioni a mano e ottenere lo stesso effetto:
module FooModule
include Extension
macro_method
end
#=> Called macro_method within FooModule
Ma come si fa ad aggiungere macro-come i metodi a tutti i moduli di Ruby?
* C'è * quel post del blog. Sapevo che l'avevo visto da qualche parte di recente. Ancora curioso del perché l'altra strategia non funzioni, e non credo che sia un serio anti-pattern tanto quanto un difetto secondario, ma sono d'accordo sul fatto che fondamentalmente "non c'è motivo di escludere di comportarsi come estendere quando Ruby fornisce entrambi. " –
+1 per "cargo-culted". –
C'è un modo per definire "macro_method" come hai fatto, ma essere in grado di scegliere quali classi hanno accesso ad esso? –