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
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+ # copied from activesupport/core_ext from Rails, MIT license
3
+ # https://github.com/rails/rails/tree/a713fdae4eb4f7ccd34932edc61561a96b8d9f35/activesupport/lib/active_support/core_ext/module
4
+
5
+ class Module
6
+ if RUBY_VERSION >= "2.3"
7
+ # Marks the named method as intended to be redefined, if it exists.
8
+ # Suppresses the Ruby method redefinition warning. Prefer
9
+ # #redefine_method where possible.
10
+ def silence_redefinition_of_method(method)
11
+ if method_defined?(method) || private_method_defined?(method)
12
+ # This suppresses the "method redefined" warning; the self-alias
13
+ # looks odd, but means we don't need to generate a unique name
14
+ alias_method method, method
15
+ end
16
+ end
17
+ else
18
+ def silence_redefinition_of_method(method)
19
+ if method_defined?(method) || private_method_defined?(method)
20
+ alias_method :__rails_redefine, method
21
+ remove_method :__rails_redefine
22
+ end
23
+ end
24
+ end
25
+
26
+ # Replaces the existing method definition, if there is one, with the passed
27
+ # block as its body.
28
+ def redefine_method(method, &block)
29
+ visibility = method_visibility(method)
30
+ silence_redefinition_of_method(method)
31
+ define_method(method, &block)
32
+ send(visibility, method)
33
+ end
34
+
35
+ # Replaces the existing singleton method definition, if there is one, with
36
+ # the passed block as its body.
37
+ def redefine_singleton_method(method, &block)
38
+ singleton_class.redefine_method(method, &block)
39
+ end
40
+
41
+ def method_visibility(method) # :nodoc:
42
+ case
43
+ when private_method_defined?(method)
44
+ :private
45
+ when protected_method_defined?(method)
46
+ :protected
47
+ else
48
+ :public
49
+ end
50
+ end
51
+ end
@@ -14,10 +14,6 @@ module DBus
14
14
  # Regular expressions that should match all interface names.
15
15
  INTERFACE_ELEMENT_RE = /^[A-Za-z][A-Za-z0-9_]*$/
16
16
 
17
- # Exception raised when an unknown signal is used.
18
- class UnknownSignal < Exception
19
- end
20
-
21
17
  # Exception raised when an invalid class definition is encountered.
22
18
  class InvalidClassDefinition < Exception
23
19
  end
@@ -30,19 +26,23 @@ module DBus
30
26
  # It also is the local definition of interface exported by the program.
31
27
  # At the client side, see ProxyObjectInterface
32
28
  class Interface
33
- # The name of the interface. String
29
+ # @return [String] The name of the interface.
34
30
  attr_reader :name
35
- # The methods that are part of the interface. Hash: Symbol => DBus::Method
31
+ # @return [Hash{Symbol => DBus::Method}] The methods that are part of the interface.
36
32
  attr_reader :methods
37
- # The signals that are part of the interface. Hash: Symbol => Signal
33
+ # @return [Hash{Symbol => Signal}] The signals that are part of the interface.
38
34
  attr_reader :signals
39
35
 
36
+ # @return [Hash{Symbol => Property}]
37
+ attr_reader :properties
38
+
40
39
  # Creates a new interface with a given _name_.
41
40
  def initialize(name)
42
41
  validate_name(name)
43
42
  @name = name
44
43
  @methods = {}
45
44
  @signals = {}
45
+ @properties = {}
46
46
  end
47
47
 
48
48
  # Validates a service _name_.
@@ -50,29 +50,37 @@ module DBus
50
50
  raise InvalidIntrospectionData if name.bytesize > 255
51
51
  raise InvalidIntrospectionData if name =~ /^\./ || name =~ /\.$/
52
52
  raise InvalidIntrospectionData if name =~ /\.\./
53
- raise InvalidIntrospectionData if !(name =~ /\./)
53
+ raise InvalidIntrospectionData if name !~ /\./
54
54
  name.split(".").each do |element|
55
- raise InvalidIntrospectionData if !(element =~ INTERFACE_ELEMENT_RE)
55
+ raise InvalidIntrospectionData if element !~ INTERFACE_ELEMENT_RE
56
56
  end
57
57
  end
58
58
 
59
- # Helper method for defining a method _m_.
60
- def define(m)
61
- if m.is_a?(Method)
62
- @methods[m.name.to_sym] = m
63
- elsif m.is_a?(Signal)
64
- @signals[m.name.to_sym] = m
65
- end
59
+ # Add _ie_ as a known {Method}, {Signal} or {Property}
60
+ # @param ie [InterfaceElement]
61
+ def define(ie)
62
+ name = ie.name.to_sym
63
+ category = if ie.is_a?(Method)
64
+ @methods
65
+ elsif ie.is_a?(Signal)
66
+ @signals
67
+ elsif ie.is_a?(Property)
68
+ @properties
69
+ end
70
+ category[name] = ie
66
71
  end
72
+ alias declare define
67
73
  alias << define
68
74
 
69
75
  # Defines a method with name _id_ and a given _prototype_ in the
70
76
  # interface.
77
+ # Better name: declare_method
71
78
  def define_method(id, prototype)
72
79
  m = Method.new(id)
73
80
  m.from_prototype(prototype)
74
81
  define(m)
75
82
  end
83
+ alias declare_method define_method
76
84
  end # class Interface
77
85
 
78
86
  # = A formal parameter has a name and a type
@@ -148,6 +156,7 @@ module DBus
148
156
  end
149
157
 
150
158
  # Add parameter types by parsing the given _prototype_.
159
+ # @param prototype [Prototype]
151
160
  def from_prototype(prototype)
152
161
  prototype.split(/, */).each do |arg|
153
162
  arg = arg.split(" ")
@@ -171,16 +180,16 @@ module DBus
171
180
 
172
181
  # Return an XML string representation of the method interface elment.
173
182
  def to_xml
174
- xml = %(<method name="#{@name}">\n)
183
+ xml = " <method name=\"#{@name}\">\n"
175
184
  @params.each do |param|
176
- name = param.name ? %(name="#{param.name}" ) : ""
177
- xml += %(<arg #{name}direction="in" type="#{param.type}"/>\n)
185
+ name = param.name ? "name=\"#{param.name}\" " : ""
186
+ xml += " <arg #{name}direction=\"in\" type=\"#{param.type}\"/>\n"
178
187
  end
179
188
  @rets.each do |param|
180
- name = param.name ? %(name="#{param.name}" ) : ""
181
- xml += %(<arg #{name}direction="out" type="#{param.type}"/>\n)
189
+ name = param.name ? "name=\"#{param.name}\" " : ""
190
+ xml += " <arg #{name}direction=\"out\" type=\"#{param.type}\"/>\n"
182
191
  end
183
- xml += %(</method>\n)
192
+ xml += " </method>\n"
184
193
  xml
185
194
  end
186
195
  end # class Method
@@ -205,13 +214,49 @@ module DBus
205
214
 
206
215
  # Return an XML string representation of the signal interface elment.
207
216
  def to_xml
208
- xml = %(<signal name="#{@name}">\n)
217
+ xml = " <signal name=\"#{@name}\">\n"
209
218
  @params.each do |param|
210
- name = param.name ? %(name="#{param.name}" ) : ""
211
- xml += %(<arg #{name}type="#{param.type}"/>\n)
219
+ name = param.name ? "name=\"#{param.name}\" " : ""
220
+ xml += " <arg #{name}type=\"#{param.type}\"/>\n"
212
221
  end
213
- xml += %(</signal>\n)
222
+ xml += " </signal>\n"
214
223
  xml
215
224
  end
216
225
  end # class Signal
226
+
227
+ # An (exported) property
228
+ # https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties
229
+ class Property
230
+ # @return [String] The name of the property, for example FooBar.
231
+ attr_reader :name
232
+ attr_reader :type
233
+ # @return [Symbol] :read :write or :readwrite
234
+ attr_reader :access
235
+
236
+ # @return [Symbol] What to call at Ruby side.
237
+ # (Always without the trailing `=`)
238
+ attr_reader :ruby_name
239
+
240
+ def initialize(name, type, access, ruby_name:)
241
+ @name = name
242
+ @type = type
243
+ @access = access
244
+ @ruby_name = ruby_name
245
+ end
246
+
247
+ # @return [Boolean]
248
+ def readable?
249
+ access == :read || access == :readwrite
250
+ end
251
+
252
+ # @return [Boolean]
253
+ def writable?
254
+ access == :write || access == :readwrite
255
+ end
256
+
257
+ # Return introspection XML string representation of the property.
258
+ def to_xml
259
+ " <property type=\"#{@type}\" name=\"#{@name}\" access=\"#{@access}\"/>\n"
260
+ end
261
+ end
217
262
  end # module DBus
data/lib/dbus/marshall.rb CHANGED
@@ -69,6 +69,7 @@ module DBus
69
69
  def align(a)
70
70
  case a
71
71
  when 1
72
+ nil
72
73
  when 2, 4, 8
73
74
  bits = a - 1
74
75
  @idx = @idx + bits & ~bits
@@ -421,7 +422,7 @@ module DBus
421
422
  ["s", value.to_str]
422
423
  elsif value.respond_to? :to_int
423
424
  i = value.to_int
424
- if -2_147_483_648 <= i && i < 2_147_483_648
425
+ if (-2_147_483_648...2_147_483_648).cover?(i)
425
426
  ["i", i]
426
427
  else
427
428
  ["x", i]
@@ -11,6 +11,7 @@ require "fcntl"
11
11
  require "socket"
12
12
 
13
13
  module DBus
14
+ # Encapsulates a socket so that we can {#push} and {#pop} {Message}s.
14
15
  class MessageQueue
15
16
  # The socket that is used to connect with the bus.
16
17
  attr_reader :socket
@@ -22,10 +23,10 @@ module DBus
22
23
  connect
23
24
  end
24
25
 
25
- # TODO: failure modes
26
- #
27
- # If _non_block_ is true, return nil instead of waiting
28
- # EOFError may be raised
26
+ # @param non_block [Boolean] if true, return nil instead of waiting
27
+ # @return [Message,nil] one message or nil if unavailable
28
+ # @raise EOFError
29
+ # @todo failure modes
29
30
  def pop(non_block = false)
30
31
  buffer_from_socket_nonblock
31
32
  message = message_from_buffer_nonblock
@@ -80,22 +81,23 @@ module DBus
80
81
 
81
82
  # Connect to a bus over tcp and initialize the connection.
82
83
  def connect_to_tcp(params)
83
- # check if the path is sufficient
84
- if params.key?("host") && params.key?("port")
84
+ host = params["host"]
85
+ port = params["port"]
86
+ if host && port
85
87
  begin
86
88
  # initialize the tcp socket
87
- @socket = TCPSocket.new(params["host"], params["port"].to_i)
89
+ @socket = TCPSocket.new(host, port.to_i)
88
90
  @socket.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
89
91
  init_connection
90
92
  @is_tcp = true
91
93
  rescue Exception => e
92
94
  puts "Oops:", e
93
- puts "Error: Could not establish connection to: #{@path}, will now exit."
95
+ puts "Error: Could not establish connection to: #{host}:#{port}, will now exit."
94
96
  exit(1) # a little harsh
95
97
  end
96
98
  else
97
99
  # Danger, Will Robinson: the specified "path" is not usable
98
- puts "Error: supplied path: #{@path}, unusable! sorry."
100
+ puts "Error: supplied params: #{@params}, unusable! sorry."
99
101
  end
100
102
  end
101
103
 
@@ -124,14 +126,14 @@ module DBus
124
126
 
125
127
  # Initialize the connection to the bus.
126
128
  def init_connection
127
- @client = Client.new(@socket)
128
- @client.authenticate
129
+ client = Client.new(@socket)
130
+ client.authenticate
129
131
  end
130
132
 
131
133
  public # FIXME: fix Main loop instead
132
134
 
133
135
  # Get and remove one message from the buffer.
134
- # Return the message or nil.
136
+ # @return [Message,nil] the message or nil if unavailable
135
137
  def message_from_buffer_nonblock
136
138
  return nil if @buffer.empty?
137
139
  ret = nil
@@ -139,7 +141,7 @@ module DBus
139
141
  ret, size = Message.new.unmarshall_buffer(@buffer)
140
142
  @buffer.slice!(0, size)
141
143
  rescue IncompleteBufferException
142
- # fall through, let ret be null
144
+ # fall through, let ret remain nil
143
145
  end
144
146
  ret
145
147
  end
@@ -149,7 +151,8 @@ module DBus
149
151
 
150
152
  # Fill (append) the buffer from data that might be available on the
151
153
  # socket.
152
- # EOFError may be raised
154
+ # @return [void]
155
+ # @raise EOFError
153
156
  def buffer_from_socket_nonblock
154
157
  @buffer += @socket.read_nonblock(MSG_BUF_SIZE)
155
158
  rescue EOFError