Pushrod

Old dogs, new tricks

Archive for the ‘testing’ Category

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
    receiver.extend(ClassMethods)
  end

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

The how and why of my streamlined Rails Facebook library

with 22 comments

As I wrote in my previous post (Rails, Ruby, Facebook and tests — my own itch scratched), I needed a Facebook library for Autopendium :: Stuff about old cars, the classic car community site we built on Ruby on Rails, but couldn’t get to grips with the existing library as there were no tests and the code seemed, well, a bit un-ruby-ish.
The solution, particularly given that no one else seemed to have a problem it, was for me to come up with something myself.

First job, try to understand the Facebook API, and in particular the Request/Response cycle. What exactly was going on and what is this Facebook object the documentation talks about?

The ‘Facebook Object’

First thing to know is that the Facebook API is a REST interface. That means there’s no state between requests. So each time you talk to Facebook it’s a fresh request, and the only info it has is that which you pass to it. Similarly, Facebook’s response tells you everything you need to know in that one response.

With this in mind and trying a few dummy requests from the command line with CURL things began to come a bit clearer, especially after looking through the Facebook php library supplied with the Facebook dummy app (my knowledge of php is elementary to say the least, but the naming of the ‘behaviours’ and the comments helped). With that in mind I began to write some dummy controller code:

facebook.fb_users_get_info(:uids => facebook.user, :fields => "first_name, last_name")

It’s worth stressing that I didn’t know what that meant, or how it would be implemented, I just thought that I should be able to ask the mystical facebook object for some info on a given user if it was going to be useful.

Next step was to write some unit tests:

def test_should_be_able_to_instantiate_a_facebook_object
assert FacebookUtilities::Facebook.new
end

Well, duh! But what is a Facebook object already! The main existing library calls it a FacebookSession, which confused me, as it made me think of server-side sessions (that’s probably my fault though). The php library, however, just calls it an object, and we know about those, don’t we. This is Ruby after all.

So the Facebook object is just an instance of the Facebook Class, which defines the Facebook objects behaviours and attributes. Great. That meant, all I had to do was to go through the php library identifying the behaviours, write the (failing) tests, then write the code that made the tests pass.

Simple. Here, for example is the test for generating the Facebook signature (this is the a verification code that’s passed from you to the Facebook API and from the Facebook API to you to confirm it’s a genuine request, and it’s generated using a secret key that Facebook gives you when you register your application).


def test_should_return_signature_from_given_params
fb_object = FacebookUtilities::Facebook.new
assert fb_object.respond_to?(:signature_from)
assert_equal Digest::MD5.hexdigest(FacebookUtilities::FACEBOOK_API_SECRET), fb_object.signature_from
assert_equal Digest::MD5.hexdigest("a_param=1234xb_param=5678yc_param=97531t#{FacebookUtilities::FACEBOOK_API_SECRET}"), fb_object.signature_from({:b_param => "5678y", :c_param => "97531t", :a_param => "1234x"})
end

I derived that from the folowing php code and the Facebook authentication page:

public static function generate_sig($params_array, $secret) {
$str = '';

ksort($params_array);
// Note: make sure that the signature parameter is not already included in
// $params_array.
foreach ($params_array as $k=>$v) {
$str .= “$k=$v”;
}
$str .= $secret;

return md5($str);
}
Once the test was written the code was straightforward:


def signature_from(params={})
request_str = params.collect {|p| "#{p[0].to_s}=#{p[1]}"}.sort.join # build key value pairs, sort in alpha order then join them
return Digest::MD5.hexdigest("#{request_str}#{FACEBOOK_API_SECRET}")
end

And I went through the library like that, simply looking at the behaviour, writing failing tests, then writing the code to make them pass.

In the end, I had a fairly elementary but streamlined library to get any Rails up and working with Facebook in no time. It doesn’t have lots of bells and whistles, but is (hopefully) fairly easy to understand just by reading the code and the tests, and is a cinch to extend and adapt to your particular needs. Oh, and it works for me.

Quick usage guide (for those who don’t like reading code or tests)

1. Download the library from here and the tests from here

2. Put the library in you /lib folder in a file marked facebook_utilities.rb and the tests in your /tests/unit folder in a file called facebook_utilities_test.rb

3. Update the FACEBOOK_API_KEY and FACEBOOK_API_SECRET constants for the ones Facebook gave you when you registered your application (see here for a good article which explains how to do that, and much more).

4. Put “include FacebookUtilities::ControllerUtilities” at the top of any controller you want to Facebook-enable, or at the top of the Application controller if you want it for all your controllers.

5. Call the Facebook object from your controllers using the #facebook convenience method, e.g. facebook.user returns the facebook user id of the user who’s logged into your Facebook app.

There are also a couple of utility methods to use with before_filters: #require_added_fb_app, #require_logged_in_to_fb_app, and #require_fb_frame. These restrict access to facebook users who have, respectively: added your Facebook application, logged in to your Facebook application, or are acessing the application with a Facebook frame (either through an iframe or an FBML canvas).

There’s also an #fb_redirect_to method (aping the similar php one) to use for redirecting to other pages while you’re in the Facebook frame. Briefly, this does a FBML redirect if you’re in a FBML canvas, a javascript redirect if you’re going to another Facebook page (you’re probably in an iframe, and doing an ordinary redirect would result in an iframe within and iframe), and a plain old vanilla redirect_to otherwise.

6. Call any of the methods in the API using a ruby-ized version of the method name preceded with ‘fb_’. So, groups.getMembers which requires a of group id is called by facebook.fb_groups_get_members(:gid => “12345”). You don’t need the api_key, session_key, call_id, sig, or v parameters that are standard required parameters. These are supplied by the facebook object automatically (after all it wouldn’t be much of a Facebook object if it didn’t).

Still confused? Have a play with a dummy app in iframe mode with your app in development mode and a Facebook controller (put something like localhost:3000/facebook in the canvas URL) and check out the requests and responses the console spits out. In development mode, the library logs all the calls and responses to and from Facebook.

Not enough? If the demand is there I might do a bit of a tutorial if I can get the Autopendium Facebook app finished in the next day or so (still getting the FBML right, but that’s another story)

Cheers
Chris
p.s. Sorry about the code formatting. Still getting to grips with WordPress

Updated 15 Aug 07: Added info about controller methods

Written by ctagg

August 15, 2007 at 7:15 pm

Posted in facebook, rails, ruby, testing

Rails, Ruby, Facebook and tests — my own itch scratched

with 8 comments

So I wanted to add a Facebook interface to Autopendium :: Stuff about old cars, the classic car community website that I’ve recently launched (that’s written using Ruby on Rails)

First thought, there must be a Ruby library for Facebook of some sort, probably even a Rails plugin — and there is. But almost from the get-go I had a few problems with it.

1) It was a gem. No problem with that as such, although I tend to be a bit more wary about installing them — they tend to make the code a bit more opaque (i.e. it’s not in my Textmate project drawer), they’re harder to tweak (plugins are dead easy in that respect, especially if you’re using piston), and they can add deployment issues when your in production and deployed across several servers (and yes, I know you can freeze the gems using gemsonrails)

2) There were no tests. Not one. And for dealing with something like Facebook, where authentication and user info was involved that seemed not such a good idea.

It also made it more difficult to understand how the code (and the Facebook API) worked (see 3, below). Perhaps I’d been spoilt by Technoweenie’s plugins that are pretty much solely documented by their tests, but this felt a bit unrailsy to me. So I decided to take a look at the code to see what was going on, perhaps even write a few tests, and I found that…

3) I didn’t understand the code. No, seriously. Felt like I was a 10-year-old reading Joyce. There’s a couple of pretty good posts out there walking you through using the library and setting up a Rails app to power a Facebook app, and by playing around a bit I could get some things working.

But it felt like trying to write a Rails app before I’d read David Black’s excellent Ruby for Rails. I could make things work, but didn’t understand why or how they worked, or why they sometimes didn’t, and that didn’t feel good. Plus, looking at the gem’s code made me more confused because it didn’t really feel like the Ruby I’d come to love and (to an extent) know.

So, I decided that as this must be my problem (nobody else seemed to have a big issue with it), so I should solve it myself. So I sent a couple of days reading the Facebook API, the dummy php app and library that Facebook provides, and doing some tests and some code.

The result is a (very) basic Facebook library that Works For Me. No, really, I mean basic. It’s a single file that I’ve put in my Rails lib folder and include in my facebook controller, and provides access to a Facebook session object.

I haven’t even made a plugin of it yet. Didn’t need to (though it shouldn’t take many minutes, and if there’s any interest in it, I will). But I have written a fair number of tests for it (in fact the tests came first, before the code), and I do feel a have a better understanding of Facebook’s Request/Response cycle, and how to tie that in to an app, especially an existing one like Autopendium. And my itch is now pretty much scratched.

Tomorrow I’ll do a brief walk through the hows and whys of the code, but if anyone wants the code in the meantime (and please do let me know via the comments if/when you find bugs, or generally want to sound off about the code), I’ve pasted it here and the test suite here.

Update: The walkthrough of the library is here

Written by ctagg

August 14, 2007 at 4:23 pm

Posted in facebook, rails, ruby, testing