6 min read

AbstractQueryFactoryFactories and alias_method_chain: The Ruby Way

In the past week, I read a couple of posts that made me really want to respond with a coherent explanation of how I build modular Ruby code.

The first post, by Nick Kallen of Twitter, gushed about the benefits of PerQueryTimingOutQueryFactory and called out Ruby (and a slew of other "hipster" languages) for using language features (like my "favorite" alias_method_chain) and leveraging dynamicism to solve problems that he argues are more appropriately solved with laugh-inducing pattern names:

In a very dynamic language like Ruby, open classes and method aliasing (e.g., alias_method_chain) mitigate this problem, but they don’t solve it. If you manipulate a class to add logging, all instances of that class will have logging; you can’t take a surgical approach and say “just objects instantiated in this context”.

If you haven't read it yet, you should probably read it now (at least skim it).

As if on cue, a post by Pivot Rob Olson demonstrated the lengths some Rubyists will go to torture alias_method_chain to solve essentially the same problem that Nick addressed.

In short, while I agree in principle with Nick, his examples and the jargon he used demonstrated exactly why so few Rubyists take his point seriously. It is possible to write modular code in Ruby with the same level of flexibility but with far less code and fewer concept hoops to jump through.

Let's take a look at the problem Rob was trying to solve:

module Teacher
  def initialize
    puts "initializing teacher"
  end
end

class Person
  include Teacher

  def initialize
    puts "initializing person"
  end
end

# Desired output:
# > Person.new
# initializing teacher
# initializing person

This is a classic problem involving modularity. In essence, Rob wants to be able to "decorate" the Person class to include teacher traits.

Nick's response would have been to create a factory that creates a Person proxy decorated with Teacher properties. And he would have been technically correct, but that description obscures the Ruby implementation, and makes it sound like we need new "Factory" and "Decorator" objects, as we do, in fact, need when programming in Java.

In Ruby, you'd solve this problem thusly:

# The base person implementation. Never instantiate this.
# Instead, create a subclass that mixes in appropriate modules.
class AbstractPerson
  def initialize
    puts "Initializing person"
  end
end

# Provide additional "teacher" functionality as a module. This can be
# mixed into subclasses of AbstractPerson, giving super access to
# methods on AbstractPerson
module Teacher
  def initialize
    puts "Initializing teacher"
    super
  end
end

# Our actual Person class. Mix in whatever modules you want to
# add new functionality.
class Person < AbstractPerson
  include Teacher
end

# > Person.new
# Initializing teacher
# Initializing person

Including modules essentially decorates existing classes with additional functionality. You can include multiple modules to layer on existing functionality, but you don't need to create special factory or decorator objects to make this work.

For those following along, the classes used here are "factories", and the modules are "decorators". But just as it's not useful to constantly think about classes as "structs with function pointers" because that's historically how they were implemented, I'd argue it's not useful to constantly think about classes and modules as factories and decorators, simply because they're analogous to those concepts in languages like Java.

The Case of the PerQueryTimingFactoryFactory

Nick's example is actually a great example of a case where modularity is important. In this case, he has a base Query class that he wants to extend to add support for timeouts. He wrote his solution in Scala; I'll transcode it into Ruby.

Feel free to skim the examples that follow. I'm transcoding the Scala into Ruby to demonstrate something which you will be able to understand without fully understanding the examples.

class QueryProxy
  def initialize(query)
    @query = query
  end

  def select
    delegate { @query.select { yield } }
  end

  def execute
    delegate { @query.execute }
  end

  def cancel
    @query.cancel
  end  

  def delegate
    yield
  end
end

Then, in order to add support for Timeouts, he creates a new subclass of QueryProxy:

class TimingOutQuery < QueryProxy
  def initialize(query, timeout)
    @timeout = timeout
    @query   = query
  end

  def delegate
    begin
      Timeout.timeout(@timeout) do
        yield
      end
    rescue Timeout::Error
      cancel
      raise SqlTimeoutException
    end
  end
end

Next, in order to instantiate a TimingOutQuery, he creates a TimingOutQueryFactory:

class TimingOutQueryFactory
  def initialize(query_factory, timeout)
    @query_factory = query_factory
    @timeout = timeout
  end

  def self.call(connection, query, *args)
    TimingOutQuery.new(@query_factory.call(connection, query, *args), timeout)
  end
end

As his coup de grâce, he shows how, now that everything is so modular, it is trivial to extend this system to support timeouts that were per-query.

class PerQueryTimingOutQueryFactory
  def initialize(query_factory, timeouts)
    @query_factory = query_factory
    @timeouts = timeouts
  end

  def self.call(connection, query, *args)
    TimingOutQuery.new(@query_factory.call(connection, query, *args), @timeouts[query])
  end
end

This is all true. By using factories and proxies, as you would in Java, this Ruby code is modular. It is possible to create a new kind of QueryFactory trivially.

However, this code, by tacking close to vocabulary created to describe Java patterns, rebuilds functionality that exists natively in Ruby. It would be equivalent to creating a Hash of Procs in Ruby when a Class would do.

The Case: Solved

Ruby natively provides factories, proxies and decorators via language features. In fact, that vocabulary obscures the obvious solution to Nick's problem.

# No need for a proxy at all, so we skip it

module Timeout
  # super allows us to delegate to the Query this
  # module is included into, even inside a block
  def select
    timeout { super }
  end

  def execute
    timeout { super }
  end

  # We get the cancel delegation natively, because
  # we can use subclasses, rather than separate
  # proxy object, to implement the proxy

private
  # Since we're not using a proxy, we'll just implement
  # the timeout method directly, and skip "delegate"
  def timeout
    # The Timeout module expects a duration method
    # which classes that include Timeout should provide
    Timeout.timeout(duration) do
      yield
    end
  rescue Timeout::Error
    cancel
    raise SqlTimeoutException
  end
end

# Classes in Ruby serve double duty as "proxies" and
# "factories". This behavior is part of Ruby semantics.
class TimingOutQuery < Query
  include Timeout

private
  # implement duration to hardcode the value of 1
  def duration
    1
  end
end

# Creating a second subclass of Query, this time with
# per-query timeout semantics.
class PerQueryTimingOutQuery < Query
  TIMEOUTS = Hash.new(0.5).merge("query1" => 1, "query2" => 3)

  include Timeout

private
  def duration
    TIMEOUTS[query]
  end
end

As Nick would point out, what we're doing here, from a very abstract perspective, isn't all that different from his example. Our subclasses are proxies, our modules are decorators, and our classes are serving as factories. However, forcing that verbiage on built-in Ruby language features, in my opinion, only serves to complicate matters. More importantly, by starting to think about the problem in terms of the Java-inspired patterns, it's easy to end up building code that looks more like Nick's example than my solution above.

For the record, I think that designing modularly is very important, and while Ruby provides built-in support for these modular patterns, we don't see enough usage of them. However, we should not assume that the reason for the overuse of poor modularity patterns (like alias_method_chain) result from a lack of discussion around proxies, decorators, and factories.

By the way, ActionController in Rails 3 provides an abstract superclass called ActionController::Metal, a series of modules that users can mix in to subclasses however they like, and a pre-built ActionController::Base with all the modules mixed in (to provide the convenient "default" experience). Additionally, users or extensions can easily provide additional modules to mix in to ActionController::Metal subclasses. This is precisely the pattern I am describing here, and I strongly recommend that Rubyists use it more when writing code they wish to be modular.

Postscript: Scala

When researching for this article, I wondered why Nick hadn't used Ruby's equivalent to modules (traits) in his examples. It would be possible to write Scala code that was extremely similar to my preferred solution to the problem. I asked both Nick and the guys in #scala. Both said that while traits could solve this problem in Scala, they could not be used flexibly enough at runtime.

In particular, Nick wanted to be able to read the list of "decorators" to use at runtime, and compose something that could create queries with the appropriate elements. According to the guys in #scala, it's a well-understood issue, and Kevin Wright has a compiler plugin to solve this exact problem.

Finally, the guys there seemed to generally agree with my central thesis: that thinking about problems in terms of patterns originally devised for Java can leave a better, more implementation-appropriate solution sitting on the table, even when the better solution can be thought of in terms of the older pattern (with some contortions).