Object-specific decorators, or, decorators made simple.

I’m reading Design Patterns in Ruby. A bit outdated, but what the hell. In any event, I got to the chapter on decorator patterns. The examples seemed like a hot mess. (class << obj followed by aliasing is especially heinous!) Even the dynamic alternative is somewhat questionable, as it will break if a singleton method ever gets defined on the object without calling super. Luckily, modern Ruby versions are better equipped for the problem. Instead of extending the object, you can prepend the module to the singleton_class, call super and go about your day in all confidence that the code won’t break or fire off in unexpected orders.

class Test
  def decorate(mod)
    singleton_class.class_eval {prepend mod}
  end
  def decorated(text)
    text
  end
end
module Mew
  def decorated(text)
    super "MEW! #{text} MEW!"
  end
end
module TwoPlusText
  def decorated(text)
    super "2 + #{text}"
  end
end
test = Test.new
test.decorated "Are my modules stupid enough?"
=> "Are my modules stupid enough?"
test.decorate(Mew)
test.decorated "Are my modules stupid enough?"
=> "MEW! Are my modules stupid enough? MEW!"
test.decorate(TwoPlusText)
test.decorated "Are my modules stupid enough?"
=> "MEW! 2 + Are my modules stupid enough? MEW!"

With the prepend approach, decorators function in the order in which they are applied, instead of reverse order with the extend approach. Potential bug with singletons resolved, simplified usage. Isn’t it grand?

Advertisements
Object-specific decorators, or, decorators made simple.

Removing singleton methods.

Defining singleton methods is great, you can enhance a specific object with a quick dash of code. You can even do it dynamically.

Test = Class.new { define_method(:method) {"regular method"} }
test = Test.new
test.method # => "regular method"
test.define_singleton_method(:method) {"singleton method"}
test.method # => "singleton method"

Looking good so far… I’m thinking about writing applications where the objects I’m most likely to enhance with singleton methods are long-lived and the modifications will most likely be temporary changes designed to simplify the codebase. How do I get those singleton methods out of my way when I’m done with them?

test.singleton_class.send :remove_method, :method
test.method # => "regular method"

It would be nice if there was a pre-packaged Ruby method that did this. In the mean time, we can just punch some ducks.

class Object
  def remove_singleton_method(m)
    self.singleton_class.send :remove_method, m
  end
end
test.define_singleton_method(:method) {"singleton method"}
test.method # => "singleton method"
test.remove_singleton_method(:method)
test.method # => "regular method"

Edit: Refactored away from an obsolete looking #instance_eval block followed by eval(“class << self; remove_method \”#{m}\”; end”) for the method after learning of Object#singleton_class — That’s what I get for learning by digging around in old code from Ruby 1.8 and 1.9! Guess it was too new to make it into any of the 1.9 Ruby scripts I’ve read through, not that I’ve seen them make full use singleton methods either.

Note: class << self is a real bitch, blocks or not it will stop your nested lexical scopes cold. Hence the eval with string interpolation approach for the initial not-so-hardcoded remove_singleton_method. I didn’t think to use a class<<self;self;end.send chain, which apparently is another way around it. (Gotta love that optional whitespace for confusingly terse block returns, eh?)

Removing singleton methods.

Just so I don’t forget…

BasicObject#instance_exec is awesome. It’s an easy way to pass some instance variables to another object while still calling code in the context of the receiver.

Other thoughts: I did a quick web search on Ruby instance_exec and hit some other blog posts before posting mine. Came across a lot of confused whining about differences in bindings between passing an ordinary block and using instance_eval or instance_exec. Put plainly, they’re doing it wrong and I feel infected by their rampant stupidity.

FFS, people. The whole point of instance_eval is that it will evaluate instance variables and lookup in the context of the receiver. You do NOT want lookup to resolve in the context of the caller if you use instance_eval. The hint is in the name. You eval in the context of that particular instance of the receiver’s class. End rant.

Other, other thought: At least I’ll have an easy time going pro in another year. One year of programming, I’m already looking to ream professional engineers with more experience for their inability to adequately comprehend programming. Another year of study at this rate of progress and I should do great.

Just so I don’t forget…

Interesting use of the super keyword with module mixins.

I was reading through Metaprogramming Ruby 2 last night and had a thought that stuck with me. Specifically, it had to do with the examples of module inclusion and class ancestor chains and the possibility of using combinations of prepended and included modules to simulate multiple inheritance hierarchies. Precisely one nap later, my subconscious worked out the pitfalls and best practices for that type of design pattern, leaving me to wonder about matters of implementation. Naturally, my thoughts turned to a staple of the inheritance hierarchy – super. I fired up pry and gave it a shot.

module TestModule1
  def test_super
    puts "In the prepended module!"
    super
  end
end
module TestModule2
  def test_super
    puts "In the included module!"
    super
  end
end
class TestParentClass
  def test_super
    puts "In the parent class!"
  end
end
class TestChildClass < TestParentClass
  prepend TestModule1
  include TestModule2
  def test_super
    puts "In the child class!"
    super
  end
end
TestChildClass.ancestors
=> [TestModule1, TestChildClass, TestModule2, TestParentClass, Object, ...]
TestChildClass.new.test_super
In the prepended module!
In the child class!
In the included module!
In the parent class!
=> nil

It works! Yay! Should this be surprising? Depending on your sources of information, yes or no. Stack overflow answers, internet comments and even books typically describe the function of super as working on the superclass. To quote The Ruby Programming Language, “super works like a special method invocation: it invokes a method with the same name as the current one, in the superclass of the current class. (Note that the superclass need not define that method itself—it can inherit it from one of its ancestors.)”

This hints at the true functionality of the super keyword, sort-of, yet the following text references super as working on the superclass rather than the next ancestor up the hierarchy. There’s no mention at all of a module implementing the identically named method between the subclass and superclass.

Unsurprisingly, people simplify the information and think of super as forcing the method lookup to jump directly to the superclass. It makes sense. That concept is consistent with concepts of inheritance hierarchy and even the ancestors chain of a class… Until we get to module mix-ins. Now everything is different. There are new ancestors in the chain that do not follow strict inheritance hierarchies.

This leads to shouldn’t-be-confusing bugs when you call super in a method and that call gets unintentionally intercepted by a module mixin. You look in the subclass, see no problems. You look in the superclass, you see no problems. If you happen to intercept the method in a way that doesn’t cause an exception to be thrown then you might be in for a debugging ride, crawling through the call stack one item at a time, looking for anything that’s out of place. Once you’re out of that theatre of pain, you might be put off from using super, module mixins or both. Save yourself that pain, remember the interaction of modules and the super keyword.

The most interesting (and dangerous) interaction of modules and super comes from the fact that Ruby either will or will not ignore multiple module inclusions, depending on how your code is organized and loaded. This makes for an opportunity for bugs as well as an opportunity to sadistically inflict pain on others with obscure code that abuses bugs in the programming language to achieve desired behavior. Make sure you remain aware of this behavior when using the super keyword in conjunction with modules.

Run the following code to see for yourself:

module Mod;end
class Test1;end
class Test2 < Test1
  include Mod
end
p Test1.ancestors, Test2.ancestors
class Test1
  include Mod
end
p Test1.ancestors, Test2.ancestors

Future Ruby versions might behave differently, according to the discussion in the Ruby issue tracker and the older related issue. Should be a lot of fun if we get a more dynamic ancestors tree!

Interesting use of the super keyword with module mixins.