ruby-dbus 0.18.0.beta4 → 0.18.0.beta5

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: e6e093b55c88614ceebd2cfd0c09a5bba3886b8c561b8e153c034d9249ae5a08
4
- data.tar.gz: a2b2931d1b3b9ae560cc6204e297a7daceec2ec85a065a2782cbbeed34286f5d
3
+ metadata.gz: f8b0c43d3f27f0877e52c0adfb8014374829f97bcd38be6b9fabda1d5da8413c
4
+ data.tar.gz: 000eadf163b0fab2ef4ab39de81a79714b34392cd81b3be68e12587ddfa93372
5
5
  SHA512:
6
- metadata.gz: 66e7ad826a0f1ef5bb1abba72a6f76f02fbd4c13c7e7f14caf3abd71946ba4e5a0b9cc16950d60a22eb2d994d73c78306d270296af90f2bf9aa2aaaf4f85dab3
7
- data.tar.gz: 4641ca7b18414d7cf58d786ab05d3d3bc3a50d40311b1b5650b6f44a1bfcf0b1cca373eee4445b5cd6e0eb70a42407b244d93c09371d9ea985a66d6d4c79c0d7
6
+ metadata.gz: c2206dcd935fed4e3711b88b2c0c265e5a335700f9137d2c3972f419791d9c25198d0acf9ba7aaecdbc6a25e540005c64321e5165c6c644838fb3557355861ac
7
+ data.tar.gz: 212cb8bdcd75e3f35622843f262cdb80eaa07c1919a7d17ffdf601e05c627556343b7855a014f60a220c8a757c201431d66799a6025a98d084aef6cc12f7d8bb
data/NEWS.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## Ruby D-Bus 0.18.0.beta5 - 2022-04-27
6
+
7
+ API:
8
+ * DBus::Type instances are frozen.
9
+ * Data::Container classes (Array, Struct, DictEntry, but not Variant)
10
+ constructors (#initialize, .from_items, .from_typed) changed to have
11
+ a *type* argument instead of *member_type* or *member_types*.
12
+ * Added type factories
13
+ * Type::Array[type]
14
+ * Type::Hash[key_type, value_type]
15
+ * Type::Struct[type1, type2...]
16
+
17
+ Bug fixes:
18
+ * Properties containing Variants would return them doubly wrapped ([#111][]).
19
+
20
+ [#111]: https://github.com/mvidner/ruby-dbus/pull/111
21
+
5
22
  ## Ruby D-Bus 0.18.0.beta4 - 2022-04-21
6
23
 
7
24
  Bug fixes:
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.18.0.beta4
1
+ 0.18.0.beta5
data/lib/dbus/data.rb CHANGED
@@ -43,7 +43,7 @@ module DBus
43
43
  data_class = Data::BY_TYPE_CODE[type.sigtype]
44
44
  # not nil because DBus.type validates
45
45
 
46
- data_class.from_typed(value, member_types: type.members)
46
+ data_class.from_typed(value, type: type)
47
47
  end
48
48
  module_function :make_typed
49
49
 
@@ -67,6 +67,15 @@ module DBus
67
67
  # for the specific see {Variant#member_type}
68
68
  # @return [Type] the exact type of this value
69
69
 
70
+ # @!method self.from_typed(value, type:)
71
+ # @param value [::Object]
72
+ # @param type [Type]
73
+ # @return [Base]
74
+ # @api private
75
+ # Use {Data.make_typed} instead.
76
+ # Construct an instance of the specific subclass, with a type further
77
+ # specified in the *type* argument.
78
+
70
79
  # Child classes must validate *value*.
71
80
  def initialize(value)
72
81
  @value = value
@@ -83,6 +92,11 @@ module DBus
83
92
  # Hash key equality
84
93
  # See https://ruby-doc.org/core-3.0.0/Object.html#method-i-eql-3F
85
94
  alias eql? ==
95
+
96
+ # @param type [Type]
97
+ def self.assert_type_matches_class(type)
98
+ raise ArgumentError unless type.sigtype == type_code
99
+ end
86
100
  end
87
101
 
88
102
  # A value that is not a {Container}.
@@ -103,10 +117,10 @@ module DBus
103
117
  end
104
118
 
105
119
  # @param value [::Object]
106
- # @param member_types [::Array<Type>] (ignored, will be empty)
120
+ # @param type [Type]
107
121
  # @return [Basic]
108
- def self.from_typed(value, member_types:) # rubocop:disable Lint/UnusedMethodArgument
109
- # assert member_types.empty?
122
+ def self.from_typed(value, type:)
123
+ assert_type_matches_class(type)
110
124
  new(value)
111
125
  end
112
126
  end
@@ -132,34 +146,6 @@ module DBus
132
146
  end
133
147
  end
134
148
 
135
- # {DBus::Data::String}, {DBus::Data::ObjectPath}, or {DBus::Data::Signature}.
136
- class StringLike < Basic
137
- def self.fixed?
138
- false
139
- end
140
-
141
- def initialize(value)
142
- if value.is_a?(self.class)
143
- value = value.value
144
- else
145
- self.class.validate_raw!(value)
146
- end
147
-
148
- super(value)
149
- end
150
- end
151
-
152
- # Contains one or more other values.
153
- class Container < Base
154
- def self.basic?
155
- false
156
- end
157
-
158
- def self.fixed?
159
- false
160
- end
161
- end
162
-
163
149
  # Format strings for String#unpack, both little- and big-endian.
164
150
  Format = ::Struct.new(:little, :big)
165
151
 
@@ -398,6 +384,23 @@ module DBus
398
384
  end
399
385
  end
400
386
 
387
+ # {DBus::Data::String}, {DBus::Data::ObjectPath}, or {DBus::Data::Signature}.
388
+ class StringLike < Basic
389
+ def self.fixed?
390
+ false
391
+ end
392
+
393
+ def initialize(value)
394
+ if value.is_a?(self.class)
395
+ value = value.value
396
+ else
397
+ self.class.validate_raw!(value)
398
+ end
399
+
400
+ super(value)
401
+ end
402
+ end
403
+
401
404
  # UTF-8 encoded string.
402
405
  class String < StringLike
403
406
  def self.type_code
@@ -494,6 +497,21 @@ module DBus
494
497
  end
495
498
  end
496
499
 
500
+ # Contains one or more other values.
501
+ class Container < Base
502
+ def self.basic?
503
+ false
504
+ end
505
+
506
+ def self.fixed?
507
+ false
508
+ end
509
+
510
+ # For containers, the type varies among instances
511
+ # @see Base#type
512
+ attr_reader :type
513
+ end
514
+
497
515
  # An Array, or a Dictionary (Hash).
498
516
  class Array < Container
499
517
  def self.type_code
@@ -504,44 +522,32 @@ module DBus
504
522
  4
505
523
  end
506
524
 
507
- # @return [Type]
508
- attr_reader :member_type
509
-
510
- def type
511
- return @type if @type
512
-
513
- # TODO: reconstructing the type is cumbersome; have #initialize take *type* instead?
514
- # TODO: or rather add Type::Array[t]
515
- @type = Type.new("a")
516
- @type << member_type
517
- @type
518
- end
519
-
520
525
  # TODO: check that Hash keys are basic types
521
526
  # @param mode [:plain,:exact]
522
- # @param member_type [Type]
527
+ # @param type [Type]
523
528
  # @param hash [Boolean] are we unmarshalling an ARRAY of DICT_ENTRY
524
529
  # @return [Data::Array]
525
- def self.from_items(value, mode:, member_type:, hash: false)
530
+ def self.from_items(value, mode:, type:, hash: false)
526
531
  value = Hash[value] if hash
527
532
  return value if mode == :plain
528
533
 
529
- new(value, member_type: member_type)
534
+ new(value, type: type)
530
535
  end
531
536
 
532
537
  # @param value [::Object]
533
- # @param member_types [::Array<Type>]
538
+ # @param type [Type]
534
539
  # @return [Data::Array]
535
- def self.from_typed(value, member_types:)
540
+ def self.from_typed(value, type:)
541
+ assert_type_matches_class(type)
536
542
  # TODO: validation
537
- member_type = member_types.first
543
+ member_type = type.child
538
544
 
539
545
  # TODO: Dict??
540
546
  items = value.map do |i|
541
547
  Data.make_typed(member_type, i)
542
548
  end
543
549
 
544
- new(items, member_type: member_type) # initialize(::Array<Data::Base>)
550
+ new(items, type: type) # initialize(::Array<Data::Base>)
545
551
  end
546
552
 
547
553
  # FIXME: should Data::Array be mutable?
@@ -550,12 +556,12 @@ module DBus
550
556
  # TODO: specify type or guess type?
551
557
  # Data is the exact type, so its constructor should be exact
552
558
  # and guesswork should be clearly labeled
553
- # @param member_type [SingleCompleteType,Type]
554
- def initialize(value, member_type:)
555
- member_type = DBus.type(member_type) unless member_type.is_a?(Type)
559
+ # @param type [SingleCompleteType,Type]
560
+ def initialize(value, type:)
561
+ type = DBus.type(type) unless type.is_a?(Type)
562
+ self.class.assert_type_matches_class(type)
563
+ @type = type
556
564
  # TODO: copy from another Data::Array
557
- @member_type = member_type
558
- @type = nil
559
565
  super(value)
560
566
  end
561
567
  end
@@ -572,46 +578,32 @@ module DBus
572
578
  8
573
579
  end
574
580
 
575
- # @return [::Array<Type>]
576
- attr_reader :member_types
577
-
578
- def type
579
- return @type if @type
580
-
581
- # TODO: reconstructing the type is cumbersome; have #initialize take *type* instead?
582
- # TODO: or rather add Type::Struct[t1, t2, ...]
583
- @type = Type.new(self.class.type_code, abstract: true)
584
- @member_types.each do |member_type|
585
- @type << member_type
586
- end
587
- @type
588
- end
589
-
590
581
  # @param value [::Array]
591
- def self.from_items(value, mode:, member_types:)
582
+ def self.from_items(value, mode:, type:)
592
583
  value.freeze
593
584
  return value if mode == :plain
594
585
 
595
- new(value, member_types: member_types)
586
+ new(value, type: type)
596
587
  end
597
588
 
598
589
  # @param value [::Object] (#size, #each)
599
- # @param member_types [::Array<Type>]
590
+ # @param type [Type]
600
591
  # @return [Struct]
601
- def self.from_typed(value, member_types:)
592
+ def self.from_typed(value, type:)
602
593
  # TODO: validation
594
+ member_types = type.members
603
595
  raise unless value.size == member_types.size
604
596
 
605
597
  items = member_types.zip(value).map do |item_type, item|
606
598
  Data.make_typed(item_type, item)
607
599
  end
608
600
 
609
- new(items, member_types: member_types) # initialize(::Array<Data::Base>)
601
+ new(items, type: type) # initialize(::Array<Data::Base>)
610
602
  end
611
603
 
612
- def initialize(value, member_types:)
613
- @member_types = member_types
614
- @type = nil
604
+ def initialize(value, type:)
605
+ self.class.assert_type_matches_class(type)
606
+ @type = type
615
607
  super(value)
616
608
  end
617
609
  end
@@ -634,10 +626,10 @@ module DBus
634
626
  end
635
627
 
636
628
  # @param value [::Object]
637
- # @param member_types [::Array<Type>]
629
+ # @param type [Type]
638
630
  # @return [Variant]
639
- def self.from_typed(value, member_types:) # rubocop:disable Lint/UnusedMethodArgument
640
- # assert member_types.empty?
631
+ def self.from_typed(value, type:)
632
+ assert_type_matches_class(type)
641
633
 
642
634
  # decide on type of value
643
635
  new(value, member_type: nil)
@@ -690,31 +682,19 @@ module DBus
690
682
  8
691
683
  end
692
684
 
693
- # @return [::Array<Type>]
694
- attr_reader :member_types
695
-
696
- def type
697
- return @type if @type
698
-
699
- # TODO: reconstructing the type is cumbersome; have #initialize take *type* instead?
700
- @type = Type.new(self.class.type_code, abstract: true)
701
- @member_types.each do |member_type|
702
- @type << member_type
703
- end
704
- @type
705
- end
706
-
707
685
  # @param value [::Array]
708
- def self.from_items(value, mode:, member_types:) # rubocop:disable Lint/UnusedMethodArgument
686
+ def self.from_items(value, mode:, type:) # rubocop:disable Lint/UnusedMethodArgument
709
687
  value.freeze
710
688
  # DictEntry ignores the :exact mode
711
689
  value
712
690
  end
713
691
 
714
692
  # @param value [::Object] (#size, #each)
715
- # @param member_types [::Array<Type>]
693
+ # @param type [Type]
716
694
  # @return [DictEntry]
717
- def self.from_typed(value, member_types:)
695
+ def self.from_typed(value, type:)
696
+ assert_type_matches_class(type)
697
+ member_types = type.members
718
698
  # assert member_types.size == 2
719
699
  # TODO: duplicated from Struct. Inherit/delegate?
720
700
  # TODO: validation
@@ -724,12 +704,12 @@ module DBus
724
704
  Data.make_typed(item_type, item)
725
705
  end
726
706
 
727
- new(items, member_types: member_types) # initialize(::Array<Data::Base>)
707
+ new(items, type: type) # initialize(::Array<Data::Base>)
728
708
  end
729
709
 
730
- def initialize(value, member_types:)
731
- @member_types = member_types
732
- @type = nil
710
+ def initialize(value, type:)
711
+ self.class.assert_type_matches_class(type)
712
+ @type = type
733
713
  super(value)
734
714
  end
735
715
  end
data/lib/dbus/marshall.rb CHANGED
@@ -118,7 +118,7 @@ module DBus
118
118
  values = signature.members.map do |child_sig|
119
119
  do_parse(child_sig, mode: mode)
120
120
  end
121
- packet = data_class.from_items(values, mode: mode, member_types: signature.members)
121
+ packet = data_class.from_items(values, mode: mode, type: signature)
122
122
 
123
123
  when Type::VARIANT
124
124
  data_sig = do_parse(Data::Signature.type, mode: :exact) # -> Data::Signature
@@ -147,7 +147,7 @@ module DBus
147
147
  items << item
148
148
  end
149
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)
150
+ packet = data_class.from_items(items, mode: mode, type: signature, hash: is_hash)
151
151
  end
152
152
  end
153
153
  packet
@@ -282,8 +282,11 @@ module DBus
282
282
 
283
283
  def append_variant(val)
284
284
  vartype = nil
285
- if val.is_a?(DBus::Data::Base)
286
- vartype = val.type # FIXME: box or unbox another variant?
285
+ if val.is_a?(DBus::Data::Variant)
286
+ vartype = val.member_type
287
+ vardata = val.value
288
+ elsif val.is_a?(DBus::Data::Base)
289
+ vartype = val.type
287
290
  vardata = val.value
288
291
  elsif val.is_a?(Array) && val.size == 2
289
292
  case val[0]
data/lib/dbus/type.rb CHANGED
@@ -29,14 +29,12 @@ module DBus
29
29
  # For documentation purposes only.
30
30
  class Prototype < String; end
31
31
 
32
- # = D-Bus type module
33
- #
34
- # This module containts the constants of the types specified in the D-Bus
35
- # protocol.
32
+ # Represents the D-Bus types.
36
33
  #
37
34
  # Corresponds to {SingleCompleteType}.
35
+ # Instances are immutable/frozen once fully constructed.
38
36
  #
39
- # See also {DBus::Data::Signature}
37
+ # See also {DBus::Data::Signature} which is "type on the wire".
40
38
  class Type
41
39
  # Mapping from type number to name and alignment.
42
40
  TYPE_MAPPING = {
@@ -104,8 +102,9 @@ module DBus
104
102
  end
105
103
  end
106
104
 
107
- @sigtype = sigtype
108
- @members = []
105
+ @sigtype = sigtype.freeze
106
+ @members = [] # not frozen yet, Parser#parse_one or Factory will do it
107
+ freeze
109
108
  end
110
109
 
111
110
  # Return the required alignment for the type.
@@ -124,16 +123,15 @@ module DBus
124
123
  when DICT_ENTRY
125
124
  "{#{@members.collect(&:to_s).join}}"
126
125
  else
127
- if !TYPE_MAPPING.keys.member?(@sigtype)
128
- raise NotImplementedError
129
- end
130
-
131
126
  @sigtype.chr
132
127
  end
133
128
  end
134
129
 
135
130
  # Add a new member type _item_.
131
+ # @param item [Type]
136
132
  def <<(item)
133
+ raise ArgumentError unless item.is_a?(Type)
134
+
137
135
  if ![STRUCT, ARRAY, DICT_ENTRY].member?(@sigtype)
138
136
  raise SignatureException
139
137
  end
@@ -232,6 +230,7 @@ module DBus
232
230
  else
233
231
  res = Type.new(char)
234
232
  end
233
+ res.members.freeze
235
234
  res
236
235
  end
237
236
 
@@ -243,7 +242,7 @@ module DBus
243
242
  while (c = nextchar)
244
243
  ret << parse_one(c)
245
244
  end
246
- ret
245
+ ret.freeze
247
246
  end
248
247
 
249
248
  # Parse one {SingleCompleteType}
@@ -255,9 +254,116 @@ module DBus
255
254
  t = parse_one(c)
256
255
  raise SignatureException, "Has more than a Single Complete Type: #{@signature}" unless nextchar.nil?
257
256
 
257
+ t.freeze
258
+ end
259
+ end
260
+
261
+ class Factory
262
+ # @param type [Type,SingleCompleteType,Class]
263
+ # @see from_plain_class
264
+ # @return [Type] (frozen)
265
+ def self.make_type(type)
266
+ case type
267
+ when Type
268
+ type
269
+ when String
270
+ DBus.type(type)
271
+ when Class
272
+ from_plain_class(type)
273
+ else
274
+ msg = "Expecting DBus::Type, DBus::SingleCompleteType(aka ::String), or Class, got #{type.inspect}"
275
+ raise ArgumentError, msg
276
+ end
277
+ end
278
+
279
+ # Make a {Type} corresponding to some plain classes:
280
+ # - String
281
+ # - Float
282
+ # - DBus::ObjectPath
283
+ # - DBus::Signature, DBus::SingleCompleteType
284
+ # @param klass [Class]
285
+ # @return [Type] (frozen)
286
+ def self.from_plain_class(klass)
287
+ @signature_type ||= DBus.type(SIGNATURE)
288
+ @class_to_type ||= {
289
+ DBus::ObjectPath => DBus.type(OBJECT_PATH),
290
+ DBus::Signature => @signature_type,
291
+ DBus::SingleCompleteType => @signature_type,
292
+ String => DBus.type(STRING),
293
+ Float => DBus.type(DOUBLE)
294
+ }
295
+ t = @class_to_type[klass]
296
+ raise ArgumentError, "Cannot convert plain class #{klass} to a D-Bus type" if t.nil?
297
+
298
+ t
299
+ end
300
+ end
301
+
302
+ # Syntactic helper for constructing an array Type.
303
+ # You may be looking for {Data::Array} instead.
304
+ # @example
305
+ # t = Type::Array[Type::INT16]
306
+ class ArrayFactory < Factory
307
+ # @param member_type [Type,SingleCompleteType]
308
+ # @return [Type] (frozen)
309
+ def self.[](member_type)
310
+ t = Type.new(ARRAY)
311
+ t << make_type(member_type)
312
+ t.members.freeze
313
+ t
314
+ end
315
+ end
316
+
317
+ # @example
318
+ # t = Type::Array[Type::INT16]
319
+ Array = ArrayFactory
320
+
321
+ # Syntactic helper for constructing a hash Type.
322
+ # You may be looking for {Data::Array} and {Data::DictEntry} instead.
323
+ # @example
324
+ # t = Type::Hash[Type::STRING, Type::VARIANT]
325
+ class HashFactory < Factory
326
+ # @param key_type [Type,SingleCompleteType]
327
+ # @param value_type [Type,SingleCompleteType]
328
+ # @return [Type] (frozen)
329
+ def self.[](key_type, value_type)
330
+ t = Type.new(ARRAY)
331
+ de = Type.new(DICT_ENTRY, abstract: true)
332
+ de << make_type(key_type)
333
+ de << make_type(value_type)
334
+ de.members.freeze
335
+ t << de
336
+ t.members.freeze
258
337
  t
259
338
  end
260
339
  end
340
+
341
+ # @example
342
+ # t = Type::Hash[Type::INT16]
343
+ Hash = HashFactory
344
+
345
+ # Syntactic helper for constructing a struct Type.
346
+ # You may be looking for {Data::Struct} instead.
347
+ # @example
348
+ # t = Type::Struct[Type::INT16, Type::STRING]
349
+ class StructFactory < Factory
350
+ # @param member_types [::Array<Type,SingleCompleteType>]
351
+ # @return [Type] (frozen)
352
+ def self.[](*member_types)
353
+ raise ArgumentError if member_types.empty?
354
+
355
+ t = Type.new(STRUCT, abstract: true)
356
+ member_types.each do |mt|
357
+ t << make_type(mt)
358
+ end
359
+ t.members.freeze
360
+ t
361
+ end
362
+ end
363
+
364
+ # @example
365
+ # t = Type::Struct[Type::INT16, Type::STRING]
366
+ Struct = StructFactory
261
367
  end
262
368
 
263
369
  # shortcuts
@@ -266,7 +372,7 @@ module DBus
266
372
  # This is prefered to {Type#initialize} which allows
267
373
  # incomplete or invalid types.
268
374
  # @param string_type [SingleCompleteType]
269
- # @return [DBus::Type]
375
+ # @return [DBus::Type] (frozen)
270
376
  # @raise SignatureException
271
377
  def type(string_type)
272
378
  Type::Parser.new(string_type).parse1
@@ -275,7 +381,7 @@ module DBus
275
381
 
276
382
  # Parse a String to zero or more {DBus::Type}s.
277
383
  # @param string_type [Signature]
278
- # @return [Array<DBus::Type>]
384
+ # @return [Array<DBus::Type>] (frozen)
279
385
  # @raise SignatureException
280
386
  def types(string_type)
281
387
  Type::Parser.new(string_type).parse
data/spec/data_spec.rb CHANGED
@@ -85,6 +85,8 @@ end
85
85
  # TODO: Look at conversions? to_str, to_int?
86
86
 
87
87
  describe DBus::Data do
88
+ T = DBus::Type unless const_defined? "T"
89
+
88
90
  # test initialization, from user code, or from packet (from_raw)
89
91
  # remember to unpack if initializing from Data::Base
90
92
  # #value should recurse inside so that the user doesnt have to
@@ -245,18 +247,18 @@ describe DBus::Data do
245
247
  describe "containers" do
246
248
  describe DBus::Data::Array do
247
249
  good = [
248
- # [[1, 2, 3], member_type: nil],
249
- [[1, 2, 3], { member_type: "q" }],
250
- [[1, 2, 3], { member_type: DBus::Type::UINT16 }],
251
- [[1, 2, 3], { member_type: DBus.type("q") }],
252
- [[DBus::Data::UInt16.new(1), DBus::Data::UInt16.new(2), DBus::Data::UInt16.new(3)], { member_type: "q" }]
250
+ # [[1, 2, 3], type: nil],
251
+ [[1, 2, 3], { type: "aq" }],
252
+ [[1, 2, 3], { type: T::Array[T::UINT16] }],
253
+ [[1, 2, 3], { type: T::Array["q"] }],
254
+ [[DBus::Data::UInt16.new(1), DBus::Data::UInt16.new(2), DBus::Data::UInt16.new(3)], { type: T::Array["q"] }]
253
255
  # TODO: others
254
256
  ]
255
257
 
256
258
  bad = [
257
259
  # undesirable type guessing
258
- ## [[1, 2, 3], { member_type: nil }, DBus::InvalidPacketException, "Unknown type code"],
259
- ## [[1, 2, 3], { member_type: "!" }, DBus::InvalidPacketException, "Unknown type code"]
260
+ ## [[1, 2, 3], { type: nil }, DBus::InvalidPacketException, "Unknown type code"],
261
+ ## [[1, 2, 3], { type: "!" }, DBus::InvalidPacketException, "Unknown type code"]
260
262
  # TODO: others
261
263
  ]
262
264
 
@@ -265,8 +267,8 @@ describe DBus::Data do
265
267
 
266
268
  describe ".from_typed" do
267
269
  it "creates new instance from given object and type" do
268
- type = DBus::Type.new("s")
269
- expect(described_class.from_typed(["test", "lest"], member_types: [type])).to be_a(described_class)
270
+ type = T::Array[String]
271
+ expect(described_class.from_typed(["test", "lest"], type: type)).to be_a(described_class)
270
272
  end
271
273
  end
272
274
  end
@@ -274,7 +276,7 @@ describe DBus::Data do
274
276
  describe DBus::Data::Struct do
275
277
  three_words = ::Struct.new(:a, :b, :c)
276
278
 
277
- qqq = ["q", "q", "q"]
279
+ qqq = T::Struct[T::UINT16, T::UINT16, T::UINT16]
278
280
  integers = [1, 2, 3]
279
281
  uints = [DBus::Data::UInt16.new(1), DBus::Data::UInt16.new(2), DBus::Data::UInt16.new(3)]
280
282
 
@@ -291,28 +293,25 @@ describe DBus::Data do
291
293
  # TODO: also check data ownership: reasonable to own the data?
292
294
  # can make it explicit?
293
295
  good = [
294
- # from plain array; various m_t styles
295
- [integers, { member_types: ["q", "q", "q"] }],
296
- [integers, { member_types: [DBus::Type::UINT16, DBus::Type::UINT16, DBus::Type::UINT16] }],
297
- [integers, { member_types: DBus.types("qqq") }],
296
+ # from plain array; various *type* styles
297
+ [integers, { type: DBus.type("(qqq)") }],
298
+ [integers, { type: T::Struct["q", "q", "q"] }],
299
+ [integers, { type: T::Struct[T::UINT16, T::UINT16, T::UINT16] }],
300
+ [integers, { type: T::Struct[*DBus.types("qqq")] }],
298
301
  # plain array of data
299
- [uints, { member_types: DBus.types("qqq") }],
302
+ [uints, { type: qqq }],
300
303
  # ::Struct
301
- [three_words.new(*integers), { member_types: qqq }],
302
- [three_words.new(*uints), { member_types: qqq }]
304
+ [three_words.new(*integers), { type: qqq }],
305
+ [three_words.new(*uints), { type: qqq }]
303
306
  # TODO: others
304
307
  ]
305
308
 
309
+ # check these only when canonicalizing @value, because that will
310
+ # type-check the value deeply
306
311
  _bad_but_valid = [
307
- # Wrong member_types arg:
308
- # hmm this is another reason to pass the type
309
- # as the entire struct type, not the members:
310
- # empty struct will be caught naturally
311
- [integers, { member_types: [] }, ArgumentError, "???"],
312
- [integers, { member_types: ["!"] }, DBus::InvalidPacketException, "Unknown type code"],
313
312
  # STRUCT specific: member count mismatch
314
- [[1, 2], { member_types: DBus.types("qqq") }, ArgumentError, "???"],
315
- [[1, 2, 3, 4], { member_types: DBus.types("qqq") }, ArgumentError, "???"]
313
+ [[1, 2], { type: qqq }, ArgumentError, "???"],
314
+ [[1, 2, 3, 4], { type: qqq }, ArgumentError, "???"]
316
315
  # TODO: others
317
316
  ]
318
317
 
@@ -321,8 +320,8 @@ describe DBus::Data do
321
320
 
322
321
  describe ".from_typed" do
323
322
  it "creates new instance from given object and type" do
324
- type = DBus::Type.new("s")
325
- expect(described_class.from_typed(["test", "lest"].freeze, member_types: [type, type]))
323
+ type = T::Struct[T::STRING, T::STRING]
324
+ expect(described_class.from_typed(["test", "lest"].freeze, type: type))
326
325
  .to be_a(described_class)
327
326
  end
328
327
  end
@@ -331,16 +330,9 @@ describe DBus::Data do
331
330
  describe DBus::Data::Variant do
332
331
  describe ".from_typed" do
333
332
  it "creates new instance from given object and type" do
334
- type = DBus::Type.new("s")
335
- expect(described_class.from_typed("test", member_types: [type])).to be_a(described_class)
336
- end
337
-
338
- it "ignores the member_types argument" do
339
- type = DBus::Type.new("s")
340
- # Base.from_typed is a generic interface with a fixed signature;
341
- # So it must offer the member_types parameter, which is misleading
342
- # for a Variant
343
- value = described_class.from_typed("test", member_types: [type])
333
+ type = DBus.type(T::VARIANT)
334
+ value = described_class.from_typed("test", type: type)
335
+ expect(value).to be_a(described_class)
344
336
  expect(value.type.to_s).to eq "v"
345
337
  expect(value.member_type.to_s).to eq "s"
346
338
  end
@@ -163,7 +163,7 @@ describe "PropertyTest" do
163
163
  end
164
164
  end
165
165
 
166
- context "an dict-typed property" do
166
+ context "a dict-typed property" do
167
167
  it "gets read as a hash" do
168
168
  val = @iface["MyDict"]
169
169
  expect(val).to eq({
@@ -172,6 +172,23 @@ describe "PropertyTest" do
172
172
  "three" => [3, 3, 3]
173
173
  })
174
174
  end
175
+
176
+ it "Get returns the correctly typed value (check with dbus-send)" do
177
+ cmd = "dbus-send --print-reply " \
178
+ "--dest=org.ruby.service " \
179
+ "/org/ruby/MyInstance " \
180
+ "org.freedesktop.DBus.Properties.Get " \
181
+ "string:org.ruby.SampleInterface " \
182
+ "string:MyDict"
183
+ reply = `#{cmd}`
184
+ # a bug about variant nesting lead to a "variant variant int32 1" value
185
+ match_rx = /variant \s+ array \s \[ \s+
186
+ dict \s entry\( \s+
187
+ string \s "one" \s+
188
+ variant \s+ int32 \s 1 \s+
189
+ \)/x
190
+ expect(reply).to match(match_rx)
191
+ end
175
192
  end
176
193
 
177
194
  context "a variant-typed property" do
data/spec/type_spec.rb CHANGED
@@ -45,6 +45,7 @@ describe DBus do
45
45
  ["a{vs}", "DICT_ENTRY key must be basic (non-container)"],
46
46
  ["{sv}", "DICT_ENTRY not an immediate child of an ARRAY"],
47
47
  ["a({sv})", "DICT_ENTRY not an immediate child of an ARRAY"],
48
+ ["a{s", "DICT_ENTRY not closed"],
48
49
  ["a{sv", "DICT_ENTRY not closed"],
49
50
  ["}", "DICT_ENTRY unexpectedly closed"],
50
51
 
@@ -79,4 +80,108 @@ describe DBus do
79
80
  end
80
81
  end
81
82
  end
83
+
84
+ describe DBus::Type do
85
+ describe "#<<" do
86
+ it "raises if the argument is not a Type" do
87
+ t = DBus::Type.new(DBus::Type::ARRAY)
88
+ expect { t << "s" }.to raise_error(ArgumentError)
89
+ end
90
+
91
+ # TODO: the following raise checks do not occur in practice, as there are
92
+ # parallel checks in the parses. The code could be simplified?
93
+ it "raises if adding too much to an array" do
94
+ t = DBus::Type.new(DBus::Type::ARRAY)
95
+ b = DBus::Type.new(DBus::Type::BOOLEAN)
96
+ t << b
97
+ expect { t << b }.to raise_error(DBus::Type::SignatureException)
98
+ end
99
+
100
+ it "raises if adding too much to a dict_entry" do
101
+ t = DBus::Type.new(DBus::Type::DICT_ENTRY, abstract: true)
102
+ b = DBus::Type.new(DBus::Type::BOOLEAN)
103
+ t << b
104
+ t << b
105
+ expect { t << b }.to raise_error(DBus::Type::SignatureException)
106
+ end
107
+
108
+ it "raises if adding to a non-container" do
109
+ t = DBus::Type.new(DBus::Type::STRING)
110
+ b = DBus::Type.new(DBus::Type::BOOLEAN)
111
+ expect { t << b }.to raise_error(DBus::Type::SignatureException)
112
+
113
+ t = DBus::Type.new(DBus::Type::VARIANT)
114
+ expect { t << b }.to raise_error(DBus::Type::SignatureException)
115
+ end
116
+ end
117
+
118
+ describe DBus::Type::Array do
119
+ describe ".[]" do
120
+ it "takes Type argument" do
121
+ t = DBus::Type::Array[DBus::Type.new("s")]
122
+ expect(t.to_s).to eq "as"
123
+ end
124
+
125
+ it "takes 's':String argument" do
126
+ t = DBus::Type::Array["s"]
127
+ expect(t.to_s).to eq "as"
128
+ end
129
+
130
+ it "takes String:Class argument" do
131
+ t = DBus::Type::Array[String]
132
+ expect(t.to_s).to eq "as"
133
+ end
134
+
135
+ it "rejects Integer:Class argument" do
136
+ expect { DBus::Type::Array[Integer] }.to raise_error(ArgumentError)
137
+ end
138
+
139
+ it "rejects /./:Regexp argument" do
140
+ expect { DBus::Type::Array[/./] }.to raise_error(ArgumentError)
141
+ end
142
+ end
143
+ end
144
+
145
+ describe DBus::Type::Hash do
146
+ describe ".[]" do
147
+ it "takes Type arguments" do
148
+ t = DBus::Type::Hash[DBus::Type.new("s"), DBus::Type.new("v")]
149
+ expect(t.to_s).to eq "a{sv}"
150
+ end
151
+
152
+ it "takes 's':String arguments" do
153
+ t = DBus::Type::Hash["s", "v"]
154
+ expect(t.to_s).to eq "a{sv}"
155
+ end
156
+
157
+ it "takes String:Class argument" do
158
+ t = DBus::Type::Hash[String, DBus::Type::VARIANT]
159
+ expect(t.to_s).to eq "a{sv}"
160
+ end
161
+ end
162
+ end
163
+
164
+ describe DBus::Type::Struct do
165
+ describe ".[]" do
166
+ it "takes Type arguments" do
167
+ t = DBus::Type::Struct[DBus::Type.new("s"), DBus::Type.new("v")]
168
+ expect(t.to_s).to eq "(sv)"
169
+ end
170
+
171
+ it "takes 's':String arguments" do
172
+ t = DBus::Type::Struct["s", "v"]
173
+ expect(t.to_s).to eq "(sv)"
174
+ end
175
+
176
+ it "takes String:Class argument" do
177
+ t = DBus::Type::Struct[String, DBus::Type::VARIANT]
178
+ expect(t.to_s).to eq "(sv)"
179
+ end
180
+
181
+ it "raises on no arguments" do
182
+ expect { DBus::Type::Struct[] }.to raise_error(ArgumentError)
183
+ end
184
+ end
185
+ end
186
+ end
82
187
  end
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.beta4
4
+ version: 0.18.0.beta5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ruby DBus Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-04-21 00:00:00.000000000 Z
11
+ date: 2022-04-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rexml