ruby-dbus 0.15.0 → 0.18.0.beta1

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 (69) hide show
  1. checksums.yaml +5 -5
  2. data/NEWS.md +41 -1
  3. data/README.md +3 -5
  4. data/Rakefile +18 -8
  5. data/VERSION +1 -1
  6. data/doc/Reference.md +93 -3
  7. data/examples/doc/_extract_examples +7 -0
  8. data/examples/gdbus/gdbus +31 -24
  9. data/examples/no-introspect/nm-test.rb +2 -0
  10. data/examples/no-introspect/tracker-test.rb +3 -1
  11. data/examples/rhythmbox/playpause.rb +2 -1
  12. data/examples/service/call_service.rb +2 -1
  13. data/examples/service/service_newapi.rb +1 -1
  14. data/examples/simple/call_introspect.rb +1 -0
  15. data/examples/simple/get_id.rb +2 -1
  16. data/examples/simple/properties.rb +2 -0
  17. data/examples/utils/listnames.rb +1 -0
  18. data/examples/utils/notify.rb +1 -0
  19. data/lib/dbus/api_options.rb +9 -0
  20. data/lib/dbus/auth.rb +20 -15
  21. data/lib/dbus/bus.rb +129 -74
  22. data/lib/dbus/bus_name.rb +31 -0
  23. data/lib/dbus/core_ext/class/attribute.rb +1 -1
  24. data/lib/dbus/error.rb +4 -2
  25. data/lib/dbus/introspect.rb +90 -34
  26. data/lib/dbus/logger.rb +3 -1
  27. data/lib/dbus/marshall.rb +119 -87
  28. data/lib/dbus/matchrule.rb +16 -16
  29. data/lib/dbus/message.rb +40 -27
  30. data/lib/dbus/message_queue.rb +26 -18
  31. data/lib/dbus/object.rb +401 -0
  32. data/lib/dbus/object_path.rb +28 -0
  33. data/lib/dbus/proxy_object.rb +23 -2
  34. data/lib/dbus/proxy_object_factory.rb +11 -7
  35. data/lib/dbus/proxy_object_interface.rb +26 -21
  36. data/lib/dbus/type.rb +59 -34
  37. data/lib/dbus/xml.rb +28 -17
  38. data/lib/dbus.rb +10 -8
  39. data/ruby-dbus.gemspec +8 -4
  40. data/spec/async_spec.rb +2 -0
  41. data/spec/binding_spec.rb +2 -0
  42. data/spec/bus_and_xml_backend_spec.rb +2 -0
  43. data/spec/bus_driver_spec.rb +2 -0
  44. data/spec/bus_name_spec.rb +27 -0
  45. data/spec/bus_spec.rb +2 -0
  46. data/spec/byte_array_spec.rb +2 -0
  47. data/spec/client_robustness_spec.rb +27 -0
  48. data/spec/err_msg_spec.rb +2 -0
  49. data/spec/introspect_xml_parser_spec.rb +2 -0
  50. data/spec/introspection_spec.rb +2 -0
  51. data/spec/main_loop_spec.rb +3 -1
  52. data/spec/node_spec.rb +23 -0
  53. data/spec/object_path_spec.rb +25 -0
  54. data/spec/property_spec.rb +64 -5
  55. data/spec/proxy_object_spec.rb +2 -0
  56. data/spec/server_robustness_spec.rb +2 -0
  57. data/spec/server_spec.rb +2 -0
  58. data/spec/service_newapi.rb +39 -70
  59. data/spec/session_bus_spec.rb +3 -1
  60. data/spec/session_bus_spec_manual.rb +2 -0
  61. data/spec/signal_spec.rb +5 -3
  62. data/spec/spec_helper.rb +23 -9
  63. data/spec/thread_safety_spec.rb +2 -0
  64. data/spec/tools/dbus-limited-session.conf +4 -0
  65. data/spec/type_spec.rb +2 -0
  66. data/spec/value_spec.rb +16 -1
  67. data/spec/variant_spec.rb +4 -2
  68. metadata +32 -12
  69. data/lib/dbus/export.rb +0 -131
@@ -0,0 +1,401 @@
1
+ # frozen_string_literal: true
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_relative "core_ext/class/attribute"
12
+
13
+ module DBus
14
+ PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"
15
+
16
+ # Exported object type
17
+ # = Exportable D-Bus object class
18
+ #
19
+ # Objects that are going to be exported by a D-Bus service
20
+ # should inherit from this class. At the client side, use {ProxyObject}.
21
+ class Object
22
+ # The path of the object.
23
+ attr_reader :path
24
+
25
+ # The interfaces that the object supports. Hash: String => Interface
26
+ my_class_attribute :intfs
27
+ self.intfs = {}
28
+
29
+ # The service that the object is exported by.
30
+ attr_writer :service
31
+
32
+ @@cur_intf = nil # Interface
33
+ @@intfs_mutex = Mutex.new
34
+
35
+ # Create a new object with a given _path_.
36
+ # Use Service#export to export it.
37
+ def initialize(path)
38
+ @path = path
39
+ @service = nil
40
+ end
41
+
42
+ # Dispatch a message _msg_ to call exported methods
43
+ def dispatch(msg)
44
+ case msg.message_type
45
+ when Message::METHOD_CALL
46
+ reply = nil
47
+ begin
48
+ iface = intfs[msg.interface]
49
+ if !iface
50
+ raise DBus.error("org.freedesktop.DBus.Error.UnknownMethod"),
51
+ "Interface \"#{msg.interface}\" of object \"#{msg.path}\" doesn't exist"
52
+ end
53
+ member_sym = msg.member.to_sym
54
+ meth = iface.methods[member_sym]
55
+ if !meth
56
+ raise DBus.error("org.freedesktop.DBus.Error.UnknownMethod"),
57
+ "Method \"#{msg.member}\" on interface \"#{msg.interface}\" of object \"#{msg.path}\" doesn't exist"
58
+ end
59
+ methname = Object.make_method_name(msg.interface, msg.member)
60
+ retdata = method(methname).call(*msg.params)
61
+ retdata = [*retdata]
62
+
63
+ reply = Message.method_return(msg)
64
+ if iface.name == PROPERTY_INTERFACE && member_sym == :Get
65
+ # Use the specific property type instead of the generic variant
66
+ # returned by Get.
67
+ # TODO: GetAll and Set still missing
68
+ property = dbus_lookup_property(msg.params[0], msg.params[1])
69
+ rsigs = [property.type]
70
+ else
71
+ rsigs = meth.rets.map(&:type)
72
+ end
73
+ rsigs.zip(retdata).each do |rsig, rdata|
74
+ reply.add_param(rsig, rdata)
75
+ end
76
+ rescue StandardError => e
77
+ dbus_msg_exc = msg.annotate_exception(e)
78
+ reply = ErrorMessage.from_exception(dbus_msg_exc).reply_to(msg)
79
+ end
80
+ @service.bus.message_queue.push(reply)
81
+ end
82
+ end
83
+
84
+ # Select (and create) the interface that the following defined methods
85
+ # belong to.
86
+ # @param name [String] interface name like "org.example.ManagerManager"
87
+ # @see https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names-interface
88
+ def self.dbus_interface(name)
89
+ @@intfs_mutex.synchronize do
90
+ @@cur_intf = intfs[name]
91
+ if !@@cur_intf
92
+ @@cur_intf = Interface.new(name) # validates the name
93
+ # As this is a mutable class_attr, we cannot use
94
+ # self.intfs[name] = @@cur_intf # Hash#[]=
95
+ # as that would modify parent class attr in place.
96
+ # Using the setter lets a subclass have the new value
97
+ # while the superclass keeps the old one.
98
+ self.intfs = intfs.merge(name => @@cur_intf)
99
+ end
100
+ yield
101
+ @@cur_intf = nil
102
+ end
103
+ end
104
+
105
+ # Forgetting to declare the interface for a method/signal/property
106
+ # is a ScriptError.
107
+ class UndefinedInterface < ScriptError # rubocop:disable Lint/InheritException
108
+ def initialize(sym)
109
+ super "No interface specified for #{sym}. Enclose it in dbus_interface."
110
+ end
111
+ end
112
+
113
+ # A read-write property accessing an instance variable.
114
+ # A combination of `attr_accessor` and {.dbus_accessor}.
115
+ #
116
+ # PropertiesChanged signal will be emitted whenever `foo_bar=` is used
117
+ # but not when @foo_bar is written directly.
118
+ #
119
+ # @param ruby_name [Symbol] :foo_bar is exposed as FooBar;
120
+ # use dbus_name to override
121
+ # @param type a signature like "s" or "a(uus)" or Type::STRING
122
+ # @param dbus_name [String] if not given it is made
123
+ # by CamelCasing the ruby_name. foo_bar becomes FooBar
124
+ # to convert the Ruby convention to the DBus convention.
125
+ # @return [void]
126
+ def self.dbus_attr_accessor(ruby_name, type, dbus_name: nil)
127
+ attr_accessor(ruby_name)
128
+
129
+ dbus_accessor(ruby_name, type, dbus_name: dbus_name)
130
+ end
131
+
132
+ # A read-only property accessing an instance variable.
133
+ # A combination of `attr_reader` and {.dbus_reader}.
134
+ #
135
+ # Whenever the property value gets changed from "inside" the object,
136
+ # you should emit the `PropertiesChanged` signal by calling
137
+ #
138
+ # object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {dbus_name.to_s => value}, [])
139
+ #
140
+ # or, omitting the value in the signal,
141
+ #
142
+ # object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {}, [dbus_name.to_s])
143
+ #
144
+ # @param (see .dbus_attr_accessor)
145
+ # @return (see .dbus_attr_accessor)
146
+ def self.dbus_attr_reader(ruby_name, type, dbus_name: nil)
147
+ attr_reader(ruby_name)
148
+
149
+ dbus_reader(ruby_name, type, dbus_name: dbus_name)
150
+ end
151
+
152
+ # A write-only property accessing an instance variable.
153
+ # A combination of `attr_writer` and {.dbus_writer}.
154
+ #
155
+ # @param (see .dbus_attr_accessor)
156
+ # @return (see .dbus_attr_accessor)
157
+ def self.dbus_attr_writer(ruby_name, type, dbus_name: nil)
158
+ attr_writer(ruby_name)
159
+
160
+ dbus_writer(ruby_name, type, dbus_name: dbus_name)
161
+ end
162
+
163
+ # A read-write property using a pair of reader/writer methods
164
+ # (which must already exist).
165
+ # (To directly access an instance variable, use {.dbus_attr_accessor} instead)
166
+ #
167
+ # Uses {.dbus_watcher} to set up the PropertiesChanged signal.
168
+ #
169
+ # @param (see .dbus_attr_accessor)
170
+ # @return (see .dbus_attr_accessor)
171
+ def self.dbus_accessor(ruby_name, type, dbus_name: nil)
172
+ raise UndefinedInterface, ruby_name if @@cur_intf.nil?
173
+
174
+ dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
175
+ property = Property.new(dbus_name, type, :readwrite, ruby_name: ruby_name)
176
+ @@cur_intf.define(property)
177
+
178
+ dbus_watcher(ruby_name, dbus_name: dbus_name)
179
+ end
180
+
181
+ # A read-only property accessing a reader method (which must already exist).
182
+ # (To directly access an instance variable, use {.dbus_attr_reader} instead)
183
+ #
184
+ # Whenever the property value gets changed from "inside" the object,
185
+ # you should emit the `PropertiesChanged` signal by calling
186
+ #
187
+ # object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {dbus_name.to_s => value}, [])
188
+ #
189
+ # or, omitting the value in the signal,
190
+ #
191
+ # object[DBus::PROPERTY_INTERFACE].PropertiesChanged(interface_name, {}, [dbus_name.to_s])
192
+ #
193
+ # @param (see .dbus_attr_accessor)
194
+ # @return (see .dbus_attr_accessor)
195
+ def self.dbus_reader(ruby_name, type, dbus_name: nil)
196
+ raise UndefinedInterface, ruby_name if @@cur_intf.nil?
197
+
198
+ dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
199
+ property = Property.new(dbus_name, type, :read, ruby_name: ruby_name)
200
+ @@cur_intf.define(property)
201
+ end
202
+
203
+ # A write-only property accessing a writer method (which must already exist).
204
+ # (To directly access an instance variable, use {.dbus_attr_writer} instead)
205
+ #
206
+ # Uses {.dbus_watcher} to set up the PropertiesChanged signal.
207
+ #
208
+ # @param (see .dbus_attr_accessor)
209
+ # @return (see .dbus_attr_accessor)
210
+ def self.dbus_writer(ruby_name, type, dbus_name: nil)
211
+ raise UndefinedInterface, ruby_name if @@cur_intf.nil?
212
+
213
+ dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
214
+ property = Property.new(dbus_name, type, :write, ruby_name: ruby_name)
215
+ @@cur_intf.define(property)
216
+
217
+ dbus_watcher(ruby_name, dbus_name: dbus_name)
218
+ end
219
+
220
+ # Enables automatic sending of the PropertiesChanged signal.
221
+ # For *ruby_name* `foo_bar`, wrap `foo_bar=` so that it sends
222
+ # the signal for FooBar.
223
+ # The original version remains as `_original_foo_bar=`.
224
+ #
225
+ # @param ruby_name [Symbol] :foo_bar and :foo_bar= both mean the same thing
226
+ # @param dbus_name [String] if not given it is made
227
+ # by CamelCasing the ruby_name. foo_bar becomes FooBar
228
+ # to convert the Ruby convention to the DBus convention.
229
+ # @return [void]
230
+ def self.dbus_watcher(ruby_name, dbus_name: nil)
231
+ raise UndefinedInterface, ruby_name if @@cur_intf.nil?
232
+
233
+ cur_intf = @@cur_intf
234
+
235
+ ruby_name = ruby_name.to_s.sub(/=$/, "").to_sym
236
+ ruby_name_eq = "#{ruby_name}=".to_sym
237
+ original_ruby_name_eq = "_original_#{ruby_name_eq}"
238
+
239
+ dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
240
+
241
+ # the argument order is alias_method(new_name, existing_name)
242
+ alias_method original_ruby_name_eq, ruby_name_eq
243
+ define_method ruby_name_eq do |value|
244
+ public_send(original_ruby_name_eq, value)
245
+
246
+ # TODO: respect EmitsChangedSignal to use invalidated_properties instead
247
+ # PropertiesChanged, "interface:s, changed_properties:a{sv}, invalidated_properties:as"
248
+ PropertiesChanged(cur_intf.name, { dbus_name.to_s => value }, [])
249
+ end
250
+ end
251
+
252
+ # Defines an exportable method on the object with the given name _sym_,
253
+ # _prototype_ and the code in a block.
254
+ # @param prototype [Prototype]
255
+ def self.dbus_method(sym, prototype = "", &block)
256
+ raise UndefinedInterface, sym if @@cur_intf.nil?
257
+
258
+ @@cur_intf.define(Method.new(sym.to_s).from_prototype(prototype))
259
+
260
+ ruby_name = Object.make_method_name(@@cur_intf.name, sym.to_s)
261
+ # ::Module#define_method(name) { body }
262
+ define_method(ruby_name, &block)
263
+ end
264
+
265
+ # Emits a signal from the object with the given _interface_, signal
266
+ # _sig_ and arguments _args_.
267
+ # @param intf [Interface]
268
+ # @param sig [Signal]
269
+ # @param args arguments for the signal
270
+ def emit(intf, sig, *args)
271
+ @service.bus.emit(@service, self, intf, sig, *args)
272
+ end
273
+
274
+ # Defines a signal for the object with a given name _sym_ and _prototype_.
275
+ def self.dbus_signal(sym, prototype = "")
276
+ raise UndefinedInterface, sym if @@cur_intf.nil?
277
+
278
+ cur_intf = @@cur_intf
279
+ signal = Signal.new(sym.to_s).from_prototype(prototype)
280
+ cur_intf.define(Signal.new(sym.to_s).from_prototype(prototype))
281
+
282
+ # ::Module#define_method(name) { body }
283
+ define_method(sym.to_s) do |*args|
284
+ emit(cur_intf, signal, *args)
285
+ end
286
+ end
287
+
288
+ # Helper method that returns a method name generated from the interface
289
+ # name _intfname_ and method name _methname_.
290
+ # @api private
291
+ def self.make_method_name(intfname, methname)
292
+ "#{intfname}%%#{methname}"
293
+ end
294
+
295
+ # TODO: borrow a proven implementation
296
+ # @param str [String]
297
+ # @return [String]
298
+ # @api private
299
+ def self.camelize(str)
300
+ str.split(/_/).map(&:capitalize).join("")
301
+ end
302
+
303
+ # Make a D-Bus conventional name, CamelCased.
304
+ # @param ruby_name [String,Symbol] eg :do_something
305
+ # @param dbus_name [String,Symbol,nil] use this if given
306
+ # @return [Symbol] eg DoSomething
307
+ def self.make_dbus_name(ruby_name, dbus_name: nil)
308
+ dbus_name ||= camelize(ruby_name.to_s)
309
+ dbus_name.to_sym
310
+ end
311
+
312
+ # @param interface_name [String]
313
+ # @param property_name [String]
314
+ # @return [Property]
315
+ # @raise [DBus::Error]
316
+ # @api private
317
+ def dbus_lookup_property(interface_name, property_name)
318
+ # what should happen for unknown properties
319
+ # plasma: InvalidArgs (propname), UnknownInterface (interface)
320
+ # systemd: UnknownProperty
321
+ interface = intfs[interface_name]
322
+ if !interface
323
+ raise DBus.error("org.freedesktop.DBus.Error.UnknownProperty"),
324
+ "Property '#{interface_name}.#{property_name}' (on object '#{@path}') not found: no such interface"
325
+ end
326
+
327
+ property = interface.properties[property_name.to_sym]
328
+ if !property
329
+ raise DBus.error("org.freedesktop.DBus.Error.UnknownProperty"),
330
+ "Property '#{interface_name}.#{property_name}' (on object '#{@path}') not found"
331
+ end
332
+
333
+ property
334
+ end
335
+
336
+ ####################################################################
337
+
338
+ # use the above defined methods to declare the property-handling
339
+ # interfaces and methods
340
+
341
+ dbus_interface PROPERTY_INTERFACE do
342
+ dbus_method :Get, "in interface_name:s, in property_name:s, out value:v" do |interface_name, property_name|
343
+ property = dbus_lookup_property(interface_name, property_name)
344
+
345
+ if property.readable?
346
+ ruby_name = property.ruby_name
347
+ value = public_send(ruby_name)
348
+ [value]
349
+ else
350
+ raise DBus.error("org.freedesktop.DBus.Error.PropertyWriteOnly"),
351
+ "Property '#{interface_name}.#{property_name}' (on object '#{@path}') is not readable"
352
+ end
353
+ end
354
+
355
+ dbus_method :Set, "in interface_name:s, in property_name:s, in val:v" do |interface_name, property_name, value|
356
+ property = dbus_lookup_property(interface_name, property_name)
357
+
358
+ if property.writable?
359
+ ruby_name_eq = "#{property.ruby_name}="
360
+ public_send(ruby_name_eq, value)
361
+ else
362
+ raise DBus.error("org.freedesktop.DBus.Error.PropertyReadOnly"),
363
+ "Property '#{interface_name}.#{property_name}' (on object '#{@path}') is not writable"
364
+ end
365
+ end
366
+
367
+ dbus_method :GetAll, "in interface_name:s, out value:a{sv}" do |interface_name|
368
+ interface = intfs[interface_name]
369
+ if !interface
370
+ raise DBus.error("org.freedesktop.DBus.Error.UnknownProperty"),
371
+ "Properties '#{interface_name}.*' (on object '#{@path}') not found: no such interface"
372
+ end
373
+
374
+ p_hash = {}
375
+ interface.properties.each do |p_name, property|
376
+ next unless property.readable?
377
+
378
+ ruby_name = property.ruby_name
379
+ begin
380
+ # D-Bus spec says:
381
+ # > If GetAll is called with a valid interface name for which some
382
+ # > properties are not accessible to the caller (for example, due
383
+ # > to per-property access control implemented in the service),
384
+ # > those properties should be silently omitted from the result
385
+ # > array.
386
+ # so we will silently omit properties that fail to read.
387
+ # Get'ting them individually will send DBus.Error
388
+ p_hash[p_name.to_s] = public_send(ruby_name)
389
+ rescue StandardError
390
+ DBus.logger.debug "Property '#{interface_name}.#{p_name}' (on object '#{@path}')" \
391
+ " has raised during GetAll, omitting it"
392
+ end
393
+ end
394
+
395
+ [p_hash]
396
+ end
397
+
398
+ dbus_signal :PropertiesChanged, "interface:s, changed_properties:a{sv}, invalidated_properties:as"
399
+ end
400
+ end
401
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of the ruby-dbus project
4
+ # Copyright (C) 2019 Martin Vidner
5
+ #
6
+ # This library is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU Lesser General Public
8
+ # License, version 2.1 as published by the Free Software Foundation.
9
+ # See the file "COPYING" for the exact licensing terms.
10
+
11
+ module DBus
12
+ # A {::String} that validates at initialization time
13
+ # @see https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-marshaling-object-path
14
+ class ObjectPath < String
15
+ # @raise Error if not a valid object path
16
+ def initialize(str)
17
+ unless self.class.valid?(str)
18
+ raise DBus::Error, "Invalid object path #{str.inspect}"
19
+ end
20
+
21
+ super
22
+ end
23
+
24
+ def self.valid?(str)
25
+ str == "/" || str =~ %r{\A(/[A-Za-z0-9_]+)+\z}
26
+ end
27
+ end
28
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of the ruby-dbus project
2
4
  # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
3
5
  # Copyright (C) 2009-2014 Martin Vidner
@@ -36,7 +38,7 @@ module DBus
36
38
  def initialize(bus, dest, path, api: ApiOptions::CURRENT)
37
39
  @bus = bus
38
40
  @destination = dest
39
- @path = path
41
+ @path = ObjectPath.new(path)
40
42
  @introspected = false
41
43
  @interfaces = {}
42
44
  @subnodes = []
@@ -44,6 +46,7 @@ module DBus
44
46
  end
45
47
 
46
48
  # Returns the interfaces of the object.
49
+ # @return [Array<String>] names of the interfaces
47
50
  def interfaces
48
51
  introspect unless introspected
49
52
  @interfaces.keys
@@ -56,6 +59,7 @@ module DBus
56
59
  introspect unless introspected
57
60
  ifc = @interfaces[intfname]
58
61
  raise DBus::Error, "no such interface `#{intfname}' on object `#{@path}'" unless ifc
62
+
59
63
  ifc
60
64
  end
61
65
 
@@ -92,6 +96,7 @@ module DBus
92
96
  # don't overwrite instance methods!
93
97
  next if dup_meths.include?(name)
94
98
  next if self.class.instance_methods.include?(name)
99
+
95
100
  if univocal_meths.include? name
96
101
  univocal_meths.delete name
97
102
  dup_meths << name
@@ -123,12 +128,20 @@ module DBus
123
128
  def on_signal(name, &block)
124
129
  # TODO: improve
125
130
  raise NoMethodError unless @default_iface && has_iface?(@default_iface)
131
+
126
132
  @interfaces[@default_iface].on_signal(name, &block)
127
133
  end
128
134
 
129
135
  ####################################################
130
136
  private
131
137
 
138
+ # rubocop:disable Lint/MissingSuper
139
+ # as this should forward everything
140
+ #
141
+ # https://github.com/rubocop-hq/ruby-style-guide#no-method-missing
142
+ # and http://blog.marc-andre.ca/2010/11/15/methodmissing-politely/
143
+ # have a point to be investigated
144
+
132
145
  # Handles all unkown methods, mostly to route method calls to the
133
146
  # default interface.
134
147
  def method_missing(name, *args, &reply_handler)
@@ -146,9 +159,17 @@ module DBus
146
159
  # interesting, foo.method("unknown")
147
160
  # raises NameError, not NoMethodError
148
161
  raise unless e.to_s =~ /undefined method `#{name}'/
162
+
149
163
  # BTW e.exception("...") would preserve the class.
150
164
  raise NoMethodError, "undefined method `#{name}' for DBus interface `#{@default_iface}' on object `#{@path}'"
151
165
  end
152
166
  end
153
- end # class ProxyObject
167
+ # rubocop:enable Lint/MissingSuper
168
+
169
+ def respond_to_missing?(name, _include_private = false)
170
+ @default_iface &&
171
+ has_iface?(@default_iface) &&
172
+ @interfaces[@default_iface].methods.key?(name) or super
173
+ end
174
+ end
154
175
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of the ruby-dbus project
2
4
  # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
3
5
  # Copyright (C) 2009-2014 Martin Vidner
@@ -22,17 +24,19 @@ module DBus
22
24
  @api = api
23
25
  end
24
26
 
25
- # Investigates the sub-nodes of the proxy object _po_ based on the
27
+ # Investigates the sub-nodes of the proxy object _pobj_ based on the
26
28
  # introspection XML data _xml_ and sets them up recursively.
27
- def self.introspect_into(po, xml)
28
- intfs, po.subnodes = IntrospectXMLParser.new(xml).parse
29
+ # @param pobj [ProxyObject]
30
+ # @param xml [String]
31
+ def self.introspect_into(pobj, xml)
32
+ intfs, pobj.subnodes = IntrospectXMLParser.new(xml).parse
29
33
  intfs.each do |i|
30
- poi = ProxyObjectInterface.new(po, i.name)
34
+ poi = ProxyObjectInterface.new(pobj, i.name)
31
35
  i.methods.each_value { |m| poi.define(m) }
32
36
  i.signals.each_value { |s| poi.define(s) }
33
- po[i.name] = poi
37
+ pobj[i.name] = poi
34
38
  end
35
- po.introspected = true
39
+ pobj.introspected = true
36
40
  end
37
41
 
38
42
  # Generates, sets up and returns the proxy object.
@@ -41,5 +45,5 @@ module DBus
41
45
  ProxyObjectFactory.introspect_into(po, @xml)
42
46
  po
43
47
  end
44
- end # class ProxyObjectFactory
48
+ end
45
49
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file is part of the ruby-dbus project
2
4
  # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
3
5
  # Copyright (C) 2009-2014 Martin Vidner
@@ -36,27 +38,28 @@ module DBus
36
38
  @name
37
39
  end
38
40
 
39
- # Defines a method on the interface from the Method descriptor _m_.
40
- def define_method_from_descriptor(m)
41
- m.params.each do |fpar|
41
+ # Defines a method on the interface from the Method descriptor _method_.
42
+ # @param method [Method]
43
+ def define_method_from_descriptor(method)
44
+ method.params.each do |fpar|
42
45
  par = fpar.type
43
46
  # This is the signature validity check
44
47
  Type::Parser.new(par).parse
45
48
  end
46
49
 
47
50
  singleton_class.class_eval do
48
- define_method m.name do |*args, &reply_handler|
49
- if m.params.size != args.size
50
- raise ArgumentError, "wrong number of arguments (#{args.size} for #{m.params.size})"
51
+ define_method method.name do |*args, &reply_handler|
52
+ if method.params.size != args.size
53
+ raise ArgumentError, "wrong number of arguments (#{args.size} for #{method.params.size})"
51
54
  end
52
55
 
53
56
  msg = Message.new(Message::METHOD_CALL)
54
57
  msg.path = @object.path
55
58
  msg.interface = @name
56
59
  msg.destination = @object.destination
57
- msg.member = m.name
60
+ msg.member = method.name
58
61
  msg.sender = @object.bus.unique_name
59
- m.params.each do |fpar|
62
+ method.params.each do |fpar|
60
63
  par = fpar.type
61
64
  msg.add_param(par, args.shift)
62
65
  end
@@ -64,25 +67,27 @@ module DBus
64
67
  if ret.nil? || @object.api.proxy_method_returns_array
65
68
  ret
66
69
  else
67
- m.rets.size == 1 ? ret.first : ret
70
+ method.rets.size == 1 ? ret.first : ret
68
71
  end
69
72
  end
70
73
  end
71
74
 
72
- @methods[m.name] = m
75
+ @methods[method.name] = method
73
76
  end
74
77
 
75
- # Defines a signal from the descriptor _s_.
76
- def define_signal_from_descriptor(s)
77
- @signals[s.name] = s
78
+ # Defines a signal from the descriptor _sig_.
79
+ # @param sig [Signal]
80
+ def define_signal_from_descriptor(sig)
81
+ @signals[sig.name] = sig
78
82
  end
79
83
 
80
- # Defines a signal or method based on the descriptor _m_.
81
- def define(m)
82
- if m.is_a?(Method)
83
- define_method_from_descriptor(m)
84
- elsif m.is_a?(Signal)
85
- define_signal_from_descriptor(m)
84
+ # Defines a signal or method based on the descriptor _ifc_el_.
85
+ def define(ifc_el)
86
+ case ifc_el
87
+ when Method
88
+ define_method_from_descriptor(ifc_el)
89
+ when Signal
90
+ define_signal_from_descriptor(ifc_el)
86
91
  end
87
92
  end
88
93
 
@@ -109,7 +114,7 @@ module DBus
109
114
  end
110
115
  end
111
116
 
112
- PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties".freeze
117
+ PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"
113
118
 
114
119
  # Read a property.
115
120
  # @param propname [String]
@@ -141,5 +146,5 @@ module DBus
141
146
  ret
142
147
  end
143
148
  end
144
- end # class ProxyObjectInterface
149
+ end
145
150
  end