The Joy of Separation

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.

Tags: , , ,

Comments are closed.