a work on process

Viewing posts tagged: MVC

The Joy of Separation

28 March 2007 (3:58 pm)

By James Stewart
Filed under: Notes
Tagged: , , ,

The thing I probably enjoy most about working with Rails is that it makes it easier to put logic in the right place than not to. To get back in the swing of blogging after a light couple of weeks, I thought it might be useful to run through an example of how that’s worked out for me recently.

From time to time I’ve been working on an application that will contain a database of musicians. To be able to add new musicians quickly I’ve been using:

new_artist = Artist.find_or_create_by_name('My New Artist')

So far, that’s worked fine. But as any self-respecting music fan knows there are always variations in how artists are referred to. What do we do if we need to represent, say, Black Rebel Motorcycle Club, aka BRMC, aka B.R.M.C. Or, even worse, Guns-n-Roses, and their many variations.

Thankfully, there’s musicbrainz which can provide a list of aliases. So once I have a table of aliases linked to artists, I can simply add a quick convenience method to the Artist model and call:

new_artist = Artist.find_or_create_by_name_or_aliases('My New Artist')

It looks a little like:

def self.find_or_create_by_name_and_aliases(name)
  artist = Artist.find_by_name(name)
 
  if artist.nil?
    aliased = Alias.find_by_alias(name)
    artist = aliased.artist if aliased
  end
 
  artist ||= Artist.create(:name => name)
end

In reality, it’s a little more work than that as the alias data has to be imported, and for performance reasons it’s probably best not to hold up every insert of a new artist to give us time to hit musicbrainz. But it’s pretty easy to create the artist up front and then either trigger a backgroundrb task or leave the cleanup for a rake task. I added another method on my Artist model to be used when cleaning out duplicates. It runs through the relationships and reassigns them (in reality, I’d do a bit more testing to make sure I’m not creating duplicated relationships), creates the alias, and then gets rid of the duplicate:

def convert_to_alias(duplicate)
  duplicate.appearances.each do |appearance|
    appearance.update_attributes(:artist_id => self.id)
  end
 
  artist.aliases.create(:alias => duplicate.name)
  duplicate.destroy
end

So the domain remains neatly self-contained and the controllers or other interfaces need only know a little bit about it. The code is easy to test, and easy to work with. And it’s less work than doing it any other way. For a little more on that way of working I’d highly recommend Jamis Buck’s post Skinny Controller, Fat Model.

Recommend this post:

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

 

Musings on Application Structure

22 April 2005 (11:08 am)

By James Stewart
Filed under: Software Design
Tagged: , ,

Among other things, I’m currently working on a relatively large CMS/membership site. At the project’s initiation I decided to steer clear of off-the-shelf solutions, partly because I wanted to take myself through the learning curve that the project was sure to involve, partly because none of the CMS or framework options I looked at really appealed to me, and partly because it seemed as though the customisations required to manage this dataset would take nearly as much time as a from-scratch solution.

So far, the development has gone pretty smoothly, but for the past couple of days I’ve been revisiting a few early design decisions with the hope of making the site more flexible. Since google has yet to provide much of use when it comes to this aspect of PHP and Design Patterns, I’m going to try and document my thinking here.

This is by far the largest project I’ve undertaken since developing an awareness of MVC and other design patterns, and I’ve been trying to toe the fine-line of being informed but not defined by them. The largest challenge so far seems to have come as I try to loosen the ties between the layers.

I want to make it easier to add alternative representations of various resources. In some cases, that means atom feeds, in others it may mean FOAF or other RDF representations, and right now it means reusing View classes within the management system. I’ve spent most of the morning reworking my Dispatcher/Controller, and building some sample classes to work with it, and what I have at present is:

  1. Dispatcher uses switch statement to call Controller which builds a model. There are a number of generic model classes, as well as a few specific-use ones
  2. Dispatcher queries request to see if we’re asking for a particular representation, defaulting to XHTML
  3. Dispatcher calls a factory to retrieve a View object, passing the type and a reference to the model
  4. Factory first checks for generic model types and then for specifics, and returns an appropriate View
  5. Dispatcher asks View to produce the output

This is largely achieving the required result, but it’s relying on several large ’switch’ statements. There’s one in the dispatcher to choose a Controller/Model, there’s one to check the request type, and there’s one to select the appropriate View. I’m trying to work out whether there’s a good way to combine some of that logic, and thereby make it easier to add new Views and new actions.

When it comes to location-awareness, my hunch is that I’d be best off doing some parsing of the referer and the request_uri to work out where we are, and who called us, and then allowing each View to apply its own logic to decide on a template to show and a place to return the user to. Here, I need to work out how best to do that while keeping the system as portable as possible and ensuring code-reuse.

Recommend this post:

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]