Saturday, August 18, 2007

Three Foibles in Rails' MVC

There are three things I've run into which are kind of nagging at me in Rails, three flaws in the logical purity of Rails' MVC implementation.

1. The Presenter pattern
2. Ajax MVC
3. ActiveResource model/controller confusion

Presenters

Presenters are pretty much necessary in Rails apps of a certain level of complexity, and that level doesn't even necessarily have to be very high. I'm using a presenter in an app I'm working on now. I created a Struct, added HTML-generating methods to it, and got a ton of flexibility that I really would have had to otherwise use Seaside to get. But it's living in the /models dir, when it should really be under /presenters - or possibly /components, if /components wasn't already used for something else - and to bring it under test, I had to manually load its "fixtures" from the YAML file directly, since Rails' fixtures assume your fixture objects are ActiveRecord objects. (This raises questions about unit testing ActiveResource models, but that's a whole 'nother ball of wax.)

Ajax

MVC was originally created to handle GUIs. A sufficiently complex Ajax GUI justifies MVC, even if it's just a tiny implementation. Sometimes a nanoframework is better than a framework; design patterns don't have to be complicated in their implementation to be useful. They just have to be clean and powerful. But I had one project which not only justified an Ajax MVC implementation in JavaScript, in addition to the back-end Web MVC implementation in Rails, it also absolutely needed an ActionScript MVC implementation as well, to support a complex set of options in a Flash component of the UI, which needed to interact both with the browser's Ajax and DOM, and with Rails. Coordinating two distinct simultaneous MVC implementations challenges you; coordinating three gets really hard.

ActiveResource

Concerning ActiveResource, you define all your API methods in the controller, not the model. But insofar as ActiveResource seems like a networked version of ActiveRecord, putting the definition for something like Widget.get(:stuff) in the controller instead of on the model really breaks my habits and assumptions. I know it matches official REST dogma, but personally, it drives me fucking nuts. It just seems so counter-intuitive. I don't know if other developers expect to be able to simply substitute ActiveResource for ActiveRecord without thinking about it any further than that. That's certainly what I want to do, but maybe that's just me. Certainly Rails is still a long way from enabling that, and may never go in that direction, but from the standpoint of ease of development and simplicity of migration, it would be really cool if it did.

So what?

Obviously, the goal of Rails isn't to reflect the magical purity of a holy logical thought-form. The goal of Rails is to get stuff done. But the clarity of its original MVC model is a big part of its success and its elegance.

Even given the powerful personality of its chief kahuna, Rails is a community project, and the community's both huge and enthusiastic. Rails' adoption curve is crazy. As people are using Rails in all kinds of contexts beyond the one it was originally designed for, it's taking on all kinds of alternative dimensions. The bigger and more ambitious Rails gets, the more valuable its simplicity and elegance will become. That's the whole point of elegance: you reduce unmanageably complex detail to a few simple ideas.

Anyway, these are really just some things bugging me slightly, things you might have to work around in certain situations, potential pitfalls and gratuitous gotchas of the most abstract, theoretical kind.

2 comments:

  1. "I don't know if other developers expect to be able to simply substitute ActiveResource for ActiveRecord without thinking about it any further than that. That's certainly what I want to do"

    Sounds like a design pattern to me.

    ReplyDelete
  2. Would be interested in hearing more about how you implemented the presenter. Struggled with something similar when generating HTML/FBML for a Facebook API call, which is triggered by an observer.

    In the end I implemented it in a FacebookTemplate class which calls ActionView the same way that ActionMailer does. Means you can use render and partials, but I'm not 100% convinced yet it's the right solution.

    ReplyDelete

Note: Only a member of this blog may post a comment.