Copland an IOC Containers in Real Use

Hi,

every so often I go and read the wonderful stuff, that Jamis has put together for copland (http://copland.rubyforge.org). Everything makes sense to me so far and the examples are simple enough to follow how it works.

Still I just don't get it. I still can't grasp what it is that I get, when I use copland. So my question to you, fellow rubyists: Is someone out there actually using it for a real world project? Would care and enlighten me why you chose copland and how it improves your project?

Yes and thanks to Jamis for making such a nice Product even if it's way over my head :slight_smile:

Cheers,
Carsten.

Carsten Eckelmann wrote:

Hi,

every so often I go and read the wonderful stuff, that Jamis has put together for copland (http://copland.rubyforge.org). Everything makes sense to me so far and the examples are simple enough to follow how it works.

I'm so glad to hear that the documentation is readable. :slight_smile:

Still I just don't get it. I still can't grasp what it is that I get, when I use copland. So my question to you, fellow rubyists: Is someone out there actually using it for a real world project? Would care and enlighten me why you chose copland and how it improves your project?

This is a common complaint that I hear about IoC containers in general, and Copland specifically. Especially in Ruby. Given Ruby's dynamic nature, I will confess that an IoC container does not buy you as much functionality as in a language like, say, Java. However, I have found that for large projects, Copland *can* make life easier. I'll say it again: *for large projects, Copland can make life easier.* Copland will buy you very little for small projects, and can in fact overcomplicate things if the project is of insufficient scope.

One more caveat before I launch into my use case: everything I use Copland for, can *definately* be done in another way in Ruby. I just know I'm going to get someone saying "but you can do that in Ruby by doing X, instead of using Copland!" 'Tis true. However, Copland brings together lots of interesting features in one place, and implements them for you. Why not take advantage of an existing project, instead of reinventing the wheel? :slight_smile:

So, on to my use-case:

I'm (off-and-on) working on a personal finance manager, written in Ruby, using ActiveRecord for the OR mapping and some to-be-determined WEBrick-based front-end. This is one of the larger-scale apps I've implemented (by myself--for work I fry much larger fish, but on a team). For me, in this app, Copland fills the following niches:

1) Debugging. By using logging interceptors, Copland will automatically log whenever a method of a service is invoked, what it returned, and whether it threw an exception or not. This removes a lot of boilerplate code from my app. What is more, the interceptors are "attached" to services via configuration files, which means I can easily remove them (or reattach them) without the possibility of accidentally deleting crucial LOC. :slight_smile: I've done it before... it hurts.

2) Delayed Instantiation. This is of dubious usefulness. I like it, others may not. What it means is, a service is not instantiated until a method is actually invoked on it. For services that are expensive to instantiate, this can save some cycles, especially if the service your request may not actually be used for some paths through your code. (I should note: the delayed instantiation feature is configurable. You can define services in such a way that they will always be instantiated immediately, if you prefer.)

3) Dependency Injection. By putting most of my functionality in various Copland services, I no longer have to explicitly instantiate dependencies between these services in code. Instead, I just specify the dependencies in the configuration files, and Copland "magically" determines the dependencies at instantiation time. It will instantiate dependant services, assign properties, etc, etc, all behind the curtains. This may make some people nervous, but it cleans up your code a lot more than you'd think. And clean code, is maintainable code.

Let me elaborate on #3 a bit more, since that's the one that took me the longest to grasp. Specifically, take the "Server" service in my PFM. It is simply a WEBrick server instance. In my configuration file, I specify that when the service is instantiated, it should be magically "hooked up" to a "Servlet" service. (The servlet is the WEBrick servlet that implements the front-end for the app.) The servlet service, in turn, may depend on other services, such as a "template" service for parsing and rendering templated data.

Copland, in this case, would process the dependency graph, and determine that it needs to first instantiate the "template" service, followed by the "servlet" service. Then it would assign the "template" service to some property of the servlet (as defined in the configuration file), or perhaps pass the template service as a parameter to the servlet's constructor. Once the servlet has been instantiated, Copland will instantiate the server.

When delayed instantiation is thrown into the mix, this becomes a bit muddier, since (for instance) the template service will not actually be instantiated until the servlet service tries to render a template...and the servlet service may not actually be instantiated until the server service tries to pass a request to it... But from the developer's perspective, it really doesn't matter (usually) when a service is instantiated, so long as it is instantiated in time to do what it is asked to do. And Copland makes sure that all happens.

Yes and thanks to Jamis for making such a nice Product even if it's way over my head :slight_smile:

You're welcome. :slight_smile: Thanks for asking the question: the fact that it needed to be asked just shows that there remain some pretty significant holes in my documentation. *sigh* :wink:

ยทยทยท

--
Jamis Buck
jgb3@email.byu.edu
http://www.jamisbuck.org/jamis

ruby -ropenssl -e'k="01234567";p((c,c.padding,c.iv,c.key=OpenSSL::Cipher::BF.new,0,k,k*2)[0].decrypt.update("1A81803C452C324619D319F980D5B84DBB45FC0FE2BAA045".scan(/../).map{|n|n.to_i(16).chr}.join))'

I use a little IOC in java, and for me it was one of those
unexplainable "ah hah!" moments. I didn't think I needed it (and in
truth, you don't /need/ it..., but you don't /need/ dynamicity or OO,
either), but when I figured it out it became darned useful.