Writing Plasma Data Engines in Ruby

It sounds as though exciting things are happening at the Milan Tokamak Plasma sprint, with an api review and the Widgets on Canvas changes happening at the moment. Meanwhile, I've been having my own 'mini-sprint' this last week in Gran Canaria. I've ported the digital clock as well as the analog one, along with the web applet, plasmoid viewer and data engine browsers apps, and the time data engine to Ruby.

I'll describe how the 'ruby-time' Data Engine code compares with the C++ one, and how the .desktop file looks like for a Ruby data engine plugin. This is the code for the time engine:

require 'plasma_applet'

class TimeEngine < Plasma::DataEngine

  def initialize(parent, args)

    # To have translated timezone names
    # (effectively a noop if the catalog is already present).

  def sourceRequested(name)
    # puts "TimeEngine#sourceRequested #{name}"
    return updateSource(name)

  def updateSource(tz)
    # puts "TimeEngine#updateTime"
    localName = I18N_NOOP("Local")
    if tz == localName
        setData(localName, I18N_NOOP("Time"), Qt::Variant.new(Qt::Time.currentTime))
        setData(localName, I18N_NOOP("Date"), Qt::Variant.new(Qt::Date.currentDate))
        # this is relatively cheap - KSTZ.local is cached
        timezone = KDE::SystemTimeZones.local.name
        newTz = KDE::SystemTimeZones.zone(tz)
        unless newTz.valid?
            return false

        dt = KDE::DateTime.currentDateTime(KDE::DateTime::Spec.new(newTz))
        setData(tz, I18N_NOOP("Time"), Qt::Variant.new(dt.time))
        setData(tz, I18N_NOOP("Date"), Qt::Variant.new(dt.date))
        timezone = tz

    trTimezone = i18n(timezone)
    setData(tz, I18N_NOOP("Timezone"), Qt::Variant.new(trTimezone))
    tzParts = trTimezone.split("/")

    setData(tz, I18N_NOOP("Timezone Continent"), Qt::Variant.new(tzParts[0]))
    setData(tz, I18N_NOOP("Timezone City"), Qt::Variant.new(tzParts[1]))

    return true

In the constructor, setMinimumUpdateInterval(333) specifies that the data should be update every third of a second. The engine implements the method updateSource() and uses setData() calls to pass the new values. Note that because Ruby doesn't have implicit constructors, you have to create Qt::Variants explicitely. Similarly, in the above example an instance of KDE::DateTime::Spec needs to be created, whereas in C++ it happens 'behind the scenes'.

The .desktop file is called plasma-dataengine-ruby-time.desktop and here are the entries for the a Ruby engine:

[Desktop Entry]
Name=Time Data Engine
Comment=Time data for Plasmoids

The expected class of the data engine is derived from the ruby source filename, and so in this case 'TimeEngine' should be in time_engine.rb. The service type is just the same as for a C++ engine, and the X-KDE-Library and X-KDE-PluginKeyword entries specify how to load the ruby code. The library used is the same 'krubypluginfactory' for all KDE Ruby plugins.

To install the engine, there is a very simple two line CMakeLists.txt file:

install(FILES plasma-dataengine-ruby-time.desktop DESTINATION ${SERVICES_INSTALL_DIR} )
install(FILES time_engine.rb DESTINATION ${DATA_INSTALL_DIR}/plasma-ruby-engine-time)

All the example code is now under kdebindings/ruby/plasma/examples, and I've moved the clock example from the playground to there. One issue that I've come across is that in Ruby you need to make sure that the class names of your applets are unique. For example, both the digital and analog clock classes were called 'Clock' in C++, and that meant you couldn't run them at the Ruby versions at the same time. So I've renamed them DigitalClock and AnalogClock respectively. Similarly, the classes in the .ui files needed renaming as they were both called 'clockConfig' in the C++ versions.

Tobias Koenig has written a nifty Ruby plasma applet that tells fortunes, and he will be giving a talk in May about Plasma, and using scripting languages with Plasma. I think writing applets and engines in Ruby is great fun anyway, and hope other people will start whipping them up soon..


This was a nice read!
Is the reason for two separate paths for timezones that getting local time+date is faster?


By apanloco at Mon, 04/14/2008 - 14:27

Yes, I think so - that's what the C++ engine did. A comment says 'this is relatively cheap - KSTZ.local is cached', and so I assume only local timezone stuff is cached.

By Richard Dale at Mon, 04/14/2008 - 16:07

"In the constructor, setMinimumUpdateInterval(333) specifies that the data should be update every third of a second."

not quite. it means that the data will never ever be updated any more frequently than 3 times a second. the exact update interval can be chosen by the applet using it - so it could be slower.

By chani at Mon, 04/14/2008 - 16:20