SWIGForEDSTutorial

From Evolution

Contents

Introduction

This document is intended to be a guide for the SWIG for EDS bindings user. As so, it will include a description of the generic SWIG bindings and their functionality. When examples are due, we will be using Ruby as our coding language, although we will not use any of the ruby specific extensions. Such extensions (as other language specific extensions) can be found documented here.

Overall design

The overall design of the bindings closely mimic that of the e-d-s C library with some variations where those are fit. As a rule of thumb, one can consider that an e-d-s class corresponds to a binding class. There are however exceptions to this rule. As a second rule of thumb the following type of objects are translated to language specific types:

  • GList, GSList, etc.
  • Date and Time structures (ECalDateTime, icaltime. etc).
  • Primitive types (gint, gstring, gboolean, etc).
  • Any non Glib object will not be mapped.

Such conversions are done, and hopefully documented, in the language specific SWIG file (ruby.i for the ruby set of bindings)

Object mapping

Since e-d-s API is clearly object orientated, the bindings try hard to map getter/setter functions to properties. for example a call to e_source_peek_name(src) is mapped as an access to src.name property. More complex access like obtaining a GSList property work as expected and are translated to language specific classes (for example, in Ruby, an object of class GSList will get translated to an object of class Array). Some working examples, again in Ruby, follow:

irb> require 'evolution'
irb> src = Evolution::ESource.new("name","uri://",relative)  #creation of an ESource object
irb> src.name = "new_name" 
"new_name"  #setting it's name returns the new value
irb> puts src.name # print the name to ensure it is correct...
"new_name"	

irb> #get a Ruby Array containing the EsourceGroups present in the computer
irb> groups = Evolution.get_esource_groups # groups is now an ordinary Ruby array containing ESourceGroup objects
irb> groups.each { |group| puts group.name } # list the name of EsourceGroups

Some equivalences between e-d-s objects and SWIG objects are given in the following table:

e-d-s                          SWIG                     Ruby
================================================================================
GSList                        language specific         Array
GList                         language specific         Array
ECalComponentDateTime         language specific         Time
icaltimetype                  language specific         Time
ECalComponentText             language specific         Array with 2 Strings or a String
ESourceGroup                  ESourceGroup              Evolution::ESourceGroup
ESource                       ESource                   Evolution::ESource
ECal                          ECal                      Evolution::ECal 
ECalComponent                 ECalComponent             Evolution::ECalComponent


Memory management

Memory management is too broad a subject to cover in such little space. However it's important for the users (and programmers) of the bindings to understand the goals SWIGforEDS tries to achieve and the mechanisms that it uses. This will be language specific, but will have consequences that apply to all language bindings the same.

In first place, we must explain the object mappings that SWIGforEDS uses. As can be seen in the diagram below, there are 3 different object layers which correspond to the two layers that are present in e-d-s and the SWIG objects.


                1           *               1            1
Logical object <-------------> Glib object <--------------> SWIG object

The logical object layer represents the logical objects in the model, for instance a calendar event. The Glib object is the glib representation the logical object, for instance, ECalComponent. The SWIG object is that language specific object, for example a Python or Ruby object, that interfaces the Glib object.

As noted in the diagram, Glib objects and SWIG objects are mapped 1 to 1. This is important, specially for binding programmers. because it means that garbage collection issues must be solved as there can be a mismatch between the reference counted GC scheme in GLib and the language specific GC mechanism (mark & sweep in ruby for instance). Also, due to the way objects are mapped in the ruby bindings, only Glib objects can be mapped.

While this should not matter for the ordinary binding user, the fact that 2 differnt GLib objects can refer to the same logical object still remains important. This mean's that ordinary object comparison will not work as most binding users would expect. If for instance we get 2 different ECalComponent objects ev1, ev2 that refer to the same logical event (identified by the same uid) then the comparison ev1==ev2 will return false.

To overcome this, the bindings should try to override the comparison operator where it is fit to do a semantic comparison rather than a mere object identifier comparison. Any exception to this rule should be reported since it probably is a bug in the binding.

Getting started

As a brief introduction we will present an example of a very simple app, a "calendar-grep" written in Ruby. This utility will grep the regular expression we feed it and present us a list of events that fullfill the regular expression (it will check for the summary field) and print some information about them. Nothing easier:

[1] require 'evolution'
    events=[]
    re=ARGV[0]
[2] Evolution::get_esource_groups.each{|grp|
[3]   grp.each{|src|
[4]       cal=Evolution::ECal.new(src)
[5]       events+=cal.select {|ev|			
				 ev.summary if ev.summary.to_a.select {|summary| summary.match(re)} != []
			}
      }
    }

[6] events.each { |ev|
	puts "#{ev.summary}\n\tDescription: #{ev.descriptions}\n\tAt: #{ev.location}\n\tStart: #{ev.dtstart}\n\tEnd: #{ev.dtend}"
    }


Let's see point by point what we are doing.

  • [1] This one is easy. We just load the evolution module and initialize the variables.
  • [2] Here we get a list of all the ESourceGroups present in the computer. ESourceGroups represent, somehow, "kind's" of calendars (they actually are more than that but for now it will suffice), so we will have one for Local calendars, one for Web calendars, etc. So basically what we are doing here is iterate over all the different kind of calendars we have in the system.
  • [3] ESourceGroups contain Esources wich contain info about present calendars in the computer. What we are doing here is iterate over all the calendars of one kind in the system (using the standard ruby idiom for iteration implemented as a ruby specific extension to the SWIG interface). Given the fact that in the outer iteration we were iterating over all kinds of calendars we are actually iterating over all the calendars in the system.
  • [4] We create a new ECal object passing it the ESource. This will actually open the calendar specified by the ESource (passing no ESource to the ECal constructor will open the system calendar)
  • [5] Here we use select (again a ruby idiom) wich will select all events contained in the calendar that fullfill the condition (similar to the list comprehension idiom in Python). Since summary is a mapping of an ECalComponentText it can be a String or an Array of String. Since we want to try to match (standard Ruby method) all the strings in the array we coerce it first to an Array with to_a. in the case summary was a String it will return as an array containing the string. In the case it was already an Array it will return itself. Note that we concatenate all events found in the select invocation with the events array. There are better Ruby idioms for doing this, but I preferred something easier to understand for non Ruby programmers.
  • [6] Iterate over the collected arrays and print them.


And that's all for a getting started guide :-)

API Reference

Evolution functions

The Evolution module has the following general use functions:

functions

List get_esource_groups(): Returns a list of ESourceGroup objects representing all the ESourceGroups present in the system.

ECal API

The ECal class has the following public API:

methods:

	ECal(): Opens the default calendar.
	ECal(ESource src, bool create=false): Opens the calendar specified in src. The create flag determines if the calendar should be created 
                                              in case it does not exist.
 	append(ECalComponent comp): Adds the component comp to the calendar
	del(ECalComponent comp): Deletes the component comp from the calendar
	del(string uid): Deletes the component identified by uid from the calendar.
	operator [](string uid): Retrieves the component identified by uid from the calendar.
	List get_list(): Returns the list of all the ECalComponent objects stored in the calendar.
	string to_string: Returns a valid ical text representation of the calendar.

properties:

	int length: number of elements in the calendar.

ECalComponent API

The ECalComponent class has the following public API:

methods:

	ECalComponent(): creates an empty ECalComponent
	ECalComponent(string ical): creates an ECalComponent that wraps the ical component represented by the string
	ECalComponent(ECalComponent e): Copy constructor
	string to_string: Returns a valid ical text representation of the ECalComponent

properties:

	uid: String representing the ECalComponent uid
	url: String representing the ECalComponent url
	categories: Colon delimited string with a list of categories
	descriptions: Array of descriptions where each element may be an array of 2 strings or a string 
	comments: Array of comments where each element may be an array of 2 strings or a string 
	contacts: Array of contacts where each element may be an array of 2 strings or a string 
	created: Date of creation of the ECalComponent
	completed: Date for completion of the ECalComponent
	summary: String representing the summary of the ECalComponent
	dtend: Date marking the end of the ECalComponent
	dtstart: Date marking the beginning of the ECalComponent
	due: When the ECalComponent is due for
	dtstamp: Last time the ECalComponent was modified

ESource API

The ESource class has the following public API:

methods:

	ESource(string name, string uri, bool relative=true): Create an esource with the name and uri passed. Uri of the
                                                              Esource will be relative to it's parent ESourceGroup according to the relative flag
	string to_string(): Returns a string XML represention of the ESource (as can be seen in GConf)

properties:

	name: A String representing the name of the ESource
	relative: Boolean marking if the ESource uri is relative to its ESourceGroup base uri
	readonly: Boolean marking if the ESource is readonly or not
	uid: String representing the uid of the ESource
	uri: String representing the uri of the ESource
	relative_uri: String representing the uri of the ESource relative to it's ESourceGroup
	absolute_uri: String representing the absolute uri of the ESource
	group: The ESourceGroup to which this ESource belongs

ESourceGroup API

The ESourceGroup class has the following public API:

methods:

	ESourceGroup(string name, string uri): Create an ESourceGroup with the given name and uri.
	operator [](int n): Retrieves the nth ESource from the ESourceGroup
	insert(int index, ESource src): Insert the src at the position given by index
	append(ESource src): Append the source at the end

properties:

	readonly: boolean property marking if the group is readonly
	name: String representing the name of the ESourceGroup
	base_uri: String representing the base uri of the ESourceGroup
	uid: String representing the uid of the ESourceGroup
	int length: number of ESource objects in the ESourceGroup.