From: Jamis Buck Date: 2004-10-05T13:01:04+09:00 Subject: Ruby-esque Inversion of Control First, let me just say THANK-YOU to everyone who gave me feedback on my presentation on Sunday. Rich Kilmer's suggestion that I serialize to Ruby code, and various people (Austin, and various others that I can't recall immediately) that suggested I ditch the YAML configuration file, and Nathaniel Talbott who inadvertantly gave me some inspiration during his test/unit presentation... I've spent the last couple of days since my presentation thinking, and thinking, and thinking. I'm going to do a brain dump here, and since I know some of you have opinions on this matter, I hope you'll take a moment to let me know if you think I'm on the right track to ruby-izing Copland. :) First of all, I realized that configuration points are just services that implement a Hash-like or Array-like interface. Thus, there is no really compelling need to treat them differently. In fact, implementing them as service points allows you to use your own custom Hash-like or Array-like class instead of Hash or Array. Second, I think that Rich's suggestion of a Ruby-based domain-language is very applicable, and ties in nicely with the inspiration I got from Nathaniel's presentation. (Rich, any comments on the "domain language" demonstrated below would be appreciated.) Putting it all together, I'm thinking along the lines of the following code snippet for defining service points: registry = Copland::Registry.construct do package "calc" do service_point "Adder", Calc::Adder service_point "Subtractor", Calc::Subtractor service_point "Divider", Calc::Divider service_point "Multiplier", Calc::Multiplier service_point "Operations", Hash service_point "Calculator" do factory service { "copland.BuilderFactory" } implementor do klass Calc::Calculator parameters [ service { "calc.Operations" } ] end end with "calc.Operations" do |ops| ops[:add] = service { "calc.Adder" } ops[:subtract] = service { "calc.Subtractor" } ops[:multiply] = service { "calc.Multiplier" } ops[:divide] = service { "calc.Divider" } end end end calc = registry.service( "calc.Calculator" ) p calc.add( 8, 5 ) p calc.subtract( 8, 5 ) p calc.divide( 8, 5 ) p calc.multiply( 8, 5 ) It's still more verbose than I would like it, and there are problems remaining that the above does not solve, but as a first pass, I think it looks pretty nice. (Note that the "service {...}" syntax is necessary so that the string is interpreted as a service reference, and not as a string literal). For those of you that were vocal in your opposition to the external configuration file--how does the above strike you? What do you like/dislike about it? Other ideas: * Allow "package" to be optional. If omitted, service points are added to the global namespace. * Allow true nesting of namespaces, for hierarchical package definitions. Anyway, just getting my thoughts out. Comments would be greatly appreciated! - Jamis -- Jamis Buck jgb3@email.byu.edu http://www.jamisbuck.org/jamis