Difference between do-end and {} blocks

Posted by Moser on 07 Feb 2010

There is a important difference between the two block syntaxes in Ruby: Their precedence. Consider following code:

def method1(*args)
  puts "method1 got a block" if block_given?
end

def method2(*args)
  puts "method2 got a block" if block_given?
end

method1 method2 do
end

method1 method2 {
}

You would expect both method calls to produce the same result, wouldn’t you? Output:

method1 got a block
method2 got a block

Obviously the do-end block is passed to the first method in the expression while the {} block is passed to the method called directly before it. Another example shows that the assignment does not count as a method call here:

#[...]
class Foo
  def bar=(o)
    puts "Foo#bar= got a block" if block_given?
  end
end
foo = Foo.new

foo.bar = method1 method2 do
end 
#method1 got a block

foo.bar = method1 method2 {
} 
#method2 got a block

Accessing DeviceKit with DBus and Python

Posted by Moser on 08 Jan 2010

Under the impression of loosing some really important data because of a damaged partition table on a USB flash drive I am developing a backup strategy for my system. I know myself and so I decided that I need to automate this. While trying to figure out how one can run a script as soon as a specific drive is mounted, I came across DeviceKit. It’s the planned replacement of HAL and is used in Ubuntu Karmic. Udev is not an option for me, because I don’t want to mess around with mounting myself (and I hate running stuff as root…). So here is what I found out about using DBus and DeviceKit in Python:

import dbus

bus = dbus.SystemBus()

proxy = bus.get_object("org.freedesktop.DeviceKit.Disks", 
                       "/org/freedesktop/DeviceKit/Disks")
iface = dbus.Interface(proxy, "org.freedesktop.DeviceKit.Disks")
#enumerates all devices
print iface.EnumerateDevices()

#gets the device kit path of a specific device
path = iface.FindDeviceByDeviceFile("/dev/sdc1") 
#= "/org/freedesktop/DeviceKit/Disks/devices/sdc"

#gets an object representing the device specified by the path
device = bus.get_object("org.freedesktop.DeviceKit.Disks", path) 

#prints some XML that shows you the available methods, signals and properties
print device.Introspect()

#gets a proxy for getting properties
device_prop = dbus.Interface(device, "org.freedesktop.DBus.Properties")

#you need to specify an interface (properties could be ambiguous)
print device_prop.Get("org.freedesktop.DeviceKit.Disks.Device", "device-mount-paths")

#gets a proxy you can call methods on
device_iface = dbus.Interface(device, "org.freedesktop.DeviceKit.Disks.Device")

#unmounts the partition
device_iface.FilesystemUnmount(dbus.Array([], 's'))

The DBus API of DeviceKit is documented here. Now we want to be notified when a drive is mounted:

import dbus
import gobject
from dbus.mainloop.glib import DBusGMainLoop

def device_added_callback(device):
    print 'Device %s was added' % (device)

def device_changed_callback(device):
    print 'Device %s was changed' % (device)

#must be done before connecting to DBus
DBusGMainLoop(set_as_default=True)

bus = dbus.SystemBus()

proxy = bus.get_object("org.freedesktop.DeviceKit.Disks", 
                       "/org/freedesktop/DeviceKit/Disks")
iface = dbus.Interface(proxy, "org.freedesktop.DeviceKit.Disks")

#addes two signal listeners
iface.connect_to_signal('DeviceAdded', device_added_callback)
iface.connect_to_signal('DeviceChanged', device_changed_callback)

#start the main loop
mainloop = gobject.MainLoop()
mainloop.run()

A typical output when a flash drive is plugged in looks like this:

Device /org/freedesktop/DeviceKit/Disks/devices/sdc was added
Device /org/freedesktop/DeviceKit/Disks/devices/sdc1 was added
Device /org/freedesktop/DeviceKit/Disks/devices/sdc1 was changed

With this knowledge I’m currently working on a little python script that runs in background and executes a shell script when a file system is mounted. I’ll post it, when it’s finished :-)

Sequel single table inheritance

Posted by Moser on 20 Oct 2009

Sequel’s STI is not so well documented as ActiveRecord’s is. Because it cost me about 20 minutes to find out about it’s syntax, I post a little example.

class A < Sequel::Model
  plugin :single_table_inheritance, :object_type
end

class B < A
end

The second argument is the name of the column to be used to store the class name in DB. Don’t use ‘type’ here, it collides with Ruby’s Object#type. This works with version 3.5.0 and should work with any version >= 2.12.0.

Monkeybars - Little bug

Posted by Moser on 10 Sep 2009

For a little side project I am currently evaluating different ways of GUI programming using Ruby. Monkeybars is one of the most interesting candidates. There is a really good article about it, if you want to learn more. Version 1.0.4 has a little, but annoying bug: If you generate a new application skeleton it won’t compile/run:

manifest.rb:32:in `require': no such file to load -- monkeybars (LoadError)
    from manifest.rb:32
    from manifest.rb:21:in `require'
    from main.rb:21

To fix it, you got to change line 21 of manifest.rb:

add_to_classpath '../lib/java/monkeybars-1.0.2.jar'
#to
add_to_classpath '../lib/java/monkeybars-1.0.4.jar'

ActiveRecord: write_attribute and UTC conversion

Posted by Moser on 23 Jun 2009

A little gotcha with custom setters for datetime attributes is, that when setting an attribute through ‘write_attribute’ it is not converted to UTC (or whatever else your default time zone is). This problem can easily be reproduced:

./script/generate model Thing a:datetime b:datetime

My model:

class Thing < ActiveRecord::Base
  def b=(d)
    write_attribute(:b, d)
  end
end

My tests:

require 'test_helper'

class ThingTest < ActiveSupport::TestCase
  test "a and b" do
    d = DateTime.now
    t = Thing.new
    t.a = d
    t.b = d
    t.save
    assert_equal t.a, t.b
    t.reload
    assert_equal t.a, t.b
  end
end

The second assertion will fail. Is this intended or a bug? Quick fix:

def b=(d)
    d = d.utc
    write_attribute(:b, d)
  end

jQuery, jRails and the Accept header

Posted by Moser on 18 Jun 2009

I included jQuery by installing the jRails plugin, which seems nice because you can continue using rails’ ajax helpers. But when I tried to implement a more special functionality by using the $.ajax function it proved impossible to set the Accept header for my request. Neither using the dataType option nor by setting it directly in a beforeSend function. The Accept header always read:

text/javascript, text/html, application/xml, text/xml, */*

I was about to uninstall and hate jQuery for the rest of my life. By coincidence I took a look at jrails.js which is a part of the jRails plugin:

(function($)
    {
      $().ajaxSend(
        function(a,xhr,s){
          xhr.setRequestHeader("Accept","text/javascript, text/html, application/xml, text/xml, */*")
        }
      )
    }
    )(jQuery); 
    [...]

WTF? Is that documented anywhere? This hard coded shit stuff breaks rails’ respond_to functionality, doesn’t it?t To be fair, it works if you use extensions to determine what datatype you expect. (Like /things/1.js) But I don’t do that when I build a custom ajax request where I can set the Accept header directly.

Update: Some research on the topic “accept header vs. extension” showed that in terms of cross browser compatibility I should favor the extension approach. And I’m not surprised it’s Microsoft’s fault :-)