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
@return [ObjectPath] The path of the object.
Public Class Methods
Source
# File lib/dbus/object.rb, line 375 def self.camelize(str) str.split(/_/).map(&:capitalize).join("") end
TODO: borrow a proven implementation @param str [String] @return [String] @api private
Source
# File lib/dbus/object.rb, line 213 def self.dbus_accessor(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) raise UndefinedInterface, ruby_name if @@cur_intf.nil? dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name) property = Property.new(dbus_name, type, :readwrite, ruby_name: ruby_name) @@cur_intf.define(property) dbus_watcher(ruby_name, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal) end
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)
Source
# File lib/dbus/object.rb, line 153 def self.dbus_attr_accessor(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) attr_accessor(ruby_name) dbus_accessor(ruby_name, type, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal) end
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]
Source
# File lib/dbus/object.rb, line 188 def self.dbus_attr_reader(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) attr_reader(ruby_name) dbus_reader(ruby_name, type, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal) end
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)
Source
# File lib/dbus/object.rb, line 199 def self.dbus_attr_writer(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) attr_writer(ruby_name) dbus_writer(ruby_name, type, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal) end
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)
Source
# File lib/dbus/object.rb, line 97 def self.dbus_interface(name) @@intfs_mutex.synchronize do @@cur_intf = intfs[name] if !@@cur_intf @@cur_intf = Interface.new(name) # validates the name # As this is a mutable class_attr, we cannot use # self.intfs[name] = @@cur_intf # Hash#[]= # as that would modify parent class attr in place. # Using the setter lets a subclass have the new value # while the superclass keeps the old one. self.intfs = intfs.merge(name => @@cur_intf) end begin yield ensure @@cur_intf = nil end end end
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
Source
# File lib/dbus/object.rb, line 329 def self.dbus_method(sym, prototype = "", &block) raise UndefinedInterface, sym if @@cur_intf.nil? @@cur_intf.define(Method.new(sym.to_s).from_prototype(prototype)) ruby_name = Object.make_method_name(@@cur_intf.name, sym.to_s) # ::Module#define_method(name) { body } define_method(ruby_name, &block) end
Defines an exportable method on the object with the given name sym, prototype and the code in a block. @param prototype [Prototype]
Source
# File lib/dbus/object.rb, line 249 def self.dbus_reader(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) raise UndefinedInterface, ruby_name if @@cur_intf.nil? dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name) property = Property.new(dbus_name, type, :read, ruby_name: ruby_name) @@cur_intf.define(property) ruby_name_eq = :"#{ruby_name}=" return unless method_defined?(ruby_name_eq) dbus_watcher(ruby_name, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal) end
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)
Source
# File lib/dbus/object.rb, line 164 def self.dbus_reader_attr_accessor(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) attr_accessor(ruby_name) dbus_reader(ruby_name, type, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal) end
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)
Source
# File lib/dbus/object.rb, line 351 def self.dbus_signal(sym, prototype = "") raise UndefinedInterface, sym if @@cur_intf.nil? cur_intf = @@cur_intf signal = Signal.new(sym.to_s).from_prototype(prototype) cur_intf.define(signal) # ::Module#define_method(name) { body } define_method(sym.to_s) do |*args| emit(cur_intf, signal, *args) end end
Defines a signal for the object with a given name sym and prototype.
Source
# File lib/dbus/object.rb, line 291 def self.dbus_watcher(ruby_name, dbus_name: nil, emits_changed_signal: nil) raise UndefinedInterface, ruby_name if @@cur_intf.nil? interface_name = @@cur_intf.name ruby_name = ruby_name.to_s.sub(/=$/, "").to_sym ruby_name_eq = :"#{ruby_name}=" original_ruby_name_eq = "_original_#{ruby_name_eq}" dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name) emits_changed_signal = EmitsChangedSignal.new(emits_changed_signal, interface: @@cur_intf) # the argument order is alias_method(new_name, existing_name) alias_method original_ruby_name_eq, ruby_name_eq define_method ruby_name_eq do |value| result = public_send(original_ruby_name_eq, value) case emits_changed_signal.value when true # signature: "interface:s, changed_props:a{sv}, invalidated_props:as" dbus_properties_changed(interface_name, { dbus_name.to_s => value }, []) when :invalidates dbus_properties_changed(interface_name, {}, [dbus_name.to_s]) when :const # Oh my, seeing a value change of a supposedly constant property. # Maybe should have raised at declaration time, don't make a fuss now. when false # Do nothing end result end end
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]
Source
# File lib/dbus/object.rb, line 269 def self.dbus_writer(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) raise UndefinedInterface, ruby_name if @@cur_intf.nil? dbus_name = make_dbus_name(ruby_name, dbus_name: dbus_name) property = Property.new(dbus_name, type, :write, ruby_name: ruby_name) @@cur_intf.define(property) dbus_watcher(ruby_name, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal) end
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)
Source
# File lib/dbus/object.rb, line 131 def self.emits_changed_signal=(value) raise UndefinedInterface, :emits_changed_signal if @@cur_intf.nil? @@cur_intf.emits_changed_signal = EmitsChangedSignal.new(value) end
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
Source
# File lib/dbus/object.rb, line 383 def self.make_dbus_name(ruby_name, dbus_name: nil) dbus_name ||= camelize(ruby_name.to_s) dbus_name.to_sym end
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
Source
# File lib/dbus/object.rb, line 367 def self.make_method_name(intfname, methname) "#{intfname}%%#{methname}" end
Helper method that returns a method name generated from the interface name intfname and method name methname. @api private
Source
# File lib/dbus/object.rb, line 35 def initialize(path) @path = path # TODO: what parts of our API are supposed to work before we're exported? self.object_server = nil end
Create a new object with a given path. Use ObjectServer#export
to export it. @param path [ObjectPath] The path of the object.
Public Instance Methods
Source
# File lib/dbus/object.rb, line 412 def dbus_lookup_property(interface_name, property_name) # what should happen for unknown properties # plasma: InvalidArgs (propname), UnknownInterface (interface) # systemd: UnknownProperty interface = intfs[interface_name] if !interface raise DBus.error("org.freedesktop.DBus.Error.UnknownProperty"), "Property '#{interface_name}.#{property_name}' (on object '#{@path}') not found: no such interface" end property = interface.properties[property_name.to_sym] if !property raise DBus.error("org.freedesktop.DBus.Error.UnknownProperty"), "Property '#{interface_name}.#{property_name}' (on object '#{@path}') not found" end property end
@param interface_name [String] @param property_name [String] @return [Property] @raise [DBus::Error] @api private
Source
# File lib/dbus/object.rb, line 396 def dbus_properties_changed(interface_name, changed_props, invalidated_props) typed_changed_props = changed_props.map do |dbus_name, value| property = dbus_lookup_property(interface_name, dbus_name) type = property.type typed_value = Data.make_typed(type, value) variant = Data::Variant.new(typed_value, member_type: type) [dbus_name, variant] end.to_h PropertiesChanged(interface_name, typed_changed_props, invalidated_props) end
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
Source
# File lib/dbus/object.rb, line 58 def dispatch(msg) case msg.message_type when Message::METHOD_CALL reply = nil begin iface = intfs[msg.interface] if !iface raise DBus.error("org.freedesktop.DBus.Error.UnknownMethod"), "Interface \"#{msg.interface}\" of object \"#{msg.path}\" doesn't exist" end member_sym = msg.member.to_sym meth = iface.methods[member_sym] if !meth raise DBus.error("org.freedesktop.DBus.Error.UnknownMethod"), "Method \"#{msg.member}\" on interface \"#{msg.interface}\" of object \"#{msg.path}\" doesn't exist" end methname = Object.make_method_name(msg.interface, msg.member) retdata = method(methname).call(*msg.params) retdata = [*retdata] reply = Message.method_return(msg) rsigs = meth.rets.map(&:type) rsigs.zip(retdata).each do |rsig, rdata| reply.add_param(rsig, rdata) end rescue StandardError => e dbus_msg_exc = msg.annotate_exception(e) reply = ErrorMessage.from_exception(dbus_msg_exc).reply_to(msg) end # TODO: this method chain is too long, # we should probably just return reply [Message] like we get a [Message] object_server.connection.message_queue.push(reply) end end
Dispatch a message msg to call exported methods @param msg [Message] only METHOD_CALLS do something @api private
Source
# File lib/dbus/object.rb, line 344 def emit(intf, sig, *args) raise "Cannot emit signal #{intf.name}.#{sig.name} before #{path} is exported" if object_server.nil? object_server.connection.emit(nil, self, intf, sig, *args) end
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
Source
# File lib/dbus/object.rb, line 440 def interfaces_and_properties get_all_method = self.class.make_method_name("org.freedesktop.DBus.Properties", :GetAll) intfs.keys.each_with_object({}) do |interface, hash| hash[interface] = public_send(get_all_method, interface).first end end
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
Source
# File lib/dbus/object.rb, line 42 def object_server # tests may mock the old ivar @object_server || @service end
@return [ObjectServer] the server the object is exported by
Source
# File lib/dbus/object.rb, line 49 def object_server=(server) # until v0.22.1 there was attr_writer :service # so subclasses only could use @service @object_server = @service = server end
@param server [ObjectServer] the server the object is exported by @note only the server itself should call this in its export/#unexport