Last year I created a small project manager in Ruby on Rails that among other things lets the users reference (or mention) tickets by number when posting messages and comments. Each ticket is assigned a unique number, beginning with 1. So when I reference "ticket #1" in a comment on an otherwise unrelated message, the system is smart enough to know that the ticket exists, and will automatically link to it in my comment.

Instead of just scanning the body of the comment for any pattern matching "#n" on the fly and assuming the ticket exists, I decided it would be better to scan the body once when the comment is created, and implicitly check each match to make sure that a ticket with that number exists. If it does, then an association between the comment and the ticket is created using a join table.

Using a join table for this purpose has a few benefits. For instance, I can easily search for all comments referencing a specific ticket without using a regular expression in the query. It also ensures that the reference was intentional, or at least the ticket existed at the time the comment was created. If I wanted to get really fancy, I could allow the user to remove a reference that incidentally happens to match the search pattern.

I had originally built this as a simple Rails plugin in vendor/plugins, but since those plugins are now deprecated in Rails 3.2, it was a good opportunity to package it as a gem and release into the wild.

So, I'm introducing a little gem called "hearsay" that aids in creating associations between model attributes and other models:

I've included basic usage instructions in the readme, but the code is documented with all the available options if you want to dig deeper.

Because the regular expression and finder method are configurable, this could be used for any situation where you want to match some text and use it to associate other objects; one alternative example could be twitter style mentions, where the regular expression is /@(\w+)/i and the finder might be find_by_username.

I'm considering this beta until I can get around to writing some tests. Contributions and suggestions are welcome.