Showing posts with label facade. Show all posts
Showing posts with label facade. Show all posts

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.

Saturday, March 31, 2007

[PLUGIN RELEASE] - Facade

Due to a slew of interesting runtime dependencies, we needed to be able to quickly mock out thorny pieces of our architecture.

  • I might not want to run a huge MOA stack to develop software that touches some arcane piece of our application that happens to use messaging.
  • I might want to hit a fully-functional mock (e.g. built in Active Record) instead of hitting live services that we don't own or might cost money.
  • I might want to crank out a naive implementation of a particular data source/sink with the intent of putting in a more 'industrial' solution if the product is a success.

Facade is a library for modeling objects that delegate some or all of their behavior to a surrogate class. It's essentially a DSL for describing a delegation arrangement, as well as a mechanism to bind a particular implementation to those delegator models. It's a little bit of dependency injection mixed with a little bit of IDL.

It also was a relatively enjoyable exercise in Ruby metaprogramming; this would have been quite painful in a few other languages.

Don't worry, it sounds much worse (and Java-esque) than it really is. Actual usage is pretty straightforward and doesn't create a lot of senseless abstraction.

Here's a little self-explanatory peek into the three pieces of the puzzle (the configuration, the facade model, and the implementation model(s).)

Facade model:

class Foo < Facade::Base
backend_class_method :delegated_class_method, :delegated_returns_a_collection_of_self
backend_instance_method :delegated_instance_method
def local_method
puts 'I do stuff that is indepenent of implmentation of delegated stuff'
end
end


Mock delegatee:

class Mock::Foo
def delegated_instance_method
puts 'I performed a mock implementation of my_instance_method'
end
def self.delegated_class_method
puts 'I performed a mock implementation of my_class_method'
end
def self.delegated_returns_a_collection_of_self
return [self.new, self.new]
end
end


Live delegatee:

class Live::Foo
def delegated_instance_method
puts 'I performed a live implementation of my_instance_method'
end
def self.delegated_class_method
puts 'I performed a live implementation of my_class_method'
end
def self.delegated_returns_a_collection_of_self
return [self.new, self.new]
end
end


Config file:

development:
foo:
backed_by: mock
production:
foo:
backed_by: live


The mock/live distinction isn't important--you could just as well have two mock implementations or three live implementations. It's just the name of the module that holds the implementation.

The unique thing about Facade (vis-a-vis other mocking solutions) is its autoboxing support. This allows delegatees to return instances of the delegator without even being aware that its a delegatee (in fact, the delegatee may be a class that you didn't even write.) Whenever instances of the delegatee are returned from a delegated method, an instance of the delegator is returned to the invoker instead. The reason we chose autoboxing over duck-typing is that you want to keep a bulk of the functionality in the implementation-agnostic facade model, and those methods would not be available if we simply returned references to the delegatee whenever the delegatee returned references of itself.

Example: If model Foo contains lots of AR-agnostic business logic but happens to delegate a couple of methods to its AR-backed delegatee--say, for persistence support--we want the results of those ActiveRecord finders to be instances of Foo < Facade::Base, not instances of ArModels::Foo < ActiveRecord::Base, or else we won't be able to invoke Foo's instance methods on the objects in the collection.

Here's a little console session that illustrates the usage of the models shown above:


epf-lap:/tmp/facade_hello_world $ ruby script/console
Loading development environment.
>> Foo
=> Foo
>> Foo.my_class_method
I performed a mock implementation of my_class_method
=> nil
>> foo = Foo.new
=> #<Foo:0x27bb584 @facade_backend=#<Mock::Foo:0x27bb55c>>
>> foo.my_instance_method
I performed a mock implementation of my_instance_method
=> nil

epf-lap:/tmp/facade_hello_world $ ruby script/console
Loading development environment.
>> foo = Foo.new
=> #<Foo:0x27c370c @facade_backend=#<Mock::Foo:0x27bf1c0>>
>> foo.delegated_instance_method
I performed a mock implementation of my_instance_method
=> nil
>> foo.local_method
I do stuff that is indepenent of implmentation of delegated stuff
=> nil
>> Foo.delegated_class_method
I performed a mock implementation of my_class_method
=> nil
>> Foo.delegated_returns_a_collection_of_self
=> [#<Foo:0x27ac64c @facade_backend=#<Mock::Foo:0x27ac728 @my_facade_model=#<Foo:0x27ac64c ...>>>, #<Foo:0x27ac50c @facade_backend=#<Mock::Foo:0x27ac714 @my_facade_model=#<Foo:0x27ac50c ...>>>]
>> ret_foo = Foo.delegated_returns_a_collection_of_self.first
=> #<Foo:0x279c454 @facade_backend=#<Mock::Foo:0x279cbe8 @my_facade_model=#<Foo:0x279c454 ...>>>
>> ret_foo.class
=> Foo
>> ret_foo.delegated_instance_method
I performed a mock implementation of my_instance_method
=> nil
>> ret_foo.local_method
I do stuff that is indepenent of implmentation of delegated stuff
=> nil


epf-lap:/tmp/facade_hello_world $ ruby script/console production
Loading production environment.
>> # notice it's using the other implementation now that we're in prod mode
>> Foo.delegated_class_method
I performed a live implementation of my_class_method
=> nil
>> # and you can dynamically change the implementation, complete with autobox
?> Foo.reset_backend(Mock::Foo)
=> Mock::Foo
>> Foo.delegated_class_method
I performed a mock implementation of my_class_method
=> nil
>> Foo.delegated_returns_a_collection_of_self.first.class
=> Foo
>> Foo.delegated_returns_a_collection_of_self.first.delegated_instance_method
I performed a mock implementation of my_instance_method


Check out the README for information on installation (it's simple!) and getting started.

Readme: svn cat \
svn://rubyforge.org/var/svn/facade/trunk/vendor/plugins/facade/README
Repository: svn checkout \
svn://rubyforge.org/var/svn/facade/trunk/vendor/plugins/facade
Rubyforge: http://rubyforge.org/projects/facade/

Installation:

ruby script/plugin install \
svn://rubyforge.org/var/svn/facade/trunk/vendor/plugins/facade

ruby script/plugin install \
svn://rubyforge.org/var/svn/config-loader/trunk/vendor/plugins/configuration_loader