Friday, April 10, 2009

Rails has no application context

Something my developers have been complaining about lately is the difficulty in creating a generic dependency injection framework for a project with a lot of different plug-ins. I am asserting that there should be a generalized mechanism to set properties on a Rails domain model, rather than allowing plug-ins unholy access to reek havoc on my model objects.

We have a use case where we want to be able to inject different implementations for the same interface. The idea is that we are using attachment_fu to decorate models, but we want to be able to store attachments in a different persistence mechanism (currently local filesystem or Amazon S3). We've implemented a generic framework as an engine that depends on attachment_fu to handle the attachments. For a given client project, we'd like to able to inject the implementation for S3 or filesystem persistence, but since attachment_fu totally hacks our objects to bits, we have to write some exotic and smelly code to configure a given model to do what we want.

This is not the first time they've lodged this complaint, which is more or less that no matter how much (by much I mean well-defined, encapsulated, well designed OO etc) of an interface one of our in-house APIs surfaces, we are always already limited by the hackers who infiltrate our code with no contract. Plugins like attachment_fu offer no contract, they simply save the desperate ruby hacker from having to write lines of code, but the end result is a totally non-transferable hunk of smelly spaghetti code.

Perhaps Rails just doesn't support our over-arching design goal, which is the ability to write one piece of code to handle the use cases all of our clients ask for on every project we do. Maybe it's faster to just re-write the same code over and over. I called this article "Rails has no application context" because I am used to working in an environment like Spring where I can request any implementation I want through the application context. Objects communicate through an interface and there is no shared knowledge of implementation details. I want this same kind of service bus in Rails, where objects can register themselves and be available, and other objects can request an implementation of an interface and know that the required operations (eg save to storage mechanism) will be available without having any idea about implementation details of each other. Jaroslav Tulach calls this a "Teleinterface" which I think describes this situation accurately.

I've proposed to my development team that we create some kind of proxy for attachment_fu to rape as the sacrifical vigrin, rather than treat our whole domain model as an open harem. We must repel the godless Romans from our code. We can create some kind of proxy object that gets wired up with the attachment_fu hooks, and this object can then act as an API for our code to consume.

No comments: