Considering an interface like this:
foo = manager.queue_task :foo bar = manager.queue_task :bar manager.execute foo = foo.unwrap bar = bar.unwrap
I wanted to see if I could avoid requiring the unwrap step for “ergonomics”. So, I came up with this very dangerous-seeming beauty:
class ForwardingSubstitute def self.new backchannel =  obj = super true_exec = BasicObject.instance_method :instance_exec true_exec.bind(obj).call do @backchannel = backchannel end [obj, backchannel] end instance_methods.each do |name| undef_method name end def method_missing(name, *args, &block) @backchannel.last.public_send name, *args, &block end end
Now you can hold on to the backchannel
Array and slip the value in whenever it’s actually ready. Using the object before the value is present just results in a NoMethodError on nil.
Now you don’t have to unwrap. The execute function can just put the results into the appropriate backchannels as saved by the queue_task calls.
Ruby warns me undefing
__send__ are particularly fraught, but if they’re all being forwarded to another object (which do work as expected), it seems like it might be mostly ok?
I’ve found two significant issues myself.
Class ===still truly recognizes that the substitute is not the same as the delegated object, while
#is_a?refers to the delegated object.
- Methods like
selfand that transparently strips the delegator. I don’t think the stripping is actually a problem since you get back the delegated object itself anyway.
I scrapped it for being too subtle in any case, but I’d love to hear of any other issues it might have.