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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 54a45385340217271e3fb445cddf6afcddb9892c4bf75b503e7859390f4d2ed7
4
- data.tar.gz: 3c4f71745992c5afece87e34909e67f451717c9398ac6efb6235cc64ad1d1b0c
3
+ metadata.gz: 07f1f2fd26d13923b2601c9c3f29ccf57afd42c0348b937bceea0804eece6d66
4
+ data.tar.gz: 5ad80792234951e7ed422b6eba16e7f6e55b41f24a116ef40afaa7f32cdeef74
5
5
  SHA512:
6
- metadata.gz: 864aaaaa867ea04247515d875d836560a558b7a3d780712a41dcd52214d95e47e857d640d66a3573eaf4df778a0320f89ad2c59918c005a2fc80adb8a63074cb
7
- data.tar.gz: cabd9d699d8b7aad7c5f77a83aa07389b59c11b34b1219822909a031774b078d0bf001a1bce9bb5912be42caa4adba7608906ebc76dde57ff9f06d13afd7850f
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.0.beta6
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. A pair [{DBus::Type}, value] specifies to marshall *value* as
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
- 2. Other values are tried to fit one of these:
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
- 3. **Deprecated:** A pair [String, value], where String is a valid
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 (2) but happen to have
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
- each_pair do |k, _v|
174
+ each_key do |k|
175
175
  xml += " <node name=\"#{k}\" />\n"
176
176
  end
177
- @object&.intfs&.each_pair do |_k, v|
178
- xml += " <interface name=\"#{v.name}\">\n"
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
- raise ArgumentError, "Specified type is #{type} but value type is #{value.type}" \
605
- unless value.type == type
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
- raise ArgumentError, "Specified type is #{type} but value type is #{value.type}" \
655
- unless value.type == type
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
@@ -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 [String] The name of the property, for example FooBar.
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
- # object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {dbus_name.to_s => value}, [])
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
- # object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {}, [dbus_name.to_s])
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
- # Whenever the property value gets changed from "inside" the object,
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
- # object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {dbus_name.to_s => value}, [])
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
- # object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {}, [dbus_name.to_s])
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
- cur_intf = @@cur_intf
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
- # TODO: respect EmitsChangedSignal to use invalidated_properties instead
239
- # PropertiesChanged, "interface:s, changed_properties:a{sv}, invalidated_properties:as"
240
- PropertiesChanged(cur_intf.name, { dbus_name.to_s => value }, [])
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
- # The proxied methods contained in the interface.
19
- attr_accessor :methods
20
- # The proxied signals contained in the interface.
21
- attr_accessor :signals
22
- # The proxy object to which this interface belongs.
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 propname [String]
144
+ # @param property_name [String]
133
145
  # @param value [Object]
134
- def []=(propname, value)
135
- object[PROPERTY_INTERFACE].Set(name, propname, value)
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
- [type(string_type), value]
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
@@ -134,6 +134,10 @@ module DBus
134
134
  parse_methsig(se, s)
135
135
  i << s
136
136
  end
137
+ e.each("property") do |pe|
138
+ p = Property.from_xml(pe)
139
+ i << p
140
+ end
137
141
  end
138
142
  d = Time.now - t
139
143
  if d > 2
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
- ## [[1, 2, 3], { type: nil }, DBus::InvalidPacketException, "Unknown type code"],
402
- ## [[1, 2, 3], { type: "!" }, DBus::InvalidPacketException, "Unknown type code"]
403
- # TODO: others
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 .uuu./)
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 .uu./)
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
@@ -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
@@ -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)
@@ -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.0.beta6
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-05-25 00:00:00.000000000 Z
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: 1.3.1
221
+ version: '0'
219
222
  requirements: []
220
- rubygems_version: 3.3.0.dev
221
- signing_key:
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: []