ruby-dbus 0.18.0.beta6 → 0.18.1
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 +68 -0
- data/VERSION +1 -1
- data/doc/Reference.md +13 -4
- data/lib/dbus/bus.rb +3 -7
- data/lib/dbus/data.rb +39 -5
- data/lib/dbus/emits_changed_signal.rb +83 -0
- data/lib/dbus/introspect.rb +44 -4
- 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 +2 -1
- data/lib/dbus/xml.rb +4 -0
- data/lib/dbus.rb +1 -0
- data/spec/data_spec.rb +44 -8
- data/spec/emits_changed_signal_spec.rb +58 -0
- data/spec/object_spec.rb +138 -0
- data/spec/property_spec.rb +35 -2
- data/spec/service_newapi.rb +13 -0
- metadata +12 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 07f1f2fd26d13923b2601c9c3f29ccf57afd42c0348b937bceea0804eece6d66
|
4
|
+
data.tar.gz: 5ad80792234951e7ed422b6eba16e7f6e55b41f24a116ef40afaa7f32cdeef74
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: acbe18b2d9695f8959ef6d51a3871e989600291076d6eef870a9215768ed6c7afd599d3af2741e13a733fe2f5cc9f75fdf075a130cafd0a9144b4ca00bd80dc9
|
7
|
+
data.tar.gz: 0363375dd8c4465aa2650649d0bd62a4f95431b78178edb624a84f5326e395476e0c54e4755b27fedc86caf94b7c059abbdc15af28832417ec1d3e4770ea9ae0
|
data/NEWS.md
CHANGED
@@ -2,6 +2,74 @@
|
|
2
2
|
|
3
3
|
## Unreleased
|
4
4
|
|
5
|
+
## Ruby D-Bus 0.18.1 - 2022-07-13
|
6
|
+
|
7
|
+
No changes since 0.18.0.beta8.
|
8
|
+
Repeating the most important changes since 0.17.0:
|
9
|
+
|
10
|
+
API:
|
11
|
+
* Introduced DBus::Data classes, use them in Properties.Get,
|
12
|
+
Properties.GetAll to return correct types as declared ([#97][]).
|
13
|
+
* Introduced Object#dbus_properties_changed to send correctly typed property
|
14
|
+
values ([#115][]). Avoid calling PropertiesChanged directly as it will
|
15
|
+
guess the types.
|
16
|
+
* Service side `emits_changed_signal` to control emission of
|
17
|
+
PropertiesChanged: can be assigned within `dbus_interface` or as an option
|
18
|
+
when declaring properties ([#117][]).
|
19
|
+
* DBus.variant(type, value) is deprecated in favor of
|
20
|
+
Data::Variant.new(value, member_type:)
|
21
|
+
* Added type factories
|
22
|
+
* Type::Array[type]
|
23
|
+
* Type::Hash[key_type, value_type]
|
24
|
+
* Type::Struct[type1, type2...]
|
25
|
+
|
26
|
+
Bug fixes:
|
27
|
+
* Fix Object.dbus_reader to work with attr_accessor and automatically produce
|
28
|
+
dbus_properties_changed for properties that are read-write at
|
29
|
+
implementation side and read-only at D-Bus side ([#96][])
|
30
|
+
* Service-side properties: Fix Properties.Get, Properties.GetAll
|
31
|
+
to use the specific property signature, not the generic
|
32
|
+
Variant ([#97][], [#105][], [#109][]).
|
33
|
+
* Client-side properties: When calling Properties.Set in
|
34
|
+
ProxyObjectInterface#[]=, use the correct type ([#108][]).
|
35
|
+
* Added thorough tests (`spec/data/marshall.yaml`) to detect nearly all
|
36
|
+
invalid data at unmarshalling time.
|
37
|
+
|
38
|
+
Requirements:
|
39
|
+
* Require Ruby 2.4, because of RuboCop 1.0.
|
40
|
+
|
41
|
+
## Ruby D-Bus 0.18.0.beta8 - 2022-06-21
|
42
|
+
|
43
|
+
Bug fixes:
|
44
|
+
* Introduced Object#dbus_properties_changed to send correctly typed property
|
45
|
+
values ([#115][]). Avoid calling PropertiesChanged directly as it will
|
46
|
+
guess the types.
|
47
|
+
* Fix Object.dbus_reader to work with attr_accessor and automatically produce
|
48
|
+
dbus_properties_changed for properties that are read-write at
|
49
|
+
implementation side and read-only at D-Bus side ([#96][])
|
50
|
+
|
51
|
+
[#96]: https://github.com/mvidner/ruby-dbus/issues/96
|
52
|
+
|
53
|
+
API:
|
54
|
+
* Service side `emits_changed_signal` to control emission of
|
55
|
+
PropertiesChanged: can be assigned within `dbus_interface` or as an option
|
56
|
+
when declaring properties ([#117][]).
|
57
|
+
|
58
|
+
[#115]: https://github.com/mvidner/ruby-dbus/issues/115
|
59
|
+
[#117]: https://github.com/mvidner/ruby-dbus/pulls/117
|
60
|
+
|
61
|
+
## Ruby D-Bus 0.18.0.beta7 - 2022-05-29
|
62
|
+
|
63
|
+
API:
|
64
|
+
* DBus.variant(type, value) is deprecated in favor of
|
65
|
+
Data::Variant.new(value, member_type:)
|
66
|
+
|
67
|
+
Bug fixes:
|
68
|
+
* Client-side properties: When calling Properties.Set in
|
69
|
+
ProxyObjectInterface#[]=, use the correct type ([#108][]).
|
70
|
+
|
71
|
+
[#108]: https://github.com/mvidner/ruby-dbus/issues/108
|
72
|
+
|
5
73
|
## Ruby D-Bus 0.18.0.beta6 - 2022-05-25
|
6
74
|
|
7
75
|
API:
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.18.
|
1
|
+
0.18.1
|
data/doc/Reference.md
CHANGED
@@ -166,12 +166,21 @@ D-Bus has stricter typing than Ruby, so the library must decide
|
|
166
166
|
which D-Bus type to choose. Most of the time the choice is dictated
|
167
167
|
by the D-Bus signature.
|
168
168
|
|
169
|
+
For exact representation of D-Bus data types, use subclasses
|
170
|
+
of {DBus::Data::Base}, such as {DBus::Data::Int16} or {DBus::Data::UInt64}.
|
171
|
+
|
169
172
|
##### Variants
|
170
173
|
|
171
174
|
If the signature expects a Variant
|
172
175
|
(which is the case for all Properties!) then an explicit mechanism is needed.
|
173
176
|
|
174
|
-
1.
|
177
|
+
1. Any {DBus::Data::Base}.
|
178
|
+
|
179
|
+
2. A {DBus::Data::Variant} made by {DBus.variant}(signature, value).
|
180
|
+
(Formerly this produced the type+value pair below, now it is just an alias
|
181
|
+
to the Variant constructor.)
|
182
|
+
|
183
|
+
3. A pair [{DBus::Type}, value] specifies to marshall *value* as
|
175
184
|
that specified type.
|
176
185
|
The pair can be produced by {DBus.variant}(signature, value) which
|
177
186
|
gives the same result as [{DBus.type}(signature), value].
|
@@ -181,13 +190,13 @@ If the signature expects a Variant
|
|
181
190
|
|
182
191
|
`foo_i["Bar"] = DBus.variant("au", [0, 1, 1, 2, 3, 5, 8])`
|
183
192
|
|
184
|
-
|
193
|
+
4. Other values are tried to fit one of these:
|
185
194
|
Boolean, Double, Array of Variants, Hash of String keyed Variants,
|
186
195
|
String, Int32, Int64.
|
187
196
|
|
188
|
-
|
197
|
+
5. **Deprecated:** A pair [String, value], where String is a valid
|
189
198
|
signature of a single complete type, marshalls value as that
|
190
|
-
type. This will hit you when you rely on method (
|
199
|
+
type. This will hit you when you rely on method (4) but happen to have
|
191
200
|
a particular string value in an array.
|
192
201
|
|
193
202
|
##### Structs
|
data/lib/dbus/bus.rb
CHANGED
@@ -171,15 +171,11 @@ module DBus
|
|
171
171
|
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
172
172
|
'
|
173
173
|
xml += "<node name=\"#{node_opath}\">\n"
|
174
|
-
|
174
|
+
each_key do |k|
|
175
175
|
xml += " <node name=\"#{k}\" />\n"
|
176
176
|
end
|
177
|
-
@object&.intfs&.
|
178
|
-
xml +=
|
179
|
-
v.methods.each_value { |m| xml += m.to_xml }
|
180
|
-
v.signals.each_value { |m| xml += m.to_xml }
|
181
|
-
v.properties.each_value { |m| xml += m.to_xml }
|
182
|
-
xml += " </interface>\n"
|
177
|
+
@object&.intfs&.each_value do |v|
|
178
|
+
xml += v.to_xml
|
183
179
|
end
|
184
180
|
xml += "</node>"
|
185
181
|
xml
|
data/lib/dbus/data.rb
CHANGED
@@ -601,8 +601,10 @@ module DBus
|
|
601
601
|
|
602
602
|
typed_value = case value
|
603
603
|
when Data::Array
|
604
|
-
|
605
|
-
|
604
|
+
unless value.type == type
|
605
|
+
raise ArgumentError,
|
606
|
+
"Specified type is #{type.inspect} but value type is #{value.type.inspect}"
|
607
|
+
end
|
606
608
|
|
607
609
|
value.exact_value
|
608
610
|
else
|
@@ -651,8 +653,10 @@ module DBus
|
|
651
653
|
|
652
654
|
typed_value = case value
|
653
655
|
when self.class
|
654
|
-
|
655
|
-
|
656
|
+
unless value.type == type
|
657
|
+
raise ArgumentError,
|
658
|
+
"Specified type is #{type.inspect} but value type is #{value.type.inspect}"
|
659
|
+
end
|
656
660
|
|
657
661
|
value.exact_value
|
658
662
|
else
|
@@ -751,13 +755,19 @@ module DBus
|
|
751
755
|
# @return [Type]
|
752
756
|
attr_reader :member_type
|
753
757
|
|
758
|
+
# Determine the type of *value*
|
759
|
+
# @param value [::Object]
|
760
|
+
# @return [Type]
|
761
|
+
# @api private
|
762
|
+
# See also {PacketMarshaller.make_variant}
|
754
763
|
def self.guess_type(value)
|
755
764
|
sct, = PacketMarshaller.make_variant(value)
|
756
765
|
DBus.type(sct)
|
757
766
|
end
|
758
767
|
|
759
|
-
# @param member_type [Type,nil]
|
768
|
+
# @param member_type [SingleCompleteType,Type,nil]
|
760
769
|
def initialize(value, member_type:)
|
770
|
+
member_type = Type::Factory.make_type(member_type) if member_type
|
761
771
|
# TODO: validate that the given *member_type* matches *value*
|
762
772
|
case value
|
763
773
|
when Data::Variant
|
@@ -775,6 +785,30 @@ module DBus
|
|
775
785
|
end
|
776
786
|
super(value)
|
777
787
|
end
|
788
|
+
|
789
|
+
# Internal helpers to keep the {DBus.variant} method working.
|
790
|
+
# Formerly it returned just a pair of [DBus.type(string_type), value]
|
791
|
+
# so let's provide [0], [1], .first, .last
|
792
|
+
def [](index)
|
793
|
+
case index
|
794
|
+
when 0
|
795
|
+
member_type
|
796
|
+
when 1
|
797
|
+
value
|
798
|
+
else
|
799
|
+
raise ArgumentError, "DBus.variant can only be indexed with 0 or 1, seen #{index.inspect}"
|
800
|
+
end
|
801
|
+
end
|
802
|
+
|
803
|
+
# @see #[]
|
804
|
+
def first
|
805
|
+
self[0]
|
806
|
+
end
|
807
|
+
|
808
|
+
# @see #[]
|
809
|
+
def last
|
810
|
+
self[1]
|
811
|
+
end
|
778
812
|
end
|
779
813
|
|
780
814
|
consts = constants.map { |c_sym| const_get(c_sym) }
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This file is part of the ruby-dbus project
|
4
|
+
# Copyright (C) 2022 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
|
+
module DBus
|
12
|
+
# Describes the behavior of PropertiesChanged signal, for a single property
|
13
|
+
# or for an entire interface.
|
14
|
+
#
|
15
|
+
# The possible values are:
|
16
|
+
#
|
17
|
+
# - *true*: the signal is emitted with the value included.
|
18
|
+
# - *:invalidates*: the signal is emitted but the value is not included
|
19
|
+
# in the signal.
|
20
|
+
# - *:const*: the property never changes value during the lifetime
|
21
|
+
# of the object it belongs to, and hence the signal
|
22
|
+
# is never emitted for it (but clients can cache the value)
|
23
|
+
# - *false*: the signal won't be emitted (clients should re-Get the property value)
|
24
|
+
#
|
25
|
+
# The default is:
|
26
|
+
# - for an interface: *true*
|
27
|
+
# - for a property: what the parent interface specifies
|
28
|
+
#
|
29
|
+
# @see DBus::Object.emits_changed_signal
|
30
|
+
# @see DBus::Object.dbus_attr_accessor
|
31
|
+
# @see https://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format
|
32
|
+
#
|
33
|
+
# Immutable once constructed.
|
34
|
+
class EmitsChangedSignal
|
35
|
+
# @return [true,false,:const,:invalidates]
|
36
|
+
attr_reader :value
|
37
|
+
|
38
|
+
# @param value [true,false,:const,:invalidates,nil]
|
39
|
+
# See class-level description above, {EmitsChangedSignal}.
|
40
|
+
# @param interface [Interface,nil]
|
41
|
+
# If the (property-level) *value* is unspecified (nil), this is the
|
42
|
+
# containing {Interface} to get the value from.
|
43
|
+
def initialize(value, interface: nil)
|
44
|
+
if value.nil?
|
45
|
+
raise ArgumentError, "Both arguments are nil" if interface.nil?
|
46
|
+
|
47
|
+
@value = interface.emits_changed_signal.value
|
48
|
+
else
|
49
|
+
expecting = [true, false, :const, :invalidates]
|
50
|
+
unless expecting.include?(value)
|
51
|
+
raise ArgumentError, "Expecting one of #{expecting.inspect}. Seen #{value.inspect}"
|
52
|
+
end
|
53
|
+
|
54
|
+
@value = value
|
55
|
+
end
|
56
|
+
|
57
|
+
freeze
|
58
|
+
end
|
59
|
+
|
60
|
+
# Return introspection XML string representation
|
61
|
+
# @return [String]
|
62
|
+
def to_xml
|
63
|
+
return "" if @value == true
|
64
|
+
|
65
|
+
" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"#{@value}\"/>\n"
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_s
|
69
|
+
@value.to_s
|
70
|
+
end
|
71
|
+
|
72
|
+
def ==(other)
|
73
|
+
if other.is_a?(self.class)
|
74
|
+
other.value == @value
|
75
|
+
else
|
76
|
+
other == value
|
77
|
+
end
|
78
|
+
end
|
79
|
+
alias eql? ==
|
80
|
+
|
81
|
+
DEFAULT_ECS = EmitsChangedSignal.new(true)
|
82
|
+
end
|
83
|
+
end
|
data/lib/dbus/introspect.rb
CHANGED
@@ -26,7 +26,7 @@ module DBus
|
|
26
26
|
# method call instantiates and configures this class for us.
|
27
27
|
#
|
28
28
|
# It also is the local definition of interface exported by the program.
|
29
|
-
# At the client side, see ProxyObjectInterface
|
29
|
+
# At the client side, see {ProxyObjectInterface}.
|
30
30
|
class Interface
|
31
31
|
# @return [String] The name of the interface.
|
32
32
|
attr_reader :name
|
@@ -38,6 +38,9 @@ module DBus
|
|
38
38
|
# @return [Hash{Symbol => Property}]
|
39
39
|
attr_reader :properties
|
40
40
|
|
41
|
+
# @return [EmitsChangedSignal]
|
42
|
+
attr_reader :emits_changed_signal
|
43
|
+
|
41
44
|
# Creates a new interface with a given _name_.
|
42
45
|
def initialize(name)
|
43
46
|
validate_name(name)
|
@@ -45,6 +48,20 @@ module DBus
|
|
45
48
|
@methods = {}
|
46
49
|
@signals = {}
|
47
50
|
@properties = {}
|
51
|
+
@emits_changed_signal = EmitsChangedSignal::DEFAULT_ECS
|
52
|
+
end
|
53
|
+
|
54
|
+
# Helper for {Object.emits_changed_signal=}.
|
55
|
+
# @api private
|
56
|
+
def emits_changed_signal=(ecs)
|
57
|
+
raise TypeError unless ecs.is_a? EmitsChangedSignal
|
58
|
+
# equal?: object identity
|
59
|
+
unless @emits_changed_signal.equal?(EmitsChangedSignal::DEFAULT_ECS) ||
|
60
|
+
@emits_changed_signal.value == ecs.value
|
61
|
+
raise "emits_change_signal was assigned more than once"
|
62
|
+
end
|
63
|
+
|
64
|
+
@emits_changed_signal = ecs
|
48
65
|
end
|
49
66
|
|
50
67
|
# Validates a service _name_.
|
@@ -85,6 +102,18 @@ module DBus
|
|
85
102
|
define(m)
|
86
103
|
end
|
87
104
|
alias declare_method define_method
|
105
|
+
|
106
|
+
# Return introspection XML string representation of the property.
|
107
|
+
# @return [String]
|
108
|
+
def to_xml
|
109
|
+
xml = " <interface name=\"#{name}\">\n"
|
110
|
+
xml += emits_changed_signal.to_xml
|
111
|
+
methods.each_value { |m| xml += m.to_xml }
|
112
|
+
signals.each_value { |m| xml += m.to_xml }
|
113
|
+
properties.each_value { |m| xml += m.to_xml }
|
114
|
+
xml += " </interface>\n"
|
115
|
+
xml
|
116
|
+
end
|
88
117
|
end
|
89
118
|
|
90
119
|
# = A formal parameter has a name and a type
|
@@ -190,6 +219,7 @@ module DBus
|
|
190
219
|
end
|
191
220
|
|
192
221
|
# Return an XML string representation of the method interface elment.
|
222
|
+
# @return [String]
|
193
223
|
def to_xml
|
194
224
|
xml = " <method name=\"#{@name}\">\n"
|
195
225
|
@params.each do |param|
|
@@ -238,19 +268,20 @@ module DBus
|
|
238
268
|
# An (exported) property
|
239
269
|
# https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties
|
240
270
|
class Property
|
241
|
-
# @return [
|
271
|
+
# @return [Symbol] The name of the property, for example FooBar.
|
242
272
|
attr_reader :name
|
243
273
|
# @return [SingleCompleteType]
|
244
274
|
attr_reader :type
|
245
275
|
# @return [Symbol] :read :write or :readwrite
|
246
276
|
attr_reader :access
|
247
277
|
|
248
|
-
# @return [Symbol] What to call at Ruby side.
|
278
|
+
# @return [Symbol,nil] What to call at Ruby side.
|
249
279
|
# (Always without the trailing `=`)
|
280
|
+
# It is `nil` IFF representing a client-side proxy.
|
250
281
|
attr_reader :ruby_name
|
251
282
|
|
252
283
|
def initialize(name, type, access, ruby_name:)
|
253
|
-
@name = name
|
284
|
+
@name = name.to_sym
|
254
285
|
@type = type
|
255
286
|
@access = access
|
256
287
|
@ruby_name = ruby_name
|
@@ -270,5 +301,14 @@ module DBus
|
|
270
301
|
def to_xml
|
271
302
|
" <property type=\"#{@type}\" name=\"#{@name}\" access=\"#{@access}\"/>\n"
|
272
303
|
end
|
304
|
+
|
305
|
+
# @param xml_node [AbstractXML::Node]
|
306
|
+
# @return [Property]
|
307
|
+
def self.from_xml(xml_node)
|
308
|
+
name = xml_node["name"].to_sym
|
309
|
+
type = xml_node["type"]
|
310
|
+
access = xml_node["access"].to_sym
|
311
|
+
new(name, type, access, ruby_name: nil)
|
312
|
+
end
|
273
313
|
end
|
274
314
|
end
|
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
@@ -415,8 +415,9 @@ module DBus
|
|
415
415
|
# @param string_type [SingleCompleteType]
|
416
416
|
# @param value [::Object]
|
417
417
|
# @return [Array(DBus::Type::Type,::Object)]
|
418
|
+
# @deprecated Use {Data::Variant#initialize} instead
|
418
419
|
def variant(string_type, value)
|
419
|
-
|
420
|
+
Data::Variant.new(value, member_type: string_type)
|
420
421
|
end
|
421
422
|
module_function :variant
|
422
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"
|
data/spec/data_spec.rb
CHANGED
@@ -193,7 +193,7 @@ RSpec.shared_examples "constructor rejects values from this list" do |bad_list|
|
|
193
193
|
describe "#initialize" do
|
194
194
|
bad_list.each do |(value, exc_class, msg_substr)|
|
195
195
|
it "rejects #{value.inspect} with #{exc_class}: #{msg_substr}" do
|
196
|
-
msg_re = Regexp.new(Regexp.quote(msg_substr))
|
196
|
+
msg_re = Regexp.try_convert(msg_substr) || Regexp.new(Regexp.quote(msg_substr))
|
197
197
|
expect { described_class.new(value) }.to raise_error(exc_class, msg_re)
|
198
198
|
end
|
199
199
|
end
|
@@ -204,7 +204,7 @@ RSpec.shared_examples "constructor (kwargs) rejects values" do |bad_list|
|
|
204
204
|
describe "#initialize" do
|
205
205
|
bad_list.each do |(value, kwargs_hash, exc_class, msg_substr)|
|
206
206
|
it "rejects #{value.inspect}, #{kwargs_hash.inspect} with #{exc_class}: #{msg_substr}" do
|
207
|
-
msg_re = Regexp.new(Regexp.quote(msg_substr))
|
207
|
+
msg_re = Regexp.try_convert(msg_substr) || Regexp.new(Regexp.quote(msg_substr))
|
208
208
|
expect { described_class.new(value, **kwargs_hash) }.to raise_error(exc_class, msg_re)
|
209
209
|
end
|
210
210
|
end
|
@@ -387,8 +387,9 @@ describe DBus::Data do
|
|
387
387
|
|
388
388
|
describe "containers" do
|
389
389
|
describe DBus::Data::Array do
|
390
|
+
aq = DBus::Data::Array.new([1, 2, 3], type: "aq")
|
391
|
+
|
390
392
|
good = [
|
391
|
-
# [[1, 2, 3], type: nil],
|
392
393
|
[[1, 2, 3], { type: "aq" }],
|
393
394
|
[[1, 2, 3], { type: T::Array[T::UINT16] }],
|
394
395
|
[[1, 2, 3], { type: T::Array["q"] }],
|
@@ -398,9 +399,14 @@ describe DBus::Data do
|
|
398
399
|
|
399
400
|
bad = [
|
400
401
|
# undesirable type guessing
|
401
|
-
|
402
|
-
|
403
|
-
|
402
|
+
[[1, 2, 3], { type: nil }, ArgumentError, /Expecting DBus::Type.*got nil/],
|
403
|
+
[[1, 2, 3], { type: "!" }, DBus::Type::SignatureException, "Unknown type code"],
|
404
|
+
[aq, { type: "q" }, ArgumentError, "Expecting \"a\""],
|
405
|
+
[aq, { type: "ao" }, ArgumentError,
|
406
|
+
"Specified type is ARRAY: [OBJECT_PATH] but value type is ARRAY: [UINT16]"]
|
407
|
+
# TODO: how to handle these?
|
408
|
+
# [{1 => 2, 3 => 4}, { type: "aq" }, ArgumentError, "?"],
|
409
|
+
# [/i am not an array/, { type: "aq" }, ArgumentError, "?"],
|
404
410
|
]
|
405
411
|
|
406
412
|
include_examples "#== and #eql? work for container types (inequal)",
|
@@ -506,7 +512,7 @@ describe DBus::Data do
|
|
506
512
|
value2 = result1
|
507
513
|
type2 = "(xxx)"
|
508
514
|
expect { described_class.new(value2, type: type2) }
|
509
|
-
.to raise_error(ArgumentError, /value type is
|
515
|
+
.to raise_error(ArgumentError, /value type is STRUCT.*UINT32/)
|
510
516
|
end
|
511
517
|
|
512
518
|
it "checks that size of type and value match" do
|
@@ -553,7 +559,7 @@ describe DBus::Data do
|
|
553
559
|
value2 = result1
|
554
560
|
type2 = T::Hash[T::UINT64, T::UINT64].child
|
555
561
|
expect { described_class.new(value2, type: type2) }
|
556
|
-
.to raise_error(ArgumentError, /value type is
|
562
|
+
.to raise_error(ArgumentError, /value type is DICT_ENTRY.*UINT32/)
|
557
563
|
end
|
558
564
|
|
559
565
|
it "checks that size of type and value match" do
|
@@ -632,6 +638,36 @@ describe DBus::Data do
|
|
632
638
|
|
633
639
|
include_examples "#== and #eql? work for container types (1 value)",
|
634
640
|
"/foo", { member_type: DBus.type(T::STRING) }
|
641
|
+
|
642
|
+
describe "DBus.variant compatibility" do
|
643
|
+
let(:v) { DBus.variant("o", "/foo") }
|
644
|
+
|
645
|
+
describe "#[]" do
|
646
|
+
it "returns the type for 0" do
|
647
|
+
expect(v[0]).to eq DBus.type(DBus::Type::OBJECT_PATH)
|
648
|
+
end
|
649
|
+
|
650
|
+
it "returns the value for 1" do
|
651
|
+
expect(v[1]).to eq DBus::ObjectPath.new("/foo")
|
652
|
+
end
|
653
|
+
|
654
|
+
it "returns an error for other indices" do
|
655
|
+
expect { v[2] }.to raise_error(ArgumentError, /DBus.variant can only be indexed with 0 or 1/)
|
656
|
+
end
|
657
|
+
end
|
658
|
+
|
659
|
+
describe "#first" do
|
660
|
+
it "returns the type" do
|
661
|
+
expect(v.first).to eq DBus.type(DBus::Type::OBJECT_PATH)
|
662
|
+
end
|
663
|
+
end
|
664
|
+
|
665
|
+
describe "#last" do
|
666
|
+
it "returns the value" do
|
667
|
+
expect(v.last).to eq DBus::ObjectPath.new("/foo")
|
668
|
+
end
|
669
|
+
end
|
670
|
+
end
|
635
671
|
end
|
636
672
|
end
|
637
673
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/usr/bin/env rspec
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative "spec_helper"
|
5
|
+
require "dbus"
|
6
|
+
|
7
|
+
describe DBus::EmitsChangedSignal do
|
8
|
+
describe "#initialize" do
|
9
|
+
it "accepts a simple value" do
|
10
|
+
expect(described_class.new(:const).value).to eq :const
|
11
|
+
end
|
12
|
+
|
13
|
+
it "avoids nil by asking the interface" do
|
14
|
+
ifc = DBus::Interface.new("org.example.Foo")
|
15
|
+
ifc.emits_changed_signal = described_class.new(:invalidates)
|
16
|
+
|
17
|
+
expect(described_class.new(nil, interface: ifc).value).to eq :invalidates
|
18
|
+
end
|
19
|
+
|
20
|
+
it "fails for unknown value" do
|
21
|
+
expect { described_class.new(:huh) }.to raise_error(ArgumentError, /Seen :huh/)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "fails for 2 nils" do
|
25
|
+
expect { described_class.new(nil, interface: nil) }.to raise_error(ArgumentError, /Both/)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#==" do
|
30
|
+
it "is true for two different objects with the same value" do
|
31
|
+
const_a = described_class.new(:const)
|
32
|
+
const_b = described_class.new(:const)
|
33
|
+
expect(const_a == const_b).to be true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "#to_xml" do
|
38
|
+
it "uses a string value" do
|
39
|
+
expect(described_class.new(:const).to_xml)
|
40
|
+
.to eq " <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"const\"/>\n"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#to_s" do
|
45
|
+
it "uses a string value" do
|
46
|
+
expect(described_class.new(:const).to_s).to eq "const"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe DBus::Interface do
|
52
|
+
describe ".emits_changed_signal=" do
|
53
|
+
it "only allows an EmitsChangedSignal as argument" do
|
54
|
+
ifc = described_class.new("org.ruby.Interface")
|
55
|
+
expect { ifc.emits_changed_signal = :const }.to raise_error(TypeError)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/spec/object_spec.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
#!/usr/bin/env rspec
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative "spec_helper"
|
5
|
+
require "dbus"
|
6
|
+
|
7
|
+
class ObjectTest < DBus::Object
|
8
|
+
T = DBus::Type unless const_defined? "T"
|
9
|
+
|
10
|
+
dbus_interface "org.ruby.ServerTest" do
|
11
|
+
dbus_attr_writer :write_me, T::Struct[String, String]
|
12
|
+
|
13
|
+
attr_accessor :read_only_for_dbus
|
14
|
+
|
15
|
+
dbus_reader :read_only_for_dbus, T::STRING, emits_changed_signal: :invalidates
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe DBus::Object do
|
20
|
+
describe ".dbus_attr_writer" do
|
21
|
+
describe "the declared assignment method" do
|
22
|
+
# Slightly advanced RSpec:
|
23
|
+
# https://rspec.info/documentation/3.9/rspec-expectations/RSpec/Matchers.html#satisfy-instance_method
|
24
|
+
let(:a_struct_in_a_variant) do
|
25
|
+
satisfying { |x| x.is_a?(DBus::Data::Variant) && x.member_type.to_s == "(ss)" }
|
26
|
+
# ^ This formatting keeps the matcher on a single line
|
27
|
+
# which enables RSpec to cite it if it fails, instead of saying "block".
|
28
|
+
end
|
29
|
+
|
30
|
+
it "emits PropertyChanged with correctly typed argument" do
|
31
|
+
obj = ObjectTest.new("/test")
|
32
|
+
expect(obj).to receive(:PropertiesChanged).with(
|
33
|
+
"org.ruby.ServerTest",
|
34
|
+
{
|
35
|
+
"WriteMe" => a_struct_in_a_variant
|
36
|
+
},
|
37
|
+
[]
|
38
|
+
)
|
39
|
+
# bug: call PC with simply the assigned value,
|
40
|
+
# which will need type guessing
|
41
|
+
obj.write_me = ["two", "strings"]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe ".dbus_accessor" do
|
47
|
+
it "can only be used within a dbus_interface" do
|
48
|
+
expect do
|
49
|
+
ObjectTest.instance_exec do
|
50
|
+
dbus_accessor :foo, DBus::Type::STRING
|
51
|
+
end
|
52
|
+
end.to raise_error(DBus::Object::UndefinedInterface)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe ".dbus_reader" do
|
57
|
+
it "can only be used within a dbus_interface" do
|
58
|
+
expect do
|
59
|
+
ObjectTest.instance_exec do
|
60
|
+
dbus_reader :foo, DBus::Type::STRING
|
61
|
+
end
|
62
|
+
end.to raise_error(DBus::Object::UndefinedInterface)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe ".dbus_reader, when paired with attr_accessor" do
|
67
|
+
describe "the declared assignment method" do
|
68
|
+
it "emits PropertyChanged" do
|
69
|
+
obj = ObjectTest.new("/test")
|
70
|
+
expect(obj).to receive(:PropertiesChanged).with(
|
71
|
+
"org.ruby.ServerTest",
|
72
|
+
{},
|
73
|
+
["ReadOnlyForDbus"]
|
74
|
+
)
|
75
|
+
obj.read_only_for_dbus = "myvalue"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe ".dbus_writer" do
|
81
|
+
it "can only be used within a dbus_interface" do
|
82
|
+
expect do
|
83
|
+
ObjectTest.instance_exec do
|
84
|
+
dbus_writer :foo, DBus::Type::STRING
|
85
|
+
end
|
86
|
+
end.to raise_error(DBus::Object::UndefinedInterface)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe ".dbus_watcher" do
|
91
|
+
it "can only be used within a dbus_interface" do
|
92
|
+
expect do
|
93
|
+
ObjectTest.instance_exec do
|
94
|
+
dbus_watcher :foo
|
95
|
+
end
|
96
|
+
end.to raise_error(DBus::Object::UndefinedInterface)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe ".dbus_method" do
|
101
|
+
it "can only be used within a dbus_interface" do
|
102
|
+
expect do
|
103
|
+
ObjectTest.instance_exec do
|
104
|
+
dbus_method :foo do
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end.to raise_error(DBus::Object::UndefinedInterface)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe ".emits_changed_signal" do
|
112
|
+
it "raises UndefinedInterface when so" do
|
113
|
+
expect { ObjectTest.emits_changed_signal = false }
|
114
|
+
.to raise_error DBus::Object::UndefinedInterface
|
115
|
+
end
|
116
|
+
|
117
|
+
it "assigns to the current interface" do
|
118
|
+
ObjectTest.instance_exec do
|
119
|
+
dbus_interface "org.ruby.Interface" do
|
120
|
+
self.emits_changed_signal = false
|
121
|
+
end
|
122
|
+
end
|
123
|
+
ecs = ObjectTest.intfs["org.ruby.Interface"].emits_changed_signal
|
124
|
+
expect(ecs).to eq false
|
125
|
+
end
|
126
|
+
|
127
|
+
it "only can be assigned once" do
|
128
|
+
expect do
|
129
|
+
Class.new(DBus::Object) do
|
130
|
+
dbus_interface "org.ruby.Interface" do
|
131
|
+
self.emits_changed_signal = false
|
132
|
+
self.emits_changed_signal = :invalidates
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end.to raise_error(RuntimeError, /assigned more than once/)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
data/spec/property_spec.rb
CHANGED
@@ -60,7 +60,7 @@ describe "PropertyTest" do
|
|
60
60
|
|
61
61
|
it "tests get all" do
|
62
62
|
all = @iface.all_properties
|
63
|
-
expect(all.keys.sort).to eq(["MyArray", "MyDict", "MyStruct", "MyVariant", "ReadMe", "ReadOrWriteMe"])
|
63
|
+
expect(all.keys.sort).to eq(["MyArray", "MyByte", "MyDict", "MyStruct", "MyVariant", "ReadMe", "ReadOrWriteMe"])
|
64
64
|
end
|
65
65
|
|
66
66
|
it "tests get all on a V1 object" do
|
@@ -68,7 +68,7 @@ describe "PropertyTest" do
|
|
68
68
|
iface = obj["org.ruby.SampleInterface"]
|
69
69
|
|
70
70
|
all = iface.all_properties
|
71
|
-
expect(all.keys.sort).to eq(["MyArray", "MyDict", "MyStruct", "MyVariant", "ReadMe", "ReadOrWriteMe"])
|
71
|
+
expect(all.keys.sort).to eq(["MyArray", "MyByte", "MyDict", "MyStruct", "MyVariant", "ReadMe", "ReadOrWriteMe"])
|
72
72
|
end
|
73
73
|
|
74
74
|
it "tests unknown property reading" do
|
@@ -110,6 +110,7 @@ describe "PropertyTest" do
|
|
110
110
|
end
|
111
111
|
|
112
112
|
@iface["ReadOrWriteMe"] = "VALUE"
|
113
|
+
@iface.SetTwoProperties("REAMDE", 255)
|
113
114
|
|
114
115
|
# loop to process the signal. complicated :-( see signal_spec.rb
|
115
116
|
loop = DBus::Main.new
|
@@ -123,6 +124,8 @@ describe "PropertyTest" do
|
|
123
124
|
quitter.join
|
124
125
|
|
125
126
|
expect(received["ReadOrWriteMe"]).to eq("VALUE")
|
127
|
+
expect(received["ReadMe"]).to eq("REAMDE")
|
128
|
+
expect(received["MyByte"]).to eq(255)
|
126
129
|
end
|
127
130
|
|
128
131
|
context "a struct-typed property" do
|
@@ -200,6 +203,36 @@ describe "PropertyTest" do
|
|
200
203
|
end
|
201
204
|
end
|
202
205
|
|
206
|
+
context "a byte-typed property" do
|
207
|
+
# Slightly advanced RSpec:
|
208
|
+
# https://rspec.info/documentation/3.9/rspec-expectations/RSpec/Matchers.html#satisfy-instance_method
|
209
|
+
let(:a_byte_in_a_variant) do
|
210
|
+
satisfying { |x| x.is_a?(DBus::Data::Variant) && x.member_type.to_s == DBus::Type::BYTE }
|
211
|
+
# ^ This formatting keeps the matcher on a single line
|
212
|
+
# which enables RSpec to cite it if it fails, instead of saying "block".
|
213
|
+
end
|
214
|
+
|
215
|
+
let(:prop_iface) { @obj[DBus::PROPERTY_INTERFACE] }
|
216
|
+
|
217
|
+
it "gets set with a correct type (#108)" do
|
218
|
+
expect(prop_iface).to receive(:Set).with(
|
219
|
+
"org.ruby.SampleInterface",
|
220
|
+
"MyByte",
|
221
|
+
a_byte_in_a_variant
|
222
|
+
)
|
223
|
+
@iface["MyByte"] = 1
|
224
|
+
end
|
225
|
+
|
226
|
+
it "gets set with a correct type (#108), when using the DBus.variant workaround" do
|
227
|
+
expect(prop_iface).to receive(:Set).with(
|
228
|
+
"org.ruby.SampleInterface",
|
229
|
+
"MyByte",
|
230
|
+
a_byte_in_a_variant
|
231
|
+
)
|
232
|
+
@iface["MyByte"] = DBus.variant("y", 1)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
203
236
|
context "marshall.yaml round-trip via a VARIANT property" do
|
204
237
|
marshall_yaml.each do |test|
|
205
238
|
t = OpenStruct.new(test)
|
data/spec/service_newapi.rb
CHANGED
@@ -28,6 +28,8 @@ class Test < DBus::Object
|
|
28
28
|
"three" => [3, 3, 3]
|
29
29
|
}
|
30
30
|
@my_variant = @my_array.dup
|
31
|
+
# 201 is a RET instruction for ZX Spectrum which has turned 40 recently
|
32
|
+
@my_byte = 201
|
31
33
|
@main_loop = nil
|
32
34
|
end
|
33
35
|
|
@@ -111,6 +113,17 @@ class Test < DBus::Object
|
|
111
113
|
dbus_attr_accessor :my_array, "aq"
|
112
114
|
dbus_attr_accessor :my_dict, "a{sv}"
|
113
115
|
dbus_attr_accessor :my_variant, "v"
|
116
|
+
|
117
|
+
dbus_attr_accessor :my_byte, "y"
|
118
|
+
|
119
|
+
# to test dbus_properties_changed
|
120
|
+
dbus_method :SetTwoProperties, "in read_me:s, in byte:y" do |read_me, byte|
|
121
|
+
@read_me = read_me
|
122
|
+
@my_byte = byte
|
123
|
+
dbus_properties_changed(INTERFACE,
|
124
|
+
{ "ReadMe" => read_me, "MyByte" => byte },
|
125
|
+
[])
|
126
|
+
end
|
114
127
|
end
|
115
128
|
|
116
129
|
# closing and reopening the same interface
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-dbus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.18.
|
4
|
+
version: 0.18.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ruby DBus Team
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-07-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rexml
|
@@ -146,6 +146,7 @@ files:
|
|
146
146
|
- lib/dbus/core_ext/class/attribute.rb
|
147
147
|
- lib/dbus/core_ext/module/redefine_method.rb
|
148
148
|
- lib/dbus/data.rb
|
149
|
+
- lib/dbus/emits_changed_signal.rb
|
149
150
|
- lib/dbus/error.rb
|
150
151
|
- lib/dbus/introspect.rb
|
151
152
|
- lib/dbus/logger.rb
|
@@ -172,12 +173,14 @@ files:
|
|
172
173
|
- spec/client_robustness_spec.rb
|
173
174
|
- spec/data/marshall.yaml
|
174
175
|
- spec/data_spec.rb
|
176
|
+
- spec/emits_changed_signal_spec.rb
|
175
177
|
- spec/err_msg_spec.rb
|
176
178
|
- spec/introspect_xml_parser_spec.rb
|
177
179
|
- spec/introspection_spec.rb
|
178
180
|
- spec/main_loop_spec.rb
|
179
181
|
- spec/node_spec.rb
|
180
182
|
- spec/object_path_spec.rb
|
183
|
+
- spec/object_spec.rb
|
181
184
|
- spec/packet_marshaller_spec.rb
|
182
185
|
- spec/packet_unmarshaller_spec.rb
|
183
186
|
- spec/property_spec.rb
|
@@ -202,7 +205,7 @@ homepage: https://github.com/mvidner/ruby-dbus
|
|
202
205
|
licenses:
|
203
206
|
- LGPL-2.1
|
204
207
|
metadata: {}
|
205
|
-
post_install_message:
|
208
|
+
post_install_message:
|
206
209
|
rdoc_options: []
|
207
210
|
require_paths:
|
208
211
|
- lib
|
@@ -213,12 +216,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
213
216
|
version: 2.4.0
|
214
217
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
215
218
|
requirements:
|
216
|
-
- - "
|
219
|
+
- - ">="
|
217
220
|
- !ruby/object:Gem::Version
|
218
|
-
version:
|
221
|
+
version: '0'
|
219
222
|
requirements: []
|
220
|
-
|
221
|
-
|
223
|
+
rubyforge_project:
|
224
|
+
rubygems_version: 2.7.6.3
|
225
|
+
signing_key:
|
222
226
|
specification_version: 4
|
223
227
|
summary: Ruby module for interaction with D-Bus
|
224
228
|
test_files: []
|