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

class User < ActiveRecord::Base
has_and_belongs_to_many :groups

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

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

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:

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?

Does .length work? Does it work if you use an explicit has_many :through instead of has_and_belongs_to_many?

I didn't try .length. Instead I ended up spending a few hours learning some more AR internals and patching the bug for has_many and habtm in my app. I had a look at the 1.2.2 code and it appears to be fixed in that line so it's really just a temporary fix. I need to get myself updated to the latest release when I get a chance.

As far as using has_many :through I didn't want to go through the exercise of trying to find a new domain model just so that I can see if that implementation works.

Anyway, for folks that are on 1.1.6 and run into this problem drop me an email at kovacs at gmail dot com and I can send you the patched versions of has_many and habtm.
Creating a view in the database is sometimes helpful in getting around some limitations of Rails. For example, I have one table with TWO columns that both contain ids for records in a second table.

I ended up creating a view that selects all records in the second table and gave it its own model. Both columns are now tied to the same set of records but Rails is tricked into thinking they are tied to two separate tables.

Not your situation, but maybe there is a hint of an approach that might twist your attention in a helpful direction.

And now I realize this was your issue TWO months ago. Oh well, I am submitting this anyway on the chance that someone might benefit.

Post a Comment

<< Home

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