mqtt 0.0.5 → 0.0.7

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.
data/README CHANGED
@@ -1,16 +1,18 @@
1
1
  ruby-mqtt
2
2
  =========
3
3
 
4
- Pure Ruby gem that implements the MQTT (Message Queue Telemetry Transport) protocol, a lightweight protocol for publish/subscribe messaging.
4
+ Pure Ruby gem that implements the MQTT (Message Queue Telemetry Transport) protocol,
5
+ a lightweight protocol for publish/subscribe messaging.
5
6
 
6
7
 
7
8
  Installing
8
9
  ----------
9
10
 
10
- You may get the latest stable version from Rubyforge. Source gems are also available.
11
+ You may get the latest stable version from Rubygems:
11
12
 
12
13
  $ gem install mqtt
13
14
 
15
+
14
16
  Synopsis
15
17
  --------
16
18
 
@@ -18,37 +20,22 @@ Synopsis
18
20
  require 'mqtt'
19
21
 
20
22
  # Publish example
21
- mqtt = MQTT::Client.new('mqtt.example.com')
22
- mqtt.connect do |c|
23
- c.publish('topic','message')
23
+ MQTT::Client.connect('test.mosquitto.org') do |c|
24
+ c.publish('topic', 'message')
24
25
  end
25
26
 
26
27
  # Subscribe example
27
- mqtt = MQTT::Client.new('mqtt.example.com')
28
- client.connect do
29
- client.subscribe('test')
30
- loop do
31
- topic,message = client.get
28
+ MQTT::Client.connect('test.mosquitto.org') do |c|
29
+ c.get('test') do |topic,message|
32
30
  puts "#{topic}: #{message}"
33
31
  end
34
32
  end
35
33
 
36
34
 
37
- TODO
38
- ----
35
+ Limitations
36
+ -----------
39
37
 
40
- * Implement Will and Testament
41
- * Process acknowledgement packets / Implement QOS 1 in client
42
- * More validations of data/parameters
43
- * More error checking and exception throwing
44
- - Check that packet data is valid - don't blindly read values
45
- - Subscribe and Unsubscribe packets should always have QOS of 1
46
- * More examples
47
- * Integration tests
48
- * Refactor to add callbacks that are called from seperate thread
49
- * Implement QOS Level 2 in client
50
- * Prevent proxy from connecting to itself
51
- * Add support for binding socket to specific local address
38
+ * Only QOS 0 currently supported
52
39
 
53
40
 
54
41
  Resources
@@ -56,7 +43,7 @@ Resources
56
43
 
57
44
  * MQTT Homepage: http://www.mqtt.org/
58
45
  * GitHub Project: http://github.com/njh/ruby-mqtt
59
- * Documentation: http://rdoc.info/github/njh/ruby-mqtt/master/frames
46
+ * API Documentation: http://rubydoc.info/gems/mqtt/frames
60
47
 
61
48
 
62
49
  Contact
data/lib/mqtt.rb CHANGED
@@ -17,7 +17,7 @@ module MQTT
17
17
 
18
18
  class ProtocolException < MQTT::Exception
19
19
  end
20
-
20
+
21
21
  class NotConnectedException < MQTT::Exception
22
22
  end
23
23
 
data/lib/mqtt/client.rb CHANGED
@@ -16,23 +16,67 @@ class MQTT::Client
16
16
  # Timeout between select polls (in seconds)
17
17
  SELECT_TIMEOUT = 0.5
18
18
 
19
+ # Default attribute values
20
+ ATTR_DEFAULTS = {
21
+ :remote_host => MQTT::DEFAULT_HOST,
22
+ :remote_port => MQTT::DEFAULT_PORT,
23
+ :keep_alive => 15,
24
+ :clean_session => true,
25
+ :client_id => nil,
26
+ :ack_timeout => 5,
27
+ :username => nil,
28
+ :password => nil
29
+ }
30
+
31
+ # Create and connect a new MQTT Client
32
+ # Accepts the same arguments as creating a new client.
33
+ # If a block is given, then it will be executed before disconnecting again.
34
+ #
35
+ # Example:
36
+ # MQTT::Client.connect('myserver.example.com') do |client|
37
+ # # do stuff here
38
+ # end
39
+ #
40
+ def self.connect(*args, &block)
41
+ client = MQTT::Client.new(*args)
42
+ client.connect(&block)
43
+ return client
44
+ end
45
+
19
46
  # Create a new MQTT Client instance
20
- def initialize(remote_host=MQTT::DEFAULT_HOST, remote_port=MQTT::DEFAULT_PORT)
21
- @remote_host = remote_host
22
- @remote_port = remote_port
23
- @keep_alive = 10
24
- @clean_session = true
25
- @client_id = nil
47
+ #
48
+ # Examples:
49
+ # client = MQTT::Client.new('myserver.example.com')
50
+ # client = MQTT::Client.new('myserver.example.com', 18830)
51
+ # client = MQTT::Client.new(:remote_host => 'myserver.example.com')
52
+ # client = MQTT::Client.new(:remote_host => 'myserver.example.com', :keep_alive => 30)
53
+ #
54
+ def initialize(*args)
55
+ if args.count == 0
56
+ args = {}
57
+ elsif args.count == 1 and args[0].is_a?(Hash)
58
+ args = args[0]
59
+ elsif args.count == 1
60
+ args = {:remote_host => args[0]}
61
+ elsif args.count == 2
62
+ args = {:remote_host => args[0], :remote_port => args[1]}
63
+ else
64
+ raise ArgumentError, "Unsupported number of arguments"
65
+ end
66
+
67
+ # Merge arguments with default values for attributes
68
+ ATTR_DEFAULTS.merge(args).each_pair do |k,v|
69
+ instance_variable_set("@#{k}", v)
70
+ end
71
+
72
+ # Initialise private instance variables
26
73
  @message_id = 0
27
- @ack_timeout = 5
28
74
  @last_pingreq = Time.now
29
75
  @last_pingresp = Time.now
30
76
  @socket = nil
31
77
  @read_queue = Queue.new
32
78
  @read_thread = nil
33
79
  @write_semaphore = Mutex.new
34
- @username = nil
35
- @password = nil
36
80
  end
37
81
 
38
82
  # Connect to the MQTT broker
@@ -40,7 +84,7 @@ class MQTT::Client
40
84
  def connect(clientid=nil)
41
85
  if !clientid.nil?
42
86
  @client_id = clientid
43
- elsif clientid.nil?
87
+ elsif @clientid.nil?
44
88
  @client_id = random_letters(16)
45
89
  @clean_session = true
46
90
  end
@@ -140,17 +184,31 @@ class MQTT::Client
140
184
  end
141
185
 
142
186
  # Return the next message recieved from the MQTT broker.
143
- # This method blocks until a message is available.
187
+ # An optional topic can be given to subscribe to.
144
188
  #
145
- # The method returns the topic and message as an array:
189
+ # The method either returns the topic and message as an array:
146
190
  # topic,message = client.get
147
191
  #
148
- def get
149
- # Wait for a packet to be available
150
- packet = @read_queue.pop
151
- topic = packet.topic
152
- payload = packet.payload
153
- return topic,payload
192
+ # Or can be used with a block to keep processing messages:
193
+ # client.get('test') do |topic,payload|
194
+ # # Do stuff here
195
+ # end
196
+ #
197
+ def get(topic=nil)
198
+ # Subscribe to a topic, if an argument is given
199
+ subscribe(topic) unless topic.nil?
200
+
201
+ if block_given?
202
+ # Loop forever!
203
+ loop do
204
+ packet = @read_queue.pop
205
+ yield(packet.topic, packet.payload)
206
+ end
207
+ else
208
+ # Wait for one packet to be available
209
+ packet = @read_queue.pop
210
+ return packet.topic, packet.payload
211
+ end
154
212
  end
155
213
 
156
214
  # Send a unsubscribe message for one or more topics on the MQTT broker
data/lib/mqtt/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module MQTT
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.7"
3
3
  end
@@ -9,45 +9,98 @@ describe MQTT::Client do
9
9
  @client = MQTT::Client.new
10
10
  @socket = StringIO.new
11
11
  end
12
-
12
+
13
+ describe "initializing a client" do
14
+ it "with no arguments, it should use the defaults" do
15
+ @client = MQTT::Client.new
16
+ @client.remote_host.should == 'localhost'
17
+ @client.remote_port.should == 1883
18
+ @client.keep_alive.should == 15
19
+ end
20
+
21
+ it "with a single string argument, it should use it has the host" do
22
+ @client = MQTT::Client.new('otherhost.mqtt.org')
23
+ @client.remote_host.should == 'otherhost.mqtt.org'
24
+ @client.remote_port.should == 1883
25
+ @client.keep_alive.should == 15
26
+ end
27
+
28
+ it "with two arguments, it should use it as the host and port" do
29
+ @client = MQTT::Client.new('otherhost.mqtt.org', 1000)
30
+ @client.remote_host.should == 'otherhost.mqtt.org'
31
+ @client.remote_port.should == 1000
32
+ @client.keep_alive.should == 15
33
+ end
34
+
35
+ it "with names arguments, it should use those as arguments" do
36
+ @client = MQTT::Client.new(:remote_host => 'otherhost.mqtt.org', :remote_port => 1000)
37
+ @client.remote_host.should == 'otherhost.mqtt.org'
38
+ @client.remote_port.should == 1000
39
+ @client.keep_alive.should == 15
40
+ end
41
+
42
+ it "with a hash, it should use those as arguments" do
43
+ @client = MQTT::Client.new({:remote_host => 'otherhost.mqtt.org', :remote_port => 1000})
44
+ @client.remote_host.should == 'otherhost.mqtt.org'
45
+ @client.remote_port.should == 1000
46
+ @client.keep_alive.should == 15
47
+ end
48
+
49
+ it "with a hash containing just a keep alive setting" do
50
+ @client = MQTT::Client.new(:keep_alive => 60)
51
+ @client.remote_host.should == 'localhost'
52
+ @client.remote_port.should == 1883
53
+ @client.keep_alive.should == 60
54
+ end
55
+
56
+ it "with three arguments" do
57
+ lambda {
58
+ @client = MQTT::Client.new(1, 2, 3)
59
+ }.should raise_error(
60
+ 'Unsupported number of arguments'
61
+ )
62
+ end
63
+ end
64
+
65
+
13
66
  describe "when calling the 'connect' method" do
14
67
  before(:each) do
15
68
  TCPSocket.stubs(:new).returns(@socket)
16
69
  Thread.stubs(:new)
17
70
  @client.stubs(:receive_connack)
18
71
  end
19
-
72
+
20
73
  it "should create a TCP Socket if not connected" do
21
74
  TCPSocket.expects(:new).once.returns(@socket)
22
75
  @client.connect('myclient')
23
76
  end
24
-
77
+
25
78
  it "should not create a new TCP Socket if connected" do
26
79
  @client.stubs(:connected?).returns(true)
27
80
  TCPSocket.expects(:new).never
28
81
  @client.connect('myclient')
29
82
  end
30
-
83
+
31
84
  it "should start the reader thread if not connected" do
32
85
  Thread.expects(:new).once
33
86
  @client.connect('myclient')
34
87
  end
35
-
88
+
36
89
  it "should write a valid CONNECT packet to the socket if not connected" do
37
90
  @client.connect('myclient')
38
- @socket.string.should == "\020\026\x00\x06MQIsdp\x03\x02\x00\x0a\x00\x08myclient"
91
+ @socket.string.should == "\020\026\x00\x06MQIsdp\x03\x02\x00\x0f\x00\x08myclient"
39
92
  end
40
-
93
+
41
94
  it "should try and read an acknowledgement packet to the socket if not connected" do
42
95
  @client.expects(:receive_connack).once
43
96
  @client.connect('myclient')
44
97
  end
45
-
98
+
46
99
  it "should disconnect after connecting, if a block is given" do
47
100
  @client.expects(:disconnect).once
48
101
  @client.connect('myclient') { nil }
49
102
  end
50
-
103
+
51
104
  it "should not disconnect after connecting, if no block is given" do
52
105
  @client.expects(:disconnect).never
53
106
  @client.connect('myclient')
@@ -60,8 +113,8 @@ describe MQTT::Client do
60
113
  @socket.string.should ==
61
114
  "\x10\x2A"+
62
115
  "\x00\x06MQIsdp"+
63
- "\x03\xC2\x00\x0a\x00"+
64
- "\x08myclient"+
116
+ "\x03\xC2\x00\x0f"+
117
+ "\x00\x08myclient"+
65
118
  "\x00\x08username"+
66
119
  "\x00\x08password"
67
120
  end
@@ -79,74 +132,74 @@ describe MQTT::Client do
79
132
  @client.instance_variable_set(:@socket, @socket)
80
133
  IO.stubs(:select).returns([[@socket], [], []])
81
134
  end
82
-
135
+
83
136
  it "should not throw an exception for a successful CONNACK packet" do
84
137
  @socket.write("\x20\x02\x00\x00")
85
138
  @socket.rewind
86
139
  lambda { @client.send(:receive_connack) }.should_not raise_error
87
140
  end
88
-
141
+
89
142
  it "should throw an exception if the packet type isn't CONNACK" do
90
143
  @socket.write("\xD0\x00")
91
144
  @socket.rewind
92
145
  lambda { @client.send(:receive_connack) }.should raise_error(MQTT::ProtocolException)
93
146
  end
94
-
147
+
95
148
  it "should throw an exception if the CONNACK packet return code is 'unacceptable protocol version'" do
96
149
  @socket.write("\x20\x02\x00\x01")
97
150
  @socket.rewind
98
151
  lambda { @client.send(:receive_connack) }.should raise_error(MQTT::ProtocolException, /unacceptable protocol version/i)
99
152
  end
100
-
153
+
101
154
  it "should throw an exception if the CONNACK packet return code is 'client identifier rejected'" do
102
155
  @socket.write("\x20\x02\x00\x02")
103
156
  @socket.rewind
104
157
  lambda { @client.send(:receive_connack) }.should raise_error(MQTT::ProtocolException, /client identifier rejected/i)
105
158
  end
106
-
159
+
107
160
  it "should throw an exception if the CONNACK packet return code is 'broker unavailable'" do
108
161
  @socket.write("\x20\x02\x00\x03")
109
162
  @socket.rewind
110
163
  lambda { @client.send(:receive_connack) }.should raise_error(MQTT::ProtocolException, /broker unavailable/i)
111
164
  end
112
-
165
+
113
166
  it "should throw an exception if the CONNACK packet return code is an unknown" do
114
167
  @socket.write("\x20\x02\x00\xAA")
115
168
  @socket.rewind
116
169
  lambda { @client.send(:receive_connack) }.should raise_error(MQTT::ProtocolException, /connection refused/i)
117
170
  end
118
171
  end
119
-
172
+
120
173
  describe "when calling the 'disconnect' method" do
121
174
  before(:each) do
122
175
  @client.instance_variable_set(:@socket, @socket)
123
176
  @client.instance_variable_set(:@read_thread, stub_everything('Read Thread'))
124
177
  end
125
-
178
+
126
179
  it "should not do anything if the socket is already disconnected" do
127
180
  @client.stubs(:connected?).returns(false)
128
181
  @client.disconnect(true)
129
182
  @socket.string.should == ""
130
183
  end
131
-
184
+
132
185
  it "should write a valid DISCONNECT packet to the socket if connected and the send_msg=true an" do
133
186
  @client.stubs(:connected?).returns(true)
134
187
  @client.disconnect(true)
135
188
  @socket.string.should == "\xE0\x00"
136
189
  end
137
-
190
+
138
191
  it "should not write anything to the socket if the send_msg=false" do
139
192
  @client.stubs(:connected?).returns(true)
140
193
  @client.disconnect(false)
141
194
  @socket.string.should be_empty
142
195
  end
143
-
196
+
144
197
  it "should call the close method on the socket" do
145
198
  @socket.expects(:close)
146
199
  @client.disconnect
147
200
  end
148
201
  end
149
-
202
+
150
203
  describe "when calling the 'ping' method" do
151
204
  before(:each) do
152
205
  @client.instance_variable_set(:@socket, @socket)
@@ -163,7 +216,7 @@ describe MQTT::Client do
163
216
  @client.instance_variable_get(:@last_pingreq).should_not == 0
164
217
  end
165
218
  end
166
-
219
+
167
220
  describe "when calling the 'publish' method" do
168
221
  before(:each) do
169
222
  @client.instance_variable_set(:@socket, @socket)
@@ -173,17 +226,17 @@ describe MQTT::Client do
173
226
  @client.publish('topic','payload', false, 0)
174
227
  @socket.string.should == "\x30\x0e\x00\x05topicpayload"
175
228
  end
176
-
229
+
177
230
  it "should write a valid PUBLISH packet to the socket with the retain flag set" do
178
231
  @client.publish('topic','payload', true, 0)
179
232
  @socket.string.should == "\x31\x0e\x00\x05topicpayload"
180
233
  end
181
-
234
+
182
235
  it "should write a valid PUBLISH packet to the socket with the QOS set to 1" do
183
236
  @client.publish('topic','payload', false, 1)
184
237
  @socket.string.should == "\x32\x10\x00\x05topic\x00\x01payload"
185
238
  end
186
-
239
+
187
240
  it "should write a valid PUBLISH packet to the socket with the QOS set to 2" do
188
241
  @client.publish('topic','payload', false, 2)
189
242
  @socket.string.should == "\x34\x10\x00\x05topic\x00\x01payload"
@@ -250,13 +303,13 @@ describe MQTT::Client do
250
303
  @client.unsubscribe('a/b')
251
304
  @socket.string.should == "\xa2\x07\x00\x01\x00\x03a/b"
252
305
  end
253
-
306
+
254
307
  it "should write a valid UNSUBSCRIBE packet to the socket if given a two topic Strings" do
255
308
  @client.unsubscribe('a/b','c/d')
256
309
  @socket.string.should == "\xa2\x0c\x00\x01\x00\x03a/b\x00\x03c/d"
257
310
  end
258
311
  end
259
-
312
+
260
313
  describe "when calling the 'receive_packet' method" do
261
314
  before(:each) do
262
315
  @client.instance_variable_set(:@socket, @socket)
@@ -278,7 +331,7 @@ describe MQTT::Client do
278
331
  @client.send(:receive_packet)
279
332
  @read_queue.size.should == 0
280
333
  end
281
-
334
+
282
335
  it "should send a ping packet if one is due" do
283
336
  IO.expects(:select).returns(nil)
284
337
  @client.instance_variable_set(:@last_pingreq, Time.at(0))
@@ -297,7 +350,7 @@ describe MQTT::Client do
297
350
  MQTT::Packet.stubs(:read).raises(MQTT::Exception)
298
351
  @client.send(:receive_packet)
299
352
  end
300
-
353
+
301
354
  end
302
-
355
+
303
356
  end
@@ -4,5 +4,5 @@ require 'spec_helper'
4
4
  require 'mqtt'
5
5
 
6
6
  describe MQTT::Proxy do
7
-
7
+
8
8
  end
@@ -13,11 +13,11 @@ describe MQTT do
13
13
  it "should be a string" do
14
14
  MQTT::VERSION.should be_a(String)
15
15
  end
16
-
16
+
17
17
  it "should be in the format x.y.z" do
18
18
  MQTT::VERSION.should =~ /^\d{1,2}\.\d{1,2}\.\d{1,2}$/
19
19
  end
20
-
21
- end
20
+
21
+ end
22
22
 
23
23
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mqtt
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 17
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 5
10
- version: 0.0.5
9
+ - 7
10
+ version: 0.0.7
11
11
  platform: ruby
12
12
  authors:
13
13
  - Nicholas J Humfrey
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-01-17 00:00:00 +00:00
18
+ date: 2012-01-19 00:00:00 +00:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency