ruby-dbus 0.18.0.beta4 → 0.18.0.beta5

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