renderer.url(some_model_object)Interesting opportunities does open up to tie this to the routing and still relatively loosely couple the classes. In fact, I would argue that a method like this reduces coupling between the controller and views: If you put URLs in your views, and you change the routing or what aspects are handled by what controllers, the URLs may change and the views may have to change with them. Contain the URLs in a rendering object passed from the controller, and you centralize those changes. If the routing mechanism is designed to do it, it could conceivably provide a rendering class if the routing information is extended somewhat. On this blog "/tag/(.*)" maps to the Tag model class, and the (.*) group refers to the "name" attribute of the Tag model. If the router instead of mapping /tag/(.*) only to a controller, also contained the knowledge of which model and attribute the static and dynamic parts of the URL map to (if any), then the rendering could be automated. Arguably, the router is part of the controller infrastructure (technically you'd call it a front controller) and centralizing limited knowledge of the model there is not a big concern, as long as this knowledge is provided by the application and not encoded in the routing component itself. To take an example from my current router for this blog, suppose I turned this:
map '/tag/(.*)', C::TagController map '/(.*)', C::ShowItem.. into this:
map '/tag/(.*)', C::TagController, M::Tag, :name map '/(.*)', C::ShowItem, M::Item, :url.. to denote that our hypothetical renderer should turn renderer.url(some_item_object) into "/#{some_item_object.url}" and renderer.url(some_tag_object) into "/tag/#{some_tag_object.name}" An alternative would be to change from straight regexps to something requiring a bit more parsing:
map '/tag/{M::Tag.name}', C::TagController... which also opens up the possibility of passing an already instantiated model object in to the controller. The downside of that route is that it requires the dispatcher to know how to create model objects, which either means it needs a factory or it would become tied to a specific mechanism for creating model objects. WIth that choice in mind the earlier syntax example seems more attractive - you trade a little bit of simplicitly in the routing for a whole lot reduction in coupling.
More people are getting exposed to MVC with web applications programming, but not everyone are making it a clean separation. Much of the benefit of MVC comes from making sure all logic is truly isolated. The article - an interview with Terence Parr, creator of ANTLR and the StringTemplate library - is interesting, but it's the paper referenced at the end of the article that really makes it worth it: "Enforcing Strict Model-View Separation in Template Engines"
I must admit I have sinned in this respect too and relaxed separation more than I should. It is extremely tempting to relax the rules and do what amounts to computation of business rules in the view. The sins may seem small - it's only a matter of encoding some minor rule here and there. But it adds up, and before you know if you need to change a rule and are chasing all over the place.
My first major web app, a Webmail application that we scaled to about 1.7 million accounts back in 99/00, used strict separation, very much in keeping with Terence Parr's points. I built a template engine that specifically only allowed variable substitution, list iteration/expansion, and simple if/else with boolean values. It was a design choice, and one that was constantly fought by the engineering team, probably in part because I was not good enough at explaining the value. At the end of our use of the system (before we sold off the e-mail system) a couple of the guys even made a version that allowed them to embed Perl in the templates, completely circumventing the design goals.
Later, I've used more powerful solutions such as XSL templates, with part of the intent being that a lot of the bad logic is incredibly hard to do in XSL, so (the theory went) people would hopefully avoid doing it and handle the logic in the model where it belonged (though the separation in the code might have been ugly and badly managed). It didn't work. The templates grew incredibly complex because people insisted on trying to shoehorn logic into the templates that didn't belong there.
Terence Parr's notion of a separate "Renderer" layer - in StringTemplate's case using toString() to contain logic relating to the formatting of a single attribute - is interesting, and would likely have done a lot to clean that code up. He also has a good solution to the one value - multiple renderings problem: Wrap the objects in objects handling the rendering.
I'll certainly take a close look at StringTemplate and see whether it's reasonable to make a Ruby-ish version.
E-mail: vidar@hokstad.com
Skype: vhokstad
View my LinkedIn profile
I was born April 21st, 1975, in Oslo, Norway. Since 2000 I've been living in London, UK. I'm married.
I'm working for Aardvark Media as Director of Technology. I'm also currently on the board of SpatialQ, a startup in the GIS space, and an advisor to Skoach, a startup doing a time management app for people with ADD.