ruby-dbus 0.18.0.beta3 → 0.18.0.beta6

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: aefe0e0b1ea07e4d659c568e496f0018e85ef0364d678169db736f967539175b
4
- data.tar.gz: 44c64528e74823a45d318be92b6fc8838b988429c2f1205287682ee40b9a8f38
3
+ metadata.gz: 54a45385340217271e3fb445cddf6afcddb9892c4bf75b503e7859390f4d2ed7
4
+ data.tar.gz: 3c4f71745992c5afece87e34909e67f451717c9398ac6efb6235cc64ad1d1b0c
5
5
  SHA512:
6
- metadata.gz: e6655ff88bcbda618eafc3ec62f74f4a2051a5f6327cd130fddd8a3aaff4b8e999fa2843edde8fd6de9cdb0df95fa6dfd56bf18c1e668ac92ffd1e131f58c9f6
7
- data.tar.gz: 4c54cad54ea4833afff8290598c8717a30cb0b221b486378d06070e010b162e54a2dcaa08cb542b21f91cf7dd8ee215646c8461504b676e977b597e325a5b7ae
6
+ metadata.gz: 864aaaaa867ea04247515d875d836560a558b7a3d780712a41dcd52214d95e47e857d640d66a3573eaf4df778a0320f89ad2c59918c005a2fc80adb8a63074cb
7
+ data.tar.gz: cabd9d699d8b7aad7c5f77a83aa07389b59c11b34b1219822909a031774b078d0bf001a1bce9bb5912be42caa4adba7608906ebc76dde57ff9f06d13afd7850f
data/NEWS.md CHANGED
@@ -2,6 +2,44 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## Ruby D-Bus 0.18.0.beta6 - 2022-05-25
6
+
7
+ API:
8
+ * Data::Base#value returns plain Ruby types;
9
+ Data::Container#exact_value contains Data::Base ([#114][]).
10
+ * Data::Base#initialize and .from_typed allow plain or exact values, validate
11
+ argument types.
12
+ * Implement #== (converting) and #eql? (strict) for Data::Base and DBus::Type.
13
+
14
+ [#114]: https://github.com/mvidner/ruby-dbus/pull/114
15
+
16
+ ## Ruby D-Bus 0.18.0.beta5 - 2022-04-27
17
+
18
+ API:
19
+ * DBus::Type instances are frozen.
20
+ * Data::Container classes (Array, Struct, DictEntry, but not Variant)
21
+ constructors (#initialize, .from_items, .from_typed) changed to have
22
+ a *type* argument instead of *member_type* or *member_types*.
23
+ * Added type factories
24
+ * Type::Array[type]
25
+ * Type::Hash[key_type, value_type]
26
+ * Type::Struct[type1, type2...]
27
+
28
+ Bug fixes:
29
+ * Properties containing Variants would return them doubly wrapped ([#111][]).
30
+
31
+ [#111]: https://github.com/mvidner/ruby-dbus/pull/111
32
+
33
+ ## Ruby D-Bus 0.18.0.beta4 - 2022-04-21
34
+
35
+ Bug fixes:
36
+ * Service-side properties: Fix Properties.Get, Properties.GetAll for
37
+ properties that contain arrays, on other than outermost level ([#109][]).
38
+ * Sending variants: fixed make_variant to correctly guess the signature
39
+ for UInt64 and number-keyed hashes/dictionaries.
40
+
41
+ [#109]: https://github.com/mvidner/ruby-dbus/pull/109
42
+
5
43
  ## Ruby D-Bus 0.18.0.beta3 - 2022-04-10
6
44
 
7
45
  Bug fixes:
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.18.0.beta3
1
+ 0.18.0.beta6
data/lib/dbus/data.rb CHANGED
@@ -35,7 +35,7 @@ module DBus
35
35
  # construct an appropriate {Data::Base} instance.
36
36
  #
37
37
  # @param type [SingleCompleteType,Type]
38
- # @param value [::Object]
38
+ # @param value [::Object,Data::Base] a plain value; exact values also allowed
39
39
  # @return [Data::Base]
40
40
  # @raise TypeError
41
41
  def make_typed(type, value)
@@ -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
 
@@ -58,15 +58,28 @@ module DBus
58
58
  # @!method self.fixed?
59
59
  # @return [Boolean]
60
60
 
61
- # @return appropriately-typed, valid value
61
+ # @return [::Object] a valid value, plain-Ruby typed.
62
+ # @see Data::Container#exact_value
62
63
  attr_reader :value
63
64
 
65
+ # @!method self.type_code
66
+ # @return [String] a single-character string, for example "a" for arrays
67
+
64
68
  # @!method type
65
69
  # @abstract
66
70
  # Note that for Variants type=="v",
67
71
  # for the specific see {Variant#member_type}
68
72
  # @return [Type] the exact type of this value
69
73
 
74
+ # @!method self.from_typed(value, type:)
75
+ # @param value [::Object]
76
+ # @param type [Type]
77
+ # @return [Base]
78
+ # @api private
79
+ # Use {Data.make_typed} instead.
80
+ # Construct an instance of the specific subclass, with a type further
81
+ # specified in the *type* argument.
82
+
70
83
  # Child classes must validate *value*.
71
84
  def initialize(value)
72
85
  @value = value
@@ -82,7 +95,19 @@ module DBus
82
95
 
83
96
  # Hash key equality
84
97
  # See https://ruby-doc.org/core-3.0.0/Object.html#method-i-eql-3F
85
- alias eql? ==
98
+ # Stricter than #== (RSpec: eq), 1==1.0 but 1.eql(1.0)->false
99
+ def eql?(other)
100
+ return false unless other.class == self.class
101
+
102
+ other.value.eql?(@value)
103
+ # TODO: this should work, now check derived classes, exact_value
104
+ end
105
+
106
+ # @param type [Type]
107
+ def self.assert_type_matches_class(type)
108
+ raise ArgumentError, "Expecting #{type_code.inspect} for class #{self}, got #{type.sigtype.inspect}" \
109
+ unless type.sigtype == type_code
110
+ end
86
111
  end
87
112
 
88
113
  # A value that is not a {Container}.
@@ -103,10 +128,10 @@ module DBus
103
128
  end
104
129
 
105
130
  # @param value [::Object]
106
- # @param member_types [::Array<Type>] (ignored, will be empty)
131
+ # @param type [Type]
107
132
  # @return [Basic]
108
- def self.from_typed(value, member_types:) # rubocop:disable Lint/UnusedMethodArgument
109
- # assert member_types.empty?
133
+ def self.from_typed(value, type:)
134
+ assert_type_matches_class(type)
110
135
  new(value)
111
136
  end
112
137
  end
@@ -132,34 +157,6 @@ module DBus
132
157
  end
133
158
  end
134
159
 
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
160
  # Format strings for String#unpack, both little- and big-endian.
164
161
  Format = ::Struct.new(:little, :big)
165
162
 
@@ -398,6 +395,23 @@ module DBus
398
395
  end
399
396
  end
400
397
 
398
+ # {DBus::Data::String}, {DBus::Data::ObjectPath}, or {DBus::Data::Signature}.
399
+ class StringLike < Basic
400
+ def self.fixed?
401
+ false
402
+ end
403
+
404
+ def initialize(value)
405
+ if value.is_a?(self.class)
406
+ value = value.value
407
+ else
408
+ self.class.validate_raw!(value)
409
+ end
410
+
411
+ super(value)
412
+ end
413
+ end
414
+
401
415
  # UTF-8 encoded string.
402
416
  class String < StringLike
403
417
  def self.type_code
@@ -477,7 +491,7 @@ module DBus
477
491
  Byte
478
492
  end
479
493
 
480
- # @return [Array<Type>]
494
+ # @return [::Array<Type>]
481
495
  def self.validate_raw!(value)
482
496
  DBus.types(value)
483
497
  rescue Type::SignatureException => e
@@ -494,6 +508,44 @@ module DBus
494
508
  end
495
509
  end
496
510
 
511
+ # Contains one or more other values.
512
+ class Container < Base
513
+ def self.basic?
514
+ false
515
+ end
516
+
517
+ def self.fixed?
518
+ false
519
+ end
520
+
521
+ # For containers, the type varies among instances
522
+ # @see Base#type
523
+ attr_reader :type
524
+
525
+ # @return something that is, or contains, {Data::Base}.
526
+ # Er, this docs kinda sucks.
527
+ def exact_value
528
+ @value
529
+ end
530
+
531
+ def value
532
+ @value.map(&:value)
533
+ end
534
+
535
+ # Hash key equality
536
+ # See https://ruby-doc.org/core-3.0.0/Object.html#method-i-eql-3F
537
+ # Stricter than #== (RSpec: eq), 1==1.0 but 1.eql(1.0)->false
538
+ def eql?(other)
539
+ return false unless other.class == self.class
540
+
541
+ other.exact_value.eql?(exact_value)
542
+ end
543
+
544
+ # def ==(other)
545
+ # eql?(other) || super
546
+ # end
547
+ end
548
+
497
549
  # An Array, or a Dictionary (Hash).
498
550
  class Array < Container
499
551
  def self.type_code
@@ -504,44 +556,33 @@ module DBus
504
556
  4
505
557
  end
506
558
 
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
559
  # TODO: check that Hash keys are basic types
521
560
  # @param mode [:plain,:exact]
522
- # @param member_type [Type]
561
+ # @param type [Type]
523
562
  # @param hash [Boolean] are we unmarshalling an ARRAY of DICT_ENTRY
524
563
  # @return [Data::Array]
525
- def self.from_items(value, mode:, member_type:, hash: false)
564
+ def self.from_items(value, mode:, type:, hash: false)
526
565
  value = Hash[value] if hash
527
566
  return value if mode == :plain
528
567
 
529
- new(value, member_type: member_type)
568
+ new(value, type: type)
530
569
  end
531
570
 
532
571
  # @param value [::Object]
533
- # @param member_types [::Array<Type>]
572
+ # @param type [Type]
534
573
  # @return [Data::Array]
535
- def self.from_typed(value, member_types:)
536
- # TODO: validation
537
- member_type = member_types.first
574
+ def self.from_typed(value, type:)
575
+ new(value, type: type) # initialize(::Array<Data::Base>)
576
+ end
538
577
 
539
- # TODO: Dict??
540
- items = value.map do |i|
541
- Data.make_typed(member_type, i)
578
+ def value
579
+ v = super
580
+ if type.child.sigtype == Type::DICT_ENTRY
581
+ # BTW this makes a copy so mutating it is pointless
582
+ v.to_h
583
+ else
584
+ v
542
585
  end
543
-
544
- new(items, member_type: member_type) # initialize(::Array<Data::Base>)
545
586
  end
546
587
 
547
588
  # FIXME: should Data::Array be mutable?
@@ -550,13 +591,27 @@ module DBus
550
591
  # TODO: specify type or guess type?
551
592
  # Data is the exact type, so its constructor should be exact
552
593
  # 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)
556
- # TODO: copy from another Data::Array
557
- @member_type = member_type
558
- @type = nil
559
- super(value)
594
+
595
+ # @param value [Data::Array,Enumerable]
596
+ # @param type [SingleCompleteType,Type]
597
+ def initialize(value, type:)
598
+ type = Type::Factory.make_type(type)
599
+ self.class.assert_type_matches_class(type)
600
+ @type = type
601
+
602
+ typed_value = case value
603
+ when Data::Array
604
+ raise ArgumentError, "Specified type is #{type} but value type is #{value.type}" \
605
+ unless value.type == type
606
+
607
+ value.exact_value
608
+ else
609
+ # TODO: Dict??
610
+ value.map do |i|
611
+ Data.make_typed(type.child, i)
612
+ end
613
+ end
614
+ super(typed_value)
560
615
  end
561
616
  end
562
617
 
@@ -572,51 +627,84 @@ module DBus
572
627
  8
573
628
  end
574
629
 
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
630
  # @param value [::Array]
591
- def self.from_items(value, mode:, member_types:)
631
+ def self.from_items(value, mode:, type:)
592
632
  value.freeze
593
633
  return value if mode == :plain
594
634
 
595
- new(value, member_types: member_types)
635
+ new(value, type: type)
596
636
  end
597
637
 
598
638
  # @param value [::Object] (#size, #each)
599
- # @param member_types [::Array<Type>]
639
+ # @param type [Type]
600
640
  # @return [Struct]
601
- def self.from_typed(value, member_types:)
602
- # TODO: validation
603
- raise unless value.size == member_types.size
641
+ def self.from_typed(value, type:)
642
+ new(value, type: type)
643
+ end
644
+
645
+ # @param value [Data::Struct,Enumerable]
646
+ # @param type [SingleCompleteType,Type]
647
+ def initialize(value, type:)
648
+ type = Type::Factory.make_type(type)
649
+ self.class.assert_type_matches_class(type)
650
+ @type = type
604
651
 
605
- items = member_types.zip(value).map do |item_type, item|
606
- Data.make_typed(item_type, item)
652
+ typed_value = case value
653
+ when self.class
654
+ raise ArgumentError, "Specified type is #{type} but value type is #{value.type}" \
655
+ unless value.type == type
656
+
657
+ value.exact_value
658
+ else
659
+ member_types = type.members
660
+ unless value.size == member_types.size
661
+ raise ArgumentError, "Specified type has #{member_types.size} members " \
662
+ "but value has #{value.size} members"
663
+ end
664
+
665
+ member_types.zip(value).map do |item_type, item|
666
+ Data.make_typed(item_type, item)
667
+ end
668
+ end
669
+ super(typed_value)
670
+ end
671
+
672
+ def ==(other)
673
+ case other
674
+ when ::Struct
675
+ @value.size == other.size &&
676
+ @value.zip(other.to_a).all? { |i, other_i| i == other_i }
677
+ else
678
+ super
607
679
  end
680
+ end
681
+ end
608
682
 
609
- new(items, member_types: member_types) # initialize(::Array<Data::Base>)
683
+ # Dictionary/Hash entry.
684
+ # TODO: shouldn't instantiate?
685
+ class DictEntry < Struct
686
+ def self.type_code
687
+ "e"
610
688
  end
611
689
 
612
- def initialize(value, member_types:)
613
- @member_types = member_types
614
- @type = nil
615
- super(value)
690
+ # @param value [::Array]
691
+ def self.from_items(value, mode:, type:) # rubocop:disable Lint/UnusedMethodArgument
692
+ value.freeze
693
+ # DictEntry ignores the :exact mode
694
+ value
695
+ end
696
+
697
+ # @param value [::Object] (#size, #each)
698
+ # @param type [Type]
699
+ # @return [DictEntry]
700
+ def self.from_typed(value, type:)
701
+ new(value, type: type)
616
702
  end
617
703
  end
618
704
 
619
- # A generic type
705
+ # A generic type.
706
+ #
707
+ # Implementation note: @value is a {Data::Base}.
620
708
  class Variant < Container
621
709
  def self.type_code
622
710
  "v"
@@ -626,6 +714,10 @@ module DBus
626
714
  1
627
715
  end
628
716
 
717
+ def value
718
+ @value.value
719
+ end
720
+
629
721
  # @param member_type [Type]
630
722
  def self.from_items(value, mode:, member_type:)
631
723
  return value if mode == :plain
@@ -634,12 +726,12 @@ module DBus
634
726
  end
635
727
 
636
728
  # @param value [::Object]
637
- # @param member_types [::Array<Type>]
729
+ # @param type [Type]
638
730
  # @return [Variant]
639
- def self.from_typed(value, member_types:) # rubocop:disable Lint/UnusedMethodArgument
640
- # assert member_types.empty?
731
+ def self.from_typed(value, type:)
732
+ assert_type_matches_class(type)
641
733
 
642
- # decide on type of value
734
+ # nil: decide on type of value
643
735
  new(value, member_type: nil)
644
736
  end
645
737
 
@@ -667,73 +759,24 @@ module DBus
667
759
  # @param member_type [Type,nil]
668
760
  def initialize(value, member_type:)
669
761
  # TODO: validate that the given *member_type* matches *value*
670
- if value.is_a?(self.class)
762
+ case value
763
+ when Data::Variant
671
764
  # Copy the contained value instead of boxing it more
672
765
  # TODO: except perhaps for round-tripping in exact mode?
673
766
  @member_type = value.member_type
674
- value = value.value
767
+ value = value.exact_value
768
+ when Data::Base
769
+ @member_type = member_type || value.type
770
+ raise ArgumentError, "Variant type #{@member_type} does not match value type #{value.type}" \
771
+ unless @member_type == value.type
675
772
  else
676
773
  @member_type = member_type || self.class.guess_type(value)
774
+ value = Data.make_typed(@member_type, value)
677
775
  end
678
776
  super(value)
679
777
  end
680
778
  end
681
779
 
682
- # Dictionary/Hash entry.
683
- # TODO: shouldn't instantiate?
684
- class DictEntry < Container
685
- def self.type_code
686
- "e"
687
- end
688
-
689
- def self.alignment
690
- 8
691
- end
692
-
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
- # @param value [::Array]
708
- def self.from_items(value, mode:, member_types:) # rubocop:disable Lint/UnusedMethodArgument
709
- value.freeze
710
- # DictEntry ignores the :exact mode
711
- value
712
- end
713
-
714
- # @param value [::Object] (#size, #each)
715
- # @param member_types [::Array<Type>]
716
- # @return [DictEntry]
717
- def self.from_typed(value, member_types:)
718
- # assert member_types.size == 2
719
- # TODO: duplicated from Struct. Inherit/delegate?
720
- # TODO: validation
721
- raise unless value.size == member_types.size
722
-
723
- items = member_types.zip(value).map do |item_type, item|
724
- Data.make_typed(item_type, item)
725
- end
726
-
727
- new(items, member_types: member_types) # initialize(::Array<Data::Base>)
728
- end
729
-
730
- def initialize(value, member_types:)
731
- @member_types = member_types
732
- @type = nil
733
- super(value)
734
- end
735
- end
736
-
737
780
  consts = constants.map { |c_sym| const_get(c_sym) }
738
781
  classes = consts.find_all { |c| c.respond_to?(:type_code) }
739
782
  by_type_code = classes.map { |cl| [cl.type_code, cl] }.to_h
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
@@ -250,9 +250,10 @@ module DBus
250
250
  when Type::VARIANT
251
251
  append_variant(val)
252
252
  when Type::ARRAY
253
+ val = val.exact_value if val.is_a?(Data::Array)
253
254
  append_array(type.child, val)
254
255
  when Type::STRUCT, Type::DICT_ENTRY
255
- val = val.value if val.is_a?(Data::Struct) || val.is_a?(Data::DictEntry)
256
+ val = val.exact_value if val.is_a?(Data::Struct) || val.is_a?(Data::DictEntry)
256
257
  unless val.is_a?(Array) || val.is_a?(Struct)
257
258
  type_name = Type::TYPE_MAPPING[type.sigtype].first
258
259
  raise TypeException, "#{type_name} expects an Array or Struct, seen #{val.class}"
@@ -281,8 +282,14 @@ module DBus
281
282
 
282
283
  def append_variant(val)
283
284
  vartype = nil
284
- if val.is_a?(DBus::Data::Base)
285
- 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.exact_value
288
+ elsif val.is_a?(DBus::Data::Container)
289
+ vartype = val.type
290
+ vardata = val.exact_value
291
+ elsif val.is_a?(DBus::Data::Base)
292
+ vartype = val.type
286
293
  vardata = val.value
287
294
  elsif val.is_a?(Array) && val.size == 2
288
295
  case val[0]
@@ -353,15 +360,23 @@ module DBus
353
360
  elsif value.is_a? Hash
354
361
  h = {}
355
362
  value.each_key { |k| h[k] = make_variant(value[k]) }
356
- ["a{sv}", h]
363
+ key_type = if value.empty?
364
+ "s"
365
+ else
366
+ t, = make_variant(value.first.first)
367
+ t
368
+ end
369
+ ["a{#{key_type}v}", h]
357
370
  elsif value.respond_to? :to_str
358
371
  ["s", value.to_str]
359
372
  elsif value.respond_to? :to_int
360
373
  i = value.to_int
361
- if (-2_147_483_648...2_147_483_648).cover?(i)
374
+ if Data::Int32.range.cover?(i)
362
375
  ["i", i]
363
- else
376
+ elsif Data::Int64.range.cover?(i)
364
377
  ["x", i]
378
+ else
379
+ ["t", i]
365
380
  end
366
381
  end
367
382
  end