Expanding on the Law of Demeter

Recently, my friend Avdi Grim posted a great explanation of the Law of Demeter (LoD). It’s been tweeted and posted all over the web, so hopefully you saw it and read it. I’m totally in agreement with his conclusions in that post, and LoD is a code smell I see in every MVC project I have ever worked in.

I’m going to pick on Avdi’s post just a little, but this is more-so criticism of every LoD explanation I’ve ever read. I think all of these explanations of LoD are actually quite contrite and not addressing the real problem. Avdi (and others) seem to focus on “Structrual Coupling” or exposing the “internal” (actually external*) structure of your objects to other objects that don’t need to know it.

An Example:

def user_info(user) "Name: #{user.name}. Boss: #{user.department.try(:head).try(:name)}" end

Avdi does a beautiful job illustrating why this code is a problem but I want to go a little deeper.

The more insidious LoD violation see in codebases is what I’d call “behavioral” LoD violations, or, when the behavior of a class relies on the behavior of objects that are distant in the object graph.

My example needs a little setup. Lets say you have Vehicles which has_many Platforms which has_many Models which has_many Versions (and Platforms has many Version through models). Pretty simple 4 level deep object graph that a car manufacturer might have. A very common set of objects you might want is to know which Platforms are actually in production. Some our outdated, some are future, but you want all the platforms that are currently in production.

Now, I know how I’d do this, but I commonly see code like this:

class Vehicles
  def current_platforms(options = {})
    platforms = platforms.find(:all,
                        :conditions => 
                          ["platforms.on_date >= ? AND platforms.status IN ('open', 'cancelled') AND platforms.cached_inventory_status != ?",
                            Date.today.to_s(:db), 
                            Platform.cached_inventory_status(:no_inventory)],
                        :order => 'on_date asc, time_note asc',
                        :include => {:models => :versions})
  
    if options[:skip_some_special_case]
      platforms.select { |platform| platform.versions.any? { |offer| !version.special_case} }
    elsif options[:skip_some_other_case]
      platforms.select { |platform| platform.versions.any?(&:other_case?) }
    else
      platforms
    end
  end
end

This code is a great example of my problem with how most folks end up simplifying LoD violations. No where are we chaining methods “too deep” or as it gets simplified to are we “using too many periods”. We’ve only used 1 period (because of a has_many :through =>). However, in our if statements, we are tightly coupling our code to the behavior of a class that is 3 siblings away from the one we’re working in. Nasty. Never should a Platform do operations on, or even know about, a Version.

So why is my example actually worse than something.try(:something_else).try(:a_third)? Well, lets get back to that * I left on “(actually external [structure])”.

Most people seem to classify the active_record generated attribute accessors of Rails model classes as “internal structure” (Avdi doesn’t, I should state for correctness). These are most certainly external structure. You can override them and return whatever you’d like and attributes[:foo] is actually the “internal” accessor for all active_record classes. External structure, in every project I’ve ever worked in, has been the most consistent, least volatile part of the codebases. External structure almost *never* changes, and when it does, it’s typically *very* easy to change (it’s not even a refactoring, it’s just changing the messages exchanged between classes).

My example relies on behavior of a class 3 classes away. If there’s anything I’ve learned, it’s that behavior is always more volatile than structure, and as Avdi points out, LoD really starts hurting as code churns and whatever your LoD relies on changes and becomes more and more complex.

So the big question is, how do we fix this?

It’s actually a theoretically simple fix, and similar to the fixes in Avdi’s post: delegate the behavior down the object graph!

class Vehicle
  def current_platforms(options={})
    if options[:skip_some_special_case]
      platforms.reject(&:special_case?)
    elsif options[:skip_some_other_case]
      platforms.select(&:other_case?)
    else
      platforms.select(&:regular_case)
    end
  end
end

class Platform
  def regular_case
    ...
  end
  def special_case?
    models.special_case?
  end
  def other_case?
    models.other_case?
  end
end

class Model
  def special_case?
    versions.special_case?
  end
  def other_case?
    versions.other_case?
  end
end

class Version
  def special_case?
    ...
  end
  def other_case?
    ...
  end
end

Now, Platform knows nothing about versions, versions can easily change what defines the “cases” without any changes to Platform and ironically, when I made this refactoring to the original source I got this derived example from, it’s actually *more* efficient. Removing the explicit find that started the original method means that if we’ve already loaded this object graph, even outside of this method, we can keep reusing it without going to the database.

I hope this helps, and for the love of Demeter, can we stop describing it as “using too many periods”?

Pro Tip: Encapsulate logic in Rails Views for maintainability

A Rails view anti-pattern is that they very quickly turn into a nasty tangle of conditional html blocks.

Example:


<% if logged_in? && @user == current_user %>

Simple example, but what is the intention of that code? Why are you really checking for? Give that state a name.

Change that to:


def viewing_own_profile(user)
  logged_in? && user == current_user
end

<% if viewing_own_profile(@user) %>

Now you can test that helper method without resorting to rendering a view and parsing it to check if elements exist, the state you care about is named and the intention is clear.

From Zero to Rails Hero in 11 Easy Steps (Installing Rails on OS X 10.6.5)

This is the easiest, fastest and best-to-develop-in way to get a rails dev stack up and running on OS X 10.6 (Snow Leopard).

I actually find that it’s pretty easy, if everything is done in the right order.

  1. Install XCode
  2. Install Homebrew
    $ ruby -e "$(curl -fsSL https://gist.github.com/raw/323731/install_homebrew.rb)"
  3. Install git using homebrew
    $ brew install git
  4. Install mysql using homebrew
    $ brew install mysql
  5. Install rvm (and follow the instructions to set up bash and don’t forget to open a new terminal window)
    $ bash < <( curl http://rvm.beginrescueend.com/releases/rvm-install-head )
  6. Install Ruby Enterprise Edition
    $ rvm install ree
  7. Make REE the default Ruby
    $ rvm use ree --default
  8. Tell rubygems to not install rdocs
    Put the following lines in ~/.gemrc

    install: --no-rdoc --no-ri
    update: --no-rdoc --no-ri
  9. Install Passenger
    $ gem install passenger

    Now follow that up by running the install script (and follow the directions)

    $ ./passenger-install-apache2-module

    At the end of the install process it will give you instructions to add 3 lines to the “Apache configuration file”, but it won’t tell you *where* your apache config file is. It’s at /etc/apache2/httpd.conf

  10. Restart Apache
    At this point, if you’re using 10.6.5, you might do a sudo /etc/apachectl restart and run into the following error:
    /usr/sbin/apachectl: line 82: ulimit: open files: cannot modify limit: Invalid argumentTo fix this, edit /usr/sbin/apachectl and change ULIMIT_MAX_FILES to an empty string.

    Now you can restart apache.

  11. Install PassengerPane 1.3 www.fngtps.com/files/2/2009/09/PassengerPane-1.3.tgz

BOOM! Done.

Well, for me, I had to `gem install isolate` and Rubymine and then I was done, but you get the idea. Just load up the Passenger Preference Pane, point at site at a rails project on your hard drive, and load it up in your browser. Assuming your rails app will boot, you should be good to go!

Rubymine – A Love Story

I started writing code in Flash 5. I actually learned OOP in ActionScript, but that’s another post all together. My point is, my first code editor was Flash. Then I moved to Dreamweaver + Homesite. Then I used BBEdit for a while. Then I found Rails and Textmate and I was in love. I’ve never done Java or ASP so I had no need for an IDE, or so I thought.

Anyway, about 6-8 months ago, I looked into Rubymine. I knew Pivotal Labs used Jetbrain’s IdeaJ IDE with a ruby plugin for a long time, so I thought a Rails specific version might be pretty interesting. I’ve used it full time, every day, since I tried it out and I really love it. I find it super productive and it has all the tools that I use my development flow all in one app.

First of all, I’ll state for the record that Rubymine is not a text editor. I still use Textmate every day. You won’t open up random files in Rubymine to edit them. Second, I don’t edit CSS in Rubymine. I use CSSEdit from MacRabbit. So I have no idea how it does with CSS. I also don’t really use Rubymine on my laptop. Most of the win I get from Rubymine is that it’s SUPER efficient

Here’s what my Rubymine setup looks like:

Rubymine setup

Things to note:

  1. Close the file browser and use split windows. Get rid of the toolbar, you don’t need that.
  2. Open up the Run tab on the bottom and move that out to a different window. Then fire up a Rails console and it will be out in that window also. Move this window over to your other monitor (stop reading if you don’t have one, you’re dead to me) and make the editor full screen.
  3. If you open up the preferences go to Editor => Editor Tabs and check “Show Tabs in a single row”
  4. I’ll leave this up to the reader, but figuring out how to get Spork up and running is all kinds of win.
  5. I use a modified version of the Mavenlink theme. I’m using Menlo in 14pt and 1.4 line spacing for my font.
  6. I have loads of my own tab expansions, which are called Live Templates in Rubymine. (‘%=’ + tab outputs <%=  %>). The default ones are lacking, I should package some of mine up, but since I created tons of my own in Textmate, most won’t make sense to you. Suffice to say, I don’t like haml, I like erb + live templates. That’s how many I have. 🙂

My favorite Rubymine features:

  1. Command + B with your cursor on any Ruby/Rails method will take you to where that method is defined. This is way better than any documentation because I CAN READ RUBY CODE. I have been known to write out code just to command+b it and remember if it’s the method I want.
  2. Control + H on any Ruby/Rails method shows you inline documentation. Win.
  3. Control + Spacebar in .feature files will let you ‘live complete’ Cucumber scenarios you’ve already written, including the substitution regex in them. If you don’t understand how much win this is, I can’t help you. 🙂
  4. Refactor => Rename is fucking awesome. Rubymine 2.0.2 will even rename “some_message” in SomeModel.should_receive(:some_message). Rubymine is pretty smart about this stuff, and when it isn’t they’ll fix it (I requested the should_receive stuff and they made it happen).
  5. Item #4 reminds me of my favorite feature over all. Jetbrains is actively developing Rubymine, so if you have a feature request, or you find a bug, or something annoys you, THEY WILL FIX IT. I don’t know about you, but I am morally against Textmate at this point because I don’t think the creator is working on it anymore. He won’t open source it though, because it still makes him money (and I’ve heard the internals are a nightmare). Rubymine’s developers use Cucumber to test it, and I think I even heard they use Rubymine to build Rubymine. They are committed to the product, to making it better, and to listening to the Rails community. I’m willing to bet, if the community were to put it’s weight behind it, we’d see even greater support.

My gripes:

  1. Rubymine is buggy. It just is. But I’m over it. They’ll fix them. If you find a bug in Textmate (like I don’t know, search), you’re never going to see a fix. Never.
  2. Rubymine will sometimes need to re-index your project. This usually isn’t convenient, but if you want all those cool features I just spoke about, and search that’s fast, and lots of the other things that make Rubymine awesome, then suck it up and take it. You need a break anyway.
  3. Rubymine can’t keep up with Yehuda. That’s just the facts. Rubymine has to know alot about Rails, Gems, Bundler, Cucumber, etc and changes in all those underlying libraries will sometimes make it painful to upgrade them and have Rubymine’s awesome features. That’s just life in the fast paced world. Having said that, Rubymine does a GREAT job trying to keep up. For example, it already supports bundler 0.7. Rubymine 2.0.2 will support bundler 0.8 and most of Rails 3.0. It’s got support for haml and sass and spork and lots of cutting edge stuff. And again, they are actively keeping it up to date (can I mention that enough?!?).
  4. Rubymine is a resource hog, but then again, it’s not like I have a lot of other stuff going on my laptop, so it’s not usually a big deal. Also, I’m sure others see this more as I have a SSD drive and it makes this not a big deal.

Chris Bailey prompted me to write this post, and he’s got some stuff on his post about Rubymine I’d like to clear up:

Doesn’t work (well) with MacOS X Spaces – prevents switching to another space when you select another app

I have no idea if this is true or not. I don’t use Spaces, sorry. However, I will say, I don’t switch away from the app much through out the day. Also, submit a bug! They’ll fix it if they can.

File tabs:

  • They go to multiple rows too easily.
  • They just don’t look right/good.  I’m not sure if it’s because they’re non-native tabs or what.
  • I understand, but don’t like how the rows swap when you change to a different tabs.  You lose track of where a file is in the tabs.

I address this above, but I’ll re-iterate. Making tabs into a single line in the preferences will make Rubymine majorly better.

Find in project is nowhere near as fast, or presented as usefully as Ack in Project for TextMate.  It does provide more options, such as filtering files by directory or type and so on, but the speed is generally far more important.

This is puzzling because I have found find to be very fast. And yes, it has MASSIVELY more and better options than Textmate. I haven’t used Ack in project much, so I can’t comment.

Also seems to crash enough that it bothers me.  I can tolerate an occasional crash, although with TextMate I’ve run it for weeks on end with no crash, using it constantly.  I’ve had several either hangs or crashes with RubyMine in the short couple weeks I’ve been trying it out.

I know Pat Maddox has seen his share of crashes. I never see crashes. I don’t know why, but Rubymine is actually more stable for me than Textmate. Even on big projects.

The refactoring bits were just ok.  This is a really hard feature in general though.  Simple refactorings I used, like renaming a method worked fine.  But, changing the name of a model class did about 1/4 of the work needed and was questionable as to whether it was an advantage over just doing it all myself.  It got all the obvious, low-hanging fruit, but it seemed to completely ignore Rails helpers and views.  It misses a bunch of things that I’m not surprised at of course, such as table names in SQL conditions (e.g. in named_scopes), model name used anywhere in JavaScript or CSS (which isn’t uncommon in Rails code), and then the one that I’d like to see it do a better job on, but that is hard of course is on associations.  So, in my application, I was refactoring our “Deal” model to be “HotelDeal”.  We have a “Hotel” model and it has_many deals.  Thus in the code you’d routinely see things like “hotel.deals” to reference that association.  It didn’t handle any of those.  Not surprising, but starts to devalue the refactoring feature.

I’d submit bugs for these things. Also, I don’t know what version Chris was using, but this stuff has been getting better with recent versions.

At the end of the day, there are probably specialized tools that do each of the things that Rubymine does just a little bit better. GitX is definitely a better git/diff reader. Textmate is better for just plain editing files. CSSEdit is better for CSS. Terminal is a better shell. But for me, being able to have all of these things in one tool, and the few Rails specific things that Rubymine does provide, make up for the small things it doesn’t get right. If you give Rubymine 10 minutes, I’m sure you’ll always go back to what you were using before. However I gave Rubymine a month, and now that it’s 8-9 months later, I wouldn’t ever consider going back to Textmate.

Bundler: Oh the fail I know.

First, a disclaimer: I don’t want to be negative. I am only writing this because I feel it deserves attention by the community and because I love Rails so much that the fact that something is making me lose faith in it truly makes me a little sad.

I first started using Bundler 0.7.6 in December ’09 in a Rails 2.3.5 project. We also use Bundler (not sure what version we started with) in our main Rails 2.3.x app that powers www.Goldstar.com. In the 3+ months that we have been using bundler, it has, by far, been the largest pain point and time sink of any part of our toolset to the point that today, I proclaimed on twitter, than in it’s current state, I can’t recommend Rails 3.0 to anyone starting or upgrading a product.

Now, if you know me, you know that this is pretty out-of-left-field. I’m a HUGE Rails evangelist. And I truly believe that it’s right for many projects in all kinds of scenarios.

Anyway, on to the fail. . .

Bundler 0.7.x had lots of issues, too numerous to count. However, it did have the option to pass a yaml file to it via –build-options that would allow you to pass a few arguments into the various gems it needed to build. This was good enough for us, and we followed the convention we use with database.yml to have all the correct options in the various environments we use to dev/deploy our rails apps.

Builder 0.8 soon followed suit (I believe in January) and messed us up by changing the path inside the bundler folder that all the gems where kept at to be name-spaced to what version of ruby you were using. I’m not sure what this was meant to fix, but it broke a few things for us and it broke Rubymine (my personal editor of choice). But hey, we upgraded.

I’m going to leave out all the pain that was causes by upgrading to 0.8 and just say that we are currently using Bundler 0.8 in dev and production, except that a bug, where older version of already cached gems won’t upgrade unless you rm -rf the bundler directory (json-pure-1.2.2 to json-pure-1.2.4 fwiw) is breaking our deploys to production. This means that every deploy to production, we can’t be sure the bug isn’t going to break it, so we must rebuild every gem in the app (of which there are MANY) every time we deploy. This just isn’t going to work for us.

Now, we’d like to move to Bundler 0.9 because it promises to fix our issue (bundle install correctly updates gems as required). However, –build-options has been removed from 0.9. Because –build-options was removed, bundler has moved to a don’t-vendor-some-of-your-gems solution where it will use gems from the system if they aren’t in the bundle. This allows you to pass different incantations to `gem install` for different gems (mysql, pg, etc) and it will use those already built gems when you later do `bundle install`. But that means we’re back to managing gem installs on all of our different systems in different ways, which was one of the few things that Bundler did significantly better than config.gem!

Our systems developer, Steven Baker, is currently working on re-implimenting a solution to the –build-options issue, which he first ran by “Carlhuda” in hopes that they might include it and we’re not going down a road that will just end in us playing by ourselves and having to re-impliment our Gemfile or build_options.yml file for whatever solution is, but this brings me to my point:

Rails 3.0, because it relies on Bundler, is not in anyway stable/useable/suggested unless you want to pretty continuously fix/re-impliment/cry yourself to sleep because of Bundler. Carl L. has posted a roadmap which shows at least 2 more revisions as well as closing 20+ currently open issues. I have no idea what the issue velocity is on the project, but to me, it looks like it’s in the months and not days or weeks before Bundler is going to be stable. And, as with every version before it, there have been major downsides to the implementation, that have forced pretty major breaking changes. In other words, every version of bundler is a new experiment (not to mention the various gem management techniques that merb used which were also a source of headaches), and I have little confidence that just slapping 0.9 or 0.10 is going to change that.

Bottom line: A significant piece of Rails 3 is still in the “spike” phase, and shows little to no promise that it will become solid enough to trust for _months_.

Please skip the “Patches are accepted” (we’re working on one kthxbye) and “Why are you even using bundler?” (troll). I’m not here to indict or blame anyone, just to report our experience and clarify why I’ve lost significant confidence in Rails because numerous people have asked.

Sidenote: I’m putting this at the bottom, because it’s secondary to the point of the post, but why, precisely, do we *have* to use Bundler to make Rails 3 work? Who made this decision? If it was the same people that wrote Bundler, doesn’t anyone see a problem with that (NIH)? I’m assuming there were discussions in the rails-high-council private campfire room at some point, but obviously us peons aren’t privy to those. Config.gem wasn’t the easiest thing to use, but I’ve successfully used it for close to a year with very few issues and had it’s eccentricities worked out pretty well. To have it completely removed from Rails 3, and replaced by an experiment, seems a bit premature.

Rails App Template

I start a lot of projects. More than I’d like to admit. I finish and deploy very few, but I like cranking out the first iteration of an app in a weekend. Sometimes I do it just to learn some new feature, or try out an architecture idea. I don’t like to do this in the context of whatever existing app I’m working on, because I don’t want any baggage of whatever I was thinking last week. Last week is too long ago.

Continue…