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 inC:\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.