ruby-dbus 0.23.0.beta2 → 0.24.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c671b06e04452129a899cd4b7877cf43f9c05b8e7bc6597e75bc460e250c97e7
4
- data.tar.gz: 18f660c6e5804fd133cf8351d801f8e0d4e332f1473cc390bedf77341d71845d
3
+ metadata.gz: 46409560afa18cf728a8fdc0d5a488e69f04d3bf3c14cece10f8c14af8bc8a41
4
+ data.tar.gz: f0432d7e9716f8e87ce5daaf7dc581b0cf5b89c66f5e5cc3da50bb38cae85fa3
5
5
  SHA512:
6
- metadata.gz: 5cff2ab01f4db3d069a73b28f46cf861dcc81302ed239a7fc4332f46a7583b2394d679d5d73ab13f080b674e46f8961c89fa3fccc58ce9043d0bd33c4c2d02c4
7
- data.tar.gz: 495c060e5161c2c5267b723598d3185b14d4c6517e41504f5b49971d3c355acec1ee3ab6285b7796bb7aa8cb61dfb5bd24535a2613a9f22cfb80f65ec8a1cf49
6
+ metadata.gz: e98790e7cf81e2cb7bcaf3e2e39e530b99788483fd9fb583f209ce3f2a120ef381b59d3f0683c9affcef7d1475bf52f9d91e0f66004c1e78cb4fd363639698bf
7
+ data.tar.gz: 2660d2bf3b6a511898c13048a5548c31d04674e6ad62e13aca567b1379fed0ce48b063435c5c74a680fba649427cc3a7c802c0883a6ff433a20040129604e07c
data/NEWS.md CHANGED
@@ -2,6 +2,24 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## Ruby D-Bus 0.24.0 - 2025-01-02
6
+
7
+ Bug fixes:
8
+ * Adapted for Ruby 3.4, which uses a single quote instead of a backtick
9
+ in exceptions ([#145][], by Mamoru TASAKA).
10
+
11
+ [#145]: https://github.com/mvidner/ruby-dbus/pull/145
12
+
13
+ ## Ruby D-Bus 0.23.1 - 2023-10-03
14
+
15
+ API:
16
+ * Add DBus::Object.dbus_reader_attr_accessor to declare a common use case
17
+ with a single call ([#140][]).
18
+ * BusConnection#request_name defaults to the simple use case: single owner
19
+ without queuing, failing fast; documented the complex use cases.
20
+
21
+ [#140]: https://github.com/mvidner/ruby-dbus/pull/140
22
+
5
23
  ## Ruby D-Bus 0.23.0.beta2 - 2023-06-23
6
24
 
7
25
  License:
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.23.0.beta2
1
+ 0.24.0
@@ -8,4 +8,12 @@ d = if ARGV.member?("--system")
8
8
  else
9
9
  DBus::SessionBus.instance
10
10
  end
11
- d.proxy.ListNames[0].each { |n| puts "\t#{n}" }
11
+ d.proxy.ListNames[0].each do |n|
12
+ puts "\t#{n}"
13
+ qns = d.proxy.ListQueuedOwners(n)[0]
14
+ next if qns.size == 1 && qns.first == n
15
+
16
+ qns.each do |qn|
17
+ puts "\t\t#{qn}"
18
+ end
19
+ end
data/lib/dbus/bus.rb CHANGED
@@ -55,20 +55,107 @@ module DBus
55
55
  @proxy
56
56
  end
57
57
 
58
+ # Request a well-known name so that clients can find us.
59
+ # @note Parameters other than *name* are advanced, you probably don't need them.
60
+ #
61
+ # With no boolean flags, running a second instance of a program that calls `request_name`
62
+ # will result in the second one failing, which this library translates to an exception.
63
+ # If you want the second instance to take over, you need both
64
+ # `allow_replacement: true` and `replace_existing: true.`
65
+ #
58
66
  # @param name [BusName] the requested name
59
- # @param flags [Integer] TODO: explain and add a better non-numeric API for this
60
- # @raise NameRequestError if we could not get the name
61
- # @example Usage
67
+ # @param replace_existing [Boolean]
68
+ # Replace an existing owner of the name, if that owner set *allow_replacement*.
69
+ # @param allow_replacement [Boolean]
70
+ # Other connections that specify *replace_existing* will be able to take
71
+ # the name from us. We will get {#on_name_lost NameLost}. If we specified *queue*
72
+ # we may get the name again, with {#on_name_acquired NameAcquired}.
73
+ # @param queue [Boolean]
74
+ # Affects the behavior when the bus denies the name (sooner or later).
75
+ # - If `false` (default), it is recommended to let the `NameRequestError` fall through and end your program.
76
+ # - If `true`, you should `rescue` the `NameRequestError` and set up
77
+ # {#on_name_acquired NameAcquired} and {#on_name_lost NameLost} handlers.
78
+ # Meanwhile, the bus will put us in a queue waiting for *name* (this is the "sooner" case).
79
+ # Also, if we had `allow_replacement: true`, another connection can cause us
80
+ # to lose the name. We will be moved back to the queue, waiting for when the other owners give up
81
+ # (the "later" case).
82
+ # @param flags [Integer,nil]
83
+ # If specified, overrides the boolean parameters.
84
+ # Use a bitwise sum `|` of:
85
+ # - NAME_FLAG_ALLOW_REPLACEMENT
86
+ # - NAME_FLAG_REPLACE_EXISTING
87
+ # - NAME_FLAG_DO_NOT_QUEUE
88
+ # Note that `0` implies `queue: true`.
89
+ #
90
+ # @return [REQUEST_NAME_REPLY_PRIMARY_OWNER,REQUEST_NAME_REPLY_ALREADY_OWNER] on success
91
+ # @raise [NameRequestError] with #error_code REQUEST_NAME_REPLY_EXISTS or REQUEST_NAME_REPLY_IN_QUEUE, on failure
92
+ # @raise DBus::Error another way to fail is being prohibited to own the name
93
+ # which is the default on the system bus
94
+ #
95
+ # @see https://dbus.freedesktop.org/doc/dbus-specification.html#bus-messages-request-name
96
+ #
97
+ # @example Simple usage
62
98
  # bus = DBus.session_bus
63
99
  # bus.object_server.export(DBus::Object.new("/org/example/Test"))
64
100
  # bus.request_name("org.example.Test")
65
- # @see https://dbus.freedesktop.org/doc/dbus-specification.html#bus-messages-request-name
66
- def request_name(name, flags: 0)
101
+ # # main loop
102
+ #
103
+ # @example Second instance taking over
104
+ # bus = DBus.session_bus
105
+ # bus.object_server.export(DBus::Object.new("/org/example/Test"))
106
+ # bus.on_name_lost { exit }
107
+ # bus.request_name("org.example.Test", allow_replacement: true, replace_existing: true)
108
+ # # main loop
109
+ #
110
+ # @example Second instance waiting for its turn
111
+ # bus = DBus.session_bus
112
+ # bus.object_server.export(DBus::Object.new("/org/example/Test"))
113
+ # bus.on_name_acquired { @owner = true }
114
+ # begin
115
+ # bus.request_name("org.example.Test", queue: true)
116
+ # rescue DBus::Connection::NameRequestError => e
117
+ # @owner = false
118
+ # end
119
+ # # main loop
120
+ def request_name(name,
121
+ allow_replacement: false,
122
+ replace_existing: false,
123
+ queue: false,
124
+ flags: nil)
125
+ if flags.nil?
126
+ flags = (allow_replacement ? NAME_FLAG_ALLOW_REPLACEMENT : 0) |
127
+ (replace_existing ? NAME_FLAG_REPLACE_EXISTING : 0) |
128
+ (queue ? 0 : NAME_FLAG_DO_NOT_QUEUE)
129
+ end
67
130
  name = BusName.new(name)
68
131
  r = proxy.RequestName(name, flags).first
69
132
  handle_return_of_request_name(r, name)
70
133
  end
71
134
 
135
+ # The caller has released his claim on the given name.
136
+ # Either the caller was the primary owner of the name, and the name is now unused
137
+ # or taken by somebody waiting in the queue for the name,
138
+ # or the caller was waiting in the queue for the name and has now been removed from the queue.
139
+ RELEASE_NAME_REPLY_RELEASED = 1
140
+ # The given name does not exist on this bus.
141
+ RELEASE_NAME_REPLY_NON_EXISTENT = 2
142
+ # The caller was not the primary owner of this name, and was also not waiting in the queue to own this name.
143
+ RELEASE_NAME_REPLY_NOT_OWNER = 3
144
+
145
+ # @param name [BusName] the name to release
146
+ def release_name(name)
147
+ name = BusName.new(name)
148
+ proxy.ReleaseName(name).first
149
+ end
150
+
151
+ def on_name_acquired(&handler)
152
+ proxy.on_signal("NameAcquired", &handler)
153
+ end
154
+
155
+ def on_name_lost(&handler)
156
+ proxy.on_signal("NameLost", &handler)
157
+ end
158
+
72
159
  # Asks bus to send us messages matching mr, and execute slot when
73
160
  # received
74
161
  # @param match_rule [MatchRule,#to_s]
@@ -153,17 +153,30 @@ module DBus
153
153
 
154
154
  # Exception raised when a service name is requested that is not available.
155
155
  class NameRequestError < Exception
156
+ # @return [Integer] one of
157
+ # REQUEST_NAME_REPLY_IN_QUEUE
158
+ # REQUEST_NAME_REPLY_EXISTS
159
+ attr_reader :error_code
160
+
161
+ def initialize(error_code)
162
+ @error_code = error_code
163
+ super()
164
+ end
156
165
  end
157
166
 
167
+ # In case RequestName did not succeed, raise an exception but first ask the bus who owns the name instead of us
168
+ # @param ret [Integer] what RequestName returned
169
+ # @param name Name that was requested
170
+ # @return [REQUEST_NAME_REPLY_PRIMARY_OWNER,REQUEST_NAME_REPLY_ALREADY_OWNER] on success
171
+ # @raise [NameRequestError] with #error_code REQUEST_NAME_REPLY_EXISTS or REQUEST_NAME_REPLY_IN_QUEUE, on failure
172
+ # @api private
158
173
  def handle_return_of_request_name(ret, name)
159
- details = if ret == REQUEST_NAME_REPLY_IN_QUEUE
160
- other = proxy.GetNameOwner(name).first
161
- other_creds = proxy.GetConnectionCredentials(other).first
162
- "already owned by #{other}, #{other_creds.inspect}"
163
- else
164
- "error code #{ret}"
165
- end
166
- raise NameRequestError, "Could not request #{name}, #{details}" unless ret == REQUEST_NAME_REPLY_PRIMARY_OWNER
174
+ if [REQUEST_NAME_REPLY_EXISTS, REQUEST_NAME_REPLY_IN_QUEUE].include?(ret)
175
+ other = proxy.GetNameOwner(name).first
176
+ other_creds = proxy.GetConnectionCredentials(other).first
177
+ message = "Could not request #{name}, already owned by #{other}, #{other_creds.inspect}"
178
+ raise NameRequestError.new(ret), message
179
+ end
167
180
 
168
181
  ret
169
182
  end
data/lib/dbus/object.rb CHANGED
@@ -156,18 +156,32 @@ module DBus
156
156
  dbus_accessor(ruby_name, type, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal)
157
157
  end
158
158
 
159
+ # A read-only property accessing a read-write instance variable.
160
+ # A combination of `attr_accessor` and {.dbus_reader}.
161
+ #
162
+ # @param (see .dbus_attr_accessor)
163
+ # @return (see .dbus_attr_accessor)
164
+ def self.dbus_reader_attr_accessor(ruby_name, type, dbus_name: nil, emits_changed_signal: nil)
165
+ attr_accessor(ruby_name)
166
+
167
+ dbus_reader(ruby_name, type, dbus_name: dbus_name, emits_changed_signal: emits_changed_signal)
168
+ end
169
+
159
170
  # A read-only property accessing an instance variable.
160
171
  # A combination of `attr_reader` and {.dbus_reader}.
161
172
  #
173
+ # You may be instead looking for a variant which is read-write from the Ruby side:
174
+ # {.dbus_reader_attr_accessor}.
175
+ #
162
176
  # Whenever the property value gets changed from "inside" the object,
163
177
  # you should emit the `PropertiesChanged` signal by calling
164
178
  # {#dbus_properties_changed}.
165
179
  #
166
- # dbus_properties_changed(interface_name, {dbus_name.to_s => value}, [])
180
+ # dbus_properties_changed(interface_name, {dbus_name.to_s => value}, [])
167
181
  #
168
182
  # or, omitting the value in the signal,
169
183
  #
170
- # dbus_properties_changed(interface_name, {}, [dbus_name.to_s])
184
+ # dbus_properties_changed(interface_name, {}, [dbus_name.to_s])
171
185
  #
172
186
  # @param (see .dbus_attr_accessor)
173
187
  # @return (see .dbus_attr_accessor)
@@ -213,18 +227,22 @@ module DBus
213
227
  # implement it with a read-write attr_accessor. In that case this method
214
228
  # uses {.dbus_watcher} to set up the PropertiesChanged signal.
215
229
  #
216
- # attr_accessor :foo_bar
217
- # dbus_reader :foo_bar, "s"
230
+ # attr_accessor :foo_bar
231
+ # dbus_reader :foo_bar, "s"
232
+ #
233
+ # The above two declarations have a shorthand:
234
+ #
235
+ # dbus_reader_attr_accessor :foo_bar, "s"
218
236
  #
219
237
  # If the property value should change by other means than its attr_writer,
220
238
  # you should emit the `PropertiesChanged` signal by calling
221
239
  # {#dbus_properties_changed}.
222
240
  #
223
- # dbus_properties_changed(interface_name, {dbus_name.to_s => value}, [])
241
+ # dbus_properties_changed(interface_name, {dbus_name.to_s => value}, [])
224
242
  #
225
243
  # or, omitting the value in the signal,
226
244
  #
227
- # dbus_properties_changed(interface_name, {}, [dbus_name.to_s])
245
+ # dbus_properties_changed(interface_name, {}, [dbus_name.to_s])
228
246
  #
229
247
  # @param (see .dbus_attr_accessor)
230
248
  # @return (see .dbus_attr_accessor)
@@ -33,6 +33,8 @@ module DBus
33
33
  # @return [ApiOptions]
34
34
  attr_reader :api
35
35
 
36
+ OPEN_QUOTE = RUBY_VERSION >= "3.4" ? "'" : "`"
37
+
36
38
  # Creates a new proxy object living on the given _bus_ at destination _dest_
37
39
  # on the given _path_.
38
40
  def initialize(bus, dest, path, api: ApiOptions::CURRENT)
@@ -58,7 +60,7 @@ module DBus
58
60
  def [](intfname)
59
61
  introspect unless introspected
60
62
  ifc = @interfaces[intfname]
61
- raise DBus::Error, "no such interface `#{intfname}' on object `#{@path}'" unless ifc
63
+ raise DBus::Error, "no such interface #{OPEN_QUOTE}#{intfname}' on object #{OPEN_QUOTE}#{@path}'" unless ifc
62
64
 
63
65
  ifc
64
66
  end
@@ -127,7 +129,8 @@ module DBus
127
129
  # @return [void]
128
130
  def on_signal(name, &block)
129
131
  unless @default_iface && has_iface?(@default_iface)
130
- raise NoMethodError, "undefined signal `#{name}' for DBus interface `#{@default_iface}' on object `#{@path}'"
132
+ raise NoMethodError, "undefined signal #{OPEN_QUOTE}#{name}' for DBus interface "\
133
+ "#{OPEN_QUOTE}#{@default_iface}' on object #{OPEN_QUOTE}#{@path}'"
131
134
  end
132
135
 
133
136
  @interfaces[@default_iface].on_signal(name, &block)
@@ -151,7 +154,8 @@ module DBus
151
154
  # - di not specified
152
155
  # TODO
153
156
  # - di is specified but not found in introspection data
154
- raise NoMethodError, "undefined method `#{name}' for DBus interface `#{@default_iface}' on object `#{@path}'"
157
+ raise NoMethodError, "undefined method #{OPEN_QUOTE}#{name}' for DBus interface "\
158
+ "#{OPEN_QUOTE}#{@default_iface}' on object #{OPEN_QUOTE}#{@path}'"
155
159
  end
156
160
 
157
161
  begin
@@ -159,10 +163,11 @@ module DBus
159
163
  rescue NameError => e
160
164
  # interesting, foo.method("unknown")
161
165
  # raises NameError, not NoMethodError
162
- raise unless e.to_s =~ /undefined method `#{name}'/
166
+ raise unless e.to_s =~ /undefined method #{OPEN_QUOTE}#{name}'/
163
167
 
164
168
  # BTW e.exception("...") would preserve the class.
165
- raise NoMethodError, "undefined method `#{name}' for DBus interface `#{@default_iface}' on object `#{@path}'"
169
+ raise NoMethodError, "undefined method #{OPEN_QUOTE}#{name}' for DBus interface "\
170
+ "#{OPEN_QUOTE}#{@default_iface}' on object #{OPEN_QUOTE}#{@path}'"
166
171
  end
167
172
  end
168
173
  # rubocop:enable Lint/MissingSuper
data/ruby-dbus.gemspec CHANGED
@@ -28,6 +28,8 @@ GEMSPEC = Gem::Specification.new do |s|
28
28
  s.add_runtime_dependency "rexml"
29
29
  # s.add_runtime_dependency "nokogiri"
30
30
 
31
+ # workaround: rubocop-1.0 needs base64 which is no longer in stdlib in newer rubies
32
+ s.add_development_dependency "base64"
31
33
  s.add_development_dependency "packaging_rake_tasks"
32
34
  s.add_development_dependency "rake"
33
35
  s.add_development_dependency "rspec", "~> 3"
@@ -21,7 +21,6 @@ describe DBus::BusConnection do
21
21
  end
22
22
 
23
23
  context "when the name is taken already", tag_service: true do
24
- # formerly it returned Service, now ObjectServer takes its role
25
24
  it "raises NameRequestError... too late" do
26
25
  name = "org.ruby.service"
27
26
  expect do
@@ -50,21 +49,35 @@ describe DBus::BusConnection do
50
49
 
51
50
  describe "#request_name", tag_bus: true do
52
51
  context "when the name request succeeds" do
53
- it "returns something which can export objects" do
52
+ it "returns a success code" do
54
53
  name = "org.rubygems.ruby_dbus.RequestNameTest"
55
- expect { bus.request_name(name) }.to_not raise_error
56
- bus.proxy.ReleaseName(name)
54
+ expect(bus.request_name(name)).to eq DBus::Connection::REQUEST_NAME_REPLY_PRIMARY_OWNER
55
+ # second time, considered also a success
56
+ expect(bus.request_name(name)).to eq DBus::Connection::REQUEST_NAME_REPLY_ALREADY_OWNER
57
+ bus.release_name(name)
57
58
  end
58
59
  end
59
60
 
60
61
  context "when the name is taken already", tag_service: true do
61
- # formerly it returned Service, now ObjectServer takes its role
62
62
  it "raises NameRequestError" do
63
63
  name = "org.ruby.service"
64
64
  expect do
65
- # flags: avoid getting the name sometime later, unexpectedly
66
- bus.request_name(name, flags: DBus::Connection::NAME_FLAG_DO_NOT_QUEUE)
65
+ bus.request_name(name)
66
+ end.to raise_error(DBus::Connection::NameRequestError)
67
+ end
68
+ end
69
+
70
+ context "when the name is taken already but we request queuing", tag_service: true do
71
+ it "raises NameRequestError but we are queued" do
72
+ name = "org.ruby.service"
73
+ owning = nil
74
+ # TODO: we do not expect the handlers to run
75
+ bus.on_name_acquired { owning = true }
76
+ bus.on_name_lost { owning = false }
77
+ expect do
78
+ bus.request_name(name, queue: true)
67
79
  end.to raise_error(DBus::Connection::NameRequestError)
80
+ expect(bus.release_name(name)).to eq DBus::BusConnection::RELEASE_NAME_REPLY_RELEASED
68
81
  end
69
82
  end
70
83
 
@@ -23,7 +23,7 @@ class TestChild < DBus::Object
23
23
  end
24
24
 
25
25
  dbus_interface "org.ruby.TestChild" do
26
- dbus_attr_reader :name, "s"
26
+ dbus_reader_attr_accessor :name, "s"
27
27
  end
28
28
  end
29
29
 
data/spec/signal_spec.rb CHANGED
@@ -107,9 +107,10 @@ describe "SignalHandlerTest" do
107
107
  describe DBus::ProxyObject do
108
108
  describe "#on_signal" do
109
109
  it "raises a descriptive error when the default_iface is wrong" do
110
+ open_quote = RUBY_VERSION >= "3.4" ? "'" : "`"
110
111
  @obj.default_iface = "org.ruby.NoSuchInterface"
111
112
  expect { @obj.on_signal("Foo") {} }
112
- .to raise_error(NoMethodError, /undefined signal.*interface `org.ruby.NoSuchInterface'/)
113
+ .to raise_error(NoMethodError, /undefined signal.*interface #{open_quote}org.ruby.NoSuchInterface'/)
113
114
  end
114
115
  end
115
116
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-dbus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.23.0.beta2
4
+ version: 0.24.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ruby DBus Team
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2023-06-23 00:00:00.000000000 Z
10
+ date: 2025-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rexml
@@ -24,6 +23,20 @@ dependencies:
24
23
  - - ">="
25
24
  - !ruby/object:Gem::Version
26
25
  version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: base64
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
27
40
  - !ruby/object:Gem::Dependency
28
41
  name: packaging_rake_tasks
29
42
  requirement: !ruby/object:Gem::Requirement
@@ -224,7 +237,6 @@ homepage: https://github.com/mvidner/ruby-dbus
224
237
  licenses:
225
238
  - LGPL-2.1-or-later
226
239
  metadata: {}
227
- post_install_message:
228
240
  rdoc_options: []
229
241
  require_paths:
230
242
  - lib
@@ -235,12 +247,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
235
247
  version: 2.4.0
236
248
  required_rubygems_version: !ruby/object:Gem::Requirement
237
249
  requirements:
238
- - - ">"
250
+ - - ">="
239
251
  - !ruby/object:Gem::Version
240
- version: 1.3.1
252
+ version: '0'
241
253
  requirements: []
242
- rubygems_version: 3.3.26
243
- signing_key:
254
+ rubygems_version: 3.6.2
244
255
  specification_version: 4
245
256
  summary: Ruby module for interaction with D-Bus
246
257
  test_files: []