Old dogs, new tricks

Posts Tagged ‘rails 2.0

Facebook templates made easy with Rails 2.0 custom Mime types

with 3 comments

I’ve written elsewhere about how I used my own lightweight library to add Facebook functionality to Autopendium :Stuff About Old Cars, the classic car community website I run.

The library has made it fairly easy to keep up with Facebook’s many changes, and the Facebook app has been a good marketing tool for the site. But adding more functionality to the app has meant duplicating code, as all the actions are handled by a FacebookController.

However, now that I’ve updated to Rails 2.0, adding Facebook functionality is a whole lot easier, and the solution is so simple, I’m sure it’s a common usage pattern.

Let’s take the Autopendium classic car events calendar, which we’ve just introduced:

I don’t really want to do an events action in the FacebookController just to make it available in the Facebook app; I’d rather just use the EventsController#index action and render it with a custom template. (We’re already doing something similar for ics MimeTypes — serving up the events in iCalendar format, so they can be imported directly into your electronic calendar, but that’s for another post).

Custom Mime Types to the rescue

With the new custom Mime types in Rails 2.0, it’s a breeze. Often these are used for customising apps for the iPhone (as shown here), but I reckoned the situation was pretty similar with integrating Facebook interfaces to existing apps.

If the request comes in via the the Facebook canvas we need all the custom Facebook FBML to specify style, etc (and even if you’re using an iframe, you’ll probably want a custom layout).

So this is what I did. First, add a custom facebook mimetype in your environment file:

 Mime::Type.register_alias "text/html", :facebook

Then you need to some way to recognize you’ve received a request from your Facebook app.

You could do a check on the params, as all requests from Facebook have a number of Facebook-specific parameters (fb_sig, etc, which is covered briefly in the second part of my Facebook lightweight library posts). This has the added advantage that you can use the normal URLs in your facebook templates/links. However, if you’re using REST-type routes — as I am — you may end up with difficulties for the moment, as all request from Facebook are POSTs. (According to FB, this may change in the future and already there’s a parameter in the request which says what method the original request was.)

A simpler way is to use the routes (or possibly a subdomain, as shown in the iPhone example). I’ve already got the Autopendium FB app set up so that all request from Facebook have a base URL of autopendium.com/facebook/ (i.e. what Facebook calls the callback URL) . This normally sends everything to the Facebook controller, so /facebook/latest goes to the #latest action in the FacebookController. However, if I add a couple of line to my routes.rb file:

map.connect 'facebook/:controller', :format => 'facebook', :action => 'index'
map.connect 'facebook/:controller/:id', :format => 'facebook', :action => 'show'

… I get facebook/events (which is generated by a link in the Facebook canvas of apps.facebook.com/autopendium/events routing to the index action in the Events controller with our custom :facebook format. Likewise facebook/events/3 will route to the show action in the Events controller with an :id of 3 in the params hash.

Then in my Events controller, I just add an additional line to the respond_to block:

respond_to do |format|
format.html # index.html.erb
format.facebook # index.facebook.erb

This means the response for the Facebook request will be served using a special facebook template, with no render :template => “special_facebook_template” needed.

Classic Car Events in Facebook

Even better, if it will automatically use a custom facebook application layout if it exists (called application.facebook.erb). So all your standard links, frame, css can be included without a single extra line. Mine looks something like this:

<%= stylesheet_link_tag "facebook_basic" %>
<fb:header decoration="add_border">Autopendium :: Stuff about old cars</fb:header>
<br />
<fb:tab_item href="http://apps.facebook.com/autopendium" title="Intro">Intro</fb:tab_item>
<fb:tab_item href="http://apps.facebook.com/autopendium/show" title="My Autopendium">My Autopendium</fb:tab_item>
<fb:tab_item href="http://apps.facebook.com/autopendium/events" title="Classic Car Events">Classic Car Events</fb:tab_item>
<div class="container">
<%= yield  %>

Written by ctagg

April 12, 2008 at 12:54 pm

Rails 2.0 gotcha: count_from_query plugin

leave a comment »

I’ve got a fairly comprehensive test suite for Autopendium :: Stuff About Old Cars, the classic car community site I run, which makes upgrades of the framework fairly stress-free.

By stress-free, however, I don’t mean trouble-free — there are going to be failing tests, and there are going to be problems. However, I’m fairly confident that if the tests pass, the update to the production server will be without problems (particularly since I’ve started using a staging server).

So it was with upgrading to 1.2.6 — I had only a couple of deprecation warnings, and some failing tests, most of which were due to some problems with my routing.

Upgrading to 2.0.2 has proved a bit trickier however, mainly because the error messages (and they are errors, rather than fails) aren’t helping me in finding the root cause of the problem, only telling me what ultimately brings the whole thing crashing down.

Running the unit tests via the console I get this horror:

Rails 2.0 unit test errors

OK. Let’s take this bit by bit. So I run the unit tests for WikipediaEntry:

Rails 2.0 unit test passes

Hmm. This smells… and the smell is called… fixtures.

Log story short, by trawling through the test logs, using ruby-debug, and getting to grips with the ActiveRecord code, I found out that the count_from_query plugin I use (which makes generating counts from complex custom finders a cinch) wasn’t playing well with the new ActiveRecord behaviour, which has changed a bit since the 1.2 branch, nor with the new faster fixtures.

[As an aside, this sort of thing is why if you’re serious about using Rails you must learn Ruby, and why it’s a good idea only to use lightweight plugins you can understand.]

The offending line in the plugin is towards the bottom, where the plugin’s methods are added to ActiveRecord::Base.

  def self.included(receiver)
    receiver.send :include, ClassMethods

This method (or callback) is invoked when, to quote the Pickaxe, “the receiver is included in another module or class”. Thus, “receiver.extend(ClassMethods)” extends ActiveRecord::Base with the methods which are contained in the ClassMethods module.

OK, this makes sense, as the methods consist of the count_by_query method and a method_missing, which tests whether the called missing method ends with _count and calls count_by_query if it does. This means if you have a class method called #find_complicated_stuff you can call #find_complicated_stuff_count, which is great and makes will_paginate, for example, much easier in some edge cases.

The problem lies with the previous line: “receiver.send :include, ClassMethods”, which includes the code (and method definitions) in the ClassMethods module, which has the effect of including the methods as instance methods. This doesn’t work with Rails 2.0 for two reasons:

1) If we get a tag cloud, for example, which might look something like this:

    find(:all, {
          :select => "tags.*, COUNT(*) AS tag_count", 
          :joins => "INNER JOIN taggings",
          :conditions => "taggings.tag_id = tags.id", :group => "tags.name",
          :order => "tag_count DESC, name ASC",
          :limit => 10})

The resulting count for each of the returned tags can then be accessed through #tag_count. Except that this will be intercepted by the method_missing in the count_by_query plugin (not sure why this didn’t happen in 1.2 — perhaps to do with the load order of the plugin?)

2) When a failure occurs in the #count_from_query method because of how it works we’ve left #find in an unstable state, which means calling find (which the fixtures code does) results in doing a count. Result: kaboom!

For me the solution was to delete the offending line (looking at the specs this may be intentional behaviour rather than a bug, but I can’t really see a use for it, and certainly don’t need it).

Written by ctagg

December 30, 2007 at 7:08 pm