Fun with the Kiko API and Ruby, Part 1

Introduction

If you've been meaning to write an application that interfaces with Kiko, our web-based calendar application, this article series is for you! Over the next little while, this series of articles will explore the Kiko API in depth. Since we'll be working with simple examples in the Ruby programming language, it's also an opportunity for you to take Ruby for a test spin if you haven't done so already.

What You Need

In order to work with the Kiko API, you need:

  • Ruby. It's "the new hotness" in programming languages, and the programming language in which Kiko was implemented. The library that we'll be using to use the API is written in Ruby as well (we'll put out libraries for other languages soon). If you don't have it on your system, download it from the Ruby site. If you need gentle and whimsical instructions for installing Ruby, I recommend the tutorial titled The Tiger's Vest.
  • A Kiko account. You can sign up for one at the Kiko site.
  • The Kiko Ruby library. You can get it here; save it into the appropriate Ruby "lib" directory as kiko.rb on your machine (on my Mac, it's in /usr/local/lib/ruby/1.8, on my Windows box, it's in C:\ruby\lib\ruby\1.8).
  • An API Key. You can request one from the Kiko API page; it will be emailed to you in short order.

irb, the Interactive Ruby Shell

Ruby comes with irb, the interactive Ruby shell, which lets you enter Ruby code and see results immediately. To start it, go to the command line and enter irb (I'm assuming that you've already installed Ruby on your system). You'll be greeted with a prompt that looks like this:


irb(main):001:0> 

irb is now ready to accept Ruby code that you type in. It never hurts to break in a new programming language with "Hello, world!":


print "Hello, world!"

Ruby will reply with:


Hello, world!=> nil

irb first responded by printing "Hello, world!", followed by "=>" and nil, which is the the return value of the print "Hello, world!" statement. irb always responds this way. For instance, if we assign the value 99 to the variable beers_on_wall, this is what happens:


beers_on_wall = 99
=> 99

If you'd like a good introduction to irb, see the tutorial The Tiger's Vest.

Arrays and Hashes

I'm assuming that you're a programmer, so I'll just cover those things in Ruby that might seem unfamiliar or Ruby-sepcific.

Ruby arrays are like arrays in other dynamically-typed languages: they're orded lists, they can be declared on the fly and grow or shrink as needed. Array literals are contained in square brackets. Try this in irb:


grocery_list = ["apples", "bananas", "chainsaws"]
=> ["apples", "bananas", "chainsaws"]

How can you be sure it's an array? Easy -- use the class method to ask grocery_list what type of object it is:


grocery_list.class
=> Array

Like most programming languages, Ruby arrays are indexed by number, starting at 0:


grocery_list[0]   
=> "apples"
grocery_list[2]
=> "chainsaws"
grocery_list[1]
=> "bananas"

You can use the length method or its alias, size to see how many elements are in an array:


grocery_list.length
=> 3
grocery_list.size
=> 3

Ruby's Array class is pretty handy; I recommend that you check out the "Array" page in the Ruby documentation to see what it can do. Of course, if you'd rather have Ruby tell you what it can do, that's always possible, since all objects have a method called methods, which returns an array of all methods to which the object can respond:


grocery_list.methods
=> ["respond_to?", "delete_if", "index", "select", ...]

Every object has a methods method, and everything in Ruby is an object. Even the number 5 is an object:


5.methods
=> ["to_a", "%", ">", "divmod", ...]

The other data structure you'll use often in Ruby is the hash, which is an unordered collection of key/value pairs. Depending on which programming languages you use, you may know them as "dictionaries". Hash literals are contained within braces:


person = {"name" => "Joey deVilla", "rank" => "Generalissimo", 
"serial_number" => "007"}
=> {"name"=>"Joey deVilla", "rank"=>"Generalissimo", "serial_number"=>"007"}

Accessing the contents of a hash is easy:


person["name"]
=> "Joey deVilla"
person["rank"]
=> "Generalissimo"
person["serial_number"]
=> "007"

As you might expect, hashes know that they are hashes:


person.class
=> Hash

...and you can get the number of items in a hash, using either the length or size method:


person.length
=> 3
irb(main):022:0> person.size
=> 3

You can request the keys and values in a hash, which will be returned to you in an array:


person.keys
=> ["name", "rank", "serial_number"]
person.values
=> ["Joey deVilla", "Generalissimo", "007"]

Appointments, Appts and ApptInstances

In Kiko, we refer to entries you stick into your calendar as appointments. The meeting you have scheduled for next Tuesday at 10 a.m., the concert you want to attend next week and your best friend's birthday -- all of those are appointments.

For every appointment in your calendar, Kiko associates two objects with it: an Appt object and an ApptInstance object. Having two objects for each appointment may seem strange at first, but it'll make sense in a moment.

The easiest way to explain what the Appt and ApptInstance objects are for is to use an example. Let's consider two different appointments: a concert that you will attend next week and your best friend's birthday.

Let's consider the concert. It's a one-time only event. It will have one Appt object, and this Appt object will have one ApptInstance object associated with it, as shown below:

Now let's consider your best friend's birthday. It's a recurring event -- there'll be one this year, one next year, one the year after that, and so on. Like the concert, it will have one Appt object. Unlike the concert, the Appt object will have several ApptInstance objects associated with it: one for this year, one for next year, one for the year after that, and so on, as shown below:

Simply put, an Appt object represents the appointment in general, while ApptInstance represents a specific instance of that appointment.

That's enough theory; let's actually use the API!

Let's Take the API for a Test Drive

First, a little setup might be required. If you've just signed up for a Kiko account, your calendar will be empty. Just so we'll have an appointment to fetch from the calendar, manually add an appointment and make a note of its date.

In order not to mess up your schedule, it's a good idea to set any appointments that you're using for testing the API in the past. In this example, I've created an appointment for the morning of January 1, 2006:

Now that there's an appointment in the calendar, let's read it programmatically. Go to the command line and start up the interactive Ruby shell, irb. The first thing you'll want to do is load the Kiko library by entering:


require 'kiko'

You send API commands to Kiko through an instance of the KikoSession class:


ks = KikoSession.new 'your api key', 'your username', 'your password'

The KikoSession class' initializer method takes three arguments:

  • Your API key
  • Your Kiko calendar username (optional, defaults to nil)
  • Your Kiko calendar password (optional, defaults to nil)

Only the API key is required, but providing your username and password specifies that the default calendar on which to perfom operations is yours.

Let's programmatically retrieve the data for the appointment you created moments ago. In my case, I want to get all the appointments I've made for January 1, 2006. I'd enter this into irb:


my_day = ks.appts '2006-01-01', '2006-01-01'

The appts method retrieves appointments for a specified date range. It takes three agruments:

  • A start date
  • An end date
  • A username (optional, defaults to the username you provided when initializing the KikoSession object)

The appointment data returned by Kiko is now stored in my_day. Let's see what data type it is:


my_day.class
=> Hash

A hash, eh? Let's see what its keys are:


my_day.keys
=> ["ApptInstance", "Appt"]

Those keys should be recongnizable: their names are the same as the two types of objects that are associated with an appointment. Let's look at the my_day["Appt"] object first:


my_day["Appt"].class
=> Hash

my_day["Appt"].keys
=> [510205]

(Of course, when you check my_day["Appt"].keys, you'll probably get a different number than the one shown above.)

Let's see what's my_day["Appt"][510205] is and what's in it:


my_day["Appt"][510205].class
=> Hash

my_day["Appt"][510205].keys 
=> ["created_on", "date_only", "title", "attendees", 
"recur_rule", "updated_on", "id", "website", "description", 
"visible_to", "end_time", "location", "start_time"]

From the looks of it, we've finally reached the Appt object for our hangover recovery session scheduled on January 1, 2006. Let's get a look at the data:


my_day["Appt"][510205]     
=> {"created_on"=>Thu Sep 28 00:52:44 UTC 2006, "date_only"=>false, 
"title"=>"Recover from hangover", "attendees"=>{513993=>{"user"=>{"company"=>nil, 
"created_on"=>Mon Sep 25 02:47:33 UTC 2006, "home_fax"=>nil, 
"job_title"=>nil, "last_login"=>Thu Sep 28 00:50:25 UTC 2006, 
"name"=>"Tucows Example", "work_phone"=>nil, "home_mobile"=>nil,
"language"=>nil, "first_day_of_week"=>0, "timezone"=>nil, 
"work_fax"=>nil, "id"=>102572, "work_mobile"=>nil, "birthday"=>nil, 
"european_style_dates"=>false, "home_phone"=>nil, "pager"=>nil, 
"twenty_four_hour_time"=>false, "webpage"=>nil, "home_address"=>nil, 
"login"=>"tucowsexample", "work_address"=>nil}, "accepted"=>2, 
"appt_id"=>510205, "created_on"=>Thu Sep 28 00:52:44 UTC 2006, 
"service_login"=>nil, "updated_on"=>Thu Sep 28 15:51:37 UTC 2006, 
"contact"=>nil, "contact_id"=>nil, "id"=>513993,
"owner"=>true, "user_id"=>102572, "labels"=>{}, "invited_by"=>nil, 
"service_login_id"=>nil}}, "recur_rule"=>"", 
"updated_on"=>Thu Sep 28 00:52:44 UTC 2006, "id"=>510205, 
"website"=>"", "description"=>"", "visible_to"=>1, 
"end_time"=>Sun Jan 01 12:00:00 UTC 2006, "location"=>"", 
"start_time"=>Sun Jan 01 11:00:00 UTC 2006}

That's a lot of data to look at. Let's thin it out a little by referring to the list of keys we got earlier and using some of them to retrieve the most basic calendar information: the name of the appointment, when it starts and when it ends:


my_day["Appt"][510205]["title"]
=> "Recover from hangover"

my_day["Appt"][510205]["start_time"]
=> Sun Jan 01 11:00:00 UTC 2006

my_day["Appt"][510205]["end_time"]
=> Sun Jan 01 12:00:00 UTC 2006

Let's take a look at the my_day["ApptInstance"] object now:


my_day["ApptInstance"].class
=> Hash

my_day["ApptInstance"].keys 
=> ["510205_2006-01-01"]

Note the name of the key: 510205_2006-01-01. 510205 was the key corresponding to the Appt object, and 2006-01-01 is the date of the appointment.

You've probably inferred that if we change our hangover recovery appointment into a recurring one (and why not? New Year's Eve happens every year!) and if we fetch several years' worth of appointments, we'd see more ApptInstance objects with keys like 510205_2007-01-01, 510205_2008-01-01 and so on.

Let's get a closer look at this object:


my_day["ApptInstance"]["510205_2006-01-01"].class
=> Hash

my_day["ApptInstance"]["510205_2006-01-01"].keys 
=> ["appt_id", "all_day", "orig_date", "id", "is_exception", 
"end_time", "start_time"]

my_day["ApptInstance"]["510205_2006-01-01"]     
=> {"appt_id"=>510205, "all_day"=>false, "orig_date"=>#<Date:
4907473/2,0,2299161>, "id"=>"510205_2006-01-01", "is_exception"=>false,
"end_time"=>Sun Jan 01 12:00:00 UTC 2006, 
"start_time"=>Sun Jan 01 11:00:00 UTC 2006}

As you've probably guessed, the appt_id key gives you the ID of the Appt object that this particular ApptInstance belongs to.


That's all for this session. In the next article in this series, we'll look at the other things you can do with the API, namely creating, updating and deleting appointments.

Comments
Post a comment
No comments found.
Post comment:
Format Type: 
  Convert newlines
  Receive comment notifications for this article
Subject: 
   
insert bold tagsinsert italic tagsinsert underline tagsinsert strikethough tagsinsert linkinsert blockquote tags
Comment: 
Comment verification:

Please enter the text you see inside the graphic to post your comment:
You are not currently logged in. If you would like your user information to be displayed with your comment, please enter your login information below.
Login information:
Username: 
Password: 
If you would like to post contact information on your comment, please enter your information into the optional fields below:
Contact information:
Name: 
URL:  example: http://yourdomain.com
Email: 
Please note: email will not be displayed on the site, only for the blog owner. If logged in, URL will only be used.