DRb and NSDistributedNotificationCenter in RubyCocoa
I’m writing an OS X GUI test runner for Ruby. It’s coming along nicely, and I’ll have something to show fairly soon. In the meantime, I’m going to share some things I discovered in the process of writing it.
First, DRb (Distributed Ruby) doesn’t play well with Cocoa objects, due to incompatibilities between Ruby’s threading and Cocoa’s calling model. Sometimes it works; mostly, it results in segmentation faults.
Instead of DRb, I decided to use NSDistributedNotificationCenter, OS X’s own distributed notification system. I couldn’t find many examples of usage on the internet. There were zero server examples in Ruby, and one in Python. After a bit of trial and error, I got it working; hopefully Google and this page will save someone else the trouble.
Here’s some sample server code:
require 'osx/cocoa'
include OSX
class Server < NSObject
def foo(notification)
p notification_to_hash(notification)
end
def notification_to_hash(notification)
nsd = notification.userInfo
return nsd.allKeys.to_a.inject({}){ |hash, oc_key|
hash[oc_key.to_s] = nsd.objectForKey(oc_key).to_s
hash
}
end
end
server = Server.alloc.init
center = NSDistributedNotificationCenter.defaultCenter
center.addObserver_selector_name_object(
server,
"foo:",
"My Notification",
"com.example.MyApp"
)
NSRunLoop.currentRunLoop.run
And the client:
require 'osx/cocoa'
include OSX
center = NSDistributedNotificationCenter.defaultCenter
center.postNotificationName_object_userInfo_deliverImmediately(
"My Notification",
"com.example.MyApp",
{"foo" => "bar", "baz" => 3},
true
)
It’s generally quite simple, but it’s important that the server object be an instance of an NSObject (or one of its descendants). Plain Ruby objects cause bus errors.