ruby-dbus 0.18.0.beta6 → 0.18.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|