---
title: 'Recipe: Google Calendar'
teaser: A tutorial for consuming Google Calendar data in Ruby.
tags: web,ruby,testing
author: Dan Croak
published_on: 2010-02-15
---

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

* [icalendar](http://gemcutter.org/gems/icalendar) gem
* [open-uri](http://www.ruby-doc.org/stdlib/libdoc/open-uri/rdoc) from the
  standard library

## Google Calendar

For a Google Calendar you own, navigate to the "Calendar Details" page.

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](http://betahouse.org/calendar/).

## iCalendar

For this recipe, we'll use the iCalendar format. I tried GET'ing the <abbr
title="Extensible Markup Language">XML</abbr> 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:

```ruby
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:

```ruby
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](https://github.com/thoughtbot/suspenders/blob/master/templates/errors.rb)

I use in these circumstances in combination with
[Hoptoad](http://hoptoadapp.com) 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 <abbr
title='Continuous Integration'>CI</abbr>.

## Example web app

I happened to be doing this in order to style the calendar nicely in a web app.

Here's my Sinatra route...

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

... and the relevant portion of the view:

```erb
<% gigs.each do |gig| %>*   <%= gig.dtstart.strftime("%B %d, %Y %I:%M %p") %>
    ["><%= gig.summary %>](http://maps.google.com/maps?hl=en&q=<%= gig.location %)
```

Check out [the live example](http://projectbluebook.fm/tour).

Bon appétit!
