ruby-dbus 0.16.0 → 0.25.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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/NEWS.md +288 -3
  3. data/README.md +3 -5
  4. data/Rakefile +27 -12
  5. data/VERSION +1 -1
  6. data/doc/Reference.md +109 -10
  7. data/examples/doc/_extract_examples +7 -0
  8. data/examples/no-bus/pulseaudio.rb +50 -0
  9. data/examples/no-introspect/nm-test.rb +2 -0
  10. data/examples/no-introspect/tracker-test.rb +3 -1
  11. data/examples/rhythmbox/playpause.rb +2 -1
  12. data/examples/service/call_service.rb +2 -1
  13. data/examples/service/complex-property.rb +21 -0
  14. data/examples/service/service_newapi.rb +3 -3
  15. data/examples/simple/call_introspect.rb +1 -0
  16. data/examples/simple/get_id.rb +6 -3
  17. data/examples/simple/properties.rb +2 -0
  18. data/examples/utils/listnames.rb +10 -1
  19. data/examples/utils/notify.rb +1 -0
  20. data/lib/dbus/api_options.rb +9 -0
  21. data/lib/dbus/auth.rb +307 -217
  22. data/lib/dbus/bus.rb +155 -588
  23. data/lib/dbus/bus_name.rb +12 -8
  24. data/lib/dbus/connection.rb +363 -0
  25. data/lib/dbus/core_ext/class/attribute.rb +1 -1
  26. data/lib/dbus/data.rb +821 -0
  27. data/lib/dbus/emits_changed_signal.rb +83 -0
  28. data/lib/dbus/error.rb +4 -2
  29. data/lib/dbus/introspect.rb +133 -31
  30. data/lib/dbus/logger.rb +6 -3
  31. data/lib/dbus/main.rb +66 -0
  32. data/lib/dbus/marshall.rb +247 -296
  33. data/lib/dbus/matchrule.rb +16 -16
  34. data/lib/dbus/message.rb +66 -57
  35. data/lib/dbus/message_queue.rb +40 -25
  36. data/lib/dbus/node_tree.rb +105 -0
  37. data/lib/dbus/object.rb +442 -33
  38. data/lib/dbus/object_manager.rb +61 -0
  39. data/lib/dbus/object_path.rb +11 -6
  40. data/lib/dbus/object_server.rb +149 -0
  41. data/lib/dbus/org.freedesktop.DBus.xml +97 -0
  42. data/lib/dbus/platform.rb +26 -0
  43. data/lib/dbus/proxy_object.rb +30 -11
  44. data/lib/dbus/proxy_object_factory.rb +13 -7
  45. data/lib/dbus/proxy_object_interface.rb +63 -30
  46. data/lib/dbus/proxy_service.rb +107 -0
  47. data/lib/dbus/raw_message.rb +91 -0
  48. data/lib/dbus/type.rb +319 -86
  49. data/lib/dbus/xml.rb +40 -19
  50. data/lib/dbus.rb +28 -17
  51. data/ruby-dbus.gemspec +13 -6
  52. data/spec/async_spec.rb +2 -0
  53. data/spec/auth_spec.rb +225 -0
  54. data/spec/binding_spec.rb +2 -0
  55. data/spec/bus_and_xml_backend_spec.rb +5 -1
  56. data/spec/bus_connection_spec.rb +93 -0
  57. data/spec/bus_name_spec.rb +3 -1
  58. data/spec/bus_spec.rb +2 -0
  59. data/spec/byte_array_spec.rb +2 -0
  60. data/spec/client_robustness_spec.rb +4 -2
  61. data/spec/connection_spec.rb +37 -0
  62. data/spec/coverage_helper.rb +39 -0
  63. data/spec/data/marshall.yaml +1667 -0
  64. data/spec/data_spec.rb +673 -0
  65. data/spec/dbus_spec.rb +22 -0
  66. data/spec/emits_changed_signal_spec.rb +58 -0
  67. data/spec/err_msg_spec.rb +2 -0
  68. data/spec/introspect_xml_parser_spec.rb +2 -0
  69. data/spec/introspection_spec.rb +2 -0
  70. data/spec/main_loop_spec.rb +17 -1
  71. data/spec/message_spec.rb +21 -0
  72. data/spec/mock-service/cockpit-dbustests.rb +29 -0
  73. data/spec/mock-service/com.redhat.Cockpit.DBusTests.xml +180 -0
  74. data/spec/mock-service/org.ruby.service.service +4 -0
  75. data/spec/mock-service/org.rubygems.ruby_dbus.DBusTests.service +4 -0
  76. data/spec/{service_newapi.rb → mock-service/spaghetti-monster.rb} +122 -75
  77. data/spec/node_spec.rb +65 -0
  78. data/spec/object_manager_spec.rb +33 -0
  79. data/spec/object_path_spec.rb +3 -0
  80. data/spec/object_server_spec.rb +138 -0
  81. data/spec/object_spec.rb +194 -0
  82. data/spec/packet_marshaller_spec.rb +41 -0
  83. data/spec/packet_unmarshaller_spec.rb +248 -0
  84. data/spec/platform_spec.rb +14 -0
  85. data/spec/property_spec.rb +214 -12
  86. data/spec/proxy_object_interface_spec.rb +35 -0
  87. data/spec/proxy_object_spec.rb +2 -0
  88. data/spec/{bus_driver_spec.rb → proxy_service_spec.rb} +15 -8
  89. data/spec/raw_message_spec.rb +32 -0
  90. data/spec/server_robustness_spec.rb +20 -6
  91. data/spec/session_bus_spec.rb +36 -1
  92. data/spec/session_bus_spec_manual.rb +2 -0
  93. data/spec/signal_spec.rb +16 -3
  94. data/spec/spec_helper.rb +25 -33
  95. data/spec/thread_safety_spec.rb +55 -12
  96. data/spec/tools/dbus-launch-simple +9 -6
  97. data/spec/tools/dbus-limited-session.conf +32 -0
  98. data/spec/tools/test_env +26 -6
  99. data/spec/type_spec.rb +214 -6
  100. data/spec/value_spec.rb +16 -1
  101. data/spec/variant_spec.rb +4 -2
  102. data/spec/zzz_quit_spec.rb +16 -0
  103. metadata +92 -20
  104. data/examples/gdbus/gdbus +0 -257
  105. data/examples/gdbus/gdbus.glade +0 -98
  106. data/examples/gdbus/launch.sh +0 -4
  107. data/spec/server_spec.rb +0 -53
  108. data/spec/tools/test_server +0 -39
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of the ruby-dbus project
4
+ # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
5
+ # Copyright (C) 2023 Martin Vidner
6
+ #
7
+ # This library is free software; you can redistribute it and/or
8
+ # modify it under the terms of the GNU Lesser General Public
9
+ # License, version 2.1 as published by the Free Software Foundation.
10
+ # See the file "COPYING" for the exact licensing terms.
11
+
12
+ require_relative "node_tree"
13
+
14
+ module DBus
15
+ # The part of a {Connection} that can export {DBus::Object}s to provide
16
+ # services to clients.
17
+ #
18
+ # Note that an ObjectServer does not have a name. Typically a {Connection}
19
+ # has one well known name, but can have none or more.
20
+ #
21
+ # Formerly this class was intermixed with {ProxyService} as Service.
22
+ #
23
+ # @example Usage
24
+ # bus = DBus.session_bus
25
+ # obj = DBus::Object.new("/path") # a subclass more likely
26
+ # bus.object_server.export(obj)
27
+ # bus.request_name("org.example.Test")
28
+ class ObjectServer < NodeTree
29
+ # @return [Connection] The connection we're using.
30
+ attr_reader :connection
31
+
32
+ def initialize(connection)
33
+ @connection = connection
34
+ super()
35
+ end
36
+
37
+ # Retrieves an object at the given _path_
38
+ # @param path [ObjectPath]
39
+ # @return [DBus::Object,nil]
40
+ def object(path)
41
+ node = get_node(path, create: false)
42
+ node&.object
43
+ end
44
+ alias [] object
45
+
46
+ # Export an object
47
+ # @param obj [DBus::Object]
48
+ # @raise RuntimeError if there's already an exported object at the same path
49
+ def export(obj)
50
+ node = get_node(obj.path, create: true)
51
+ raise "At #{obj.path} there is already an object #{node.object.inspect}" if node.object
52
+
53
+ node.object = obj
54
+
55
+ obj.object_server = self
56
+ object_manager_for(obj)&.object_added(obj)
57
+ end
58
+
59
+ # Undo exporting an object *obj_or_path*.
60
+ # Raises ArgumentError if it is not a DBus::Object.
61
+ # Returns the object, or false if _obj_ was not exported.
62
+ # @param obj_or_path [DBus::Object,ObjectPath,String] an object or a valid object path
63
+ def unexport(obj_or_path)
64
+ path = self.class.path_of(obj_or_path)
65
+ parent_path, _separator, node_name = path.rpartition("/")
66
+
67
+ parent_node = get_node(parent_path, create: false)
68
+ return false unless parent_node
69
+
70
+ node = if node_name == "" # path == "/"
71
+ parent_node
72
+ else
73
+ parent_node[node_name]
74
+ end
75
+ obj = node&.object
76
+ raise ArgumentError, "Cannot unexport, no object at #{path}" unless obj
77
+
78
+ object_manager_for(obj)&.object_removed(obj)
79
+ obj.object_server = nil
80
+ node.object = nil
81
+
82
+ # node can be deleted if
83
+ # - it has no children
84
+ # - it is not root
85
+ if node.empty? && !node.equal?(parent_node)
86
+ parent_node.delete(node_name)
87
+ end
88
+
89
+ obj
90
+ end
91
+
92
+ # Find the (closest) parent of *object*
93
+ # implementing the ObjectManager interface, or nil
94
+ # @return [DBus::Object,nil]
95
+ def object_manager_for(object)
96
+ path = object.path
97
+ node_chain = get_node_chain(path)
98
+ om_node = node_chain.reverse_each.find do |node|
99
+ node.object&.is_a? DBus::ObjectManager
100
+ end
101
+ om_node&.object
102
+ end
103
+
104
+ # All objects (not paths) under this path (except itself).
105
+ # @param path [ObjectPath]
106
+ # @return [Array<DBus::Object>]
107
+ # @raise ArgumentError if the *path* does not exist
108
+ def descendants_for(path)
109
+ node = get_node(path, create: false)
110
+ raise ArgumentError, "Object path #{path} doesn't exist" if node.nil?
111
+
112
+ node.descendant_objects
113
+ end
114
+
115
+ # @param obj_or_path [DBus::Object,ObjectPath,String] an object or a valid object path
116
+ # @return [ObjectPath]
117
+ # @api private
118
+ def self.path_of(obj_or_path)
119
+ case obj_or_path
120
+ when ObjectPath
121
+ obj_or_path
122
+ when String
123
+ ObjectPath.new(obj_or_path)
124
+ when DBus::Object
125
+ obj_or_path.path
126
+ else
127
+ raise ArgumentError, "Expecting a DBus::Object argument or DBus::ObjectPath or String which parses as one"
128
+ end
129
+ end
130
+
131
+ #########
132
+
133
+ private
134
+
135
+ #########
136
+
137
+ # @param path [ObjectPath] a path that must exist
138
+ # @return [Array<Node>] nodes from the root to the leaf
139
+ def get_node_chain(path)
140
+ n = @root
141
+ result = [n]
142
+ path.sub(%r{^/}, "").split("/").each do |elem|
143
+ n = n[elem]
144
+ result.push(n)
145
+ end
146
+ result
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,97 @@
1
+ <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
2
+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
3
+ <node>
4
+ <interface name="org.freedesktop.DBus.Introspectable">
5
+ <method name="Introspect">
6
+ <arg direction="out" type="s"/>
7
+ </method>
8
+ </interface>
9
+ <interface name="org.freedesktop.DBus">
10
+ <method name="Hello">
11
+ <arg direction="out" type="s"/>
12
+ </method>
13
+ <method name="RequestName">
14
+ <arg direction="in" type="s"/>
15
+ <arg direction="in" type="u"/>
16
+ <arg direction="out" type="u"/>
17
+ </method>
18
+ <method name="ReleaseName">
19
+ <arg direction="in" type="s"/>
20
+ <arg direction="out" type="u"/>
21
+ </method>
22
+ <method name="StartServiceByName">
23
+ <arg direction="in" type="s"/>
24
+ <arg direction="in" type="u"/>
25
+ <arg direction="out" type="u"/>
26
+ </method>
27
+ <method name="UpdateActivationEnvironment">
28
+ <arg direction="in" type="a{ss}"/>
29
+ </method>
30
+ <method name="NameHasOwner">
31
+ <arg direction="in" type="s"/>
32
+ <arg direction="out" type="b"/>
33
+ </method>
34
+ <method name="ListNames">
35
+ <arg direction="out" type="as"/>
36
+ </method>
37
+ <method name="ListActivatableNames">
38
+ <arg direction="out" type="as"/>
39
+ </method>
40
+ <method name="AddMatch">
41
+ <arg direction="in" type="s"/>
42
+ </method>
43
+ <method name="RemoveMatch">
44
+ <arg direction="in" type="s"/>
45
+ </method>
46
+ <method name="GetNameOwner">
47
+ <arg direction="in" type="s"/>
48
+ <arg direction="out" type="s"/>
49
+ </method>
50
+ <method name="ListQueuedOwners">
51
+ <arg direction="in" type="s"/>
52
+ <arg direction="out" type="as"/>
53
+ </method>
54
+ <method name="GetConnectionUnixUser">
55
+ <arg direction="in" type="s"/>
56
+ <arg direction="out" type="u"/>
57
+ </method>
58
+ <method name="GetConnectionUnixProcessID">
59
+ <arg direction="in" type="s"/>
60
+ <arg direction="out" type="u"/>
61
+ </method>
62
+ <method name="GetAdtAuditSessionData">
63
+ <arg direction="in" type="s"/>
64
+ <arg direction="out" type="ay"/>
65
+ </method>
66
+ <method name="GetConnectionSELinuxSecurityContext">
67
+ <arg direction="in" type="s"/>
68
+ <arg direction="out" type="ay"/>
69
+ </method>
70
+ <method name="ReloadConfig">
71
+ </method>
72
+ <method name="GetId">
73
+ <arg direction="out" type="s"/>
74
+ </method>
75
+ <method name="GetConnectionCredentials">
76
+ <arg direction="in" type="s"/>
77
+ <arg direction="out" type="a{sv}"/>
78
+ </method>
79
+ <property name="Features" type="as" access="read">
80
+ <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
81
+ </property>
82
+ <property name="Interfaces" type="as" access="read">
83
+ <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
84
+ </property>
85
+ <signal name="NameOwnerChanged">
86
+ <arg type="s"/>
87
+ <arg type="s"/>
88
+ <arg type="s"/>
89
+ </signal>
90
+ <signal name="NameLost">
91
+ <arg type="s"/>
92
+ </signal>
93
+ <signal name="NameAcquired">
94
+ <arg type="s"/>
95
+ </signal>
96
+ </interface>
97
+ </node>
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of the ruby-dbus project
4
+ # Copyright (C) 2023 Martin Vidner
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 "rbconfig"
12
+
13
+ module DBus
14
+ # Platform detection
15
+ module Platform
16
+ module_function
17
+
18
+ def freebsd?
19
+ RbConfig::CONFIG["target_os"] =~ /freebsd/
20
+ end
21
+
22
+ def macos?
23
+ RbConfig::CONFIG["target_os"] =~ /darwin/
24
+ end
25
+ end
26
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of the ruby-dbus project
2
4
  # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
3
5
  # Copyright (C) 2009-2014 Martin Vidner
@@ -8,20 +10,20 @@
8
10
  # See the file "COPYING" for the exact licensing terms.
9
11
 
10
12
  module DBus
11
- # D-Bus proxy object class
12
- #
13
- # Class representing a remote object in an external application.
13
+ # Represents a remote object in an external application.
14
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
15
+ # over the bus so that the method is executed remotely on the corresponding
16
16
  # object.
17
17
  class ProxyObject
18
18
  # The names of direct subnodes of the object in the tree.
19
19
  attr_accessor :subnodes
20
20
  # Flag determining whether the object has been introspected.
21
+ # @return [Boolean]
21
22
  attr_accessor :introspected
22
23
  # The (remote) destination of the object.
23
24
  attr_reader :destination
24
25
  # The path to the object.
26
+ # @return [ObjectPath]
25
27
  attr_reader :path
26
28
  # The bus the object is reachable via.
27
29
  attr_reader :bus
@@ -31,6 +33,8 @@ module DBus
31
33
  # @return [ApiOptions]
32
34
  attr_reader :api
33
35
 
36
+ OPEN_QUOTE = RUBY_VERSION >= "3.4" ? "'" : "`"
37
+
34
38
  # Creates a new proxy object living on the given _bus_ at destination _dest_
35
39
  # on the given _path_.
36
40
  def initialize(bus, dest, path, api: ApiOptions::CURRENT)
@@ -44,6 +48,7 @@ module DBus
44
48
  end
45
49
 
46
50
  # Returns the interfaces of the object.
51
+ # @return [Array<String>] names of the interfaces
47
52
  def interfaces
48
53
  introspect unless introspected
49
54
  @interfaces.keys
@@ -55,7 +60,8 @@ module DBus
55
60
  def [](intfname)
56
61
  introspect unless introspected
57
62
  ifc = @interfaces[intfname]
58
- raise DBus::Error, "no such interface `#{intfname}' on object `#{@path}'" unless ifc
63
+ raise DBus::Error, "no such interface #{OPEN_QUOTE}#{intfname}' on object #{OPEN_QUOTE}#{@path}'" unless ifc
64
+
59
65
  ifc
60
66
  end
61
67
 
@@ -92,6 +98,7 @@ module DBus
92
98
  # don't overwrite instance methods!
93
99
  next if dup_meths.include?(name)
94
100
  next if self.class.instance_methods.include?(name)
101
+
95
102
  if univocal_meths.include? name
96
103
  univocal_meths.delete name
97
104
  dup_meths << name
@@ -121,8 +128,11 @@ module DBus
121
128
  # It uses _default_iface_ which must have been set.
122
129
  # @return [void]
123
130
  def on_signal(name, &block)
124
- # TODO: improve
125
- raise NoMethodError unless @default_iface && has_iface?(@default_iface)
131
+ unless @default_iface && has_iface?(@default_iface)
132
+ raise NoMethodError, "undefined signal #{OPEN_QUOTE}#{name}' for DBus interface " \
133
+ "#{OPEN_QUOTE}#{@default_iface}' on object #{OPEN_QUOTE}#{@path}'"
134
+ end
135
+
126
136
  @interfaces[@default_iface].on_signal(name, &block)
127
137
  end
128
138
 
@@ -137,7 +147,8 @@ module DBus
137
147
  # - di not specified
138
148
  # TODO
139
149
  # - di is specified but not found in introspection data
140
- raise NoMethodError, "undefined method `#{name}' for DBus interface `#{@default_iface}' on object `#{@path}'"
150
+ raise NoMethodError, "undefined method #{OPEN_QUOTE}#{name}' for DBus interface " \
151
+ "#{OPEN_QUOTE}#{@default_iface}' on object #{OPEN_QUOTE}#{@path}'"
141
152
  end
142
153
 
143
154
  begin
@@ -145,10 +156,18 @@ module DBus
145
156
  rescue NameError => e
146
157
  # interesting, foo.method("unknown")
147
158
  # raises NameError, not NoMethodError
148
- raise unless e.to_s =~ /undefined method `#{name}'/
159
+ raise unless e.to_s =~ /undefined method #{OPEN_QUOTE}#{name}'/
160
+
149
161
  # BTW e.exception("...") would preserve the class.
150
- raise NoMethodError, "undefined method `#{name}' for DBus interface `#{@default_iface}' on object `#{@path}'"
162
+ raise NoMethodError, "undefined method #{OPEN_QUOTE}#{name}' for DBus interface " \
163
+ "#{OPEN_QUOTE}#{@default_iface}' on object #{OPEN_QUOTE}#{@path}'"
151
164
  end
152
165
  end
153
- end # class ProxyObject
166
+
167
+ def respond_to_missing?(name, _include_private = false)
168
+ (@default_iface &&
169
+ has_iface?(@default_iface) &&
170
+ @interfaces[@default_iface].methods.key?(name)) or super
171
+ end
172
+ end
154
173
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of the ruby-dbus project
2
4
  # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
3
5
  # Copyright (C) 2009-2014 Martin Vidner
@@ -22,17 +24,21 @@ module DBus
22
24
  @api = api
23
25
  end
24
26
 
25
- # Investigates the sub-nodes of the proxy object _po_ based on the
27
+ # Investigates the sub-nodes of the proxy object _pobj_ based on the
26
28
  # introspection XML data _xml_ and sets them up recursively.
27
- def self.introspect_into(po, xml)
28
- intfs, po.subnodes = IntrospectXMLParser.new(xml).parse
29
+ # @param pobj [ProxyObject]
30
+ # @param xml [String]
31
+ def self.introspect_into(pobj, xml)
32
+ # intfs [Array<Interface>], subnodes [Array<String>]
33
+ intfs, pobj.subnodes = IntrospectXMLParser.new(xml).parse
29
34
  intfs.each do |i|
30
- poi = ProxyObjectInterface.new(po, i.name)
35
+ poi = ProxyObjectInterface.new(pobj, i.name)
31
36
  i.methods.each_value { |m| poi.define(m) }
32
37
  i.signals.each_value { |s| poi.define(s) }
33
- po[i.name] = poi
38
+ i.properties.each_value { |p| poi.define(p) }
39
+ pobj[i.name] = poi
34
40
  end
35
- po.introspected = true
41
+ pobj.introspected = true
36
42
  end
37
43
 
38
44
  # Generates, sets up and returns the proxy object.
@@ -41,5 +47,5 @@ module DBus
41
47
  ProxyObjectFactory.introspect_into(po, @xml)
42
48
  po
43
49
  end
44
- end # class ProxyObjectFactory
50
+ end
45
51
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of the ruby-dbus project
2
4
  # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
3
5
  # Copyright (C) 2009-2014 Martin Vidner
@@ -13,13 +15,16 @@ module DBus
13
15
  # A class similar to the normal Interface used as a proxy for remote
14
16
  # object interfaces.
15
17
  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.
18
+ # @return [Hash{String => DBus::Method}]
19
+ attr_reader :methods
20
+ # @return [Hash{String => Signal}]
21
+ attr_reader :signals
22
+ # @return [Hash{Symbol => Property}]
23
+ attr_reader :properties
24
+
25
+ # @return [ProxyObject] The proxy object to which this interface belongs.
21
26
  attr_reader :object
22
- # The name of the interface.
27
+ # @return [String] The name of the interface.
23
28
  attr_reader :name
24
29
 
25
30
  # Creates a new proxy interface for the given proxy _object_
@@ -29,6 +34,7 @@ module DBus
29
34
  @name = name
30
35
  @methods = {}
31
36
  @signals = {}
37
+ @properties = {}
32
38
  end
33
39
 
34
40
  # Returns the string representation of the interface (the name).
@@ -36,27 +42,28 @@ module DBus
36
42
  @name
37
43
  end
38
44
 
39
- # Defines a method on the interface from the Method descriptor _m_.
40
- def define_method_from_descriptor(m)
41
- m.params.each do |fpar|
45
+ # Defines a method on the interface from the Method descriptor _method_.
46
+ # @param method [Method]
47
+ def define_method_from_descriptor(method)
48
+ method.params.each do |fpar|
42
49
  par = fpar.type
43
50
  # This is the signature validity check
44
51
  Type::Parser.new(par).parse
45
52
  end
46
53
 
47
54
  singleton_class.class_eval do
48
- define_method m.name do |*args, &reply_handler|
49
- if m.params.size != args.size
50
- raise ArgumentError, "wrong number of arguments (#{args.size} for #{m.params.size})"
55
+ define_method method.name do |*args, &reply_handler|
56
+ if method.params.size != args.size
57
+ raise ArgumentError, "wrong number of arguments (#{args.size} for #{method.params.size})"
51
58
  end
52
59
 
53
60
  msg = Message.new(Message::METHOD_CALL)
54
61
  msg.path = @object.path
55
62
  msg.interface = @name
56
63
  msg.destination = @object.destination
57
- msg.member = m.name
64
+ msg.member = method.name
58
65
  msg.sender = @object.bus.unique_name
59
- m.params.each do |fpar|
66
+ method.params.each do |fpar|
60
67
  par = fpar.type
61
68
  msg.add_param(par, args.shift)
62
69
  end
@@ -64,25 +71,35 @@ module DBus
64
71
  if ret.nil? || @object.api.proxy_method_returns_array
65
72
  ret
66
73
  else
67
- m.rets.size == 1 ? ret.first : ret
74
+ method.rets.size == 1 ? ret.first : ret
68
75
  end
69
76
  end
70
77
  end
71
78
 
72
- @methods[m.name] = m
79
+ @methods[method.name] = method
73
80
  end
74
81
 
75
- # Defines a signal from the descriptor _s_.
76
- def define_signal_from_descriptor(s)
77
- @signals[s.name] = s
82
+ # Defines a signal from the descriptor _sig_.
83
+ # @param sig [Signal]
84
+ def define_signal_from_descriptor(sig)
85
+ @signals[sig.name] = sig
78
86
  end
79
87
 
80
- # Defines a signal or method based on the descriptor _m_.
81
- def define(m)
82
- if m.is_a?(Method)
83
- define_method_from_descriptor(m)
84
- elsif m.is_a?(Signal)
85
- define_signal_from_descriptor(m)
88
+ # @param prop [Property]
89
+ def define_property_from_descriptor(prop)
90
+ @properties[prop.name] = prop
91
+ end
92
+
93
+ # Defines a signal or method based on the descriptor _ifc_el_.
94
+ # @param ifc_el [DBus::Method,Signal,Property]
95
+ def define(ifc_el)
96
+ case ifc_el
97
+ when Method
98
+ define_method_from_descriptor(ifc_el)
99
+ when Signal
100
+ define_signal_from_descriptor(ifc_el)
101
+ when Property
102
+ define_property_from_descriptor(ifc_el)
86
103
  end
87
104
  end
88
105
 
@@ -109,7 +126,7 @@ module DBus
109
126
  end
110
127
  end
111
128
 
112
- PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties".freeze
129
+ PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"
113
130
 
114
131
  # Read a property.
115
132
  # @param propname [String]
@@ -124,10 +141,26 @@ module DBus
124
141
  end
125
142
 
126
143
  # Write a property.
127
- # @param propname [String]
144
+ # @param property_name [String]
128
145
  # @param value [Object]
129
- def []=(propname, value)
130
- object[PROPERTY_INTERFACE].Set(name, propname, value)
146
+ def []=(property_name, value)
147
+ property = properties[property_name.to_sym]
148
+ if !property
149
+ raise DBus.error("org.freedesktop.DBus.Error.UnknownProperty"),
150
+ "Property '#{name}.#{property_name}' (on object '#{object.path}') not found"
151
+ end
152
+
153
+ case value
154
+ # accommodate former need to explicitly make a variant with the right type
155
+ when Data::Variant
156
+ variant = value
157
+ else
158
+ type = property.type
159
+ typed_value = Data.make_typed(type, value)
160
+ variant = Data::Variant.new(typed_value, member_type: type)
161
+ end
162
+
163
+ object[PROPERTY_INTERFACE].Set(name, property_name, variant)
131
164
  end
132
165
 
133
166
  # Read all properties at once, as a hash.
@@ -141,5 +174,5 @@ module DBus
141
174
  ret
142
175
  end
143
176
  end
144
- end # class ProxyObjectInterface
177
+ end
145
178
  end