ruby-dbus 0.23.0.beta2 → 0.24.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.
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: []