ruby-dbus 0.18.0.beta4 → 0.18.0.beta7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e6e093b55c88614ceebd2cfd0c09a5bba3886b8c561b8e153c034d9249ae5a08
4
- data.tar.gz: a2b2931d1b3b9ae560cc6204e297a7daceec2ec85a065a2782cbbeed34286f5d
3
+ metadata.gz: 56815cd47f001f19c6d74cc02f0f1017e6e17d8df44e0949f020e4932adbc622
4
+ data.tar.gz: 4aeda5f0c4972671adf6ae24554aa08ce5e6208846f87f5793562e73b8e7d632
5
5
  SHA512:
6
- metadata.gz: 66e7ad826a0f1ef5bb1abba72a6f76f02fbd4c13c7e7f14caf3abd71946ba4e5a0b9cc16950d60a22eb2d994d73c78306d270296af90f2bf9aa2aaaf4f85dab3
7
- data.tar.gz: 4641ca7b18414d7cf58d786ab05d3d3bc3a50d40311b1b5650b6f44a1bfcf0b1cca373eee4445b5cd6e0eb70a42407b244d93c09371d9ea985a66d6d4c79c0d7
6
+ metadata.gz: 2432d8ada23410027d00c7995c7514e3e927430844cf037fa8496509c47b10b4f5a431335762206bfc9966724d6d84014f57d9bbe31613bfd253145e25b82b77
7
+ data.tar.gz: c1b480afebcdaf53b6fbe084f6150b7c9758d1329a207b4941dd8256cbc3786d42341564b3b8220d9e897e7487d281fede15eab8a31d81741582272519e1ac19
data/NEWS.md CHANGED
@@ -2,6 +2,46 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## Ruby D-Bus 0.18.0.beta7 - 2022-05-29
6
+
7
+ API:
8
+ * DBus.variant(type, value) is deprecated in favor of
9
+ Data::Variant.new(value, member_type:)
10
+
11
+ Bug fixes:
12
+ * Client-side properties: When calling Properties.Set in
13
+ ProxyObjectInterface#[]=, use the correct type ([#108][]).
14
+
15
+ [#108]: https://github.com/mvidner/ruby-dbus/issues/108
16
+
17
+ ## Ruby D-Bus 0.18.0.beta6 - 2022-05-25
18
+
19
+ API:
20
+ * Data::Base#value returns plain Ruby types;
21
+ Data::Container#exact_value contains Data::Base ([#114][]).
22
+ * Data::Base#initialize and .from_typed allow plain or exact values, validate
23
+ argument types.
24
+ * Implement #== (converting) and #eql? (strict) for Data::Base and DBus::Type.
25
+
26
+ [#114]: https://github.com/mvidner/ruby-dbus/pull/114
27
+
28
+ ## Ruby D-Bus 0.18.0.beta5 - 2022-04-27
29
+
30
+ API:
31
+ * DBus::Type instances are frozen.
32
+ * Data::Container classes (Array, Struct, DictEntry, but not Variant)
33
+ constructors (#initialize, .from_items, .from_typed) changed to have
34
+ a *type* argument instead of *member_type* or *member_types*.
35
+ * Added type factories
36
+ * Type::Array[type]
37
+ * Type::Hash[key_type, value_type]
38
+ * Type::Struct[type1, type2...]
39
+
40
+ Bug fixes:
41
+ * Properties containing Variants would return them doubly wrapped ([#111][]).
42
+
43
+ [#111]: https://github.com/mvidner/ruby-dbus/pull/111
44
+
5
45
  ## Ruby D-Bus 0.18.0.beta4 - 2022-04-21
6
46
 
7
47
  Bug fixes:
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.18.0.beta4
1
+ 0.18.0.beta7
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/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,29 @@ 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
+ 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)
560
617
  end
561
618
  end
562
619
 
@@ -572,51 +629,86 @@ module DBus
572
629
  8
573
630
  end
574
631
 
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
632
  # @param value [::Array]
591
- def self.from_items(value, mode:, member_types:)
633
+ def self.from_items(value, mode:, type:)
592
634
  value.freeze
593
635
  return value if mode == :plain
594
636
 
595
- new(value, member_types: member_types)
637
+ new(value, type: type)
596
638
  end
597
639
 
598
640
  # @param value [::Object] (#size, #each)
599
- # @param member_types [::Array<Type>]
641
+ # @param type [Type]
600
642
  # @return [Struct]
601
- def self.from_typed(value, member_types:)
602
- # TODO: validation
603
- raise unless value.size == member_types.size
643
+ def self.from_typed(value, type:)
644
+ new(value, type: type)
645
+ end
646
+
647
+ # @param value [Data::Struct,Enumerable]
648
+ # @param type [SingleCompleteType,Type]
649
+ def initialize(value, type:)
650
+ type = Type::Factory.make_type(type)
651
+ self.class.assert_type_matches_class(type)
652
+ @type = type
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
604
675
 
605
- items = member_types.zip(value).map do |item_type, item|
606
- Data.make_typed(item_type, item)
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
607
683
  end
684
+ end
685
+ end
608
686
 
609
- new(items, member_types: member_types) # initialize(::Array<Data::Base>)
687
+ # Dictionary/Hash entry.
688
+ # TODO: shouldn't instantiate?
689
+ class DictEntry < Struct
690
+ def self.type_code
691
+ "e"
610
692
  end
611
693
 
612
- def initialize(value, member_types:)
613
- @member_types = member_types
614
- @type = nil
615
- super(value)
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)
616
706
  end
617
707
  end
618
708
 
619
- # A generic type
709
+ # A generic type.
710
+ #
711
+ # Implementation note: @value is a {Data::Base}.
620
712
  class Variant < Container
621
713
  def self.type_code
622
714
  "v"
@@ -626,6 +718,10 @@ module DBus
626
718
  1
627
719
  end
628
720
 
721
+ def value
722
+ @value.value
723
+ end
724
+
629
725
  # @param member_type [Type]
630
726
  def self.from_items(value, mode:, member_type:)
631
727
  return value if mode == :plain
@@ -634,12 +730,12 @@ module DBus
634
730
  end
635
731
 
636
732
  # @param value [::Object]
637
- # @param member_types [::Array<Type>]
733
+ # @param type [Type]
638
734
  # @return [Variant]
639
- def self.from_typed(value, member_types:) # rubocop:disable Lint/UnusedMethodArgument
640
- # assert member_types.empty?
735
+ def self.from_typed(value, type:)
736
+ assert_type_matches_class(type)
641
737
 
642
- # decide on type of value
738
+ # nil: decide on type of value
643
739
  new(value, member_type: nil)
644
740
  end
645
741
 
@@ -659,78 +755,59 @@ module DBus
659
755
  # @return [Type]
660
756
  attr_reader :member_type
661
757
 
758
+ # Determine the type of *value*
759
+ # @param value [::Object]
760
+ # @return [Type]
761
+ # @api private
762
+ # See also {PacketMarshaller.make_variant}
662
763
  def self.guess_type(value)
663
764
  sct, = PacketMarshaller.make_variant(value)
664
765
  DBus.type(sct)
665
766
  end
666
767
 
667
- # @param member_type [Type,nil]
768
+ # @param member_type [SingleCompleteType,Type,nil]
668
769
  def initialize(value, member_type:)
770
+ member_type = Type::Factory.make_type(member_type) if member_type
669
771
  # TODO: validate that the given *member_type* matches *value*
670
- if value.is_a?(self.class)
772
+ case value
773
+ when Data::Variant
671
774
  # Copy the contained value instead of boxing it more
672
775
  # TODO: except perhaps for round-tripping in exact mode?
673
776
  @member_type = value.member_type
674
- 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
675
782
  else
676
783
  @member_type = member_type || self.class.guess_type(value)
784
+ value = Data.make_typed(@member_type, value)
677
785
  end
678
786
  super(value)
679
787
  end
680
- end
681
788
 
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
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}"
703
800
  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
801
  end
713
802
 
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>)
803
+ # @see #[]
804
+ def first
805
+ self[0]
728
806
  end
729
807
 
730
- def initialize(value, member_types:)
731
- @member_types = member_types
732
- @type = nil
733
- super(value)
808
+ # @see #[]
809
+ def last
810
+ self[1]
734
811
  end
735
812
  end
736
813
 
@@ -238,19 +238,20 @@ module DBus
238
238
  # An (exported) property
239
239
  # https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties
240
240
  class Property
241
- # @return [String] The name of the property, for example FooBar.
241
+ # @return [Symbol] The name of the property, for example FooBar.
242
242
  attr_reader :name
243
243
  # @return [SingleCompleteType]
244
244
  attr_reader :type
245
245
  # @return [Symbol] :read :write or :readwrite
246
246
  attr_reader :access
247
247
 
248
- # @return [Symbol] What to call at Ruby side.
248
+ # @return [Symbol,nil] What to call at Ruby side.
249
249
  # (Always without the trailing `=`)
250
+ # It is `nil` IFF representing a client-side proxy.
250
251
  attr_reader :ruby_name
251
252
 
252
253
  def initialize(name, type, access, ruby_name:)
253
- @name = name
254
+ @name = name.to_sym
254
255
  @type = type
255
256
  @access = access
256
257
  @ruby_name = ruby_name
@@ -270,5 +271,14 @@ module DBus
270
271
  def to_xml
271
272
  " <property type=\"#{@type}\" name=\"#{@name}\" access=\"#{@access}\"/>\n"
272
273
  end
274
+
275
+ # @param xml_node [AbstractXML::Node]
276
+ # @return [Property]
277
+ def self.from_xml(xml_node)
278
+ name = xml_node["name"].to_sym
279
+ type = xml_node["type"]
280
+ access = xml_node["access"].to_sym
281
+ new(name, type, access, ruby_name: nil)
282
+ end
273
283
  end
274
284
  end