Check out Koala’s official documentation on our Github wiki! It has all the same content, but as of version 0.5.0, the wiki is more up-to-date.
I’m excited to announce the first release of Koala (http://github.com/arsduo/koala), a lightweight and flexible Facebook Graph library for Ruby.
Introductions all around
Using Koala, you have complete access to the Facebook Graph API — in a moment, we’ll go through how to use it to read from and write to the social graph. Before we dig into this very long blog post, though, it’s worth a moment to go over our four goals for the library.
Koala’s goals
- Lightweight: Koala should be as light and simple as Facebook’s own new libraries, providing API accessors and returning simple JSON. (We clock in, with comments, just over 300 lines of code.)
- Fast: Koala should, out of the box, be quick. In addition to supporting the vanilla Ruby networking libraries, it natively supports Typhoeus, our preferred gem for making fast HTTP requests. Of course, That brings us to our next topic:
- Flexible: Koala should be useful to everyone, regardless of their current configuration. (We have no dependencies beyond the JSON gem. Koala also has a built-in mechanism for using whichever HTTP library you prefer to make requests against the graph.)
- Tested: Koala should have complete test coverage, so you can rely on it. (By the time you read this the final tests are being written.)
Also, I want Koala to be great Ruby, and to be responsive to your needs and suggestions. This is my first gem, so please, send me any suggestions, feedback, or comments you have.
History for the historically minded (the rest of you can skip to the code)
How Koala came about: I was lucky to go to Facebook’s F8 conference a week and a half ago, and even luckier to have the chance to meet several Facebook Platform engineers (a big thank-you to Context Optional, my employer, for making both possible). As you may know, Facebook released their new Graph API at F8 — a much, much, much simpler way of accessing Facebook data. The Facebook engineers I met there were justifiably proud of the new interface and how its simplicity allowed them to write a complete Python library in just over 200 lines of code.
As we were talking, those engineers offhandedly suggested someone port their new library to Ruby — a challenge too good to refuse. The public beta of their new Javascript SDK had already won me over to the simple API philosophy of a generic API accessor providing JSON output, and I jumped at the chance to bring that approach to Ruby. By the end of the conference, I could fetch data, and by the end of the night, I had read/write access to the entire social graph. I’ve spent the next week building out the library with Rafi Jacoby at Context Optional, and now the Koala gem is ready for use.
Playing with Koala is every bit as warm and cuddly as the real animal would be in a perfect world.
Getting the Code
Let’s start by installing the gem:
gem install koala # sudo if needed
Once that’s done, using Koala is simple:
require 'rubygems'
require 'koala'
#
# initialize a Graph API connection
@graph = Koala::Facebook::GraphAPI.new
A note on namespaces
To avoid conflict with anyone else’s Facebook class or module, I’ve namespaced everything inside the Koala module. I highly recommend you add ‘include Koala’ (or even ‘include Koala::Facebook’) to skip all that wordiness if your environment doesn’t already have a Facebook class.
Graph API: now in your browser!
Important side note: did you know you can play with the Graph API in your browser? It’s one of my favorite aspects of the new API. For each example coming up, I include a link to the equivalent query. If you haven’t been to graph.facebook.com, you should definitely click through and play with the results. (Try your own nickname!) Since I’m abbreviating the API results for space, I strongly encourage you to check it out.
Getting public data
Let’s first get some data from Facebook. To learn about an object, just use the get_object method. For instance, you can learn about people:
# http://graph.facebook.com/koppel
@graph.get_object("koppel")
# => {"name"=>"Alex Koppel", "id"=>"2905623", "last_name"=>"Koppel", "link"=>"http://www.facebook.com/koppel", "first_name"=>"Alex"}
And Pages:
# http://graph.facebook.com/contextoptional
@graph.get_object("contextoptional")
# => {"name"=>"Context Optional, Inc.", "id"=>"7204941866", ....}
Even anonymously, you can get certain connections like a Page’s feed:
# http://graph.facebook.com/contextoptional/feed
@graph.get_connections("contextoptional", "feed")
# => {"data"=> array_of_stream stories }
As well as details about individual public stories:
# http://graph.facebook.com/7204941866_117111188317384
@graph.get_object("7204941866_117111188317384") # remember this ID
# => {"name"=>"Bare Escentuals Cosmetics",
# "message"=>"We've just launched the Bare Escentuals "Mom & Me" application!",
# "likes"=>3,
# ....
# }
You can also searches public posts for the keyword of your choice:
# http://graph.facebook.com/search?q=koala
@graph.search("koala")
# => {"data"=> [{"from"=>{"name"=>"Amy", ...}, "id"=>....,
# "message"=>"My apartment is infested with koala bears...."}]}
Getting Private Data
Public data’s cool, but it’s often not enough. You want to know who your users are — their full details, their likes, their feed, their friends. Without an access token, you can’t get that:
@graph.get_connections("koppel", "likes")
# => Koala::Facebook::GraphAPIError: An access token is required to request this resource.
We’ll go over getting an access token in a moment. For now, head over to http://graph.facebook.com — Facebook’s Graph API developer site has a bunch of links, any of which will provide you with an access token. Copy that from the URL, and check out what you can do now:
# first, initialize a Graph API with your token
@graph = Koala::Facebook::GraphAPI.new(your_oauth_token)
# now, try that query again
# http://graph.facebook.com/koppel/likes?access_token=#{your_oauth_token}
@graph.get_connections("koppel", "likes")
# => {"data"=>[{"name"=>"Mechanics' Institute Library", ....}]}
“me” is a very useful shortcut Facebook provides to getting the current user:
# http://graph.facebook.com/me?access_token=#{your_oauth_token}
@graph.get_object("me")
# => {"name"=>"Alex Koppel", ....}
With a user’s access token, you get any public information, plus additional information through extended permissions (see below).
Writing to the Graph
Reading’s all well and good, but no discussion of the graph would be complete without some writing. In order to write to the social graph, you’ll need to prompt your user for the publish_stream extended permission. (We’ll talk about permissions in a bit.)
For the sake of simplicity, let’s assume the token we got above has the right permissions; otherwise, you’ll get an error. (Since writing to the graph involves sending POST requests, the browser follow-along unfortunately has to end.)
Let’s start by posting to our own walls using the put_wall_post method.
@graph.put_wall_post("hey, i'm learning kaola")
# => {"id"=>"2905623_123183957698327"}
Oops — we misspelled Koala! How embarrassing.
@graph.delete_object("2905623_123183957698327")
# => true
Let’s never speak of that again, okay?
You can also write to other people’s walls. For instance, I’ll send a link to a cool (if sappy) comic to my husband.
@graph.put_wall_post("explodingdog!", {:name => "i love loving you", :link => "http://www.explodingdog.com/title/ilovelovingyou.html"}, "tmiley")
=> {"id"=>"83901496_520908898070"}
Speaking of family, Mother’s Day is coming up. Let’s go back to that earlier post about the Mother’s Day app my company just released, and like it.
@graph.put_like("7204941866_117111188317384")
# => true
Now it has 4 likes:
@graph.get_object("7204941866_117111188317384")
# => {"message"=>"We've just launched the Bare Escentuals "Mom & Me" application", "likes"=>4...}
Personal confession: I still haven’t picked out a gift for my mom. Time to talk about how I need to do that.
@graph.put_comment("7204941866_117111188317384", "Can't believe Mother's Day's just a week away! I'd better get on that gift.")
# => {"id"=>"7204941866_117111188317384_448625"} # ID of the comment
That’s more or less it for the Graph API. All the methods you just saw wrap a generic put_object method, which you can use whenever the convenience methods don’t work for you.
def put_object(parent_object, connection_name, args = {}) ....
Koala also includes an OAuth class, which helps with two common authentication-related tasks: verifying cookies and generates authentication URLs.
OAuth URLs and tokens
In order to use OAuth — a much cleaner way of verifying yourself to Facebook — you have to get tokens for your users. If you’re using the Javascript library, it will provide the OAuth token in the cookies it creates (we’ll talk about verifying those in a moment). If not, don’t worry — there’s a redirect-based method that works too (and can be played with in the browser). To learn more about OAuth, check out Facebook’s page on OAuth authentication at http://developers.facebook.com/docs/api#authorization.
Here’s how to get an OAuth token in two steps:
- First, send your users to the OAuth code URL. Facebook verifies that they’re logged in, want to access your app, and are willing to grant you any permissions you request. They are then redirect the user to a callback on your registered site with an OAuth code in the GET parameters.
- Now that you have the code, you can get the token. (Don’t confuse one for the other; I’ve made that mistake.) You can now send a request to a second OAuth URL with that code, your application’s super-secret code, and some other parameters. Facebook returns the code to you in the body of the response (you should do this from your server behind-the-scenes).
Let’s walk through it together. Koala makes it easy to generate the URLs you need. All you have to do is create an OAuth helper with information for your app. (callback_url is optional, but if you don’t include it here, you have to include it in later calls.)
@oauth = Facebook::OAuth.new(app_id, code, callback_url)
# => #<Koala::Facebook::OAuth:0x1017177b0 @app_id=#{your_app_id}, @oauth_callback_url=#{your_callback_url}, @app_secret=#{your_secret_code}>
Now we can use @oauth to generate the URL for the authentication code. url_for_oauth_code takes a hash, which can include the :permissions you want (either as an array or a comma-separated string) and a :callback URL (in case you want to point the callback to a specific address in your domain).
@oauth.url_for_oauth_code
# => "https://graph.facebook.com/oauth/authorize?client_id=#{app_id}&redirect_uri=#{callback_url}"
@oauth.url_for_oauth_code(:permissions => "stream_publish")
# => "https://graph.facebook.com/oauth/authorize?client_id=#{app_id}&redirect_uri=#{callback_url}
&scope=stream_publish"
Now that you have the URL, you can send your user there. Once she’s logged into Facebook and granted your permissions (if requested), she’ll be redirected to your callback; the code will be in that request’s GET parameters (under the key, you guessed it, code). Now you can generate the URL that will yield up the access token.
@oauth.url_for_access_token(user_code)
# => "https://graph.facebook.com/oauth/access_token?client_id=#{app_id}&redirect_uri=#{callback_url}&
client_secret=#{app_secret}&code=#{user_code}"
Fire off a request to that address, and you’ll get the access token and how long until it expires.
Note: Koala OAuth currently doesn’t include methods to grab and parse the token directly, but those will be coming in the near future.
Cookie Parsing
The other common OAuth related-task is parsing and validating Facebook’s cookies. The new Javascript library (formerly Facebook Connect) is awesome — small, clean, and very useful for adding social touches to your website. It’ll take care of logging the user in and it handles all the OAuth token craziness behind the scenes. All you have to do is verify the cookies are legit — the user is indeed who his browser says he is.
Let’s test that. I’ve taken some real-live cookies from a website my husband and I are working on, which uses Koala for cookie validation. (Sorry about them breaking the formatting. Some cookies just don’t have good manners.)
{"fbs_190889632882"=>"\"access_token=190889632882|2.U25mFtixF8Pqth45AtnsBQ__.3600.1272909600-2905623|DNjswq9Q-fKDrP60TY76Tv8GxCc.&expires=1272909600&secret=QR_id58vqV_qW7MnfJlmLw__&session_key=2.U25mFtixF8Pqth45AtnsBQ__.3600.1272909600-2905623&sig=a76960c0c3669470f7ca53b53e034ac4&uid=2905623\"", [other cookies]...}
The important cookie is the fbs_#{app_id} cookie, which contains a string (complete with inner quotes) containing all the information you need to verify Facebook has, indeed, confirmed this user. To verify the cookies, just instantiate an OAuth instance like you did above (no callback URL needed) and run:
@oauth.get_user_from_cookie(cookies)
# => {"session_key"=>"2.U25mFtixF8Pqth45AtnsBQ__.3600.1272909600-2905623", "expires"=>"1272909600", "uid"=>"2905623", "sig"=>"a76960c0c3669470f7ca53b53e034ac4", "secret"=>"QR_id58vqV_qW7MnfJlmLw__", "access_token"=>"190889632882|2.U25mFtixF8Pqth45AtnsBQ__.3600.1272909600-2905623|DNjswq9Q-fKDrP60TY76Tv8GxCc."
There you go — you have a validated user ID and an access token to boot. Of course, if the cookies aren’t valid (expired, falsified, etc.) the method will simply return nil.
One of my goals in writing Koala is to make a library anyone can use. In particular, I didn’t want to tie the library to a particular mechanism for making network requests to Facebook. Typhoeus may be our library of choice at Context Optional, but Koala doesn’t really care if you’d rather use Curb or HTTParty or just plain old Net::HTTP.
Using Koala with another HTTP library is simple — just write a really quick module defining one class method called make_request:
def self.make_request(path, args, verb)
# make the request
# return the body of the result as a string for JSON parsing
end
When Koala is loaded, it checks to see if Typhoeus is installed; if not, it silently falls back to Net::HTTP for requests. To use your module instead, just call
Koala.http_service = YourModule
The http_service= method will include your module, overwriting the existing make_request method. Voila! You’re done.
(You can check out lib/http_services.rb to see how the built-in HTTP modules are designed.)
This works because all the methods we just saw are wrappers for a method named api, which makes and parses the request. In everyday life you probably won’t need to access the api method directly, but it’s important to know in case you want to extend Koala to use a different HTTP library.
def api(path, args = {}, verb = "get")
# other code....
# make the request via the provided module's make_request method
result = Koala.make_request(path, args, verb)
# other code....
end
Request logging and the like
You can also use the same mechanism to implement request logging. Just stick some logging inside the make_request method — you can even call super to actually send the request.
Koala inside other libraries
Since Koala just implements a basic wrapper for the Graph API, there’s no reason Koala couldn’t be used inside any other systems, including other Facebook libraries that provide higher-level data encapsulation and convenience methods. Just sayin’.
Koala’s going to have full test coverage. Right now, Koala::Facebook::GraphAPI has two test suites — one with and one without an access token — which put it through its paces. I’m still working on the OAuth test suite and plan to have that update out within a week.
The tests are written in RSpec and can be run by calling
spec koala_tests.rb
from the test directory. (I hear it’s proper form to include a Rake task that runs the tests. Stay tuned.)
Test data
Since we’re using the Facebook API, we have to play by the rules of the Facebook API. Unfortunately, that means using cookies and OAuth access codes that expire. (While we could stub the Facebook API, that wouldn’t get us much by way of testing, given how simple Koala is.) So, you’ll need to supply your own information in tests/facebook_data.yml. Sorry! Let me know if you have a better idea.
Tests and Graph nodes
When you run the tests with an access token, the put_object tests will create and then immediately delete several wall posts on your own wall. As Koala cleans up after itself, it also outputs (via puts) the IDs of each node created, in case anything goes wrong.
Wrapup
Well, that’s about it — thanks for making it all the way through this very long blog post! I’m very excited about the launch of Koala (and a bit nervous – it’s my first gem, after all). I hope you’ll find the Koala library useful as you implement Facebook’s new Graph API.
Stay tuned for more updates on Github — there’ll be more tests, new OAuth methods, and no doubt further changes on the way in the next week or two.
If you have any questions/comments/bug reports, feel free to drop me a message on Github. I’d love to hear from you.
Cheers!
Alex
http://github.com/arsduo/koala