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. :)

> 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? :)

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. :) 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 :)

You're welcome. :) 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* ;)

-- 
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))'