ruby-dbus 0.6.0 → 0.7.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.
data/NEWS CHANGED
@@ -5,6 +5,22 @@ Note about bug numbers:
5
5
  Issue#1 - http://github.com/mvidner/ruby-dbus/issues#issue/1
6
6
  bnc#1 - https://bugzilla.novell.com/show_bug.cgi?id=1
7
7
 
8
+ == Ruby D-Bus 0.7.0 - 2011-07-26
9
+
10
+ Features:
11
+ * Added ASystemBus and ASessionBus, non-singletons useful in tests
12
+ and threads.
13
+
14
+ Bug fixes:
15
+ * Fixed handling of multibyte strings (Issue#8, by Takayuki YAMAGUCHI).
16
+ * Allow reopening of a dbus_interface declaration (Issue#9, by T. YAMAGUCHI).
17
+ * Fixed ruby-1.9.2 compatibility again (Issue#12).
18
+ * Fixed authentication on BSD (Issue#11, by Jonathan Walker)
19
+ * Fixed exiting a nested event loop for synchronous calls
20
+ (reported by Timo Warns).
21
+ * Fixed introspection calls leaking reply handlers.
22
+ * "rake test" now works, doing what was called "rake env:test"
23
+
8
24
  == Ruby D-Bus 0.6.0 - 2010-12-11
9
25
 
10
26
  Features:
data/README CHANGED
@@ -5,7 +5,7 @@ D-Bus system can be used in the Ruby programming language.
5
5
 
6
6
  == Requirements
7
7
 
8
- * Ruby 1.8 (>= 1.8.6?) or 1.9
8
+ * Ruby 1.8.7 or 1.9
9
9
 
10
10
  == Installation
11
11
 
data/Rakefile CHANGED
@@ -3,33 +3,32 @@ require 'rake'
3
3
  require 'rake/gempackagetask'
4
4
  require 'fileutils'
5
5
  include FileUtils
6
+ require 'tmpdir'
6
7
  require 'rake/rdoctask'
7
8
  require 'rake/testtask'
8
9
 
9
10
  desc 'Default: run tests in the proper environment'
10
- task :default => "env:test"
11
+ task :default => :test
11
12
 
12
13
  def common_test_task(t)
13
14
  t.libs << "lib"
14
- t.test_files = FileList['test/*_test.rb', 'test/t*.rb']
15
+ t.test_files = FileList['test/*_test.rb', 'test/t[0-9]*.rb']
15
16
  t.verbose = true
16
17
  end
17
- Rake::TestTask.new {|t| common_test_task t }
18
+ Rake::TestTask.new("bare:test") {|t| common_test_task t }
18
19
 
19
20
  begin
20
21
  require 'rcov/rcovtask'
21
- Rcov::RcovTask.new {|t| common_test_task t }
22
+ Rcov::RcovTask.new("bare:rcov") {|t| common_test_task t }
22
23
  rescue LoadError
23
24
  # no rcov, never mind
24
25
  end
25
26
 
26
27
  %w(test rcov).each do |tname|
27
- namespace :env do
28
- desc "Run #{tname} in the proper environment"
29
- task tname do |t|
30
- cd "test" do
31
- system "./test_env rake #{tname}"
32
- end
28
+ desc "Run bare:#{tname} in the proper environment"
29
+ task tname do |t|
30
+ cd "test" do
31
+ system "./test_env rake bare:#{tname}"
33
32
  end
34
33
  end
35
34
  end
@@ -40,6 +39,17 @@ Rake::GemPackageTask.new(GEMSPEC) do |pkg|
40
39
  # no other formats needed
41
40
  end
42
41
 
42
+ desc "Build a package from a clone of the local Git repo"
43
+ task :package_git do |t|
44
+ Dir.mktmpdir do |temp|
45
+ sh "git clone . #{temp}"
46
+ cd temp do
47
+ sh "rake package"
48
+ end
49
+ cp_r "#{temp}/pkg", "."
50
+ end
51
+ end
52
+
43
53
  Rake::RDocTask.new do |rd|
44
54
  rd.rdoc_dir = 'doc/rdoc'
45
55
  rd.rdoc_files.include("README", "lib/**/*.rb")
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.0
1
+ 0.7.0
data/lib/dbus/auth.rb CHANGED
@@ -8,6 +8,8 @@
8
8
 
9
9
  $debug = $DEBUG #it's all over the state machine
10
10
 
11
+ require 'rbconfig'
12
+
11
13
  module DBus
12
14
  # Exception raised when authentication fails somehow.
13
15
  class AuthenticationFailed < Exception
@@ -62,7 +64,7 @@ module DBus
62
64
  # name of cookie file, id of cookie in file, servers random challenge
63
65
  context, id, s_challenge = data.split(' ')
64
66
  # Random client challenge
65
- c_challenge = Array.new(s_challenge.length/2).map{|obj|obj=rand(255).to_s}.join
67
+ c_challenge = Array.new(s_challenge.bytesize/2).map{|obj|obj=rand(255).to_s}.join
66
68
  # Search cookie file for id
67
69
  path = File.join(ENV['HOME'], '.dbus-keyrings', context)
68
70
  puts "DEBUG: path: #{path.inspect}" if $debug
@@ -118,7 +120,11 @@ module DBus
118
120
 
119
121
  # Start the authentication process.
120
122
  def authenticate
121
- @socket.write(0.chr)
123
+ if (RbConfig::CONFIG["target_os"] =~ /bsd/)
124
+ @socket.sendmsg(0.chr, 0, nil, [:SOCKET, :SCM_CREDS, ""])
125
+ else
126
+ @socket.write(0.chr)
127
+ end
122
128
  next_authenticator
123
129
  @state = :Starting
124
130
  while @state != :Authenticated
@@ -142,12 +148,12 @@ module DBus
142
148
  # Try authentication using the next authenticator.
143
149
  def next_authenticator
144
150
  begin
145
- raise AuthException if @auth_list.size == 0
151
+ raise AuthenticationFailed if @auth_list.size == 0
146
152
  @authenticator = @auth_list.shift.new
147
153
  auth_msg = ["AUTH", @authenticator.name, @authenticator.authenticate]
148
154
  puts "DEBUG: auth_msg: #{auth_msg.inspect}" if $debug
149
155
  send(auth_msg)
150
- rescue AuthException
156
+ rescue AuthenticationFailed
151
157
  @socket.close
152
158
  raise
153
159
  end
@@ -161,7 +167,7 @@ module DBus
161
167
  while left > 0
162
168
  buf = @socket.read( left > 1 ? 1 : left )
163
169
  break if buf.nil?
164
- left -= buf.size
170
+ left -= buf.bytesize
165
171
  data += buf
166
172
  break if data.include? crlf #crlf means line finished, the TCP socket keeps on listening, so we break
167
173
  end
data/lib/dbus/bus.rb CHANGED
@@ -140,6 +140,7 @@ module DBus
140
140
  end
141
141
 
142
142
  # Return an XML string representation of the node.
143
+ # It is shallow, not recursing into subnodes
143
144
  def to_xml
144
145
  xml = '<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
145
146
  "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
@@ -383,33 +384,52 @@ module DBus
383
384
  '
384
385
  # This apostroph is for syntax highlighting editors confused by above xml: "
385
386
 
386
- def introspect_data(dest, path)
387
- m = DBus::Message.new(DBus::Message::METHOD_CALL)
388
- m.path = path
389
- m.interface = "org.freedesktop.DBus.Introspectable"
390
- m.destination = dest
391
- m.member = "Introspect"
392
- m.sender = unique_name
393
- if not block_given?
394
- # introspect in synchronous !
395
- send_sync(m) do |rmsg|
387
+ # Send a _message_.
388
+ # If _reply_handler_ is not given, wait for the reply
389
+ # and return the reply, or raise the error.
390
+ # If _reply_handler_ is given, it will be called when the reply
391
+ # eventually arrives, with the reply message as the 1st param
392
+ # and its params following
393
+ def send_sync_or_async(message, &reply_handler)
394
+ ret = nil
395
+ if reply_handler.nil?
396
+ send_sync(message) do |rmsg|
396
397
  if rmsg.is_a?(Error)
397
398
  raise rmsg
398
399
  else
399
- return rmsg.params[0] # return value of introspect_data
400
+ ret = rmsg.params
400
401
  end
401
402
  end
402
403
  else
403
- send(m.marshall)
404
- on_return(m) do |rmsg|
404
+ on_return(message) do |rmsg|
405
405
  if rmsg.is_a?(Error)
406
- yield rmsg
406
+ reply_handler.call(rmsg)
407
407
  else
408
- yield rmsg.params[0]
408
+ reply_handler.call(rmsg, * rmsg.params)
409
409
  end
410
410
  end
411
+ send(message.marshall)
412
+ end
413
+ ret
414
+ end
415
+
416
+ def introspect_data(dest, path, &reply_handler)
417
+ m = DBus::Message.new(DBus::Message::METHOD_CALL)
418
+ m.path = path
419
+ m.interface = "org.freedesktop.DBus.Introspectable"
420
+ m.destination = dest
421
+ m.member = "Introspect"
422
+ m.sender = unique_name
423
+ if reply_handler.nil?
424
+ send_sync_or_async(m).first
425
+ else
426
+ send_sync_or_async(m) do |*args|
427
+ # TODO test async introspection, is it used at all?
428
+ args.shift # forget the message, pass only the text
429
+ reply_handler.call(*args)
430
+ nil
431
+ end
411
432
  end
412
- nil
413
433
  end
414
434
 
415
435
  # Issues a call to the org.freedesktop.DBus.Introspectable.Introspect method
@@ -549,9 +569,7 @@ module DBus
549
569
  return if retm.nil? #check if somethings wrong
550
570
 
551
571
  process(retm)
552
- until [DBus::Message::ERROR,
553
- DBus::Message::METHOD_RETURN].include?(retm.message_type) and
554
- retm.reply_serial == m.serial
572
+ while @method_call_replies.has_key? m.serial
555
573
  retm = wait_for_message
556
574
  process(retm)
557
575
  end
@@ -695,10 +713,10 @@ module DBus
695
713
  # = D-Bus session bus class
696
714
  #
697
715
  # The session bus is a session specific bus (mostly for desktop use).
698
- # This is a singleton class.
699
- class SessionBus < Connection
700
- include Singleton
701
-
716
+ #
717
+ # Use SessionBus, the non-singleton ASessionBus is
718
+ # for the test suite.
719
+ class ASessionBus < Connection
702
720
  # Get the the default session bus.
703
721
  def initialize
704
722
  super(ENV["DBUS_SESSION_BUS_ADDRESS"] || address_from_file)
@@ -719,13 +737,20 @@ module DBus
719
737
  end
720
738
  end
721
739
 
740
+ # See ASessionBus
741
+ class SessionBus < ASessionBus
742
+ include Singleton
743
+ end
744
+
745
+
722
746
  # = D-Bus system bus class
723
747
  #
724
748
  # The system bus is a system-wide bus mostly used for global or
725
- # system usages. This is a singleton class.
726
- class SystemBus < Connection
727
- include Singleton
728
-
749
+ # system usages.
750
+ #
751
+ # Use SystemBus, the non-singleton ASystemBus is
752
+ # for the test suite.
753
+ class ASystemBus < Connection
729
754
  # Get the default system bus.
730
755
  def initialize
731
756
  super(SystemSocketName)
@@ -755,6 +780,11 @@ module DBus
755
780
  end
756
781
  end
757
782
 
783
+ # See ASystemBus
784
+ class SystemBus < ASystemBus
785
+ include Singleton
786
+ end
787
+
758
788
  # Shortcut for the SystemBus instance
759
789
  def DBus.system_bus
760
790
  SystemBus.instance
data/lib/dbus/export.rb CHANGED
@@ -15,19 +15,20 @@ module DBus
15
15
  # = Exportable D-Bus object class
16
16
  #
17
17
  # Objects that are going to be exported by a D-Bus service
18
- # should inherit from this class.
18
+ # should inherit from this class. At the client side, use ProxyObject.
19
19
  class Object
20
20
  # The path of the object.
21
21
  attr_reader :path
22
- # The interfaces that the object supports.
22
+ # The interfaces that the object supports. Hash: String => Interface
23
23
  class_attribute :intfs
24
24
  # The service that the object is exported by.
25
25
  attr_writer :service
26
26
 
27
- @@cur_intf = nil
27
+ @@cur_intf = nil # Interface
28
28
  @@intfs_mutex = Mutex.new
29
29
 
30
30
  # Create a new object with a given _path_.
31
+ # Use Service#export to export it.
31
32
  def initialize(path)
32
33
  @path = path
33
34
  @service = nil
@@ -39,7 +40,7 @@ module DBus
39
40
  self.intfs = (self.intfs || {}).merge({intf.name => intf})
40
41
  end
41
42
 
42
- # Dispatch a message _msg_.
43
+ # Dispatch a message _msg_ to call exported methods
43
44
  def dispatch(msg)
44
45
  case msg.message_type
45
46
  when Message::METHOD_CALL
@@ -73,8 +74,10 @@ module DBus
73
74
  # belong to.
74
75
  def self.dbus_interface(s)
75
76
  @@intfs_mutex.synchronize do
76
- @@cur_intf = Interface.new(s)
77
- self.intfs = (self.intfs || {}).merge({s => @@cur_intf})
77
+ unless @@cur_intf = (self.intfs && self.intfs[s])
78
+ @@cur_intf = Interface.new(s)
79
+ self.intfs = (self.intfs || {}).merge({s => @@cur_intf})
80
+ end
78
81
  yield
79
82
  @@cur_intf = nil
80
83
  end
@@ -8,6 +8,7 @@
8
8
  # License, version 2.1 as published by the Free Software Foundation.
9
9
  # See the file "COPYING" for the exact licensing terms.
10
10
 
11
+ # TODO check if it is slow, make replaceable
11
12
  require 'rexml/document'
12
13
 
13
14
  module DBus
@@ -27,15 +28,16 @@ module DBus
27
28
  # = D-Bus interface class
28
29
  #
29
30
  # This class is the interface descriptor. In most cases, the Introspect()
30
- # method call instanciates and configures this class for us.
31
+ # method call instantiates and configures this class for us.
31
32
  #
32
33
  # It also is the local definition of interface exported by the program.
34
+ # At the client side, see ProxyObjectInterface
33
35
  class Interface
34
- # The name of the interface.
36
+ # The name of the interface. String
35
37
  attr_reader :name
36
- # The methods that are part of the interface.
38
+ # The methods that are part of the interface. Hash: Symbol => DBus::Method
37
39
  attr_reader :methods
38
- # The signals that are part of the interface.
40
+ # The signals that are part of the interface. Hash: Symbol => Signal
39
41
  attr_reader :signals
40
42
 
41
43
  # Creates a new interface with a given _name_.
@@ -47,7 +49,7 @@ module DBus
47
49
 
48
50
  # Validates a service _name_.
49
51
  def validate_name(name)
50
- raise InvalidIntrospectionData if name.size > 255
52
+ raise InvalidIntrospectionData if name.bytesize > 255
51
53
  raise InvalidIntrospectionData if name =~ /^\./ or name =~ /\.$/
52
54
  raise InvalidIntrospectionData if name =~ /\.\./
53
55
  raise InvalidIntrospectionData if not name =~ /\./
@@ -100,14 +102,14 @@ module DBus
100
102
  # This is a generic class for entities that are part of the interface
101
103
  # such as methods and signals.
102
104
  class InterfaceElement
103
- # The name of the interface element.
105
+ # The name of the interface element. Symbol
104
106
  attr_reader :name
105
- # The parameters of the interface element
107
+ # The parameters of the interface element. Array: FormalParameter
106
108
  attr_reader :params
107
109
 
108
110
  # Validates element _name_.
109
111
  def validate_name(name)
110
- if (not name =~ MethodSignalRE) or (name.size > 255)
112
+ if (not name =~ MethodSignalRE) or (name.bytesize > 255)
111
113
  raise InvalidMethodName, name
112
114
  end
113
115
  end
@@ -135,7 +137,7 @@ module DBus
135
137
  #
136
138
  # This is a class representing methods that are part of an interface.
137
139
  class Method < InterfaceElement
138
- # The list of return values for the method.
140
+ # The list of return values for the method. Array: FormalParameter
139
141
  attr_reader :rets
140
142
 
141
143
  # Creates a new method interface element with the given _name_.
@@ -227,10 +229,9 @@ module DBus
227
229
  @xml = xml
228
230
  end
229
231
 
230
- # Recursively parses the subnodes, constructing the tree.
232
+ # return the names of direct subnodes
231
233
  def parse_subnodes
232
234
  subnodes = Array.new
233
- t = Time.now
234
235
  d = REXML::Document.new(@xml)
235
236
  d.elements.each("node/node") do |e|
236
237
  subnodes << e.attributes["name"]
@@ -238,9 +239,9 @@ module DBus
238
239
  subnodes
239
240
  end
240
241
 
241
- # Parses the XML, constructing the tree.
242
+ # return a pair: [list of Interfaces, list of direct subnode names]
242
243
  def parse
243
- ret = Array.new
244
+ interfaces = Array.new
244
245
  subnodes = Array.new
245
246
  t = Time.now
246
247
  d = REXML::Document.new(@xml)
@@ -259,13 +260,13 @@ module DBus
259
260
  parse_methsig(se, s)
260
261
  i << s
261
262
  end
262
- ret << i
263
+ interfaces << i
263
264
  end
264
265
  d = Time.now - t
265
266
  if d > 2
266
267
  puts "Some XML took more that two secs to parse. Optimize me!" if $DEBUG
267
268
  end
268
- [ret, subnodes]
269
+ [interfaces, subnodes]
269
270
  end
270
271
 
271
272
  ######################################################################
@@ -342,7 +343,7 @@ module DBus
342
343
  methdef = "def #{m.name}("
343
344
  methdef += (0..(m.params.size - 1)).to_a.collect { |n|
344
345
  "arg#{n}"
345
- }.join(", ")
346
+ }.push("&reply_handler").join(", ")
346
347
  methdef += %{)
347
348
  msg = Message.new(Message::METHOD_CALL)
348
349
  msg.path = @object.path
@@ -365,26 +366,7 @@ module DBus
365
366
  idx += 1
366
367
  end
367
368
  methdef += "
368
- ret = nil
369
- if block_given?
370
- @object.bus.on_return(msg) do |rmsg|
371
- if rmsg.is_a?(Error)
372
- yield(rmsg)
373
- else
374
- yield(rmsg, *rmsg.params)
375
- end
376
- end
377
- @object.bus.send(msg.marshall)
378
- else
379
- @object.bus.send_sync(msg) do |rmsg|
380
- if rmsg.is_a?(Error)
381
- raise rmsg
382
- else
383
- ret = rmsg.params
384
- end
385
- end
386
- end
387
- ret
369
+ @object.bus.send_sync_or_async(msg, &reply_handler)
388
370
  end
389
371
  "
390
372
  singleton_class.class_eval(methdef)
@@ -448,7 +430,7 @@ module DBus
448
430
  # over the bus so that the method is executed remotely on the correctponding
449
431
  # object.
450
432
  class ProxyObject
451
- # The subnodes of the object in the tree.
433
+ # The names of direct subnodes of the object in the tree.
452
434
  attr_accessor :subnodes
453
435
  # Flag determining whether the object has been introspected.
454
436
  attr_accessor :introspected
@@ -458,7 +440,7 @@ module DBus
458
440
  attr_reader :path
459
441
  # The bus the object is reachable via.
460
442
  attr_reader :bus
461
- # The default interface of the object.
443
+ # The default interface of the object, as String.
462
444
  attr_accessor :default_iface
463
445
 
464
446
  # Creates a new proxy object living on the given _bus_ at destination _dest_
@@ -495,11 +477,12 @@ module DBus
495
477
 
496
478
  # Returns whether the object has an interface with the given _name_.
497
479
  def has_iface?(name)
498
- raise "Cannot call has_iface? is not introspected" if not @introspected
480
+ raise "Cannot call has_iface? if not introspected" if not @introspected
499
481
  @interfaces.member?(name)
500
482
  end
501
483
 
502
484
  # Registers a handler, the code block, for a signal with the given _name_.
485
+ # It uses _default_iface_ which must have been set.
503
486
  def on_signal(name, &block)
504
487
  if @default_iface and has_iface?(@default_iface)
505
488
  @interfaces[@default_iface].on_signal(@bus, name, &block)
@@ -521,10 +504,9 @@ module DBus
521
504
  rescue NameError => e
522
505
  # interesting, foo.method("unknown")
523
506
  # raises NameError, not NoMethodError
524
- match = /undefined method `([^']*)' for class `([^']*)'/.match e.to_s
525
- raise unless match and match[2] == "DBus::ProxyObjectInterface"
507
+ raise unless e.to_s =~ /undefined method `#{name}'/
526
508
  # BTW e.exception("...") would preserve the class.
527
- raise NoMethodError,"undefined method `#{match[1]}' for DBus interface `#{@default_iface}' on object `#{@path}'"
509
+ raise NoMethodError,"undefined method `#{name}' for DBus interface `#{@default_iface}' on object `#{@path}'"
528
510
  end
529
511
  else
530
512
  # TODO distinguish: