em-ruby-dbus 0.11.0

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 (76) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +504 -0
  3. data/NEWS +253 -0
  4. data/README.md +93 -0
  5. data/Rakefile +58 -0
  6. data/VERSION +1 -0
  7. data/doc/Reference.md +207 -0
  8. data/doc/Tutorial.md +480 -0
  9. data/doc/ex-calling-methods.body.rb +8 -0
  10. data/doc/ex-calling-methods.rb +3 -0
  11. data/doc/ex-properties.body.rb +9 -0
  12. data/doc/ex-properties.rb +3 -0
  13. data/doc/ex-setup.rb +7 -0
  14. data/doc/ex-signal.body.rb +20 -0
  15. data/doc/ex-signal.rb +3 -0
  16. data/doc/example-helper.rb +6 -0
  17. data/em-ruby-dbus.gemspec +20 -0
  18. data/examples/gdbus/gdbus +255 -0
  19. data/examples/gdbus/gdbus.glade +184 -0
  20. data/examples/gdbus/launch.sh +4 -0
  21. data/examples/no-introspect/nm-test.rb +21 -0
  22. data/examples/no-introspect/tracker-test.rb +16 -0
  23. data/examples/rhythmbox/playpause.rb +25 -0
  24. data/examples/service/call_service.rb +25 -0
  25. data/examples/service/service_newapi.rb +51 -0
  26. data/examples/simple/call_introspect.rb +34 -0
  27. data/examples/simple/properties.rb +19 -0
  28. data/examples/utils/listnames.rb +11 -0
  29. data/examples/utils/notify.rb +19 -0
  30. data/lib/dbus.rb +82 -0
  31. data/lib/dbus/auth.rb +269 -0
  32. data/lib/dbus/bus.rb +739 -0
  33. data/lib/dbus/core_ext/array/extract_options.rb +31 -0
  34. data/lib/dbus/core_ext/class/attribute.rb +129 -0
  35. data/lib/dbus/core_ext/kernel/singleton_class.rb +8 -0
  36. data/lib/dbus/core_ext/module/remove_method.rb +14 -0
  37. data/lib/dbus/error.rb +46 -0
  38. data/lib/dbus/export.rb +128 -0
  39. data/lib/dbus/introspect.rb +219 -0
  40. data/lib/dbus/logger.rb +31 -0
  41. data/lib/dbus/loop-em.rb +19 -0
  42. data/lib/dbus/marshall.rb +434 -0
  43. data/lib/dbus/matchrule.rb +101 -0
  44. data/lib/dbus/message.rb +276 -0
  45. data/lib/dbus/message_queue.rb +166 -0
  46. data/lib/dbus/proxy_object.rb +149 -0
  47. data/lib/dbus/proxy_object_factory.rb +41 -0
  48. data/lib/dbus/proxy_object_interface.rb +128 -0
  49. data/lib/dbus/type.rb +193 -0
  50. data/lib/dbus/xml.rb +161 -0
  51. data/test/async_spec.rb +47 -0
  52. data/test/binding_spec.rb +74 -0
  53. data/test/bus_and_xml_backend_spec.rb +39 -0
  54. data/test/bus_driver_spec.rb +20 -0
  55. data/test/bus_spec.rb +20 -0
  56. data/test/byte_array_spec.rb +38 -0
  57. data/test/err_msg_spec.rb +42 -0
  58. data/test/introspect_xml_parser_spec.rb +26 -0
  59. data/test/introspection_spec.rb +32 -0
  60. data/test/main_loop_spec.rb +82 -0
  61. data/test/property_spec.rb +53 -0
  62. data/test/server_robustness_spec.rb +66 -0
  63. data/test/server_spec.rb +53 -0
  64. data/test/service_newapi.rb +217 -0
  65. data/test/session_bus_spec_manual.rb +15 -0
  66. data/test/signal_spec.rb +90 -0
  67. data/test/spec_helper.rb +33 -0
  68. data/test/thread_safety_spec.rb +31 -0
  69. data/test/tools/dbus-launch-simple +35 -0
  70. data/test/tools/dbus-limited-session.conf +28 -0
  71. data/test/tools/test_env +13 -0
  72. data/test/tools/test_server +39 -0
  73. data/test/type_spec.rb +19 -0
  74. data/test/value_spec.rb +81 -0
  75. data/test/variant_spec.rb +66 -0
  76. metadata +145 -0
@@ -0,0 +1,149 @@
1
+ # This file is part of the ruby-dbus project
2
+ # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
3
+ # Copyright (C) 2009-2014 Martin Vidner
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License, version 2.1 as published by the Free Software Foundation.
8
+ # See the file "COPYING" for the exact licensing terms.
9
+
10
+ module DBus
11
+ # D-Bus proxy object class
12
+ #
13
+ # Class representing a remote object in an external application.
14
+ # Typically, calling a method on an instance of a ProxyObject sends a message
15
+ # over the bus so that the method is executed remotely on the correctponding
16
+ # object.
17
+ class ProxyObject
18
+ # The names of direct subnodes of the object in the tree.
19
+ attr_accessor :subnodes
20
+ # Flag determining whether the object has been introspected.
21
+ attr_accessor :introspected
22
+ # The (remote) destination of the object.
23
+ attr_reader :destination
24
+ # The path to the object.
25
+ attr_reader :path
26
+ # The bus the object is reachable via.
27
+ attr_reader :bus
28
+ # @return [String] The name of the default interface of the object.
29
+ attr_accessor :default_iface
30
+
31
+ # Creates a new proxy object living on the given _bus_ at destination _dest_
32
+ # on the given _path_.
33
+ def initialize(bus, dest, path)
34
+ @bus, @destination, @path = bus, dest, path
35
+ @interfaces = Hash.new
36
+ @subnodes = Array.new
37
+ end
38
+
39
+ # Returns the interfaces of the object.
40
+ def interfaces
41
+ @interfaces.keys
42
+ end
43
+
44
+ # Retrieves an interface of the proxy object
45
+ # @param [String] intfname
46
+ # @return [ProxyObjectInterface]
47
+ def [](intfname)
48
+ @interfaces[intfname]
49
+ end
50
+
51
+ # Maps the given interface name _intfname_ to the given interface _intf.
52
+ # @param [String] intfname
53
+ # @param [ProxyObjectInterface] intf
54
+ # @return [ProxyObjectInterface]
55
+ def []=(intfname, intf)
56
+ @interfaces[intfname] = intf
57
+ end
58
+
59
+ # Introspects the remote object. Allows you to find and select
60
+ # interfaces on the object.
61
+ def introspect
62
+ # Synchronous call here.
63
+ xml = @bus.introspect_data(@destination, @path)
64
+ ProxyObjectFactory.introspect_into(self, xml)
65
+ define_shortcut_methods()
66
+ xml
67
+ end
68
+
69
+ # For each non duplicated method name in any interface present on the
70
+ # caller, defines a shortcut method dynamically.
71
+ # This function is automatically called when a {ProxyObject} is
72
+ # introspected.
73
+ def define_shortcut_methods
74
+ # builds a list of duplicated methods
75
+ dup_meths, univocal_meths = [],{}
76
+ @interfaces.each_value do |intf|
77
+ intf.methods.each_value do |meth|
78
+ name = meth.name.to_sym
79
+ # don't overwrite instance methods!
80
+ if dup_meths.include? name or self.class.instance_methods.include? name
81
+ next
82
+ elsif univocal_meths.include? name
83
+ univocal_meths.delete name
84
+ dup_meths << name
85
+ else
86
+ univocal_meths[name] = intf
87
+ end
88
+ end
89
+ end
90
+ univocal_meths.each do |name, intf|
91
+ # creates a shortcut function that forwards each call to the method on
92
+ # the appropriate intf
93
+ singleton_class.class_eval do
94
+ define_method name do |*args, &reply_handler|
95
+ intf.method(name).call(*args, &reply_handler)
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ # Returns whether the object has an interface with the given _name_.
102
+ def has_iface?(name)
103
+ raise "Cannot call has_iface? if not introspected" if not @introspected
104
+ @interfaces.member?(name)
105
+ end
106
+
107
+ # Registers a handler, the code block, for a signal with the given _name_.
108
+ # It uses _default_iface_ which must have been set.
109
+ # @return [void]
110
+ def on_signal(name, &block)
111
+ if @default_iface and has_iface?(@default_iface)
112
+ @interfaces[@default_iface].on_signal(name, &block)
113
+ else
114
+ # TODO improve
115
+ raise NoMethodError
116
+ end
117
+ end
118
+
119
+ ####################################################
120
+ private
121
+
122
+ # Handles all unkown methods, mostly to route method calls to the
123
+ # default interface.
124
+ def method_missing(name, *args, &reply_handler)
125
+ if @default_iface and has_iface?(@default_iface)
126
+ begin
127
+ @interfaces[@default_iface].method(name).call(*args, &reply_handler)
128
+ rescue NameError => e
129
+ # interesting, foo.method("unknown")
130
+ # raises NameError, not NoMethodError
131
+ raise unless e.to_s =~ /undefined method `#{name}'/
132
+ # BTW e.exception("...") would preserve the class.
133
+ raise NoMethodError,"undefined method `#{name}' for DBus interface `#{@default_iface}' on object `#{@path}'"
134
+ end
135
+ else
136
+ # TODO distinguish:
137
+ # - di not specified
138
+ #TODO
139
+ # - di is specified but not found in introspection data
140
+ raise NoMethodError, "undefined method `#{name}' for DBus interface `#{@default_iface}' on object `#{@path}'"
141
+ end
142
+ end
143
+
144
+ # Returns the singleton class of the object.
145
+ def singleton_class
146
+ (class << self ; self ; end)
147
+ end
148
+ end # class ProxyObject
149
+ end
@@ -0,0 +1,41 @@
1
+ # This file is part of the ruby-dbus project
2
+ # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
3
+ # Copyright (C) 2009-2014 Martin Vidner
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License, version 2.1 as published by the Free Software Foundation.
8
+ # See the file "COPYING" for the exact licensing terms.
9
+
10
+ module DBus
11
+ # = D-Bus proxy object factory class
12
+ #
13
+ # Class that generates and sets up a proxy object based on introspection data.
14
+ class ProxyObjectFactory
15
+ # Creates a new proxy object factory for the given introspection XML _xml_,
16
+ # _bus_, destination _dest_, and _path_.
17
+ def initialize(xml, bus, dest, path)
18
+ @xml, @bus, @path, @dest = xml, bus, path, dest
19
+ end
20
+
21
+ # Investigates the sub-nodes of the proxy object _po_ based on the
22
+ # introspection XML data _xml_ and sets them up recursively.
23
+ def ProxyObjectFactory.introspect_into(po, xml)
24
+ intfs, po.subnodes = IntrospectXMLParser.new(xml).parse
25
+ intfs.each do |i|
26
+ poi = ProxyObjectInterface.new(po, i.name)
27
+ i.methods.each_value { |m| poi.define(m) }
28
+ i.signals.each_value { |s| poi.define(s) }
29
+ po[i.name] = poi
30
+ end
31
+ po.introspected = true
32
+ end
33
+
34
+ # Generates, sets up and returns the proxy object.
35
+ def build
36
+ po = ProxyObject.new(@bus, @dest, @path)
37
+ ProxyObjectFactory.introspect_into(po, @xml)
38
+ po
39
+ end
40
+ end # class ProxyObjectFactory
41
+ end
@@ -0,0 +1,128 @@
1
+ # This file is part of the ruby-dbus project
2
+ # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
3
+ # Copyright (C) 2009-2014 Martin Vidner
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License, version 2.1 as published by the Free Software Foundation.
8
+ # See the file "COPYING" for the exact licensing terms.
9
+
10
+ module DBus
11
+ # = D-Bus proxy object interface class
12
+ #
13
+ # A class similar to the normal Interface used as a proxy for remote
14
+ # object interfaces.
15
+ class ProxyObjectInterface
16
+ # The proxied methods contained in the interface.
17
+ attr_accessor :methods
18
+ # The proxied signals contained in the interface.
19
+ attr_accessor :signals
20
+ # The proxy object to which this interface belongs.
21
+ attr_reader :object
22
+ # The name of the interface.
23
+ attr_reader :name
24
+
25
+ # Creates a new proxy interface for the given proxy _object_
26
+ # and the given _name_.
27
+ def initialize(object, name)
28
+ @object, @name = object, name
29
+ @methods, @signals = Hash.new, Hash.new
30
+ end
31
+
32
+ # Returns the string representation of the interface (the name).
33
+ def to_str
34
+ @name
35
+ end
36
+
37
+ # Returns the singleton class of the interface.
38
+ def singleton_class
39
+ (class << self ; self ; end)
40
+ end
41
+
42
+ # Defines a method on the interface from the Method descriptor _m_.
43
+ def define_method_from_descriptor(m)
44
+ m.params.each do |fpar|
45
+ par = fpar.type
46
+ # This is the signature validity check
47
+ Type::Parser.new(par).parse
48
+ end
49
+
50
+ singleton_class.class_eval do
51
+ define_method m.name do |*args, &reply_handler|
52
+ if m.params.size != args.size
53
+ raise ArgumentError, "wrong number of arguments (#{args.size} for #{m.params.size})"
54
+ end
55
+
56
+ msg = Message.new(Message::METHOD_CALL)
57
+ msg.path = @object.path
58
+ msg.interface = @name
59
+ msg.destination = @object.destination
60
+ msg.member = m.name
61
+ msg.sender = @object.bus.unique_name
62
+ m.params.each do |fpar|
63
+ par = fpar.type
64
+ msg.add_param(par, args.shift)
65
+ end
66
+ @object.bus.send_sync_or_async(msg, &reply_handler)
67
+ end
68
+ end
69
+
70
+ @methods[m.name] = m
71
+ end
72
+
73
+ # Defines a signal from the descriptor _s_.
74
+ def define_signal_from_descriptor(s)
75
+ @signals[s.name] = s
76
+ end
77
+
78
+ # Defines a signal or method based on the descriptor _m_.
79
+ def define(m)
80
+ if m.kind_of?(Method)
81
+ define_method_from_descriptor(m)
82
+ elsif m.kind_of?(Signal)
83
+ define_signal_from_descriptor(m)
84
+ end
85
+ end
86
+
87
+ # Defines a proxied method on the interface.
88
+ def define_method(methodname, prototype)
89
+ m = Method.new(methodname)
90
+ m.from_prototype(prototype)
91
+ define(m)
92
+ end
93
+
94
+ # @overload on_signal(name, &block)
95
+ # @overload on_signal(bus, name, &block)
96
+ # Registers a handler (code block) for a signal with _name_ arriving
97
+ # over the given _bus_. If no block is given, the signal is unregistered.
98
+ # Note that specifying _bus_ is discouraged and the option is kept only for
99
+ # backward compatibility.
100
+ # @return [void]
101
+ def on_signal(bus = @object.bus, name, &block)
102
+ mr = DBus::MatchRule.new.from_signal(self, name)
103
+ if block.nil?
104
+ bus.remove_match(mr)
105
+ else
106
+ bus.add_match(mr) { |msg| block.call(*msg.params) }
107
+ end
108
+ end
109
+
110
+ PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"
111
+
112
+ # Read a property.
113
+ def [](propname)
114
+ self.object[PROPERTY_INTERFACE].Get(self.name, propname)[0]
115
+ end
116
+
117
+ # Write a property.
118
+ def []=(propname, value)
119
+ self.object[PROPERTY_INTERFACE].Set(self.name, propname, value)
120
+ end
121
+
122
+ # Read all properties at once, as a hash.
123
+ # @return [Hash{String}]
124
+ def all_properties
125
+ self.object[PROPERTY_INTERFACE].GetAll(self.name)[0]
126
+ end
127
+ end # class ProxyObjectInterface
128
+ end
data/lib/dbus/type.rb ADDED
@@ -0,0 +1,193 @@
1
+ # dbus/type.rb - module containing low-level D-Bus data type information
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
+ module DBus
12
+
13
+ # = D-Bus type module
14
+ #
15
+ # This module containts the constants of the types specified in the D-Bus
16
+ # protocol.
17
+ module Type
18
+ # Mapping from type number to name and alignment.
19
+ TypeMapping = {
20
+ 0 => ["INVALID", nil],
21
+ ?y => ["BYTE", 1],
22
+ ?b => ["BOOLEAN", 4],
23
+ ?n => ["INT16", 2],
24
+ ?q => ["UINT16", 2],
25
+ ?i => ["INT32", 4],
26
+ ?u => ["UINT32", 4],
27
+ ?x => ["INT64", 8],
28
+ ?t => ["UINT64", 8],
29
+ ?d => ["DOUBLE", 8],
30
+ ?r => ["STRUCT", 8],
31
+ ?a => ["ARRAY", 4],
32
+ ?v => ["VARIANT", 1],
33
+ ?o => ["OBJECT_PATH", 4],
34
+ ?s => ["STRING", 4],
35
+ ?g => ["SIGNATURE", 1],
36
+ ?e => ["DICT_ENTRY", 8],
37
+ ?h => ["UNIX_FD", 4],
38
+ }
39
+ # Defines the set of constants
40
+ TypeMapping.each_pair do |key, value|
41
+ Type.const_set(value.first, key)
42
+ end
43
+
44
+ # Exception raised when an unknown/incorrect type is encountered.
45
+ class SignatureException < Exception
46
+ end
47
+
48
+ # = D-Bus type conversion class
49
+ #
50
+ # Helper class for representing a D-Bus type.
51
+ class Type
52
+ # Returns the signature type number.
53
+ attr_reader :sigtype
54
+ # Return contained member types.
55
+ attr_reader :members
56
+
57
+ # Create a new type instance for type number _sigtype_.
58
+ def initialize(sigtype)
59
+ if not TypeMapping.keys.member?(sigtype)
60
+ raise SignatureException, "Unknown key in signature: #{sigtype.chr}"
61
+ end
62
+ @sigtype = sigtype
63
+ @members = Array.new
64
+ end
65
+
66
+ # Return the required alignment for the type.
67
+ def alignment
68
+ TypeMapping[@sigtype].last
69
+ end
70
+
71
+ # Return a string representation of the type according to the
72
+ # D-Bus specification.
73
+ def to_s
74
+ case @sigtype
75
+ when STRUCT
76
+ "(" + @members.collect { |t| t.to_s }.join + ")"
77
+ when ARRAY
78
+ "a" + child.to_s
79
+ when DICT_ENTRY
80
+ "{" + @members.collect { |t| t.to_s }.join + "}"
81
+ else
82
+ if not TypeMapping.keys.member?(@sigtype)
83
+ raise NotImplementedError
84
+ end
85
+ @sigtype.chr
86
+ end
87
+ end
88
+
89
+ # Add a new member type _a_.
90
+ def <<(a)
91
+ if not [STRUCT, ARRAY, DICT_ENTRY].member?(@sigtype)
92
+ raise SignatureException
93
+ end
94
+ raise SignatureException if @sigtype == ARRAY and @members.size > 0
95
+ if @sigtype == DICT_ENTRY
96
+ if @members.size == 2
97
+ raise SignatureException, "Dict entries have exactly two members"
98
+ end
99
+ if @members.size == 0
100
+ if [STRUCT, ARRAY, DICT_ENTRY].member?(a.sigtype)
101
+ raise SignatureException, "Dict entry keys must be basic types"
102
+ end
103
+ end
104
+ end
105
+ @members << a
106
+ end
107
+
108
+ # Return the first contained member type.
109
+ def child
110
+ @members[0]
111
+ end
112
+
113
+ def inspect
114
+ s = TypeMapping[@sigtype].first
115
+ if [STRUCT, ARRAY].member?(@sigtype)
116
+ s += ": " + @members.inspect
117
+ end
118
+ s
119
+ end
120
+ end # class Type
121
+
122
+ # = D-Bus type parser class
123
+ #
124
+ # Helper class to parse a type signature in the protocol.
125
+ class Parser
126
+ # Create a new parser for the given _signature_.
127
+ def initialize(signature)
128
+ @signature = signature
129
+ @idx = 0
130
+ end
131
+
132
+ # Returns the next character from the signature.
133
+ def nextchar
134
+ c = @signature[@idx]
135
+ @idx += 1
136
+ c
137
+ end
138
+
139
+ # Parse one character _c_ of the signature.
140
+ def parse_one(c)
141
+ res = nil
142
+ case c
143
+ when ?a
144
+ res = Type.new(ARRAY)
145
+ c = nextchar
146
+ raise SignatureException, "Parse error in #{@signature}" if c == nil
147
+ child = parse_one(c)
148
+ res << child
149
+ when ?(
150
+ res = Type.new(STRUCT)
151
+ while (c = nextchar) != nil and c != ?)
152
+ res << parse_one(c)
153
+ end
154
+ raise SignatureException, "Parse error in #{@signature}" if c == nil
155
+ when ?{
156
+ res = Type.new(DICT_ENTRY)
157
+ while (c = nextchar) != nil and c != ?}
158
+ res << parse_one(c)
159
+ end
160
+ raise SignatureException, "Parse error in #{@signature}" if c == nil
161
+ else
162
+ res = Type.new(c)
163
+ end
164
+ res
165
+ end
166
+
167
+ # Parse the entire signature, return a DBus::Type object.
168
+ def parse
169
+ @idx = 0
170
+ ret = Array.new
171
+ while (c = nextchar)
172
+ ret << parse_one(c)
173
+ end
174
+ ret
175
+ end
176
+ end # class Parser
177
+ end # module Type
178
+
179
+ # shortcuts
180
+
181
+ # Parse a String to a DBus::Type::Type
182
+ def type(string_type)
183
+ Type::Parser.new(string_type).parse[0]
184
+ end
185
+ module_function :type
186
+
187
+ # Make an explicit [Type, value] pair
188
+ def variant(string_type, value)
189
+ [type(string_type), value]
190
+ end
191
+ module_function :variant
192
+
193
+ end # module DBus