ruby-dbus 0.18.0.beta5 → 0.18.0.beta8

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: f8b0c43d3f27f0877e52c0adfb8014374829f97bcd38be6b9fabda1d5da8413c
4
- data.tar.gz: 000eadf163b0fab2ef4ab39de81a79714b34392cd81b3be68e12587ddfa93372
3
+ metadata.gz: 6d6ace164222b10bbf03ab5ac9e2ec3799c8847c4a8f94e1d7f509944bf3e806
4
+ data.tar.gz: 9e8a61b9fa885b36ef21d04f4eb03d4d5a5ab8ec78d5ae258e2433a9e4e5cabe
5
5
  SHA512:
6
- metadata.gz: c2206dcd935fed4e3711b88b2c0c265e5a335700f9137d2c3972f419791d9c25198d0acf9ba7aaecdbc6a25e540005c64321e5165c6c644838fb3557355861ac
7
- data.tar.gz: 212cb8bdcd75e3f35622843f262cdb80eaa07c1919a7d17ffdf601e05c627556343b7855a014f60a220c8a757c201431d66799a6025a98d084aef6cc12f7d8bb
6
+ metadata.gz: a6f4e56b35c43975202769d8085cd690c95f345b09e5c78cd2e89f373105ee0e7edc0d9954ac3c647678a41c18ffb06dbaa806f9bed269b832ad6052a9449afe
7
+ data.tar.gz: 986c96e6a2f74668cd70e111c9fd57c4dd1c30a2e1b2ed66252406bc8fc67a6ec56bc663bae111b0e331e0f8544419d7fb4facabec1a14e1407ca903695d43c9
data/NEWS.md CHANGED
@@ -2,6 +2,49 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## Ruby D-Bus 0.18.0.beta8 - 2022-06-21
6
+
7
+ Bug fixes:
8
+ * Introduced Object#dbus_properties_changed to send correctly typed property
9
+ values ([#115][]). Avoid calling PropertiesChanged directly as it will
10
+ guess the types.
11
+ * Fix Object.dbus_reader to work with attr_accessor and automatically produce
12
+ dbus_properties_changed for properties that are read-write at
13
+ implementation side and read-only at D-Bus side ([#96][])
14
+
15
+ [#96]: https://github.com/mvidner/ruby-dbus/issues/96
16
+
17
+ API:
18
+ * Service side `emits_changed_signal` to control emission of
19
+ PropertiesChanged: can be assigned within `dbus_interface` or as an option
20
+ when declaring properties ([#117][]).
21
+
22
+ [#115]: https://github.com/mvidner/ruby-dbus/issues/115
23
+ [#117]: https://github.com/mvidner/ruby-dbus/pulls/117
24
+
25
+ ## Ruby D-Bus 0.18.0.beta7 - 2022-05-29
26
+
27
+ API:
28
+ * DBus.variant(type, value) is deprecated in favor of
29
+ Data::Variant.new(value, member_type:)
30
+
31
+ Bug fixes:
32
+ * Client-side properties: When calling Properties.Set in
33
+ ProxyObjectInterface#[]=, use the correct type ([#108][]).
34
+
35
+ [#108]: https://github.com/mvidner/ruby-dbus/issues/108
36
+
37
+ ## Ruby D-Bus 0.18.0.beta6 - 2022-05-25
38
+
39
+ API:
40
+ * Data::Base#value returns plain Ruby types;
41
+ Data::Container#exact_value contains Data::Base ([#114][]).
42
+ * Data::Base#initialize and .from_typed allow plain or exact values, validate
43
+ argument types.
44
+ * Implement #== (converting) and #eql? (strict) for Data::Base and DBus::Type.
45
+
46
+ [#114]: https://github.com/mvidner/ruby-dbus/pull/114
47
+
5
48
  ## Ruby D-Bus 0.18.0.beta5 - 2022-04-27
6
49
 
7
50
  API:
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.18.0.beta5
1
+ 0.18.0.beta8
data/doc/Reference.md CHANGED
@@ -166,12 +166,21 @@ D-Bus has stricter typing than Ruby, so the library must decide
166
166
  which D-Bus type to choose. Most of the time the choice is dictated
167
167
  by the D-Bus signature.
168
168
 
169
+ For exact representation of D-Bus data types, use subclasses
170
+ of {DBus::Data::Base}, such as {DBus::Data::Int16} or {DBus::Data::UInt64}.
171
+
169
172
  ##### Variants
170
173
 
171
174
  If the signature expects a Variant
172
175
  (which is the case for all Properties!) then an explicit mechanism is needed.
173
176
 
174
- 1. A pair [{DBus::Type}, value] specifies to marshall *value* as
177
+ 1. Any {DBus::Data::Base}.
178
+
179
+ 2. A {DBus::Data::Variant} made by {DBus.variant}(signature, value).
180
+ (Formerly this produced the type+value pair below, now it is just an alias
181
+ to the Variant constructor.)
182
+
183
+ 3. A pair [{DBus::Type}, value] specifies to marshall *value* as
175
184
  that specified type.
176
185
  The pair can be produced by {DBus.variant}(signature, value) which
177
186
  gives the same result as [{DBus.type}(signature), value].
@@ -181,13 +190,13 @@ If the signature expects a Variant
181
190
 
182
191
  `foo_i["Bar"] = DBus.variant("au", [0, 1, 1, 2, 3, 5, 8])`
183
192
 
184
- 2. Other values are tried to fit one of these:
193
+ 4. Other values are tried to fit one of these:
185
194
  Boolean, Double, Array of Variants, Hash of String keyed Variants,
186
195
  String, Int32, Int64.
187
196
 
188
- 3. **Deprecated:** A pair [String, value], where String is a valid
197
+ 5. **Deprecated:** A pair [String, value], where String is a valid
189
198
  signature of a single complete type, marshalls value as that
190
- type. This will hit you when you rely on method (2) but happen to have
199
+ type. This will hit you when you rely on method (4) but happen to have
191
200
  a particular string value in an array.
192
201
 
193
202
  ##### Structs
data/lib/dbus/bus.rb CHANGED
@@ -171,15 +171,11 @@ module DBus
171
171
  "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
172
172
  '
173
173
  xml += "<node name=\"#{node_opath}\">\n"
174
- each_pair do |k, _v|
174
+ each_key do |k|
175
175
  xml += " <node name=\"#{k}\" />\n"
176
176
  end
177
- @object&.intfs&.each_pair do |_k, v|
178
- xml += " <interface name=\"#{v.name}\">\n"
179
- v.methods.each_value { |m| xml += m.to_xml }
180
- v.signals.each_value { |m| xml += m.to_xml }
181
- v.properties.each_value { |m| xml += m.to_xml }
182
- xml += " </interface>\n"
177
+ @object&.intfs&.each_value do |v|
178
+ xml += v.to_xml
183
179
  end
184
180
  xml += "</node>"
185
181
  xml
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)
@@ -58,9 +58,13 @@ 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",
@@ -91,11 +95,18 @@ module DBus
91
95
 
92
96
  # Hash key equality
93
97
  # See https://ruby-doc.org/core-3.0.0/Object.html#method-i-eql-3F
94
- 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
95
105
 
96
106
  # @param type [Type]
97
107
  def self.assert_type_matches_class(type)
98
- raise ArgumentError unless type.sigtype == type_code
108
+ raise ArgumentError, "Expecting #{type_code.inspect} for class #{self}, got #{type.sigtype.inspect}" \
109
+ unless type.sigtype == type_code
99
110
  end
100
111
  end
101
112
 
@@ -480,7 +491,7 @@ module DBus
480
491
  Byte
481
492
  end
482
493
 
483
- # @return [Array<Type>]
494
+ # @return [::Array<Type>]
484
495
  def self.validate_raw!(value)
485
496
  DBus.types(value)
486
497
  rescue Type::SignatureException => e
@@ -510,6 +521,29 @@ module DBus
510
521
  # For containers, the type varies among instances
511
522
  # @see Base#type
512
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
513
547
  end
514
548
 
515
549
  # An Array, or a Dictionary (Hash).
@@ -538,16 +572,17 @@ module DBus
538
572
  # @param type [Type]
539
573
  # @return [Data::Array]
540
574
  def self.from_typed(value, type:)
541
- assert_type_matches_class(type)
542
- # TODO: validation
543
- member_type = type.child
575
+ new(value, type: type) # initialize(::Array<Data::Base>)
576
+ end
544
577
 
545
- # TODO: Dict??
546
- items = value.map do |i|
547
- 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
548
585
  end
549
-
550
- new(items, type: type) # initialize(::Array<Data::Base>)
551
586
  end
552
587
 
553
588
  # FIXME: should Data::Array be mutable?
@@ -556,13 +591,29 @@ module DBus
556
591
  # TODO: specify type or guess type?
557
592
  # Data is the exact type, so its constructor should be exact
558
593
  # and guesswork should be clearly labeled
594
+
595
+ # @param value [Data::Array,Enumerable]
559
596
  # @param type [SingleCompleteType,Type]
560
597
  def initialize(value, type:)
561
- type = DBus.type(type) unless type.is_a?(Type)
598
+ type = Type::Factory.make_type(type)
562
599
  self.class.assert_type_matches_class(type)
563
600
  @type = type
564
- # TODO: copy from another Data::Array
565
- super(value)
601
+
602
+ typed_value = case value
603
+ when Data::Array
604
+ unless value.type == type
605
+ raise ArgumentError,
606
+ "Specified type is #{type.inspect} but value type is #{value.type.inspect}"
607
+ end
608
+
609
+ value.exact_value
610
+ else
611
+ # TODO: Dict??
612
+ value.map do |i|
613
+ Data.make_typed(type.child, i)
614
+ end
615
+ end
616
+ super(typed_value)
566
617
  end
567
618
  end
568
619
 
@@ -590,25 +641,74 @@ module DBus
590
641
  # @param type [Type]
591
642
  # @return [Struct]
592
643
  def self.from_typed(value, type:)
593
- # TODO: validation
594
- member_types = type.members
595
- raise unless value.size == member_types.size
596
-
597
- items = member_types.zip(value).map do |item_type, item|
598
- Data.make_typed(item_type, item)
599
- end
600
-
601
- new(items, type: type) # initialize(::Array<Data::Base>)
644
+ new(value, type: type)
602
645
  end
603
646
 
647
+ # @param value [Data::Struct,Enumerable]
648
+ # @param type [SingleCompleteType,Type]
604
649
  def initialize(value, type:)
650
+ type = Type::Factory.make_type(type)
605
651
  self.class.assert_type_matches_class(type)
606
652
  @type = type
607
- super(value)
653
+
654
+ typed_value = case value
655
+ when self.class
656
+ unless value.type == type
657
+ raise ArgumentError,
658
+ "Specified type is #{type.inspect} but value type is #{value.type.inspect}"
659
+ end
660
+
661
+ value.exact_value
662
+ else
663
+ member_types = type.members
664
+ unless value.size == member_types.size
665
+ raise ArgumentError, "Specified type has #{member_types.size} members " \
666
+ "but value has #{value.size} members"
667
+ end
668
+
669
+ member_types.zip(value).map do |item_type, item|
670
+ Data.make_typed(item_type, item)
671
+ end
672
+ end
673
+ super(typed_value)
674
+ end
675
+
676
+ def ==(other)
677
+ case other
678
+ when ::Struct
679
+ @value.size == other.size &&
680
+ @value.zip(other.to_a).all? { |i, other_i| i == other_i }
681
+ else
682
+ super
683
+ end
684
+ end
685
+ end
686
+
687
+ # Dictionary/Hash entry.
688
+ # TODO: shouldn't instantiate?
689
+ class DictEntry < Struct
690
+ def self.type_code
691
+ "e"
692
+ end
693
+
694
+ # @param value [::Array]
695
+ def self.from_items(value, mode:, type:) # rubocop:disable Lint/UnusedMethodArgument
696
+ value.freeze
697
+ # DictEntry ignores the :exact mode
698
+ value
699
+ end
700
+
701
+ # @param value [::Object] (#size, #each)
702
+ # @param type [Type]
703
+ # @return [DictEntry]
704
+ def self.from_typed(value, type:)
705
+ new(value, type: type)
608
706
  end
609
707
  end
610
708
 
611
- # A generic type
709
+ # A generic type.
710
+ #
711
+ # Implementation note: @value is a {Data::Base}.
612
712
  class Variant < Container
613
713
  def self.type_code
614
714
  "v"
@@ -618,6 +718,10 @@ module DBus
618
718
  1
619
719
  end
620
720
 
721
+ def value
722
+ @value.value
723
+ end
724
+
621
725
  # @param member_type [Type]
622
726
  def self.from_items(value, mode:, member_type:)
623
727
  return value if mode == :plain
@@ -631,7 +735,7 @@ module DBus
631
735
  def self.from_typed(value, type:)
632
736
  assert_type_matches_class(type)
633
737
 
634
- # decide on type of value
738
+ # nil: decide on type of value
635
739
  new(value, member_type: nil)
636
740
  end
637
741
 
@@ -651,66 +755,59 @@ module DBus
651
755
  # @return [Type]
652
756
  attr_reader :member_type
653
757
 
758
+ # Determine the type of *value*
759
+ # @param value [::Object]
760
+ # @return [Type]
761
+ # @api private
762
+ # See also {PacketMarshaller.make_variant}
654
763
  def self.guess_type(value)
655
764
  sct, = PacketMarshaller.make_variant(value)
656
765
  DBus.type(sct)
657
766
  end
658
767
 
659
- # @param member_type [Type,nil]
768
+ # @param member_type [SingleCompleteType,Type,nil]
660
769
  def initialize(value, member_type:)
770
+ member_type = Type::Factory.make_type(member_type) if member_type
661
771
  # TODO: validate that the given *member_type* matches *value*
662
- if value.is_a?(self.class)
772
+ case value
773
+ when Data::Variant
663
774
  # Copy the contained value instead of boxing it more
664
775
  # TODO: except perhaps for round-tripping in exact mode?
665
776
  @member_type = value.member_type
666
- value = value.value
777
+ value = value.exact_value
778
+ when Data::Base
779
+ @member_type = member_type || value.type
780
+ raise ArgumentError, "Variant type #{@member_type} does not match value type #{value.type}" \
781
+ unless @member_type == value.type
667
782
  else
668
783
  @member_type = member_type || self.class.guess_type(value)
784
+ value = Data.make_typed(@member_type, value)
669
785
  end
670
786
  super(value)
671
787
  end
672
- end
673
-
674
- # Dictionary/Hash entry.
675
- # TODO: shouldn't instantiate?
676
- class DictEntry < Container
677
- def self.type_code
678
- "e"
679
- end
680
-
681
- def self.alignment
682
- 8
683
- end
684
-
685
- # @param value [::Array]
686
- def self.from_items(value, mode:, type:) # rubocop:disable Lint/UnusedMethodArgument
687
- value.freeze
688
- # DictEntry ignores the :exact mode
689
- value
690
- end
691
788
 
692
- # @param value [::Object] (#size, #each)
693
- # @param type [Type]
694
- # @return [DictEntry]
695
- def self.from_typed(value, type:)
696
- assert_type_matches_class(type)
697
- member_types = type.members
698
- # assert member_types.size == 2
699
- # TODO: duplicated from Struct. Inherit/delegate?
700
- # TODO: validation
701
- raise unless value.size == member_types.size
702
-
703
- items = member_types.zip(value).map do |item_type, item|
704
- Data.make_typed(item_type, item)
789
+ # Internal helpers to keep the {DBus.variant} method working.
790
+ # Formerly it returned just a pair of [DBus.type(string_type), value]
791
+ # so let's provide [0], [1], .first, .last
792
+ def [](index)
793
+ case index
794
+ when 0
795
+ member_type
796
+ when 1
797
+ value
798
+ else
799
+ raise ArgumentError, "DBus.variant can only be indexed with 0 or 1, seen #{index.inspect}"
705
800
  end
801
+ end
706
802
 
707
- new(items, type: type) # initialize(::Array<Data::Base>)
803
+ # @see #[]
804
+ def first
805
+ self[0]
708
806
  end
709
807
 
710
- def initialize(value, type:)
711
- self.class.assert_type_matches_class(type)
712
- @type = type
713
- super(value)
808
+ # @see #[]
809
+ def last
810
+ self[1]
714
811
  end
715
812
  end
716
813
 
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of the ruby-dbus project
4
+ # Copyright (C) 2022 Martin Vidner
5
+ #
6
+ # This library is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU Lesser General Public
8
+ # License, version 2.1 as published by the Free Software Foundation.
9
+ # See the file "COPYING" for the exact licensing terms.
10
+
11
+ module DBus
12
+ # Describes the behavior of PropertiesChanged signal, for a single property
13
+ # or for an entire interface.
14
+ #
15
+ # The possible values are:
16
+ #
17
+ # - *true*: the signal is emitted with the value included.
18
+ # - *:invalidates*: the signal is emitted but the value is not included
19
+ # in the signal.
20
+ # - *:const*: the property never changes value during the lifetime
21
+ # of the object it belongs to, and hence the signal
22
+ # is never emitted for it (but clients can cache the value)
23
+ # - *false*: the signal won't be emitted (clients should re-Get the property value)
24
+ #
25
+ # The default is:
26
+ # - for an interface: *true*
27
+ # - for a property: what the parent interface specifies
28
+ #
29
+ # @see DBus::Object.emits_changed_signal
30
+ # @see DBus::Object.dbus_attr_accessor
31
+ # @see https://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format
32
+ #
33
+ # Immutable once constructed.
34
+ class EmitsChangedSignal
35
+ # @return [true,false,:const,:invalidates]
36
+ attr_reader :value
37
+
38
+ # @param value [true,false,:const,:invalidates,nil]
39
+ # See class-level description above, {EmitsChangedSignal}.
40
+ # @param interface [Interface,nil]
41
+ # If the (property-level) *value* is unspecified (nil), this is the
42
+ # containing {Interface} to get the value from.
43
+ def initialize(value, interface: nil)
44
+ if value.nil?
45
+ raise ArgumentError, "Both arguments are nil" if interface.nil?
46
+
47
+ @value = interface.emits_changed_signal.value
48
+ else
49
+ expecting = [true, false, :const, :invalidates]
50
+ unless expecting.include?(value)
51
+ raise ArgumentError, "Expecting one of #{expecting.inspect}. Seen #{value.inspect}"
52
+ end
53
+
54
+ @value = value
55
+ end
56
+
57
+ freeze
58
+ end
59
+
60
+ # Return introspection XML string representation
61
+ # @return [String]
62
+ def to_xml
63
+ return "" if @value == true
64
+
65
+ " <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"#{@value}\"/>\n"
66
+ end
67
+
68
+ def to_s
69
+ @value.to_s
70
+ end
71
+
72
+ def ==(other)
73
+ if other.is_a?(self.class)
74
+ other.value == @value
75
+ else
76
+ other == value
77
+ end
78
+ end
79
+ alias eql? ==
80
+
81
+ DEFAULT_ECS = EmitsChangedSignal.new(true)
82
+ end
83
+ end
@@ -26,7 +26,7 @@ module DBus
26
26
  # method call instantiates and configures this class for us.
27
27
  #
28
28
  # It also is the local definition of interface exported by the program.
29
- # At the client side, see ProxyObjectInterface
29
+ # At the client side, see {ProxyObjectInterface}.
30
30
  class Interface
31
31
  # @return [String] The name of the interface.
32
32
  attr_reader :name
@@ -38,6 +38,9 @@ module DBus
38
38
  # @return [Hash{Symbol => Property}]
39
39
  attr_reader :properties
40
40
 
41
+ # @return [EmitsChangedSignal]
42
+ attr_reader :emits_changed_signal
43
+
41
44
  # Creates a new interface with a given _name_.
42
45
  def initialize(name)
43
46
  validate_name(name)
@@ -45,6 +48,20 @@ module DBus
45
48
  @methods = {}
46
49
  @signals = {}
47
50
  @properties = {}
51
+ @emits_changed_signal = EmitsChangedSignal::DEFAULT_ECS
52
+ end
53
+
54
+ # Helper for {Object.emits_changed_signal=}.
55
+ # @api private
56
+ def emits_changed_signal=(ecs)
57
+ raise TypeError unless ecs.is_a? EmitsChangedSignal
58
+ # equal?: object identity
59
+ unless @emits_changed_signal.equal?(EmitsChangedSignal::DEFAULT_ECS) ||
60
+ @emits_changed_signal.value == ecs.value
61
+ raise "emits_change_signal was assigned more than once"
62
+ end
63
+
64
+ @emits_changed_signal = ecs
48
65
  end
49
66
 
50
67
  # Validates a service _name_.
@@ -85,6 +102,18 @@ module DBus
85
102
  define(m)
86
103
  end
87
104
  alias declare_method define_method
105
+
106
+ # Return introspection XML string representation of the property.
107
+ # @return [String]
108
+ def to_xml
109
+ xml = " <interface name=\"#{name}\">\n"
110
+ xml += emits_changed_signal.to_xml
111
+ methods.each_value { |m| xml += m.to_xml }
112
+ signals.each_value { |m| xml += m.to_xml }
113
+ properties.each_value { |m| xml += m.to_xml }
114
+ xml += " </interface>\n"
115
+ xml
116
+ end
88
117
  end
89
118
 
90
119
  # = A formal parameter has a name and a type
@@ -190,6 +219,7 @@ module DBus
190
219
  end
191
220
 
192
221
  # Return an XML string representation of the method interface elment.
222
+ # @return [String]
193
223
  def to_xml
194
224
  xml = " <method name=\"#{@name}\">\n"
195
225
  @params.each do |param|
@@ -238,19 +268,20 @@ module DBus
238
268
  # An (exported) property
239
269
  # https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties
240
270
  class Property
241
- # @return [String] The name of the property, for example FooBar.
271
+ # @return [Symbol] The name of the property, for example FooBar.
242
272
  attr_reader :name
243
273
  # @return [SingleCompleteType]
244
274
  attr_reader :type
245
275
  # @return [Symbol] :read :write or :readwrite
246
276
  attr_reader :access
247
277
 
248
- # @return [Symbol] What to call at Ruby side.
278
+ # @return [Symbol,nil] What to call at Ruby side.
249
279
  # (Always without the trailing `=`)
280
+ # It is `nil` IFF representing a client-side proxy.
250
281
  attr_reader :ruby_name
251
282
 
252
283
  def initialize(name, type, access, ruby_name:)
253
- @name = name
284
+ @name = name.to_sym
254
285
  @type = type
255
286
  @access = access
256
287
  @ruby_name = ruby_name
@@ -270,5 +301,14 @@ module DBus
270
301
  def to_xml
271
302
  " <property type=\"#{@type}\" name=\"#{@name}\" access=\"#{@access}\"/>\n"
272
303
  end
304
+
305
+ # @param xml_node [AbstractXML::Node]
306
+ # @return [Property]
307
+ def self.from_xml(xml_node)
308
+ name = xml_node["name"].to_sym
309
+ type = xml_node["type"]
310
+ access = xml_node["access"].to_sym
311
+ new(name, type, access, ruby_name: nil)
312
+ end
273
313
  end
274
314
  end
data/lib/dbus/marshall.rb CHANGED
@@ -250,10 +250,10 @@ module DBus
250
250
  when Type::VARIANT
251
251
  append_variant(val)
252
252
  when Type::ARRAY
253
- val = val.value if val.is_a?(Data::Array)
253
+ val = val.exact_value if val.is_a?(Data::Array)
254
254
  append_array(type.child, val)
255
255
  when Type::STRUCT, Type::DICT_ENTRY
256
- 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)
257
257
  unless val.is_a?(Array) || val.is_a?(Struct)
258
258
  type_name = Type::TYPE_MAPPING[type.sigtype].first
259
259
  raise TypeException, "#{type_name} expects an Array or Struct, seen #{val.class}"
@@ -284,7 +284,10 @@ module DBus
284
284
  vartype = nil
285
285
  if val.is_a?(DBus::Data::Variant)
286
286
  vartype = val.member_type
287
- vardata = val.value
287
+ vardata = val.exact_value
288
+ elsif val.is_a?(DBus::Data::Container)
289
+ vartype = val.type
290
+ vardata = val.exact_value
288
291
  elsif val.is_a?(DBus::Data::Base)
289
292
  vartype = val.type
290
293
  vardata = val.value