Ruby D-Bus Tutorial - Creating a Service
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:
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:
- u means unsigned integer
- i means integer
- y means byte
- (ui) means a structure having a unsigned integer and a signed one.
- a means array, so that “ai” means array of integers
- as means array of string
- a(is) means array of structures, each having an integer and a string.
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.