Class: DBus::Object
- Inherits:
-
Object
- Object
- DBus::Object
- Defined in:
- lib/dbus/object.rb
Overview
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.
Defined Under Namespace
Classes: UndefinedInterface
Constant Summary collapse
- @@cur_intf =
Interface
nil
- @@intfs_mutex =
Mutex.new
Instance Attribute Summary collapse
-
#path ⇒ Object
readonly
The path of the object.
-
#service ⇒ Object
writeonly
The service that the object is exported by.
Class Method Summary collapse
-
.camelize(str) ⇒ String
private
TODO: borrow a proven implementation.
-
.dbus_accessor(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) ⇒ void
A read-write property using a pair of reader/writer methods (which must already exist).
-
.dbus_attr_accessor(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) ⇒ void
A read-write property accessing an instance variable.
-
.dbus_attr_reader(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) ⇒ void
A read-only property accessing an instance variable.
-
.dbus_attr_writer(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) ⇒ void
A write-only property accessing an instance variable.
-
.dbus_interface(name) ⇒ Object
Select (and create) the interface that the following defined methods belong to.
-
.dbus_method(sym, prototype = "", &block) ⇒ Object
Defines an exportable method on the object with the given name sym, prototype and the code in a block.
-
.dbus_reader(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) ⇒ void
A read-only property accessing a reader method (which must already exist).
-
.dbus_signal(sym, prototype = "") ⇒ Object
Defines a signal for the object with a given name sym and prototype.
-
.dbus_watcher(ruby_name, dbus_name: nil, emits_changed_signal: nil) ⇒ void
Enables automatic sending of the PropertiesChanged signal.
-
.dbus_writer(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) ⇒ void
A write-only property accessing a writer method (which must already exist).
-
.emits_changed_signal=(value) ⇒ Object
Declare the behavior of PropertiesChanged signal, common for all properties in this interface (individual properties may override it).
-
.make_dbus_name(ruby_name, dbus_name: nil) ⇒ Symbol
Make a D-Bus conventional name, CamelCased.
-
.make_method_name(intfname, methname) ⇒ Object
private
Helper method that returns a method name generated from the interface name intfname and method name methname.
Instance Method Summary collapse
- #dbus_lookup_property(interface_name, property_name) ⇒ Property private
-
#dbus_properties_changed(interface_name, changed_props, invalidated_props) ⇒ Object
Use this instead of calling PropertiesChanged directly.
-
#dispatch(msg) ⇒ Object
Dispatch a message msg to call exported methods.
-
#emit(intf, sig, *args) ⇒ Object
Emits a signal from the object with the given interface, signal sig and arguments args.
-
#initialize(path) ⇒ Object
constructor
Create a new object with a given path.
-
#interfaces_and_properties ⇒ Hash{String => Hash{String => Data::Base}}
Generates information about interfaces and properties of the object.
Constructor Details
#initialize(path) ⇒ Object
Create a new object with a given path. Use Service#export to export it.
37 38 39 40 |
# File 'lib/dbus/object.rb', line 37 def initialize(path) @path = path @service = nil end |
Instance Attribute Details
#path ⇒ Object (readonly)
The path of the object.
23 24 25 |
# File 'lib/dbus/object.rb', line 23 def path @path end |
#service=(value) ⇒ Object (writeonly)
The service that the object is exported by.
30 31 32 |
# File 'lib/dbus/object.rb', line 30 def service=(value) @service = value end |
Class Method Details
.camelize(str) ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
TODO: borrow a proven implementation
338 339 340 |
# File 'lib/dbus/object.rb', line 338 def self.camelize(str) str.split(/_/).map(&:capitalize).join("") end |
.dbus_accessor(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) ⇒ void
This method returns an undefined value.
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.
182 183 184 185 186 187 188 189 190 |
# File 'lib/dbus/object.rb', line 182 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 |
.dbus_attr_accessor(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) ⇒ void
This method returns an undefined value.
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.
136 137 138 139 140 |
# File 'lib/dbus/object.rb', line 136 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 |
.dbus_attr_reader(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) ⇒ void
This method returns an undefined value.
A read-only property accessing an instance variable. A combination of ‘attr_reader` and dbus_reader.
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])
157 158 159 160 161 |
# File 'lib/dbus/object.rb', line 157 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 |
.dbus_attr_writer(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) ⇒ void
This method returns an undefined value.
A write-only property accessing an instance variable. A combination of ‘attr_writer` and dbus_writer.
168 169 170 171 172 |
# File 'lib/dbus/object.rb', line 168 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 |
.dbus_interface(name) ⇒ Object
Select (and create) the interface that the following defined methods belong to.
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/dbus/object.rb', line 80 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 |
.dbus_method(sym, prototype = "", &block) ⇒ Object
Defines an exportable method on the object with the given name sym, prototype and the code in a block.
294 295 296 297 298 299 300 301 302 |
# File 'lib/dbus/object.rb', line 294 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 |
.dbus_reader(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) ⇒ void
This method returns an undefined value.
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"
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])
214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/dbus/object.rb', line 214 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}=".to_sym return unless method_defined?(ruby_name_eq) dbus_watcher(ruby_name, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal) end |
.dbus_signal(sym, prototype = "") ⇒ Object
Defines a signal for the object with a given name sym and prototype.
314 315 316 317 318 319 320 321 322 323 324 325 |
# File 'lib/dbus/object.rb', line 314 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.new(sym.to_s).from_prototype(prototype)) # ::Module#define_method(name) { body } define_method(sym.to_s) do |*args| emit(cur_intf, signal, *args) end end |
.dbus_watcher(ruby_name, dbus_name: nil, emits_changed_signal: nil) ⇒ void
This method returns an undefined value.
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=`.
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 |
# File 'lib/dbus/object.rb', line 256 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}=".to_sym 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 |
.dbus_writer(ruby_name, type, dbus_name: nil, emits_changed_signal: nil) ⇒ void
This method returns an undefined value.
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.
234 235 236 237 238 239 240 241 242 |
# File 'lib/dbus/object.rb', line 234 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 |
.emits_changed_signal=(value) ⇒ Object
Declare the behavior of PropertiesChanged signal, common for all properties in this interface (individual properties may override it)
114 115 116 117 118 |
# File 'lib/dbus/object.rb', line 114 def self.emits_changed_signal=(value) raise UndefinedInterface, :emits_changed_signal if @@cur_intf.nil? @@cur_intf.emits_changed_signal = EmitsChangedSignal.new(value) end |
.make_dbus_name(ruby_name, dbus_name: nil) ⇒ Symbol
Make a D-Bus conventional name, CamelCased.
346 347 348 349 |
# File 'lib/dbus/object.rb', line 346 def self.make_dbus_name(ruby_name, dbus_name: nil) dbus_name ||= camelize(ruby_name.to_s) dbus_name.to_sym end |
.make_method_name(intfname, methname) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Helper method that returns a method name generated from the interface name intfname and method name methname.
330 331 332 |
# File 'lib/dbus/object.rb', line 330 def self.make_method_name(intfname, methname) "#{intfname}%%#{methname}" end |
Instance Method Details
#dbus_lookup_property(interface_name, property_name) ⇒ Property
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 |
# File 'lib/dbus/object.rb', line 375 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 |
#dbus_properties_changed(interface_name, changed_props, invalidated_props) ⇒ Object
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.
359 360 361 362 363 364 365 366 367 368 |
# File 'lib/dbus/object.rb', line 359 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 |
#dispatch(msg) ⇒ Object
Dispatch a message msg to call exported methods
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/dbus/object.rb', line 43 def dispatch(msg) case msg. 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 @service.bus..push(reply) end end |
#emit(intf, sig, *args) ⇒ Object
Emits a signal from the object with the given interface, signal sig and arguments args.
309 310 311 |
# File 'lib/dbus/object.rb', line 309 def emit(intf, sig, *args) @service.bus.emit(@service, self, intf, sig, *args) end |
#interfaces_and_properties ⇒ Hash{String => Hash{String => Data::Base}}
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.
403 404 405 406 407 408 409 |
# File 'lib/dbus/object.rb', line 403 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 |