# 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