em-ruby-dbus 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,31 @@
|
|
1
|
+
# copied from activesupport/core_ext from Rails, MIT license
|
2
|
+
# https://github.com/rails/rails/tree/5aa869861c192daceafe3a3ee50eb93f5a2b7bd2/activesupport/lib/active_support/core_ext
|
3
|
+
class Hash
|
4
|
+
# By default, only instances of Hash itself are extractable.
|
5
|
+
# Subclasses of Hash may implement this method and return
|
6
|
+
# true to declare themselves as extractable. If a Hash
|
7
|
+
# is extractable, Array#extract_options! pops it from
|
8
|
+
# the Array when it is the last element of the Array.
|
9
|
+
def extractable_options?
|
10
|
+
instance_of?(Hash)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Array
|
15
|
+
# Extracts options from a set of arguments. Removes and returns the last
|
16
|
+
# element in the array if it's a hash, otherwise returns a blank hash.
|
17
|
+
#
|
18
|
+
# def options(*args)
|
19
|
+
# args.extract_options!
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# options(1, 2) # => {}
|
23
|
+
# options(1, 2, a: :b) # => {:a=>:b}
|
24
|
+
def extract_options!
|
25
|
+
if last.is_a?(Hash) && last.extractable_options?
|
26
|
+
pop
|
27
|
+
else
|
28
|
+
{}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# copied from activesupport/core_ext from Rails, MIT license
|
2
|
+
# https://github.com/rails/rails/tree/5aa869861c192daceafe3a3ee50eb93f5a2b7bd2/activesupport/lib/active_support/core_ext
|
3
|
+
require 'dbus/core_ext/kernel/singleton_class'
|
4
|
+
require 'dbus/core_ext/module/remove_method'
|
5
|
+
require 'dbus/core_ext/array/extract_options'
|
6
|
+
|
7
|
+
class Class
|
8
|
+
# Declare a class-level attribute whose value is inheritable by subclasses.
|
9
|
+
# Subclasses can change their own value and it will not impact parent class.
|
10
|
+
#
|
11
|
+
# class Base
|
12
|
+
# class_attribute :setting
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# class Subclass < Base
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# Base.setting = true
|
19
|
+
# Subclass.setting # => true
|
20
|
+
# Subclass.setting = false
|
21
|
+
# Subclass.setting # => false
|
22
|
+
# Base.setting # => true
|
23
|
+
#
|
24
|
+
# In the above case as long as Subclass does not assign a value to setting
|
25
|
+
# by performing <tt>Subclass.setting = _something_ </tt>, <tt>Subclass.setting</tt>
|
26
|
+
# would read value assigned to parent class. Once Subclass assigns a value then
|
27
|
+
# the value assigned by Subclass would be returned.
|
28
|
+
#
|
29
|
+
# This matches normal Ruby method inheritance: think of writing an attribute
|
30
|
+
# on a subclass as overriding the reader method. However, you need to be aware
|
31
|
+
# when using +class_attribute+ with mutable structures as +Array+ or +Hash+.
|
32
|
+
# In such cases, you don't want to do changes in places but use setters:
|
33
|
+
#
|
34
|
+
# Base.setting = []
|
35
|
+
# Base.setting # => []
|
36
|
+
# Subclass.setting # => []
|
37
|
+
#
|
38
|
+
# # Appending in child changes both parent and child because it is the same object:
|
39
|
+
# Subclass.setting << :foo
|
40
|
+
# Base.setting # => [:foo]
|
41
|
+
# Subclass.setting # => [:foo]
|
42
|
+
#
|
43
|
+
# # Use setters to not propagate changes:
|
44
|
+
# Base.setting = []
|
45
|
+
# Subclass.setting += [:foo]
|
46
|
+
# Base.setting # => []
|
47
|
+
# Subclass.setting # => [:foo]
|
48
|
+
#
|
49
|
+
# For convenience, an instance predicate method is defined as well.
|
50
|
+
# To skip it, pass <tt>instance_predicate: false</tt>.
|
51
|
+
#
|
52
|
+
# Subclass.setting? # => false
|
53
|
+
#
|
54
|
+
# Instances may overwrite the class value in the same way:
|
55
|
+
#
|
56
|
+
# Base.setting = true
|
57
|
+
# object = Base.new
|
58
|
+
# object.setting # => true
|
59
|
+
# object.setting = false
|
60
|
+
# object.setting # => false
|
61
|
+
# Base.setting # => true
|
62
|
+
#
|
63
|
+
# To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
|
64
|
+
#
|
65
|
+
# object.setting # => NoMethodError
|
66
|
+
# object.setting? # => NoMethodError
|
67
|
+
#
|
68
|
+
# To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
|
69
|
+
#
|
70
|
+
# object.setting = false # => NoMethodError
|
71
|
+
#
|
72
|
+
# To opt out of both instance methods, pass <tt>instance_accessor: false</tt>.
|
73
|
+
def class_attribute(*attrs)
|
74
|
+
options = attrs.extract_options!
|
75
|
+
instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true)
|
76
|
+
instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true)
|
77
|
+
instance_predicate = options.fetch(:instance_predicate, true)
|
78
|
+
|
79
|
+
attrs.each do |name|
|
80
|
+
define_singleton_method(name) { nil }
|
81
|
+
define_singleton_method("#{name}?") { !!public_send(name) } if instance_predicate
|
82
|
+
|
83
|
+
ivar = "@#{name}"
|
84
|
+
|
85
|
+
define_singleton_method("#{name}=") do |val|
|
86
|
+
singleton_class.class_eval do
|
87
|
+
remove_possible_method(name)
|
88
|
+
define_method(name) { val }
|
89
|
+
end
|
90
|
+
|
91
|
+
if singleton_class?
|
92
|
+
class_eval do
|
93
|
+
remove_possible_method(name)
|
94
|
+
define_method(name) do
|
95
|
+
if instance_variable_defined? ivar
|
96
|
+
instance_variable_get ivar
|
97
|
+
else
|
98
|
+
singleton_class.send name
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
val
|
104
|
+
end
|
105
|
+
|
106
|
+
if instance_reader
|
107
|
+
remove_possible_method name
|
108
|
+
define_method(name) do
|
109
|
+
if instance_variable_defined?(ivar)
|
110
|
+
instance_variable_get ivar
|
111
|
+
else
|
112
|
+
self.class.public_send name
|
113
|
+
end
|
114
|
+
end
|
115
|
+
define_method("#{name}?") { !!public_send(name) } if instance_predicate
|
116
|
+
end
|
117
|
+
|
118
|
+
attr_writer name if instance_writer
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
unless respond_to?(:singleton_class?)
|
125
|
+
def singleton_class?
|
126
|
+
ancestors.first != self
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# copied from activesupport/core_ext from Rails, MIT license
|
2
|
+
# https://github.com/rails/rails/tree/5aa869861c192daceafe3a3ee50eb93f5a2b7bd2/activesupport/lib/active_support/core_ext
|
3
|
+
module Kernel
|
4
|
+
# class_eval on an object acts like singleton_class.class_eval.
|
5
|
+
def class_eval(*args, &block)
|
6
|
+
singleton_class.class_eval(*args, &block)
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# copied from activesupport/core_ext from Rails, MIT license
|
2
|
+
# https://github.com/rails/rails/tree/5aa869861c192daceafe3a3ee50eb93f5a2b7bd2/activesupport/lib/active_support/core_ext
|
3
|
+
class Module
|
4
|
+
def remove_possible_method(method)
|
5
|
+
if method_defined?(method) || private_method_defined?(method)
|
6
|
+
undef_method(method)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def redefine_method(method, &block)
|
11
|
+
remove_possible_method(method)
|
12
|
+
define_method(method, &block)
|
13
|
+
end
|
14
|
+
end
|
data/lib/dbus/error.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# error.rb
|
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
|
+
# Represents a D-Bus Error, both on the client and server side.
|
13
|
+
class Error < StandardError
|
14
|
+
# error_name. +message+ is inherited from +Exception+
|
15
|
+
attr_reader :name
|
16
|
+
# for received errors, the raw D-Bus message
|
17
|
+
attr_reader :dbus_message
|
18
|
+
|
19
|
+
# If +msg+ is a +DBus::Message+, its contents is used for initialization.
|
20
|
+
# Otherwise, +msg+ is taken as a string and +name+ is used.
|
21
|
+
def initialize(msg, name = "org.freedesktop.DBus.Error.Failed")
|
22
|
+
if msg.is_a? DBus::Message
|
23
|
+
@dbus_message = msg
|
24
|
+
@name = msg.error_name
|
25
|
+
super(msg.params[0]) # or nil
|
26
|
+
if msg.params[1].is_a? Array
|
27
|
+
set_backtrace msg.params[1]
|
28
|
+
end
|
29
|
+
else
|
30
|
+
@name = name
|
31
|
+
super(msg)
|
32
|
+
end
|
33
|
+
# TODO validate error name
|
34
|
+
end
|
35
|
+
end # class Error
|
36
|
+
|
37
|
+
# @example raise a generic error
|
38
|
+
# raise DBus.error, "message"
|
39
|
+
# @example raise a specific error
|
40
|
+
# raise DBus.error("org.example.Error.SeatOccupied"), "Seat #{n} is occupied"
|
41
|
+
def error(name = "org.freedesktop.DBus.Error.Failed")
|
42
|
+
# message will be set by Kernel.raise
|
43
|
+
DBus::Error.new(nil, name)
|
44
|
+
end
|
45
|
+
module_function :error
|
46
|
+
end # module DBus
|
data/lib/dbus/export.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
# dbus/introspection.rb - module containing a low-level D-Bus introspection implementation
|
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
|
+
require 'thread'
|
12
|
+
|
13
|
+
module DBus
|
14
|
+
# Exported object type
|
15
|
+
# = Exportable D-Bus object class
|
16
|
+
#
|
17
|
+
# Objects that are going to be exported by a D-Bus service
|
18
|
+
# should inherit from this class. At the client side, use ProxyObject.
|
19
|
+
class Object
|
20
|
+
# The path of the object.
|
21
|
+
attr_reader :path
|
22
|
+
# The interfaces that the object supports. Hash: String => Interface
|
23
|
+
class_attribute :intfs
|
24
|
+
# The service that the object is exported by.
|
25
|
+
attr_writer :service
|
26
|
+
|
27
|
+
@@cur_intf = nil # Interface
|
28
|
+
@@intfs_mutex = Mutex.new
|
29
|
+
|
30
|
+
# Create a new object with a given _path_.
|
31
|
+
# Use Service#export to export it.
|
32
|
+
def initialize(path)
|
33
|
+
@path = path
|
34
|
+
@service = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
# State that the object implements the given _intf_.
|
38
|
+
def implements(intf)
|
39
|
+
# use a setter
|
40
|
+
self.intfs = (self.intfs || {}).merge({intf.name => intf})
|
41
|
+
end
|
42
|
+
|
43
|
+
# Dispatch a message _msg_ to call exported methods
|
44
|
+
def dispatch(msg)
|
45
|
+
case msg.message_type
|
46
|
+
when Message::METHOD_CALL
|
47
|
+
reply = nil
|
48
|
+
begin
|
49
|
+
if not self.intfs[msg.interface]
|
50
|
+
raise DBus.error("org.freedesktop.DBus.Error.UnknownMethod"),
|
51
|
+
"Interface \"#{msg.interface}\" of object \"#{msg.path}\" doesn't exist"
|
52
|
+
end
|
53
|
+
meth = self.intfs[msg.interface].methods[msg.member.to_sym]
|
54
|
+
if not meth
|
55
|
+
raise DBus.error("org.freedesktop.DBus.Error.UnknownMethod"),
|
56
|
+
"Method \"#{msg.member}\" on interface \"#{msg.interface}\" of object \"#{msg.path}\" doesn't exist"
|
57
|
+
end
|
58
|
+
methname = Object.make_method_name(msg.interface, msg.member)
|
59
|
+
retdata = method(methname).call(*msg.params)
|
60
|
+
retdata = [*retdata]
|
61
|
+
|
62
|
+
reply = Message.method_return(msg)
|
63
|
+
meth.rets.zip(retdata).each do |rsig, rdata|
|
64
|
+
reply.add_param(rsig.type, rdata)
|
65
|
+
end
|
66
|
+
rescue => ex
|
67
|
+
dbus_msg_exc = msg.annotate_exception(ex)
|
68
|
+
reply = ErrorMessage.from_exception(dbus_msg_exc).reply_to(msg)
|
69
|
+
end
|
70
|
+
@service.bus.message_queue.push(reply)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Select (and create) the interface that the following defined methods
|
75
|
+
# belong to.
|
76
|
+
def self.dbus_interface(s)
|
77
|
+
@@intfs_mutex.synchronize do
|
78
|
+
unless @@cur_intf = (self.intfs && self.intfs[s])
|
79
|
+
@@cur_intf = Interface.new(s)
|
80
|
+
self.intfs = (self.intfs || {}).merge({s => @@cur_intf})
|
81
|
+
end
|
82
|
+
yield
|
83
|
+
@@cur_intf = nil
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Dummy undefined interface class.
|
88
|
+
class UndefinedInterface < ScriptError
|
89
|
+
def initialize(sym)
|
90
|
+
super "No interface specified for #{sym}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Defines an exportable method on the object with the given name _sym_,
|
95
|
+
# _prototype_ and the code in a block.
|
96
|
+
def self.dbus_method(sym, protoype = "", &block)
|
97
|
+
raise UndefinedInterface, sym if @@cur_intf.nil?
|
98
|
+
@@cur_intf.define(Method.new(sym.to_s).from_prototype(protoype))
|
99
|
+
define_method(Object.make_method_name(@@cur_intf.name, sym.to_s), &block)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Emits a signal from the object with the given _interface_, signal
|
103
|
+
# _sig_ and arguments _args_.
|
104
|
+
def emit(intf, sig, *args)
|
105
|
+
@service.bus.emit(@service, self, intf, sig, *args)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Defines a signal for the object with a given name _sym_ and _prototype_.
|
109
|
+
def self.dbus_signal(sym, protoype = "")
|
110
|
+
raise UndefinedInterface, sym if @@cur_intf.nil?
|
111
|
+
cur_intf = @@cur_intf
|
112
|
+
signal = Signal.new(sym.to_s).from_prototype(protoype)
|
113
|
+
cur_intf.define(Signal.new(sym.to_s).from_prototype(protoype))
|
114
|
+
define_method(sym.to_s) do |*args|
|
115
|
+
emit(cur_intf, signal, *args)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
####################################################################
|
120
|
+
private
|
121
|
+
|
122
|
+
# Helper method that returns a method name generated from the interface
|
123
|
+
# name _intfname_ and method name _methname_.
|
124
|
+
def self.make_method_name(intfname, methname)
|
125
|
+
"#{intfname}%%#{methname}"
|
126
|
+
end
|
127
|
+
end # class Object
|
128
|
+
end # module DBus
|
@@ -0,0 +1,219 @@
|
|
1
|
+
# dbus/introspection.rb - module containing a low-level D-Bus introspection implementation
|
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
|
+
# Regular expressions that should match all method names.
|
13
|
+
MethodSignalRE = /^[A-Za-z][A-Za-z0-9_]*$/
|
14
|
+
# Regular expressions that should match all interface names.
|
15
|
+
InterfaceElementRE = /^[A-Za-z][A-Za-z0-9_]*$/
|
16
|
+
|
17
|
+
# Exception raised when an unknown signal is used.
|
18
|
+
class UnknownSignal < Exception
|
19
|
+
end
|
20
|
+
|
21
|
+
# Exception raised when an invalid class definition is encountered.
|
22
|
+
class InvalidClassDefinition < Exception
|
23
|
+
end
|
24
|
+
|
25
|
+
# = D-Bus interface class
|
26
|
+
#
|
27
|
+
# This class is the interface descriptor. In most cases, the Introspect()
|
28
|
+
# method call instantiates and configures this class for us.
|
29
|
+
#
|
30
|
+
# It also is the local definition of interface exported by the program.
|
31
|
+
# At the client side, see ProxyObjectInterface
|
32
|
+
class Interface
|
33
|
+
# The name of the interface. String
|
34
|
+
attr_reader :name
|
35
|
+
# The methods that are part of the interface. Hash: Symbol => DBus::Method
|
36
|
+
attr_reader :methods
|
37
|
+
# The signals that are part of the interface. Hash: Symbol => Signal
|
38
|
+
attr_reader :signals
|
39
|
+
|
40
|
+
# Creates a new interface with a given _name_.
|
41
|
+
def initialize(name)
|
42
|
+
validate_name(name)
|
43
|
+
@name = name
|
44
|
+
@methods, @signals = Hash.new, Hash.new
|
45
|
+
end
|
46
|
+
|
47
|
+
# Validates a service _name_.
|
48
|
+
def validate_name(name)
|
49
|
+
raise InvalidIntrospectionData if name.bytesize > 255
|
50
|
+
raise InvalidIntrospectionData if name =~ /^\./ or name =~ /\.$/
|
51
|
+
raise InvalidIntrospectionData if name =~ /\.\./
|
52
|
+
raise InvalidIntrospectionData if not name =~ /\./
|
53
|
+
name.split(".").each do |element|
|
54
|
+
raise InvalidIntrospectionData if not element =~ InterfaceElementRE
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Helper method for defining a method _m_.
|
59
|
+
def define(m)
|
60
|
+
if m.kind_of?(Method)
|
61
|
+
@methods[m.name.to_sym] = m
|
62
|
+
elsif m.kind_of?(Signal)
|
63
|
+
@signals[m.name.to_sym] = m
|
64
|
+
end
|
65
|
+
end
|
66
|
+
alias :<< :define
|
67
|
+
|
68
|
+
# Defines a method with name _id_ and a given _prototype_ in the
|
69
|
+
# interface.
|
70
|
+
def define_method(id, prototype)
|
71
|
+
m = Method.new(id)
|
72
|
+
m.from_prototype(prototype)
|
73
|
+
define(m)
|
74
|
+
end
|
75
|
+
end # class Interface
|
76
|
+
|
77
|
+
# = A formal parameter has a name and a type
|
78
|
+
class FormalParameter
|
79
|
+
attr_reader :name
|
80
|
+
attr_reader :type
|
81
|
+
|
82
|
+
def initialize(name, type)
|
83
|
+
@name = name
|
84
|
+
@type = type
|
85
|
+
end
|
86
|
+
|
87
|
+
# backward compatibility, deprecated
|
88
|
+
def [](index)
|
89
|
+
case index
|
90
|
+
when 0 then name
|
91
|
+
when 1 then type
|
92
|
+
else nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# = D-Bus interface element class
|
98
|
+
#
|
99
|
+
# This is a generic class for entities that are part of the interface
|
100
|
+
# such as methods and signals.
|
101
|
+
class InterfaceElement
|
102
|
+
# The name of the interface element. Symbol
|
103
|
+
attr_reader :name
|
104
|
+
# The parameters of the interface element. Array: FormalParameter
|
105
|
+
attr_reader :params
|
106
|
+
|
107
|
+
# Validates element _name_.
|
108
|
+
def validate_name(name)
|
109
|
+
if (not name =~ MethodSignalRE) or (name.bytesize > 255)
|
110
|
+
raise InvalidMethodName, name
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Creates a new element with the given _name_.
|
115
|
+
def initialize(name)
|
116
|
+
validate_name(name.to_s)
|
117
|
+
@name = name
|
118
|
+
@params = Array.new
|
119
|
+
end
|
120
|
+
|
121
|
+
# Adds a formal parameter with _name_ and _signature_
|
122
|
+
# (See also Message#add_param which takes signature+value)
|
123
|
+
def add_fparam(name, signature)
|
124
|
+
@params << FormalParameter.new(name, signature)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Deprecated, for backward compatibility
|
128
|
+
def add_param(name_signature_pair)
|
129
|
+
add_fparam(*name_signature_pair)
|
130
|
+
end
|
131
|
+
end # class InterfaceElement
|
132
|
+
|
133
|
+
# = D-Bus interface method class
|
134
|
+
#
|
135
|
+
# This is a class representing methods that are part of an interface.
|
136
|
+
class Method < InterfaceElement
|
137
|
+
# The list of return values for the method. Array: FormalParameter
|
138
|
+
attr_reader :rets
|
139
|
+
|
140
|
+
# Creates a new method interface element with the given _name_.
|
141
|
+
def initialize(name)
|
142
|
+
super(name)
|
143
|
+
@rets = Array.new
|
144
|
+
end
|
145
|
+
|
146
|
+
# Add a return value _name_ and _signature_.
|
147
|
+
def add_return(name, signature)
|
148
|
+
@rets << FormalParameter.new(name, signature)
|
149
|
+
end
|
150
|
+
|
151
|
+
# Add parameter types by parsing the given _prototype_.
|
152
|
+
def from_prototype(prototype)
|
153
|
+
prototype.split(/, */).each do |arg|
|
154
|
+
arg = arg.split(" ")
|
155
|
+
raise InvalidClassDefinition if arg.size != 2
|
156
|
+
dir, arg = arg
|
157
|
+
if arg =~ /:/
|
158
|
+
arg = arg.split(":")
|
159
|
+
name, sig = arg
|
160
|
+
else
|
161
|
+
sig = arg
|
162
|
+
end
|
163
|
+
case dir
|
164
|
+
when "in"
|
165
|
+
add_fparam(name, sig)
|
166
|
+
when "out"
|
167
|
+
add_return(name, sig)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
self
|
171
|
+
end
|
172
|
+
|
173
|
+
# Return an XML string representation of the method interface elment.
|
174
|
+
def to_xml
|
175
|
+
xml = %{<method name="#{@name}">\n}
|
176
|
+
@params.each do |param|
|
177
|
+
name = param.name ? %{name="#{param.name}" } : ""
|
178
|
+
xml += %{<arg #{name}direction="in" type="#{param.type}"/>\n}
|
179
|
+
end
|
180
|
+
@rets.each do |param|
|
181
|
+
name = param.name ? %{name="#{param.name}" } : ""
|
182
|
+
xml += %{<arg #{name}direction="out" type="#{param.type}"/>\n}
|
183
|
+
end
|
184
|
+
xml += %{</method>\n}
|
185
|
+
xml
|
186
|
+
end
|
187
|
+
end # class Method
|
188
|
+
|
189
|
+
# = D-Bus interface signal class
|
190
|
+
#
|
191
|
+
# This is a class representing signals that are part of an interface.
|
192
|
+
class Signal < InterfaceElement
|
193
|
+
# Add parameter types based on the given _prototype_.
|
194
|
+
def from_prototype(prototype)
|
195
|
+
prototype.split(/, */).each do |arg|
|
196
|
+
if arg =~ /:/
|
197
|
+
arg = arg.split(":")
|
198
|
+
name, sig = arg
|
199
|
+
else
|
200
|
+
sig = arg
|
201
|
+
end
|
202
|
+
add_fparam(name, sig)
|
203
|
+
end
|
204
|
+
self
|
205
|
+
end
|
206
|
+
|
207
|
+
# Return an XML string representation of the signal interface elment.
|
208
|
+
def to_xml
|
209
|
+
xml = %{<signal name="#{@name}">\n}
|
210
|
+
@params.each do |param|
|
211
|
+
name = param.name ? %{name="#{param.name}" } : ""
|
212
|
+
xml += %{<arg #{name}type="#{param.type}"/>\n}
|
213
|
+
end
|
214
|
+
xml += %{</signal>\n}
|
215
|
+
xml
|
216
|
+
end
|
217
|
+
end # class Signal
|
218
|
+
end # module DBus
|
219
|
+
|