ruby-dbus 0.14.0 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
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