ruby-dbus 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/NEWS.md +344 -0
  3. data/Rakefile +21 -15
  4. data/VERSION +1 -1
  5. data/doc/Reference.md +26 -37
  6. data/examples/doc/_extract_examples +4 -4
  7. data/examples/gdbus/gdbus +40 -39
  8. data/examples/no-introspect/nm-test.rb +1 -3
  9. data/examples/no-introspect/tracker-test.rb +2 -4
  10. data/examples/rhythmbox/playpause.rb +1 -2
  11. data/examples/service/call_service.rb +0 -3
  12. data/examples/service/service_newapi.rb +3 -4
  13. data/examples/simple/call_introspect.rb +0 -3
  14. data/examples/simple/get_id.rb +1 -3
  15. data/examples/simple/properties.rb +3 -4
  16. data/examples/utils/listnames.rb +6 -7
  17. data/examples/utils/notify.rb +3 -5
  18. data/lib/dbus.rb +8 -20
  19. data/lib/dbus/auth.rb +59 -61
  20. data/lib/dbus/bus.rb +86 -97
  21. data/lib/dbus/error.rb +1 -1
  22. data/lib/dbus/export.rb +20 -18
  23. data/lib/dbus/introspect.rb +26 -28
  24. data/lib/dbus/logger.rb +1 -1
  25. data/lib/dbus/marshall.rb +72 -74
  26. data/lib/dbus/matchrule.rb +22 -26
  27. data/lib/dbus/message.rb +24 -33
  28. data/lib/dbus/message_queue.rb +30 -30
  29. data/lib/dbus/proxy_object.rb +34 -30
  30. data/lib/dbus/proxy_object_factory.rb +5 -2
  31. data/lib/dbus/proxy_object_interface.rb +10 -8
  32. data/lib/dbus/type.rb +154 -156
  33. data/lib/dbus/xml.rb +22 -16
  34. data/ruby-dbus.gemspec +15 -3
  35. data/spec/async_spec.rb +2 -4
  36. data/spec/binding_spec.rb +1 -5
  37. data/spec/bus_and_xml_backend_spec.rb +6 -9
  38. data/spec/bus_spec.rb +1 -1
  39. data/spec/byte_array_spec.rb +0 -2
  40. data/spec/err_msg_spec.rb +13 -10
  41. data/spec/introspect_xml_parser_spec.rb +2 -2
  42. data/spec/introspection_spec.rb +1 -1
  43. data/spec/main_loop_spec.rb +4 -5
  44. data/spec/property_spec.rb +0 -3
  45. data/spec/proxy_object_spec.rb +42 -0
  46. data/spec/server_robustness_spec.rb +0 -2
  47. data/spec/service_newapi.rb +51 -41
  48. data/spec/session_bus_spec.rb +6 -7
  49. data/spec/signal_spec.rb +2 -3
  50. data/spec/spec_helper.rb +28 -24
  51. data/spec/thread_safety_spec.rb +1 -2
  52. data/spec/type_spec.rb +2 -2
  53. data/spec/value_spec.rb +7 -10
  54. data/spec/variant_spec.rb +15 -16
  55. metadata +60 -3
  56. data/NEWS +0 -279
@@ -16,7 +16,7 @@ module DBus
16
16
  # FIXME
17
17
  class MatchRule
18
18
  # The list of possible match filters. TODO argN, argNpath
19
- FILTERS = [:sender, :interface, :member, :path, :destination, :type]
19
+ FILTERS = [:sender, :interface, :member, :path, :destination, :type].freeze
20
20
  # The sender filter.
21
21
  attr_accessor :sender
22
22
  # The interface filter.
@@ -38,35 +38,31 @@ module DBus
38
38
  # Set the message types to filter to type _t_.
39
39
  # Possible message types are: signal, method_call, method_return, and error.
40
40
  def type=(t)
41
- if not ['signal', 'method_call', 'method_return', 'error'].member?(t)
41
+ if !["signal", "method_call", "method_return", "error"].member?(t)
42
42
  raise MatchRuleException, t
43
43
  end
44
44
  @type = t
45
45
  end
46
46
 
47
- # Returns a match rule string version of the object.
48
- # E.g.: "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='Foo',path='/bar/foo',destination=':452345.34',arg2='bar'"
47
+ # Returns a match rule string version of the object. E.g.:
48
+ # "type='signal',sender='org.freedesktop.DBus'," +
49
+ # "interface='org.freedesktop.DBus',member='Foo'," +
50
+ # "path='/bar/foo',destination=':452345.34',arg2='bar'"
49
51
  def to_s
50
- FILTERS.select do |sym|
51
- not method(sym).call.nil?
52
- end.collect do |sym|
53
- "#{sym.to_s}='#{method(sym).call}'"
54
- end.join(",")
52
+ present_rules = FILTERS.select { |sym| method(sym).call }
53
+ present_rules.map! { |sym| "#{sym}='#{method(sym).call}'" }
54
+ present_rules.join(",")
55
55
  end
56
56
 
57
57
  # Parses a match rule string _s_ and sets the filters on the object.
58
58
  def from_s(str)
59
59
  str.split(",").each do |eq|
60
- if eq =~ /^(.*)='([^']*)'$/
61
- # "
62
- name = $1
63
- val = $2
64
- if FILTERS.member?(name.to_sym)
65
- method(name + "=").call(val)
66
- else
67
- raise MatchRuleException, name
68
- end
69
- end
60
+ next unless eq =~ /^(.*)='([^']*)'$/
61
+ # "
62
+ name = Regexp.last_match(1)
63
+ val = Regexp.last_match(2)
64
+ raise MatchRuleException, name unless FILTERS.member?(name.to_sym)
65
+ method(name + "=").call(val)
70
66
  end
71
67
  self
72
68
  end
@@ -85,16 +81,16 @@ module DBus
85
81
  # Determines whether a message _msg_ matches the match rule.
86
82
  def match(msg)
87
83
  if @type
88
- if {Message::SIGNAL => "signal", Message::METHOD_CALL => "method_call",
89
- Message::METHOD_RETURN => "method_return",
90
- Message::ERROR => "error"}[msg.message_type] != @type
84
+ if { Message::SIGNAL => "signal", Message::METHOD_CALL => "method_call",
85
+ Message::METHOD_RETURN => "method_return",
86
+ Message::ERROR => "error" }[msg.message_type] != @type
91
87
  return false
92
88
  end
93
89
  end
94
- return false if @interface and @interface != msg.interface
95
- return false if @member and @member != msg.member
96
- return false if @path and @path != msg.path
97
- # FIXME sender and destination are ignored
90
+ return false if @interface && @interface != msg.interface
91
+ return false if @member && @member != msg.member
92
+ return false if @path && @path != msg.path
93
+ # FIXME: sender and destination are ignored
98
94
  true
99
95
  end
100
96
  end # class MatchRule
@@ -27,7 +27,7 @@ module DBus
27
27
  # Mutex that protects updates on the serial number.
28
28
  @@serial_mutex = Mutex.new
29
29
  # Type of a message (by specification).
30
- MESSAGE_SIGNATURE = "yyyyuua(yv)"
30
+ MESSAGE_SIGNATURE = "yyyyuua(yv)".freeze
31
31
 
32
32
  # FIXME: following message type constants should be under Message::Type IMO
33
33
  # well, yeah sure
@@ -45,7 +45,7 @@ module DBus
45
45
 
46
46
  # Message flag signyfing that no reply is expected.
47
47
  NO_REPLY_EXPECTED = 0x1
48
- # Message flag signifying that no automatic start is required/must be
48
+ # Message flag signifying that no automatic start is required/must be
49
49
  # performed.
50
50
  NO_AUTO_START = 0x2
51
51
 
@@ -83,26 +83,26 @@ module DBus
83
83
  @flags = 0
84
84
  @protocol = 1
85
85
  @body_length = 0
86
- @signature = String.new
86
+ @signature = ""
87
87
  @@serial_mutex.synchronize do
88
88
  @serial = @@serial
89
89
  @@serial += 1
90
90
  end
91
- @params = Array.new
91
+ @params = []
92
92
  @destination = nil
93
93
  @interface = nil
94
94
  @error_name = nil
95
95
  @member = nil
96
96
  @path = nil
97
97
  @reply_serial = nil
98
-
99
- if mtype == METHOD_RETURN
100
- @flags = NO_REPLY_EXPECTED
101
- end
98
+ @flags = NO_REPLY_EXPECTED if mtype == METHOD_RETURN
102
99
  end
103
100
 
104
101
  def to_s
105
- "#{message_type} sender=#{sender} -> dest=#{destination} serial=#{serial} reply_serial=#{reply_serial} path=#{path}; interface=#{interface}; member=#{member} error_name=#{error_name}"
102
+ "#{message_type} sender=#{sender} -> dest=#{destination} " \
103
+ "serial=#{serial} reply_serial=#{reply_serial} " \
104
+ "path=#{path}; interface=#{interface}; member=#{member} " \
105
+ "error_name=#{error_name}"
106
106
  end
107
107
 
108
108
  # Create a regular reply to a message _m_.
@@ -111,7 +111,7 @@ module DBus
111
111
  end
112
112
 
113
113
  # Create an error reply to a message _m_.
114
- def self.error(m, error_name, description=nil)
114
+ def self.error(m, error_name, description = nil)
115
115
  ErrorMessage.new(error_name, description).reply_to(m)
116
116
  end
117
117
 
@@ -126,7 +126,7 @@ module DBus
126
126
 
127
127
  # Add a parameter _val_ of type _type_ to the message.
128
128
  def add_param(type, val)
129
- type = type.chr if type.kind_of?(Fixnum)
129
+ type = type.chr if type.is_a?(Fixnum)
130
130
  @signature += type.to_s
131
131
  @params << [type, val]
132
132
  end
@@ -187,18 +187,19 @@ module DBus
187
187
  # Unmarshall a packet contained in the buffer _buf_ and set the
188
188
  # parameters of the message object according the data found in the
189
189
  # buffer.
190
- # Return the detected message and the index pointer of the buffer where
191
- # the message data ended.
190
+ # @return [Array(Message,Integer)]
191
+ # the detected message (self) and
192
+ # the index pointer of the buffer where the message data ended.
192
193
  def unmarshall_buffer(buf)
193
194
  buf = buf.dup
194
- if buf[0] == ?l
195
- endianness = LIL_END
196
- else
197
- endianness = BIG_END
198
- end
195
+ endianness = if buf[0] == "l"
196
+ LIL_END
197
+ else
198
+ BIG_END
199
+ end
199
200
  pu = PacketUnmarshaller.new(buf, endianness)
200
201
  mdata = pu.unmarshall(MESSAGE_SIGNATURE)
201
- _, @message_type, @flags, @protocol, @body_length, @serial,
202
+ _, @message_type, @flags, @protocol, @body_length, @serial,
202
203
  headers = mdata
203
204
 
204
205
  headers.each do |struct|
@@ -222,18 +223,10 @@ module DBus
222
223
  end
223
224
  end
224
225
  pu.align(8)
225
- if @body_length > 0 and @signature
226
+ if @body_length > 0 && @signature
226
227
  @params = pu.unmarshall(@signature, @body_length)
227
228
  end
228
229
  [self, pu.idx]
229
- end # def unmarshall_buf
230
-
231
- # Unmarshall the data of a message found in the buffer _buf_ using
232
- # Message#unmarshall_buf.
233
- # Return the message.
234
- def unmarshall(buf)
235
- ret, _ = unmarshall_buffer(buf)
236
- ret
237
230
  end
238
231
 
239
232
  # Make a new exception from ex, mark it as being caused by this message
@@ -252,12 +245,10 @@ module DBus
252
245
  end
253
246
 
254
247
  class ErrorMessage < Message
255
- def initialize(error_name, description=nil)
248
+ def initialize(error_name, description = nil)
256
249
  super(ERROR)
257
250
  @error_name = error_name
258
- unless description.nil?
259
- add_param(Type::STRING, description)
260
- end
251
+ add_param(Type::STRING, description) unless description.nil?
261
252
  end
262
253
 
263
254
  def self.from_exception(ex)
@@ -268,7 +259,7 @@ module DBus
268
259
  # ex.class.to_s # RuntimeError is not a valid name, has no dot
269
260
  end
270
261
  description = ex.message
271
- msg = self.new(name, description)
262
+ msg = new(name, description)
272
263
  msg.add_param(DBus.type("as"), ex.backtrace)
273
264
  msg
274
265
  end
@@ -22,7 +22,7 @@ module DBus
22
22
  connect
23
23
  end
24
24
 
25
- # TODO failure modes
25
+ # TODO: failure modes
26
26
  #
27
27
  # If _non_block_ is true, return nil instead of waiting
28
28
  # EOFError may be raised
@@ -32,8 +32,8 @@ module DBus
32
32
  unless non_block
33
33
  # we can block
34
34
  while message.nil?
35
- r, d, d = IO.select([@socket])
36
- if r and r[0] == @socket
35
+ r, _d, _d = IO.select([@socket])
36
+ if r && r[0] == @socket
37
37
  buffer_from_socket_nonblock
38
38
  message = message_from_buffer_nonblock
39
39
  end
@@ -45,7 +45,7 @@ module DBus
45
45
  def push(message)
46
46
  @socket.write(message.marshall)
47
47
  end
48
- alias :<< :push
48
+ alias << push
49
49
 
50
50
  private
51
51
 
@@ -56,20 +56,20 @@ module DBus
56
56
  worked = addresses.find do |a|
57
57
  transport, keyvaluestring = a.split ":"
58
58
  kv_list = keyvaluestring.split ","
59
- kv_hash = Hash.new
59
+ kv_hash = {}
60
60
  kv_list.each do |kv|
61
61
  key, escaped_value = kv.split "="
62
- value = escaped_value.gsub(/%(..)/) {|m| [$1].pack "H2" }
62
+ value = escaped_value.gsub(/%(..)/) { |_m| [Regexp.last_match(1)].pack "H2" }
63
63
  kv_hash[key] = value
64
64
  end
65
65
  case transport
66
- when "unix"
66
+ when "unix"
67
67
  connect_to_unix kv_hash
68
- when "tcp"
68
+ when "tcp"
69
69
  connect_to_tcp kv_hash
70
- when "launchd"
70
+ when "launchd"
71
71
  connect_to_launchd kv_hash
72
- else
72
+ else
73
73
  # ignore, report?
74
74
  end
75
75
  end
@@ -80,46 +80,46 @@ module DBus
80
80
 
81
81
  # Connect to a bus over tcp and initialize the connection.
82
82
  def connect_to_tcp(params)
83
- #check if the path is sufficient
84
- if params.key?("host") and params.key?("port")
83
+ # check if the path is sufficient
84
+ if params.key?("host") && params.key?("port")
85
85
  begin
86
- #initialize the tcp socket
87
- @socket = TCPSocket.new(params["host"],params["port"].to_i)
86
+ # initialize the tcp socket
87
+ @socket = TCPSocket.new(params["host"], params["port"].to_i)
88
88
  @socket.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
89
89
  init_connection
90
90
  @is_tcp = true
91
91
  rescue Exception => e
92
92
  puts "Oops:", e
93
93
  puts "Error: Could not establish connection to: #{@path}, will now exit."
94
- exit(1) #a little harsh
94
+ exit(1) # a little harsh
95
95
  end
96
96
  else
97
- #Danger, Will Robinson: the specified "path" is not usable
97
+ # Danger, Will Robinson: the specified "path" is not usable
98
98
  puts "Error: supplied path: #{@path}, unusable! sorry."
99
99
  end
100
100
  end
101
101
 
102
102
  # Connect to an abstract unix bus and initialize the connection.
103
103
  def connect_to_unix(params)
104
- @socket = Socket.new(Socket::Constants::PF_UNIX,Socket::Constants::SOCK_STREAM, 0)
104
+ @socket = Socket.new(Socket::Constants::PF_UNIX, Socket::Constants::SOCK_STREAM, 0)
105
105
  @socket.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
106
- if ! params['abstract'].nil?
107
- if HOST_END == LIL_END
108
- sockaddr = "\1\0\0#{params['abstract']}"
109
- else
110
- sockaddr = "\0\1\0#{params['abstract']}"
111
- end
112
- elsif ! params['path'].nil?
113
- sockaddr = Socket.pack_sockaddr_un(params['path'])
106
+ if !params["abstract"].nil?
107
+ sockaddr = if HOST_END == LIL_END
108
+ "\1\0\0#{params["abstract"]}"
109
+ else
110
+ "\0\1\0#{params["abstract"]}"
111
+ end
112
+ elsif !params["path"].nil?
113
+ sockaddr = Socket.pack_sockaddr_un(params["path"])
114
114
  end
115
115
  @socket.connect(sockaddr)
116
116
  init_connection
117
117
  end
118
118
 
119
119
  def connect_to_launchd(params)
120
- socket_var = params['env']
120
+ socket_var = params["env"]
121
121
  socket = `launchctl getenv #{socket_var}`.chomp
122
- connect_to_unix 'path' => socket
122
+ connect_to_unix "path" => socket
123
123
  end
124
124
 
125
125
  # Initialize the connection to the bus.
@@ -128,7 +128,7 @@ module DBus
128
128
  @client.authenticate
129
129
  end
130
130
 
131
- public # FIXME: fix Main loop instead
131
+ public # FIXME: fix Main loop instead
132
132
 
133
133
  # Get and remove one message from the buffer.
134
134
  # Return the message or nil.
@@ -153,12 +153,12 @@ module DBus
153
153
  def buffer_from_socket_nonblock
154
154
  @buffer += @socket.read_nonblock(MSG_BUF_SIZE)
155
155
  rescue EOFError
156
- raise # the caller expects it
156
+ raise # the caller expects it
157
157
  rescue Errno::EAGAIN
158
158
  # fine, would block
159
159
  rescue Exception => e
160
160
  puts "Oops:", e
161
- raise if @is_tcp # why?
161
+ raise if @is_tcp # why?
162
162
  puts "WARNING: read_nonblock failed, falling back to .recv"
163
163
  @buffer += @socket.recv(MSG_BUF_SIZE)
164
164
  end
@@ -34,14 +34,18 @@ module DBus
34
34
  # Creates a new proxy object living on the given _bus_ at destination _dest_
35
35
  # on the given _path_.
36
36
  def initialize(bus, dest, path, api: ApiOptions::CURRENT)
37
- @bus, @destination, @path = bus, dest, path
38
- @interfaces = Hash.new
39
- @subnodes = Array.new
37
+ @bus = bus
38
+ @destination = dest
39
+ @path = path
40
+ @introspected = false
41
+ @interfaces = {}
42
+ @subnodes = []
40
43
  @api = api
41
44
  end
42
45
 
43
46
  # Returns the interfaces of the object.
44
47
  def interfaces
48
+ introspect unless introspected
45
49
  @interfaces.keys
46
50
  end
47
51
 
@@ -49,6 +53,7 @@ module DBus
49
53
  # @param [String] intfname
50
54
  # @return [ProxyObjectInterface]
51
55
  def [](intfname)
56
+ introspect unless introspected
52
57
  @interfaces[intfname]
53
58
  end
54
59
 
@@ -56,6 +61,7 @@ module DBus
56
61
  # @param [String] intfname
57
62
  # @param [ProxyObjectInterface] intf
58
63
  # @return [ProxyObjectInterface]
64
+ # @api private
59
65
  def []=(intfname, intf)
60
66
  @interfaces[intfname] = intf
61
67
  end
@@ -66,7 +72,7 @@ module DBus
66
72
  # Synchronous call here.
67
73
  xml = @bus.introspect_data(@destination, @path)
68
74
  ProxyObjectFactory.introspect_into(self, xml)
69
- define_shortcut_methods()
75
+ define_shortcut_methods
70
76
  xml
71
77
  end
72
78
 
@@ -76,14 +82,15 @@ module DBus
76
82
  # introspected.
77
83
  def define_shortcut_methods
78
84
  # builds a list of duplicated methods
79
- dup_meths, univocal_meths = [],{}
85
+ dup_meths = []
86
+ univocal_meths = {}
80
87
  @interfaces.each_value do |intf|
81
88
  intf.methods.each_value do |meth|
82
89
  name = meth.name.to_sym
83
90
  # don't overwrite instance methods!
84
- if dup_meths.include? name or self.class.instance_methods.include? name
85
- next
86
- elsif univocal_meths.include? name
91
+ next if dup_meths.include?(name)
92
+ next if self.class.instance_methods.include?(name)
93
+ if univocal_meths.include? name
87
94
  univocal_meths.delete name
88
95
  dup_meths << name
89
96
  else
@@ -95,7 +102,7 @@ module DBus
95
102
  # creates a shortcut function that forwards each call to the method on
96
103
  # the appropriate intf
97
104
  singleton_class.class_eval do
98
- define_method name do |*args, &reply_handler|
105
+ redefine_method name do |*args, &reply_handler|
99
106
  intf.method(name).call(*args, &reply_handler)
100
107
  end
101
108
  end
@@ -104,20 +111,17 @@ module DBus
104
111
 
105
112
  # Returns whether the object has an interface with the given _name_.
106
113
  def has_iface?(name)
107
- raise "Cannot call has_iface? if not introspected" if not @introspected
108
- @interfaces.member?(name)
114
+ introspect unless introspected
115
+ @interfaces.key?(name)
109
116
  end
110
117
 
111
118
  # Registers a handler, the code block, for a signal with the given _name_.
112
119
  # It uses _default_iface_ which must have been set.
113
120
  # @return [void]
114
121
  def on_signal(name, &block)
115
- if @default_iface and has_iface?(@default_iface)
116
- @interfaces[@default_iface].on_signal(name, &block)
117
- else
118
- # TODO improve
119
- raise NoMethodError
120
- end
122
+ # TODO: improve
123
+ raise NoMethodError unless @default_iface && has_iface?(@default_iface)
124
+ @interfaces[@default_iface].on_signal(name, &block)
121
125
  end
122
126
 
123
127
  ####################################################
@@ -126,23 +130,23 @@ module DBus
126
130
  # Handles all unkown methods, mostly to route method calls to the
127
131
  # default interface.
128
132
  def method_missing(name, *args, &reply_handler)
129
- if @default_iface and has_iface?(@default_iface)
130
- begin
131
- @interfaces[@default_iface].method(name).call(*args, &reply_handler)
132
- rescue NameError => e
133
- # interesting, foo.method("unknown")
134
- # raises NameError, not NoMethodError
135
- raise unless e.to_s =~ /undefined method `#{name}'/
136
- # BTW e.exception("...") would preserve the class.
137
- raise NoMethodError,"undefined method `#{name}' for DBus interface `#{@default_iface}' on object `#{@path}'"
138
- end
139
- else
140
- # TODO distinguish:
133
+ unless @default_iface && has_iface?(@default_iface)
134
+ # TODO: distinguish:
141
135
  # - di not specified
142
- #TODO
136
+ # TODO
143
137
  # - di is specified but not found in introspection data
144
138
  raise NoMethodError, "undefined method `#{name}' for DBus interface `#{@default_iface}' on object `#{@path}'"
145
139
  end
140
+
141
+ begin
142
+ @interfaces[@default_iface].method(name).call(*args, &reply_handler)
143
+ rescue NameError => e
144
+ # interesting, foo.method("unknown")
145
+ # raises NameError, not NoMethodError
146
+ raise unless e.to_s =~ /undefined method `#{name}'/
147
+ # BTW e.exception("...") would preserve the class.
148
+ raise NoMethodError, "undefined method `#{name}' for DBus interface `#{@default_iface}' on object `#{@path}'"
149
+ end
146
150
  end
147
151
  end # class ProxyObject
148
152
  end