nutella_lib 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,271 @@
1
+ module Nutella
2
+ module Net
3
+
4
+ # This module implements the pub/sub and request/response APIs at the application level
5
+ module App
6
+
7
+
8
+ # @!group Application-level communication APIs
9
+
10
+ # Subscribes to a channel or to a set of channels at the application-level.
11
+ #
12
+ # @param [String] channel the application-level channel or filter we are subscribing to. Can contain wildcard(s)
13
+ # @param [Proc] callback a lambda expression that is fired whenever a message is received.
14
+ # The passed callback takes the following parameters:
15
+ # - [String] message: the received message. Messages that are not JSON are discarded.
16
+ # - [String] channel: the application-level channel the message was received on (optional, only for wildcard subscriptions)
17
+ # - [Hash] from: the sender's identifiers (run_id, app_id, component_id and optionally resource_id)
18
+ def App.subscribe (channel, callback)
19
+ Nutella::Net.subscribe_to(channel, callback, Nutella.app_id, nil)
20
+ end
21
+
22
+
23
+ # Un-subscribes from an application-level channel
24
+ #
25
+ # @param [String] channel the application level channel we want to unsubscribe from. Can contain wildcard(s).
26
+ def App.unsubscribe( channel )
27
+ Nutella::Net.unsubscribe_to(channel, Nutella.app_id, nil)
28
+ end
29
+
30
+
31
+ # Publishes a message to an application-level channel
32
+ #
33
+ # @param [String] channel the application-level channel we want to publish the message to. *CANNOT* contain wildcard(s)!
34
+ # @param [Object] message the message we are publishing. This can be,
35
+ # nil/empty (default), a string, a hash and, in general, anything with a .to_json method.
36
+ def App.publish(channel, message=nil)
37
+ Nutella::Net.publish_to(channel, message, Nutella.app_id, nil)
38
+ end
39
+
40
+
41
+ # Performs a synchronous request at the application-level
42
+ #
43
+ # @param [String] channel the application-level channel we want to make the request to. *CANNOT* contain wildcard(s)!
44
+ # @param [Object] message the body of request. This can be,
45
+ # nil/empty (default), a string, a hash and, in general, anything with a .to_json method.
46
+ def App.sync_request ( channel, message=nil )
47
+ Nutella::Net.sync_request_to(channel, message, Nutella.app_id, nil)
48
+ end
49
+
50
+
51
+ # Performs an asynchronous request at the application-level
52
+ #
53
+ # @param [String] channel the application-level channel we want to make the request to. *CANNOT* contain wildcard(s)!
54
+ # @param [Object] message the body of request. This can be,
55
+ # nil/empty (default), a string, a hash and, in general, anything with a .to_json method.
56
+ # @param [Proc] callback the callback that is fired whenever a response is received. It takes one parameter (response).
57
+ def App.async_request ( channel, message=nil, callback )
58
+ Nutella::Net.async_request_to(channel, message, callback, Nutella.app_id, nil)
59
+ end
60
+
61
+
62
+ # Handles requests on a certain application-level channel
63
+ #
64
+ # @param [String] channel tha application-level channel we want to listen for requests on. Can contain wildcard(s).
65
+ # @param [Proc] callback a lambda expression that is fired whenever a message is received.
66
+ # The passed callback takes the following parameters:
67
+ # - [String] the received message (payload). Messages that are not JSON are discarded.
68
+ # - [Hash] the sender's identifiers (run_id, app_id, component_id and optionally resource_id)
69
+ # - [*returns* Hash] The response sent back to the client that performed the request. Whatever is returned by the callback is marshaled into a JSON string and sent via MQTT.
70
+ def App.handle_requests( channel, callback )
71
+ Nutella::Net.handle_requests_on(channel, callback, Nutella.app_id, nil)
72
+ end
73
+
74
+ # @!endgroup
75
+
76
+
77
+ # @!group Application-level APIs to communicate at the run-level
78
+
79
+ # Allows application-level APIs to subscribe to a run-level channel within a specific run
80
+ #
81
+ # @param [String] run_id the specific run we are subscribing to
82
+ # @param [String] channel the run-level channel we are subscribing to. Can be wildcard.
83
+ # @param [Proc] callback the callback that is fired whenever a message is received on the channel.
84
+ # The passed callback takes the following parameters:
85
+ # - [String] message: the received message. Messages that are not JSON are discarded.
86
+ # - [String] channel: the application-level channel the message was received on (optional, only for wildcard subscriptions)
87
+ # - [Hash] from: the sender's identifiers (run_id, app_id, component_id and optionally resource_id)
88
+ def App.subscribe_to_run( run_id, channel, callback )
89
+ Nutella::Net.subscribe_to(channel, callback, Nutella.app_id, run_id)
90
+ end
91
+
92
+
93
+ # Allows application-level APIs to unsubscribe from a run-level channel within a specific run
94
+ #
95
+ # @param [String] run_id the specific run we are un-subscribing from
96
+ # @param [String] channel the run-level channel we want to unsubscribe from. Can contain wildcard(s).
97
+ def App.unsubscribe_to_run( run_id, channel )
98
+ Nutella::Net.unsubscribe_to(channel, Nutella.app_id, run_id)
99
+ end
100
+
101
+
102
+ # Allows application-level APIs to publish to a run-level channel within a specific run
103
+ #
104
+ # @param [String] run_id the specific run we are publishing to
105
+ # @param [String] channel the run-level channel we want to publish the message to. *CANNOT* contain wildcard(s)!
106
+ # @param [String] message the message we are publishing. This can be,
107
+ # nil/empty (default), a string, a hash and, in general, anything with a .to_json method.
108
+ def App.publish_to_run( run_id, channel, message )
109
+ Nutella::Net.publish_to(channel, message, Nutella.app_id, run_id)
110
+ end
111
+
112
+
113
+ # Allows application-level APIs to make a synchronous request to a run-level channel within a specific run
114
+ #
115
+ # @param [String] run_id the specific run we are making the request to
116
+ # @param [String] channel the channel we want to make the request to. *CANNOT* contain wildcard(s)!
117
+ # @param [Object] request the body of request. This can be,
118
+ # nil/empty (default), a string, a hash and, in general, anything with a .to_json method.
119
+ def App.sync_request_to_run( run_id, channel, request)
120
+ Nutella::Net.sync_request_to(channel, request, Nutella.app_id, run_id)
121
+ end
122
+
123
+
124
+ # Allows application-level APIs to make an asynchronous request to a run-level channel within a specific run
125
+ #
126
+ # @param [String] run_id the specific run we are making the request to
127
+ # @param [String] channel the channel we want to make the request to. *CANNOT* contain wildcard(s)!
128
+ # @param [Object] request the body of request. This can be,
129
+ # nil/empty (default), a string, a hash and, in general, anything with a .to_json method.
130
+ # @param [Proc] callback the callback that is fired whenever a response is received. It takes one parameter (response).
131
+ def App.async_request_to_run( run_id, channel, request, callback)
132
+ Nutella::Net.async_request_to(channel, request, callback, Nutella.app_id, run_id)
133
+ end
134
+
135
+
136
+ # Allows application-level APIs to handle requests on a run-level channel within a specific run
137
+ #
138
+ # @param [String] run_id the specific run requests are coming from
139
+ # @param [String] channel we want to listen for requests on. Can contain wildcard(s).
140
+ # @param [Proc] callback a lambda expression that is fired whenever a message is received.
141
+ # The passed callback takes the following parameters:
142
+ # - [String] the received message (payload). Messages that are not JSON are discarded.
143
+ # - [Hash] the sender's identifiers (run_id, app_id, component_id and optionally resource_id)
144
+ # - [*returns* Hash] The response sent back to the client that performed the request. Whatever is returned by the callback is marshaled into a JSON string and sent via MQTT.
145
+ def App.handle_requests_on_run( run_id, channel, callback )
146
+ Nutella::Net.handle_requests_on(channel, callback, Nutella.app_id, run_id)
147
+ end
148
+
149
+ # @!endgroup
150
+
151
+
152
+ # @!group Application level APIs to communicate at the run-level (broadcast)
153
+
154
+ # Allows application-level APIs to subscribe to a run-level channel *for ALL runs*
155
+ #
156
+ # @param [String] channel the run-level channel we are subscribing to. Can be wildcard.
157
+ # @param [Proc] callback the callback that is fired whenever a message is received on the channel.
158
+ # The passed callback takes the following parameters:
159
+ # - [String] message: the received message. Messages that are not JSON are discarded.
160
+ # - [String] run_id: the run_id of the channel the message was sent on
161
+ # - [Hash] from: the sender's identifiers (run_id, app_id, component_id and optionally resource_id)
162
+ def App.subscribe_to_all_runs( channel, callback )
163
+ # Check the passed callback has the right number of arguments
164
+ raise 'You need to pass a callback with 3 parameters (payload, run_id, from) when subscribing to all runs!' if callback.parameters.length!=3
165
+ # Pad channel
166
+ padded_channel = Nutella::Net.pad_channel(channel, Nutella.app_id, '+')
167
+ mqtt_cb = lambda do |mqtt_message, mqtt_channel|
168
+ begin
169
+ type, from, payload, _ = Nutella::Net.extract_fields_from_message mqtt_message
170
+ run_id = extract_run_id_from_ch(Nutella.app_id, mqtt_channel)
171
+ callback.call(payload, run_id, from) if type=='publish'
172
+ rescue JSON::ParserError
173
+ # Make sure the message is JSON, if not drop the message
174
+ return
175
+ rescue
176
+ # Check the passed callback has the right number of arguments
177
+ STDERR.puts "The callback you passed to subscribe has the #{$!}: it needs 'payload', 'run_id' and 'from'"
178
+ end
179
+ end
180
+ # Add to subscriptions, save mqtt callback and subscribe
181
+ Nutella::Net.subscriptions.push padded_channel
182
+ Nutella::Net.callbacks.push mqtt_cb
183
+ Nutella.mqtt.subscribe( padded_channel, mqtt_cb )
184
+ end
185
+
186
+
187
+ # Allows application-level APIs to unsubscribe from a run-level channel *for ALL runs*
188
+ #
189
+ # @param [String] channel the run-level channel we want to unsubscribe from. Can contain wildcard(s).
190
+ def App.unsubscribe_from_all_runs( channel )
191
+ Nutella::Net.unsubscribe_to(channel, Nutella.app_id, '+')
192
+ end
193
+
194
+
195
+ # Allows application-level APIs to publish a message to a run-level channel *for ALL runs*
196
+ #
197
+ # @param [String] channel the run-level channel we want to publish the message to. *CANNOT* contain wildcard(s)!
198
+ # @param [Object] message the message we are publishing. This can be,
199
+ # nil/empty (default), a string, a hash and, in general, anything with a .to_json method.
200
+ def App.publish_to_all_runs( channel, message )
201
+ Nutella.app_runs_list.each do |run_id|
202
+ Nutella::Net.publish_to(channel, message, Nutella.app_id, run_id)
203
+ end
204
+ end
205
+
206
+
207
+ # Allows application-level APIs to send a request to a run-level channel *for ALL runs*
208
+ #
209
+ # @param [String] channel the run-level channel we want to make the request to. *CANNOT* contain wildcard(s)!
210
+ # @param [Object] request the body of request. This can be,
211
+ # nil/empty (default), a string, a hash and, in general, anything with a .to_json method.
212
+ # @param [Proc] callback the callback that is fired whenever a response is received. It takes one parameter (response).
213
+ def App.async_request_to_all_runs(channel, request, callback)
214
+ Nutella.app_runs_list.each do |run_id|
215
+ Nutella::Net.async_request_to(channel, request, callback, Nutella.app_id, run_id)
216
+ end
217
+ end
218
+
219
+
220
+ # Allows application-level APIs to handle requests to a run-level channel *for ALL runs*
221
+ #
222
+ # @param [String] channel tha run-level channel we want to listen for requests on. Can contain wildcard(s).
223
+ # @param [Proc] callback a lambda expression that is fired whenever a message is received.
224
+ # The passed callback takes the following parameters:
225
+ # - [String] the received message (request). Messages that are not JSON are discarded.
226
+ # - [String] run_id: the run_id of the channel the message was sent on
227
+ # - [Hash] the sender's identifiers (from containing, run_id, app_id, component_id and optionally resource_id)
228
+ # - [*returns* Hash] The response sent back to the client that performed the request. Whatever is returned by the callback is marshaled into a JSON string and sent via MQTT.
229
+ def App.handle_requests_on_all_runs(channel, callback)
230
+ # Check the passed callback has the right number of arguments
231
+ raise 'You need to pass a callback with 3 parameters (request, run_id, from) when handling requests!' if callback.parameters.length!=3
232
+ # Pad channel
233
+ padded_channel = Nutella::Net.pad_channel(channel, Nutella.app_id, '+')
234
+ mqtt_cb = lambda do |request, mqtt_channel|
235
+ begin
236
+ # Extract nutella fields
237
+ type, from, payload, id = Nutella::Net.extract_fields_from_message request
238
+ run_id = extract_run_id_from_ch(Nutella.app_id, mqtt_channel)
239
+ # Only handle requests that have proper id set
240
+ return if type!='request' || id.nil?
241
+ # Execute callback and send response
242
+ m = Net.prepare_message_for_response( callback.call( payload, run_id, from), id )
243
+ Nutella.mqtt.publish( mqtt_channel, m )
244
+ rescue JSON::ParserError
245
+ # Make sure that request contains JSON, if not drop the message
246
+ return
247
+ rescue
248
+ # Check the passed callback has the right number of arguments
249
+ STDERR.puts "The callback you passed to subscribe has the #{$!}: it needs 'request', 'run_id' and 'from'"
250
+ end
251
+ end
252
+ # Subscribe to the channel
253
+ Nutella.mqtt.subscribe( padded_channel, mqtt_cb )
254
+ end
255
+
256
+
257
+ # @!endgroup
258
+
259
+
260
+ private
261
+
262
+ def App.extract_run_id_from_ch( app_id, mqtt_channel )
263
+ head = "/nutella/apps/#{app_id}/runs/"
264
+ mqtt_channel.gsub(head, '').split('/')[0]
265
+ end
266
+
267
+
268
+ end
269
+
270
+ end
271
+ end
@@ -1,9 +1,11 @@
1
1
  require 'json'
2
2
  require 'pstore'
3
3
  require 'fileutils'
4
+ require 'thread'
4
5
 
5
6
  module Nutella
6
7
 
8
+ # Implements basic run-dependent persistence for nutella bots.
7
9
  module Persist
8
10
 
9
11
  # This module exposes a single method to retrieve a JSONStore
@@ -32,6 +34,11 @@ module Nutella
32
34
 
33
35
  class JSONStore < PStore
34
36
 
37
+ def initialize(path)
38
+ super
39
+ @semaphore = Mutex.new
40
+ end
41
+
35
42
  def dump(table)
36
43
  table.to_json
37
44
  end
@@ -40,6 +47,13 @@ module Nutella
40
47
  JSON.parse(content)
41
48
  end
42
49
 
50
+ def transaction(read_only=false, &block)
51
+ @semaphore.synchronize {
52
+ # access shared resource
53
+ super
54
+ }
55
+ end
56
+
43
57
  # Dumps the whole store to hash
44
58
  # example:
45
59
  # store = JSONStore.new("my_file.json")
@@ -68,6 +82,4 @@ module Nutella
68
82
  end
69
83
 
70
84
 
71
- end
72
-
73
-
85
+ end
data/lib/nutella_lib.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'nutella_lib/core'
2
2
  require 'nutella_lib/net'
3
+ require 'nutella_lib/net_app'
3
4
  require 'nutella_lib/persist'
4
5
  require 'simple_mqtt_client/simple_mqtt_client'
5
6
 
@@ -12,15 +13,3 @@ require 'set'
12
13
  unless defined?(Nutella::NO_EXT)
13
14
  require 'nutella_lib/ext/kernel'
14
15
  end
15
-
16
- # Adding a convenience method to the string class
17
- # to test if it contains properly formatted JSON
18
- class String
19
- def is_json?
20
- begin
21
- !!JSON.parse(self)
22
- rescue
23
- false
24
- end
25
- end
26
- end
@@ -1,35 +1,45 @@
1
1
  require 'mqtt'
2
2
 
3
- # Simple asynchronous MQTT client
4
- # @author Alessandro Gnoli <tebemis@gmail.com>
3
+ # A simple MQTT client
5
4
  class SimpleMQTTClient
6
-
5
+
6
+ ## Private instance variables
7
+ # `@channels` is a hash that associates to each channels a set of callbacks
8
+ # `@client` low-level mqtt client
9
+ # `@thread` the packet reader thread
10
+
11
+
7
12
  # Creates a new MQTT client
8
13
  # @param [String] host the hostname of the MQTT broker we are connecting to
9
14
  # @param [String] client_id the **unique** client identifier
10
- def initialize(host, client_id=nil)
11
- @host = host
15
+ def initialize( host, client_id=nil )
12
16
  @channels = Hash.new
13
17
  @client = client_id.nil? ? MQTT::Client.connect(:host => host) : MQTT::Client.connect(host: host, client_id: client_id)
14
18
  @thread = Thread.new('mqtt') do
15
19
  @client.get do |channel, message|
20
+ # Execute all the appropriate callbacks:
21
+ # the ones specific to this channel with a single parameter (message)
22
+ # the ones associated to a wildcard channel, with two parameters (message and channel)
16
23
  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) }
24
+ begin
25
+ cbs.each { |cb| cb.parameters.length==1 ? cb.call(message) : cb.call(message, channel) }
26
+ rescue ArgumentError
27
+ STDERR.puts "The callback you passed for #{channel} has the #{$!}"
21
28
  end
22
29
  end
23
30
  end
24
31
  end
25
32
 
26
33
  # 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
34
+ # Single channel callbacks take only one parameter: the received message.
35
+ # Wildcard callbacks take two parameters: the received message and the channel the message was sent to.
36
+ # It is possible to register multiple callbacks per channel. All of them will be executed whenever a message
37
+ # on that channel is received.
38
+ # Note that overlaps between channel-specific callbacks and wildcard-filters are allowed.
29
39
  # @param [String] channel the channel or filter we are subscribing to
30
40
  # @param [Proc] callback the callback that gets called
31
41
  # whenever a messages is received
32
- def subscribe(channel, callback)
42
+ def subscribe( channel, callback )
33
43
  if @channels.include?(channel)
34
44
  @channels[channel] << callback
35
45
  else
@@ -41,7 +51,7 @@ class SimpleMQTTClient
41
51
  # Un-subscribes a specific callback from a channel
42
52
  # @param [String] channel the channel we are un-subscribing from
43
53
  # @param [Proc] callback the specific callback we want to remove
44
- def unsubscribe(channel, callback)
54
+ def unsubscribe( channel, callback )
45
55
  if @channels.include? channel
46
56
  @channels[channel].delete(callback)
47
57
  end
@@ -51,14 +61,29 @@ class SimpleMQTTClient
51
61
  end
52
62
  end
53
63
 
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
64
+ # Publishes a message to a channel
65
+ # @param [String] channel the channel we are publishing to
66
+ # @param [String] message the message we are publishing
67
+ def publish( channel, message )
68
+ # Check we are not publishing to a wildcard channel
69
+ STDERR.puts 'Can\'t publish to a wildcard channel!' if is_channel_wildcard? channel
70
+ @client.publish(channel, message)
71
+ end
72
+
73
+ # Disconnects this simple MQTT client instance from the broker
74
+ def disconnect
75
+ @thread.exit
76
+ @client.disconnect
77
+ @channels.clear
78
+ end
79
+
80
+ # Returns a hash of all the channels this client is currently subscribed to with relative callbacks
81
+ # @return [Hash] all channels this client is currently subscribed to, and relative callbacks
57
82
  def get_subscribed_channels
58
83
  @channels
59
84
  end
60
85
 
61
- # Returns true
86
+ # Returns true if a channel is a wildcard channel
62
87
  # @return [Boolean] true if the channel is a wildcard channel. See MQTT specification for wildcard channels
63
88
  # {http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718106 here}
64
89
  # @param [String] channel the channel we are testing for wildcard
@@ -66,53 +91,48 @@ class SimpleMQTTClient
66
91
  channel.include?('#') || channel.include?('+')
67
92
  end
68
93
 
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
94
+ private
75
95
 
76
- # Disconnects this simple MQTT client instance from the broker
77
- def disconnect
78
- @client.disconnect
96
+ # Gets all the callbacks associated to a channel
97
+ def get_callbacks( channel )
98
+ cbs = Array.new
99
+ # First, fetch all the channel-specific callbacks...
100
+ cbs.concat @channels[channel] if @channels.has_key? channel
101
+ # ...then fetch the callbacks matching all wildcard-filters
102
+ cbs.concat wildcard_callbacks channel
103
+ cbs
79
104
  end
80
105
 
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
106
+ # Gets all wildcard callbacks associated to a channel
107
+ # Among the filters we are subscribed to, which ones match the channel?
108
+ def wildcard_callbacks( channel )
109
+ # First select all filters
110
+ filters = @channels.keys.select { |ch| is_channel_wildcard? ch}
111
+ # Then select filters that match channel
112
+ matching_filters = filters.select { |filter| matches_wildcard_pattern(channel, filter) }
113
+ # Add all callbacks that are associated to matching filters
114
+ cbs = Array.new
115
+ matching_filters.each { |ch| cbs.concat @channels[ch] }
116
+ cbs
93
117
  end
94
118
 
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
119
+ # Returns true if the string matches a pattern
120
+ # See http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718107
121
+ # for a formal description of the rules
122
+ def matches_wildcard_pattern(str, pattern)
123
+ # First we need to build a regex out of the pattern
124
+ regex = build_regex_from_pattern pattern
125
+ # Then we check if the regex matches the string
126
+ !!(regex =~ str)
104
127
  end
105
128
 
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
129
+ # Escape '/'
130
+ # Substitute '+' for '[^"\/"]+' (a string of one or more characters that is are not '/')
131
+ # Substitute '/#' with '.*' (a string of zero or more characters)
132
+ # Substitute '#' for '.*' (a string of zero or more characters)
133
+ def build_regex_from_pattern( pattern )
134
+ regex_str = pattern.gsub('/','\\/').gsub('+','[^"\/"]+').gsub('\/#','.*').gsub('#','.*')
135
+ Regexp.new regex_str
116
136
  end
117
137
 
118
138
  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.3.1 ruby lib
5
+ # stub: nutella_lib 0.4.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "nutella_lib"
9
- s.version = "0.3.1"
9
+ s.version = "0.4.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 = "2015-02-17"
14
+ s.date = "2015-03-13"
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 = [
@@ -30,17 +30,19 @@ Gem::Specification.new do |s|
30
30
  "lib/nutella_lib/core.rb",
31
31
  "lib/nutella_lib/ext/kernel.rb",
32
32
  "lib/nutella_lib/net.rb",
33
+ "lib/nutella_lib/net_app.rb",
33
34
  "lib/nutella_lib/noext.rb",
34
35
  "lib/nutella_lib/persist.rb",
35
36
  "lib/simple_mqtt_client/simple_mqtt_client.rb",
36
37
  "nutella_lib.gemspec",
37
38
  "test/helper.rb",
38
- "test/test_nutella_lib.rb",
39
+ "test/test_nutella_net.rb",
40
+ "test/test_nutella_net_app.rb",
39
41
  "test/test_simple_mqtt_client.rb"
40
42
  ]
41
43
  s.homepage = "https://github.com/nutella-framework/nutella_lib.rb"
42
44
  s.licenses = ["MIT"]
43
- s.rubygems_version = "2.2.2"
45
+ s.rubygems_version = "2.4.3"
44
46
  s.summary = "nutella protocol library for ruby"
45
47
 
46
48
  if s.respond_to? :specification_version then
@@ -0,0 +1,79 @@
1
+ require 'helper'
2
+
3
+ class TestNutellaNet < MiniTest::Test
4
+
5
+
6
+ # def test_send_receive
7
+ # nutella.init('localhost', 'my_app_id', 'my_run_id' , 'my_component_id')
8
+ # cb_executed = false
9
+ # cb = lambda do |message, from|
10
+ # cb_executed = true
11
+ # puts "Received message from #{from['component_id']}/#{from['resource_id']}. Message: #{message}"
12
+ # end
13
+ # nutella.net.subscribe('demo0', cb)
14
+ # sleep 1
15
+ # nutella.net.publish('demo0', 'test_message')
16
+ # # Make sure we wait for the message to be delivered
17
+ # sleep 1
18
+ # assert cb_executed
19
+ # end
20
+ #
21
+ #
22
+ # def test_send_receive_wildcard
23
+ # cb_executed = false
24
+ # nutella.init('localhost', 'my_app_id', 'my_run_id' , 'my_component_id')
25
+ # nutella.set_resource_id 'my_resource_id_1'
26
+ # cb = lambda do |message, channel, from|
27
+ # cb_executed = true
28
+ # puts "Received message on #{channel} from #{from['component_id']}/#{from['resource_id']}. Message: #{message}"
29
+ # end
30
+ # nutella.net.subscribe('demo1/#', cb)
31
+ # sleep 1
32
+ # nutella.net.publish('demo1/demo', 'test_message')
33
+ # # Make sure we wait for the message to be delivered
34
+ # sleep 1
35
+ # assert cb_executed
36
+ # end
37
+ #
38
+ #
39
+ # def test_multiple_subscriptions
40
+ # nutella.init('localhost', 'my_app_id', 'my_run_id' , 'my_component_id')
41
+ # nutella.set_resource_id 'my_resource_id_2'
42
+ # cb = lambda do |message, from|
43
+ # puts "Received message #{from['component_id']}/#{from['resource_id']}. Message: #{message}"
44
+ # end
45
+ # assert_raises RuntimeError do
46
+ # nutella.net.subscribe('demo2', cb)
47
+ # nutella.net.subscribe('demo2', cb)
48
+ # end
49
+ # nutella.net.unsubscribe('demo2')
50
+ # nutella.net.subscribe('demo2', cb)
51
+ # end
52
+ #
53
+ #
54
+ # def test_request_response
55
+ # nutella.init('localhost', 'my_app_id', 'my_run_id' , 'my_component_id')
56
+ # nutella.set_resource_id 'my_resource_id_3'
57
+ #
58
+ # nutella.net.subscribe('demo3', lambda do |message, from|
59
+ # puts "Received a message from #{from['component_id']}/#{from['resource_id']}. Message: #{message}"
60
+ # end)
61
+ #
62
+ # nutella.net.handle_requests( 'demo3', lambda do |message, from|
63
+ # puts "We received a request: message #{message}, from #{from['component_id']}/#{from['resource_id']}."
64
+ # #Then we are going to return some random JSON
65
+ # {my:'json'}
66
+ # end)
67
+ #
68
+ # response = nutella.net.sync_request( 'demo3', 'my request is a string' )
69
+ # assert_equal({'my' => 'json'}, response)
70
+ #
71
+ # nutella.net.async_request( 'demo3', 'my request is a string', lambda do |response|
72
+ # assert_equal({'my' => 'json'}, response)
73
+ # end)
74
+ #
75
+ # sleep(2)
76
+ # end
77
+
78
+
79
+ end