giant robots smashing into other giant robots

Written by thoughtbot

dancroak

Recipe: Google Calendar

A recipe for accessing Google Calendar data from Ruby.

Why?

Google Calendar is a great interface for a few people to share management of events. You might have a client that has this need. Neither you nor the client want to spend time or money duplicating an ‘admin interface’ for managing events. You want to focus development time on displaying those events in a custom way.

Ingredients

Google Calendar

For a Google Calendar you own, navigate to the “Calendar Details” page:

Google Calendar details

Your calendar has an address and can be accessed as XML/RSS, iCalendar, or HTML.

A good example of the Google Calendar embed option is Betahouse’s calendar.

iCalendar

For this recipe, we’ll use the iCalendar format. I tried GET’ing the XML version and parsing it with Nokogiri but found the iCalendar version more manageable.

Copy the “ICAL” button’s value. Save it in a comment in your code for now. Also download it. We’ll use it for our spec:

describe Tour do
  before do
    ics   = File.join(File.dirname(__FILE__), 'local_copy.ics')
    @tour = Tour.new(ics)
  end

  it "should find upcoming gigs" do
    @tour.upcoming_gigs.all? { |gig| gig.dtstart.should > DateTime.now }
  end
end

Make it go:

require 'open-uri'
class Tour
  GOOGLE_ICS = "http://google.com/calendar/ical/you@gmail.com/public/basic.ics"

  attr_accessor :cal

  def initialize(ics = GOOGLE_ICS)
    self.cal = Icalendar.parse(open(ics).read).first
  rescue *HTTP_ERRORS => error
    HoptoadNotifier.notify(error)
  end

  def upcoming_gigs
    cal.events.select  { |event| event.dtstart > DateTime.now }.
               sort_by { |event| event.dtstart }
  end
end

The default behavior uses the remote Google ics file, but we set up a constructor to allow us to easily replace the file in our specs.

The rescue is optional. HTTP_ERRORS is an Array from Suspenders I use in these circumstances in combination with Hoptoad to be notified if the HTTP calls to a remote service stop working for some reason.

If you wanted to get really fancy, you could have a nightly cron job that downloads a new copy of the .ics file and runs the spec on CI.

Example web app

I happened to be doing this in order to style the calendar nicely in a web app. He’s my Sinatra route…

get '/tour' do
  erb :tour, :locals => { :gigs => Tour.new.upcoming_gigs }
end

.. and the relevant portion of the view:

<ul class="gigs">
  <% gigs.each do |gig| %>
    <li>
      <%= gig.dtstart.strftime("%B %d, %Y %I:%M %p") %>
      <a href="http://maps.google.com/maps?hl=en&q=<%= gig.location %>"><%= gig.summary %></a>
    </li>
  <% end %>
</ul>

Check out the live example.

Bon appétit!

emill

Jester In The News

Jester is seeing a little action nowadays. The Google Code Blog has a post today about Google Gears on Rails, a Rails plugin by Michael Marcus and Rui Ma, that extends Jester to wrap over Google Gears. It effectively lets your controllers read and write directly to your users’ machines, so they can use your app offline. Check out the install and usage instructions for details, and listen to the Google Developer Podcast episode where the authors are interviewed.

Nat Budin also just announced that he used Jester to build an in-place-editor plugin called JIPE. He’s built some pretty cool Rails helpers for in-place editing that are more sophisticated than what Rails offers by default. Check out the README for install/usage instructions, it’s pretty easy.

These two projects are great evidence that Jester makes for a great supporting library, something easily built on and extended. Rails’ REST conventions are powerful stuff, and I hope people keep thinking of more ways to prove that.

Using Jester
Jester on Github