ruby-dbus 0.18.0.beta1 → 0.18.0.beta4

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.
@@ -240,6 +240,7 @@ module DBus
240
240
  class Property
241
241
  # @return [String] The name of the property, for example FooBar.
242
242
  attr_reader :name
243
+ # @return [SingleCompleteType]
243
244
  attr_reader :type
244
245
  # @return [Symbol] :read :write or :readwrite
245
246
  attr_reader :access
data/lib/dbus/marshall.rb CHANGED
@@ -12,6 +12,8 @@
12
12
 
13
13
  require "socket"
14
14
 
15
+ require_relative "../dbus/type"
16
+
15
17
  # = D-Bus main module
16
18
  #
17
19
  # Module containing all the D-Bus modules and classes.
@@ -23,217 +25,130 @@ module DBus
23
25
  # = D-Bus packet unmarshaller class
24
26
  #
25
27
  # Class that handles the conversion (unmarshalling) of payload data
26
- # to Array.
28
+ # to #{::Object}s (in **plain** mode) or to {Data::Base} (in **exact** mode)
29
+ #
30
+ # Spelling note: this codebase always uses a double L
31
+ # in the "marshall" word and its inflections.
27
32
  class PacketUnmarshaller
28
- # Index pointer that points to the byte in the data that is
29
- # currently being processed.
30
- #
31
- # Used to kown what part of the buffer has been consumed by unmarshalling.
32
- # FIXME: Maybe should be accessed with a "consumed_size" method.
33
- attr_reader :idx
34
-
35
- # Create a new unmarshaller for the given data _buffer_ and _endianness_.
33
+ # Create a new unmarshaller for the given data *buffer*.
34
+ # @param buffer [String]
35
+ # @param endianness [:little,:big]
36
36
  def initialize(buffer, endianness)
37
- @buffy = buffer.dup
38
- @endianness = endianness
39
- case @endianness
40
- when BIG_END
41
- @uint32 = "N"
42
- @uint16 = "n"
43
- @double = "G"
44
- when LIL_END
45
- @uint32 = "V"
46
- @uint16 = "v"
47
- @double = "E"
48
- else
49
- raise InvalidPacketException, "Incorrect endianness #{@endianness}"
50
- end
51
- @idx = 0
37
+ # TODO: this dup can be avoided if we can prove
38
+ # that an IncompleteBufferException leaves the original *buffer* intact
39
+ buffer = buffer.dup
40
+ @raw_msg = RawMessage.new(buffer, endianness)
52
41
  end
53
42
 
54
43
  # Unmarshall the buffer for a given _signature_ and length _len_.
55
- # Return an array of unmarshalled objects
44
+ # Return an array of unmarshalled objects.
56
45
  # @param signature [Signature]
57
46
  # @param len [Integer,nil] if given, and there is not enough data
58
47
  # in the buffer, raise {IncompleteBufferException}
59
- # @return [Array<::Object>]
48
+ # @param mode [:plain,:exact]
49
+ # @return [Array<::Object,DBus::Data::Base>]
50
+ # Objects in `:plain` mode, {DBus::Data::Base} in `:exact` mode
51
+ # The array size corresponds to the number of types in *signature*.
60
52
  # @raise IncompleteBufferException
61
- def unmarshall(signature, len = nil)
62
- raise IncompleteBufferException if len && @buffy.bytesize < @idx + len
53
+ # @raise InvalidPacketException
54
+ def unmarshall(signature, len = nil, mode: :plain)
55
+ @raw_msg.want!(len) if len
63
56
 
64
57
  sigtree = Type::Parser.new(signature).parse
65
58
  ret = []
66
59
  sigtree.each do |elem|
67
- ret << do_parse(elem)
60
+ ret << do_parse(elem, mode: mode)
68
61
  end
69
62
  ret
70
63
  end
71
64
 
72
- # Align the pointer index on a byte index of _alignment_, which
73
- # must be 1, 2, 4 or 8.
74
- def align(alignment)
75
- case alignment
76
- when 1
77
- nil
78
- when 2, 4, 8
79
- bits = alignment - 1
80
- @idx = @idx + bits & ~bits
81
- raise IncompleteBufferException if @idx > @buffy.bytesize
82
- else
83
- raise ArgumentError, "Unsupported alignment #{alignment}"
84
- end
65
+ # after the headers, the body starts 8-aligned
66
+ def align_body
67
+ @raw_msg.align(8)
85
68
  end
86
69
 
87
- ###############################################################
88
- # FIXME: does anyone except the object itself call the above methods?
89
- # Yes : Message marshalling code needs to align "body" to 8 byte boundary
90
- private
70
+ # @return [Integer]
71
+ def consumed_size
72
+ @raw_msg.pos
73
+ end
91
74
 
92
- # Retrieve the next _nbytes_ number of bytes from the buffer.
93
- def read(nbytes)
94
- raise IncompleteBufferException if @idx + nbytes > @buffy.bytesize
75
+ private
95
76
 
96
- ret = @buffy.slice(@idx, nbytes)
97
- @idx += nbytes
98
- ret
77
+ # @param data_class [Class] a subclass of Data::Base (specific?)
78
+ # @return [::Integer,::Float]
79
+ def aligned_read_value(data_class)
80
+ @raw_msg.align(data_class.alignment)
81
+ bytes = @raw_msg.read(data_class.alignment)
82
+ bytes.unpack1(data_class.format[@raw_msg.endianness])
99
83
  end
100
84
 
101
- # Read the string length and string itself from the buffer.
102
- # Return the string.
103
- def read_string
104
- align(4)
105
- str_sz = read(4).unpack1(@uint32)
106
- ret = @buffy.slice(@idx, str_sz)
107
- raise IncompleteBufferException if @idx + str_sz + 1 > @buffy.bytesize
85
+ # Based on the _signature_ type, retrieve a packet from the buffer
86
+ # and return it.
87
+ # @param signature [Type]
88
+ # @param mode [:plain,:exact]
89
+ # @return [Data::Base]
90
+ def do_parse(signature, mode: :plain)
91
+ # FIXME: better naming for packet vs value
92
+ packet = nil
93
+ data_class = Data::BY_TYPE_CODE[signature.sigtype]
108
94
 
109
- @idx += str_sz
110
- if @buffy[@idx].ord != 0
111
- raise InvalidPacketException, "String is not nul-terminated"
95
+ if data_class.nil?
96
+ raise NotImplementedError,
97
+ "sigtype: #{signature.sigtype} (#{signature.sigtype.chr})"
112
98
  end
113
99
 
114
- @idx += 1
115
- # no exception, see check above
116
- ret
117
- end
100
+ if data_class.fixed?
101
+ value = aligned_read_value(data_class)
102
+ packet = data_class.from_raw(value, mode: mode)
103
+ elsif data_class.basic?
104
+ size = aligned_read_value(data_class.size_class)
105
+ # @raw_msg.align(data_class.alignment)
106
+ # ^ is not necessary because we've just read a suitably-aligned *size*
107
+ value = @raw_msg.read(size)
108
+ nul = @raw_msg.read(1)
109
+ if nul != "\u0000"
110
+ raise InvalidPacketException, "#{data_class} is not NUL-terminated"
111
+ end
118
112
 
119
- # Read the signature length and signature itself from the buffer.
120
- # Return the signature.
121
- def read_signature
122
- str_sz = read(1).unpack1("C")
123
- ret = @buffy.slice(@idx, str_sz)
124
- raise IncompleteBufferException if @idx + str_sz + 1 >= @buffy.bytesize
113
+ packet = data_class.from_raw(value, mode: mode)
114
+ else
115
+ @raw_msg.align(data_class.alignment)
116
+ case signature.sigtype
117
+ when Type::STRUCT, Type::DICT_ENTRY
118
+ values = signature.members.map do |child_sig|
119
+ do_parse(child_sig, mode: mode)
120
+ end
121
+ packet = data_class.from_items(values, mode: mode, member_types: signature.members)
125
122
 
126
- @idx += str_sz
127
- if @buffy[@idx].ord != 0
128
- raise InvalidPacketException, "Type is not nul-terminated"
129
- end
123
+ when Type::VARIANT
124
+ data_sig = do_parse(Data::Signature.type, mode: :exact) # -> Data::Signature
125
+ types = Type::Parser.new(data_sig.value).parse # -> Array<Type>
126
+ unless types.size == 1
127
+ raise InvalidPacketException, "VARIANT must contain 1 value, #{types.size} found"
128
+ end
130
129
 
131
- @idx += 1
132
- # no exception, see check above
133
- ret
134
- end
130
+ type = types.first
131
+ value = do_parse(type, mode: mode)
132
+ packet = data_class.from_items(value, mode: mode, member_type: type)
135
133
 
136
- # Based on the _signature_ type, retrieve a packet from the buffer
137
- # and return it.
138
- def do_parse(signature)
139
- packet = nil
140
- case signature.sigtype
141
- when Type::BYTE
142
- packet = read(1).unpack1("C")
143
- when Type::UINT16
144
- align(2)
145
- packet = read(2).unpack1(@uint16)
146
- when Type::INT16
147
- align(2)
148
- packet = read(2).unpack1(@uint16)
149
- if (packet & 0x8000) != 0
150
- packet -= 0x10000
151
- end
152
- when Type::UINT32, Type::UNIX_FD
153
- align(4)
154
- packet = read(4).unpack1(@uint32)
155
- when Type::INT32
156
- align(4)
157
- packet = read(4).unpack1(@uint32)
158
- if (packet & 0x80000000) != 0
159
- packet -= 0x100000000
160
- end
161
- when Type::UINT64
162
- align(8)
163
- packet_l = read(4).unpack1(@uint32)
164
- packet_h = read(4).unpack1(@uint32)
165
- packet = if @endianness == LIL_END
166
- packet_l + packet_h * 2**32
167
- else
168
- packet_l * 2**32 + packet_h
169
- end
170
- when Type::INT64
171
- align(8)
172
- packet_l = read(4).unpack1(@uint32)
173
- packet_h = read(4).unpack1(@uint32)
174
- packet = if @endianness == LIL_END
175
- packet_l + packet_h * 2**32
176
- else
177
- packet_l * 2**32 + packet_h
178
- end
179
- if (packet & 0x8000000000000000) != 0
180
- packet -= 0x10000000000000000
181
- end
182
- when Type::DOUBLE
183
- align(8)
184
- packet = read(8).unpack1(@double)
185
- when Type::BOOLEAN
186
- align(4)
187
- v = read(4).unpack1(@uint32)
188
- raise InvalidPacketException if ![0, 1].member?(v)
189
-
190
- packet = (v == 1)
191
- when Type::ARRAY
192
- align(4)
193
- # checks please
194
- array_sz = read(4).unpack1(@uint32)
195
- raise InvalidPacketException if array_sz > 67_108_864
196
-
197
- align(signature.child.alignment)
198
- raise IncompleteBufferException if @idx + array_sz > @buffy.bytesize
199
-
200
- packet = []
201
- start_idx = @idx
202
- while @idx - start_idx < array_sz
203
- packet << do_parse(signature.child)
204
- end
134
+ when Type::ARRAY
135
+ array_bytes = aligned_read_value(Data::UInt32)
136
+ if array_bytes > 67_108_864
137
+ raise InvalidPacketException, "ARRAY body longer than 64MiB"
138
+ end
205
139
 
206
- if signature.child.sigtype == Type::DICT_ENTRY
207
- packet = Hash[packet]
208
- end
209
- when Type::STRUCT
210
- align(8)
211
- packet = []
212
- signature.members.each do |elem|
213
- packet << do_parse(elem)
140
+ # needed here because of empty arrays
141
+ @raw_msg.align(signature.child.alignment)
142
+
143
+ items = []
144
+ end_pos = @raw_msg.pos + array_bytes
145
+ while @raw_msg.pos < end_pos
146
+ item = do_parse(signature.child, mode: mode)
147
+ items << item
148
+ end
149
+ is_hash = signature.child.sigtype == Type::DICT_ENTRY
150
+ packet = data_class.from_items(items, mode: mode, member_type: signature.child, hash: is_hash)
214
151
  end
215
- packet.freeze
216
- when Type::VARIANT
217
- string = read_signature
218
- # error checking please
219
- sig = Type::Parser.new(string).parse[0]
220
- align(sig.alignment)
221
- packet = do_parse(sig)
222
- when Type::OBJECT_PATH
223
- packet = read_string
224
- when Type::STRING
225
- packet = read_string
226
- packet.force_encoding("UTF-8")
227
- when Type::SIGNATURE
228
- packet = read_signature
229
- when Type::DICT_ENTRY
230
- align(8)
231
- key = do_parse(signature.members[0])
232
- value = do_parse(signature.members[1])
233
- packet = [key, value]
234
- else
235
- raise NotImplementedError,
236
- "sigtype: #{signature.sigtype} (#{signature.sigtype.chr})"
237
152
  end
238
153
  packet
239
154
  end
@@ -246,11 +161,16 @@ module DBus
246
161
  class PacketMarshaller
247
162
  # The current or result packet.
248
163
  # FIXME: allow access only when marshalling is finished
164
+ # @return [String]
249
165
  attr_reader :packet
250
166
 
167
+ # @return [:little,:big]
168
+ attr_reader :endianness
169
+
251
170
  # Create a new marshaller, setting the current packet to the
252
171
  # empty packet.
253
- def initialize(offset = 0)
172
+ def initialize(offset = 0, endianness: HOST_ENDIANNESS)
173
+ @endianness = endianness
254
174
  @packet = ""
255
175
  @offset = offset # for correct alignment of nested marshallers
256
176
  end
@@ -272,17 +192,6 @@ module DBus
272
192
  @packet = @packet.ljust(pad_count, 0.chr)
273
193
  end
274
194
 
275
- # Append the the string _str_ itself to the packet.
276
- def append_string(str)
277
- align(4)
278
- @packet += [str.bytesize].pack("L") + [str].pack("Z*")
279
- end
280
-
281
- # Append the the signature _signature_ itself to the packet.
282
- def append_signature(str)
283
- @packet += "#{str.bytesize.chr}#{str}\u0000"
284
- end
285
-
286
195
  # Append the array type _type_ to the packet and allow for appending
287
196
  # the child elements.
288
197
  def array(type)
@@ -296,7 +205,8 @@ module DBus
296
205
  sz = @packet.bytesize - contentidx
297
206
  raise InvalidPacketException if sz > 67_108_864
298
207
 
299
- @packet[sizeidx...sizeidx + 4] = [sz].pack("L")
208
+ sz_data = Data::UInt32.new(sz)
209
+ @packet[sizeidx...sizeidx + 4] = sz_data.marshall(endianness)
300
210
  end
301
211
 
302
212
  # Align and allow for appending struct fields.
@@ -308,84 +218,79 @@ module DBus
308
218
  # Append a value _val_ to the packet based on its _type_.
309
219
  #
310
220
  # Host native endianness is used, declared in Message#marshall
221
+ #
222
+ # @param type [SingleCompleteType] (or Integer or {Type})
223
+ # @param val [::Object]
311
224
  def append(type, val)
312
225
  raise TypeException, "Cannot send nil" if val.nil?
313
226
 
314
227
  type = type.chr if type.is_a?(Integer)
315
228
  type = Type::Parser.new(type).parse[0] if type.is_a?(String)
316
- case type.sigtype
317
- when Type::BYTE
318
- @packet += val.chr
319
- when Type::UINT32, Type::UNIX_FD
320
- align(4)
321
- @packet += [val].pack("L")
322
- when Type::UINT64
323
- align(8)
324
- @packet += [val].pack("Q")
325
- when Type::INT64
326
- align(8)
327
- @packet += [val].pack("q")
328
- when Type::INT32
329
- align(4)
330
- @packet += [val].pack("l")
331
- when Type::UINT16
332
- align(2)
333
- @packet += [val].pack("S")
334
- when Type::INT16
335
- align(2)
336
- @packet += [val].pack("s")
337
- when Type::DOUBLE
338
- align(8)
339
- @packet += [val].pack("d")
340
- when Type::BOOLEAN
341
- align(4)
342
- @packet += if val
343
- [1].pack("L")
344
- else
345
- [0].pack("L")
346
- end
347
- when Type::OBJECT_PATH
348
- append_string(val)
349
- when Type::STRING
350
- append_string(val)
351
- when Type::SIGNATURE
352
- append_signature(val)
353
- when Type::VARIANT
354
- append_variant(val)
355
- when Type::ARRAY
356
- append_array(type.child, val)
357
- when Type::STRUCT, Type::DICT_ENTRY
358
- unless val.is_a?(Array) || val.is_a?(Struct)
359
- type_name = Type::TYPE_MAPPING[type.sigtype].first
360
- raise TypeException, "#{type_name} expects an Array or Struct"
361
- end
229
+ # type is [Type] now
230
+ data_class = Data::BY_TYPE_CODE[type.sigtype]
231
+ if data_class.nil?
232
+ raise NotImplementedError,
233
+ "sigtype: #{type.sigtype} (#{type.sigtype.chr})"
234
+ end
362
235
 
363
- if type.sigtype == Type::DICT_ENTRY && val.size != 2
364
- raise TypeException, "DICT_ENTRY expects a pair"
365
- end
236
+ if data_class.fixed?
237
+ align(data_class.alignment)
238
+ data = data_class.new(val)
239
+ @packet += data.marshall(endianness)
240
+ elsif data_class.basic?
241
+ val = val.value if val.is_a?(Data::Basic)
242
+ align(data_class.size_class.alignment)
243
+ size_data = data_class.size_class.new(val.bytesize)
244
+ @packet += size_data.marshall(endianness)
245
+ # Z* makes a binary string, as opposed to interpolation
246
+ @packet += [val].pack("Z*")
247
+ else
248
+ case type.sigtype
249
+
250
+ when Type::VARIANT
251
+ append_variant(val)
252
+ when Type::ARRAY
253
+ val = val.value if val.is_a?(Data::Array)
254
+ append_array(type.child, val)
255
+ when Type::STRUCT, Type::DICT_ENTRY
256
+ val = val.value if val.is_a?(Data::Struct) || val.is_a?(Data::DictEntry)
257
+ unless val.is_a?(Array) || val.is_a?(Struct)
258
+ type_name = Type::TYPE_MAPPING[type.sigtype].first
259
+ raise TypeException, "#{type_name} expects an Array or Struct, seen #{val.class}"
260
+ end
366
261
 
367
- if type.members.size != val.size
368
- type_name = Type::TYPE_MAPPING[type.sigtype].first
369
- raise TypeException, "#{type_name} has #{val.size} elements but type info for #{type.members.size}"
370
- end
262
+ if type.sigtype == Type::DICT_ENTRY && val.size != 2
263
+ raise TypeException, "DICT_ENTRY expects a pair"
264
+ end
265
+
266
+ if type.members.size != val.size
267
+ type_name = Type::TYPE_MAPPING[type.sigtype].first
268
+ raise TypeException, "#{type_name} has #{val.size} elements but type info for #{type.members.size}"
269
+ end
371
270
 
372
- struct do
373
- type.members.zip(val).each do |t, v|
374
- append(t, v)
271
+ struct do
272
+ type.members.zip(val).each do |t, v|
273
+ append(t, v)
274
+ end
375
275
  end
276
+ else
277
+ raise NotImplementedError,
278
+ "sigtype: #{type.sigtype} (#{type.sigtype.chr})"
376
279
  end
377
- else
378
- raise NotImplementedError,
379
- "sigtype: #{type.sigtype} (#{type.sigtype.chr})"
380
280
  end
381
281
  end
382
282
 
383
283
  def append_variant(val)
384
284
  vartype = nil
385
- if val.is_a?(Array) && val.size == 2
285
+ if val.is_a?(DBus::Data::Base)
286
+ vartype = val.type # FIXME: box or unbox another variant?
287
+ vardata = val.value
288
+ elsif val.is_a?(Array) && val.size == 2
386
289
  case val[0]
387
- when DBus::Type::Type
290
+ when Type
388
291
  vartype, vardata = val
292
+ # Ambiguous but easy to use, because Type
293
+ # cannot construct "as" "a{sv}" easily
389
294
  when String
390
295
  begin
391
296
  parsed = Type::Parser.new(val[0]).parse
@@ -401,14 +306,14 @@ module DBus
401
306
  vartype = Type::Parser.new(vartype).parse[0]
402
307
  end
403
308
 
404
- append_signature(vartype.to_s)
309
+ append(Data::Signature.type, vartype.to_s)
405
310
  align(vartype.alignment)
406
- sub = PacketMarshaller.new(@offset + @packet.bytesize)
311
+ sub = PacketMarshaller.new(@offset + @packet.bytesize, endianness: endianness)
407
312
  sub.append(vartype, vardata)
408
313
  @packet += sub.packet
409
314
  end
410
315
 
411
- # @param child_type [DBus::Type::Type]
316
+ # @param child_type [Type]
412
317
  def append_array(child_type, val)
413
318
  if val.is_a?(Hash)
414
319
  raise TypeException, "Expected an Array but got a Hash" if child_type.sigtype != Type::DICT_ENTRY
@@ -449,15 +354,23 @@ module DBus
449
354
  elsif value.is_a? Hash
450
355
  h = {}
451
356
  value.each_key { |k| h[k] = make_variant(value[k]) }
452
- ["a{sv}", h]
357
+ key_type = if value.empty?
358
+ "s"
359
+ else
360
+ t, = make_variant(value.first.first)
361
+ t
362
+ end
363
+ ["a{#{key_type}v}", h]
453
364
  elsif value.respond_to? :to_str
454
365
  ["s", value.to_str]
455
366
  elsif value.respond_to? :to_int
456
367
  i = value.to_int
457
- if (-2_147_483_648...2_147_483_648).cover?(i)
368
+ if Data::Int32.range.cover?(i)
458
369
  ["i", i]
459
- else
370
+ elsif Data::Int64.range.cover?(i)
460
371
  ["x", i]
372
+ else
373
+ ["t", i]
461
374
  end
462
375
  end
463
376
  end
data/lib/dbus/message.rb CHANGED
@@ -172,7 +172,7 @@ module DBus
172
172
  @body_length = params.packet.bytesize
173
173
 
174
174
  marshaller = PacketMarshaller.new
175
- marshaller.append(Type::BYTE, HOST_END)
175
+ marshaller.append(Type::BYTE, HOST_END.ord)
176
176
  marshaller.append(Type::BYTE, @message_type)
177
177
  marshaller.append(Type::BYTE, @flags)
178
178
  marshaller.append(Type::BYTE, @protocol)
@@ -204,13 +204,7 @@ module DBus
204
204
  # the detected message (self) and
205
205
  # the index pointer of the buffer where the message data ended.
206
206
  def unmarshall_buffer(buf)
207
- buf = buf.dup
208
- endianness = if buf[0] == "l"
209
- LIL_END
210
- else
211
- BIG_END
212
- end
213
- pu = PacketUnmarshaller.new(buf, endianness)
207
+ pu = PacketUnmarshaller.new(buf, RawMessage.endianness(buf[0]))
214
208
  mdata = pu.unmarshall(MESSAGE_SIGNATURE)
215
209
  _, @message_type, @flags, @protocol, @body_length, @serial,
216
210
  headers = mdata
@@ -235,11 +229,11 @@ module DBus
235
229
  @signature = struct[1]
236
230
  end
237
231
  end
238
- pu.align(8)
232
+ pu.align_body
239
233
  if @body_length.positive? && @signature
240
234
  @params = pu.unmarshall(@signature, @body_length)
241
235
  end
242
- [self, pu.idx]
236
+ [self, pu.consumed_size]
243
237
  end
244
238
 
245
239
  # Make a new exception from ex, mark it as being caused by this message
data/lib/dbus/object.rb CHANGED
@@ -61,15 +61,7 @@ module DBus
61
61
  retdata = [*retdata]
62
62
 
63
63
  reply = Message.method_return(msg)
64
- if iface.name == PROPERTY_INTERFACE && member_sym == :Get
65
- # Use the specific property type instead of the generic variant
66
- # returned by Get.
67
- # TODO: GetAll and Set still missing
68
- property = dbus_lookup_property(msg.params[0], msg.params[1])
69
- rsigs = [property.type]
70
- else
71
- rsigs = meth.rets.map(&:type)
72
- end
64
+ rsigs = meth.rets.map(&:type)
73
65
  rsigs.zip(retdata).each do |rsig, rdata|
74
66
  reply.add_param(rsig, rdata)
75
67
  end
@@ -345,7 +337,9 @@ module DBus
345
337
  if property.readable?
346
338
  ruby_name = property.ruby_name
347
339
  value = public_send(ruby_name)
348
- [value]
340
+ # may raise, DBus.error or https://ruby-doc.com/core-3.1.0/TypeError.html
341
+ typed_value = Data.make_typed(property.type, value)
342
+ [typed_value]
349
343
  else
350
344
  raise DBus.error("org.freedesktop.DBus.Error.PropertyWriteOnly"),
351
345
  "Property '#{interface_name}.#{property_name}' (on object '#{@path}') is not readable"
@@ -357,6 +351,9 @@ module DBus
357
351
 
358
352
  if property.writable?
359
353
  ruby_name_eq = "#{property.ruby_name}="
354
+ # TODO: declare dbus_method :Set to take :exact argument
355
+ # and type check it here before passing its :plain value
356
+ # to the implementation
360
357
  public_send(ruby_name_eq, value)
361
358
  else
362
359
  raise DBus.error("org.freedesktop.DBus.Error.PropertyReadOnly"),
@@ -385,7 +382,10 @@ module DBus
385
382
  # > array.
386
383
  # so we will silently omit properties that fail to read.
387
384
  # Get'ting them individually will send DBus.Error
388
- p_hash[p_name.to_s] = public_send(ruby_name)
385
+ value = public_send(ruby_name)
386
+ # may raise, DBus.error or https://ruby-doc.com/core-3.1.0/TypeError.html
387
+ typed_value = Data.make_typed(property.type, value)
388
+ p_hash[p_name.to_s] = typed_value
389
389
  rescue StandardError
390
390
  DBus.logger.debug "Property '#{interface_name}.#{p_name}' (on object '#{@path}')" \
391
391
  " has raised during GetAll, omitting it"
@@ -9,7 +9,8 @@
9
9
  # See the file "COPYING" for the exact licensing terms.
10
10
 
11
11
  module DBus
12
- # A {::String} that validates at initialization time
12
+ # A {::String} that validates at initialization time.
13
+ # See also {DBus::Data::ObjectPath}
13
14
  # @see https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-marshaling-object-path
14
15
  class ObjectPath < String
15
16
  # @raise Error if not a valid object path