Friday, May 26, 2006

Ruby on Rails : Has_One versus Belongs_To

There's a question that comes up when learning Rails associations: when do you use has_one, and when do you use belongs_to? Sometimes it's not as obvious as you might think. Even the wiki page isn't very helpful, saying in general Foo belongs to Bar if table foo has a bar_id column. That's not very helpful when I'm creating my model from scratch and wondering why, not how. I can put the index wherever it makes sense.

Imagine you've got a small library of ContentTemplate objects. You're going to have instances of a Content object, and each of those pieces of Content will have a pointer to one ContentTemplate object. I suppose you'd call this a one-way many-to-one relationship. Many Content objects point to one ContentTemplate, but that one ContentTemplate does not need to know about its many Content objects. According to the traditional rules of composition you might think that Content has_one ContentTemplate, or even that ContentTemplate belongs_to Content. Both are incorrect. Let's look at why.

The second one first. If ContentTemplate belongs_to Content, then ContentTemplate will need to contain a key that says what single Content it belongs to. But ContentTemplates are used by many Content objects. So unless we want to get into a "has_and_belongs_to_many" relationship, this is not the answer. That's also wrong, since the Content object only needs to know about one ContentTemplate. It's not many to many. Also for this example our ContentTemplate does not need to know about the Content objects that use it.

So then the first is right - Content has_one ContentTemplate, yes? Nope. It appears from my experience that has_one implies a one:one relationship between the objects. That is, it assumes that when you create a new Content object, you're also going to create a new ContentTemplate object. Because the ContentTemplate object has to contain a content_id that points back to its parent. I found this concept articulated nicely at the very bottom of the wiki ForumExample: "With belongs_to, the table accepts responsibility for the foreign key. With has_one, the table expect the other table to hold it." So if Content has_one ContentTemplate, then ContentTemplate has to have the key. But as we already said, ContentTemplates are used by multiple Content objects. I don't want multiple instances of them, each pointing to its parent.

The answer turns out to be the combination of the two that might be counter-intuitive when you see it -- Content belongs_to ContentTemplate. Go back and look at how I described the problem, and you'll see that it works. With belongs_to, the table accepts responsibility for the foreign key. So Content has a content_template_id. And ContentTemplate doesn't need anything. I can point to it at will. Done.

Another way to think about this is to ask whether you're talking about creating objects or linking to pre-existing ones. If A has_one B, then when you create a new A you're probably going to be creating a new B as well. But if A belongs_to B, then you can point to a pre-existing B and hang yourself off of it. Say you've got a Person object that has_one Address. When you create a new Person, you're going to create a new Address to go with it. However, if you wanted to come back later and add another address (or change the current one) then you have to have something for that Address to belong to.

I have no idea if that made it easier to understand or not :). But I've just spent a hunk of the last 2 days trying to figure it out, and this seems to be the answer in this case, so I wanted to document it in case anybody else out there is having the same problem.


AddThis Social Bookmark Button




Technorati Tags: ,

Monday, May 01, 2006

Smart Headphones

I think it's neat that my powerbook recognizes when the headphones are plugged in. I don't really do anything with this information, but still, it's kinda cool. Opens up new areas of feedback that you might not have traditionally thought about.

This weekend I switched from earbuds over to a fuller pair of headphones, complete with that cushiony bit that attempts to cancel out the surrounding noise. My earbuds keep breaking, probably because i let them dangle and end up running over them with my chair.

I have an idea I'd love to see implemented. When I take off my headphones, I want iTunes to pause itself. Makes perfect sense to me. If there's headphones connected, then chances are that any music playing is going through those headphones. Thus, if I take them off, you can assume that I'm not listening anymore. While it might be allowable to let the sound continue -- for instance, if you're just listening to generic background music -- it's much more obvious to assume that you should stop the music. I've certainly been in situations where a coworker leaves his music playing through headphones, but it is so loud that it can be heard (in that tinny, scratchy way) over the cube wall.

So, how would you do it? I figure there are two problems to solve -- how to figure out when the headphones are on/off, and how to transmit that information to the computer. The second is probably the harder, especially if you're talking about just a generic RCA line-out plug. This would probably only work over Bluetooth or something. For the former, detecting when they are on/off, maybe there could be a switch in the springy portion of the band that keeps the headphones together, so it can tell when it is at rest, and when it is clamped down over somebody's head? A friend suggests maybe attempting to measure the heat coming off the listener's body, but I'm wondering how dynamically that would change when you take them off.

If somebody could make such a thing, I'd be happy with a button on the headphones that I could press as I'm taking them off. Grabbing the mouse, switching to iTunes and pressing Pause is just too many steps.