circus-deployment 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. data/LICENSE +23 -0
  2. data/README.md +0 -0
  3. data/bin/circus +24 -0
  4. data/lib/bundler/circus_bundler.rb +24 -0
  5. data/lib/bundler/circus_util.rb +43 -0
  6. data/lib/bundler/patches.rb +18 -0
  7. data/lib/circus/act.rb +74 -0
  8. data/lib/circus/actstore_client.rb +30 -0
  9. data/lib/circus/agents/agent.rb +59 -0
  10. data/lib/circus/agents/client.rb +77 -0
  11. data/lib/circus/agents/connection.rb +76 -0
  12. data/lib/circus/agents/conversation.rb +17 -0
  13. data/lib/circus/agents/dbus_connection.rb +85 -0
  14. data/lib/circus/agents/dbus_logger.rb +34 -0
  15. data/lib/circus/agents/encoding.rb +32 -0
  16. data/lib/circus/agents/logger.rb +52 -0
  17. data/lib/circus/agents/params.rb +47 -0
  18. data/lib/circus/agents/ssh_connection.rb +120 -0
  19. data/lib/circus/application.rb +99 -0
  20. data/lib/circus/booth_client.rb +25 -0
  21. data/lib/circus/booth_tool.rb +98 -0
  22. data/lib/circus/cli.rb +147 -0
  23. data/lib/circus/clown_client.rb +27 -0
  24. data/lib/circus/connection_builder.rb +32 -0
  25. data/lib/circus/external_util.rb +14 -0
  26. data/lib/circus/local_config.rb +65 -0
  27. data/lib/circus/profiles/base.rb +115 -0
  28. data/lib/circus/profiles/django.rb +90 -0
  29. data/lib/circus/profiles/jekyll.rb +90 -0
  30. data/lib/circus/profiles/make_base.rb +17 -0
  31. data/lib/circus/profiles/pure_py.rb +46 -0
  32. data/lib/circus/profiles/pure_rb.rb +48 -0
  33. data/lib/circus/profiles/python_base.rb +39 -0
  34. data/lib/circus/profiles/rack.rb +59 -0
  35. data/lib/circus/profiles/ruby_base.rb +52 -0
  36. data/lib/circus/profiles/shell.rb +46 -0
  37. data/lib/circus/profiles.rb +10 -0
  38. data/lib/circus/repos/git.rb +42 -0
  39. data/lib/circus/repos/mercurial.rb +42 -0
  40. data/lib/circus/repos.rb +16 -0
  41. data/lib/circus/resource_allocator_client.rb +19 -0
  42. data/lib/circus/stdout_logger.rb +11 -0
  43. data/lib/circus/version.rb +3 -0
  44. data/lib/circus.rb +9 -0
  45. data/vendor/ruby-dbus/COPYING +504 -0
  46. data/vendor/ruby-dbus/ChangeLog +782 -0
  47. data/vendor/ruby-dbus/HOWTO-RELEASE +14 -0
  48. data/vendor/ruby-dbus/NEWS +104 -0
  49. data/vendor/ruby-dbus/README +53 -0
  50. data/vendor/ruby-dbus/Rakefile +47 -0
  51. data/vendor/ruby-dbus/doc/tutorial/src/00.index.page +12 -0
  52. data/vendor/ruby-dbus/doc/tutorial/src/10.intro.page +127 -0
  53. data/vendor/ruby-dbus/doc/tutorial/src/20.basic_client.page +174 -0
  54. data/vendor/ruby-dbus/doc/tutorial/src/30.service.page +121 -0
  55. data/vendor/ruby-dbus/doc/tutorial/src/default.css +129 -0
  56. data/vendor/ruby-dbus/doc/tutorial/src/default.template +46 -0
  57. data/vendor/ruby-dbus/examples/gdbus/gdbus +255 -0
  58. data/vendor/ruby-dbus/examples/gdbus/gdbus.glade +184 -0
  59. data/vendor/ruby-dbus/examples/gdbus/launch.sh +4 -0
  60. data/vendor/ruby-dbus/examples/no-introspect/nm-test.rb +21 -0
  61. data/vendor/ruby-dbus/examples/no-introspect/tracker-test.rb +16 -0
  62. data/vendor/ruby-dbus/examples/rhythmbox/playpause.rb +25 -0
  63. data/vendor/ruby-dbus/examples/service/call_service.rb +25 -0
  64. data/vendor/ruby-dbus/examples/service/service_newapi.rb +51 -0
  65. data/vendor/ruby-dbus/examples/simple/call_introspect.rb +34 -0
  66. data/vendor/ruby-dbus/examples/utils/listnames.rb +11 -0
  67. data/vendor/ruby-dbus/examples/utils/notify.rb +19 -0
  68. data/vendor/ruby-dbus/lib/dbus/auth.rb +156 -0
  69. data/vendor/ruby-dbus/lib/dbus/bus.rb +750 -0
  70. data/vendor/ruby-dbus/lib/dbus/export.rb +133 -0
  71. data/vendor/ruby-dbus/lib/dbus/introspect.rb +544 -0
  72. data/vendor/ruby-dbus/lib/dbus/marshall.rb +443 -0
  73. data/vendor/ruby-dbus/lib/dbus/matchrule.rb +100 -0
  74. data/vendor/ruby-dbus/lib/dbus/message.rb +293 -0
  75. data/vendor/ruby-dbus/lib/dbus/type.rb +222 -0
  76. data/vendor/ruby-dbus/lib/dbus.rb +89 -0
  77. data/vendor/ruby-dbus/ruby-dbus.gemspec +28 -0
  78. data/vendor/ruby-dbus/setup.rb +1585 -0
  79. data/vendor/ruby-dbus/test/Makefile +4 -0
  80. data/vendor/ruby-dbus/test/bus_driver_test.rb +21 -0
  81. data/vendor/ruby-dbus/test/server_robustness_test.rb +41 -0
  82. data/vendor/ruby-dbus/test/server_test.rb +44 -0
  83. data/vendor/ruby-dbus/test/service_newapi.rb +99 -0
  84. data/vendor/ruby-dbus/test/session_bus_test_manual.rb +20 -0
  85. data/vendor/ruby-dbus/test/signal_test.rb +57 -0
  86. data/vendor/ruby-dbus/test/t1 +4 -0
  87. data/vendor/ruby-dbus/test/t2.rb +66 -0
  88. data/vendor/ruby-dbus/test/t3-ticket27.rb +18 -0
  89. data/vendor/ruby-dbus/test/t5-report-dbus-interface.rb +58 -0
  90. data/vendor/ruby-dbus/test/t6-loop.rb +85 -0
  91. data/vendor/ruby-dbus/test/test_all +26 -0
  92. data/vendor/ruby-dbus/test/test_server +74 -0
  93. data/vendor/ruby-dbus/test/variant_test.rb +66 -0
  94. metadata +225 -0
@@ -0,0 +1,133 @@
1
+ # dbus/introspection.rb - module containing a low-level D-Bus introspection implementation
2
+ #
3
+ # This file is part of the ruby-dbus project
4
+ # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
5
+ #
6
+ # This library is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU Lesser General Public
8
+ # License, version 2.1 as published by the Free Software Foundation.
9
+ # See the file "COPYING" for the exact licensing terms.
10
+
11
+ require 'thread'
12
+
13
+ module DBus
14
+ # Exception raised when an interface cannot be found in an object.
15
+ class InterfaceNotInObject < Exception
16
+ end
17
+
18
+ # Exception raised when a method cannot be found in an inferface.
19
+ class MethodNotInInterface < Exception
20
+ end
21
+
22
+ # Method raised when a method returns an invalid return type.
23
+ class InvalidReturnType < Exception
24
+ end
25
+
26
+ # Exported object type
27
+ # = Exportable D-Bus object class
28
+ #
29
+ # Objects that are going to be exported by a D-Bus service
30
+ # should inherit from this class.
31
+ class Object
32
+ # The path of the object.
33
+ attr_reader :path
34
+ # The interfaces that the object supports.
35
+ attr_reader :intfs
36
+ # The service that the object is exported by.
37
+ attr_writer :service
38
+
39
+ @@intfs = Hash.new
40
+ @@cur_intf = nil
41
+ @@intfs_mutex = Mutex.new
42
+
43
+ # Create a new object with a given _path_.
44
+ def initialize(path)
45
+ @path = path
46
+ @intfs = @@intfs.dup
47
+ @service = nil
48
+ end
49
+
50
+ # State that the object implements the given _intf_.
51
+ def implements(intf)
52
+ @intfs[intf.name] = intf
53
+ end
54
+
55
+ # Dispatch a message _msg_.
56
+ def dispatch(msg)
57
+ case msg.message_type
58
+ when Message::METHOD_CALL
59
+ if not @intfs[msg.interface]
60
+ raise InterfaceNotInObject, msg.interface
61
+ end
62
+ meth = @intfs[msg.interface].methods[msg.member.to_sym]
63
+ raise MethodNotInInterface if not meth
64
+ methname = Object.make_method_name(msg.interface, msg.member)
65
+ reply = nil
66
+ begin
67
+ retdata = method(methname).call(*msg.params)
68
+ retdata = [*retdata]
69
+
70
+ reply = Message.new.reply_to(msg)
71
+ meth.rets.zip(retdata).each do |rsig, rdata|
72
+ reply.add_param(rsig.type, rdata)
73
+ end
74
+ rescue => ex
75
+ puts("DBus call Error: #{ex.to_s}")
76
+ reply = Message.error(msg, "org.freedesktop.DBus.Error.Failed", "#{ex.class}: #{ex}\n==== Backtrace ====\n#{ex.backtrace.join("\n")}")
77
+ end
78
+ @service.bus.send(reply.marshall)
79
+ end
80
+ end
81
+
82
+ # Select (and create) the interface that the following defined methods
83
+ # belong to.
84
+ def self.dbus_interface(s)
85
+ @@intfs_mutex.synchronize do
86
+ @@cur_intf = @@intfs[s] = Interface.new(s)
87
+ yield
88
+ @@cur_intf = nil
89
+ end
90
+ end
91
+
92
+ # Dummy undefined interface class.
93
+ class UndefinedInterface < ScriptError
94
+ def initialize(sym)
95
+ super "No interface specified for #{sym}"
96
+ end
97
+ end
98
+
99
+ # Defines an exportable method on the object with the given name _sym_,
100
+ # _prototype_ and the code in a block.
101
+ def self.dbus_method(sym, protoype = "", &block)
102
+ raise UndefinedInterface, sym if @@cur_intf.nil?
103
+ @@cur_intf.define(Method.new(sym.to_s).from_prototype(protoype))
104
+ define_method(Object.make_method_name(@@cur_intf.name, sym.to_s), &block)
105
+ end
106
+
107
+ # Emits a signal from the object with the given _interface_, signal
108
+ # _sig_ and arguments _args_.
109
+ def emit(intf, sig, *args)
110
+ @service.bus.emit(@service, self, intf, sig, *args)
111
+ end
112
+
113
+ # Defines a signal for the object with a given name _sym_ and _prototype_.
114
+ def self.dbus_signal(sym, protoype = "")
115
+ raise UndefinedInterface, sym if @@cur_intf.nil?
116
+ cur_intf = @@cur_intf
117
+ signal = Signal.new(sym.to_s).from_prototype(protoype)
118
+ cur_intf.define(Signal.new(sym.to_s).from_prototype(protoype))
119
+ define_method(sym.to_s) do |*args|
120
+ emit(cur_intf, signal, *args)
121
+ end
122
+ end
123
+
124
+ ####################################################################
125
+ private
126
+
127
+ # Helper method that returns a method name generated from the interface
128
+ # name _intfname_ and method name _methname_.
129
+ def self.make_method_name(intfname, methname)
130
+ "#{intfname}%%#{methname}"
131
+ end
132
+ end # class Object
133
+ end # module DBus
@@ -0,0 +1,544 @@
1
+ # dbus/introspection.rb - module containing a low-level D-Bus introspection implementation
2
+ #
3
+ # This file is part of the ruby-dbus project
4
+ # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
5
+ #
6
+ # This library is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU Lesser General Public
8
+ # License, version 2.1 as published by the Free Software Foundation.
9
+ # See the file "COPYING" for the exact licensing terms.
10
+
11
+ require 'rexml/document'
12
+
13
+ module DBus
14
+ # Regular expressions that should match all method names.
15
+ MethodSignalRE = /^[A-Za-z][A-Za-z0-9_]*$/
16
+ # Regular expressions that should match all interface names.
17
+ InterfaceElementRE = /^[A-Za-z][A-Za-z0-9_]*$/
18
+
19
+ # Exception raised when an unknown signal is used.
20
+ class UnknownSignal < Exception
21
+ end
22
+
23
+ # Exception raised when an invalid class definition is encountered.
24
+ class InvalidClassDefinition < Exception
25
+ end
26
+
27
+ # = D-Bus interface class
28
+ #
29
+ # This class is the interface descriptor. In most cases, the Introspect()
30
+ # method call instanciates and configures this class for us.
31
+ #
32
+ # It also is the local definition of interface exported by the program.
33
+ class Interface
34
+ # The name of the interface.
35
+ attr_reader :name
36
+ # The methods that are part of the interface.
37
+ attr_reader :methods
38
+ # The signals that are part of the interface.
39
+ attr_reader :signals
40
+
41
+ # Creates a new interface with a given _name_.
42
+ def initialize(name)
43
+ validate_name(name)
44
+ @name = name
45
+ @methods, @signals = Hash.new, Hash.new
46
+ end
47
+
48
+ # Validates a service _name_.
49
+ def validate_name(name)
50
+ raise InvalidIntrospectionData if name.size > 255
51
+ raise InvalidIntrospectionData if name =~ /^\./ or name =~ /\.$/
52
+ raise InvalidIntrospectionData if name =~ /\.\./
53
+ raise InvalidIntrospectionData if not name =~ /\./
54
+ name.split(".").each do |element|
55
+ raise InvalidIntrospectionData if not element =~ InterfaceElementRE
56
+ end
57
+ end
58
+
59
+ # Helper method for defining a method _m_.
60
+ def define(m)
61
+ if m.kind_of?(Method)
62
+ @methods[m.name.to_sym] = m
63
+ elsif m.kind_of?(Signal)
64
+ @signals[m.name.to_sym] = m
65
+ end
66
+ end
67
+ alias :<< :define
68
+
69
+ # Defines a method with name _id_ and a given _prototype_ in the
70
+ # interface.
71
+ def define_method(id, prototype)
72
+ m = Method.new(id)
73
+ m.from_prototype(prototype)
74
+ define(m)
75
+ end
76
+ end # class Interface
77
+
78
+ # = A formal parameter has a name and a type
79
+ class FormalParameter
80
+ attr_reader :name
81
+ attr_reader :type
82
+
83
+ def initialize(name, type)
84
+ @name = name
85
+ @type = type
86
+ end
87
+
88
+ # backward compatibility, deprecated
89
+ def [](index)
90
+ case index
91
+ when 0: name
92
+ when 1: type
93
+ else nil
94
+ end
95
+ end
96
+ end
97
+
98
+ # = D-Bus interface element class
99
+ #
100
+ # This is a generic class for entities that are part of the interface
101
+ # such as methods and signals.
102
+ class InterfaceElement
103
+ # The name of the interface element.
104
+ attr_reader :name
105
+ # The parameters of the interface element
106
+ attr_reader :params
107
+
108
+ # Validates element _name_.
109
+ def validate_name(name)
110
+ if (not name =~ MethodSignalRE) or (name.size > 255)
111
+ raise InvalidMethodName name
112
+ end
113
+ end
114
+
115
+ # Creates a new element with the given _name_.
116
+ def initialize(name)
117
+ validate_name(name.to_s)
118
+ @name = name
119
+ @params = Array.new
120
+ end
121
+
122
+ # Adds a formal parameter with _name_ and _signature_
123
+ # (See also Message#add_param which takes signature+value)
124
+ def add_fparam(name, signature)
125
+ @params << FormalParameter.new(name, signature)
126
+ end
127
+ end # class InterfaceElement
128
+
129
+ # = D-Bus interface method class
130
+ #
131
+ # This is a class representing methods that are part of an interface.
132
+ class Method < InterfaceElement
133
+ # The list of return values for the method.
134
+ attr_reader :rets
135
+
136
+ # Creates a new method interface element with the given _name_.
137
+ def initialize(name)
138
+ super(name)
139
+ @rets = Array.new
140
+ end
141
+
142
+ # Add a return value _name_ and _signature_.
143
+ def add_return(name, signature)
144
+ @rets << FormalParameter.new(name, signature)
145
+ end
146
+
147
+ # Add parameter types by parsing the given _prototype_.
148
+ def from_prototype(prototype)
149
+ prototype.split(/, */).each do |arg|
150
+ arg = arg.split(" ")
151
+ raise InvalidClassDefinition if arg.size != 2
152
+ dir, arg = arg
153
+ if arg =~ /:/
154
+ arg = arg.split(":")
155
+ name, sig = arg
156
+ else
157
+ sig = arg
158
+ end
159
+ case dir
160
+ when "in"
161
+ add_fparam(name, sig)
162
+ when "out"
163
+ add_return(name, sig)
164
+ end
165
+ end
166
+ self
167
+ end
168
+
169
+ # Return an XML string representation of the method interface elment.
170
+ def to_xml
171
+ xml = %{<method name="#{@name}">\n}
172
+ @params.each do |param|
173
+ name = param.name ? %{name="#{param.name}" } : ""
174
+ xml += %{<arg #{name}direction="in" type="#{param.type}"/>\n}
175
+ end
176
+ @rets.each do |param|
177
+ name = param.name ? %{name="#{param.name}" } : ""
178
+ xml += %{<arg #{name}direction="out" type="#{param.type}"/>\n}
179
+ end
180
+ xml += %{</method>\n}
181
+ xml
182
+ end
183
+ end # class Method
184
+
185
+ # = D-Bus interface signal class
186
+ #
187
+ # This is a class representing signals that are part of an interface.
188
+ class Signal < InterfaceElement
189
+ # Add parameter types based on the given _prototype_.
190
+ def from_prototype(prototype)
191
+ prototype.split(/, */).each do |arg|
192
+ if arg =~ /:/
193
+ arg = arg.split(":")
194
+ name, sig = arg
195
+ else
196
+ sig = arg
197
+ end
198
+ add_fparam(name, sig)
199
+ end
200
+ self
201
+ end
202
+
203
+ # Return an XML string representation of the signal interface elment.
204
+ def to_xml
205
+ xml = %{<signal name="#{@name}">\n}
206
+ @params.each do |param|
207
+ name = param.name ? %{name="#{param.name}" } : ""
208
+ xml += %{<arg #{name}type="#{param.type}"/>\n}
209
+ end
210
+ xml += %{</signal>\n}
211
+ xml
212
+ end
213
+ end # class Signal
214
+
215
+ # = D-Bus introspect XML parser class
216
+ #
217
+ # This class parses introspection XML of an object and constructs a tree
218
+ # of Node, Interface, Method, Signal instances.
219
+ class IntrospectXMLParser
220
+ # Creates a new parser for XML data in string _xml_.
221
+ def initialize(xml)
222
+ @xml = xml
223
+ end
224
+
225
+ # Recursively parses the subnodes, constructing the tree.
226
+ def parse_subnodes
227
+ subnodes = Array.new
228
+ t = Time.now
229
+ d = REXML::Document.new(@xml)
230
+ d.elements.each("node/node") do |e|
231
+ subnodes << e.attributes["name"]
232
+ end
233
+ subnodes
234
+ end
235
+
236
+ # Parses the XML, constructing the tree.
237
+ def parse
238
+ ret = Array.new
239
+ subnodes = Array.new
240
+ t = Time.now
241
+ d = REXML::Document.new(@xml)
242
+ d.elements.each("node/node") do |e|
243
+ subnodes << e.attributes["name"]
244
+ end
245
+ d.elements.each("node/interface") do |e|
246
+ i = Interface.new(e.attributes["name"])
247
+ e.elements.each("method") do |me|
248
+ m = Method.new(me.attributes["name"])
249
+ parse_methsig(me, m)
250
+ i << m
251
+ end
252
+ e.elements.each("signal") do |se|
253
+ s = Signal.new(se.attributes["name"])
254
+ parse_methsig(se, s)
255
+ i << s
256
+ end
257
+ ret << i
258
+ end
259
+ d = Time.now - t
260
+ if d > 2
261
+ puts "Some XML took more that two secs to parse. Optimize me!" if $DEBUG
262
+ end
263
+ [ret, subnodes]
264
+ end
265
+
266
+ ######################################################################
267
+ private
268
+
269
+ # Parses a method signature XML element _e_ and initialises
270
+ # method/signal _m_.
271
+ def parse_methsig(e, m)
272
+ e.elements.each("arg") do |ae|
273
+ name = ae.attributes["name"]
274
+ dir = ae.attributes["direction"]
275
+ sig = ae.attributes["type"]
276
+ if m.is_a?(DBus::Signal)
277
+ m.add_fparam(name, sig)
278
+ elsif m.is_a?(DBus::Method)
279
+ case dir
280
+ when "in"
281
+ m.add_fparam(name, sig)
282
+ when "out"
283
+ m.add_return(name, sig)
284
+ end
285
+ else
286
+ raise NotImplementedError, dir
287
+ end
288
+ end
289
+ end
290
+ end # class IntrospectXMLParser
291
+
292
+ # = D-Bus proxy object interface class
293
+ #
294
+ # A class similar to the normal Interface used as a proxy for remote
295
+ # object interfaces.
296
+ class ProxyObjectInterface
297
+ # The proxied methods contained in the interface.
298
+ attr_accessor :methods
299
+ # The proxied signals contained in the interface.
300
+ attr_accessor :signals
301
+ # The proxy object to which this interface belongs.
302
+ attr_reader :object
303
+ # The name of the interface.
304
+ attr_reader :name
305
+
306
+ # Creates a new proxy interface for the given proxy _object_
307
+ # and the given _name_.
308
+ def initialize(object, name)
309
+ @object, @name = object, name
310
+ @methods, @signals = Hash.new, Hash.new
311
+ end
312
+
313
+ # Returns the string representation of the interface (the name).
314
+ def to_str
315
+ @name
316
+ end
317
+
318
+ # Returns the singleton class of the interface.
319
+ def singleton_class
320
+ (class << self ; self ; end)
321
+ end
322
+
323
+ # FIXME
324
+ def check_for_eval(s)
325
+ raise RuntimeError, "invalid internal data '#{s}'" if not s.to_s =~ /^[A-Za-z0-9_]*$/
326
+ end
327
+
328
+ # FIXME
329
+ def check_for_quoted_eval(s)
330
+ raise RuntimeError, "invalid internal data '#{s}'" if not s.to_s =~ /^[^"]+$/
331
+ end
332
+
333
+ # Defines a method on the interface from the Method descriptor _m_.
334
+ def define_method_from_descriptor(m)
335
+ check_for_eval(m.name)
336
+ check_for_quoted_eval(@name)
337
+ methdef = "def #{m.name}("
338
+ methdef += (0..(m.params.size - 1)).to_a.collect { |n|
339
+ "arg#{n}"
340
+ }.join(", ")
341
+ methdef += %{)
342
+ msg = Message.new(Message::METHOD_CALL)
343
+ msg.path = @object.path
344
+ msg.interface = "#{@name}"
345
+ msg.destination = @object.destination
346
+ msg.member = "#{m.name}"
347
+ msg.sender = @object.bus.unique_name
348
+ }
349
+ idx = 0
350
+ m.params.each do |fpar|
351
+ par = fpar.type
352
+ check_for_quoted_eval(par)
353
+
354
+ # This is the signature validity check
355
+ Type::Parser.new(par).parse
356
+
357
+ methdef += %{
358
+ msg.add_param("#{par}", arg#{idx})
359
+ }
360
+ idx += 1
361
+ end
362
+ methdef += "
363
+ ret = nil
364
+ if block_given?
365
+ @object.bus.on_return(msg) do |rmsg|
366
+ if rmsg.is_a?(Error)
367
+ yield(rmsg)
368
+ else
369
+ yield(rmsg, *rmsg.params)
370
+ end
371
+ end
372
+ @object.bus.send(msg.marshall)
373
+ else
374
+ @object.bus.send_sync(msg) do |rmsg|
375
+ if rmsg.is_a?(Error)
376
+ raise rmsg
377
+ else
378
+ ret = rmsg.params
379
+ end
380
+ end
381
+ end
382
+ ret
383
+ end
384
+ "
385
+ singleton_class.class_eval(methdef)
386
+ @methods[m.name] = m
387
+ end
388
+
389
+ # Defines a signal from the descriptor _s_.
390
+ def define_signal_from_descriptor(s)
391
+ @signals[s.name] = s
392
+ end
393
+
394
+ # Defines a signal or method based on the descriptor _m_.
395
+ def define(m)
396
+ if m.kind_of?(Method)
397
+ define_method_from_descriptor(m)
398
+ elsif m.kind_of?(Signal)
399
+ define_signal_from_descriptor(m)
400
+ end
401
+ end
402
+
403
+ # Defines a proxied method on the interface.
404
+ def define_method(methodname, prototype)
405
+ m = Method.new(methodname)
406
+ m.from_prototype(prototype)
407
+ define(m)
408
+ end
409
+
410
+ # Registers a handler (code block) for a signal with _name_ arriving
411
+ # over the given _bus_.
412
+ def on_signal(bus, name, &block)
413
+ mr = DBus::MatchRule.new.from_signal(self, name)
414
+ bus.add_match(mr) { |msg| block.call(*msg.params) }
415
+ end
416
+ end # class ProxyObjectInterface
417
+
418
+ # D-Bus proxy object class
419
+ #
420
+ # Class representing a remote object in an external application.
421
+ # Typically, calling a method on an instance of a ProxyObject sends a message
422
+ # over the bus so that the method is executed remotely on the correctponding
423
+ # object.
424
+ class ProxyObject
425
+ # The subnodes of the object in the tree.
426
+ attr_accessor :subnodes
427
+ # Flag determining whether the object has been introspected.
428
+ attr_accessor :introspected
429
+ # The (remote) destination of the object.
430
+ attr_reader :destination
431
+ # The path to the object.
432
+ attr_reader :path
433
+ # The bus the object is reachable via.
434
+ attr_reader :bus
435
+ # The default interface of the object.
436
+ attr_accessor :default_iface
437
+
438
+ # Creates a new proxy object living on the given _bus_ at destination _dest_
439
+ # on the given _path_.
440
+ def initialize(bus, dest, path)
441
+ @bus, @destination, @path = bus, dest, path
442
+ @interfaces = Hash.new
443
+ @subnodes = Array.new
444
+ end
445
+
446
+ # Returns the interfaces of the object.
447
+ def interfaces
448
+ @interfaces.keys
449
+ end
450
+
451
+ # Retrieves an interface of the proxy object (ProxyObjectInterface instance).
452
+ def [](intfname)
453
+ @interfaces[intfname]
454
+ end
455
+
456
+ # Maps the given interface name _intfname_ to the given interface _intf.
457
+ def []=(intfname, intf)
458
+ @interfaces[intfname] = intf
459
+ end
460
+
461
+ # Introspects the remote object. Allows you to find and select
462
+ # interfaces on the object.
463
+ def introspect
464
+ # Synchronous call here.
465
+ xml = @bus.introspect_data(@destination, @path)
466
+ ProxyObjectFactory.introspect_into(self, xml)
467
+ xml
468
+ end
469
+
470
+ # Returns whether the object has an interface with the given _name_.
471
+ def has_iface?(name)
472
+ raise "Cannot call has_iface? is not introspected" if not @introspected
473
+ @interfaces.member?(name)
474
+ end
475
+
476
+ # Registers a handler, the code block, for a signal with the given _name_.
477
+ def on_signal(name, &block)
478
+ if @default_iface and has_iface?(@default_iface)
479
+ @interfaces[@default_iface].on_signal(@bus, name, &block)
480
+ else
481
+ # TODO improve
482
+ raise NoMethodError
483
+ end
484
+ end
485
+
486
+ ####################################################
487
+ private
488
+
489
+ # Handles all unkown methods, mostly to route method calls to the
490
+ # default interface.
491
+ def method_missing(name, *args)
492
+ if @default_iface and has_iface?(@default_iface)
493
+ begin
494
+ @interfaces[@default_iface].method(name).call(*args)
495
+ rescue NameError => e
496
+ # interesting, foo.method("unknown")
497
+ # raises NameError, not NoMethodError
498
+ match = /undefined method `([^']*)' for class `([^']*)'/.match e
499
+ raise unless match and match[2] == "DBus::ProxyObjectInterface"
500
+ # BTW e.exception("...") would preserve the class.
501
+ raise NoMethodError,"undefined method `#{match[1]}' for DBus interface `#{@default_iface}' on object `#{@path}'"
502
+ end
503
+ else
504
+ # TODO distinguish:
505
+ # - di not specified
506
+ #TODO
507
+ # - di is specified but not found in introspection data
508
+ raise NoMethodError, "undefined method `#{name}' for DBus interface `#{@default_iface}' on object `#{@path}'"
509
+ end
510
+ end
511
+ end # class ProxyObject
512
+
513
+ # = D-Bus proxy object factory class
514
+ #
515
+ # Class that generates and sets up a proxy object based on introspection data.
516
+ class ProxyObjectFactory
517
+ # Creates a new proxy object factory for the given introspection XML _xml_,
518
+ # _bus_, destination _dest_, and _path_.
519
+ def initialize(xml, bus, dest, path)
520
+ @xml, @bus, @path, @dest = xml, bus, path, dest
521
+ end
522
+
523
+ # Investigates the sub-nodes of the proxy object _po_ based on the
524
+ # introspection XML data _xml_ and sets them up recursively.
525
+ def ProxyObjectFactory.introspect_into(po, xml)
526
+ intfs, po.subnodes = IntrospectXMLParser.new(xml).parse
527
+ intfs.each do |i|
528
+ poi = ProxyObjectInterface.new(po, i.name)
529
+ i.methods.each_value { |m| poi.define(m) }
530
+ i.signals.each_value { |s| poi.define(s) }
531
+ po[i.name] = poi
532
+ end
533
+ po.introspected = true
534
+ end
535
+
536
+ # Generates, sets up and returns the proxy object.
537
+ def build
538
+ po = ProxyObject.new(@bus, @dest, @path)
539
+ ProxyObjectFactory.introspect_into(po, @xml)
540
+ po
541
+ end
542
+ end # class ProxyObjectFactory
543
+ end # module DBus
544
+