em-ruby-dbus 0.11.0
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 +7 -0
- data/COPYING +504 -0
- data/NEWS +253 -0
- data/README.md +93 -0
- data/Rakefile +58 -0
- data/VERSION +1 -0
- data/doc/Reference.md +207 -0
- data/doc/Tutorial.md +480 -0
- data/doc/ex-calling-methods.body.rb +8 -0
- data/doc/ex-calling-methods.rb +3 -0
- data/doc/ex-properties.body.rb +9 -0
- data/doc/ex-properties.rb +3 -0
- data/doc/ex-setup.rb +7 -0
- data/doc/ex-signal.body.rb +20 -0
- data/doc/ex-signal.rb +3 -0
- data/doc/example-helper.rb +6 -0
- data/em-ruby-dbus.gemspec +20 -0
- data/examples/gdbus/gdbus +255 -0
- data/examples/gdbus/gdbus.glade +184 -0
- data/examples/gdbus/launch.sh +4 -0
- data/examples/no-introspect/nm-test.rb +21 -0
- data/examples/no-introspect/tracker-test.rb +16 -0
- data/examples/rhythmbox/playpause.rb +25 -0
- data/examples/service/call_service.rb +25 -0
- data/examples/service/service_newapi.rb +51 -0
- data/examples/simple/call_introspect.rb +34 -0
- data/examples/simple/properties.rb +19 -0
- data/examples/utils/listnames.rb +11 -0
- data/examples/utils/notify.rb +19 -0
- data/lib/dbus.rb +82 -0
- data/lib/dbus/auth.rb +269 -0
- data/lib/dbus/bus.rb +739 -0
- data/lib/dbus/core_ext/array/extract_options.rb +31 -0
- data/lib/dbus/core_ext/class/attribute.rb +129 -0
- data/lib/dbus/core_ext/kernel/singleton_class.rb +8 -0
- data/lib/dbus/core_ext/module/remove_method.rb +14 -0
- data/lib/dbus/error.rb +46 -0
- data/lib/dbus/export.rb +128 -0
- data/lib/dbus/introspect.rb +219 -0
- data/lib/dbus/logger.rb +31 -0
- data/lib/dbus/loop-em.rb +19 -0
- data/lib/dbus/marshall.rb +434 -0
- data/lib/dbus/matchrule.rb +101 -0
- data/lib/dbus/message.rb +276 -0
- data/lib/dbus/message_queue.rb +166 -0
- data/lib/dbus/proxy_object.rb +149 -0
- data/lib/dbus/proxy_object_factory.rb +41 -0
- data/lib/dbus/proxy_object_interface.rb +128 -0
- data/lib/dbus/type.rb +193 -0
- data/lib/dbus/xml.rb +161 -0
- data/test/async_spec.rb +47 -0
- data/test/binding_spec.rb +74 -0
- data/test/bus_and_xml_backend_spec.rb +39 -0
- data/test/bus_driver_spec.rb +20 -0
- data/test/bus_spec.rb +20 -0
- data/test/byte_array_spec.rb +38 -0
- data/test/err_msg_spec.rb +42 -0
- data/test/introspect_xml_parser_spec.rb +26 -0
- data/test/introspection_spec.rb +32 -0
- data/test/main_loop_spec.rb +82 -0
- data/test/property_spec.rb +53 -0
- data/test/server_robustness_spec.rb +66 -0
- data/test/server_spec.rb +53 -0
- data/test/service_newapi.rb +217 -0
- data/test/session_bus_spec_manual.rb +15 -0
- data/test/signal_spec.rb +90 -0
- data/test/spec_helper.rb +33 -0
- data/test/thread_safety_spec.rb +31 -0
- data/test/tools/dbus-launch-simple +35 -0
- data/test/tools/dbus-limited-session.conf +28 -0
- data/test/tools/test_env +13 -0
- data/test/tools/test_server +39 -0
- data/test/type_spec.rb +19 -0
- data/test/value_spec.rb +81 -0
- data/test/variant_spec.rb +66 -0
- metadata +145 -0
@@ -0,0 +1,149 @@
|
|
1
|
+
# This file is part of the ruby-dbus project
|
2
|
+
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
|
3
|
+
# Copyright (C) 2009-2014 Martin Vidner
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License, version 2.1 as published by the Free Software Foundation.
|
8
|
+
# See the file "COPYING" for the exact licensing terms.
|
9
|
+
|
10
|
+
module DBus
|
11
|
+
# D-Bus proxy object class
|
12
|
+
#
|
13
|
+
# Class representing a remote object in an external application.
|
14
|
+
# Typically, calling a method on an instance of a ProxyObject sends a message
|
15
|
+
# over the bus so that the method is executed remotely on the correctponding
|
16
|
+
# object.
|
17
|
+
class ProxyObject
|
18
|
+
# The names of direct subnodes of the object in the tree.
|
19
|
+
attr_accessor :subnodes
|
20
|
+
# Flag determining whether the object has been introspected.
|
21
|
+
attr_accessor :introspected
|
22
|
+
# The (remote) destination of the object.
|
23
|
+
attr_reader :destination
|
24
|
+
# The path to the object.
|
25
|
+
attr_reader :path
|
26
|
+
# The bus the object is reachable via.
|
27
|
+
attr_reader :bus
|
28
|
+
# @return [String] The name of the default interface of the object.
|
29
|
+
attr_accessor :default_iface
|
30
|
+
|
31
|
+
# Creates a new proxy object living on the given _bus_ at destination _dest_
|
32
|
+
# on the given _path_.
|
33
|
+
def initialize(bus, dest, path)
|
34
|
+
@bus, @destination, @path = bus, dest, path
|
35
|
+
@interfaces = Hash.new
|
36
|
+
@subnodes = Array.new
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns the interfaces of the object.
|
40
|
+
def interfaces
|
41
|
+
@interfaces.keys
|
42
|
+
end
|
43
|
+
|
44
|
+
# Retrieves an interface of the proxy object
|
45
|
+
# @param [String] intfname
|
46
|
+
# @return [ProxyObjectInterface]
|
47
|
+
def [](intfname)
|
48
|
+
@interfaces[intfname]
|
49
|
+
end
|
50
|
+
|
51
|
+
# Maps the given interface name _intfname_ to the given interface _intf.
|
52
|
+
# @param [String] intfname
|
53
|
+
# @param [ProxyObjectInterface] intf
|
54
|
+
# @return [ProxyObjectInterface]
|
55
|
+
def []=(intfname, intf)
|
56
|
+
@interfaces[intfname] = intf
|
57
|
+
end
|
58
|
+
|
59
|
+
# Introspects the remote object. Allows you to find and select
|
60
|
+
# interfaces on the object.
|
61
|
+
def introspect
|
62
|
+
# Synchronous call here.
|
63
|
+
xml = @bus.introspect_data(@destination, @path)
|
64
|
+
ProxyObjectFactory.introspect_into(self, xml)
|
65
|
+
define_shortcut_methods()
|
66
|
+
xml
|
67
|
+
end
|
68
|
+
|
69
|
+
# For each non duplicated method name in any interface present on the
|
70
|
+
# caller, defines a shortcut method dynamically.
|
71
|
+
# This function is automatically called when a {ProxyObject} is
|
72
|
+
# introspected.
|
73
|
+
def define_shortcut_methods
|
74
|
+
# builds a list of duplicated methods
|
75
|
+
dup_meths, univocal_meths = [],{}
|
76
|
+
@interfaces.each_value do |intf|
|
77
|
+
intf.methods.each_value do |meth|
|
78
|
+
name = meth.name.to_sym
|
79
|
+
# don't overwrite instance methods!
|
80
|
+
if dup_meths.include? name or self.class.instance_methods.include? name
|
81
|
+
next
|
82
|
+
elsif univocal_meths.include? name
|
83
|
+
univocal_meths.delete name
|
84
|
+
dup_meths << name
|
85
|
+
else
|
86
|
+
univocal_meths[name] = intf
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
univocal_meths.each do |name, intf|
|
91
|
+
# creates a shortcut function that forwards each call to the method on
|
92
|
+
# the appropriate intf
|
93
|
+
singleton_class.class_eval do
|
94
|
+
define_method name do |*args, &reply_handler|
|
95
|
+
intf.method(name).call(*args, &reply_handler)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Returns whether the object has an interface with the given _name_.
|
102
|
+
def has_iface?(name)
|
103
|
+
raise "Cannot call has_iface? if not introspected" if not @introspected
|
104
|
+
@interfaces.member?(name)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Registers a handler, the code block, for a signal with the given _name_.
|
108
|
+
# It uses _default_iface_ which must have been set.
|
109
|
+
# @return [void]
|
110
|
+
def on_signal(name, &block)
|
111
|
+
if @default_iface and has_iface?(@default_iface)
|
112
|
+
@interfaces[@default_iface].on_signal(name, &block)
|
113
|
+
else
|
114
|
+
# TODO improve
|
115
|
+
raise NoMethodError
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
####################################################
|
120
|
+
private
|
121
|
+
|
122
|
+
# Handles all unkown methods, mostly to route method calls to the
|
123
|
+
# default interface.
|
124
|
+
def method_missing(name, *args, &reply_handler)
|
125
|
+
if @default_iface and has_iface?(@default_iface)
|
126
|
+
begin
|
127
|
+
@interfaces[@default_iface].method(name).call(*args, &reply_handler)
|
128
|
+
rescue NameError => e
|
129
|
+
# interesting, foo.method("unknown")
|
130
|
+
# raises NameError, not NoMethodError
|
131
|
+
raise unless e.to_s =~ /undefined method `#{name}'/
|
132
|
+
# BTW e.exception("...") would preserve the class.
|
133
|
+
raise NoMethodError,"undefined method `#{name}' for DBus interface `#{@default_iface}' on object `#{@path}'"
|
134
|
+
end
|
135
|
+
else
|
136
|
+
# TODO distinguish:
|
137
|
+
# - di not specified
|
138
|
+
#TODO
|
139
|
+
# - di is specified but not found in introspection data
|
140
|
+
raise NoMethodError, "undefined method `#{name}' for DBus interface `#{@default_iface}' on object `#{@path}'"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Returns the singleton class of the object.
|
145
|
+
def singleton_class
|
146
|
+
(class << self ; self ; end)
|
147
|
+
end
|
148
|
+
end # class ProxyObject
|
149
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# This file is part of the ruby-dbus project
|
2
|
+
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
|
3
|
+
# Copyright (C) 2009-2014 Martin Vidner
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License, version 2.1 as published by the Free Software Foundation.
|
8
|
+
# See the file "COPYING" for the exact licensing terms.
|
9
|
+
|
10
|
+
module DBus
|
11
|
+
# = D-Bus proxy object factory class
|
12
|
+
#
|
13
|
+
# Class that generates and sets up a proxy object based on introspection data.
|
14
|
+
class ProxyObjectFactory
|
15
|
+
# Creates a new proxy object factory for the given introspection XML _xml_,
|
16
|
+
# _bus_, destination _dest_, and _path_.
|
17
|
+
def initialize(xml, bus, dest, path)
|
18
|
+
@xml, @bus, @path, @dest = xml, bus, path, dest
|
19
|
+
end
|
20
|
+
|
21
|
+
# Investigates the sub-nodes of the proxy object _po_ based on the
|
22
|
+
# introspection XML data _xml_ and sets them up recursively.
|
23
|
+
def ProxyObjectFactory.introspect_into(po, xml)
|
24
|
+
intfs, po.subnodes = IntrospectXMLParser.new(xml).parse
|
25
|
+
intfs.each do |i|
|
26
|
+
poi = ProxyObjectInterface.new(po, i.name)
|
27
|
+
i.methods.each_value { |m| poi.define(m) }
|
28
|
+
i.signals.each_value { |s| poi.define(s) }
|
29
|
+
po[i.name] = poi
|
30
|
+
end
|
31
|
+
po.introspected = true
|
32
|
+
end
|
33
|
+
|
34
|
+
# Generates, sets up and returns the proxy object.
|
35
|
+
def build
|
36
|
+
po = ProxyObject.new(@bus, @dest, @path)
|
37
|
+
ProxyObjectFactory.introspect_into(po, @xml)
|
38
|
+
po
|
39
|
+
end
|
40
|
+
end # class ProxyObjectFactory
|
41
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# This file is part of the ruby-dbus project
|
2
|
+
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
|
3
|
+
# Copyright (C) 2009-2014 Martin Vidner
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License, version 2.1 as published by the Free Software Foundation.
|
8
|
+
# See the file "COPYING" for the exact licensing terms.
|
9
|
+
|
10
|
+
module DBus
|
11
|
+
# = D-Bus proxy object interface class
|
12
|
+
#
|
13
|
+
# A class similar to the normal Interface used as a proxy for remote
|
14
|
+
# object interfaces.
|
15
|
+
class ProxyObjectInterface
|
16
|
+
# The proxied methods contained in the interface.
|
17
|
+
attr_accessor :methods
|
18
|
+
# The proxied signals contained in the interface.
|
19
|
+
attr_accessor :signals
|
20
|
+
# The proxy object to which this interface belongs.
|
21
|
+
attr_reader :object
|
22
|
+
# The name of the interface.
|
23
|
+
attr_reader :name
|
24
|
+
|
25
|
+
# Creates a new proxy interface for the given proxy _object_
|
26
|
+
# and the given _name_.
|
27
|
+
def initialize(object, name)
|
28
|
+
@object, @name = object, name
|
29
|
+
@methods, @signals = Hash.new, Hash.new
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the string representation of the interface (the name).
|
33
|
+
def to_str
|
34
|
+
@name
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns the singleton class of the interface.
|
38
|
+
def singleton_class
|
39
|
+
(class << self ; self ; end)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Defines a method on the interface from the Method descriptor _m_.
|
43
|
+
def define_method_from_descriptor(m)
|
44
|
+
m.params.each do |fpar|
|
45
|
+
par = fpar.type
|
46
|
+
# This is the signature validity check
|
47
|
+
Type::Parser.new(par).parse
|
48
|
+
end
|
49
|
+
|
50
|
+
singleton_class.class_eval do
|
51
|
+
define_method m.name do |*args, &reply_handler|
|
52
|
+
if m.params.size != args.size
|
53
|
+
raise ArgumentError, "wrong number of arguments (#{args.size} for #{m.params.size})"
|
54
|
+
end
|
55
|
+
|
56
|
+
msg = Message.new(Message::METHOD_CALL)
|
57
|
+
msg.path = @object.path
|
58
|
+
msg.interface = @name
|
59
|
+
msg.destination = @object.destination
|
60
|
+
msg.member = m.name
|
61
|
+
msg.sender = @object.bus.unique_name
|
62
|
+
m.params.each do |fpar|
|
63
|
+
par = fpar.type
|
64
|
+
msg.add_param(par, args.shift)
|
65
|
+
end
|
66
|
+
@object.bus.send_sync_or_async(msg, &reply_handler)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
@methods[m.name] = m
|
71
|
+
end
|
72
|
+
|
73
|
+
# Defines a signal from the descriptor _s_.
|
74
|
+
def define_signal_from_descriptor(s)
|
75
|
+
@signals[s.name] = s
|
76
|
+
end
|
77
|
+
|
78
|
+
# Defines a signal or method based on the descriptor _m_.
|
79
|
+
def define(m)
|
80
|
+
if m.kind_of?(Method)
|
81
|
+
define_method_from_descriptor(m)
|
82
|
+
elsif m.kind_of?(Signal)
|
83
|
+
define_signal_from_descriptor(m)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Defines a proxied method on the interface.
|
88
|
+
def define_method(methodname, prototype)
|
89
|
+
m = Method.new(methodname)
|
90
|
+
m.from_prototype(prototype)
|
91
|
+
define(m)
|
92
|
+
end
|
93
|
+
|
94
|
+
# @overload on_signal(name, &block)
|
95
|
+
# @overload on_signal(bus, name, &block)
|
96
|
+
# Registers a handler (code block) for a signal with _name_ arriving
|
97
|
+
# over the given _bus_. If no block is given, the signal is unregistered.
|
98
|
+
# Note that specifying _bus_ is discouraged and the option is kept only for
|
99
|
+
# backward compatibility.
|
100
|
+
# @return [void]
|
101
|
+
def on_signal(bus = @object.bus, name, &block)
|
102
|
+
mr = DBus::MatchRule.new.from_signal(self, name)
|
103
|
+
if block.nil?
|
104
|
+
bus.remove_match(mr)
|
105
|
+
else
|
106
|
+
bus.add_match(mr) { |msg| block.call(*msg.params) }
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"
|
111
|
+
|
112
|
+
# Read a property.
|
113
|
+
def [](propname)
|
114
|
+
self.object[PROPERTY_INTERFACE].Get(self.name, propname)[0]
|
115
|
+
end
|
116
|
+
|
117
|
+
# Write a property.
|
118
|
+
def []=(propname, value)
|
119
|
+
self.object[PROPERTY_INTERFACE].Set(self.name, propname, value)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Read all properties at once, as a hash.
|
123
|
+
# @return [Hash{String}]
|
124
|
+
def all_properties
|
125
|
+
self.object[PROPERTY_INTERFACE].GetAll(self.name)[0]
|
126
|
+
end
|
127
|
+
end # class ProxyObjectInterface
|
128
|
+
end
|
data/lib/dbus/type.rb
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
# dbus/type.rb - module containing low-level D-Bus data type information
|
2
|
+
#
|
3
|
+
# This file is part of the ruby-dbus project
|
4
|
+
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
|
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
|
+
|
13
|
+
# = D-Bus type module
|
14
|
+
#
|
15
|
+
# This module containts the constants of the types specified in the D-Bus
|
16
|
+
# protocol.
|
17
|
+
module Type
|
18
|
+
# Mapping from type number to name and alignment.
|
19
|
+
TypeMapping = {
|
20
|
+
0 => ["INVALID", nil],
|
21
|
+
?y => ["BYTE", 1],
|
22
|
+
?b => ["BOOLEAN", 4],
|
23
|
+
?n => ["INT16", 2],
|
24
|
+
?q => ["UINT16", 2],
|
25
|
+
?i => ["INT32", 4],
|
26
|
+
?u => ["UINT32", 4],
|
27
|
+
?x => ["INT64", 8],
|
28
|
+
?t => ["UINT64", 8],
|
29
|
+
?d => ["DOUBLE", 8],
|
30
|
+
?r => ["STRUCT", 8],
|
31
|
+
?a => ["ARRAY", 4],
|
32
|
+
?v => ["VARIANT", 1],
|
33
|
+
?o => ["OBJECT_PATH", 4],
|
34
|
+
?s => ["STRING", 4],
|
35
|
+
?g => ["SIGNATURE", 1],
|
36
|
+
?e => ["DICT_ENTRY", 8],
|
37
|
+
?h => ["UNIX_FD", 4],
|
38
|
+
}
|
39
|
+
# Defines the set of constants
|
40
|
+
TypeMapping.each_pair do |key, value|
|
41
|
+
Type.const_set(value.first, key)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Exception raised when an unknown/incorrect type is encountered.
|
45
|
+
class SignatureException < Exception
|
46
|
+
end
|
47
|
+
|
48
|
+
# = D-Bus type conversion class
|
49
|
+
#
|
50
|
+
# Helper class for representing a D-Bus type.
|
51
|
+
class Type
|
52
|
+
# Returns the signature type number.
|
53
|
+
attr_reader :sigtype
|
54
|
+
# Return contained member types.
|
55
|
+
attr_reader :members
|
56
|
+
|
57
|
+
# Create a new type instance for type number _sigtype_.
|
58
|
+
def initialize(sigtype)
|
59
|
+
if not TypeMapping.keys.member?(sigtype)
|
60
|
+
raise SignatureException, "Unknown key in signature: #{sigtype.chr}"
|
61
|
+
end
|
62
|
+
@sigtype = sigtype
|
63
|
+
@members = Array.new
|
64
|
+
end
|
65
|
+
|
66
|
+
# Return the required alignment for the type.
|
67
|
+
def alignment
|
68
|
+
TypeMapping[@sigtype].last
|
69
|
+
end
|
70
|
+
|
71
|
+
# Return a string representation of the type according to the
|
72
|
+
# D-Bus specification.
|
73
|
+
def to_s
|
74
|
+
case @sigtype
|
75
|
+
when STRUCT
|
76
|
+
"(" + @members.collect { |t| t.to_s }.join + ")"
|
77
|
+
when ARRAY
|
78
|
+
"a" + child.to_s
|
79
|
+
when DICT_ENTRY
|
80
|
+
"{" + @members.collect { |t| t.to_s }.join + "}"
|
81
|
+
else
|
82
|
+
if not TypeMapping.keys.member?(@sigtype)
|
83
|
+
raise NotImplementedError
|
84
|
+
end
|
85
|
+
@sigtype.chr
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Add a new member type _a_.
|
90
|
+
def <<(a)
|
91
|
+
if not [STRUCT, ARRAY, DICT_ENTRY].member?(@sigtype)
|
92
|
+
raise SignatureException
|
93
|
+
end
|
94
|
+
raise SignatureException if @sigtype == ARRAY and @members.size > 0
|
95
|
+
if @sigtype == DICT_ENTRY
|
96
|
+
if @members.size == 2
|
97
|
+
raise SignatureException, "Dict entries have exactly two members"
|
98
|
+
end
|
99
|
+
if @members.size == 0
|
100
|
+
if [STRUCT, ARRAY, DICT_ENTRY].member?(a.sigtype)
|
101
|
+
raise SignatureException, "Dict entry keys must be basic types"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
@members << a
|
106
|
+
end
|
107
|
+
|
108
|
+
# Return the first contained member type.
|
109
|
+
def child
|
110
|
+
@members[0]
|
111
|
+
end
|
112
|
+
|
113
|
+
def inspect
|
114
|
+
s = TypeMapping[@sigtype].first
|
115
|
+
if [STRUCT, ARRAY].member?(@sigtype)
|
116
|
+
s += ": " + @members.inspect
|
117
|
+
end
|
118
|
+
s
|
119
|
+
end
|
120
|
+
end # class Type
|
121
|
+
|
122
|
+
# = D-Bus type parser class
|
123
|
+
#
|
124
|
+
# Helper class to parse a type signature in the protocol.
|
125
|
+
class Parser
|
126
|
+
# Create a new parser for the given _signature_.
|
127
|
+
def initialize(signature)
|
128
|
+
@signature = signature
|
129
|
+
@idx = 0
|
130
|
+
end
|
131
|
+
|
132
|
+
# Returns the next character from the signature.
|
133
|
+
def nextchar
|
134
|
+
c = @signature[@idx]
|
135
|
+
@idx += 1
|
136
|
+
c
|
137
|
+
end
|
138
|
+
|
139
|
+
# Parse one character _c_ of the signature.
|
140
|
+
def parse_one(c)
|
141
|
+
res = nil
|
142
|
+
case c
|
143
|
+
when ?a
|
144
|
+
res = Type.new(ARRAY)
|
145
|
+
c = nextchar
|
146
|
+
raise SignatureException, "Parse error in #{@signature}" if c == nil
|
147
|
+
child = parse_one(c)
|
148
|
+
res << child
|
149
|
+
when ?(
|
150
|
+
res = Type.new(STRUCT)
|
151
|
+
while (c = nextchar) != nil and c != ?)
|
152
|
+
res << parse_one(c)
|
153
|
+
end
|
154
|
+
raise SignatureException, "Parse error in #{@signature}" if c == nil
|
155
|
+
when ?{
|
156
|
+
res = Type.new(DICT_ENTRY)
|
157
|
+
while (c = nextchar) != nil and c != ?}
|
158
|
+
res << parse_one(c)
|
159
|
+
end
|
160
|
+
raise SignatureException, "Parse error in #{@signature}" if c == nil
|
161
|
+
else
|
162
|
+
res = Type.new(c)
|
163
|
+
end
|
164
|
+
res
|
165
|
+
end
|
166
|
+
|
167
|
+
# Parse the entire signature, return a DBus::Type object.
|
168
|
+
def parse
|
169
|
+
@idx = 0
|
170
|
+
ret = Array.new
|
171
|
+
while (c = nextchar)
|
172
|
+
ret << parse_one(c)
|
173
|
+
end
|
174
|
+
ret
|
175
|
+
end
|
176
|
+
end # class Parser
|
177
|
+
end # module Type
|
178
|
+
|
179
|
+
# shortcuts
|
180
|
+
|
181
|
+
# Parse a String to a DBus::Type::Type
|
182
|
+
def type(string_type)
|
183
|
+
Type::Parser.new(string_type).parse[0]
|
184
|
+
end
|
185
|
+
module_function :type
|
186
|
+
|
187
|
+
# Make an explicit [Type, value] pair
|
188
|
+
def variant(string_type, value)
|
189
|
+
[type(string_type), value]
|
190
|
+
end
|
191
|
+
module_function :variant
|
192
|
+
|
193
|
+
end # module DBus
|