ruby-dbus 0.18.0.beta3 → 0.18.0.beta6

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