Saturday, April 21, 2007

Facade To The Rescue

Eddie talked in detail about Facade in this blog before. I would like to provide a couple of examples how that great plugin helps with writing runtime-specific implementations.

User Story: Jim's AWS S3 plugin (test/development/production all use different implementations)

For his rails application, Jim wrote a plugin that allows storage on Amazon S3 (not knowing there is already a very good one). Not wanting to hit the service in test and development mode, he is using mocks with Hash storage for tests, and ActiveRecord for development. He put the mocks in corresponding app/mocks directories and everything works as expected. There is something bothering him though. First, he does not like an idea that various implementation of the same functionality are scattered across the application structure. He would prefer to have a hierarchy that indicates an implementation type (i.e. mocks, active_record, service). More importantly, he is going to release the plugin on rubyforge so he needs a clean way to package all implementations with it. He read about Facade so he decided to give it a try.

First, Jim creates an implementations hierarchy in his plugin:

vendor
plugins
aws_s3
lib
s3.rb
service
s3.rb
active_record
s3.rb
mock
s3.rb



Then, he adds facade configuration (vendor/plugins/aws_s3/config/facade.yml):

production:
s3:
backed_by: service

development:
s3:
backed_by: active_record

test:
s3:
backed_by: mock


He modifies the top s3.rb to define the interface:

class S3 < Facade::Base
backend_class_method :create, :find
end


No changes required for implementation classes, so for example his test mock stays as it was:
class Mock::S3

@@storage = {}

def self.create(key, data)
@@storage[key] = data
end

def self.find(key)
@@storage[key]
end

end


Jim extracts a plugin, packages it, and publishes it to rubyforge for public consumption.


User Story: Betty's Crypto Library (packaging gem with its own mock implementations but allowing an override in applications)

Betty works for a large healthcare provider with many internal projects embracing Rails for web-based implementations (now you know this is a fictional story ;-) ). They lack a strong crypto library they can reuse across applications to keep them compliant with HIPPA regulations. Betty has experience with RubyInline C so she volunteers to write one. She releases it as a gem to an internal gem server together with a Facade based mock implementation using ROT13 instead of strong encryption, so rails developers have an easy way to recover encrypted data in unit tests. The gem contains config directory with facade.yml:

production:
healthcare/scrypto:
backed_by: healthcare/live

development:
healthcare/scrypto:
backed_by: healthcare/live

test:
healthcare/scrypto:
backed_by: healthcare/mock

Joe is a developer of a small internal application that utilizes the crypto library gem. He found that, since requirements for the library was to withstand to a brute-force attack by an average NSA computer for at least 48 seconds, the library is very CPU demanding and it makes development on his old iBook unbearably slow. Joe decides to override the development mode facade configuration for the gem so it falls back to the mock implementation for both development and testing. He adds facade.yml to his application's config directory with a single entry:

development:
healthcare/scrypto:
backed_by: healthcare/mock

Since Facade uses ConfigurationLoader for configuration files loading, application config is being merged with the one enclosed with the gem, thus providing a customized behavior for Joe's application.

No comments: