Location: Start / Creating a Service Language: en

This chapter deals with the opposite side of the basic client usage, namely the creation of a D-Bus service. It contains the following sections:

  1. Registering a service
  2. Exporting an object
    1. Using the exported object
  3. Emitting a signal

Registering a service

Now that you know how to perform D-Bus calls, and how to wait for and handle signals, you might want to learn how to publish some object and interface to provide them to the D-Bus world. Here is how you do that.

As you should already know, D-Bus clients that provide some object to be called remotely are services. Here is how to allocate a name on a bus:

bus = DBus.session_bus
service = bus.request_service("org.ruby.service")

Now this client is know to the outside world as org.ruby.service. Note that this is a request and it can be denied! When it is denied, an exception (DBus::NameRequestError) is thrown.

Exporting an object

Now, let’s define a class that we want to export:

class Test < DBus::Object
  # Create an interface.
  dbus_interface "org.ruby.SampleInterface" do
    # Create a hello method in that interface.
    dbus_method :hello, "in name:s, in name2:s" do |name, name2|
      puts "hello(#{name}, #{name2})" 
    end
  end
end

As you can see, we define a Test class in which we define a org.ruby.SampleInterface interface. In this interface, we define a method. The given code block is the method’s implementation. This will be executed when remote programs performs a D-Bus call. Now the annoying part: the actual method definition. As you can guess the call

dbus_method :hello, "in name:s, in name2:s" do ...

creates a hello method that takes two parameters both of type string. The :s means “of type string”. Let’s have a look at some other common parameter types:

For a full description of the available D-Bus types, please refer to the D-Bus specification.

Now that the class has been defined, we can instantiate an object and export it as follows:

exported_obj = Test.new("/org/ruby/MyInstance")
service.export(exported_obj)

This piece of code above instantiates a Test object with a D-Bus object path. This object is reachable from the outside world after service.export(exported_obj) is called.

Using the exported object

Now, let’s consider another program that will access our newly created service:

ruby_service = bus.service("org.ruby.service")
obj = ruby_service.object("/org/ruby/MyInstance")
obj.introspect
obj.default_iface = "org.ruby.SampleInterface" 
obj.hello("giligiligiligili", "haaaaaaa")

As you can see, the object we defined earlier is automatically introspectable. See also Basic Client Usage.

Emitting a signal

Let’s add some example method so you can see how to return a value to the caller and let’s also define another example interface that has a signal.

class Test2 < DBus::Object
  # Create an interface
  dbus_interface "org.ruby.SampleInterface" do
    # Create a hello method in the interface:
    dbus_method :hello, "in name:s, in name2:s" do |name, name2|
      puts "hello(#{name}, #{name2})" 
    end
    # Define a signal in the interface:
    dbus_signal :SomethingJustHappened, "toto:s, tutu:u" 
  end
end
dbus_interface "org.ruby.AnotherInterface" do
  dbus_method :ThatsALongMethodNameIThink, "in name:s, out ret:s" do |name|
    ["So your name is #{name}"] 
  end
end

Triggering the signal is a easy as calling a method, but then this time on a local (exported) object and not on a remote/proxy object:

exported_obj.SomethingJustHappened("blah", 1)

Note that the ThatsALongMethodNameIThink method is returning a single value to the caller. Notice that you always have to return an array. If you want to return multiple values, just have an array with multiple values.