ruby-dbus 0.14.0 → 0.17.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 (44) hide show
  1. checksums.yaml +5 -5
  2. data/NEWS.md +44 -0
  3. data/README.md +3 -5
  4. data/Rakefile +26 -8
  5. data/VERSION +1 -1
  6. data/doc/Reference.md +84 -1
  7. data/examples/doc/_extract_examples +5 -0
  8. data/examples/gdbus/gdbus +21 -20
  9. data/examples/service/call_service.rb +1 -1
  10. data/lib/dbus/auth.rb +8 -8
  11. data/lib/dbus/bus.rb +23 -11
  12. data/lib/dbus/bus_name.rb +27 -0
  13. data/lib/dbus/core_ext/class/attribute.rb +23 -41
  14. data/lib/dbus/core_ext/module/redefine_method.rb +51 -0
  15. data/lib/dbus/introspect.rb +71 -26
  16. data/lib/dbus/marshall.rb +2 -1
  17. data/lib/dbus/message_queue.rb +17 -14
  18. data/lib/dbus/object.rb +380 -0
  19. data/lib/dbus/object_path.rb +24 -0
  20. data/lib/dbus/proxy_object.rb +11 -2
  21. data/lib/dbus/proxy_object_interface.rb +1 -0
  22. data/lib/dbus/type.rb +18 -0
  23. data/lib/dbus/xml.rb +4 -8
  24. data/lib/dbus.rb +3 -2
  25. data/ruby-dbus.gemspec +11 -9
  26. data/spec/binding_spec.rb +6 -2
  27. data/spec/bus_name_spec.rb +25 -0
  28. data/spec/client_robustness_spec.rb +25 -0
  29. data/spec/introspect_xml_parser_spec.rb +13 -13
  30. data/spec/main_loop_spec.rb +1 -1
  31. data/spec/object_path_spec.rb +23 -0
  32. data/spec/property_spec.rb +53 -3
  33. data/spec/proxy_object_spec.rb +9 -0
  34. data/spec/service_newapi.rb +20 -66
  35. data/spec/session_bus_spec.rb +6 -6
  36. data/spec/signal_spec.rb +33 -18
  37. data/spec/spec_helper.rb +23 -11
  38. data/spec/tools/dbus-limited-session.conf +4 -0
  39. data/spec/type_spec.rb +2 -2
  40. metadata +32 -15
  41. data/lib/dbus/core_ext/array/extract_options.rb +0 -31
  42. data/lib/dbus/core_ext/kernel/singleton_class.rb +0 -8
  43. data/lib/dbus/core_ext/module/remove_method.rb +0 -14
  44. data/lib/dbus/export.rb +0 -130
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 95dfa24c76e4ed5aeb5997bd160a4efd69a8c146
4
- data.tar.gz: 244e215c35cff67f53c7f7ac51a699c2fec22b2d
2
+ SHA256:
3
+ metadata.gz: ee629ce8a78d7cd23f718103cfd622d2a5fd67c51dab6b2a88e453c512c608f3
4
+ data.tar.gz: a8995a9526e3b957d32c503de7ad5ef781e5c464925fec8e2107eda4529aa447
5
5
  SHA512:
6
- metadata.gz: f020e993e5a6777c7ea540cf1197200aa6f1c3c7003f331b42b73cea3d5937925243bf46d2668488e8854bb7ac34d88a20934a18e90a2bb46d9fca7bcb8de764
7
- data.tar.gz: 160ebb98a13e02b982c7b634a02aa271b5899d54be289c9f5d71c0a2f8b9fcfc1190e4e19854a1a236a237f875cd108bad280a6bdbe571e68cb38afaad10b6b4
6
+ metadata.gz: 531e01c12aed4aeedaa5ce55fec2fee0c3da714211adc768c8990d2ce79509e6b925bc06d4308e9628cff109b9880dbea72878e7aeaf23c8e5fb19ddf8a249e2
7
+ data.tar.gz: 6449aa2798ed78d29c430423a92d7c8a921fd4ea244eea11fb8a84ca2eb880a4e6860325dc6a05254dc432b971d4929fc4d3a5057e423939c0574a687e7680f7
data/NEWS.md CHANGED
@@ -2,6 +2,50 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## Ruby D-Bus 0.17.0 - 2022-02-11
6
+
7
+ API:
8
+ * Export properties with `dbus_attr_accessor`, `dbus_reader` etc. ([#86][]).
9
+
10
+ Bug fixes:
11
+ * Depend on rexml which is separate since Ruby 3.0 ([#87][],
12
+ by Toshiaki Asai).
13
+ Nokogiri is faster but bigger so it remains optional.
14
+ * Fix connection in case ~/.dbus-keyrings has multiple cookies, showing
15
+ as "Oops: undefined method `zero?' for nil:NilClass".
16
+ * Add the missing name to the root introspection node.
17
+
18
+ [#86]: https://github.com/mvidner/ruby-dbus/pull/86
19
+ [#87]: https://github.com/mvidner/ruby-dbus/pull/87
20
+
21
+ ## Ruby D-Bus 0.16.0 - 2019-10-15
22
+
23
+ API:
24
+ * An invalid service name or an invalid object path will raise
25
+ instead of being sent to the bus. The bus would then drop the connection,
26
+ producing EOFError here ([#80][]).
27
+
28
+ [#80]: https://github.com/mvidner/ruby-dbus/issues/80
29
+
30
+ ## Ruby D-Bus 0.15.0 - 2018-04-30
31
+
32
+ API:
33
+ * Accessing an unknown interface will raise instead of returning nil ([#74][]).
34
+
35
+ Bug fixes:
36
+ * Fixed a conflict with activesupport 5.2 ([#71])
37
+
38
+ [#71]: https://github.com/mvidner/ruby-dbus/issues/71
39
+ [#74]: https://github.com/mvidner/ruby-dbus/pull/74
40
+
41
+ ## Ruby D-Bus 0.14.1 - 2018-01-05
42
+
43
+ Bug fixes:
44
+ * Allow registering signal handlers while a signal is being handled
45
+ ([#70][], Jan Biniok).
46
+
47
+ [#70]: https://github.com/mvidner/ruby-dbus/pull/70
48
+
5
49
  ## Ruby D-Bus 0.14.0 - 2017-10-13
6
50
 
7
51
  Bug fixes:
data/README.md CHANGED
@@ -11,14 +11,13 @@ Ruby D-Bus is a pure Ruby library for writing clients and services for D-Bus.
11
11
  [![Coverage Status][CS img]][Coverage Status]
12
12
 
13
13
  [Gem Version]: https://rubygems.org/gems/ruby-dbus
14
- [Build Status]: https://travis-ci.org/mvidner/ruby-dbus
15
- [travis pull requests]: https://travis-ci.org/mvidner/ruby-dbus/pull_requests
14
+ [Build Status]: https://github.com/mvidner/ruby-dbus/actions?query=branch%3Amaster
16
15
  [Dependency Status]: https://gemnasium.com/mvidner/ruby-dbus
17
16
  [Code Climate]: https://codeclimate.com/github/mvidner/ruby-dbus
18
17
  [Coverage Status]: https://coveralls.io/r/mvidner/ruby-dbus
19
18
 
20
19
  [GV img]: https://badge.fury.io/rb/ruby-dbus.png
21
- [BS img]: https://travis-ci.org/mvidner/ruby-dbus.png
20
+ [BS img]: https://github.com/mvidner/ruby-dbus/workflows/CI/badge.svg?branch=master
22
21
  [DS img]: https://gemnasium.com/mvidner/ruby-dbus.png
23
22
  [CC img]: https://codeclimate.com/github/mvidner/ruby-dbus.png
24
23
  [CS img]: https://coveralls.io/repos/mvidner/ruby-dbus/badge.png?branch=master
@@ -32,7 +31,6 @@ via [UPower](http://upower.freedesktop.org/docs/UPower.html#UPower:OnBattery)
32
31
  sysbus = DBus.system_bus
33
32
  upower_service = sysbus["org.freedesktop.UPower"]
34
33
  upower_object = upower_service["/org/freedesktop/UPower"]
35
- upower_object.introspect
36
34
  upower_interface = upower_object["org.freedesktop.UPower"]
37
35
  on_battery = upower_interface["OnBattery"]
38
36
  if on_battery
@@ -43,7 +41,7 @@ via [UPower](http://upower.freedesktop.org/docs/UPower.html#UPower:OnBattery)
43
41
 
44
42
  ## Requirements
45
43
 
46
- - Ruby 2.0 or newer.
44
+ - Ruby 2.1 or newer.
47
45
 
48
46
 
49
47
  ## Installation
data/Rakefile CHANGED
@@ -9,6 +9,12 @@ begin
9
9
  rescue LoadError
10
10
  nil
11
11
  end
12
+ begin
13
+ require "yard"
14
+ rescue LoadError
15
+ nil
16
+ end
17
+
12
18
  require "packaging"
13
19
 
14
20
  Packaging.configuration do |conf|
@@ -29,7 +35,7 @@ task test: :spec
29
35
 
30
36
  RSpec::Core::RakeTask.new("bare:spec")
31
37
 
32
- %w(spec).each do |tname|
38
+ ["spec"].each do |tname|
33
39
  desc "Run bare:#{tname} in the proper environment"
34
40
  task tname do |_t|
35
41
  cd "spec/tools" do
@@ -38,12 +44,6 @@ RSpec::Core::RakeTask.new("bare:spec")
38
44
  end
39
45
  end
40
46
 
41
- if ENV["TRAVIS"]
42
- require "coveralls/rake/task"
43
- Coveralls::RakeTask.new
44
- task default: "coveralls:push"
45
- end
46
-
47
47
  # remove tarball implementation and create gem for this gemfile
48
48
  Rake::Task[:tarball].clear
49
49
 
@@ -68,4 +68,22 @@ namespace :doc do
68
68
  end
69
69
  end
70
70
 
71
- RuboCop::RakeTask.new if Object.const_defined? :RuboCop
71
+ if Object.const_defined? :RuboCop
72
+ if RUBY_VERSION.start_with?("2.")
73
+ RuboCop::RakeTask.new
74
+ else
75
+ desc "Run RuboCop (dummy)"
76
+ task :rubocop do
77
+ warn "The code is not adapted to recent RuboCop yet,\n" \
78
+ "and the old one no longer works with Ruby 3.x.\n" \
79
+ "Switch back to Ruby 2.x to run it."
80
+ end
81
+ end
82
+ else
83
+ desc "Run RuboCop (dummy)"
84
+ task :rubocop do
85
+ warn "RuboCop not installed"
86
+ end
87
+ end
88
+
89
+ YARD::Rake::YardocTask.new if Object.const_defined? :YARD
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.14.0
1
+ 0.17.0
data/doc/Reference.md CHANGED
@@ -179,7 +179,7 @@ If the signature expects a Variant
179
179
  ISSUE: using something else than cryptic signatures is even more painful
180
180
  than remembering the signatures!
181
181
 
182
- foo_i["Bar"] = DBus.variant("au", [0, 1, 1, 2, 3, 5, 8])
182
+ `foo_i["Bar"] = DBus.variant("au", [0, 1, 1, 2, 3, 5, 8])`
183
183
 
184
184
  2. Other values are tried to fit one of these:
185
185
  Boolean, Double, Array of Variants, Hash of String keyed Variants,
@@ -244,14 +244,97 @@ When you want to provide a DBus API.
244
244
  (check that client and service side have their counterparts)
245
245
 
246
246
  ### Basic
247
+
247
248
  #### Exporting a Method
249
+
248
250
  ##### Interfaces
251
+
249
252
  ##### Methods
253
+
250
254
  ##### Bus Names
255
+
251
256
  ##### Errors
257
+
252
258
  #### Exporting Properties
259
+
260
+ Similar to plain Ruby attributes, declared with
261
+
262
+ - {https://docs.ruby-lang.org/en/3.1/Module.html#method-i-attr_accessor attr_accessor}
263
+ - {https://docs.ruby-lang.org/en/3.1/Module.html#method-i-attr_reader attr_reader}
264
+ - {https://docs.ruby-lang.org/en/3.1/Module.html#method-i-attr_writer attr_writer}
265
+
266
+ These methods declare the attributes and export them as properties:
267
+
268
+ - {DBus::Object.dbus_attr_accessor}
269
+ - {DBus::Object.dbus_attr_reader}
270
+ - {DBus::Object.dbus_attr_writer}
271
+
272
+ For making properties out of Ruby methods (which are not attributes), use:
273
+
274
+ - {DBus::Object.dbus_accessor}
275
+ - {DBus::Object.dbus_reader}
276
+ - {DBus::Object.dbus_writer}
277
+
278
+ Note that the properties are declared in the Ruby naming convention with
279
+ `snake_case` and D-Bus sees them `CamelCased`. Use the `dbus_name` argument
280
+ for overriding this.
281
+
282
+  
283
+
284
+ class Note < DBus::Object
285
+ dbus_interface "net.vidner.Example.Properties" do
286
+ # A read-write property "Title",
287
+ # with `title` and `title=` accessing @title.
288
+ dbus_attr_accessor :title, DBus::Type::STRING
289
+
290
+ # A read-only property "Author"
291
+ # (type specified via DBus signature)
292
+ # with `author` reading `@author`
293
+ dbus_attr_reader :author, "s"
294
+
295
+ # A read-only property `Clock`
296
+ def clock
297
+ Time.now.to_s
298
+ end
299
+ dbus_reader :clock, "s"
300
+
301
+ # Name mapping: `CreationTime`
302
+ def creation_time
303
+ "1993-01-01 00:00:00 +0100"
304
+ end
305
+ dbus_reader :creation_time, "s"
306
+
307
+ dbus_attr_accessor :book_volume, DBus::Type::VARIANT, dbus_name: "Volume"
308
+ end
309
+
310
+ dbus_interface "net.vidner.Example.Audio" do
311
+ dbus_attr_accessor :speaker_volume, DBus::Type::BYTE, dbus_name: "Volume"
312
+ end
313
+
314
+ # Must assign values because `nil` would crash our connection
315
+ def initialize(opath)
316
+ super
317
+ @title = "Ahem"
318
+ @author = "Martin"
319
+ @book_volume = 1
320
+ @speaker_volume = 11
321
+ end
322
+ end
323
+
324
+ obj = Note.new("/net/vidner/Example/Properties")
325
+
326
+ bus = DBus::SessionBus.instance
327
+ service = bus.request_service("net.vidner.Example")
328
+ service.export(obj)
329
+
330
+ main = DBus::Main.new
331
+ main << bus
332
+ main.run
333
+
253
334
  ### Advanced
335
+
254
336
  #### Inheritance
337
+
255
338
  #### Names
256
339
 
257
340
  Specification Conformance
@@ -4,6 +4,9 @@ if ARGV[0].nil?
4
4
  exit
5
5
  end
6
6
 
7
+ base_url = "https://github.com/mvidner/ruby-dbus/blob/master/"
8
+ base_url += ARGV[0].gsub("../", "")
9
+
7
10
  File.open(ARGV[0]) do |f|
8
11
  title = nil
9
12
  setup = ""
@@ -20,7 +23,9 @@ File.open(ARGV[0]) do |f|
20
23
  setup = example
21
24
  else
22
25
  File.open("#{basename}.rb", "w") do |e|
26
+ anchor = title.downcase.gsub(/ +/, "-")
23
27
  e.write setup
28
+ e.write "# #{base_url}##{anchor}\n"
24
29
  e.write example
25
30
  e.chmod(0o755)
26
31
  end
data/examples/gdbus/gdbus CHANGED
@@ -119,25 +119,26 @@ class DBusUI
119
119
  model = Gtk::ListStore.new(String, String, DBus::Method,
120
120
  DBus::ProxyObjectInterface)
121
121
  @methsigtreeview.model = model
122
- if selected
123
- if (intf = selected[1])
124
- intf.methods.keys.sort.each do |mi|
125
- m = intf.methods[mi]
126
- subiter = model.append
127
- subiter[0] = beautify_method(m)
128
- subiter[1] = "M"
129
- subiter[2] = m
130
- subiter[3] = intf
131
- end
132
- intf.signals.keys.sort.each do |mi|
133
- m = intf.signals[mi]
134
- subiter = model.append
135
- subiter[0] = beautify_method(m)
136
- subiter[1] = "S"
137
- subiter[2] = m
138
- subiter[3] = intf
139
- end
140
- end
122
+ return unless selected
123
+
124
+ intf = selected[1]
125
+ return unless intf
126
+
127
+ intf.methods.keys.sort.each do |mi|
128
+ m = intf.methods[mi]
129
+ subiter = model.append
130
+ subiter[0] = beautify_method(m)
131
+ subiter[1] = "M"
132
+ subiter[2] = m
133
+ subiter[3] = intf
134
+ end
135
+ intf.signals.keys.sort.each do |mi|
136
+ m = intf.signals[mi]
137
+ subiter = model.append
138
+ subiter[0] = beautify_method(m)
139
+ subiter[1] = "S"
140
+ subiter[2] = m
141
+ subiter[3] = intf
141
142
  end
142
143
  end
143
144
 
@@ -226,7 +227,7 @@ class DBusUI
226
227
 
227
228
  def introspect_services(model, bus)
228
229
  el = @introspect_array.shift
229
- if !(el =~ /^:/)
230
+ if el !~ /^:/
230
231
  iter = model.append(nil)
231
232
  iter[0] = el
232
233
  puts "introspecting: #{el}"
@@ -14,7 +14,7 @@ player.test_variant(["s", "coucou"])
14
14
  player.on_signal("SomethingJustHappened") do |u, v|
15
15
  puts "SomethingJustHappened: #{u} #{v}"
16
16
  end
17
- player.hello("8=======D", "(_._)")
17
+ player.hello("Hey", "there!")
18
18
  p player["org.ruby.AnotherInterface"].Reverse("Hello world!")
19
19
 
20
20
  main = DBus::Main.new
data/lib/dbus/auth.rb CHANGED
@@ -73,7 +73,7 @@ module DBus
73
73
  path = File.join(ENV["HOME"], ".dbus-keyrings", context)
74
74
  DBus.logger.debug "path: #{path.inspect}"
75
75
  File.foreach(path) do |line|
76
- if line.index(id) == 0
76
+ if line.start_with?(id)
77
77
  # Right line of file, read cookie
78
78
  cookie = line.split(" ")[2].chomp
79
79
  DBus.logger.debug "cookie: #{cookie.inspect}"
@@ -87,14 +87,14 @@ module DBus
87
87
  return response
88
88
  end
89
89
  end
90
+ return if @retries <= 0
91
+
90
92
  # a little rescue magic
91
- unless @retries <= 0
92
- puts "ERROR: Could not auth, will now exit."
93
- puts "ERROR: Unable to locate cookie, retry in 1 second."
94
- @retries -= 1
95
- sleep 1
96
- data(hexdata)
97
- end
93
+ puts "ERROR: Could not auth, will now exit."
94
+ puts "ERROR: Unable to locate cookie, retry in 1 second."
95
+ @retries -= 1
96
+ sleep 1
97
+ data(hexdata)
98
98
  end
99
99
 
100
100
  # encode plain to hex
data/lib/dbus/bus.rb CHANGED
@@ -17,7 +17,7 @@ require "singleton"
17
17
  # Module containing all the D-Bus modules and classes.
18
18
  module DBus
19
19
  # This represents a remote service. It should not be instantiated directly
20
- # Use Bus::service()
20
+ # Use {Connection#service}
21
21
  class Service
22
22
  # The service name.
23
23
  attr_reader :name
@@ -28,7 +28,7 @@ module DBus
28
28
 
29
29
  # Create a new service with a given _name_ on a given _bus_.
30
30
  def initialize(name, bus)
31
- @name = name
31
+ @name = BusName.new(name)
32
32
  @bus = bus
33
33
  @root = Node.new("/")
34
34
  end
@@ -148,20 +148,22 @@ module DBus
148
148
 
149
149
  # Return an XML string representation of the node.
150
150
  # It is shallow, not recursing into subnodes
151
- def to_xml
151
+ # @param node_opath [String]
152
+ def to_xml(node_opath)
152
153
  xml = '<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
153
154
  "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
154
- <node>
155
155
  '
156
+ xml += "<node name=\"#{node_opath}\">\n"
156
157
  each_pair do |k, _v|
157
- xml += "<node name=\"#{k}\" />"
158
+ xml += " <node name=\"#{k}\" />\n"
158
159
  end
159
160
  if @object
160
161
  @object.intfs.each_pair do |_k, v|
161
- xml += %(<interface name="#{v.name}">\n)
162
+ xml += " <interface name=\"#{v.name}\">\n"
162
163
  v.methods.each_value { |m| xml += m.to_xml }
163
164
  v.signals.each_value { |m| xml += m.to_xml }
164
- xml += "</interface>\n"
165
+ v.properties.each_value { |m| xml += m.to_xml }
166
+ xml += " </interface>\n"
165
167
  end
166
168
  end
167
169
  xml += "</node>"
@@ -387,7 +389,7 @@ module DBus
387
389
  # introspect in synchronous !
388
390
  data = introspect_data(dest, path)
389
391
  pof = DBus::ProxyObjectFactory.new(data, self, dest, path)
390
- return pof.build
392
+ pof.build
391
393
  else
392
394
  introspect_data(dest, path) do |async_data|
393
395
  yield(DBus::ProxyObjectFactory.new(async_data, self, dest, path).build)
@@ -458,6 +460,9 @@ module DBus
458
460
  retm = wait_for_message
459
461
  process(retm)
460
462
  end
463
+ rescue EOFError
464
+ new_err = DBus::Error.new("Connection dropped after we sent #{m.inspect}")
465
+ raise new_err
461
466
  end
462
467
 
463
468
  # @api private
@@ -529,7 +534,8 @@ module DBus
529
534
  m.member == "Introspect"
530
535
  reply = Message.new(Message::METHOD_RETURN).reply_to(m)
531
536
  reply.sender = @unique_name
532
- reply.add_param(Type::STRING, node.to_xml)
537
+ xml = node.to_xml(m.path)
538
+ reply.add_param(Type::STRING, xml)
533
539
  @message_queue.push(reply)
534
540
  else
535
541
  obj = node.object
@@ -538,7 +544,8 @@ module DBus
538
544
  end
539
545
  when DBus::Message::SIGNAL
540
546
  # the signal can match multiple different rules
541
- @signal_matchrules.each do |mrs, slot|
547
+ # clone to allow new signale handlers to be registered
548
+ @signal_matchrules.dup.each do |mrs, slot|
542
549
  if DBus::MatchRule.new.from_s(mrs).match(m)
543
550
  slot.call(m)
544
551
  end
@@ -562,6 +569,11 @@ module DBus
562
569
  # @api private
563
570
  # Emit a signal event for the given _service_, object _obj_, interface
564
571
  # _intf_ and signal _sig_ with arguments _args_.
572
+ # @param service [Service]
573
+ # @param obj [DBus::Object]
574
+ # @param intf [Interface]
575
+ # @param sig [Signal]
576
+ # @param args arguments for the signal
565
577
  def emit(service, obj, intf, sig, *args)
566
578
  m = Message.new(DBus::Message::SIGNAL)
567
579
  m.path = obj.path
@@ -664,7 +676,7 @@ module DBus
664
676
  # (for Unix-socket) unix:path=/tmp/my_funky_bus_socket
665
677
  #
666
678
  # you'll need to take care about authentification then, more info here:
667
- # http://github.com/pangdudu/ruby-dbus/blob/master/README.rdoc
679
+ # https://gitlab.com/pangdudu/ruby-dbus/-/blob/master/README.rdoc
668
680
  class RemoteBus < Connection
669
681
  # Get the remote bus.
670
682
  def initialize(socket_name)
@@ -0,0 +1,27 @@
1
+ # This file is part of the ruby-dbus project
2
+ # Copyright (C) 2019 Martin Vidner
3
+ #
4
+ # This library is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU Lesser General Public
6
+ # License, version 2.1 as published by the Free Software Foundation.
7
+ # See the file "COPYING" for the exact licensing terms.
8
+
9
+ module DBus
10
+ # A {::String} that validates at initialization time
11
+ # @see https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names-bus
12
+ class BusName < String
13
+ # @raise Error if not a valid bus name
14
+ def initialize(s)
15
+ unless self.class.valid?(s)
16
+ raise DBus::Error, "Invalid bus name #{s.inspect}"
17
+ end
18
+ super
19
+ end
20
+
21
+ def self.valid?(s)
22
+ s.size <= 255 &&
23
+ (s =~ /\A:[A-Za-z0-9_-]+(\.[A-Za-z0-9_-]+)+\z/ ||
24
+ s =~ /\A[A-Za-z_-][A-Za-z0-9_-]*(\.[A-Za-z_-][A-Za-z0-9_-]*)+\z/)
25
+ end
26
+ end
27
+ end
@@ -1,15 +1,17 @@
1
+ # frozen_string_literal: true
1
2
  # copied from activesupport/core_ext from Rails, MIT license
2
- # https://github.com/rails/rails/tree/5aa869861c192daceafe3a3ee50eb93f5a2b7bd2/activesupport/lib/active_support/core_ext
3
- require 'dbus/core_ext/kernel/singleton_class'
4
- require 'dbus/core_ext/module/remove_method'
5
- require 'dbus/core_ext/array/extract_options'
3
+ # https://github.com/rails/rails/tree/9794e85351243cac6d4e78adaba634b8e4ecad0a/activesupport/lib/active_support/core_ext
4
+
5
+ require_relative "../module/redefine_method"
6
6
 
7
7
  class Class
8
8
  # Declare a class-level attribute whose value is inheritable by subclasses.
9
9
  # Subclasses can change their own value and it will not impact parent class.
10
10
  #
11
+ # ==== Examples
12
+ #
11
13
  # class Base
12
- # class_attribute :setting
14
+ # my_class_attribute :setting
13
15
  # end
14
16
  #
15
17
  # class Subclass < Base
@@ -22,14 +24,14 @@ class Class
22
24
  # Base.setting # => true
23
25
  #
24
26
  # In the above case as long as Subclass does not assign a value to setting
25
- # by performing <tt>Subclass.setting = _something_ </tt>, <tt>Subclass.setting</tt>
27
+ # by performing <tt>Subclass.setting = _something_</tt>, <tt>Subclass.setting</tt>
26
28
  # would read value assigned to parent class. Once Subclass assigns a value then
27
29
  # the value assigned by Subclass would be returned.
28
30
  #
29
31
  # This matches normal Ruby method inheritance: think of writing an attribute
30
32
  # on a subclass as overriding the reader method. However, you need to be aware
31
33
  # when using +class_attribute+ with mutable structures as +Array+ or +Hash+.
32
- # In such cases, you don't want to do changes in places but use setters:
34
+ # In such cases, you don't want to do changes in place. Instead use setters:
33
35
  #
34
36
  # Base.setting = []
35
37
  # Base.setting # => []
@@ -59,39 +61,25 @@ class Class
59
61
  # object.setting = false
60
62
  # object.setting # => false
61
63
  # Base.setting # => true
62
- #
63
- # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
64
- #
65
- # object.setting # => NoMethodError
66
- # object.setting? # => NoMethodError
67
- #
68
- # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
69
- #
70
- # object.setting = false # => NoMethodError
71
- #
72
- # To opt out of both instance methods, pass <tt>instance_accessor: false</tt>.
73
- def class_attribute(*attrs)
74
- options = attrs.extract_options!
75
- instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true)
76
- instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true)
77
- instance_predicate = options.fetch(:instance_predicate, true)
64
+ def my_class_attribute(*attrs)
65
+ instance_reader = true
66
+ instance_writer = true
78
67
 
79
68
  attrs.each do |name|
69
+ singleton_class.silence_redefinition_of_method(name)
80
70
  define_singleton_method(name) { nil }
81
- define_singleton_method("#{name}?") { !!public_send(name) } if instance_predicate
82
71
 
83
- ivar = "@#{name}"
72
+ ivar = "@#{name}".to_sym
84
73
 
74
+ singleton_class.silence_redefinition_of_method("#{name}=")
85
75
  define_singleton_method("#{name}=") do |val|
86
76
  singleton_class.class_eval do
87
- remove_possible_method(name)
88
- define_method(name) { val }
77
+ redefine_method(name) { val }
89
78
  end
90
79
 
91
80
  if singleton_class?
92
81
  class_eval do
93
- remove_possible_method(name)
94
- define_method(name) do
82
+ redefine_method(name) do
95
83
  if instance_variable_defined? ivar
96
84
  instance_variable_get ivar
97
85
  else
@@ -104,26 +92,20 @@ class Class
104
92
  end
105
93
 
106
94
  if instance_reader
107
- remove_possible_method name
108
- define_method(name) do
95
+ redefine_method(name) do
109
96
  if instance_variable_defined?(ivar)
110
97
  instance_variable_get ivar
111
98
  else
112
99
  self.class.public_send name
113
100
  end
114
101
  end
115
- define_method("#{name}?") { !!public_send(name) } if instance_predicate
116
102
  end
117
103
 
118
- attr_writer name if instance_writer
119
- end
120
- end
121
-
122
- private
123
-
124
- unless respond_to?(:singleton_class?)
125
- def singleton_class?
126
- ancestors.first != self
104
+ if instance_writer
105
+ redefine_method("#{name}=") do |val|
106
+ instance_variable_set ivar, val
107
+ end
127
108
  end
128
109
  end
110
+ end
129
111
  end