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.
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
334 335 336 |
# File 'lib/dbus/object.rb', line 334 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.
178 179 180 181 182 183 184 185 186 |
# File 'lib/dbus/object.rb', line 178 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.
132 133 134 135 136 |
# File 'lib/dbus/object.rb', line 132 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])
153 154 155 156 157 |
# File 'lib/dbus/object.rb', line 153 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.
164 165 166 167 168 |
# File 'lib/dbus/object.rb', line 164 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 |
# 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 yield @@cur_intf = nil 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.
290 291 292 293 294 295 296 297 298 |
# File 'lib/dbus/object.rb', line 290 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])
210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/dbus/object.rb', line 210 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.
310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/dbus/object.rb', line 310 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=`.
252 253 254 255 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 |
# File 'lib/dbus/object.rb', line 252 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.
230 231 232 233 234 235 236 237 238 |
# File 'lib/dbus/object.rb', line 230 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)
111 112 113 114 115 |
# File 'lib/dbus/object.rb', line 111 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.
342 343 344 345 |
# File 'lib/dbus/object.rb', line 342 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.
326 327 328 |
# File 'lib/dbus/object.rb', line 326 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.
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 |
# File 'lib/dbus/object.rb', line 371 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.
355 356 357 358 359 360 361 362 363 364 |
# File 'lib/dbus/object.rb', line 355 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.
305 306 307 |
# File 'lib/dbus/object.rb', line 305 def emit(intf, sig, *args) @service.bus.emit(@service, self, intf, sig, *args) end |