avdi + metaprogramming   10

Extend modules instead of defining methods on a metaclass
In the entry Replace method_missing with dynamic method definitions I have the following example code.class Decorator def initialize(subject) subject.public_methods(false).each do |meth| (class << self; self; end).class_eval do define_method meth do |*args| subject.send meth, *args end end end endendThe context of the example can be summarized as, you want to delegate from the instance all the public methods defined on the constructor argument.Ali Aghareza pointed out to me that defining methods on the metaclass of an instance isn't the nicest thing to do. The problem with it is that you've made it much harder for anyone else to change the behavior of the instance.Here's a more simplified example. The following code creates a new Object and defines the hello_world method on the Object instance.class Object def metaclass class << self; self; end endendobj = Object.newobj.metaclass.class_eval do def hello_world "hello" endendobj.hello_world # => "hello"This works fine; however, if someone wanted to change the way hello_world behaved, by defining the method on the metaclass you force them to make their change by redefining the method on the metaclass. The current solution does not allow you to extend modules and alter the behavior of the instance.The following example demonstrates that extending a module does not change the behavior of an instance if the behavior has been defined on the metaclass.class Object def metaclass class << self; self; end endendobj = Object.newobj.metaclass.class_eval do def hello_world "hello" endendobj.hello_world # => "hello"module Spanish def hello_world "hola" endendobj.extend Spanishobj.hello_world # => "hello"A better solution is to change the behavior of the instance by extending modules instead of defining behavior on the metaclass.obj = Object.newmodule English def hello_world "hello" endendobj.extend(English).hello_world # => "hello"Now that the behavior is defined on an ancestor instead of the metaclass you can change the behavior by extending another module.obj = Object.newmodule English def hello_world "hello" endendobj.extend(English).hello_world # => "hello"module Spanish def hello_world "hola" endendobj.extend(Spanish).hello_world # => "hola"This solution works fine for our simple example, but it can also be applied to our first (much more complicated) example, even without knowing how to define the module. In the case of the Decorator, you can simply define an anonymous module and immediately extend it.class Decorator def initialize(subject) mod = Module.new do subject.public_methods(false).each do |meth| define_method meth do |*args| subject.send meth, *args end end end extend mod endend© Jay Fields - www.jayfields.com
define_method  def  metaprogramming  metaclass  from google
april 2008 by avdi

Copy this bookmark:



description:


tags: