nutella_lib 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/nutella_lib.svg)](http://badge.fury.io/rb/nutella_lib)
|
2
|
+
[![Build Status](https://travis-ci.org/nutella-framework/nutella_lib.rb.svg)](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
|