Lifejot.com re-launched

October 24th, 2007

Man we've been busy!

We threw our hat in the Rails Rumble ring with our buddies Mike and El. We didn't win but 48 hours of pure coding was a rush and our app worked. Good times.

We've been working on a few different projects, too. Lifejot.com is our recently re-launched shopping list maker. JasonC started this one a couple years ago using .net. It was working fine but we needed to add some features and the site needed a facelift. There was no way we were going to hassle with doing it in .net. Converting this app to Rails was a piece of cake.

Lifejot lets a user create unlimited shopping lists. Usually these lists are created by store but you can create them however you like; Shopping list, Christmas list, Wish list, any list will do. Each list has drag-and-drop reordering. Most of the forms are ajax-loaded and most parts of the main list page are also ajax-refreshed. Lists can be printed, emailed, and merged or copied. It's a great improvement over v1!

We're also working on some ideas to monitize the site. I don't think we'll ever charge a user but we think there are many opportunities to partner up with services and, of course, advertisers. We'd love to hear if anyone has any other ideas.

Fun with Exception Notifier

August 1st, 2007

Fun? Perhaps I exaggerate. I’ve not had any fun with Exception Notifier today. In fact, I’ve had a lot of NOT fun! So I’m here to help you have less anti-fun than I had.

Exception Notifier (here’s a tutorial) is a great way to get Rails to email you when a critical error occurs in your app. It automatically ignores 404-like errors which is probably good most of the time.

I started by installing the plugin earlier today which is totally straight-forward…

script/install plugin exception_notification

Pretty easy, right? Right.

The README tells you all you need to know about how to configure it…or so you would think.

Personally I like to try my shiz out on my development box before I just throw someone else’s code (granted, Jamis is no slouch) up on my production site. So, I commence with the configuring and forcing my app to error out. I even try hitting my site from a totally different computer so it’ll ignore my “local” request. I get lots of errors, lots of trace info, but no email. Nothing even looking like an attempt.

Digging through the code I find some stuff about ignoring local requests so I figure I should hack that to allow my request to appear non-local. If you’re not a Rails noob (I am) you’ll have already figured out my problem by what I just said. You see, when you’re in development mode, EVERY REQUEST IS LOCAL by default. It doesn’t matter if you are hitting the site from around the globe, if you’re in development mode, it’s a local request. Normally that’s great but when you’re trying to test your freakin exception mailer, IT SUCKS!

Fortunately all problems were solved simply by editing development.rb. Change the line that says config.action_controller.consider_all_requests_local = true to false and BOOM, exception mailing! This is assuming, of course, that you have the ActionMailer stuff configured properly (which was another part of my problem). By the way, there’s also a line in development.rb that looks like config.action_mailer.raise_delivery_errors = false

I like to set that to true so I can see the whole conversation between the app and the mail server.

By the way, I’m using Google Apps for my email server (which I love) but it takes some massaging to get Rails to mail to it. There are a number of articles out on how to do it.

Oh, and if you happened to read the tutorial I posted at the beginning of the article, including the comments, you would notice that what I just told you was in there. Guess who didn’t read it all the way through? Ya. G’night.

Setting up acts_as_paranoid

July 28th, 2007

Due to a very small amount of documentation, and the loads of outdated documentation, on how to get started, I'm going writing a quick how to. It's very quick and easy; not much to it, just make sure you are working with the most up to date svn repository before you start. Follow the steps below.
  1. Navigate to your rails project folder and run the following command ruby script/plugin install http://svn.techno-weenie.net/projects/plugins/acts_as_paranoid/
  2. Create a migration(s) for all your database tables that do not contain a deleted_at (datetime) column that you wish to use this with by running ruby script/generate migration add_deleted_at_column
  3. In the migration use the add_column :table_name, :column_name, :datetime syntax, and the opposite, remove_column for migrating backwards.
  4. Add acts_as_paranoid under the model name of all your models you wish to use this with.
  5. Test it out and you should be populating the field deleted_at now instead of deleting the entire row.
Acts_as_paranoid is a great way to keep data in your database for future reference and reporting; as well as a fail safe if anyone accidentally deletes things they should not. Additionally, :dependent => :destroy applied to your has_many association method works great with acts_as_paranoid as well since it overrides :destroy method with the deleted_at update.

So here I am tearing through my tickets when all of a sudden my ticket shredder jams. On what? Jason wants me to do some fancy filtering of a list that requires some :joins, some :conditions, and it has to work through a has_many :through join that’s already there. Oh, and since the items are also tagged it has to use acts_as_taggable, too.

Is he asking too much? I don’t know. I guess filtering a list by a search value, date created, a related person object, and a tag isn’t asking too much. I mean it’s for our users, right?

So being the code monkey I am, I plunge forward. I cleared the jam on my shredder, lubed it up a little and started ‘er up again.

My initial solution was, as it was with everything related to search and filtering of my objects so far, to just add another custom finder to acts_as_taggable.rb. I mean I already had hacked that thing to the point that it didn’t resemble the original file much. And I know, I shouldn’t have been putting things RIGHT into that file anyway but overriding it’s methods somewhere else in case I ever updated the plugin…I know.

Here is an example of one of my acts_as_taggable hacks…


def find_tagged_with_and_conditions(list,conditions,page=1,page_size=10)
  find_by_sql([
    "SELECT distinct #{table_name}.* FROM #{table_name}, tags, taggings " +
    "WHERE #{table_name}.#{primary_key} = taggings.taggable_id " +
    "AND taggings.taggable_type = ? " +
    "AND taggings.tag_id = tags.id AND tags.name IN (?) and #{conditions}" +
    " order by created_at desc " +
    " limit " + ((page -1) * page_size).to_s + ", " + page_size.to_s,
    acts_as_taggable_options[:taggable_type], list
  ])
end

I know it’s terrible. I know I’m breaking all kinds of rules. But hey, it’s our first “real” rails app. We’re still trying to scrub off the ASP.Net.

It’s pretty obvious what that does. It lets me get a set of results based on a tag, some conditions, and it implements some paging. Pretty straightforward once you get past all the sacrilege of how I’m doing it. It IS semi-DRY since it will work on any model that I’ve set as acts_as_taggable. So it’s…OK.

Where I run into a problem is when I have to do that same thing but through a has_many :through join model. You see I have People and I have Invitations and they join through Invitees. Invitees has some other data in it (which is why I’m not just using HABTM). The filtering I need to do is on a list of Invitations but one of the criteria is that I need to filter by who the invitation was sent to, an Invitee. Thanks Jas!

I started looking around for a solution to tagging that didn’t use acts_as_taggable. Some say it’s deprecated. I don’t know. I ran across Evan Weavers blog entry about has_many_polymorphs and how it could be used for tagging. I read through all the comments and a couple related blog entries and it looked like it would help me get where I needed to go. I jumped on IRC#polymorphs (the IRC channel for the plugin) to ask around. cdcarter was the only one in the channel that was available. He was great moral support! He suggested that I just make a branch in Subversion and hack away on the branch with HMP (has_many_polymorphs). Sounded good to me! And hey, I had never made a branch before. I was learning all kinds of new stuff!

HMP installed easily enough. Evan’s even gone through the trouble of including a generator that will make the tagging parts for you. Since I already had some of the parts I didn’t go that route but it looks like the way to go when starting from scratch. The only glitch I ran into was that I had to add require 'active_record' to my extension lib so that it would recognize ActiveRecord::Base Not sure what is going on there but it’s working.

After I got it installed and followed the directions in Evan’s blog I had the tagging working like a champ. The biggest difference (at least in the tagging sense) is that HMP uses actual models with polymorphic relationships to do the tagging. This made it possible to do what I was trying to do simply by using tag=Tag.find_by_name(@tags_filter) @invitations=tag.invitations.find(:all, :conditions=>@conditions, :include=>:people, :offset => (@page -1) * @per_page, :limit => @per_page, :order => 'invitations.created_at DESC') I have some more work to do to DRY it up but it has simplified my code tremendously. Thanks Evan!

I have to also thank the guys over at The Rails Way for reminding me of the power of join models and the “right” way to use them.