ruby-dbus 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/COPYING +504 -0
  2. data/NEWS +137 -0
  3. data/README +53 -0
  4. data/Rakefile +54 -0
  5. data/VERSION +1 -0
  6. data/doc/tutorial/index.html +356 -0
  7. data/doc/tutorial/index.markdown +467 -0
  8. data/examples/gdbus/gdbus +255 -0
  9. data/examples/gdbus/gdbus.glade +184 -0
  10. data/examples/gdbus/launch.sh +4 -0
  11. data/examples/no-introspect/nm-test.rb +21 -0
  12. data/examples/no-introspect/tracker-test.rb +16 -0
  13. data/examples/rhythmbox/playpause.rb +25 -0
  14. data/examples/service/call_service.rb +25 -0
  15. data/examples/service/service_newapi.rb +51 -0
  16. data/examples/simple/call_introspect.rb +34 -0
  17. data/examples/utils/listnames.rb +11 -0
  18. data/examples/utils/notify.rb +19 -0
  19. data/lib/dbus.rb +91 -0
  20. data/lib/dbus/auth.rb +258 -0
  21. data/lib/dbus/bus.rb +816 -0
  22. data/lib/dbus/core_ext/class/attribute.rb +91 -0
  23. data/lib/dbus/core_ext/kernel/singleton_class.rb +14 -0
  24. data/lib/dbus/core_ext/module/remove_method.rb +12 -0
  25. data/lib/dbus/error.rb +44 -0
  26. data/lib/dbus/export.rb +132 -0
  27. data/lib/dbus/introspect.rb +553 -0
  28. data/lib/dbus/marshall.rb +443 -0
  29. data/lib/dbus/matchrule.rb +100 -0
  30. data/lib/dbus/message.rb +310 -0
  31. data/lib/dbus/type.rb +222 -0
  32. data/ruby-dbus.gemspec +18 -0
  33. data/test/binding_test.rb +56 -0
  34. data/test/bus_driver_test.rb +22 -0
  35. data/test/dbus-launch-simple +35 -0
  36. data/test/dbus-limited-session.conf +28 -0
  37. data/test/server_robustness_test.rb +41 -0
  38. data/test/server_test.rb +53 -0
  39. data/test/service_newapi.rb +129 -0
  40. data/test/session_bus_test_manual.rb +20 -0
  41. data/test/signal_test.rb +64 -0
  42. data/test/t1 +4 -0
  43. data/test/t2.rb +66 -0
  44. data/test/t3-ticket27.rb +18 -0
  45. data/test/t5-report-dbus-interface.rb +58 -0
  46. data/test/t6-loop.rb +82 -0
  47. data/test/test_env +13 -0
  48. data/test/test_server +39 -0
  49. data/test/variant_test.rb +66 -0
  50. metadata +117 -0
data/NEWS ADDED
@@ -0,0 +1,137 @@
1
+ = Ruby D-Bus NEWS
2
+
3
+ Note about bug numbers:
4
+ Ticket#1 - https://trac.luon.net/ruby-dbus/ticket/1
5
+ Issue#1 - http://github.com/mvidner/ruby-dbus/issues#issue/1
6
+ bnc#1 - https://bugzilla.novell.com/show_bug.cgi?id=1
7
+
8
+ == Ruby D-Bus 0.5.0 - 2010-11-07
9
+
10
+ Features:
11
+ * Better binding of Ruby Exceptions to D-Bus Errors.
12
+ * Converted the package to a Gem (Issue#6).
13
+ * Converted the tutorial from Webgen to Markdown.
14
+
15
+ Bug fixes:
16
+ * Don't pass file descriptors to subprocesses.
17
+ * Fixed InterfaceElement::validate_name (Ticket#38, by Herwin Weststrate).
18
+ * Fixed a typo in InvalidDestinationName description (Ticket#40).
19
+
20
+ == Ruby D-Bus 0.4.0 - 2010-08-20
21
+
22
+ Features:
23
+ * TCP transport (by pangdudu)
24
+ * Enabled test code coverage report (rcov)
25
+
26
+ Bug fixes:
27
+ * Classes should not share all interfaces (Ticket#36/Issue#5)
28
+ * Ruby 1.9 compatibility (Ticket#37, by Myra Nelson)
29
+
30
+ == Ruby D-Bus 0.3.1 - 2010-07-22
31
+
32
+ Bug fixes:
33
+ * Many on_signal could cause DBus.Error.LimitsExceeded bnc#617350).
34
+ Don't add a match rule that already exists, enable removing match
35
+ rules. Now only one handler for a rule is called (but it is possible
36
+ for one signal to match more rules). This reverts the half-fix done
37
+ to fix Issue#3
38
+ * Re-added InterfaceElement#add_param for compatibility.
39
+ * Handle more ways which tell us that a bus connection has died.
40
+
41
+ == Ruby D-Bus 0.3.0 - 2010-03-28
42
+
43
+ Bug fixes:
44
+
45
+ * Fixed "undefined method `get_node' for nil:NilClass"
46
+ on Ubuntu Karmic (Ticket#34).
47
+ * Get the session bus address even if unset in ENV (Issue#4).
48
+ * Improved exceptions a bit:
49
+ UndefinedInterface, InvalidMethodName, NoMethodError, no RuntimeException
50
+
51
+ These are by Klaus Kaempf:
52
+ * Make the signal dispatcher call all handlers (Issue#3).
53
+ * Run on Ruby < 1.8.7 (Issue#2).
54
+ * Avoid needless DBus::IncompleteBufferException (Ticket#33).
55
+ * Don't ignore DBus Errors in request_service, raise them (Ticket#32).
56
+
57
+ Features:
58
+
59
+ * Automatic signature inference for variants.
60
+ * Introduced FormalParameter where a plain pair had been used.
61
+
62
+ == Ruby D-Bus 0.2.12 - 2010-01-24
63
+
64
+ Bug fixes:
65
+
66
+ * Fixed a long-standing bug where a service activated by the bus
67
+ would fail with "undefined method `get_node' for nil:NilClass"
68
+ (Tickets#25 and #29).
69
+
70
+ == Ruby D-Bus 0.2.11 - 2009-11-12
71
+
72
+ Features:
73
+
74
+ * Added DBus::Service#unexport (da1l6).
75
+
76
+ Bug fixes:
77
+
78
+ * Return org.freedesktop.DBus.Error.UnknownObject instead of crashing
79
+ (Ticket#31).
80
+ * Rescue exceptions in dbus_methods and reply with DBus errors instead of
81
+ crashing (da1l6).
82
+ * Better exception messages when sending nil, or mismatched structs.
83
+ * Call mktemp without --tmpdir, to build on older distros.
84
+
85
+ == Ruby D-Bus 0.2.10 - 2009-09-10
86
+
87
+ Bug fixes:
88
+
89
+ * DBus::Service.exists? fixed (Murat Demirten).
90
+ * Ruby 1.9 fixes (Jedediah Smith).
91
+ * Fixed an endless sleep in DBus::Main.run (bnc#537401).
92
+ * Added details to PacketMarshaller exceptions (bnc#538050).
93
+
94
+ (bnc#FOO refers to https://bugzilla.novell.com/show_bug.cgi?id=FOO )
95
+
96
+ == Ruby D-Bus "I'm not dead" 0.2.9 - 2009-08-26
97
+
98
+ Thank you to Paul and Arnaud for starting the project. I, Martin
99
+ Vidner, am continuing with it on GitHub.
100
+
101
+ * Fixed passing an array through a variant (no ticket).
102
+ * Fixed marshalling "av" (Ticket #30).
103
+ * Fixed variant alignment (Ticket #27).
104
+ * Added DBus::Main.quit.
105
+ * Mention the DBus interface in a NameError for an unknown method.
106
+ * Fixed ruby-1.9 "warning: default `to_a' will be obsolete".
107
+ * Added Rakefile and gemspec.
108
+
109
+ == Ruby D-Bus "Thanks for all the fish" 0.2.1 - 2007-12-29
110
+
111
+ More bugfixes, mostly supplied by users supplying us with patches. Thanks!
112
+
113
+ * Support for new types added:
114
+ - dict (courtesy of Drake Wilson);
115
+ - double (courtesy of Patrick Sissons);
116
+ - variant.
117
+ * Improved exception raise support (courtesy of Sjoerd Simons,
118
+ Patrick Sissons).
119
+ * Some polish (removed debug output, solved unnecessary warnings).
120
+ * Documentation updates, example fixes and updates.
121
+
122
+ == Ruby D-Bus "Almost live from DebConf 7" 0.2.0 - 2007-06-02
123
+
124
+ Again a bugfix release, also meant to be the public release
125
+ for exploratory purposes. New in 0.2.0:
126
+
127
+ * Complete tutorial revamp.
128
+ * Relicensed to the LGPL.
129
+
130
+ == Ruby D-Bus "Release Often" 0.1.1 - 2007-04-23
131
+
132
+ Bugfix release. Fixes hardcoded string for requesting bus names,
133
+ found by Rudi Cilibrasi.
134
+
135
+ == Ruby D-Bus "Happy Birthday Paul" 0.1.0 - 2007-04-17
136
+
137
+ First release. Supports most of D-Bus' features.
data/README ADDED
@@ -0,0 +1,53 @@
1
+ = Ruby D-Bus README
2
+
3
+ Ruby D-Bus provides an implementation of the D-Bus protocol such that the
4
+ D-Bus system can be used in the Ruby programming language.
5
+
6
+ == Requirements
7
+
8
+ * Ruby 1.8 (>= 1.8.6?)
9
+
10
+ Optionally, for generating the tutorial:
11
+ * Webgen (>= 0.4)
12
+
13
+ == Installation
14
+
15
+ 1. Decompress the Ruby D-Bus tarball (ruby-dbus-<version>.tar.gz).
16
+ 2. Move to top-level directory and type:
17
+
18
+ $ ruby setup.rb config
19
+ $ ruby setup.rb setup
20
+ ($ su)
21
+ # ruby setup.rb install
22
+
23
+ You can also install files in your favorite directory by
24
+ supplying setup.rb some options. Try "ruby setup.rb --help".
25
+
26
+ == Feature
27
+
28
+ Ruby D-Bus currently supports the following features:
29
+
30
+ * Connecting to local buses.
31
+ * Accessing remote services, objects and interfaces.
32
+ * Invoking methods on remote objects synchronously and asynchronously.
33
+ * Catch signals on remote objects and handle them via callbacks.
34
+ * Remote object introspection.
35
+ * Walking object trees.
36
+ * Creating services and registering them on the bus.
37
+ * Exporting objects with interfaces on a bus for remote use.
38
+ * Rubyish D-Bus object and interface syntax support that automatically
39
+ allows for introspection.
40
+ * Emitting signals on exported objects.
41
+
42
+ == Usage
43
+
44
+ See some of the examples in the examples/ subdirectory of the tarball.
45
+ Also, check out the included tutorial (in Webgen format) in doc/tutorial/
46
+ or view it online on http://trac.luon.net/data/ruby-dbus/tutorial/.
47
+
48
+ == License
49
+
50
+ Ruby D-Bus is free software; you can redistribute it and/or modify it
51
+ under the terms of the GNU Lesser General Public License as published by the
52
+ Free Software Foundation; either version 2.1 of the License, or (at
53
+ your option) any later version.
@@ -0,0 +1,54 @@
1
+ #! /usr/bin/env ruby
2
+ require 'rake'
3
+ require 'rake/gempackagetask'
4
+ require 'fileutils'
5
+ include FileUtils
6
+ require 'rake/rdoctask'
7
+ require 'rake/testtask'
8
+
9
+ desc 'Default: run tests in the proper environment'
10
+ task :default => "env:test"
11
+
12
+ def common_test_task(t)
13
+ t.libs << "lib"
14
+ t.test_files = FileList['test/*_test.rb', 'test/t*.rb']
15
+ t.verbose = true
16
+ end
17
+ Rake::TestTask.new {|t| common_test_task t }
18
+
19
+ begin
20
+ require 'rcov/rcovtask'
21
+ Rcov::RcovTask.new {|t| common_test_task t }
22
+ rescue LoadError
23
+ # no rcov, never mind
24
+ end
25
+
26
+ %w(test rcov).each do |tname|
27
+ namespace :env do
28
+ desc "Run #{tname} in the proper environment"
29
+ task tname do |t|
30
+ cd "test" do
31
+ system "./test_env rake #{tname}"
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ load "ruby-dbus.gemspec"
38
+
39
+ Rake::GemPackageTask.new(GEMSPEC) do |pkg|
40
+ # no other formats needed
41
+ end
42
+
43
+ Rake::RDocTask.new do |rd|
44
+ rd.rdoc_dir = 'doc/rdoc'
45
+ rd.rdoc_files.include("README", "lib/**/*.rb")
46
+ # rd.options << "--diagram"
47
+ # rd.options << "--all"
48
+ end
49
+
50
+ desc "Render the tutorial in HTML"
51
+ task :tutorial => "doc/tutorial/index.html"
52
+ file "doc/tutorial/index.html" => "doc/tutorial/index.markdown" do |t|
53
+ sh "markdown #{t.prerequisites[0]} > #{t.name}"
54
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.5.0
@@ -0,0 +1,356 @@
1
+ <style>
2
+ code { background-color: #F0E7E7; }
3
+ pre code { background-color: #F0DDDD; }
4
+ pre {
5
+ font-size: 90%;
6
+ overflow: hidden;
7
+ padding-left: 10pt;
8
+ border: thin solid #F0B4B4;
9
+ background-color: #F0DDDD;
10
+ }
11
+ </style>
12
+
13
+ <h1>Welcome</h1>
14
+ <p>This is the Ruby D-Bus tutorial. It aims to show you the features of Ruby
15
+ D-Bus and as you read through the tutorial also how to use them.</p>
16
+ <p>&copy; Arnaud Cornet and Paul van Tilburg; this tutorial is part of
17
+ free software; you can redistribute it and/or modify it under the
18
+ terms of the <a href="http://www.gnu.org/licenses/lgpl.html">GNU Lesser General Public License,
19
+ version 2.1</a> as published by the
20
+ <a href="http://www.fsf.org/">Free Software Foundation</a>.</p>
21
+ <h1>Introduction</h1>
22
+ <p>This is a tutorial for Ruby D-Bus, a library to access D-Bus facilities of your
23
+ system.</p>
24
+ <h2>What is D-Bus?</h2>
25
+ <p>D-Bus is an RPC(Remote Procedure Call) protocol. A common setup can have
26
+ multiple D-Bus daemons running that route procedure calls and signals in
27
+ the form of messages. Each of these daemons supports a bus. A bus that
28
+ is often used by modern desktop environments, and is available per session, is
29
+ called the <em>session bus</em>. Another bus that can be available, but in a
30
+ system-wide manner, is called the <em>system bus</em>. It is used for example by
31
+ the <a href="http://hal.freedesktop.org/">Hardware Abstraction Layer</a> daemon. Note
32
+ that theoretically the D-Bus RPC protocol can be used without a system or
33
+ session bus. I never came across any actual use of this though.</p>
34
+ <p>At the desktop level, D-Bus allows some components to interact. Typically
35
+ if you are writing an application or a personal script that wants to
36
+ interact with your web browser, your music player, or that simply wants to
37
+ pop-up a desktop notification, D-Bus comes into play.</p>
38
+ <p>At the system level, the Hardware Abstraction Layer is a privileged daemon
39
+ that notifies other software of hardware activities. Typically, if you
40
+ want to be notified if a CD-ROM has been loaded in, of if you want to
41
+ explore hardware, the system daemon comes into play.</p>
42
+ <p>The D-Bus RPC system is as we will see <em>object oriented</em>.</p>
43
+ <p>Buses provide access to <em>services</em> provided in turn by running or ready to
44
+ run processes. Let me introduce some D-Bus terminology before we discuss
45
+ the API of Ruby D-Bus.</p>
46
+ <h2>Client</h2>
47
+ <p>A D-Bus client is a process that connects to a D-Bus. They issue method
48
+ calls and register to the bus for signals and events.</p>
49
+ <h2>Service</h2>
50
+ <p>A connected client can export some of its objects and let other clients
51
+ call some of its methods. Such clients typically register a special name
52
+ like <code>org.freedesktop.Notifications</code>, the service name.</p>
53
+ <p>There is slightly different type of service. They are provided by
54
+ processes that can be launched by a D-Bus daemon on demand. Once they are
55
+ started by D-Bus they register a service name and behave like another
56
+ client.</p>
57
+ <p>Note that the buses themselves provide the <code>org.freedesktop.DBus</code> service,
58
+ and provide some features through it.</p>
59
+ <h2>Object path</h2>
60
+ <p>An object path is the D-Bus way to specify an object <em>instance</em> address. A
61
+ service can provide different object instances to the outside world, so
62
+ that external processes can call methods on each of them. An object path
63
+ is an address of an instance in a very similar way that the path is an
64
+ address of a file on a file system. For example:
65
+ <code>/org/freedesktop/Notification</code> is an object path of an object provided by
66
+ the <code>org.freedesktop.Notification</code> service</p>
67
+ <p><strong>Beware</strong>: service names and object paths can, but do <em>not</em> have to be
68
+ related! You'll probably encounter a lot of cases though, where the
69
+ object path is a slashed version of the dotted service name.</p>
70
+ <h2>Interface</h2>
71
+ <p>Classically in an object model, classes can implement interfaces. That is,
72
+ some method definitions grouped in an interface. This is exactly what a
73
+ D-Bus interface is as well. In D-Bus interfaces have names. These names must be
74
+ specified on method calls.</p>
75
+ <p>The <code>org.freedesktop.Notification</code> service provides an object instance
76
+ called <code>/org/freedesktop/Notification</code>. This instance object implements an
77
+ interface called <code>org.freedesktop.Notifications</code>. It also provides two
78
+ special D-Bus specific interfaces: <code>org.freedesktop.DBus.Introspect</code> and
79
+ <code>org.freedesktop.DBus.Properties</code>. Again, object paths, service names,
80
+ and interface names can be related but do not have to be.</p>
81
+ <p>Basically the <code>org.freedesktop.DBus.Introspect</code> has an <code>Introspect</code> method,
82
+ that returns XML data describing the <code>/org/freedesktop/Notification</code> object
83
+ interfaces. This is used heavily internally by Ruby D-Bus.</p>
84
+ <h2>Method</h2>
85
+ <p>A method is, well, a method in the classical meaning. It's a function that
86
+ is called in the context of an object instance. Methods have typed
87
+ parameters and return typed return values.</p>
88
+ <h2>Signal</h2>
89
+ <p>Signals are simplified method calls that do not have a return value. They
90
+ do have typed parameters though.</p>
91
+ <h2>Message</h2>
92
+ <p>Method calls, method returns, signals, errors: all are encoded as D-Bus
93
+ messages sent over a bus. They are made of a packet header with source and
94
+ destination address, a type (method call, method reply, signal) and the
95
+ body containing the parameters (for signals and method calls) or the return
96
+ values (for a method return message).</p>
97
+ <h2>Signature</h2>
98
+ <p>Because D-Bus is typed and dynamic, each message comes with a signature that
99
+ describes the types of the data that is contained within the message. The
100
+ signature is a string with an extremely basic language that only describes
101
+ a data type. You will need to have some knowledge of what a signature
102
+ looks like if you are setting up a service. If you are just programming a
103
+ D-Bus client, you can live without knowing about them.</p>
104
+ <h1>Client Usage</h1>
105
+ <p>This chapter discusses basic client usage
106
+ and has the following topics:</p>
107
+ <h2>Using the library</h2>
108
+ <p>If you want to use the library, you have to make Ruby load it by issuing:</p>
109
+ <pre><code>require 'dbus'
110
+ </code></pre>
111
+ <p>That's all! Now we can move on to really using it...</p>
112
+ <h2>Connecting to a bus</h2>
113
+ <p>On a typical system, two buses are running, the system bus and the session
114
+ bus. The system bus can be accessed by:</p>
115
+ <pre><code>bus = DBus::SystemBus.instance
116
+ </code></pre>
117
+ <p>Probably you already have guessed how to access the session bus. This
118
+ can be done by:</p>
119
+ <pre><code>bus = DBus::SessionBus.instance
120
+ </code></pre>
121
+ <h2>Performing method calls</h2>
122
+ <p>Let me continue this example using the session bus. Let's say that I want
123
+ to access an object of some client on the session bus. This particular
124
+ D-Bus client provides a service called <code>org.gnome.Rhythmbox</code>. Let me
125
+ access this service:</p>
126
+ <pre><code>rb_service = bus.service("org.gnome.Rhythmbox")
127
+ </code></pre>
128
+ <p>In this example I access the <code>org.gnome.Rhythmbox</code> service, which is
129
+ provided by the application
130
+ <a href="http://www.gnome.org/projects/rhythmbox/">Rhythmbox</a>.
131
+ OK, I have a service handle now, and I know that it exports the object
132
+ "/org/gnome/Rhythmbox/Player". I will trivially access this remote object
133
+ using:</p>
134
+ <pre><code>rb_player = rb_service.object("/org/gnome/Rhythmbox/Player")
135
+ </code></pre>
136
+ <h2>Introspection</h2>
137
+ <p>Well, that was easy. Let's say that I know that this particular object is
138
+ introspectable. In real life most of them are. The <code>rb_object</code> object we
139
+ have here is just a handle of a remote object, in general they are called
140
+ <em>proxy objects</em>, because they are the local handle of a remote object. It
141
+ would be nice to be able to make it have methods, and that its methods send
142
+ a D-Bus call to remotely execute the actual method in another process.
143
+ Well, instating these methods for a <em>introspectable</em> object is trivial:</p>
144
+ <pre><code>rb_player.introspect
145
+ </code></pre>
146
+ <p>And there you go. Note that not all services or objects can be
147
+ introspected, therefore you have to do this manually! Let me remind you
148
+ that objects in D-Bus have interfaces and interfaces have methods. Let's
149
+ now access these methods:</p>
150
+ <pre><code>rb_player_iface = rb_player["org.gnome.Rhythmbox.Player"]
151
+ puts rb_player_iface.getPlayingUri
152
+ </code></pre>
153
+ <p>As you can see, when you want to call a method on an instance object, you have
154
+ to get the correct interface. It is a bit tedious, so we have the following
155
+ shortcut that does the same thing as before:</p>
156
+ <pre><code>rb_player.default_iface = "org.gnome.Rhythmbox.Player"
157
+ puts rb_player.getPlayingUri
158
+ </code></pre>
159
+ <p>The <code>default_iface=</code> call specifies the default interface that should be
160
+ used when non existing methods are called directly on a proxy object, and
161
+ not on one of its interfaces.</p>
162
+ <p>Note that the bus itself has a corresponding introspectable object. You can
163
+ access it with <code>bus.proxy</code> method. For example, you can retrieve an array of
164
+ exported service names of a bus like this:</p>
165
+ <pre><code>bus.proxy.ListNames[0]
166
+ </code></pre>
167
+ <h2>Calling a method asynchronously</h2>
168
+ <p>D-Bus is <em>asynchronous</em>. This means that you do not have to wait for a
169
+ reply when you send a message. When you call a remote method that takes a
170
+ lot of time to process remotely, you don't want your application to hang,
171
+ right? Well the asychronousness exists for this reason. What if you dont'
172
+ want to wait for the return value of a method, but still you want to take
173
+ some action when you receive it?</p>
174
+ <p>There is a classical method to program this event-driven mechanism. You do
175
+ some computation, perform some method call, and at the same time you setup
176
+ a callback that will be triggered once you receive a reply. Then you run a
177
+ main loop that is responsible to call the callbacks properly. Here is how
178
+ you do it:</p>
179
+ <pre><code>rb_player.getPlayingUri do |resp|
180
+ puts "The playing URI is #{resp}"
181
+ end
182
+ puts "See, I'm not waiting!"
183
+ loop = DBus::Main.new
184
+ loop &lt;&lt; bus
185
+ loop.run
186
+ </code></pre>
187
+ <p>This code will print the following:</p>
188
+ <pre><code>See, I'm not waiting!
189
+ The playing URI is file:///music/papapingoin.mp3
190
+ </code></pre>
191
+ <h2>Waiting for a signal</h2>
192
+ <p>Signals are calls from the remote object to your program. As a client, you
193
+ set yourself up to receive a signal and handle it with a callback. Then running
194
+ the main loop triggers the callback. You can register a callback handler
195
+ as allows:</p>
196
+ <pre><code>rb_player.on_signal("elapsedChanged") do |u|
197
+ puts u
198
+ end
199
+ </code></pre>
200
+ <h2>More about introspection</h2>
201
+ <p>There are various ways to inspect a remote service. You can simply call
202
+ <code>Introspect()</code> and read the XML output. However, in this tutorial I assume
203
+ that you want to do it using the Ruby D-Bus API.</p>
204
+ <p>Notice that you can introspect a service, and not only objects:</p>
205
+ <pre><code>rb_service = bus.service("org.gnome.Rhythmbox")
206
+ rb_service.introspect
207
+ p rb_service.root
208
+ </code></pre>
209
+ <p>This dumps a tree-like structure that represents multiple object paths. In
210
+ this particular case the output is:</p>
211
+ <pre><code>&lt;/: {org =&gt; {gnome =&gt; {Rhythmbox =&gt; {Player =&gt; ..fdbe625de {},Shell =&gt; ..fdbe6852e {},PlaylistManager =&gt; ..fdbe4e340 {}}&gt;
212
+ </code></pre>
213
+ <p>Read this left to right: the root node is "/", it has one child node "org",
214
+ "org" has one child node "gnome", and "gnome" has one child node "Rhythmbox".
215
+ Rhythmbox has Tree child nodes "Player", "Shell" and "PlaylistManager".
216
+ These three last child nodes have a weird digit that means it has an object
217
+ instance. Such object instances are already introspected.</p>
218
+ <p>If the prose wasn't clear, maybe the following ASCII art will help you:</p>
219
+ <pre><code>/
220
+ org
221
+ gnome
222
+ Rhythmbox
223
+ Shell (with object)
224
+ Player (with object)
225
+ PlaylistManager (with object)
226
+ </code></pre>
227
+ <h3>Walking the object tree</h3>
228
+ <p>You can have an object on any node, i.e. it is not limited to leaves.
229
+ You can access a specific node like this:</p>
230
+ <pre><code>rb_player = rb_service.root["org"]["gnome"]["Rhythmbox"]["Player"]
231
+ rb_player = rb_service.object("/org/gnome/Rhythmbox/Player")
232
+ </code></pre>
233
+ <p>The difference between the two is that for the first one, <code>rb_service</code>
234
+ needs to have been introspected. Also the obtained <code>rb_player</code> is already
235
+ introspected whereas the second <code>rb_player</code> isn't yet.</p>
236
+ <h2>Errors</h2>
237
+ <p>D-Bus calls can reply with an error instead of a return value. An error is
238
+ translated to a Ruby exception.</p>
239
+ <pre><code>begin
240
+ network_manager.sleep
241
+ rescue DBus::Error =&gt; e
242
+ puts e unless e.name == "org.freedesktop.NetworkManager.AlreadyAsleepOrAwake"
243
+ end
244
+ </code></pre>
245
+ <h1>Creating a Service</h1>
246
+ <p>This chapter deals with the opposite side of the basic client usage, namely
247
+ the creation of a D-Bus service.</p>
248
+ <h2>Registering a service</h2>
249
+ <p>Now that you know how to perform D-Bus calls, and how to wait for and
250
+ handle signals, you might want to learn how to publish some object and
251
+ interface to provide them to the D-Bus world. Here is how you do that.</p>
252
+ <p>As you should already know, D-Bus clients that provide some object to be
253
+ called remotely are services. Here is how to allocate a name on a bus:</p>
254
+ <pre><code>bus = DBus.session_bus
255
+ service = bus.request_service("org.ruby.service")
256
+ </code></pre>
257
+ <p>Now this client is know to the outside world as <code>org.ruby.service</code>.
258
+ Note that this is a request and it <em>can</em> be denied! When it
259
+ is denied, an exception (<code>DBus::NameRequestError</code>) is thrown.</p>
260
+ <h2>Exporting an object</h2>
261
+ <p>Now, let's define a class that we want to export:</p>
262
+ <pre><code>class Test &lt; DBus::Object
263
+ # Create an interface.
264
+ dbus_interface "org.ruby.SampleInterface" do
265
+ # Create a hello method in that interface.
266
+ dbus_method :hello, "in name:s, in name2:s" do |name, name2|
267
+ puts "hello(#{name}, #{name2})"
268
+ end
269
+ end
270
+ end
271
+ </code></pre>
272
+ <p>As you can see, we define a <code>Test</code> class in which we define a
273
+ <code>org.ruby.SampleInterface</code> interface. In this interface, we define a
274
+ method. The given code block is the method's implementation. This will be
275
+ executed when remote programs performs a D-Bus call. Now the annoying part:
276
+ the actual method definition. As you can guess the call</p>
277
+ <pre><code>dbus_method :hello, "in name:s, in name2:s" do ...
278
+ </code></pre>
279
+ <p>creates a <code>hello</code> method that takes two parameters both of type string.
280
+ The <em>:s</em> means "of type string". Let's have a look at some other common
281
+ parameter types:</p>
282
+ <ul>
283
+ <li><em>u</em> means unsigned integer</li>
284
+ <li><em>i</em> means integer</li>
285
+ <li><em>y</em> means byte</li>
286
+ <li><em>(ui)</em> means a structure having a unsigned integer and a signed one.</li>
287
+ <li><em>a</em> means array, so that "ai" means array of integers<ul>
288
+ <li><em>as</em> means array of string</li>
289
+ <li><em>a(is)</em> means array of structures, each having an integer and a string.</li>
290
+ </ul>
291
+ </li>
292
+ </ul>
293
+ <p>For a full description of the available D-Bus types, please refer to the
294
+ <a href="http://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-signatures">D-Bus specification</a>.</p>
295
+ <p>Now that the class has been defined, we can instantiate an object
296
+ and export it as follows:</p>
297
+ <pre><code>exported_obj = Test.new("/org/ruby/MyInstance")
298
+ service.export(exported_obj)
299
+ </code></pre>
300
+ <p>This piece of code above instantiates a <code>Test</code> object with a D-Bus object
301
+ path. This object is reachable from the outside world after
302
+ <code>service.export(exported_obj)</code> is called.</p>
303
+ <p>We also need a loop which will read and process the calls coming over the bus:</p>
304
+ <pre><code>loop = DBus::Main.new
305
+ loop &lt;&lt; bus
306
+ loop.run
307
+ </code></pre>
308
+ <h3>Using the exported object</h3>
309
+ <p>Now, let's consider another program that will access our newly created service:</p>
310
+ <pre><code>ruby_service = bus.service("org.ruby.service")
311
+ obj = ruby_service.object("/org/ruby/MyInstance")
312
+ obj.introspect
313
+ obj.default_iface = "org.ruby.SampleInterface"
314
+ obj.hello("giligiligiligili", "haaaaaaa")
315
+ </code></pre>
316
+ <p>As you can see, the object we defined earlier is automatically introspectable.
317
+ See also "Basic Client Usage".</p>
318
+ <h2>Emitting a signal</h2>
319
+ <p>Let's add some example method so you can see how to return a value to the
320
+ caller and let's also define another example interface that has a signal.</p>
321
+ <pre><code>class Test2 &lt; DBus::Object
322
+ # Create an interface
323
+ dbus_interface "org.ruby.SampleInterface" do
324
+ # Create a hello method in the interface:
325
+ dbus_method :hello, "in name:s, in name2:s" do |name, name2|
326
+ puts "hello(#{name}, #{name2})"
327
+ end
328
+ # Define a signal in the interface:
329
+ dbus_signal :SomethingJustHappened, "toto:s, tutu:u"
330
+ end
331
+
332
+ dbus_interface "org.ruby.AnotherInterface" do
333
+ dbus_method :ThatsALongMethodNameIThink, "in name:s, out ret:s" do |name|
334
+ ["So your name is #{name}"]
335
+ end
336
+ end
337
+ end
338
+ </code></pre>
339
+ <p>Triggering the signal is a easy as calling a method, but then this time on
340
+ a local (exported) object and not on a remote/proxy object:</p>
341
+ <pre><code>exported_obj.SomethingJustHappened("blah", 1)
342
+ </code></pre>
343
+ <p>Note that the <code>ThatsALongMethodNameIThink</code> method is returning a single
344
+ value to the caller. Notice that you always have to return an array. If
345
+ you want to return multiple values, just have an array with multiple
346
+ values.</p>
347
+ <h2>Replying with an error</h2>
348
+ <p>To reply to a dbus_method with a D-Bus error, raise a <code>DBus::Error</code>,
349
+ as constructed by the <code>error</code> convenience function:</p>
350
+ <pre><code>raise DBus.error("org.example.Error.SeatOccupied"), "Seat #{seat} is occupied"
351
+ </code></pre>
352
+ <p>If the error name is not specified, the generic
353
+ <code>org.freedesktop.DBus.Error.Failed</code> is used.</p>
354
+ <pre><code>raise DBus.error, "Seat #{seat} is occupied"
355
+ raise DBus.error
356
+ </code></pre>