• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mvidner / ruby-dbus / 14242378194

03 Apr 2025 11:27AM UTC coverage: 96.719% (+0.006%) from 96.713%
14242378194

Pull #147

github

mvidner
Mention qualified property name in Get or Set errors (TODO PR num)

Before:
The caller gets a D-Bus Error which includes interface and method name
and they are org.freedesktop.DBus.Properties.Get which is not enough.

After:
Also include the property name and its interface.
Pull Request #147: Mention qualified property name in Get or Set errors

561 of 649 branches covered (86.44%)

19 of 19 new or added lines in 5 files covered. (100.0%)

1 existing line in 1 file now uncovered.

2152 of 2225 relevant lines covered (96.72%)

4395.12 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.46
/lib/dbus/proxy_object.rb
1
# frozen_string_literal: true
2

3
# This file is part of the ruby-dbus project
4
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
5
# Copyright (C) 2009-2014 Martin Vidner
6
#
7
# This library is free software; you can redistribute it and/or
8
# modify it under the terms of the GNU Lesser General Public
9
# License, version 2.1 as published by the Free Software Foundation.
10
# See the file "COPYING" for the exact licensing terms.
11

12
module DBus
7✔
13
  # Represents a remote object in an external application.
14
  # Typically, calling a method on an instance of a ProxyObject sends a message
15
  # over the bus so that the method is executed remotely on the corresponding
16
  # object.
17
  class ProxyObject
7✔
18
    # The names of direct subnodes of the object in the tree.
19
    attr_accessor :subnodes
7✔
20
    # Flag determining whether the object has been introspected.
21
    # @return [Boolean]
22
    attr_accessor :introspected
7✔
23
    # The (remote) destination of the object.
24
    attr_reader :destination
7✔
25
    # The path to the object.
26
    # @return [ObjectPath]
27
    attr_reader :path
7✔
28
    # The bus the object is reachable via.
29
    attr_reader :bus
7✔
30
    # @return [String] The name of the default interface of the object.
31
    attr_accessor :default_iface
7✔
32
    # @api private
33
    # @return [ApiOptions]
34
    attr_reader :api
7✔
35

36
    OPEN_QUOTE = RUBY_VERSION >= "3.4" ? "'" : "`"
7!
37

38
    # Creates a new proxy object living on the given _bus_ at destination _dest_
39
    # on the given _path_.
40
    def initialize(bus, dest, path, api: ApiOptions::CURRENT)
7✔
41
      @bus = bus
229✔
42
      @destination = dest
229✔
43
      @path = ObjectPath.new(path)
229✔
44
      @introspected = false
228✔
45
      @interfaces = {}
228✔
46
      @subnodes = []
228✔
47
      @api = api
228✔
48
    end
49

50
    # Returns the interfaces of the object.
51
    # @return [Array<String>] names of the interfaces
52
    def interfaces
7✔
53
      introspect unless introspected
1!
54
      @interfaces.keys
1✔
55
    end
56

57
    # Retrieves an interface of the proxy object
58
    # @param [String] intfname
59
    # @return [ProxyObjectInterface]
60
    def [](intfname)
7✔
61
      introspect unless introspected
437✔
62
      ifc = @interfaces[intfname]
437✔
63
      raise DBus::Error, "no such interface #{OPEN_QUOTE}#{intfname}' on object #{OPEN_QUOTE}#{@path}'" unless ifc
437✔
64

65
      ifc
434✔
66
    end
67

68
    # Maps the given interface name _intfname_ to the given interface _intf.
69
    # @param [String] intfname
70
    # @param [ProxyObjectInterface] intf
71
    # @return [ProxyObjectInterface]
72
    # @api private
73
    def []=(intfname, intf)
7✔
74
      @interfaces[intfname] = intf
1,805✔
75
    end
76

77
    # Introspects the remote object. Allows you to find and select
78
    # interfaces on the object.
79
    def introspect
7✔
80
      # Synchronous call here.
81
      xml = @bus.introspect_data(@destination, @path)
197✔
82
      ProxyObjectFactory.introspect_into(self, xml)
196✔
83
      define_shortcut_methods
196✔
84
      xml
196✔
85
    end
86

87
    # For each non duplicated method name in any interface present on the
88
    # caller, defines a shortcut method dynamically.
89
    # This function is automatically called when a {ProxyObject} is
90
    # introspected.
91
    def define_shortcut_methods
7✔
92
      # builds a list of duplicated methods
93
      dup_meths = []
196✔
94
      univocal_meths = {}
196✔
95
      @interfaces.each_value do |intf|
196✔
96
        intf.methods.each_value do |meth|
1,740✔
97
          name = meth.name.to_sym
5,795✔
98
          # don't overwrite instance methods!
99
          next if dup_meths.include?(name)
5,795!
100
          next if self.class.instance_methods.include?(name)
5,795✔
101

102
          if univocal_meths.include? name
5,602✔
103
            univocal_meths.delete name
193✔
104
            dup_meths << name
193✔
105
          else
5,409✔
106
            univocal_meths[name] = intf
5,409✔
107
          end
108
        end
109
      end
110
      univocal_meths.each do |name, intf|
196✔
111
        # creates a shortcut function that forwards each call to the method on
112
        # the appropriate intf
113
        singleton_class.class_eval do
5,216✔
114
          redefine_method name do |*args, &reply_handler|
5,216✔
115
            intf.method(name).call(*args, &reply_handler)
24✔
116
          end
117
        end
118
      end
119
    end
120

121
    # Returns whether the object has an interface with the given _name_.
122
    def has_iface?(name)
7✔
123
      introspect unless introspected
186✔
124
      @interfaces.key?(name)
186✔
125
    end
126

127
    # Registers a handler, the code block, for a signal with the given _name_.
128
    # It uses _default_iface_ which must have been set.
129
    # @return [void]
130
    def on_signal(name, &block)
7✔
131
      unless @default_iface && has_iface?(@default_iface)
108✔
132
        raise NoMethodError, "undefined signal #{OPEN_QUOTE}#{name}' for DBus interface "\
1✔
133
                             "#{OPEN_QUOTE}#{@default_iface}' on object #{OPEN_QUOTE}#{@path}'"
134
      end
135

136
      @interfaces[@default_iface].on_signal(name, &block)
107✔
137
    end
138

139
    ####################################################
140
    private
7✔
141

142
    # rubocop:disable Lint/MissingSuper
143
    # as this should forward everything
144
    #
145
    # https://github.com/rubocop-hq/ruby-style-guide#no-method-missing
146
    # and http://blog.marc-andre.ca/2010/11/15/methodmissing-politely/
147
    # have a point to be investigated
148

149
    # Handles all unkown methods, mostly to route method calls to the
150
    # default interface.
151
    def method_missing(name, *args, &reply_handler)
7✔
152
      unless @default_iface && has_iface?(@default_iface)
79✔
153
        # TODO: distinguish:
154
        # - di not specified
155
        # TODO
156
        # - di is specified but not found in introspection data
1✔
157
        raise NoMethodError, "undefined method #{OPEN_QUOTE}#{name}' for DBus interface "\
1✔
158
                             "#{OPEN_QUOTE}#{@default_iface}' on object #{OPEN_QUOTE}#{@path}'"
159
      end
160

161
      begin
162
        @interfaces[@default_iface].method(name).call(*args, &reply_handler)
78✔
163
      rescue NameError => e
164
        # interesting, foo.method("unknown")
165
        # raises NameError, not NoMethodError
166
        raise unless e.to_s =~ /undefined method #{OPEN_QUOTE}#{name}'/
1!
167

168
        # BTW e.exception("...") would preserve the class.
169
        raise NoMethodError, "undefined method #{OPEN_QUOTE}#{name}' for DBus interface "\
1✔
170
                             "#{OPEN_QUOTE}#{@default_iface}' on object #{OPEN_QUOTE}#{@path}'"
171
      end
172
    end
173
    # rubocop:enable Lint/MissingSuper
174

175
    def respond_to_missing?(name, _include_private = false)
7✔
UNCOV
176
      @default_iface &&
×
177
        has_iface?(@default_iface) &&
178
        @interfaces[@default_iface].methods.key?(name) or super
179
    end
180
  end
181
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc