Eval-less Metaprogramming #
I often find myself resorting to eval
when writing class methods for metaprogramming, simply because define_method
hasn’t penetrated my brain basket yet. No more!
Here goes Florian GroBB changing Ruby as I know it—yet again—with his implementation of an eval-less has_many
class method:
class Meta def self.has_many( sym ) ivar = :"@#{ sym }" define_method( sym.to_sym ) do new_value = instance_variable_get( ivar ) || [] instance_variable_set( ivar, new_value ) new_value end end end
Even though has_many
is a sorta poor name (we’re not talking about databases here), this method acts like an attr
method, but the attribute is always an Array.
class Person < Meta attr_accessor :name has_many :friends def initialize( name ); @name = name; end end >> fred = Person.new( "Fred" ) >> joe = Person.new( "Joe" ) >> teagle = Person.new( "T. Eagle" ) >> teagle.friends << fred >> teagle.friends << joe => [#<Person:0x80f3ebc @name="Fred">, #<Person:0x81fd404 @name="Joe">]
greasygreasy
Man, that is nice. Goodbye heredocs.
JasonWatkins
Hrmm, I wonder why the “at” sign is forced on the symbol. That seems like the syntax sugar is in the wrong place. We’ve already telling it we’re looking for an instance variable by our choice in the method we’re calling.
Still, there’s no real reason to resort to eval. The other day I scribbled out an example of runtime aspect oriented programming in ruby using ObjectSpace and method_define. I had to use send in order to “go meta” and sidestep the private nature of method_define. In real code you’d probibly put a wrapping api in a module that defined methods for aspect hooks, then just include that in object if you really did want it to apply to everything.
MenTaLguY
Ahh, and that’s what I’ve been looking for. The eval business just seemed so … un-Rubyesque, but I wasn’t aware there was a better way yet.
Is this something worth exploring in Dwemthy’s Array, perhaps?
flgr
GroBB? Am I a bulletin board now? ;)
why
JasonWatkins: Yeah, the private checking on
define_method
is screwy.MenTaLguY: Dwemthy’s Array is metaprogramming two stairs down. It creates a
traits
class method, which must create instance methods for each trait. I would need to resort to usingsend
like Jason described, which.. eh.. I dunno.. maybe.. but goshhhh.Sam
Oh man, that’s so the name of my Rails-based BB software now.
bodin
Looks like Dwemthys array needs to be repolished!
chris2
JasonWatkins: Look at the implementation of instance variables to see why prefixing with @ is required.
MenTaLguY
Why, why would send be necessary? At worst you can instance_eval/class_eval, can’t you?
MenTaLguY
That is, eval with blocks rather than strings (I’ve managed to confuse myself badly here…). Anyway, c.f. my poignant-stiffs post. Turns out variable scoping isn’t a problem.
flgr
I think .send() is to be prefered over .instance_eval() for calling private methods as it has less side effects.
eean
eval has always annoyed me (try reading the RSS library sometime), good to see there are better alternatives.
Comments are closed for this entry.