www.flickr.com
Michael Kovacs' photos More of Michael Kovacs' photos
Recommend Me Cable Car Software logo

Tuesday, February 20, 2007

Rails realities part 23 (what's the count blue?)

Ahh... spring is nearly in the air. Baseball season is starting to get underway. Barry has finally signed his escape clause laden contract and is lookin to juice his way past Hank Aaron this year. Soon enough it'll be sunny and I'll be sitting at the game, half paying attention as to what it going on. But every once in awhile you want to engage and see "what's happening" in the game, and as any baseball player or fan knows one of the most important things to know if you have your "head in the game" is the count. The count is one of the most important pieces of information that provides you insight into what's happening AT THAT MOMENT in baseball.

When building our applications in the geek world we have those same needs. "How many users do we have? How many listings are there? How many sales have we made in the past week?" etc. The number of times that I'm saying "give me the count" in my applications is pretty high.

So it's a little surprising to me that there would be an outage in ActiveRecord on this front. Perhaps I'm "not cheating enough" as DHH says because I'm finding myself running into a wall and patching AR on an ever more frequent basis. To be fair I'm still using rails 1.1.6 but I've taken a look at the 1.2 code line and there's nothing in there that would appear to fix the problem I've run into.

Basically if you have a has_many or habtm relation and you'd like to apply conditions that rely on a related object you are out of luck if you'd like to get a count of those objects. Let's look at some code to clarify what I mean:



class Group < ActiveRecord::Base
has_and_belongs_to_many :users
end

class User < ActiveRecord::Base
has_and_belongs_to_many :groups
end

class Account < ActiveRecord::Base
has_many :elite_users,
:class_name => 'User',
:conditions => 'groups.name = "Elite"',
:include => :groups
end

What that relation gives me is a way to get at the elite users that belong to a given account.
account = Account.find(:first)
account.elite_users

This works just fine if I'm only ever just going to get and use my objects. If I ever want to count them I'm in for some heartache as:
account.elite_users.count 
account.elite_users.size

are both broken because they aren't honoring the :include on the relationship declaration.

I've been in the process of trying to patch habtm and has_many to fix this problem but haven't got it licked yet. I'm wondering if I should be modeling my classes differently but I can only see two ways of accomplishing what I want to do:

  1. Account -> User -> Groups

  2. Account -> EliteUser



The second way is the way I started out modeling this relationship because it wasn't clear that there was a reason to ever support more than one group type for a given user. That way works fine because it's just a single join from parent to child with no additional joins and conditions.

I feel like this might be one of those times where you need input from another developer so they can either confirm, "yeah that's a real problem and those are the options in terms of solving it" or "hey you're totally on the wrong track, how about if you did this instead..."

So anyway, I *think* I'm going to do 1 of 4 things:

  1. Fix has_many and habtm count and monkey patch it

  2. implement :count_sql on the relation

  3. stay with the EliteUser implementation and basically denormalize my group information

  4. create finders on my class with my conditions and ditch the relation


Anyone else run into this issue ever? Or are most people just executing a find method instead of declaring a relation?

This page is powered by Blogger. Isn't yours?