The Proxy Pattern in Ruby
My new favorite idiom to use in Ruby is the proxy pattern. But I’m not using it to reduce the memory footprint or to manage some complex resource. I’m using it to keep the original class clean of my torrent of methods and to make my API easy to use.
One of the biggest problems with adding methods to a core class is the chance of collision with other libraries or code, in order to keep this chance low you don’t want to flood classes with new methods. Imagine we want to perform some textual operations on a String, for example ‘Textilize’ it.
require 'redcloth'
RedCloth.new('h1. Proxies!').to_html
But that doesn’t look very nice, especially if we have to duplicate it. Duplication will get us in trouble when we want to change the parameters we send to RedCloth or when we want to switch to another textile processor.
class Formatters
def self.textilize(str)
RedCloth.new(str).to_html
end
end
Formatters.textilize 'h1. Proxies!'
Even though we packaged the code nicely in a method, I’m still not satisfied. This will require us to type that long classname before every call to textilize. And what if we want to perform a number of operations on the same string? Brackets will have to come into play and Lisp our code.
module Formatters
def textilized
RedCloth.new(self).to_html
end
def unnewlined
self.gsub!(/\r\n/, "\n")
end
end
String.send :include, Formatters
"h1. Proxies!\r\n".unnewlined.textilized #=> "<h1>Proxies!</h1>"
Very nice! But there are still some problems with this solution. The formatting methods might accidentally override other methods on String, especially if we define even more of these formatting methods. Even though the methods work on the string, they don’t really have much to do with the String class.
This is where the proxy class comes into the picture, it will only take one method on the String class and work as a portal to all our formatting code. In the process the proxy class is even going to make our solution pluggable.
require 'rubygems'
require 'redcloth'
module Formatting
class Formatter
def initialize(str)
@str = str
end
def to_str
@str
end
alias_method :to_s, :to_str
end
module StringExtension
def format
Formatting::Formatter.new self
end
end
def register(mod)
Formatting::Formatter.send :include, mod
end
module_function :register
end
module Formatting::Textilize
def textilized
@str = RedCloth.new(@str).to_html
self
end
end
Formatting.register Formatting::Textilize
module Formatting::UnNewline
def unnewlined
@str.gsub!(/\r\n/, "\n")
self
end
end
Formatting.register Formatting::UnNewline
String.send :include, Formatting::StringExtension
See how the well placed self
enables us to to chain methods?
"h1. Proxies!\r\n".format.unnewlined #=> #<Formatting::Formatter:0x1087c4c @str="h1. Proxies!\n">
"h1. Proxies!\r\n".format.unnewlined.textilized.to_s #=> "<h1>Proxies!</h1>"
There we go, a nice proxy implementation with all the benefits of the original solution. Unfortunately there are some drawbacks, instead of a String instance the methods return a Formatter instance. The to_str
keeps us safe in most cases, like concatenation, but not in all cases. Further cloaking measures are left as an exercise for the reader (hint: method_missing, the boat and Comparable).