Demeter: inviolable or too sexy to resist?

April 21st, 2008 by ymendel 5 comments »

Lots of people talk about the Law of Demeter and how important it is. Some deride it, some want to make it seem less stringent by calling it merely a “suggestion”. Me, I’m so familiar with it I call it “James”.

That’s a joke, son. Settle in ‘cause we’re just getting started.

There’s a lot I could link right here, but it takes a bit of effort to compile the best of the best. A good starting point is to go to Jay Fields’s blog and search for “demeter”. You’ll probably come back with anywhere from one quarter to one half of the entries. I’m just going to dive in to the real meat of this.

And so I introduce to you shmemeter. This came out of efforts started a few jobs ago when I ran up against limitations of the delegation built in to Rails. It’s great for a lot of things, but why should I have to write my own method if I’m going to delegate customer_name to customer.name? I would have rather used Forwardable, but there’s something to be said about going against convention and instead putting in a bunch of extend Forwardable calls.

More than wanting to change the target method, I wanted to have these “wrapper” methods, these demeter artifacts (or “demartifacts”?), these saviors-or-violations-depending-on-whom-you-ask be safe. I was working with optional associations and didn’t want to get NoMethodError if the association wasn’t set. Tell me what you’d rather write:

class User < ActiveRecord::Base
  delegate :username, :to => :login, :nil_is_okay_please_do_not_mess_me_up => true
end

or

class User < ActiveRecord::Base
  def username
    login && login.username
  end
end

Okay, maybe that option doesn’t have the best name. It’s a good thing I didn’t stick with it.

As I said, this started a few jobs ago, and I put it aside for a while. The latest job brought the concerns back into the forefront, especially when I was writing methods like

class Entry < ActiveRecord::Base
  def person_name
    person ? person.name : ''
  end
end

This is what BDD gives you. I very carefully planned out how I wanted Entry#person_name to be the name of the entry’s person and that it should be the empty string if there was no person. Good thing that screwed me out of delegation. In fact, each one of those desires screwed me out of delegation the way Rails does it, and put together they just laughed in my face.

With shmemeter, I can do something simpler and cleaner

class Entry < ActiveRecord::Base
  delegate :person_name, :to => :person, :as => :name, :missing_target => ''
end

That example shows both extra options in concert. They can be used separately, of course, with :as specifying the method to call on the target and :missing_target specifying the value to use if the target is nil.

Do what you want. Me, I say “demeter shmemeter”.

Obligatory github link: http://github.com/flogic/shmemeter/tree/master
Obligatory tracking link: http://tasks.ogconsultin.gs/projects/show/shmemeter

5 Responses to “Demeter: inviolable or too sexy to resist?”

  1. atmos Says:

    http://ozmm.org/posts/try.html

  2. Yossef Says:

    try doesn’t pass along arguments (bad for delegation) or allow the try-failed value to be anything other than nil

  3. atmos Says:

    In both of your examples the delegation is just syntactic sugar for wrapping a call around an object that could be nil. You could easily add a second parameter to try(with a default of nil) to return something custom.

  4. Yossef Says:

    That’s all delegate is for, though: syntactic sugar. Even were I to use try internally, I’d still want delegate around so I could easily create these methods, just like I prefer to use attr_accessor :name instead of defining two simple, repetitive methods myself.

    Or maybe I’m just misunderstanding your point.

  5. atmos Says:

    I guess I can see why you like this. Appending ’’ to every instance method call as a second param could get quite repetitive, but I do like the flexibility of being able to return values on a per instance basis. I guess I’ve just always underutilized delegate. :)

Leave a Reply