ruby-dbus 0.18.0.beta5 → 0.18.0.beta8
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 +43 -0
- data/VERSION +1 -1
- data/doc/Reference.md +13 -4
- data/lib/dbus/bus.rb +3 -7
- data/lib/dbus/data.rb +165 -68
- data/lib/dbus/emits_changed_signal.rb +83 -0
- data/lib/dbus/introspect.rb +44 -4
- data/lib/dbus/marshall.rb +6 -3
- data/lib/dbus/object.rb +84 -22
- data/lib/dbus/proxy_object_factory.rb +2 -0
- data/lib/dbus/proxy_object_interface.rb +37 -9
- data/lib/dbus/type.rb +26 -2
- data/lib/dbus/xml.rb +4 -0
- data/lib/dbus.rb +1 -0
- data/spec/data_spec.rb +342 -14
- data/spec/emits_changed_signal_spec.rb +58 -0
- data/spec/object_spec.rb +138 -0
- data/spec/packet_unmarshaller_spec.rb +2 -16
- data/spec/property_spec.rb +35 -2
- data/spec/service_newapi.rb +13 -0
- data/spec/type_spec.rb +40 -0
- metadata +5 -2
data/lib/dbus/object.rb
CHANGED
@@ -102,6 +102,18 @@ module DBus
|
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
|
+
# Declare the behavior of PropertiesChanged signal,
|
106
|
+
# common for all properties in this interface
|
107
|
+
# (individual properties may override it)
|
108
|
+
# @example
|
109
|
+
# self.emits_changed_signal = :invalidates
|
110
|
+
# @param [true,false,:const,:invalidates] value
|
111
|
+
def self.emits_changed_signal=(value)
|
112
|
+
raise UndefinedInterface, :emits_changed_signal if @@cur_intf.nil?
|
113
|
+
|
114
|
+
@@cur_intf.emits_changed_signal = EmitsChangedSignal.new(value)
|
115
|
+
end
|
116
|
+
|
105
117
|
# A read-write property accessing an instance variable.
|
106
118
|
# A combination of `attr_accessor` and {.dbus_accessor}.
|
107
119
|
#
|
@@ -114,11 +126,13 @@ module DBus
|
|
114
126
|
# @param dbus_name [String] if not given it is made
|
115
127
|
# by CamelCasing the ruby_name. foo_bar becomes FooBar
|
116
128
|
# to convert the Ruby convention to the DBus convention.
|
129
|
+
# @param emits_changed_signal [true,false,:const,:invalidates]
|
130
|
+
# see {EmitsChangedSignal}; if unspecified, ask the interface.
|
117
131
|
# @return [void]
|
118
|
-
def self.dbus_attr_accessor(ruby_name, type, dbus_name: nil)
|
132
|
+
def self.dbus_attr_accessor(ruby_name, type, dbus_name: nil, emits_changed_signal: nil)
|
119
133
|
attr_accessor(ruby_name)
|
120
134
|
|
121
|
-
dbus_accessor(ruby_name, type, dbus_name: dbus_name)
|
135
|
+
dbus_accessor(ruby_name, type, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal)
|
122
136
|
end
|
123
137
|
|
124
138
|
# A read-only property accessing an instance variable.
|
@@ -126,19 +140,20 @@ module DBus
|
|
126
140
|
#
|
127
141
|
# Whenever the property value gets changed from "inside" the object,
|
128
142
|
# you should emit the `PropertiesChanged` signal by calling
|
143
|
+
# {#dbus_properties_changed}.
|
129
144
|
#
|
130
|
-
#
|
145
|
+
# dbus_properties_changed(interface_name, {dbus_name.to_s => value}, [])
|
131
146
|
#
|
132
147
|
# or, omitting the value in the signal,
|
133
148
|
#
|
134
|
-
#
|
149
|
+
# dbus_properties_changed(interface_name, {}, [dbus_name.to_s])
|
135
150
|
#
|
136
151
|
# @param (see .dbus_attr_accessor)
|
137
152
|
# @return (see .dbus_attr_accessor)
|
138
|
-
def self.dbus_attr_reader(ruby_name, type, dbus_name: nil)
|
153
|
+
def self.dbus_attr_reader(ruby_name, type, dbus_name: nil, emits_changed_signal: nil)
|
139
154
|
attr_reader(ruby_name)
|
140
155
|
|
141
|
-
dbus_reader(ruby_name, type, dbus_name: dbus_name)
|
156
|
+
dbus_reader(ruby_name, type, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal)
|
142
157
|
end
|
143
158
|
|
144
159
|
# A write-only property accessing an instance variable.
|
@@ -146,10 +161,10 @@ module DBus
|
|
146
161
|
#
|
147
162
|
# @param (see .dbus_attr_accessor)
|
148
163
|
# @return (see .dbus_attr_accessor)
|
149
|
-
def self.dbus_attr_writer(ruby_name, type, dbus_name: nil)
|
164
|
+
def self.dbus_attr_writer(ruby_name, type, dbus_name: nil, emits_changed_signal: nil)
|
150
165
|
attr_writer(ruby_name)
|
151
166
|
|
152
|
-
dbus_writer(ruby_name, type, dbus_name: dbus_name)
|
167
|
+
dbus_writer(ruby_name, type, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal)
|
153
168
|
end
|
154
169
|
|
155
170
|
# A read-write property using a pair of reader/writer methods
|
@@ -160,36 +175,49 @@ module DBus
|
|
160
175
|
#
|
161
176
|
# @param (see .dbus_attr_accessor)
|
162
177
|
# @return (see .dbus_attr_accessor)
|
163
|
-
def self.dbus_accessor(ruby_name, type, dbus_name: nil)
|
178
|
+
def self.dbus_accessor(ruby_name, type, dbus_name: nil, emits_changed_signal: nil)
|
164
179
|
raise UndefinedInterface, ruby_name if @@cur_intf.nil?
|
165
180
|
|
166
181
|
dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
|
167
182
|
property = Property.new(dbus_name, type, :readwrite, ruby_name: ruby_name)
|
168
183
|
@@cur_intf.define(property)
|
169
184
|
|
170
|
-
dbus_watcher(ruby_name, dbus_name: dbus_name)
|
185
|
+
dbus_watcher(ruby_name, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal)
|
171
186
|
end
|
172
187
|
|
173
188
|
# A read-only property accessing a reader method (which must already exist).
|
174
189
|
# (To directly access an instance variable, use {.dbus_attr_reader} instead)
|
175
190
|
#
|
176
|
-
#
|
191
|
+
# At the D-Bus side the property is read only but it makes perfect sense to
|
192
|
+
# implement it with a read-write attr_accessor. In that case this method
|
193
|
+
# uses {.dbus_watcher} to set up the PropertiesChanged signal.
|
194
|
+
#
|
195
|
+
# attr_accessor :foo_bar
|
196
|
+
# dbus_reader :foo_bar, "s"
|
197
|
+
#
|
198
|
+
# If the property value should change by other means than its attr_writer,
|
177
199
|
# you should emit the `PropertiesChanged` signal by calling
|
200
|
+
# {#dbus_properties_changed}.
|
178
201
|
#
|
179
|
-
#
|
202
|
+
# dbus_properties_changed(interface_name, {dbus_name.to_s => value}, [])
|
180
203
|
#
|
181
204
|
# or, omitting the value in the signal,
|
182
205
|
#
|
183
|
-
#
|
206
|
+
# dbus_properties_changed(interface_name, {}, [dbus_name.to_s])
|
184
207
|
#
|
185
208
|
# @param (see .dbus_attr_accessor)
|
186
209
|
# @return (see .dbus_attr_accessor)
|
187
|
-
def self.dbus_reader(ruby_name, type, dbus_name: nil)
|
210
|
+
def self.dbus_reader(ruby_name, type, dbus_name: nil, emits_changed_signal: nil)
|
188
211
|
raise UndefinedInterface, ruby_name if @@cur_intf.nil?
|
189
212
|
|
190
213
|
dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
|
191
214
|
property = Property.new(dbus_name, type, :read, ruby_name: ruby_name)
|
192
215
|
@@cur_intf.define(property)
|
216
|
+
|
217
|
+
ruby_name_eq = "#{ruby_name}=".to_sym
|
218
|
+
return unless method_defined?(ruby_name_eq)
|
219
|
+
|
220
|
+
dbus_watcher(ruby_name, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal)
|
193
221
|
end
|
194
222
|
|
195
223
|
# A write-only property accessing a writer method (which must already exist).
|
@@ -199,14 +227,14 @@ module DBus
|
|
199
227
|
#
|
200
228
|
# @param (see .dbus_attr_accessor)
|
201
229
|
# @return (see .dbus_attr_accessor)
|
202
|
-
def self.dbus_writer(ruby_name, type, dbus_name: nil)
|
230
|
+
def self.dbus_writer(ruby_name, type, dbus_name: nil, emits_changed_signal: nil)
|
203
231
|
raise UndefinedInterface, ruby_name if @@cur_intf.nil?
|
204
232
|
|
205
233
|
dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
|
206
234
|
property = Property.new(dbus_name, type, :write, ruby_name: ruby_name)
|
207
235
|
@@cur_intf.define(property)
|
208
236
|
|
209
|
-
dbus_watcher(ruby_name, dbus_name: dbus_name)
|
237
|
+
dbus_watcher(ruby_name, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal)
|
210
238
|
end
|
211
239
|
|
212
240
|
# Enables automatic sending of the PropertiesChanged signal.
|
@@ -218,11 +246,13 @@ module DBus
|
|
218
246
|
# @param dbus_name [String] if not given it is made
|
219
247
|
# by CamelCasing the ruby_name. foo_bar becomes FooBar
|
220
248
|
# to convert the Ruby convention to the DBus convention.
|
249
|
+
# @param emits_changed_signal [true,false,:const,:invalidates]
|
250
|
+
# see {EmitsChangedSignal}; if unspecified, ask the interface.
|
221
251
|
# @return [void]
|
222
|
-
def self.dbus_watcher(ruby_name, dbus_name: nil)
|
252
|
+
def self.dbus_watcher(ruby_name, dbus_name: nil, emits_changed_signal: nil)
|
223
253
|
raise UndefinedInterface, ruby_name if @@cur_intf.nil?
|
224
254
|
|
225
|
-
|
255
|
+
interface_name = @@cur_intf.name
|
226
256
|
|
227
257
|
ruby_name = ruby_name.to_s.sub(/=$/, "").to_sym
|
228
258
|
ruby_name_eq = "#{ruby_name}=".to_sym
|
@@ -230,14 +260,27 @@ module DBus
|
|
230
260
|
|
231
261
|
dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
|
232
262
|
|
263
|
+
emits_changed_signal = EmitsChangedSignal.new(emits_changed_signal, interface: @@cur_intf)
|
264
|
+
|
233
265
|
# the argument order is alias_method(new_name, existing_name)
|
234
266
|
alias_method original_ruby_name_eq, ruby_name_eq
|
235
267
|
define_method ruby_name_eq do |value|
|
236
|
-
public_send(original_ruby_name_eq, value)
|
268
|
+
result = public_send(original_ruby_name_eq, value)
|
269
|
+
|
270
|
+
case emits_changed_signal.value
|
271
|
+
when true
|
272
|
+
# signature: "interface:s, changed_props:a{sv}, invalidated_props:as"
|
273
|
+
dbus_properties_changed(interface_name, { dbus_name.to_s => value }, [])
|
274
|
+
when :invalidates
|
275
|
+
dbus_properties_changed(interface_name, {}, [dbus_name.to_s])
|
276
|
+
when :const
|
277
|
+
# Oh my, seeing a value change of a supposedly constant property.
|
278
|
+
# Maybe should have raised at declaration time, don't make a fuss now.
|
279
|
+
when false
|
280
|
+
# Do nothing
|
281
|
+
end
|
237
282
|
|
238
|
-
|
239
|
-
# PropertiesChanged, "interface:s, changed_properties:a{sv}, invalidated_properties:as"
|
240
|
-
PropertiesChanged(cur_intf.name, { dbus_name.to_s => value }, [])
|
283
|
+
result
|
241
284
|
end
|
242
285
|
end
|
243
286
|
|
@@ -301,6 +344,25 @@ module DBus
|
|
301
344
|
dbus_name.to_sym
|
302
345
|
end
|
303
346
|
|
347
|
+
# Use this instead of calling PropertiesChanged directly. This one
|
348
|
+
# considers not only the PC signature (which says that all property values
|
349
|
+
# are variants) but also the specific property type.
|
350
|
+
# @param interface_name [String] interface name like "org.example.ManagerManager"
|
351
|
+
# @param changed_props [Hash{String => ::Object}]
|
352
|
+
# changed properties (D-Bus names) and their values.
|
353
|
+
# @param invalidated_props [Array<String>]
|
354
|
+
# names of properties whose changed value is not specified
|
355
|
+
def dbus_properties_changed(interface_name, changed_props, invalidated_props)
|
356
|
+
typed_changed_props = changed_props.map do |dbus_name, value|
|
357
|
+
property = dbus_lookup_property(interface_name, dbus_name)
|
358
|
+
type = property.type
|
359
|
+
typed_value = Data.make_typed(type, value)
|
360
|
+
variant = Data::Variant.new(typed_value, member_type: type)
|
361
|
+
[dbus_name, variant]
|
362
|
+
end.to_h
|
363
|
+
PropertiesChanged(interface_name, typed_changed_props, invalidated_props)
|
364
|
+
end
|
365
|
+
|
304
366
|
# @param interface_name [String]
|
305
367
|
# @param property_name [String]
|
306
368
|
# @return [Property]
|
@@ -29,11 +29,13 @@ module DBus
|
|
29
29
|
# @param pobj [ProxyObject]
|
30
30
|
# @param xml [String]
|
31
31
|
def self.introspect_into(pobj, xml)
|
32
|
+
# intfs [Array<Interface>], subnodes [Array<String>]
|
32
33
|
intfs, pobj.subnodes = IntrospectXMLParser.new(xml).parse
|
33
34
|
intfs.each do |i|
|
34
35
|
poi = ProxyObjectInterface.new(pobj, i.name)
|
35
36
|
i.methods.each_value { |m| poi.define(m) }
|
36
37
|
i.signals.each_value { |s| poi.define(s) }
|
38
|
+
i.properties.each_value { |p| poi.define(p) }
|
37
39
|
pobj[i.name] = poi
|
38
40
|
end
|
39
41
|
pobj.introspected = true
|
@@ -15,13 +15,16 @@ module DBus
|
|
15
15
|
# A class similar to the normal Interface used as a proxy for remote
|
16
16
|
# object interfaces.
|
17
17
|
class ProxyObjectInterface
|
18
|
-
#
|
19
|
-
|
20
|
-
#
|
21
|
-
|
22
|
-
#
|
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.
|
23
26
|
attr_reader :object
|
24
|
-
# The name of the interface.
|
27
|
+
# @return [String] The name of the interface.
|
25
28
|
attr_reader :name
|
26
29
|
|
27
30
|
# Creates a new proxy interface for the given proxy _object_
|
@@ -31,6 +34,7 @@ module DBus
|
|
31
34
|
@name = name
|
32
35
|
@methods = {}
|
33
36
|
@signals = {}
|
37
|
+
@properties = {}
|
34
38
|
end
|
35
39
|
|
36
40
|
# Returns the string representation of the interface (the name).
|
@@ -81,13 +85,21 @@ module DBus
|
|
81
85
|
@signals[sig.name] = sig
|
82
86
|
end
|
83
87
|
|
88
|
+
# @param prop [Property]
|
89
|
+
def define_property_from_descriptor(prop)
|
90
|
+
@properties[prop.name] = prop
|
91
|
+
end
|
92
|
+
|
84
93
|
# Defines a signal or method based on the descriptor _ifc_el_.
|
94
|
+
# @param ifc_el [DBus::Method,Signal,Property]
|
85
95
|
def define(ifc_el)
|
86
96
|
case ifc_el
|
87
97
|
when Method
|
88
98
|
define_method_from_descriptor(ifc_el)
|
89
99
|
when Signal
|
90
100
|
define_signal_from_descriptor(ifc_el)
|
101
|
+
when Property
|
102
|
+
define_property_from_descriptor(ifc_el)
|
91
103
|
end
|
92
104
|
end
|
93
105
|
|
@@ -129,10 +141,26 @@ module DBus
|
|
129
141
|
end
|
130
142
|
|
131
143
|
# Write a property.
|
132
|
-
# @param
|
144
|
+
# @param property_name [String]
|
133
145
|
# @param value [Object]
|
134
|
-
def []=(
|
135
|
-
|
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)
|
136
164
|
end
|
137
165
|
|
138
166
|
# Read all properties at once, as a hash.
|
data/lib/dbus/type.rb
CHANGED
@@ -107,6 +107,29 @@ module DBus
|
|
107
107
|
freeze
|
108
108
|
end
|
109
109
|
|
110
|
+
# A Type is equal to
|
111
|
+
# - another Type with the same string representation
|
112
|
+
# - a String ({SingleCompleteType}) describing the type
|
113
|
+
def ==(other)
|
114
|
+
case other
|
115
|
+
when ::String
|
116
|
+
to_s == other
|
117
|
+
else
|
118
|
+
eql?(other)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# A Type is eql? to
|
123
|
+
# - another Type with the same string representation
|
124
|
+
#
|
125
|
+
# Hash key equality
|
126
|
+
# See https://ruby-doc.org/core-3.0.0/Object.html#method-i-eql-3F
|
127
|
+
def eql?(other)
|
128
|
+
return false unless other.is_a?(Type)
|
129
|
+
|
130
|
+
@sigtype == other.sigtype && @members == other.members
|
131
|
+
end
|
132
|
+
|
110
133
|
# Return the required alignment for the type.
|
111
134
|
def alignment
|
112
135
|
TYPE_MAPPING[@sigtype].last
|
@@ -157,7 +180,7 @@ module DBus
|
|
157
180
|
|
158
181
|
def inspect
|
159
182
|
s = TYPE_MAPPING[@sigtype].first
|
160
|
-
if [STRUCT, ARRAY].member?(@sigtype)
|
183
|
+
if [STRUCT, ARRAY, DICT_ENTRY].member?(@sigtype)
|
161
184
|
s += ": #{@members.inspect}"
|
162
185
|
end
|
163
186
|
s
|
@@ -392,8 +415,9 @@ module DBus
|
|
392
415
|
# @param string_type [SingleCompleteType]
|
393
416
|
# @param value [::Object]
|
394
417
|
# @return [Array(DBus::Type::Type,::Object)]
|
418
|
+
# @deprecated Use {Data::Variant#initialize} instead
|
395
419
|
def variant(string_type, value)
|
396
|
-
|
420
|
+
Data::Variant.new(value, member_type: string_type)
|
397
421
|
end
|
398
422
|
module_function :variant
|
399
423
|
end
|
data/lib/dbus/xml.rb
CHANGED
data/lib/dbus.rb
CHANGED
@@ -15,6 +15,7 @@ require_relative "dbus/auth"
|
|
15
15
|
require_relative "dbus/bus"
|
16
16
|
require_relative "dbus/bus_name"
|
17
17
|
require_relative "dbus/data"
|
18
|
+
require_relative "dbus/emits_changed_signal"
|
18
19
|
require_relative "dbus/error"
|
19
20
|
require_relative "dbus/introspect"
|
20
21
|
require_relative "dbus/logger"
|