ruby-dbus 0.12.0 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
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