nutella_lib 0.2.2 → 0.3.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 +4 -4
- data/Gemfile +1 -1
- data/README.md +5 -2
- data/VERSION +1 -1
- data/lib/nutella_lib.rb +1 -1
- data/lib/nutella_lib/core.rb +35 -15
- data/lib/nutella_lib/net.rb +145 -131
- data/lib/simple_mqtt_client/simple_mqtt_client.rb +118 -0
- data/nutella_lib.gemspec +9 -7
- data/test/test_nutella_lib.rb +66 -10
- data/test/test_simple_mqtt_client.rb +55 -0
- metadata +9 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 452fc30366c7f02a64fb608208189e791be03b59
|
4
|
+
data.tar.gz: 9707d639e096aa37f9d34fefba639b198309ddb6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23a92b1be185d721717535403503bba4eb316ac7a771d2b927329b1748e4a60e81334423bda05aa0d1872188b36e3c2ea88bdd21752b7326963ec21b0fe4fe8f
|
7
|
+
data.tar.gz: 98788b6730515f32623ce4ca3fb785221d1d7454117c4fe4eb577b950c667a2d07b7d6cb4ec89fb5ab8ab71e5401207fd43cf52f9ce5230cb252d4def4663c30
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
-
|
2
|
-
nutella
|
1
|
+
[](http://badge.fury.io/rb/nutella_lib)
|
2
|
+
[](https://travis-ci.org/nutella-framework/nutella_lib.rb)
|
3
|
+
|
4
|
+
# nutella_lib.rb
|
5
|
+
nutella library for Ruby
|
3
6
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/lib/nutella_lib.rb
CHANGED
data/lib/nutella_lib/core.rb
CHANGED
@@ -1,37 +1,57 @@
|
|
1
1
|
# This module is the wrapper around the whole nutella library.
|
2
2
|
module Nutella
|
3
3
|
|
4
|
+
|
4
5
|
# Initializes the nutella library
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
@
|
11
|
-
@
|
12
|
-
@mqtt = SimpleMQTTClient.new
|
6
|
+
# @param [String] run_id
|
7
|
+
# @param [String] broker_hostname
|
8
|
+
# @param [String] component_id
|
9
|
+
def self.init(run_id, broker_hostname, component_id)
|
10
|
+
@run_id = run_id
|
11
|
+
@component_id = component_id
|
12
|
+
@resource_id = nil
|
13
|
+
@mqtt = SimpleMQTTClient.new broker_hostname
|
13
14
|
end
|
14
15
|
|
15
16
|
|
16
17
|
# Accessors for module instance variables
|
17
|
-
def
|
18
|
-
def
|
19
|
-
def
|
18
|
+
def self.run_id; @run_id end
|
19
|
+
def self.component_id; @component_id end
|
20
|
+
def self.resource_id; @resource_id end
|
21
|
+
def self.mqtt; @mqtt end
|
22
|
+
|
20
23
|
|
21
24
|
|
22
25
|
# Nutella library modules loading
|
23
|
-
def
|
24
|
-
def
|
26
|
+
def self.net; Nutella::Net end
|
27
|
+
def self.persist; Nutella::Persist end
|
28
|
+
|
25
29
|
|
30
|
+
# Utility functions
|
31
|
+
|
32
|
+
|
33
|
+
# Parse command line arguments
|
34
|
+
def self.parse_args(args)
|
35
|
+
if args.length < 2
|
36
|
+
STDERR.puts "Couldn't read run_id and broker address from the command line, impossible to initialize library!"
|
37
|
+
return
|
38
|
+
end
|
39
|
+
return args[0], args[1]
|
40
|
+
end
|
26
41
|
|
27
|
-
private
|
28
42
|
|
29
43
|
# Extracts the actor name based on the the folder where we are executing
|
30
|
-
def
|
44
|
+
def self.extract_component_id
|
31
45
|
path = Dir.pwd
|
32
46
|
path[path.rindex('/')+1..path.length-1]
|
33
47
|
end
|
34
48
|
|
49
|
+
|
50
|
+
# Sets the resource id
|
51
|
+
def self.set_resource_id( resource_id )
|
52
|
+
@resource_id = resource_id
|
53
|
+
end
|
54
|
+
|
35
55
|
end
|
36
56
|
|
37
57
|
|
data/lib/nutella_lib/net.rb
CHANGED
@@ -1,62 +1,79 @@
|
|
1
1
|
module Nutella
|
2
2
|
|
3
|
-
# This class implements the pub/sub nutella protocol
|
3
|
+
# This class implements the pub/sub and request/response nutella protocol
|
4
4
|
# @author Alessandro Gnoli <tebemis@gmail.com>
|
5
5
|
module Net
|
6
6
|
|
7
|
-
#
|
8
|
-
|
9
|
-
|
7
|
+
# Store the subscriptions and the relative callbacks
|
8
|
+
@subscriptions = []
|
9
|
+
@callbacks = []
|
10
|
+
|
11
|
+
# Subscribe to a channel or to a set of channels if using wildcards
|
12
|
+
#
|
13
|
+
# @param [String] channel the channel we are subscribing to, can be wildcard
|
14
|
+
# @param [Proc] callback a lambda expression that takes as parameters:
|
15
|
+
# - the received message. Messages that are not JSON are discarded.
|
16
|
+
# - the channel the message was received on (in case of wildcard subscription)
|
17
|
+
# - the sender's component_id
|
18
|
+
# - the sender's resource_id (if set by the sender)
|
10
19
|
def Net.subscribe (channel, callback)
|
20
|
+
# Maintain unique subscriptions
|
21
|
+
raise 'You can`t subscribe twice to the same channel`' if @subscriptions.include? channel
|
11
22
|
# Pad the channel
|
12
|
-
new_channel = Nutella.run_id
|
13
|
-
# Subscribe
|
23
|
+
new_channel = "#{Nutella.run_id}/#{channel}"
|
14
24
|
# Depending on what type of channel we are subscribing to (wildcard or simple)
|
15
25
|
# register a different kind of callback
|
16
26
|
if Nutella.mqtt.is_channel_wildcard?(channel)
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
28
|
-
)
|
27
|
+
mqtt_cb = lambda do |mqtt_message, mqtt_channel|
|
28
|
+
# Make sure the message is JSON, if not drop the message
|
29
|
+
begin
|
30
|
+
mqtt_channel.slice!("#{Nutella.run_id}/")
|
31
|
+
type, payload, component_id, resource_id = extract_fields_from_message mqtt_message
|
32
|
+
callback.call(payload, mqtt_channel, component_id, resource_id) if type=='publish'
|
33
|
+
rescue
|
34
|
+
return
|
35
|
+
end
|
36
|
+
end
|
29
37
|
else
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
40
|
-
end
|
41
|
-
)
|
38
|
+
mqtt_cb = lambda do |message|
|
39
|
+
# Make sure the message is JSON, if not drop the message
|
40
|
+
begin
|
41
|
+
type, payload, component_id, resource_id = extract_fields_from_message message
|
42
|
+
callback.call(payload, component_id, resource_id) if type=='publish'
|
43
|
+
rescue
|
44
|
+
return
|
45
|
+
end
|
46
|
+
end
|
42
47
|
end
|
48
|
+
# Subscribe
|
49
|
+
@subscriptions.push channel
|
50
|
+
@callbacks.push mqtt_cb
|
51
|
+
Nutella.mqtt.subscribe(new_channel, mqtt_cb)
|
43
52
|
end
|
44
53
|
|
54
|
+
|
45
55
|
# Unsubscribe from a channel
|
46
56
|
def Net.unsubscribe(channel)
|
57
|
+
idx = @subscriptions.index channel
|
58
|
+
# If we are not subscribed to this channel, return (no error is given)
|
59
|
+
return if idx.nil?
|
47
60
|
# Pad the channel
|
61
|
+
mqtt_cb = @callbacks[idx]
|
48
62
|
new_channel = Nutella.run_id + '/' + channel
|
49
63
|
# Unsubscribe
|
50
|
-
|
64
|
+
@subscriptions.delete_at idx
|
65
|
+
@callbacks.delete_at idx
|
66
|
+
Nutella.mqtt.unsubscribe( new_channel, mqtt_cb )
|
51
67
|
end
|
52
68
|
|
69
|
+
|
53
70
|
# Publishes a message to a channel
|
54
71
|
# Message can be:
|
55
72
|
# empty (equivalent of a GET)
|
56
73
|
# string (the string will be wrapped into a JSON string automatically. Format: {"payload":"<message>"})
|
57
74
|
# hash (the hash will be converted into a JSON string automatically)
|
58
75
|
# json string (the JSON string will be sent as is)
|
59
|
-
def Net.publish(channel, message)
|
76
|
+
def Net.publish(channel, message=nil)
|
60
77
|
# Pad the channel
|
61
78
|
new_channel = Nutella.run_id + '/' + channel
|
62
79
|
# Publish
|
@@ -68,105 +85,92 @@ module Nutella
|
|
68
85
|
end
|
69
86
|
end
|
70
87
|
|
88
|
+
|
71
89
|
# Performs a synchronous request
|
72
90
|
# Message can be:
|
73
91
|
# empty (equivalent of a GET)
|
74
92
|
# string (the string will be wrapped into a JSON string automatically. Format: {"payload":"<message>"})
|
75
93
|
# hash (the hash will be converted into a JSON string automatically)
|
76
94
|
# json string (the JSON string will be sent as is)
|
77
|
-
def Net.sync_req (channel, message=
|
78
|
-
#
|
79
|
-
|
80
|
-
#
|
81
|
-
|
82
|
-
|
83
|
-
rescue
|
84
|
-
STDERR.puts $!
|
85
|
-
return
|
86
|
-
end
|
87
|
-
# Initialize response and response counter
|
88
|
-
ready_to_go = 2
|
95
|
+
def Net.sync_req (channel, message=nil)
|
96
|
+
# Pad channel
|
97
|
+
new_channel = "#{Nutella.run_id}/#{channel}"
|
98
|
+
# Prepare message
|
99
|
+
m, id = prepare_message_for_request message
|
100
|
+
# Initialize response
|
89
101
|
response = nil
|
90
|
-
#
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
end
|
102
|
+
# Prepare callback
|
103
|
+
mqtt_cb = lambda do |mqtt_message|
|
104
|
+
m_id = extract_id_from_message mqtt_message
|
105
|
+
type, payload = extract_fields_from_response mqtt_message
|
106
|
+
if m_id==id && type=='response'
|
107
|
+
response = payload
|
108
|
+
Nutella.mqtt.unsubscribe( new_channel, mqtt_cb )
|
98
109
|
end
|
99
|
-
end
|
100
|
-
#
|
101
|
-
|
110
|
+
end
|
111
|
+
# Subscribe
|
112
|
+
Nutella.mqtt.subscribe( new_channel, mqtt_cb )
|
113
|
+
# Publish message
|
114
|
+
Nutella.mqtt.publish( new_channel, m )
|
102
115
|
# Wait for the response to come back
|
103
|
-
sleep(0.
|
116
|
+
sleep(0.1) while response.nil?
|
104
117
|
response
|
105
118
|
end
|
106
119
|
|
107
|
-
|
120
|
+
|
121
|
+
# Performs an asynchronous request
|
108
122
|
# Message can be:
|
109
123
|
# empty (equivalent of a GET)
|
110
124
|
# string (the string will be wrapped into a JSON string automatically. Format: {"payload":"<message>"})
|
111
125
|
# hash (the hash will be converted into a JSON string automatically)
|
112
126
|
# json string (the JSON string will be sent as is)
|
113
|
-
def Net.async_req (channel, message=
|
114
|
-
#
|
115
|
-
|
116
|
-
#
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
Net.subscribe(channel, lambda do |res|
|
127
|
-
# Check that the message we receive is not the one we are sending ourselves.
|
128
|
-
if res["id"]==id
|
129
|
-
if ready_to_go
|
130
|
-
Net.unsubscribe(channel)
|
131
|
-
callback.call(res)
|
132
|
-
else
|
133
|
-
ready_to_go = true
|
134
|
-
end
|
127
|
+
def Net.async_req (channel, message=nil, callback)
|
128
|
+
# Pad channel
|
129
|
+
new_channel = "#{Nutella.run_id}/#{channel}"
|
130
|
+
# Prepare message
|
131
|
+
m, id = prepare_message_for_request message
|
132
|
+
# Initialize response
|
133
|
+
# Prepare callback
|
134
|
+
mqtt_cb = lambda do |message|
|
135
|
+
m_id = extract_id_from_message message
|
136
|
+
type, payload = extract_fields_from_response message
|
137
|
+
if m_id==id && type=='response'
|
138
|
+
callback.call(payload)
|
139
|
+
Nutella.mqtt.unsubscribe( new_channel, mqtt_cb )
|
135
140
|
end
|
136
|
-
end
|
137
|
-
#
|
138
|
-
|
141
|
+
end
|
142
|
+
# Subscribe
|
143
|
+
Nutella.mqtt.subscribe( new_channel, mqtt_cb )
|
144
|
+
# Publish message
|
145
|
+
Nutella.mqtt.publish( new_channel, m )
|
139
146
|
end
|
140
147
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
end
|
149
|
-
# Ignore recently processed requests
|
150
|
-
if @last_requests.nil?
|
151
|
-
@last_requests = Set.new
|
152
|
-
end
|
153
|
-
if @last_requests.include?(id)
|
154
|
-
@last_requests.delete(id)
|
155
|
-
return
|
156
|
-
end
|
157
|
-
@last_requests.add(id)
|
158
|
-
req.delete("id")
|
159
|
-
res = handler.call(req)
|
148
|
+
|
149
|
+
|
150
|
+
# Handle requests
|
151
|
+
def Net.handle_requests( channel, callback)
|
152
|
+
# Pad the channel
|
153
|
+
new_channel = "#{Nutella.run_id}/#{channel}"
|
154
|
+
mqtt_cb = lambda do |request|
|
160
155
|
begin
|
161
|
-
|
162
|
-
|
156
|
+
# Extract nutella fields
|
157
|
+
type, payload, component_id, resource_id = extract_fields_from_message request
|
158
|
+
id = extract_id_from_message request
|
159
|
+
# Only handle requests that have proper id set
|
160
|
+
return if type!='request' || id.nil?
|
161
|
+
m = Net.prepare_message_for_response( callback.call( payload, component_id, resource_id ), id )
|
162
|
+
Nutella.mqtt.publish( new_channel, m )
|
163
|
+
# Assemble the response and check that it's proper JSON
|
163
164
|
rescue
|
164
|
-
|
165
|
+
return
|
165
166
|
end
|
166
|
-
end
|
167
|
+
end
|
168
|
+
# Subscribe to the channel
|
169
|
+
Nutella.mqtt.subscribe(new_channel, mqtt_cb)
|
167
170
|
end
|
168
171
|
|
169
172
|
|
173
|
+
# Listens for incoming messages
|
170
174
|
def Net.listen
|
171
175
|
begin
|
172
176
|
sleep
|
@@ -178,37 +182,47 @@ module Nutella
|
|
178
182
|
|
179
183
|
private
|
180
184
|
|
181
|
-
def Net.
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
185
|
+
def Net.extract_fields_from_message(message)
|
186
|
+
mh = JSON.parse(message)
|
187
|
+
from = mh['from'].split('/')
|
188
|
+
r_id = from.length==1 ? nil : from[1]
|
189
|
+
return mh['type'], mh['payload'], from[0], r_id
|
190
|
+
end
|
191
|
+
|
192
|
+
def Net.extract_id_from_message( message )
|
193
|
+
mh = JSON.parse(message)
|
194
|
+
mh['id']
|
195
|
+
end
|
196
|
+
|
197
|
+
def Net.extract_fields_from_response( message )
|
198
|
+
mh = JSON.parse(message)
|
199
|
+
return mh['type'], mh['payload']
|
200
|
+
end
|
201
|
+
|
202
|
+
def Net.prepare_message_for_publish( message )
|
203
|
+
from = Nutella.resource_id.nil? ? Nutella.component_id : "#{Nutella.component_id}/#{Nutella.resource_id}"
|
204
|
+
if message.nil?
|
205
|
+
return {type: 'publish', from: from}.to_json
|
193
206
|
end
|
194
|
-
payload
|
207
|
+
{type: 'publish', from: from, payload: message}.to_json
|
195
208
|
end
|
196
209
|
|
197
|
-
def Net.
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
210
|
+
def Net.prepare_message_for_response( message, id )
|
211
|
+
from = Nutella.resource_id.nil? ? Nutella.component_id : "#{Nutella.component_id}/#{Nutella.resource_id}"
|
212
|
+
if message.nil?
|
213
|
+
return {id: id, type: 'response', from: from}.to_json
|
214
|
+
end
|
215
|
+
{id: id, type: 'response', from: from, payload: message}.to_json
|
216
|
+
end
|
217
|
+
|
218
|
+
def Net.prepare_message_for_request( message )
|
219
|
+
from = Nutella.resource_id.nil? ? Nutella.component_id : "#{Nutella.component_id}/#{Nutella.resource_id}"
|
220
|
+
if message.nil?
|
221
|
+
return {id: message.hash, type: 'request', from: from}.to_json, message.hash
|
209
222
|
end
|
210
|
-
payload
|
223
|
+
return {id: message.hash, type: 'request', from: from, payload: message}.to_json, message.hash
|
211
224
|
end
|
212
225
|
|
226
|
+
|
213
227
|
end
|
214
228
|
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'mqtt'
|
2
|
+
|
3
|
+
# Simple asynchronous MQTT client
|
4
|
+
# @author Alessandro Gnoli <tebemis@gmail.com>
|
5
|
+
class SimpleMQTTClient
|
6
|
+
|
7
|
+
# Creates a new MQTT client
|
8
|
+
# @param [String] host the hostname of the MQTT broker we are connecting to
|
9
|
+
# @param [String] client_id the **unique** client identifier
|
10
|
+
def initialize(host, client_id=nil)
|
11
|
+
@host = host
|
12
|
+
@channels = Hash.new
|
13
|
+
@client = client_id.nil? ? MQTT::Client.connect(:host => host) : MQTT::Client.connect(host: host, client_id: client_id)
|
14
|
+
@thread = Thread.new('mqtt') do
|
15
|
+
@client.get do |channel, message|
|
16
|
+
cbs = get_callbacks channel
|
17
|
+
# If there is no callback (cb=nil) do nothing, otherwise call the right callback:
|
18
|
+
# single channel callback with one parameter, wildcard channel callback with two.
|
19
|
+
unless cbs.nil?
|
20
|
+
(@channels.has_key? channel) ? cbs.each { |cb| cb.call(message) } : cbs.each { |cb| cb.call(message, channel) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Subscribes to a channel and registers a callback
|
27
|
+
# Single channel callbacks take only one parameter: the received message
|
28
|
+
# Wildcard callbacks take two parameters: the received message and the channel the message was sent to
|
29
|
+
# @param [String] channel the channel or filter we are subscribing to
|
30
|
+
# @param [Proc] callback the callback that gets called
|
31
|
+
# whenever a messages is received
|
32
|
+
def subscribe(channel, callback)
|
33
|
+
if @channels.include?(channel)
|
34
|
+
@channels[channel] << callback
|
35
|
+
else
|
36
|
+
@channels[channel]=[callback]
|
37
|
+
@client.subscribe channel
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Un-subscribes a specific callback from a channel
|
42
|
+
# @param [String] channel the channel we are un-subscribing from
|
43
|
+
# @param [Proc] callback the specific callback we want to remove
|
44
|
+
def unsubscribe(channel, callback)
|
45
|
+
if @channels.include? channel
|
46
|
+
@channels[channel].delete(callback)
|
47
|
+
end
|
48
|
+
if @channels[channel].empty?
|
49
|
+
@client.unsubscribe channel
|
50
|
+
@channels.delete(channel)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns the a hash of all the channels this client is currently subscribed to
|
55
|
+
# with relative callbacks
|
56
|
+
# @return [Hash] the hash of all the channels this client is currently subscribed to and relative callbacks
|
57
|
+
def get_subscribed_channels
|
58
|
+
@channels
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns true
|
62
|
+
# @return [Boolean] true if the channel is a wildcard channel. See MQTT specification for wildcard channels
|
63
|
+
# {http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718106 here}
|
64
|
+
# @param [String] channel the channel we are testing for wildcard
|
65
|
+
def is_channel_wildcard?( channel )
|
66
|
+
channel.include?('#') || channel.include?('+')
|
67
|
+
end
|
68
|
+
|
69
|
+
# Publishes a message to a channel
|
70
|
+
# @param [String] channel the channel we are publishing to
|
71
|
+
# @param [String] message the message we are publishing
|
72
|
+
def publish(channel, message)
|
73
|
+
@client.publish(channel, message)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Disconnects this simple MQTT client instance from the broker
|
77
|
+
def disconnect
|
78
|
+
@client.disconnect
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
# Gets the right callback associated to a channel
|
84
|
+
# Specific callback gets precedence over wildcards
|
85
|
+
def get_callbacks(channel)
|
86
|
+
# First try to see if a callback for the exact channel exists
|
87
|
+
return @channels[channel] if @channels.has_key? channel
|
88
|
+
# If it doesn't then let's try to find a wildcard match
|
89
|
+
pattern = wildcard_match channel
|
90
|
+
return @channels[pattern] unless pattern.nil?
|
91
|
+
# If there's no exact match or wildcard we have to return nil
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns the wildcard pattern, among the ones we subscribed to, that matches the channel.
|
96
|
+
# This IGNORES exact matches!!!
|
97
|
+
# It returns nil if the channel doesn't match any of the channels/filers we are subscribed to.
|
98
|
+
def wildcard_match(channel)
|
99
|
+
@channels.keys.each do |pattern|
|
100
|
+
return pattern if matches_generic_pattern(channel, pattern)
|
101
|
+
end
|
102
|
+
# If we go through the whole list of channels and there is no generic pattern then return nil
|
103
|
+
nil
|
104
|
+
end
|
105
|
+
|
106
|
+
# Returns true if the string matches a pattern (including the exact pattern)
|
107
|
+
def matches_generic_pattern(str, pattern)
|
108
|
+
# If multi-level wildcard is the only character in pattern, then any string will match
|
109
|
+
return true if pattern=='#'
|
110
|
+
# Handle all other multi-level wildcards
|
111
|
+
p_wo_wildcard = pattern[0..-2]
|
112
|
+
str_wo_details = str[0..pattern.length-2]
|
113
|
+
return true if pattern[-1, 1]=='#' && p_wo_wildcard==str_wo_details
|
114
|
+
# TODO Handle single-level wildcards (+)
|
115
|
+
false
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
data/nutella_lib.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: nutella_lib 0.
|
5
|
+
# stub: nutella_lib 0.3.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "nutella_lib"
|
9
|
-
s.version = "0.
|
9
|
+
s.version = "0.3.0"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib"]
|
13
13
|
s.authors = ["Alessandro Gnoli"]
|
14
|
-
s.date = "
|
14
|
+
s.date = "2015-02-17"
|
15
15
|
s.description = "Implements the nutella protocol and exposes it natively to ruby developers"
|
16
16
|
s.email = "tebemis@gmail.com"
|
17
17
|
s.extra_rdoc_files = [
|
@@ -32,9 +32,11 @@ Gem::Specification.new do |s|
|
|
32
32
|
"lib/nutella_lib/net.rb",
|
33
33
|
"lib/nutella_lib/noext.rb",
|
34
34
|
"lib/nutella_lib/persist.rb",
|
35
|
+
"lib/simple_mqtt_client/simple_mqtt_client.rb",
|
35
36
|
"nutella_lib.gemspec",
|
36
37
|
"test/helper.rb",
|
37
|
-
"test/test_nutella_lib.rb"
|
38
|
+
"test/test_nutella_lib.rb",
|
39
|
+
"test/test_simple_mqtt_client.rb"
|
38
40
|
]
|
39
41
|
s.homepage = "https://github.com/nutella-framework/nutella_lib.rb"
|
40
42
|
s.licenses = ["MIT"]
|
@@ -45,7 +47,7 @@ Gem::Specification.new do |s|
|
|
45
47
|
s.specification_version = 4
|
46
48
|
|
47
49
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
48
|
-
s.add_runtime_dependency(%q<
|
50
|
+
s.add_runtime_dependency(%q<mqtt>, [">= 0.3", "~> 0.3"])
|
49
51
|
s.add_development_dependency(%q<shoulda>, [">= 3", "~> 3"])
|
50
52
|
s.add_development_dependency(%q<minitest>, [">= 5", "~> 5.4"])
|
51
53
|
s.add_development_dependency(%q<yard>, [">= 0.8.7", "~> 0.8"])
|
@@ -54,7 +56,7 @@ Gem::Specification.new do |s|
|
|
54
56
|
s.add_development_dependency(%q<jeweler>, [">= 2.0.1", "~> 2.0.1"])
|
55
57
|
s.add_development_dependency(%q<simplecov>, [">= 0", "~> 0"])
|
56
58
|
else
|
57
|
-
s.add_dependency(%q<
|
59
|
+
s.add_dependency(%q<mqtt>, [">= 0.3", "~> 0.3"])
|
58
60
|
s.add_dependency(%q<shoulda>, [">= 3", "~> 3"])
|
59
61
|
s.add_dependency(%q<minitest>, [">= 5", "~> 5.4"])
|
60
62
|
s.add_dependency(%q<yard>, [">= 0.8.7", "~> 0.8"])
|
@@ -64,7 +66,7 @@ Gem::Specification.new do |s|
|
|
64
66
|
s.add_dependency(%q<simplecov>, [">= 0", "~> 0"])
|
65
67
|
end
|
66
68
|
else
|
67
|
-
s.add_dependency(%q<
|
69
|
+
s.add_dependency(%q<mqtt>, [">= 0.3", "~> 0.3"])
|
68
70
|
s.add_dependency(%q<shoulda>, [">= 3", "~> 3"])
|
69
71
|
s.add_dependency(%q<minitest>, [">= 5", "~> 5.4"])
|
70
72
|
s.add_dependency(%q<yard>, [">= 0.8.7", "~> 0.8"])
|
data/test/test_nutella_lib.rb
CHANGED
@@ -2,21 +2,77 @@ require 'helper'
|
|
2
2
|
|
3
3
|
class TestNutellaLib < MiniTest::Test
|
4
4
|
|
5
|
-
|
6
|
-
#
|
5
|
+
|
6
|
+
# def test_connect_and_send_receive_messages_correctly
|
7
|
+
# cb_executed = false
|
8
|
+
# nutella.init('my_run_id', 'ltg.evl.uic.edu', 'my_bot_component')
|
9
|
+
# nutella.set_resource_id 'my_resource_id'
|
10
|
+
# cb = lambda do |message, component_id, resource_id|
|
11
|
+
# cb_executed = true
|
12
|
+
# puts "Received message from #{component_id}/#{resource_id}. Message: #{message}"
|
13
|
+
# end
|
14
|
+
# nutella.net.subscribe('demo1', cb)
|
15
|
+
# sleep 1
|
16
|
+
# nutella.net.publish('demo1', 'test_message')
|
17
|
+
# # Make sure we wait for the message to be delivered
|
18
|
+
# sleep 1
|
19
|
+
# assert cb_executed
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
#
|
23
|
+
# def test_connect_and_send_receive_wildcard_messages_correctly
|
24
|
+
# cb_executed = false
|
25
|
+
# nutella.init('my_run_id', 'ltg.evl.uic.edu', 'my_bot_component')
|
26
|
+
# nutella.set_resource_id 'my_resource_id'
|
27
|
+
# cb = lambda do |message, channel, component_id, resource_id|
|
28
|
+
# cb_executed = true
|
29
|
+
# puts "Received message on #{channel} from #{component_id}/#{resource_id}. Message: #{message}"
|
30
|
+
# end
|
31
|
+
# nutella.net.subscribe('demo1/#', cb)
|
32
|
+
# sleep 1
|
33
|
+
# nutella.net.publish('demo1/demo', 'test_message')
|
34
|
+
# # Make sure we wait for the message to be delivered
|
35
|
+
# sleep 1
|
36
|
+
# assert cb_executed
|
7
37
|
# end
|
8
38
|
|
9
|
-
|
10
|
-
#
|
39
|
+
|
40
|
+
# def test_multiple_subscriptions
|
41
|
+
# nutella.init('my_run_id', 'ltg.evl.uic.edu', 'my_bot_component')
|
42
|
+
# nutella.set_resource_id 'my_resource_id'
|
43
|
+
# cb = lambda do |message, component_id, resource_id|
|
44
|
+
# puts "Received message #{component_id}/#{resource_id}. Message: #{message}"
|
45
|
+
# end
|
46
|
+
# nutella.net.subscribe('demo1', cb)
|
47
|
+
# nutella.net.subscribe('demo1', cb) # This must raise an error
|
11
48
|
# end
|
12
49
|
|
13
50
|
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
51
|
+
# def test_request_response
|
52
|
+
# nutella.init('my_run_id', 'ltg.evl.uic.edu', 'my_bot_component')
|
53
|
+
# nutella.set_resource_id 'my_resource_id'
|
54
|
+
#
|
55
|
+
# nutella.net.subscribe('demo1', lambda do |message, component_id, resource_id|
|
56
|
+
# puts "Received a message from #{component_id}/#{resource_id}. Message: #{message}"
|
57
|
+
# end)
|
58
|
+
#
|
59
|
+
# nutella.net.handle_requests( 'demo1', lambda do |message, component_id, resource_id|
|
60
|
+
# puts "We received a request: message #{message}, from #{component_id}/#{resource_id}"
|
61
|
+
# #Then we are going to return some JSON
|
62
|
+
# {my:'json'}
|
63
|
+
# end)
|
64
|
+
#
|
65
|
+
# response = nutella.net.sync_req( 'demo1', 'my request is a string' )
|
66
|
+
# puts 'Response to sync'
|
67
|
+
# p response
|
68
|
+
#
|
69
|
+
# nutella.net.async_req( 'demo1', 'my request is a string', lambda do |response|
|
70
|
+
# puts 'Response to async'
|
71
|
+
# p response
|
72
|
+
# end)
|
73
|
+
#
|
74
|
+
# nutella.net.listen
|
75
|
+
# end
|
20
76
|
|
21
77
|
|
22
78
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestSimpleRubyMqttClient < MiniTest::Test
|
4
|
+
|
5
|
+
# def test_connect_and_send_receive_messages_correctly
|
6
|
+
# cb_executed = false
|
7
|
+
# sc1 = SimpleMQTTClient.new 'ltg.evl.uic.edu'
|
8
|
+
# cb1 = lambda do |message|
|
9
|
+
# cb_executed = true
|
10
|
+
# assert_equal 'test-message-1', message
|
11
|
+
# sc1.unsubscribe 'demo1', cb1
|
12
|
+
# end
|
13
|
+
# sc1.subscribe('demo1', cb1)
|
14
|
+
#
|
15
|
+
# sc2 = SimpleMQTTClient.new 'ltg.evl.uic.edu'
|
16
|
+
# sc2.publish('demo1', 'test-message-1')
|
17
|
+
# # Make sure we wait for the message to be delivered
|
18
|
+
# sleep(1)
|
19
|
+
# assert cb_executed
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# def test_list_current_subscriptions_correctly
|
23
|
+
# sc3 = SimpleMQTTClient.new 'ltg.evl.uic.edu'
|
24
|
+
# cb2 = lambda {|message| puts message}
|
25
|
+
# cb3 = lambda {|message| puts message}
|
26
|
+
# sc3.subscribe( 'channel_1', cb2 )
|
27
|
+
# sc3.subscribe( 'channel_2', cb2 )
|
28
|
+
# sc3.subscribe( 'channel_3', cb2 )
|
29
|
+
# sc3.subscribe( 'channel_3', cb3 )
|
30
|
+
# assert_equal sc3.get_subscribed_channels['channel_3'].length, 2
|
31
|
+
# sc3.unsubscribe( 'channel_3', cb2 )
|
32
|
+
# assert_equal sc3.get_subscribed_channels['channel_3'].length, 1
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# def test_recognize_wildcard_patters_correctly
|
36
|
+
# sc4 = SimpleMQTTClient.new 'ltg.evl.uic.edu'
|
37
|
+
# sc4.subscribe('run_id/#', lambda {|m| puts m})
|
38
|
+
# refute_nil sc4.send(:get_callbacks, 'run_id/one')
|
39
|
+
# refute_nil sc4.send(:get_callbacks, 'run_id/one/two')
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# def test_multiple_subscriptions
|
43
|
+
# sc5 = SimpleMQTTClient.new 'ltg.evl.uic.edu'
|
44
|
+
# total = 0
|
45
|
+
# cb1 = lambda { |message| total += 3; puts "CB1: #{message}"}
|
46
|
+
# cb2 = lambda { |message| total += 1; puts "CB2: #{message}"}
|
47
|
+
# sc5.subscribe('demo2', cb1)
|
48
|
+
# sc5.subscribe('demo2', cb2)
|
49
|
+
# sc6 = SimpleMQTTClient.new 'ltg.evl.uic.edu'
|
50
|
+
# sc6.publish('demo2', 'test-message-2')
|
51
|
+
# # Make sure we wait for the message to be delivered
|
52
|
+
# sleep(1)
|
53
|
+
# assert_equal total, 4
|
54
|
+
# end
|
55
|
+
end
|
metadata
CHANGED
@@ -1,35 +1,35 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nutella_lib
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alessandro Gnoli
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-02-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: mqtt
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.3
|
19
|
+
version: '0.3'
|
20
20
|
- - "~>"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 0.3
|
22
|
+
version: '0.3'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
27
|
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: 0.3
|
29
|
+
version: '0.3'
|
30
30
|
- - "~>"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: 0.3
|
32
|
+
version: '0.3'
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: shoulda
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -191,9 +191,11 @@ files:
|
|
191
191
|
- lib/nutella_lib/net.rb
|
192
192
|
- lib/nutella_lib/noext.rb
|
193
193
|
- lib/nutella_lib/persist.rb
|
194
|
+
- lib/simple_mqtt_client/simple_mqtt_client.rb
|
194
195
|
- nutella_lib.gemspec
|
195
196
|
- test/helper.rb
|
196
197
|
- test/test_nutella_lib.rb
|
198
|
+
- test/test_simple_mqtt_client.rb
|
197
199
|
homepage: https://github.com/nutella-framework/nutella_lib.rb
|
198
200
|
licenses:
|
199
201
|
- MIT
|