class DBus::Object

Exported object type

Exportable D-Bus object class

Objects that are going to be exported by a D-Bus service should inherit from this class. At the client side, use {ProxyObject}.

Attributes

path[R]

@return [ObjectPath] The path of the object.

Public Class Methods

camelize(str) click to toggle source

TODO: borrow a proven implementation @param str [String] @return [String] @api private

    # File lib/dbus/object.rb
375 def self.camelize(str)
376   str.split(/_/).map(&:capitalize).join("")
377 end
dbus_accessor(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) click to toggle source

A read-write property using a pair of reader/writer methods (which must already exist). (To directly access an instance variable, use {.dbus_attr_accessor} instead)

Uses {.dbus_watcher} to set up the PropertiesChanged signal.

@param (see .dbus_attr_accessor) @return (see .dbus_attr_accessor)

    # File lib/dbus/object.rb
213 def self.dbus_accessor(ruby_name, type, dbus_name: nil, emits_changed_signal: nil)
214   raise UndefinedInterface, ruby_name if @@cur_intf.nil?
215 
216   dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
217   property = Property.new(dbus_name, type, :readwrite, ruby_name: ruby_name)
218   @@cur_intf.define(property)
219 
220   dbus_watcher(ruby_name, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal)
221 end
dbus_attr_accessor(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) click to toggle source

A read-write property accessing an instance variable. A combination of ‘attr_accessor` and {.dbus_accessor}.

PropertiesChanged signal will be emitted whenever ‘foo_bar=` is used but not when @foo_bar is written directly.

@param ruby_name [Symbol] :foo_bar is exposed as FooBar;

use dbus_name to override

@param type [Type,SingleCompleteType]

a signature like "s" or "a(uus)" or Type::STRING

@param dbus_name [String] if not given it is made

by CamelCasing the ruby_name. foo_bar becomes FooBar
to convert the Ruby convention to the DBus convention.

@param emits_changed_signal [true,false,:const,:invalidates]

see {EmitsChangedSignal}; if unspecified, ask the interface.

@return [void]

    # File lib/dbus/object.rb
153 def self.dbus_attr_accessor(ruby_name, type, dbus_name: nil, emits_changed_signal: nil)
154   attr_accessor(ruby_name)
155 
156   dbus_accessor(ruby_name, type, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal)
157 end
dbus_attr_reader(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) click to toggle source

A read-only property accessing an instance variable. A combination of ‘attr_reader` and {.dbus_reader}.

You may be instead looking for a variant which is read-write from the Ruby side: {.dbus_reader_attr_accessor}.

Whenever the property value gets changed from “inside” the object, you should emit the ‘PropertiesChanged` signal by calling {#dbus_properties_changed}.

dbus_properties_changed(interface_name, {dbus_name.to_s => value}, [])

or, omitting the value in the signal,

dbus_properties_changed(interface_name, {}, [dbus_name.to_s])

@param (see .dbus_attr_accessor) @return (see .dbus_attr_accessor)

    # File lib/dbus/object.rb
188 def self.dbus_attr_reader(ruby_name, type, dbus_name: nil, emits_changed_signal: nil)
189   attr_reader(ruby_name)
190 
191   dbus_reader(ruby_name, type, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal)
192 end
dbus_attr_writer(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) click to toggle source

A write-only property accessing an instance variable. A combination of ‘attr_writer` and {.dbus_writer}.

@param (see .dbus_attr_accessor) @return (see .dbus_attr_accessor)

    # File lib/dbus/object.rb
199 def self.dbus_attr_writer(ruby_name, type, dbus_name: nil, emits_changed_signal: nil)
200   attr_writer(ruby_name)
201 
202   dbus_writer(ruby_name, type, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal)
203 end
dbus_interface(name) { || ... } click to toggle source

Select (and create) the interface that the following defined methods belong to. @param name [String] interface name like “org.example.ManagerManager” @see dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names-interface

    # File lib/dbus/object.rb
 97 def self.dbus_interface(name)
 98   @@intfs_mutex.synchronize do
 99     @@cur_intf = intfs[name]
100     if !@@cur_intf
101       @@cur_intf = Interface.new(name) # validates the name
102       # As this is a mutable class_attr, we cannot use
103       #   self.intfs[name] = @@cur_intf                      # Hash#[]=
104       # as that would modify parent class attr in place.
105       # Using the setter lets a subclass have the new value
106       # while the superclass keeps the old one.
107       self.intfs = intfs.merge(name => @@cur_intf)
108     end
109     begin
110       yield
111     ensure
112       @@cur_intf = nil
113     end
114   end
115 end
dbus_method(sym, prototype = "", &block) click to toggle source

Defines an exportable method on the object with the given name sym, prototype and the code in a block. @param prototype [Prototype]

    # File lib/dbus/object.rb
329 def self.dbus_method(sym, prototype = "", &block)
330   raise UndefinedInterface, sym if @@cur_intf.nil?
331 
332   @@cur_intf.define(Method.new(sym.to_s).from_prototype(prototype))
333 
334   ruby_name = Object.make_method_name(@@cur_intf.name, sym.to_s)
335   # ::Module#define_method(name) { body }
336   define_method(ruby_name, &block)
337 end
dbus_reader(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) click to toggle source

A read-only property accessing a reader method (which must already exist). (To directly access an instance variable, use {.dbus_attr_reader} instead)

At the D-Bus side the property is read only but it makes perfect sense to implement it with a read-write attr_accessor. In that case this method uses {.dbus_watcher} to set up the PropertiesChanged signal.

attr_accessor :foo_bar
dbus_reader :foo_bar, "s"

The above two declarations have a shorthand:

dbus_reader_attr_accessor :foo_bar, "s"

If the property value should change by other means than its attr_writer, you should emit the ‘PropertiesChanged` signal by calling {#dbus_properties_changed}.

dbus_properties_changed(interface_name, {dbus_name.to_s => value}, [])

or, omitting the value in the signal,

dbus_properties_changed(interface_name, {}, [dbus_name.to_s])

@param (see .dbus_attr_accessor) @return (see .dbus_attr_accessor)

    # File lib/dbus/object.rb
249 def self.dbus_reader(ruby_name, type, dbus_name: nil, emits_changed_signal: nil)
250   raise UndefinedInterface, ruby_name if @@cur_intf.nil?
251 
252   dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
253   property = Property.new(dbus_name, type, :read, ruby_name: ruby_name)
254   @@cur_intf.define(property)
255 
256   ruby_name_eq = :"#{ruby_name}="
257   return unless method_defined?(ruby_name_eq)
258 
259   dbus_watcher(ruby_name, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal)
260 end
dbus_reader_attr_accessor(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) click to toggle source

A read-only property accessing a read-write instance variable. A combination of ‘attr_accessor` and {.dbus_reader}.

@param (see .dbus_attr_accessor) @return (see .dbus_attr_accessor)

    # File lib/dbus/object.rb
164 def self.dbus_reader_attr_accessor(ruby_name, type, dbus_name: nil, emits_changed_signal: nil)
165   attr_accessor(ruby_name)
166 
167   dbus_reader(ruby_name, type, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal)
168 end
dbus_signal(sym, prototype = "") click to toggle source

Defines a signal for the object with a given name sym and prototype.

    # File lib/dbus/object.rb
351 def self.dbus_signal(sym, prototype = "")
352   raise UndefinedInterface, sym if @@cur_intf.nil?
353 
354   cur_intf = @@cur_intf
355   signal = Signal.new(sym.to_s).from_prototype(prototype)
356   cur_intf.define(signal)
357 
358   # ::Module#define_method(name) { body }
359   define_method(sym.to_s) do |*args|
360     emit(cur_intf, signal, *args)
361   end
362 end
dbus_watcher(ruby_name, dbus_name: nil, emits_changed_signal: nil) click to toggle source

Enables automatic sending of the PropertiesChanged signal. For ruby_name ‘foo_bar`, wrap `foo_bar=` so that it sends the signal for FooBar. The original version remains as `_original_foo_bar=`.

@param ruby_name [Symbol] :foo_bar and :foo_bar= both mean the same thing @param dbus_name [String] if not given it is made

by CamelCasing the ruby_name. foo_bar becomes FooBar
to convert the Ruby convention to the DBus convention.

@param emits_changed_signal [true,false,:const,:invalidates]

see {EmitsChangedSignal}; if unspecified, ask the interface.

@return [void]

    # File lib/dbus/object.rb
291 def self.dbus_watcher(ruby_name, dbus_name: nil, emits_changed_signal: nil)
292   raise UndefinedInterface, ruby_name if @@cur_intf.nil?
293 
294   interface_name = @@cur_intf.name
295 
296   ruby_name = ruby_name.to_s.sub(/=$/, "").to_sym
297   ruby_name_eq = :"#{ruby_name}="
298   original_ruby_name_eq = "_original_#{ruby_name_eq}"
299 
300   dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
301 
302   emits_changed_signal = EmitsChangedSignal.new(emits_changed_signal, interface: @@cur_intf)
303 
304   # the argument order is alias_method(new_name, existing_name)
305   alias_method original_ruby_name_eq, ruby_name_eq
306   define_method ruby_name_eq do |value|
307     result = public_send(original_ruby_name_eq, value)
308 
309     case emits_changed_signal.value
310     when true
311       # signature: "interface:s, changed_props:a{sv}, invalidated_props:as"
312       dbus_properties_changed(interface_name, { dbus_name.to_s => value }, [])
313     when :invalidates
314       dbus_properties_changed(interface_name, {}, [dbus_name.to_s])
315     when :const
316       # Oh my, seeing a value change of a supposedly constant property.
317       # Maybe should have raised at declaration time, don't make a fuss now.
318     when false
319       # Do nothing
320     end
321 
322     result
323   end
324 end
dbus_writer(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) click to toggle source

A write-only property accessing a writer method (which must already exist). (To directly access an instance variable, use {.dbus_attr_writer} instead)

Uses {.dbus_watcher} to set up the PropertiesChanged signal.

@param (see .dbus_attr_accessor) @return (see .dbus_attr_accessor)

    # File lib/dbus/object.rb
269 def self.dbus_writer(ruby_name, type, dbus_name: nil, emits_changed_signal: nil)
270   raise UndefinedInterface, ruby_name if @@cur_intf.nil?
271 
272   dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name)
273   property = Property.new(dbus_name, type, :write, ruby_name: ruby_name)
274   @@cur_intf.define(property)
275 
276   dbus_watcher(ruby_name, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal)
277 end
emits_changed_signal=(value) click to toggle source

Declare the behavior of PropertiesChanged signal, common for all properties in this interface (individual properties may override it) @example

self.emits_changed_signal = :invalidates

@param [true,false,:const,:invalidates] value

    # File lib/dbus/object.rb
131 def self.emits_changed_signal=(value)
132   raise UndefinedInterface, :emits_changed_signal if @@cur_intf.nil?
133 
134   @@cur_intf.emits_changed_signal = EmitsChangedSignal.new(value)
135 end
make_dbus_name(ruby_name, dbus_name: nil) click to toggle source

Make a D-Bus conventional name, CamelCased. @param ruby_name [String,Symbol] eg :do_something @param dbus_name [String,Symbol,nil] use this if given @return [Symbol] eg DoSomething

    # File lib/dbus/object.rb
383 def self.make_dbus_name(ruby_name, dbus_name: nil)
384   dbus_name ||= camelize(ruby_name.to_s)
385   dbus_name.to_sym
386 end
make_method_name(intfname, methname) click to toggle source

Helper method that returns a method name generated from the interface name intfname and method name methname. @api private

    # File lib/dbus/object.rb
367 def self.make_method_name(intfname, methname)
368   "#{intfname}%%#{methname}"
369 end
new(path) click to toggle source

Create a new object with a given path. Use ObjectServer#export to export it. @param path [ObjectPath] The path of the object.

   # File lib/dbus/object.rb
35 def initialize(path)
36   @path = path
37   # TODO: what parts of our API are supposed to work before we're exported?
38   self.object_server = nil
39 end

Public Instance Methods

dbus_lookup_property(interface_name, property_name) click to toggle source

@param interface_name [String] @param property_name [String] @return [Property] @raise [DBus::Error] @api private

    # File lib/dbus/object.rb
412 def dbus_lookup_property(interface_name, property_name)
413   # what should happen for unknown properties
414   # plasma: InvalidArgs (propname), UnknownInterface (interface)
415   # systemd: UnknownProperty
416   interface = intfs[interface_name]
417   if !interface
418     raise DBus.error("org.freedesktop.DBus.Error.UnknownProperty"),
419           "Property '#{interface_name}.#{property_name}' (on object '#{@path}') not found: no such interface"
420   end
421 
422   property = interface.properties[property_name.to_sym]
423   if !property
424     raise DBus.error("org.freedesktop.DBus.Error.UnknownProperty"),
425           "Property '#{interface_name}.#{property_name}' (on object '#{@path}') not found"
426   end
427 
428   property
429 end
dbus_properties_changed(interface_name, changed_props, invalidated_props) click to toggle source

Use this instead of calling PropertiesChanged directly. This one considers not only the PC signature (which says that all property values are variants) but also the specific property type. @param interface_name [String] interface name like “org.example.ManagerManager” @param changed_props [Hash{String => ::Object}]

changed properties (D-Bus names) and their values.

@param invalidated_props [Array<String>]

names of properties whose changed value is not specified
    # File lib/dbus/object.rb
396 def dbus_properties_changed(interface_name, changed_props, invalidated_props)
397   typed_changed_props = changed_props.map do |dbus_name, value|
398     property = dbus_lookup_property(interface_name, dbus_name)
399     type = property.type
400     typed_value = Data.make_typed(type, value)
401     variant = Data::Variant.new(typed_value, member_type: type)
402     [dbus_name, variant]
403   end.to_h
404   PropertiesChanged(interface_name, typed_changed_props, invalidated_props)
405 end
dispatch(msg) click to toggle source

Dispatch a message msg to call exported methods @param msg [Message] only METHOD_CALLS do something @api private

   # File lib/dbus/object.rb
58 def dispatch(msg)
59   case msg.message_type
60   when Message::METHOD_CALL
61     reply = nil
62     begin
63       iface = intfs[msg.interface]
64       if !iface
65         raise DBus.error("org.freedesktop.DBus.Error.UnknownMethod"),
66               "Interface \"#{msg.interface}\" of object \"#{msg.path}\" doesn't exist"
67       end
68       member_sym = msg.member.to_sym
69       meth = iface.methods[member_sym]
70       if !meth
71         raise DBus.error("org.freedesktop.DBus.Error.UnknownMethod"),
72               "Method \"#{msg.member}\" on interface \"#{msg.interface}\" of object \"#{msg.path}\" doesn't exist"
73       end
74       methname = Object.make_method_name(msg.interface, msg.member)
75       retdata = method(methname).call(*msg.params)
76       retdata = [*retdata]
77 
78       reply = Message.method_return(msg)
79       rsigs = meth.rets.map(&:type)
80       rsigs.zip(retdata).each do |rsig, rdata|
81         reply.add_param(rsig, rdata)
82       end
83     rescue StandardError => e
84       dbus_msg_exc = msg.annotate_exception(e)
85       reply = ErrorMessage.from_exception(dbus_msg_exc).reply_to(msg)
86     end
87     # TODO: this method chain is too long,
88     # we should probably just return reply [Message] like we get a [Message]
89     object_server.connection.message_queue.push(reply)
90   end
91 end
emit(intf, sig, *args) click to toggle source

Emits a signal from the object with the given interface, signal sig and arguments args. @param intf [Interface] @param sig [Signal] @param args arguments for the signal

    # File lib/dbus/object.rb
344 def emit(intf, sig, *args)
345   raise "Cannot emit signal #{intf.name}.#{sig.name} before #{path} is exported" if object_server.nil?
346 
347   object_server.connection.emit(nil, self, intf, sig, *args)
348 end
interfaces_and_properties() click to toggle source

Generates information about interfaces and properties of the object

Returns a hash containing interfaces names as keys. Each value is the same hash that would be returned by the org.freedesktop.DBus.Properties.GetAll() method for that combination of object path and interface. If an interface has no properties, the empty hash is returned.

@return [Hash{String => Hash{String => Data::Base}}] interface -> property -> value

    # File lib/dbus/object.rb
440 def interfaces_and_properties
441   get_all_method = self.class.make_method_name("org.freedesktop.DBus.Properties", :GetAll)
442 
443   intfs.keys.each_with_object({}) do |interface, hash|
444     hash[interface] = public_send(get_all_method, interface).first
445   end
446 end
object_server() click to toggle source

@return [ObjectServer] the server the object is exported by

   # File lib/dbus/object.rb
42 def object_server
43   # tests may mock the old ivar
44   @object_server || @service
45 end
object_server=(server) click to toggle source

@param server [ObjectServer] the server the object is exported by @note only the server itself should call this in its export/#unexport

   # File lib/dbus/object.rb
49 def object_server=(server)
50   # until v0.22.1 there was attr_writer :service
51   # so subclasses only could use @service
52   @object_server = @service = server
53 end