ruby-dbus 0.18.0.beta7 → 0.18.0.beta8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 56815cd47f001f19c6d74cc02f0f1017e6e17d8df44e0949f020e4932adbc622
4
- data.tar.gz: 4aeda5f0c4972671adf6ae24554aa08ce5e6208846f87f5793562e73b8e7d632
3
+ metadata.gz: 6d6ace164222b10bbf03ab5ac9e2ec3799c8847c4a8f94e1d7f509944bf3e806
4
+ data.tar.gz: 9e8a61b9fa885b36ef21d04f4eb03d4d5a5ab8ec78d5ae258e2433a9e4e5cabe
5
5
  SHA512:
6
- metadata.gz: 2432d8ada23410027d00c7995c7514e3e927430844cf037fa8496509c47b10b4f5a431335762206bfc9966724d6d84014f57d9bbe31613bfd253145e25b82b77
7
- data.tar.gz: c1b480afebcdaf53b6fbe084f6150b7c9758d1329a207b4941dd8256cbc3786d42341564b3b8220d9e897e7487d281fede15eab8a31d81741582272519e1ac19
6
+ metadata.gz: a6f4e56b35c43975202769d8085cd690c95f345b09e5c78cd2e89f373105ee0e7edc0d9954ac3c647678a41c18ffb06dbaa806f9bed269b832ad6052a9449afe
7
+ data.tar.gz: 986c96e6a2f74668cd70e111c9fd57c4dd1c30a2e1b2ed66252406bc8fc67a6ec56bc663bae111b0e331e0f8544419d7fb4facabec1a14e1407ca903695d43c9
data/NEWS.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## Ruby D-Bus 0.18.0.beta8 - 2022-06-21
6
+
7
+ Bug fixes:
8
+ * Introduced Object#dbus_properties_changed to send correctly typed property
9
+ values ([#115][]). Avoid calling PropertiesChanged directly as it will
10
+ guess the types.
11
+ * Fix Object.dbus_reader to work with attr_accessor and automatically produce
12
+ dbus_properties_changed for properties that are read-write at
13
+ implementation side and read-only at D-Bus side ([#96][])
14
+
15
+ [#96]: https://github.com/mvidner/ruby-dbus/issues/96
16
+
17
+ API:
18
+ * Service side `emits_changed_signal` to control emission of
19
+ PropertiesChanged: can be assigned within `dbus_interface` or as an option
20
+ when declaring properties ([#117][]).
21
+
22
+ [#115]: https://github.com/mvidner/ruby-dbus/issues/115
23
+ [#117]: https://github.com/mvidner/ruby-dbus/pulls/117
24
+
5
25
  ## Ruby D-Bus 0.18.0.beta7 - 2022-05-29
6
26
 
7
27
  API:
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.18.0.beta7
1
+ 0.18.0.beta8
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
@@ -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|
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]
data/lib/dbus/type.rb CHANGED
@@ -415,7 +415,7 @@ 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.new} instead
418
+ # @deprecated Use {Data::Variant#initialize} instead
419
419
  def variant(string_type, value)
420
420
  Data::Variant.new(value, member_type: string_type)
421
421
  end
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"
@@ -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
@@ -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
@@ -206,7 +209,7 @@ describe "PropertyTest" do
206
209
  let(:a_byte_in_a_variant) do
207
210
  satisfying { |x| x.is_a?(DBus::Data::Variant) && x.member_type.to_s == DBus::Type::BYTE }
208
211
  # ^ This formatting keeps the matcher on a single line
209
- # which enables RSpect to cite it if it fails, instead of saying "block".
212
+ # which enables RSpec to cite it if it fails, instead of saying "block".
210
213
  end
211
214
 
212
215
  let(:prop_iface) { @obj[DBus::PROPERTY_INTERFACE] }
@@ -115,6 +115,15 @@ class Test < DBus::Object
115
115
  dbus_attr_accessor :my_variant, "v"
116
116
 
117
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
118
127
  end
119
128
 
120
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.beta7
4
+ version: 0.18.0.beta8
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-29 00:00:00.000000000 Z
11
+ date: 2022-06-21 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
@@ -217,8 +220,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
217
220
  - !ruby/object:Gem::Version
218
221
  version: 1.3.1
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: []