ruby-dbus 0.18.0.beta5 → 0.18.0.beta8
Sign up to get free protection for your applications and to get access to all the features.
- 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"
|