Active Resource in practice
I’m working on app to integrate Pivotal Tracker and Harvest. There’s a great ruby wrapper around Harvest’s API, but there isn’t a decent Ruby wrapper for Tracker’s v3 API, so I thought I would just build one as I needed it.
If this app were read only, I would probably use HTTParty and HappyMapper, but since I also want to be able to update timers and stories, Active Resource seemed like the right tool for the job. Active Resource in theory is great. Active Resource in practice is not so great. I’ve toyed around with it in the past, but using it for something real I found it…lacking.
Fortunately the Harvest gem had solved a lot of these problems. I write about them here in hopes that they will be useful to you.
Challenge 1: Headers don’t inherit
Pivotal Tracker uses a token for authentication and looks for it in a header called “X-TrackerToken”. It would be nice if you could just set this once, and all Active Resource classes would use it. But unfortunately, headers don’t inherit.
So the trick is to define a base class for all of your models to inherit from, and in that override how Active Resource treats headers.
module PivotalTracker class Resource < ActiveResource::Base Resource.site = "https://www.pivotaltracker.com/services/v3" class << self # If headers are not defined in a given subclass, then obtain # headers from the superclass. def headers if defined?(@headers) @headers elsif superclass != Object && superclass.headers superclass.headers else @headers ||= {} end end end class Project < Resource end end
Now we can set our token once and subclasses will inherit it:
PivotalTracker::Resource.headers['X-TrackerToken'] = "mytoken" projects = PivotalTracker::Project.find(:all)
Challenge 2: “Associations”
I find it strange that Active Resource doesn’t support associations. Rails has a standard way of defining embedded resources, so you would think that Active Resource would have a standard way of consuming them (I know, I should get off my lazy duff and contribute a patch, but it’s so much easier to just complain about it).
So for APIs that have nested resources like Pivotal Tracker’s, Active Resource forces you to hard code the parent resource id. If you want to get the iterations for a project, then you have to set the project_id on the Iteration resource.
PROJECT_ID = 1738 module PivotalTracker class Iteration < Resource self.prefix = "/services/v3/projects/#{PROJECT_ID}" end end
This is just not a scalable solution. I’m going to need to be able to access multiple projects in the app that I’m working on. So the Harvest gem had a really clever (and evil) solution, which I’ve modified a bit here.
It basically involves creating an anonymous subclass of our resource, and setting the prefix just for that subclass.
module PivotalTracker class Project < Resource def iterations Iteration.build_subclass.tap do |iteration| iteration.prefix = "/services/v3/projects/#{self.id}" end end end end
Now we can access iterations for any project.
iterations = Project.find(x).iterations.find(:all)
The #build_subclass method is defined on the base resource and just creates an anonymous subclass and copies some settings that don’t inherit.
Onward Ho!
I don’t have a lot built out yet for the new Pivotal Tracker wrapper, but you can check out the latest progress on GitHub. I feel like I’ve overcome most of the bit barriers, so it shouldn’t take much to finish it up.
Do you have any other tips or tricks for working with Active Resource?
10 Comments
Thanks Brandon, stumbled across this post while looking for a solution to the inheritable headers problem. Double lucky for me as I was actually working on a ARes wrapper for Pivotal Tracker, so will fork yours instead.
Awesome. I look forward to seeing your changes.
I think the statement that you need to hard code project_id is incorrect. See my blog post:
http://wholemeal.co.nz/blog/2010/03/08/active-resource-associations-and-nested-resources
@malclocke:
Awesome, Thanks! I didn’t know about that.
I did a pull request to rails that adds associations to active resource:
http://github.com/rails/rails/pull/70
I hope that will be applied
Nice writeup. I’m working with ActiveResource now (service-oriented ecosystem) and kind of a POS. Knowing what you know now, would you use something else, like Typhoeus, etc, and roll more of it yourself?
Jesse,
Depends on the situation. If I needed ActiveRecord-like CRUD, I would probably still use ActiveResource, just because I like how similar it is.
Wow, thank you very much for “Challenge 1.” I tried to figure out why headers for my resources are not being sent out, when I stumbled upon this awesome article! Kudos for you, Brandon! :D
Thanks for this. A newbie’s question. I am lost on which file to add the header below:
PivotalTracker::Resource.headers[‘X-TrackerToken’] = “mytoken”
projects = PivotalTracker::Project.find(:all)
I use ActiveResource in poject where server uses pagination and return information about record in headers. To make it work I created a patch for connection. It stores headers before objects loading from response.
http://phpblog.com.ua/2012/01/rails-activeresource-i-zagolovki/
Post a Comment