Technomancy fletter

One of the handy things about Lisp that I've gotten attached to is the flet function. Using it you can temporarily redefine a function but have its definition revert back to the original when its body exits. This is analagous to the way a block in Ruby can have some specific behaviour that goes away when it exits, but flet doesn't seem to have a Ruby equivalent. (The best my searches found was pstickne enlightening folks in #ruby-lang about this lisptastic functionality.) Anyway, I just figured out an implementation.

 def Object.flet(bindings,  &block)
 old_methods = {}

bindings.each do |the_method,  body|
 old_methods[the_method] = method(the_method)
 define_method(the_method,  body)
 end

 begin
 block.call
 ensure
 bindings.each do |the_method,  body|
 define_method(the_method) { |*args| old_methods[the_method].call(*args) }
 end
 end
 end
 

This lets you do stuff like:

 puts "foo"

 Object.flet(: puts => λ { |str| print "#{str.reverse}
 " }) do
 puts "foo"
 end

 puts "foo"
 

... which will output:

foo
 oof
 foo

Pretty cool, huh? I haven't done much funky metaprogramming in Ruby, so it's quite possible my attempts to Lispify it here are dangerous and/or misguided. (That's what comments are for, BTW.) But it's coming in pretty handy for me so far.

Update: Josh points out that this is not thread-safe and thoroughly violates encapsulation in a way that should earn me a severe thrashing (dare I say—flogging?) were I to use it in production in anything but the simplest of circumstances. In my mind it's kind of like using eval: all advice concerning actually using it goes along the lines of "never do this". Only once you learn when it's OK to ignore such strong warnings can you be trusted to posess the necessary discretion to use it in practice.

The context here is that I want to run a bunch of tests and gather failure data within my library. But when I require test/unit, it sets up an at_exit block that actually initiates the running of the tests as a final act before the execution finishes. This is redundant since I already initiated them myself in the library. But once you've created an at_exit, there's no mechanism for disabling it. I could just redefine at_exit permanently, but it's quite possible I'd want to use it elsewhere. flet allows me to limit the scope of this change. But it's only forgivable because I'm not really working with an object-oriented phenomenon here; I'm working with a lower-level feature of Ruby.

 Object.flet(: at_exit => lambda {}) do
 # keep test/unit's at_exit block from running
 require 'test/unit'
 end
 

And as Josh notes, Rubinius will make stuff like this much easier. I imagine I wouldn't even have to resort to flet to get around my at_exit problem.

« 2007-10-13T00:00:01Z »

josh2007-10-14T15:58:00Z
Interesting. It's like micro-mocking. Seems very functional, meaning antithetical to how object-oriented programming is supposed to work. Of course Ruby is full of stuff like that, so whatever. Paradigm wars aside, I have two comments:





1) Restoring the old methods should happen in an ensure clause, in case something raises an exception in the block.





2) This doesn't appear thread safe. Yeah I know, who cares about threads in Ruby?





By the way, this should be a little easier in Rubinius.
Tore Darell2007-10-14T23:43:20Z
As long as you're already touching onto other paradigms (hey, it's lisp after all), here's a version that works on objects instead of classes:





http://pastie.caboo.se/107161





It uses a prototype-ish proxy object which receives the "fletted" methods and the block is evaluated in its context.. It looks a little funky because of all the different scopes it has to access at different times, but it should work nonetheless. I know next to nothing about threads, but shouldn't this also be thread safe?
Phil2007-10-15T01:02:40Z
Tore: That's pretty cool. I love the sneaky stuff you can get away with when you're prototype-based.

Name

URL

HTML will be escaped for now. Comments are currently moderated; sorry.