ruby-dbus 0.16.0 → 0.18.0.beta2
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.
- checksums.yaml +4 -4
- data/NEWS.md +46 -0
- data/README.md +3 -5
- data/Rakefile +18 -8
- data/VERSION +1 -1
- data/doc/Reference.md +94 -4
- data/examples/doc/_extract_examples +7 -0
- data/examples/gdbus/gdbus +31 -24
- data/examples/no-introspect/nm-test.rb +2 -0
- data/examples/no-introspect/tracker-test.rb +3 -1
- data/examples/rhythmbox/playpause.rb +2 -1
- data/examples/service/call_service.rb +2 -1
- data/examples/service/complex-property.rb +21 -0
- data/examples/service/service_newapi.rb +1 -1
- data/examples/simple/call_introspect.rb +1 -0
- data/examples/simple/get_id.rb +2 -1
- data/examples/simple/properties.rb +2 -0
- data/examples/utils/listnames.rb +1 -0
- data/examples/utils/notify.rb +1 -0
- data/lib/dbus/api_options.rb +9 -0
- data/lib/dbus/auth.rb +20 -15
- data/lib/dbus/bus.rb +126 -74
- data/lib/dbus/bus_name.rb +12 -8
- data/lib/dbus/core_ext/class/attribute.rb +1 -1
- data/lib/dbus/data.rb +725 -0
- data/lib/dbus/error.rb +4 -2
- data/lib/dbus/introspect.rb +91 -30
- data/lib/dbus/logger.rb +3 -1
- data/lib/dbus/marshall.rb +228 -294
- data/lib/dbus/matchrule.rb +16 -16
- data/lib/dbus/message.rb +44 -37
- data/lib/dbus/message_queue.rb +16 -10
- data/lib/dbus/object.rb +296 -24
- data/lib/dbus/object_path.rb +11 -6
- data/lib/dbus/proxy_object.rb +22 -1
- data/lib/dbus/proxy_object_factory.rb +11 -7
- data/lib/dbus/proxy_object_interface.rb +26 -21
- data/lib/dbus/raw_message.rb +91 -0
- data/lib/dbus/type.rb +182 -80
- data/lib/dbus/xml.rb +28 -17
- data/lib/dbus.rb +13 -7
- data/ruby-dbus.gemspec +7 -3
- data/spec/async_spec.rb +2 -0
- data/spec/binding_spec.rb +2 -0
- data/spec/bus_and_xml_backend_spec.rb +2 -0
- data/spec/bus_driver_spec.rb +2 -0
- data/spec/bus_name_spec.rb +3 -1
- data/spec/bus_spec.rb +2 -0
- data/spec/byte_array_spec.rb +2 -0
- data/spec/client_robustness_spec.rb +4 -2
- data/spec/data/marshall.yaml +1639 -0
- data/spec/data_spec.rb +298 -0
- data/spec/err_msg_spec.rb +2 -0
- data/spec/introspect_xml_parser_spec.rb +2 -0
- data/spec/introspection_spec.rb +2 -0
- data/spec/main_loop_spec.rb +3 -1
- data/spec/node_spec.rb +23 -0
- data/spec/object_path_spec.rb +3 -0
- data/spec/packet_marshaller_spec.rb +34 -0
- data/spec/packet_unmarshaller_spec.rb +262 -0
- data/spec/property_spec.rb +88 -5
- data/spec/proxy_object_spec.rb +2 -0
- data/spec/server_robustness_spec.rb +2 -0
- data/spec/server_spec.rb +2 -0
- data/spec/service_newapi.rb +39 -70
- data/spec/session_bus_spec.rb +3 -1
- data/spec/session_bus_spec_manual.rb +2 -0
- data/spec/signal_spec.rb +5 -3
- data/spec/spec_helper.rb +35 -9
- data/spec/thread_safety_spec.rb +2 -0
- data/spec/tools/dbus-limited-session.conf +4 -0
- data/spec/type_spec.rb +69 -6
- data/spec/value_spec.rb +16 -1
- data/spec/variant_spec.rb +4 -2
- metadata +32 -10
data/lib/dbus/object.rb
CHANGED
@@ -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
|
#
|
@@ -6,10 +8,11 @@
|
|
6
8
|
# License, version 2.1 as published by the Free Software Foundation.
|
7
9
|
# See the file "COPYING" for the exact licensing terms.
|
8
10
|
|
9
|
-
|
10
|
-
require "dbus/core_ext/class/attribute"
|
11
|
+
require_relative "core_ext/class/attribute"
|
11
12
|
|
12
13
|
module DBus
|
14
|
+
PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"
|
15
|
+
|
13
16
|
# Exported object type
|
14
17
|
# = Exportable D-Bus object class
|
15
18
|
#
|
@@ -18,6 +21,7 @@ module DBus
|
|
18
21
|
class Object
|
19
22
|
# The path of the object.
|
20
23
|
attr_reader :path
|
24
|
+
|
21
25
|
# The interfaces that the object supports. Hash: String => Interface
|
22
26
|
my_class_attribute :intfs
|
23
27
|
self.intfs = {}
|
@@ -41,11 +45,13 @@ module DBus
|
|
41
45
|
when Message::METHOD_CALL
|
42
46
|
reply = nil
|
43
47
|
begin
|
44
|
-
|
48
|
+
iface = intfs[msg.interface]
|
49
|
+
if !iface
|
45
50
|
raise DBus.error("org.freedesktop.DBus.Error.UnknownMethod"),
|
46
51
|
"Interface \"#{msg.interface}\" of object \"#{msg.path}\" doesn't exist"
|
47
52
|
end
|
48
|
-
|
53
|
+
member_sym = msg.member.to_sym
|
54
|
+
meth = iface.methods[member_sym]
|
49
55
|
if !meth
|
50
56
|
raise DBus.error("org.freedesktop.DBus.Error.UnknownMethod"),
|
51
57
|
"Method \"#{msg.member}\" on interface \"#{msg.interface}\" of object \"#{msg.path}\" doesn't exist"
|
@@ -55,11 +61,12 @@ module DBus
|
|
55
61
|
retdata = [*retdata]
|
56
62
|
|
57
63
|
reply = Message.method_return(msg)
|
58
|
-
meth.rets.
|
59
|
-
|
64
|
+
rsigs = meth.rets.map(&:type)
|
65
|
+
rsigs.zip(retdata).each do |rsig, rdata|
|
66
|
+
reply.add_param(rsig, rdata)
|
60
67
|
end
|
61
|
-
rescue =>
|
62
|
-
dbus_msg_exc = msg.annotate_exception(
|
68
|
+
rescue StandardError => e
|
69
|
+
dbus_msg_exc = msg.annotate_exception(e)
|
63
70
|
reply = ErrorMessage.from_exception(dbus_msg_exc).reply_to(msg)
|
64
71
|
end
|
65
72
|
@service.bus.message_queue.push(reply)
|
@@ -68,62 +75,327 @@ module DBus
|
|
68
75
|
|
69
76
|
# Select (and create) the interface that the following defined methods
|
70
77
|
# belong to.
|
71
|
-
|
78
|
+
# @param name [String] interface name like "org.example.ManagerManager"
|
79
|
+
# @see https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names-interface
|
80
|
+
def self.dbus_interface(name)
|
72
81
|
@@intfs_mutex.synchronize do
|
73
|
-
@@cur_intf = intfs[
|
82
|
+
@@cur_intf = intfs[name]
|
74
83
|
if !@@cur_intf
|
75
|
-
@@cur_intf = Interface.new(
|
84
|
+
@@cur_intf = Interface.new(name) # validates the name
|
76
85
|
# As this is a mutable class_attr, we cannot use
|
77
|
-
# self.intfs[
|
86
|
+
# self.intfs[name] = @@cur_intf # Hash#[]=
|
78
87
|
# as that would modify parent class attr in place.
|
79
88
|
# Using the setter lets a subclass have the new value
|
80
89
|
# while the superclass keeps the old one.
|
81
|
-
self.intfs = intfs.merge(
|
90
|
+
self.intfs = intfs.merge(name => @@cur_intf)
|
82
91
|
end
|
83
92
|
yield
|
84
93
|
@@cur_intf = nil
|
85
94
|
end
|
86
95
|
end
|
87
96
|
|
88
|
-
#
|
89
|
-
|
97
|
+
# Forgetting to declare the interface for a method/signal/property
|
98
|
+
# is a ScriptError.
|
99
|
+
class UndefinedInterface < ScriptError # rubocop:disable Lint/InheritException
|
90
100
|
def initialize(sym)
|
91
|
-
super "No interface specified for #{sym}"
|
101
|
+
super "No interface specified for #{sym}. Enclose it in dbus_interface."
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# A read-write property accessing an instance variable.
|
106
|
+
# A combination of `attr_accessor` and {.dbus_accessor}.
|
107
|
+
#
|
108
|
+
# PropertiesChanged signal will be emitted whenever `foo_bar=` is used
|
109
|
+
# but not when @foo_bar is written directly.
|
110
|
+
#
|
111
|
+
# @param ruby_name [Symbol] :foo_bar is exposed as FooBar;
|
112
|
+
# use dbus_name to override
|
113
|
+
# @param type a signature like "s" or "a(uus)" or Type::STRING
|
114
|
+
# @param dbus_name [String] if not given it is made
|
115
|
+
# by CamelCasing the ruby_name. foo_bar becomes FooBar
|
116
|
+
# to convert the Ruby convention to the DBus convention.
|
117
|
+
# @return [void]
|
118
|
+
def self.dbus_attr_accessor(ruby_name, type, dbus_name: nil)
|
119
|
+
attr_accessor(ruby_name)
|
120
|
+
|
121
|
+
dbus_accessor(ruby_name, type, dbus_name: dbus_name)
|
122
|
+
end
|
123
|
+
|
124
|
+
# A read-only property accessing an instance variable.
|
125
|
+
# A combination of `attr_reader` and {.dbus_reader}.
|
126
|
+
#
|
127
|
+
# Whenever the property value gets changed from "inside" the object,
|
128
|
+
# you should emit the `PropertiesChanged` signal by calling
|
129
|
+
#
|
130
|
+
# object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {dbus_name.to_s => value}, [])
|
131
|
+
#
|
132
|
+
# or, omitting the value in the signal,
|
133
|
+
#
|
134
|
+
# object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {}, [dbus_name.to_s])
|
135
|
+
#
|
136
|
+
# @param (see .dbus_attr_accessor)
|
137
|
+
# @return (see .dbus_attr_accessor)
|
138
|
+
def self.dbus_attr_reader(ruby_name, type, dbus_name: nil)
|
139
|
+
attr_reader(ruby_name)
|
140
|
+
|
141
|
+
dbus_reader(ruby_name, type, dbus_name: dbus_name)
|
142
|
+
end
|
143
|
+
|
144
|
+
# A write-only property accessing an instance variable.
|
145
|
+
# A combination of `attr_writer` and {.dbus_writer}.
|
146
|
+
#
|
147
|
+
# @param (see .dbus_attr_accessor)
|
148
|
+
# @return (see .dbus_attr_accessor)
|
149
|
+
def self.dbus_attr_writer(ruby_name, type, dbus_name: nil)
|
150
|
+
attr_writer(ruby_name)
|
151
|
+
|
152
|
+
dbus_writer(ruby_name, type, dbus_name: dbus_name)
|
153
|
+
end
|
154
|
+
|
155
|
+
# A read-write property using a pair of reader/writer methods
|
156
|
+
# (which must already exist).
|
157
|
+
# (To directly access an instance variable, use {.dbus_attr_accessor} instead)
|
158
|
+
#
|
159
|
+
# Uses {.dbus_watcher} to set up the PropertiesChanged signal.
|
160
|
+
#
|
161
|
+
# @param (see .dbus_attr_accessor)
|
162
|
+
# @return (see .dbus_attr_accessor)
|
163
|
+
def self.dbus_accessor(ruby_name, type, dbus_name: nil)
|
164
|
+
raise UndefinedInterface, ruby_name if @@cur_intf.nil?
|
165
|
+
|
166
|
+
dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
|
167
|
+
property = Property.new(dbus_name, type, :readwrite, ruby_name: ruby_name)
|
168
|
+
@@cur_intf.define(property)
|
169
|
+
|
170
|
+
dbus_watcher(ruby_name, dbus_name: dbus_name)
|
171
|
+
end
|
172
|
+
|
173
|
+
# A read-only property accessing a reader method (which must already exist).
|
174
|
+
# (To directly access an instance variable, use {.dbus_attr_reader} instead)
|
175
|
+
#
|
176
|
+
# Whenever the property value gets changed from "inside" the object,
|
177
|
+
# you should emit the `PropertiesChanged` signal by calling
|
178
|
+
#
|
179
|
+
# object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {dbus_name.to_s => value}, [])
|
180
|
+
#
|
181
|
+
# or, omitting the value in the signal,
|
182
|
+
#
|
183
|
+
# object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {}, [dbus_name.to_s])
|
184
|
+
#
|
185
|
+
# @param (see .dbus_attr_accessor)
|
186
|
+
# @return (see .dbus_attr_accessor)
|
187
|
+
def self.dbus_reader(ruby_name, type, dbus_name: nil)
|
188
|
+
raise UndefinedInterface, ruby_name if @@cur_intf.nil?
|
189
|
+
|
190
|
+
dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
|
191
|
+
property = Property.new(dbus_name, type, :read, ruby_name: ruby_name)
|
192
|
+
@@cur_intf.define(property)
|
193
|
+
end
|
194
|
+
|
195
|
+
# A write-only property accessing a writer method (which must already exist).
|
196
|
+
# (To directly access an instance variable, use {.dbus_attr_writer} instead)
|
197
|
+
#
|
198
|
+
# Uses {.dbus_watcher} to set up the PropertiesChanged signal.
|
199
|
+
#
|
200
|
+
# @param (see .dbus_attr_accessor)
|
201
|
+
# @return (see .dbus_attr_accessor)
|
202
|
+
def self.dbus_writer(ruby_name, type, dbus_name: nil)
|
203
|
+
raise UndefinedInterface, ruby_name if @@cur_intf.nil?
|
204
|
+
|
205
|
+
dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
|
206
|
+
property = Property.new(dbus_name, type, :write, ruby_name: ruby_name)
|
207
|
+
@@cur_intf.define(property)
|
208
|
+
|
209
|
+
dbus_watcher(ruby_name, dbus_name: dbus_name)
|
210
|
+
end
|
211
|
+
|
212
|
+
# Enables automatic sending of the PropertiesChanged signal.
|
213
|
+
# For *ruby_name* `foo_bar`, wrap `foo_bar=` so that it sends
|
214
|
+
# the signal for FooBar.
|
215
|
+
# The original version remains as `_original_foo_bar=`.
|
216
|
+
#
|
217
|
+
# @param ruby_name [Symbol] :foo_bar and :foo_bar= both mean the same thing
|
218
|
+
# @param dbus_name [String] if not given it is made
|
219
|
+
# by CamelCasing the ruby_name. foo_bar becomes FooBar
|
220
|
+
# to convert the Ruby convention to the DBus convention.
|
221
|
+
# @return [void]
|
222
|
+
def self.dbus_watcher(ruby_name, dbus_name: nil)
|
223
|
+
raise UndefinedInterface, ruby_name if @@cur_intf.nil?
|
224
|
+
|
225
|
+
cur_intf = @@cur_intf
|
226
|
+
|
227
|
+
ruby_name = ruby_name.to_s.sub(/=$/, "").to_sym
|
228
|
+
ruby_name_eq = "#{ruby_name}=".to_sym
|
229
|
+
original_ruby_name_eq = "_original_#{ruby_name_eq}"
|
230
|
+
|
231
|
+
dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
|
232
|
+
|
233
|
+
# the argument order is alias_method(new_name, existing_name)
|
234
|
+
alias_method original_ruby_name_eq, ruby_name_eq
|
235
|
+
define_method ruby_name_eq do |value|
|
236
|
+
public_send(original_ruby_name_eq, value)
|
237
|
+
|
238
|
+
# TODO: respect EmitsChangedSignal to use invalidated_properties instead
|
239
|
+
# PropertiesChanged, "interface:s, changed_properties:a{sv}, invalidated_properties:as"
|
240
|
+
PropertiesChanged(cur_intf.name, { dbus_name.to_s => value }, [])
|
92
241
|
end
|
93
242
|
end
|
94
243
|
|
95
244
|
# Defines an exportable method on the object with the given name _sym_,
|
96
245
|
# _prototype_ and the code in a block.
|
97
|
-
|
246
|
+
# @param prototype [Prototype]
|
247
|
+
def self.dbus_method(sym, prototype = "", &block)
|
98
248
|
raise UndefinedInterface, sym if @@cur_intf.nil?
|
99
|
-
|
100
|
-
|
249
|
+
|
250
|
+
@@cur_intf.define(Method.new(sym.to_s).from_prototype(prototype))
|
251
|
+
|
252
|
+
ruby_name = Object.make_method_name(@@cur_intf.name, sym.to_s)
|
253
|
+
# ::Module#define_method(name) { body }
|
254
|
+
define_method(ruby_name, &block)
|
101
255
|
end
|
102
256
|
|
103
257
|
# Emits a signal from the object with the given _interface_, signal
|
104
258
|
# _sig_ and arguments _args_.
|
259
|
+
# @param intf [Interface]
|
260
|
+
# @param sig [Signal]
|
261
|
+
# @param args arguments for the signal
|
105
262
|
def emit(intf, sig, *args)
|
106
263
|
@service.bus.emit(@service, self, intf, sig, *args)
|
107
264
|
end
|
108
265
|
|
109
266
|
# Defines a signal for the object with a given name _sym_ and _prototype_.
|
110
|
-
def self.dbus_signal(sym,
|
267
|
+
def self.dbus_signal(sym, prototype = "")
|
111
268
|
raise UndefinedInterface, sym if @@cur_intf.nil?
|
269
|
+
|
112
270
|
cur_intf = @@cur_intf
|
113
|
-
signal = Signal.new(sym.to_s).from_prototype(
|
114
|
-
cur_intf.define(Signal.new(sym.to_s).from_prototype(
|
271
|
+
signal = Signal.new(sym.to_s).from_prototype(prototype)
|
272
|
+
cur_intf.define(Signal.new(sym.to_s).from_prototype(prototype))
|
273
|
+
|
274
|
+
# ::Module#define_method(name) { body }
|
115
275
|
define_method(sym.to_s) do |*args|
|
116
276
|
emit(cur_intf, signal, *args)
|
117
277
|
end
|
118
278
|
end
|
119
279
|
|
120
|
-
####################################################################
|
121
|
-
|
122
280
|
# Helper method that returns a method name generated from the interface
|
123
281
|
# name _intfname_ and method name _methname_.
|
124
282
|
# @api private
|
125
283
|
def self.make_method_name(intfname, methname)
|
126
284
|
"#{intfname}%%#{methname}"
|
127
285
|
end
|
286
|
+
|
287
|
+
# TODO: borrow a proven implementation
|
288
|
+
# @param str [String]
|
289
|
+
# @return [String]
|
290
|
+
# @api private
|
291
|
+
def self.camelize(str)
|
292
|
+
str.split(/_/).map(&:capitalize).join("")
|
293
|
+
end
|
294
|
+
|
295
|
+
# Make a D-Bus conventional name, CamelCased.
|
296
|
+
# @param ruby_name [String,Symbol] eg :do_something
|
297
|
+
# @param dbus_name [String,Symbol,nil] use this if given
|
298
|
+
# @return [Symbol] eg DoSomething
|
299
|
+
def self.make_dbus_name(ruby_name, dbus_name: nil)
|
300
|
+
dbus_name ||= camelize(ruby_name.to_s)
|
301
|
+
dbus_name.to_sym
|
302
|
+
end
|
303
|
+
|
304
|
+
# @param interface_name [String]
|
305
|
+
# @param property_name [String]
|
306
|
+
# @return [Property]
|
307
|
+
# @raise [DBus::Error]
|
308
|
+
# @api private
|
309
|
+
def dbus_lookup_property(interface_name, property_name)
|
310
|
+
# what should happen for unknown properties
|
311
|
+
# plasma: InvalidArgs (propname), UnknownInterface (interface)
|
312
|
+
# systemd: UnknownProperty
|
313
|
+
interface = intfs[interface_name]
|
314
|
+
if !interface
|
315
|
+
raise DBus.error("org.freedesktop.DBus.Error.UnknownProperty"),
|
316
|
+
"Property '#{interface_name}.#{property_name}' (on object '#{@path}') not found: no such interface"
|
317
|
+
end
|
318
|
+
|
319
|
+
property = interface.properties[property_name.to_sym]
|
320
|
+
if !property
|
321
|
+
raise DBus.error("org.freedesktop.DBus.Error.UnknownProperty"),
|
322
|
+
"Property '#{interface_name}.#{property_name}' (on object '#{@path}') not found"
|
323
|
+
end
|
324
|
+
|
325
|
+
property
|
326
|
+
end
|
327
|
+
|
328
|
+
####################################################################
|
329
|
+
|
330
|
+
# use the above defined methods to declare the property-handling
|
331
|
+
# interfaces and methods
|
332
|
+
|
333
|
+
dbus_interface PROPERTY_INTERFACE do
|
334
|
+
dbus_method :Get, "in interface_name:s, in property_name:s, out value:v" do |interface_name, property_name|
|
335
|
+
property = dbus_lookup_property(interface_name, property_name)
|
336
|
+
|
337
|
+
if property.readable?
|
338
|
+
ruby_name = property.ruby_name
|
339
|
+
value = public_send(ruby_name)
|
340
|
+
# may raise, DBus.error or https://ruby-doc.com/core-3.1.0/TypeError.html
|
341
|
+
typed_value = Data.make_typed(property.type, value)
|
342
|
+
[typed_value]
|
343
|
+
else
|
344
|
+
raise DBus.error("org.freedesktop.DBus.Error.PropertyWriteOnly"),
|
345
|
+
"Property '#{interface_name}.#{property_name}' (on object '#{@path}') is not readable"
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
dbus_method :Set, "in interface_name:s, in property_name:s, in val:v" do |interface_name, property_name, value|
|
350
|
+
property = dbus_lookup_property(interface_name, property_name)
|
351
|
+
|
352
|
+
if property.writable?
|
353
|
+
ruby_name_eq = "#{property.ruby_name}="
|
354
|
+
# TODO: declare dbus_method :Set to take :exact argument
|
355
|
+
# and type check it here before passing its :plain value
|
356
|
+
# to the implementation
|
357
|
+
public_send(ruby_name_eq, value)
|
358
|
+
else
|
359
|
+
raise DBus.error("org.freedesktop.DBus.Error.PropertyReadOnly"),
|
360
|
+
"Property '#{interface_name}.#{property_name}' (on object '#{@path}') is not writable"
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
dbus_method :GetAll, "in interface_name:s, out value:a{sv}" do |interface_name|
|
365
|
+
interface = intfs[interface_name]
|
366
|
+
if !interface
|
367
|
+
raise DBus.error("org.freedesktop.DBus.Error.UnknownProperty"),
|
368
|
+
"Properties '#{interface_name}.*' (on object '#{@path}') not found: no such interface"
|
369
|
+
end
|
370
|
+
|
371
|
+
p_hash = {}
|
372
|
+
interface.properties.each do |p_name, property|
|
373
|
+
next unless property.readable?
|
374
|
+
|
375
|
+
ruby_name = property.ruby_name
|
376
|
+
begin
|
377
|
+
# D-Bus spec says:
|
378
|
+
# > If GetAll is called with a valid interface name for which some
|
379
|
+
# > properties are not accessible to the caller (for example, due
|
380
|
+
# > to per-property access control implemented in the service),
|
381
|
+
# > those properties should be silently omitted from the result
|
382
|
+
# > array.
|
383
|
+
# so we will silently omit properties that fail to read.
|
384
|
+
# Get'ting them individually will send DBus.Error
|
385
|
+
value = public_send(ruby_name)
|
386
|
+
# may raise, DBus.error or https://ruby-doc.com/core-3.1.0/TypeError.html
|
387
|
+
typed_value = Data.make_typed(property.type, value)
|
388
|
+
p_hash[p_name.to_s] = typed_value
|
389
|
+
rescue StandardError
|
390
|
+
DBus.logger.debug "Property '#{interface_name}.#{p_name}' (on object '#{@path}')" \
|
391
|
+
" has raised during GetAll, omitting it"
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
[p_hash]
|
396
|
+
end
|
397
|
+
|
398
|
+
dbus_signal :PropertiesChanged, "interface:s, changed_properties:a{sv}, invalidated_properties:as"
|
399
|
+
end
|
128
400
|
end
|
129
401
|
end
|
data/lib/dbus/object_path.rb
CHANGED
@@ -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) 2019 Martin Vidner
|
3
5
|
#
|
@@ -7,18 +9,21 @@
|
|
7
9
|
# See the file "COPYING" for the exact licensing terms.
|
8
10
|
|
9
11
|
module DBus
|
10
|
-
# A {::String} that validates at initialization time
|
12
|
+
# A {::String} that validates at initialization time.
|
13
|
+
# See also {DBus::Data::ObjectPath}
|
14
|
+
# @see https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-marshaling-object-path
|
11
15
|
class ObjectPath < String
|
12
16
|
# @raise Error if not a valid object path
|
13
|
-
def initialize(
|
14
|
-
unless self.class.valid?(
|
15
|
-
raise DBus::Error, "Invalid object path #{
|
17
|
+
def initialize(str)
|
18
|
+
unless self.class.valid?(str)
|
19
|
+
raise DBus::Error, "Invalid object path #{str.inspect}"
|
16
20
|
end
|
21
|
+
|
17
22
|
super
|
18
23
|
end
|
19
24
|
|
20
|
-
def self.valid?(
|
21
|
-
|
25
|
+
def self.valid?(str)
|
26
|
+
str == "/" || str =~ %r{\A(/[A-Za-z0-9_]+)+\z}
|
22
27
|
end
|
23
28
|
end
|
24
29
|
end
|
data/lib/dbus/proxy_object.rb
CHANGED
@@ -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
|
@@ -44,6 +46,7 @@ module DBus
|
|
44
46
|
end
|
45
47
|
|
46
48
|
# Returns the interfaces of the object.
|
49
|
+
# @return [Array<String>] names of the interfaces
|
47
50
|
def interfaces
|
48
51
|
introspect unless introspected
|
49
52
|
@interfaces.keys
|
@@ -56,6 +59,7 @@ module DBus
|
|
56
59
|
introspect unless introspected
|
57
60
|
ifc = @interfaces[intfname]
|
58
61
|
raise DBus::Error, "no such interface `#{intfname}' on object `#{@path}'" unless ifc
|
62
|
+
|
59
63
|
ifc
|
60
64
|
end
|
61
65
|
|
@@ -92,6 +96,7 @@ module DBus
|
|
92
96
|
# don't overwrite instance methods!
|
93
97
|
next if dup_meths.include?(name)
|
94
98
|
next if self.class.instance_methods.include?(name)
|
99
|
+
|
95
100
|
if univocal_meths.include? name
|
96
101
|
univocal_meths.delete name
|
97
102
|
dup_meths << name
|
@@ -123,12 +128,20 @@ module DBus
|
|
123
128
|
def on_signal(name, &block)
|
124
129
|
# TODO: improve
|
125
130
|
raise NoMethodError unless @default_iface && has_iface?(@default_iface)
|
131
|
+
|
126
132
|
@interfaces[@default_iface].on_signal(name, &block)
|
127
133
|
end
|
128
134
|
|
129
135
|
####################################################
|
130
136
|
private
|
131
137
|
|
138
|
+
# rubocop:disable Lint/MissingSuper
|
139
|
+
# as this should forward everything
|
140
|
+
#
|
141
|
+
# https://github.com/rubocop-hq/ruby-style-guide#no-method-missing
|
142
|
+
# and http://blog.marc-andre.ca/2010/11/15/methodmissing-politely/
|
143
|
+
# have a point to be investigated
|
144
|
+
|
132
145
|
# Handles all unkown methods, mostly to route method calls to the
|
133
146
|
# default interface.
|
134
147
|
def method_missing(name, *args, &reply_handler)
|
@@ -146,9 +159,17 @@ module DBus
|
|
146
159
|
# interesting, foo.method("unknown")
|
147
160
|
# raises NameError, not NoMethodError
|
148
161
|
raise unless e.to_s =~ /undefined method `#{name}'/
|
162
|
+
|
149
163
|
# BTW e.exception("...") would preserve the class.
|
150
164
|
raise NoMethodError, "undefined method `#{name}' for DBus interface `#{@default_iface}' on object `#{@path}'"
|
151
165
|
end
|
152
166
|
end
|
153
|
-
|
167
|
+
# rubocop:enable Lint/MissingSuper
|
168
|
+
|
169
|
+
def respond_to_missing?(name, _include_private = false)
|
170
|
+
@default_iface &&
|
171
|
+
has_iface?(@default_iface) &&
|
172
|
+
@interfaces[@default_iface].methods.key?(name) or super
|
173
|
+
end
|
174
|
+
end
|
154
175
|
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,19 @@ module DBus
|
|
22
24
|
@api = api
|
23
25
|
end
|
24
26
|
|
25
|
-
# Investigates the sub-nodes of the proxy object
|
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
|
-
|
28
|
-
|
29
|
+
# @param pobj [ProxyObject]
|
30
|
+
# @param xml [String]
|
31
|
+
def self.introspect_into(pobj, xml)
|
32
|
+
intfs, pobj.subnodes = IntrospectXMLParser.new(xml).parse
|
29
33
|
intfs.each do |i|
|
30
|
-
poi = ProxyObjectInterface.new(
|
34
|
+
poi = ProxyObjectInterface.new(pobj, i.name)
|
31
35
|
i.methods.each_value { |m| poi.define(m) }
|
32
36
|
i.signals.each_value { |s| poi.define(s) }
|
33
|
-
|
37
|
+
pobj[i.name] = poi
|
34
38
|
end
|
35
|
-
|
39
|
+
pobj.introspected = true
|
36
40
|
end
|
37
41
|
|
38
42
|
# Generates, sets up and returns the proxy object.
|
@@ -41,5 +45,5 @@ module DBus
|
|
41
45
|
ProxyObjectFactory.introspect_into(po, @xml)
|
42
46
|
po
|
43
47
|
end
|
44
|
-
end
|
48
|
+
end
|
45
49
|
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
|
@@ -36,27 +38,28 @@ module DBus
|
|
36
38
|
@name
|
37
39
|
end
|
38
40
|
|
39
|
-
# Defines a method on the interface from the Method descriptor
|
40
|
-
|
41
|
-
|
41
|
+
# Defines a method on the interface from the Method descriptor _method_.
|
42
|
+
# @param method [Method]
|
43
|
+
def define_method_from_descriptor(method)
|
44
|
+
method.params.each do |fpar|
|
42
45
|
par = fpar.type
|
43
46
|
# This is the signature validity check
|
44
47
|
Type::Parser.new(par).parse
|
45
48
|
end
|
46
49
|
|
47
50
|
singleton_class.class_eval do
|
48
|
-
define_method
|
49
|
-
if
|
50
|
-
raise ArgumentError, "wrong number of arguments (#{args.size} for #{
|
51
|
+
define_method method.name do |*args, &reply_handler|
|
52
|
+
if method.params.size != args.size
|
53
|
+
raise ArgumentError, "wrong number of arguments (#{args.size} for #{method.params.size})"
|
51
54
|
end
|
52
55
|
|
53
56
|
msg = Message.new(Message::METHOD_CALL)
|
54
57
|
msg.path = @object.path
|
55
58
|
msg.interface = @name
|
56
59
|
msg.destination = @object.destination
|
57
|
-
msg.member =
|
60
|
+
msg.member = method.name
|
58
61
|
msg.sender = @object.bus.unique_name
|
59
|
-
|
62
|
+
method.params.each do |fpar|
|
60
63
|
par = fpar.type
|
61
64
|
msg.add_param(par, args.shift)
|
62
65
|
end
|
@@ -64,25 +67,27 @@ module DBus
|
|
64
67
|
if ret.nil? || @object.api.proxy_method_returns_array
|
65
68
|
ret
|
66
69
|
else
|
67
|
-
|
70
|
+
method.rets.size == 1 ? ret.first : ret
|
68
71
|
end
|
69
72
|
end
|
70
73
|
end
|
71
74
|
|
72
|
-
@methods[
|
75
|
+
@methods[method.name] = method
|
73
76
|
end
|
74
77
|
|
75
|
-
# Defines a signal from the descriptor
|
76
|
-
|
77
|
-
|
78
|
+
# Defines a signal from the descriptor _sig_.
|
79
|
+
# @param sig [Signal]
|
80
|
+
def define_signal_from_descriptor(sig)
|
81
|
+
@signals[sig.name] = sig
|
78
82
|
end
|
79
83
|
|
80
|
-
# Defines a signal or method based on the descriptor
|
81
|
-
def define(
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
84
|
+
# Defines a signal or method based on the descriptor _ifc_el_.
|
85
|
+
def define(ifc_el)
|
86
|
+
case ifc_el
|
87
|
+
when Method
|
88
|
+
define_method_from_descriptor(ifc_el)
|
89
|
+
when Signal
|
90
|
+
define_signal_from_descriptor(ifc_el)
|
86
91
|
end
|
87
92
|
end
|
88
93
|
|
@@ -109,7 +114,7 @@ module DBus
|
|
109
114
|
end
|
110
115
|
end
|
111
116
|
|
112
|
-
PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"
|
117
|
+
PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"
|
113
118
|
|
114
119
|
# Read a property.
|
115
120
|
# @param propname [String]
|
@@ -141,5 +146,5 @@ module DBus
|
|
141
146
|
ret
|
142
147
|
end
|
143
148
|
end
|
144
|
-
end
|
149
|
+
end
|
145
150
|
end
|