ruby-dbus 0.18.0.beta7 → 0.18.0.beta8

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: 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: []