Rails 3 UJS custom ajax response observers

Wednesday, December 29th, 2010

I used to brush aside UJS as unnecessary and a source of maintenance problems because of the disconnect from the HTML. Developers coming in with no knowledge of the JS will have problems debugging complex JS that’s been unobtrusively created. I do still feel this is true but the benefit of UJS is in the simplicity and elegance of your JS code in the end. It might add a layer of complexity because of the detatchment from the HTML but the JS you end up developing will be cleaner, more concise, and ultimately much much more readable than embedding it into the HTML.

With that rant out of the way, lets examine a problem with the Prototype library. When doing Ajax calls we can set onSuccess, onFailure, and a few other observers. With UJS your Ajax observers might look something like this:


$('something').observe('ajax:success', function(right_evt) {
alert('success');
});
$('something').observe('ajax:failure', function(right_evt) {
alert('success');
});

But what if our Rails app makes smart use of HTTP status codes and returns status codes like 401 or 302? With Prototype, we can specify on401 or on302 but the catch all observers like onFailure won’t get called in those cases. For instance, just adding the following to the above code:


$('something').observe('ajax:on401', function(right_evt) {
alert('unauthenticated');
});

Has 2 problems. First, it might seem like this is possible with Prototype the Rails UJS, rails.js, doesn’t actually recognize any specific status code observers. So that on401 will just get ignored. There are 2 solutions to this. The first is to hack up rails.js and add a listener for on401 or whatever other status codes you want to specify. The problem is Prototype will ignore the other catch all observers like onFailure if you start specifying specific status codes. A better solution is to just catch the specific status codes that you want custom inside of the catch all onFailure observers. You can do that like this:


$('something').observe('ajax:failure', function(evt) {
if(evt.memo.status == 401) {
alert('unauthenticated');
return false;
};
alert('Failure');
});

Now this will handle 401s differently than other failures but still catch all the other failures without having to specify all of the status codes individually.

GitHub down yet again

Tuesday, February 2nd, 2010

I am experimenting with a new Engine Yard account today and their Setup basically requires you to have your projects in git. Since the site was from a private svn repos I decided I’d go ahead and signup for a paid Github account so I can host my projects there privately instead of having to setup my own git server. I should have trusted my gut because not more than a few hours later here I am trying to access my Github account and the site is broken again.

Rails threading with Spawn plugin

Tuesday, December 8th, 2009

My previous post was about using Thread in Rails which simply doesn’t work properly when you’re doing anything with ActiveRecord despite what anyone else is claiming.

This post will focus on my second attempt at a solution to intra-request threading in Rails. Basically, I have a Rails app where I want to run multiple computations at the same time. Since I had problems with Ruby’s native Thread method previously, I had no intention of going down that route again. I decided to try out the Spawn plugin.

Spawn was able to successfully segregate my multiple threads, or forks in my case. This got around the MySQL errors I reported in my previous post but it created a whole new set of problems all its own.

The first problem I had was that the spawn forks I was creating weren’t able to communicate their changes back to the main Rails request. I was creating forks and they were running fine and even saving data via ActiveRecord. I could confirm this via the console. The problem was that I need to know what data was being written by the spawn forks inside of the main Rails request. It was as if the main Rails request didn’t even know that something new was written to the database. I was able to get around this by forcing a reload from the database on the object I was trying to get association data from.

No big deal and it seems to make sense to do that anyway. That’s not where the real problem occurs though. After I got past that I realized that since each fork was essentially using a ‘copy’ of the database and not actually live connections that the data validations randomly failed. Basically if two forks are writing data to the database at the same time when there is a validates_uniqueness on the data. They both write to the database and both actually pass validation. The result is a database full of incorrect data which should have never passed the validation. I’m still not sure if that’s a problem with Spawn or an inherent problem with ActiveRecord’s connection pool.

Fun with Rails ActiveRecord and Ruby’s Thread

Tuesday, December 8th, 2009

I’ve been working on threading a Rails application lately and after reading headlines like ‘Rails is thread safe’ I figured how hard could it be. My first discovery was that when people talk about Rails and threads there are two different types of threading in Rails.

      Multiple request threading – This is where Rails itself threads among different requests to your web server and allows ActiveRecord to behave properly without having to keep a copy of Rails in memory for each request.
      Intra-request threading – This is where you have 1 request to your web server and inside the action you want to create multiple threads that run concurrently.

I’ll be talking about Intra-request threading. In particular, I want my threads to execute some code, read and write to the database, and play nicely with each other. My first attempt was to use the Ruby Thread method. This seemed to work somewhat until I started seeing strange errors coming in from MySQL. The problems seemed to occur randomly and what I determined was that the threads were trying to write to the database at the same time which ended up causing some collisions of sorts resulting in ‘lock wait timeout exceeded’ errors.

After considerable Googling, I found numerous posts about setting:

ActiveRecord::Base.allow_concurrency = true

The problem with that is that this is deprecated in the newest version of Rails in favor of connection pooling.

The short answer: Don’t use the Ruby Thread method within Rails when doing anything with ActiveRecord.

The World’s Oyster screencast

Saturday, September 5th, 2009

Here is Micah Friedline with another screencast. This time it’s about The World’s Oyster, a self-updating address book. Stay in touch with friends, business contacts, and more with the easy to use address book that keeps itself updated as you move.

The World’s Oyster

Thursday, August 20th, 2009

Its been a long year and I’ve had very little time to keep things updated around this site but here’s a preview of something we’ve been working on with a client. Its a self updating address book so you never lose track of your contacts when their information changes. Signup, try it out, and let us know if you have any ideas for improving it.

The World’s Oyster

Rails HTML Sanitize gem

Saturday, January 17th, 2009

I was recently working on improving the search engine rankings of a site with lots of user generated content and noticed that users were creating 404s through bad links. The users were able to add links to other sites in their comments and such but sometimes the links were bad. Sometimes they were even local links so the search engines were effectively seeing a bunch of internal 404s from the user generated content. This was essentially defeating any seo being done elsewhere on the site and needed to be fixed quickly. My original idea was to use hpricot to scrub all the anchor tags and append a rel=”nofollow” tag to them all. I was mulling over how to write the hpricot parsing code when I found the Sanitize gem. It does exactly what I needed and saved me the hassle of writing the hpricot parsing code. The gist of it is:

[source:ruby]
Sanitize.clean(html, Sanitize::Config::BASIC)
[/source]

As an added bonus, it also can scrub out unwanted script tags and more. Now, the site won’t be nicked for having internal 404s from the user generated content since they’ll all have rel=”nofollow” on them.

Ditching Mongrel for mod_rails

Sunday, May 25th, 2008

I build a lot of Rails apps on a regular basis and each one I add to my server takes another bite out of my limited resources. The way I’ve traditionally setup a new Rails app was using a Mongrel cluster. I found it to be a lot more reliable and faster than the fcgi approach people use to use (and some still do). The downside to setting up a few dozen Rails apps on your server with each running a Mongrel cluster is that it eats up all your memory. One of my sites is starting to get a lot more traffic than it has been in the past and its putting additional strain on the server. As a result I decided to find an alternative to Mongrel. I’ve tried searching for alternatives in the past but everything sent me back to Mongrel. Until today of course when I came across Jamie Flournoy’s blog about mod_rails.

Excited for an alternative to raising a pack of resource hungry mongrels on my server I installed the gem and tried it out. It was exactly what I was looking for as far as ease of use straight away. All I needed to do was stop a mongrel cluster and simplify its virtual host directive in Apache to leave out the mod_proxy_rewrite and the other wonky rewrite rules. The first app I tested went smoothly but suddenly the server started misbehaving. Resources were being eaten and it wasn’t clear what was doing it because the app I was testing with is behind an Apache password and I’m the only user. I ended up having to turn off the mod_rails to get my system back in control. The problem turned out to be that by default mod_rails tries to test if your virtual host directory is a rails app or not. I have a few apps that I tossed in an instance of WordPress into a blog directory inside my rails app directory. I found it convenient to toss them all into the same directory since its all the same website. As a result mod_rails was doing a ../ check to see if the blog directory was a rails app which it decided it was. That’s where the craziness came in because its a php application. Anyway, the quick solution was to move the blog directory out of the Rails app directory.

Other than that my memory usage is way down. I’ve migrated all my low traffic sites to mod_rails and I’m happy with how they’re performing. There is a little delay on the initial load of the app but subsequent calls are quick because its already loaded. I can wait an extra 2-5 seconds for my low traffic apps to load in exchange for hundreds of extra megs of free memory.

I haven’t moved over my higher traffic money making sites yet and I’m not entirely sure I will until I’ve tested mod_rails a bit more. I’m extremely happy with the results thus far though.

Reducing Rails model callbacks

Tuesday, April 22nd, 2008

I’ve been working with a client to optimize parts of their Rails application. The problem is that a method in the app does some simple updating of a few model objects but because the model has so many relations it goes through a ton of unnecessary callbacks. There are issues related to data concurrency which means you have to do the callbacks but in this particular situation there won’t be any concurrent updates to the data so the callbacks can be omitted. The solution to the problem was trivial when using the save_without_callbacks Rails plugin. Just adding a simple:

[source:ruby]
some_object.skip_callbacks = true
[/source]

before the update_attribute reduced the number of SQL UPDATE queries from 90 to 8. Lesson learned. If you need to skip model relation callbacks on save this plugin is for you. Be careful about data concurrency issues though.

Googlebot and redirect_to :back

Thursday, February 28th, 2008

The other day I noticed a pretty significant SEO related problem with using a built in Rails construct. I noticed a problem when I started getting application errors that were letting me know that the user agent was none other than our friendly Googlebot. A closer look at the app shed light on a problem you may not have even expected. When using

[source:ruby]
redirect_to :back
[/source]

it will take a look at the HTTP_REFERRER and redirect the user to that url. The problem, however, is that Googlebot doesn’t send a referrer and neither do a whole bunch of other search engine spiders. The result is that when they visit your site they get a nice 502 server error because Rails raises an exception. It doesn’t know what url to redirect to so it send a 502 error. Googlebot then sees your site as a bunch of 502 errors in the situations where you’re using redirect_to :back. Take a look at the Rails API and you’ll see the last line clearly mentions this.

The solution is to catch the RedirectBackError the redirect_to raises when there’s no referrer. Its a simple fix but one you need to be on the lookout for or else you might end up with a few 502 errors giving you bad mojo with the Google gods.