ruby-dbus 0.14.0 → 0.17.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.
Files changed (44) hide show
  1. checksums.yaml +5 -5
  2. data/NEWS.md +44 -0
  3. data/README.md +3 -5
  4. data/Rakefile +26 -8
  5. data/VERSION +1 -1
  6. data/doc/Reference.md +84 -1
  7. data/examples/doc/_extract_examples +5 -0
  8. data/examples/gdbus/gdbus +21 -20
  9. data/examples/service/call_service.rb +1 -1
  10. data/lib/dbus/auth.rb +8 -8
  11. data/lib/dbus/bus.rb +23 -11
  12. data/lib/dbus/bus_name.rb +27 -0
  13. data/lib/dbus/core_ext/class/attribute.rb +23 -41
  14. data/lib/dbus/core_ext/module/redefine_method.rb +51 -0
  15. data/lib/dbus/introspect.rb +71 -26
  16. data/lib/dbus/marshall.rb +2 -1
  17. data/lib/dbus/message_queue.rb +17 -14
  18. data/lib/dbus/object.rb +380 -0
  19. data/lib/dbus/object_path.rb +24 -0
  20. data/lib/dbus/proxy_object.rb +11 -2
  21. data/lib/dbus/proxy_object_interface.rb +1 -0
  22. data/lib/dbus/type.rb +18 -0
  23. data/lib/dbus/xml.rb +4 -8
  24. data/lib/dbus.rb +3 -2
  25. data/ruby-dbus.gemspec +11 -9
  26. data/spec/binding_spec.rb +6 -2
  27. data/spec/bus_name_spec.rb +25 -0
  28. data/spec/client_robustness_spec.rb +25 -0
  29. data/spec/introspect_xml_parser_spec.rb +13 -13
  30. data/spec/main_loop_spec.rb +1 -1
  31. data/spec/object_path_spec.rb +23 -0
  32. data/spec/property_spec.rb +53 -3
  33. data/spec/proxy_object_spec.rb +9 -0
  34. data/spec/service_newapi.rb +20 -66
  35. data/spec/session_bus_spec.rb +6 -6
  36. data/spec/signal_spec.rb +33 -18
  37. data/spec/spec_helper.rb +23 -11
  38. data/spec/tools/dbus-limited-session.conf +4 -0
  39. data/spec/type_spec.rb +2 -2
  40. metadata +32 -15
  41. data/lib/dbus/core_ext/array/extract_options.rb +0 -31
  42. data/lib/dbus/core_ext/kernel/singleton_class.rb +0 -8
  43. data/lib/dbus/core_ext/module/remove_method.rb +0 -14
  44. data/lib/dbus/export.rb +0 -130
@@ -0,0 +1,380 @@
1
+ # This file is part of the ruby-dbus project
2
+ # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
3
+ #
4
+ # This library is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU Lesser General Public
6
+ # License, version 2.1 as published by the Free Software Foundation.
7
+ # See the file "COPYING" for the exact licensing terms.
8
+
9
+ require "thread"
10
+ require_relative "core_ext/class/attribute"
11
+
12
+ module DBus
13
+ PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties".freeze
14
+
15
+ # Exported object type
16
+ # = Exportable D-Bus object class
17
+ #
18
+ # Objects that are going to be exported by a D-Bus service
19
+ # should inherit from this class. At the client side, use {ProxyObject}.
20
+ class Object
21
+ # The path of the object.
22
+ attr_reader :path
23
+ # The interfaces that the object supports. Hash: String => Interface
24
+ my_class_attribute :intfs
25
+ self.intfs = {}
26
+
27
+ # The service that the object is exported by.
28
+ attr_writer :service
29
+
30
+ @@cur_intf = nil # Interface
31
+ @@intfs_mutex = Mutex.new
32
+
33
+ # Create a new object with a given _path_.
34
+ # Use Service#export to export it.
35
+ def initialize(path)
36
+ @path = path
37
+ @service = nil
38
+ end
39
+
40
+ # Dispatch a message _msg_ to call exported methods
41
+ def dispatch(msg)
42
+ case msg.message_type
43
+ when Message::METHOD_CALL
44
+ reply = nil
45
+ begin
46
+ if !intfs[msg.interface]
47
+ raise DBus.error("org.freedesktop.DBus.Error.UnknownMethod"),
48
+ "Interface \"#{msg.interface}\" of object \"#{msg.path}\" doesn't exist"
49
+ end
50
+ meth = intfs[msg.interface].methods[msg.member.to_sym]
51
+ if !meth
52
+ raise DBus.error("org.freedesktop.DBus.Error.UnknownMethod"),
53
+ "Method \"#{msg.member}\" on interface \"#{msg.interface}\" of object \"#{msg.path}\" doesn't exist"
54
+ end
55
+ methname = Object.make_method_name(msg.interface, msg.member)
56
+ retdata = method(methname).call(*msg.params)
57
+ retdata = [*retdata]
58
+
59
+ reply = Message.method_return(msg)
60
+ meth.rets.zip(retdata).each do |rsig, rdata|
61
+ reply.add_param(rsig.type, rdata)
62
+ end
63
+ rescue StandardError => ex
64
+ dbus_msg_exc = msg.annotate_exception(ex)
65
+ reply = ErrorMessage.from_exception(dbus_msg_exc).reply_to(msg)
66
+ end
67
+ @service.bus.message_queue.push(reply)
68
+ end
69
+ end
70
+
71
+ # Select (and create) the interface that the following defined methods
72
+ # belong to.
73
+ def self.dbus_interface(s)
74
+ @@intfs_mutex.synchronize do
75
+ @@cur_intf = intfs[s]
76
+ if !@@cur_intf
77
+ @@cur_intf = Interface.new(s)
78
+ # As this is a mutable class_attr, we cannot use
79
+ # self.intfs[s] = @@cur_intf # Hash#[]=
80
+ # as that would modify parent class attr in place.
81
+ # Using the setter lets a subclass have the new value
82
+ # while the superclass keeps the old one.
83
+ self.intfs = intfs.merge(s => @@cur_intf)
84
+ end
85
+ yield
86
+ @@cur_intf = nil
87
+ end
88
+ end
89
+
90
+ # Forgetting to declare the interface for a method/signal/property
91
+ # is a ScriptError.
92
+ class UndefinedInterface < ScriptError # rubocop:disable Lint/InheritException
93
+ def initialize(sym)
94
+ super "No interface specified for #{sym}. Enclose it in dbus_interface."
95
+ end
96
+ end
97
+
98
+ # A read-write property accessing an instance variable.
99
+ # A combination of `attr_accessor` and {.dbus_accessor}.
100
+ #
101
+ # PropertiesChanged signal will be emitted whenever `foo_bar=` is used
102
+ # but not when @foo_bar is written directly.
103
+ #
104
+ # @param ruby_name [Symbol] :foo_bar is exposed as FooBar;
105
+ # use dbus_name to override
106
+ # @param type a signature like "s" or "a(uus)" or Type::STRING
107
+ # @param dbus_name [String] if not given it is made
108
+ # by CamelCasing the ruby_name. foo_bar becomes FooBar
109
+ # to convert the Ruby convention to the DBus convention.
110
+ # @return [void]
111
+ def self.dbus_attr_accessor(ruby_name, type, dbus_name: nil)
112
+ attr_accessor(ruby_name)
113
+ dbus_accessor(ruby_name, type, dbus_name: dbus_name)
114
+ end
115
+
116
+ # A read-only property accessing an instance variable.
117
+ # A combination of `attr_reader` and {.dbus_reader}.
118
+ #
119
+ # Whenever the property value gets changed from "inside" the object,
120
+ # you should emit the `PropertiesChanged` signal by calling
121
+ #
122
+ # object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {dbus_name.to_s => value}, [])
123
+ #
124
+ # or, omitting the value in the signal,
125
+ #
126
+ # object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {}, [dbus_name.to_s])
127
+ #
128
+ # @param (see .dbus_attr_accessor)
129
+ # @return (see .dbus_attr_accessor)
130
+ def self.dbus_attr_reader(ruby_name, type, dbus_name: nil)
131
+ attr_reader(ruby_name)
132
+ dbus_reader(ruby_name, type, dbus_name: dbus_name)
133
+ end
134
+
135
+ # A write-only property accessing an instance variable.
136
+ # A combination of `attr_writer` and {.dbus_writer}.
137
+ #
138
+ # @param (see .dbus_attr_accessor)
139
+ # @return (see .dbus_attr_accessor)
140
+ def self.dbus_attr_writer(ruby_name, type, dbus_name: nil)
141
+ attr_writer(ruby_name)
142
+ dbus_writer(ruby_name, type, dbus_name: dbus_name)
143
+ end
144
+
145
+ # A read-write property using a pair of reader/writer methods
146
+ # (which must already exist).
147
+ # (To directly access an instance variable, use {.dbus_attr_accessor} instead)
148
+ #
149
+ # Uses {.dbus_watcher} to set up the PropertiesChanged signal.
150
+ #
151
+ # @param (see .dbus_attr_accessor)
152
+ # @return (see .dbus_attr_accessor)
153
+ def self.dbus_accessor(ruby_name, type, dbus_name: nil)
154
+ raise UndefinedInterface, ruby_name if @@cur_intf.nil?
155
+
156
+ dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
157
+ property = Property.new(dbus_name, type, :readwrite, ruby_name: ruby_name)
158
+ @@cur_intf.define(property)
159
+
160
+ dbus_watcher(ruby_name, dbus_name: dbus_name)
161
+ end
162
+
163
+ # A read-only property accessing a reader method (which must already exist).
164
+ # (To directly access an instance variable, use {.dbus_attr_reader} instead)
165
+ #
166
+ # Whenever the property value gets changed from "inside" the object,
167
+ # you should emit the `PropertiesChanged` signal by calling
168
+ #
169
+ # object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {dbus_name.to_s => value}, [])
170
+ #
171
+ # or, omitting the value in the signal,
172
+ #
173
+ # object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {}, [dbus_name.to_s])
174
+ #
175
+ # @param (see .dbus_attr_accessor)
176
+ # @return (see .dbus_attr_accessor)
177
+ def self.dbus_reader(ruby_name, type, dbus_name: nil)
178
+ raise UndefinedInterface, ruby_name if @@cur_intf.nil?
179
+
180
+ dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
181
+ property = Property.new(dbus_name, type, :read, ruby_name: ruby_name)
182
+ @@cur_intf.define(property)
183
+ end
184
+
185
+ # A write-only property accessing a writer method (which must already exist).
186
+ # (To directly access an instance variable, use {.dbus_attr_writer} instead)
187
+ #
188
+ # Uses {.dbus_watcher} to set up the PropertiesChanged signal.
189
+ #
190
+ # @param (see .dbus_attr_accessor)
191
+ # @return (see .dbus_attr_accessor)
192
+ def self.dbus_writer(ruby_name, type, dbus_name: nil)
193
+ raise UndefinedInterface, ruby_name if @@cur_intf.nil?
194
+
195
+ dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
196
+ property = Property.new(dbus_name, type, :write, ruby_name: ruby_name)
197
+ @@cur_intf.define(property)
198
+
199
+ dbus_watcher(ruby_name, dbus_name: dbus_name)
200
+ end
201
+
202
+ # Enables automatic sending of the PropertiesChanged signal.
203
+ # For *ruby_name* `foo_bar`, wrap `foo_bar=` so that it sends
204
+ # the signal for FooBar.
205
+ # The original version remains as `_original_foo_bar=`.
206
+ #
207
+ # @param ruby_name [Symbol] :foo_bar and :foo_bar= both mean the same thing
208
+ # @param dbus_name [String] if not given it is made
209
+ # by CamelCasing the ruby_name. foo_bar becomes FooBar
210
+ # to convert the Ruby convention to the DBus convention.
211
+ # @return [void]
212
+ def self.dbus_watcher(ruby_name, dbus_name: nil)
213
+ raise UndefinedInterface, ruby_name if @@cur_intf.nil?
214
+ cur_intf = @@cur_intf
215
+
216
+ ruby_name = ruby_name.to_s.sub(/=$/, "").to_sym
217
+ ruby_name_eq = "#{ruby_name}=".to_sym
218
+ original_ruby_name_eq = "_original_#{ruby_name_eq}"
219
+
220
+ dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
221
+
222
+ # the argument order is alias_method(new_name, existing_name)
223
+ alias_method original_ruby_name_eq, ruby_name_eq
224
+ define_method ruby_name_eq do |value|
225
+ public_send(original_ruby_name_eq, value)
226
+
227
+ # TODO: respect EmitsChangedSignal to use invalidated_properties instead
228
+ # PropertiesChanged, "interface:s, changed_properties:a{sv}, invalidated_properties:as"
229
+ PropertiesChanged(cur_intf.name, { dbus_name.to_s => value }, [])
230
+ end
231
+ end
232
+
233
+ # Defines an exportable method on the object with the given name _sym_,
234
+ # _prototype_ and the code in a block.
235
+ # @param prototype [Prototype]
236
+ def self.dbus_method(sym, prototype = "", &block)
237
+ raise UndefinedInterface, sym if @@cur_intf.nil?
238
+ @@cur_intf.define(Method.new(sym.to_s).from_prototype(prototype))
239
+
240
+ ruby_name = Object.make_method_name(@@cur_intf.name, sym.to_s)
241
+ # ::Module#define_method(name) { body }
242
+ define_method(ruby_name, &block)
243
+ end
244
+
245
+ # Emits a signal from the object with the given _interface_, signal
246
+ # _sig_ and arguments _args_.
247
+ # @param intf [Interface]
248
+ # @param sig [Signal]
249
+ # @param args arguments for the signal
250
+ def emit(intf, sig, *args)
251
+ @service.bus.emit(@service, self, intf, sig, *args)
252
+ end
253
+
254
+ # Defines a signal for the object with a given name _sym_ and _prototype_.
255
+ def self.dbus_signal(sym, prototype = "")
256
+ raise UndefinedInterface, sym if @@cur_intf.nil?
257
+ cur_intf = @@cur_intf
258
+ signal = Signal.new(sym.to_s).from_prototype(prototype)
259
+ cur_intf.define(Signal.new(sym.to_s).from_prototype(prototype))
260
+
261
+ # ::Module#define_method(name) { body }
262
+ define_method(sym.to_s) do |*args|
263
+ emit(cur_intf, signal, *args)
264
+ end
265
+ end
266
+
267
+ # Helper method that returns a method name generated from the interface
268
+ # name _intfname_ and method name _methname_.
269
+ # @api private
270
+ def self.make_method_name(intfname, methname)
271
+ "#{intfname}%%#{methname}"
272
+ end
273
+
274
+ # TODO: borrow a proven implementation
275
+ # @param str [String]
276
+ # @return [String]
277
+ # @api private
278
+ def self.camelize(str)
279
+ str.split(/_/).map(&:capitalize).join("")
280
+ end
281
+
282
+ # Make a D-Bus conventional name, CamelCased.
283
+ # @param ruby_name [String,Symbol] eg :do_something
284
+ # @param dbus_name [String,Symbol,nil] use this if given
285
+ # @return [Symbol] eg DoSomething
286
+ def self.make_dbus_name(ruby_name, dbus_name: nil)
287
+ dbus_name ||= camelize(ruby_name.to_s)
288
+ dbus_name.to_sym
289
+ end
290
+
291
+ # @param interface_name [String]
292
+ # @param property_name [String]
293
+ # @return [Property]
294
+ # @raise [DBus::Error]
295
+ # @api private
296
+ def dbus_lookup_property(interface_name, property_name)
297
+ # what should happen for unknown properties
298
+ # plasma: InvalidArgs (propname), UnknownInterface (interface)
299
+ # systemd: UnknownProperty
300
+ interface = intfs[interface_name]
301
+ if !interface
302
+ raise DBus.error("org.freedesktop.DBus.Error.UnknownProperty"),
303
+ "Property '#{interface_name}.#{property_name}' (on object '#{@path}') not found: no such interface"
304
+ end
305
+
306
+ property = interface.properties[property_name.to_sym]
307
+ if !property
308
+ raise DBus.error("org.freedesktop.DBus.Error.UnknownProperty"),
309
+ "Property '#{interface_name}.#{property_name}' (on object '#{@path}') not found"
310
+ end
311
+
312
+ property
313
+ end
314
+
315
+ ####################################################################
316
+
317
+ # use the above defined methods to declare the property-handling
318
+ # interfaces and methods
319
+
320
+ dbus_interface PROPERTY_INTERFACE do
321
+ dbus_method :Get, "in interface_name:s, in property_name:s, out value:v" do |interface_name, property_name|
322
+ property = dbus_lookup_property(interface_name, property_name)
323
+
324
+ if property.readable?
325
+ ruby_name = property.ruby_name
326
+ value = public_send(ruby_name)
327
+ [value]
328
+ else
329
+ raise DBus.error("org.freedesktop.DBus.Error.PropertyWriteOnly"),
330
+ "Property '#{interface_name}.#{property_name}' (on object '#{@path}') is not readable"
331
+ end
332
+ end
333
+
334
+ dbus_method :Set, "in interface_name:s, in property_name:s, in val:v" do |interface_name, property_name, value|
335
+ property = dbus_lookup_property(interface_name, property_name)
336
+
337
+ if property.writable?
338
+ ruby_name_eq = "#{property.ruby_name}="
339
+ public_send(ruby_name_eq, value)
340
+ else
341
+ raise DBus.error("org.freedesktop.DBus.Error.PropertyReadOnly"),
342
+ "Property '#{interface_name}.#{property_name}' (on object '#{@path}') is not writable"
343
+ end
344
+ end
345
+
346
+ dbus_method :GetAll, "in interface_name:s, out value:a{sv}" do |interface_name|
347
+ interface = intfs[interface_name]
348
+ if !interface
349
+ raise DBus.error("org.freedesktop.DBus.Error.UnknownProperty"),
350
+ "Properties '#{interface_name}.*' (on object '#{@path}') not found: no such interface"
351
+ end
352
+
353
+ p_hash = {}
354
+ interface.properties.each do |p_name, property|
355
+ next unless property.readable?
356
+
357
+ ruby_name = property.ruby_name
358
+ begin
359
+ # D-Bus spec says:
360
+ # > If GetAll is called with a valid interface name for which some
361
+ # > properties are not accessible to the caller (for example, due
362
+ # > to per-property access control implemented in the service),
363
+ # > those properties should be silently omitted from the result
364
+ # > array.
365
+ # so we will silently omit properties that fail to read.
366
+ # Get'ting them individually will send DBus.Error
367
+ p_hash[p_name.to_s] = public_send(ruby_name)
368
+ rescue StandardError
369
+ DBus.logger.debug "Property '#{interface_name}.#{p_name}' (on object '#{@path}')" \
370
+ " has raised during GetAll, omitting it"
371
+ end
372
+ end
373
+
374
+ [p_hash]
375
+ end
376
+
377
+ dbus_signal :PropertiesChanged, "interface:s, changed_properties:a{sv}, invalidated_properties:as"
378
+ end
379
+ end
380
+ end
@@ -0,0 +1,24 @@
1
+ # This file is part of the ruby-dbus project
2
+ # Copyright (C) 2019 Martin Vidner
3
+ #
4
+ # This library is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU Lesser General Public
6
+ # License, version 2.1 as published by the Free Software Foundation.
7
+ # See the file "COPYING" for the exact licensing terms.
8
+
9
+ module DBus
10
+ # A {::String} that validates at initialization time
11
+ class ObjectPath < String
12
+ # @raise Error if not a valid object path
13
+ def initialize(s)
14
+ unless self.class.valid?(s)
15
+ raise DBus::Error, "Invalid object path #{s.inspect}"
16
+ end
17
+ super
18
+ end
19
+
20
+ def self.valid?(s)
21
+ s == "/" || s =~ %r{\A(/[A-Za-z0-9_]+)+\z}
22
+ end
23
+ end
24
+ end
@@ -36,7 +36,7 @@ module DBus
36
36
  def initialize(bus, dest, path, api: ApiOptions::CURRENT)
37
37
  @bus = bus
38
38
  @destination = dest
39
- @path = path
39
+ @path = ObjectPath.new(path)
40
40
  @introspected = false
41
41
  @interfaces = {}
42
42
  @subnodes = []
@@ -44,6 +44,7 @@ module DBus
44
44
  end
45
45
 
46
46
  # Returns the interfaces of the object.
47
+ # @return [Array<String>] names of the interfaces
47
48
  def interfaces
48
49
  introspect unless introspected
49
50
  @interfaces.keys
@@ -54,7 +55,9 @@ module DBus
54
55
  # @return [ProxyObjectInterface]
55
56
  def [](intfname)
56
57
  introspect unless introspected
57
- @interfaces[intfname]
58
+ ifc = @interfaces[intfname]
59
+ raise DBus::Error, "no such interface `#{intfname}' on object `#{@path}'" unless ifc
60
+ ifc
58
61
  end
59
62
 
60
63
  # Maps the given interface name _intfname_ to the given interface _intf.
@@ -127,6 +130,11 @@ module DBus
127
130
  ####################################################
128
131
  private
129
132
 
133
+ # rubocop:disable Style/MethodMissing
134
+ # but https://github.com/rubocop-hq/ruby-style-guide#no-method-missing
135
+ # and http://blog.marc-andre.ca/2010/11/15/methodmissing-politely/
136
+ # have a point to be investigated
137
+
130
138
  # Handles all unkown methods, mostly to route method calls to the
131
139
  # default interface.
132
140
  def method_missing(name, *args, &reply_handler)
@@ -148,5 +156,6 @@ module DBus
148
156
  raise NoMethodError, "undefined method `#{name}' for DBus interface `#{@default_iface}' on object `#{@path}'"
149
157
  end
150
158
  end
159
+ # rubocop:enable Style/MethodMissing
151
160
  end # class ProxyObject
152
161
  end
@@ -87,6 +87,7 @@ module DBus
87
87
  end
88
88
 
89
89
  # Defines a proxied method on the interface.
90
+ # Better name: declare_method
90
91
  def define_method(methodname, prototype)
91
92
  m = Method.new(methodname)
92
93
  m.from_prototype(prototype)
data/lib/dbus/type.rb CHANGED
@@ -9,6 +9,24 @@
9
9
  # See the file "COPYING" for the exact licensing terms.
10
10
 
11
11
  module DBus
12
+ # Like a {Signature} but containing only a single complete type.
13
+ #
14
+ # For documentation purposes only.
15
+ class SingleCompleteType < String; end
16
+
17
+ # Zero or more {SingleCompleteType}s; its own type code is "g".
18
+ # For example "ssv" for a method taking two Strings and a Variant/
19
+ #
20
+ # For documentation purposes only.
21
+ class Signature < String; end
22
+
23
+ # Similar to {Signature} but for {DBus::Object.define_method},
24
+ # contains names and direction of the parameters.
25
+ # For example "in query:s, in case_sensitive:b, out results:ao".
26
+ #
27
+ # For documentation purposes only.
28
+ class Prototype < String; end
29
+
12
30
  # = D-Bus type module
13
31
  #
14
32
  # This module containts the constants of the types specified in the D-Bus
data/lib/dbus/xml.rb CHANGED
@@ -41,21 +41,17 @@ module DBus
41
41
 
42
42
  # required methods
43
43
  # returns node attribute value
44
- def [](key)
45
- end
44
+ def [](key); end
46
45
 
47
46
  # yields child nodes which match xpath of type AbstractXML::Node
48
- def each(xpath)
49
- end
47
+ def each(xpath); end
50
48
  end
51
49
  # required methods
52
50
  # initialize parser with xml string
53
- def initialize(xml)
54
- end
51
+ def initialize(xml); end
55
52
 
56
53
  # yields nodes which match xpath of type AbstractXML::Node
57
- def each(xpath)
58
- end
54
+ def each(xpath); end
59
55
  end
60
56
 
61
57
  class NokogiriParser < AbstractXML
data/lib/dbus.rb CHANGED
@@ -11,15 +11,16 @@
11
11
  require_relative "dbus/api_options"
12
12
  require_relative "dbus/auth"
13
13
  require_relative "dbus/bus"
14
- require_relative "dbus/core_ext/class/attribute"
14
+ require_relative "dbus/bus_name"
15
15
  require_relative "dbus/error"
16
- require_relative "dbus/export"
17
16
  require_relative "dbus/introspect"
18
17
  require_relative "dbus/logger"
19
18
  require_relative "dbus/marshall"
20
19
  require_relative "dbus/matchrule"
21
20
  require_relative "dbus/message"
22
21
  require_relative "dbus/message_queue"
22
+ require_relative "dbus/object"
23
+ require_relative "dbus/object_path"
23
24
  require_relative "dbus/proxy_object"
24
25
  require_relative "dbus/proxy_object_factory"
25
26
  require_relative "dbus/proxy_object_interface"
data/ruby-dbus.gemspec CHANGED
@@ -1,6 +1,5 @@
1
1
  # -*- ruby -*-
2
2
  require "rubygems"
3
- require "rake"
4
3
 
5
4
  GEMSPEC = Gem::Specification.new do |s|
6
5
  s.name = "ruby-dbus"
@@ -8,25 +7,28 @@ GEMSPEC = Gem::Specification.new do |s|
8
7
  s.summary = "Ruby module for interaction with D-Bus"
9
8
  s.description = "Pure Ruby module for interaction with D-Bus IPC system"
10
9
  s.version = File.read("VERSION").strip
11
- s.license = "LGPL v2.1"
10
+ s.license = "LGPL-2.1"
12
11
  s.author = "Ruby DBus Team"
13
- s.email = "ruby-dbus-devel@lists.luon.net"
14
- s.homepage = "https://trac.luon.net/ruby-dbus"
15
- s.files = FileList[
12
+ s.email = "martin.github@vidner.net"
13
+ s.homepage = "https://github.com/mvidner/ruby-dbus"
14
+ s.files = Dir[
16
15
  "{doc,examples,lib,spec}/**/*",
17
16
  "COPYING", "NEWS.md", "Rakefile", "README.md",
18
- "ruby-dbus.gemspec", "VERSION", ".rspec"].to_a.sort
17
+ "ruby-dbus.gemspec", "VERSION", ".rspec"
18
+ ]
19
19
  s.require_path = "lib"
20
20
 
21
- s.required_ruby_version = ">= 2.0.0"
21
+ s.required_ruby_version = ">= 2.1.0"
22
+
23
+ s.add_dependency "rexml"
22
24
 
23
25
  # This is optional
24
26
  # s.add_runtime_dependency "nokogiri"
25
27
 
26
- s.add_development_dependency "coveralls"
27
28
  s.add_development_dependency "packaging_rake_tasks"
28
29
  s.add_development_dependency "rake"
29
30
  s.add_development_dependency "rspec", "~> 3"
30
- s.add_development_dependency "rubocop", "= 0.41.2"
31
+ s.add_development_dependency "rubocop", "= 0.50.0"
31
32
  s.add_development_dependency "simplecov"
33
+ s.add_development_dependency "simplecov-lcov"
32
34
  end
data/spec/binding_spec.rb CHANGED
@@ -28,10 +28,14 @@ describe "BindingTest" do
28
28
  # it should have its own interface
29
29
  expect(test2["org.ruby.Test2"]).not_to be_nil
30
30
  # but not an interface of the Test class
31
- expect(test2["org.ruby.SampleInterface"]).to be_nil
31
+ expect { test2["org.ruby.SampleInterface"] }.to raise_error(DBus::Error) do |e|
32
+ expect(e.message).to match(/no such interface/)
33
+ end
32
34
 
33
35
  # and the parent should not get polluted by the child
34
- expect(@base["org.ruby.Test2"]).to be_nil
36
+ expect { @base["org.ruby.Test2"] }.to raise_error(DBus::Error) do |e|
37
+ expect(e.message).to match(/no such interface/)
38
+ end
35
39
  end
36
40
 
37
41
  it "tests translating errors into exceptions" do
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env rspec
2
+ require_relative "spec_helper"
3
+ require "dbus"
4
+
5
+ describe DBus::BusName do
6
+ describe ".valid?" do
7
+ it "recognizes valid bus names" do
8
+ expect(described_class.valid?("org.freedesktop.DBus")).to be_truthy
9
+ expect(described_class.valid?(":1.42")).to be_truthy
10
+ expect(described_class.valid?("org._7_zip.Archiver")).to be_truthy
11
+ end
12
+
13
+ it "recognizes invalid bus names" do
14
+ expect(described_class.valid?("")).to be_falsey
15
+ expect(described_class.valid?("Empty..Component")).to be_falsey
16
+ expect(described_class.valid?(".Empty.First.Component")).to be_falsey
17
+ expect(described_class.valid?("Empty.Last.Component.")).to be_falsey
18
+ expect(described_class.valid?("Invalid.Ch@r@cter")).to be_falsey
19
+ expect(described_class.valid?("/Invalid-Character")).to be_falsey
20
+ long_name = "a." + ("long." * 100) + "name"
21
+ expect(described_class.valid?(long_name)).to be_falsey
22
+ expect(described_class.valid?("org.7_zip.Archiver")).to be_falsey
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env rspec
2
+ # Test that a client survives various error cases
3
+ require_relative "spec_helper"
4
+ require "dbus"
5
+
6
+ describe "ClientRobustnessTest" do
7
+ before(:each) do
8
+ @bus = DBus::ASessionBus.new
9
+ @svc = @bus.service("org.ruby.service")
10
+ end
11
+
12
+ context "when the bus name is invalid" do
13
+ it "tells the user the bus name is invalid" do
14
+ # user mistake, should be "org.ruby.service"
15
+ expect { @bus.service(".org.ruby.service") }.to raise_error(DBus::Error)
16
+ end
17
+ end
18
+
19
+ context "when the object path is invalid" do
20
+ it "tells the user the path is invalid" do
21
+ # user mistake, should be "/org/ruby/MyInstance"
22
+ expect { @svc.object("org.ruby.MyInstance") }.to raise_error(DBus::Error)
23
+ end
24
+ end
25
+ end
@@ -4,19 +4,19 @@ require "dbus"
4
4
 
5
5
  describe "IntrospectXMLParserTest" do
6
6
  it "tests split interfaces" do
7
- xml = <<EOS
8
- <node>
9
- <interface name="org.example.Foo">
10
- <method name="Dwim"/>
11
- </interface>
12
- <interface name="org.example.Bar">
13
- <method name="Drink"/>
14
- </interface>
15
- <interface name="org.example.Foo">
16
- <method name="Smurf"/>
17
- </interface>
18
- </node>
19
- EOS
7
+ xml = <<-XML
8
+ <node>
9
+ <interface name="org.example.Foo">
10
+ <method name="Dwim"/>
11
+ </interface>
12
+ <interface name="org.example.Bar">
13
+ <method name="Drink"/>
14
+ </interface>
15
+ <interface name="org.example.Foo">
16
+ <method name="Smurf"/>
17
+ </interface>
18
+ </node>
19
+ XML
20
20
 
21
21
  interfaces, _subnodes = DBus::IntrospectXMLParser.new(xml).parse
22
22