nutella_framework 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -0
- data/README.md +3 -4
- data/VERSION +1 -1
- data/data/startup +3 -3
- data/framework_components/example_framework_interface/dandelion-flowers-card.jpg +0 -0
- data/framework_components/example_framework_interface/index.html +18 -0
- data/framework_components/main_interface/main_interface_bot.rb +183 -0
- data/framework_components/main_interface/public/index.html +54 -0
- data/framework_components/main_interface/views/index.erb +63 -0
- data/framework_components/order.json.example +6 -0
- data/framework_components/runs_list_bot/app_runs_list_bot.rb +15 -0
- data/lib/{core/commands → commands}/broker.rb +2 -2
- data/lib/{core/commands → commands}/checkup.rb +2 -2
- data/lib/commands/compile.rb +13 -0
- data/lib/commands/dependencies.rb +13 -0
- data/lib/commands/help.rb +35 -0
- data/lib/{core/commands → commands}/install.rb +19 -15
- data/lib/commands/meta/command.rb +19 -0
- data/lib/commands/meta/run_command.rb +114 -0
- data/lib/{core → commands/meta}/template_command.rb +1 -1
- data/lib/commands/new.rb +60 -0
- data/lib/commands/runs.rb +54 -0
- data/lib/commands/start.rb +321 -0
- data/lib/commands/stop.rb +101 -0
- data/lib/{core/commands → commands}/template.rb +59 -39
- data/lib/config/current_app_utils.rb +51 -0
- data/lib/config/persisted_hash.rb +14 -12
- data/lib/config/runlist.rb +116 -16
- data/lib/{cli → core}/nutella_cli.rb +1 -1
- data/lib/core/nutella_core.rb +2 -6
- data/lib/nutella_framework.rb +5 -3
- data/lib/nutella_lib_framework/api.rb +333 -0
- data/lib/tmux/tmux.rb +76 -0
- data/nutella_framework.gemspec +42 -29
- data/test/commands/test_cmd_cli_params_parsing.rb +56 -0
- data/test/commands/test_command_template.rb +31 -0
- data/test/config/test_current_app_utils.rb +34 -0
- data/test/config/test_persisted_hash.rb +48 -0
- data/test/config/test_runlist.rb +15 -23
- data/test/framework_apis/test_framework_api.rb +74 -0
- metadata +74 -27
- data/actors/main_interface/main_interface_bot.rb +0 -163
- data/actors/main_interface/public/index.html +0 -51
- data/actors/main_interface/views/index.erb +0 -45
- data/lib/config/current_project.rb +0 -58
- data/lib/core/command.rb +0 -12
- data/lib/core/commands/compile.rb +0 -21
- data/lib/core/commands/dependencies.rb +0 -21
- data/lib/core/commands/help.rb +0 -28
- data/lib/core/commands/new.rb +0 -60
- data/lib/core/commands/runs.rb +0 -52
- data/lib/core/commands/start.rb +0 -271
- data/lib/core/commands/stop.rb +0 -100
- data/lib/core/run_command.rb +0 -106
- data/lib/core/tmux.rb +0 -38
- data/test/config/test_config.rb +0 -48
- data/test/config/test_project.rb +0 -34
- data/test/test_run_command.rb +0 -54
- /data/{actors → framework_components}/main_interface/startup +0 -0
- /data/{actors → framework_components}/main_interface/views/not_found_404.erb +0 -0
@@ -0,0 +1,333 @@
|
|
1
|
+
require 'nutella_lib'
|
2
|
+
require 'config/runlist'
|
3
|
+
|
4
|
+
module Nutella
|
5
|
+
|
6
|
+
|
7
|
+
# Initializes this component as a framework component
|
8
|
+
# @param [String] broker_hostname
|
9
|
+
# @param [String] component_id
|
10
|
+
def self.init_as_f_component( broker_hostname, component_id )
|
11
|
+
@app_id = nil
|
12
|
+
@run_id = nil
|
13
|
+
@component_id = component_id
|
14
|
+
@resource_id = nil
|
15
|
+
@mqtt = SimpleMQTTClient.new broker_hostname
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
# Parse command line arguments for framework level components
|
20
|
+
#
|
21
|
+
# @param [Array] args command line arguments array
|
22
|
+
# @return [String] broker
|
23
|
+
def self.parse_f_component_args(args)
|
24
|
+
if args.length < 1
|
25
|
+
STDERR.puts 'Couldn\'t read broker address from the command line, impossible to initialize component!'
|
26
|
+
return
|
27
|
+
end
|
28
|
+
return args[0]
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
module Net
|
33
|
+
|
34
|
+
# Provides access to the net.app sub-module
|
35
|
+
def Net.f; Nutella::Net::Framework end
|
36
|
+
|
37
|
+
|
38
|
+
# This module implements the pub/sub and request/response APIs at the framework-level
|
39
|
+
module Framework
|
40
|
+
|
41
|
+
|
42
|
+
# @!group Framework-level communication APIs
|
43
|
+
|
44
|
+
# Subscribes to a channel or to a set of channels at the framework-level.
|
45
|
+
#
|
46
|
+
# @param [String] channel the framework-level channel or filter we are subscribing to. Can contain wildcard(s)
|
47
|
+
# @param [Proc] callback a lambda expression that is fired whenever a message is received.
|
48
|
+
# The passed callback takes the following parameters:
|
49
|
+
# - [String] message: the received message. Messages that are not JSON are discarded.
|
50
|
+
# - [String] channel: the framework-level channel the message was received on (optional, only for wildcard subscriptions)
|
51
|
+
# - [Hash] from: the sender's identifiers (run_id, app_id, component_id and optionally resource_id)
|
52
|
+
def self.subscribe (channel, callback)
|
53
|
+
Nutella::Net.subscribe_to(channel, callback, nil, nil)
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
# Un-subscribes from a framework-level channel
|
58
|
+
#
|
59
|
+
# @param [String] channel the framework-level channel we want to unsubscribe from. Can contain wildcard(s).
|
60
|
+
def self.unsubscribe( channel )
|
61
|
+
Nutella::Net.unsubscribe_to(channel, nil, nil)
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
# Publishes a message to an framework-level channel
|
66
|
+
#
|
67
|
+
# @param [String] channel the framework-level channel we want to publish the message to. *CANNOT* contain wildcard(s)!
|
68
|
+
# @param [Object] message the message we are publishing. This can be,
|
69
|
+
# nil/empty (default), a string, a hash and, in general, anything with a .to_json method.
|
70
|
+
def self.publish(channel, message=nil)
|
71
|
+
Nutella::Net.publish_to(channel, message, nil, nil)
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
# Performs a synchronous request at the framework-level
|
76
|
+
#
|
77
|
+
# @param [String] channel the framework-level channel we want to make the request to. *CANNOT* contain wildcard(s)!
|
78
|
+
# @param [Object] message the body of request. This can be,
|
79
|
+
# nil/empty (default), a string, a hash and, in general, anything with a .to_json method.
|
80
|
+
def self.sync_request ( channel, message=nil )
|
81
|
+
Nutella::Net.sync_request_to(channel, message, nil, nil)
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
# Performs an asynchronous request at the framework-level
|
86
|
+
#
|
87
|
+
# @param [String] channel the framework-level channel we want to make the request to. *CANNOT* contain wildcard(s)!
|
88
|
+
# @param [Object] message the body of request. This can be,
|
89
|
+
# nil/empty (default), a string, a hash and, in general, anything with a .to_json method.
|
90
|
+
# @param [Proc] callback the callback that is fired whenever a response is received. It takes one parameter (response).
|
91
|
+
def self.async_request ( channel, message=nil, callback )
|
92
|
+
Nutella::Net.async_request_to(channel, message, callback, nil, nil)
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
# Handles requests on a certain framework-level channel
|
97
|
+
#
|
98
|
+
# @param [String] channel tha framework-level channel we want to listen for requests on. Can contain wildcard(s).
|
99
|
+
# @param [Proc] callback a lambda expression that is fired whenever a message is received.
|
100
|
+
# The passed callback takes the following parameters:
|
101
|
+
# - [String] the received message (payload). Messages that are not JSON are discarded.
|
102
|
+
# - [Hash] the sender's identifiers (run_id, app_id, component_id and optionally resource_id)
|
103
|
+
# - [*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.
|
104
|
+
def self.handle_requests( channel, callback )
|
105
|
+
Nutella::Net.handle_requests_on(channel, callback, nil, nil)
|
106
|
+
end
|
107
|
+
|
108
|
+
# @!endgroup
|
109
|
+
|
110
|
+
# ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
111
|
+
|
112
|
+
# @!group Framework-level APIs to communicate at the run-level
|
113
|
+
|
114
|
+
# Allows framework-level APIs to subscribe to a run-level channel within a specific run
|
115
|
+
#
|
116
|
+
# @param [String] app_id the specific application we are subscribing to
|
117
|
+
# @param [String] run_id the specific run we are subscribing to
|
118
|
+
# @param [String] channel the run-level channel we are subscribing to. Can be wildcard.
|
119
|
+
# @param [Proc] callback the callback that is fired whenever a message is received on the channel.
|
120
|
+
# The passed callback takes the following parameters:
|
121
|
+
# - [String] message: the received message. Messages that are not JSON are discarded.
|
122
|
+
# - [String] channel: the framework-level channel the message was received on (optional, only for wildcard subscriptions)
|
123
|
+
# - [Hash] from: the sender's identifiers (run_id, app_id, component_id and optionally resource_id)
|
124
|
+
def self.subscribe_to_run( app_id, run_id, channel, callback )
|
125
|
+
Nutella::Net.subscribe_to(channel, callback, app_id, run_id)
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
# Allows framework-level APIs to unsubscribe from a run-level channel within a specific run
|
130
|
+
#
|
131
|
+
# @param [String] app_id the specific application we are un-subscribing from
|
132
|
+
# @param [String] run_id the specific run we are un-subscribing from
|
133
|
+
# @param [String] channel the run-level channel we want to unsubscribe from. Can contain wildcard(s).
|
134
|
+
def self.unsubscribe_to_run( app_id, run_id, channel )
|
135
|
+
Nutella::Net.unsubscribe_to(channel, app_id, run_id)
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
# Allows framework-level APIs to publish to a run-level channel within a specific run
|
140
|
+
#
|
141
|
+
# @param [String] app_id the specific application we are publishing to
|
142
|
+
# @param [String] run_id the specific run we are publishing to
|
143
|
+
# @param [String] channel the run-level channel we want to publish the message to. *CANNOT* contain wildcard(s)!
|
144
|
+
# @param [String] message the message we are publishing. This can be,
|
145
|
+
# nil/empty (default), a string, a hash and, in general, anything with a .to_json method.
|
146
|
+
def self.publish_to_run( app_id, run_id, channel, message )
|
147
|
+
Nutella::Net.publish_to(channel, message, app_id, run_id)
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
# Allows framework-level APIs to make a synchronous request to a run-level channel within a specific run
|
152
|
+
#
|
153
|
+
# @param [String] app_id the specific application we are making the request to
|
154
|
+
# @param [String] run_id the specific run we are making the request to
|
155
|
+
# @param [String] channel the channel we want to make the request to. *CANNOT* contain wildcard(s)!
|
156
|
+
# @param [Object] request the body of request. This can be,
|
157
|
+
# nil/empty (default), a string, a hash and, in general, anything with a .to_json method.
|
158
|
+
def self.sync_request_to_run( app_id, run_id, channel, request)
|
159
|
+
Nutella::Net.sync_request_to(channel, request, app_id, run_id)
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
# Allows framework-level APIs to make an asynchronous request to a run-level channel within a specific run
|
164
|
+
#
|
165
|
+
# @param [String] app_id the specific application we are making the request to
|
166
|
+
# @param [String] run_id the specific run we are making the request to
|
167
|
+
# @param [String] channel the channel we want to make the request to. *CANNOT* contain wildcard(s)!
|
168
|
+
# @param [Object] request the body of request. This can be,
|
169
|
+
# nil/empty (default), a string, a hash and, in general, anything with a .to_json method.
|
170
|
+
# @param [Proc] callback the callback that is fired whenever a response is received. It takes one parameter (response).
|
171
|
+
def self.async_request_to_run( app_id, run_id, channel, request, callback)
|
172
|
+
Nutella::Net.async_request_to(channel, request, callback, app_id, run_id)
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
# Allows framework-level APIs to handle requests on a run-level channel within a specific run
|
177
|
+
#
|
178
|
+
# @param [String] app_id the specific application requests are coming from
|
179
|
+
# @param [String] run_id the specific run requests are coming from
|
180
|
+
# @param [String] channel we want to listen for requests on. Can contain wildcard(s).
|
181
|
+
# @param [Proc] callback a lambda expression that is fired whenever a message is received.
|
182
|
+
# The passed callback takes the following parameters:
|
183
|
+
# - [String] the received message (payload). Messages that are not JSON are discarded.
|
184
|
+
# - [Hash] the sender's identifiers (run_id, app_id, component_id and optionally resource_id)
|
185
|
+
# - [*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.
|
186
|
+
def self.handle_requests_on_run( app_id, run_id, channel, callback )
|
187
|
+
Nutella::Net.handle_requests_on(channel, callback, app_id, run_id)
|
188
|
+
end
|
189
|
+
|
190
|
+
# @!endgroup
|
191
|
+
|
192
|
+
# ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
193
|
+
|
194
|
+
# @!group Framework-level APIs to communicate at the run-level (broadcast)
|
195
|
+
|
196
|
+
# Allows framework-level APIs to subscribe to a run-level channel *for ALL runs*
|
197
|
+
#
|
198
|
+
# @param [String] channel the run-level channel we are subscribing to. Can be wildcard.
|
199
|
+
# @param [Proc] callback the callback that is fired whenever a message is received on the channel.
|
200
|
+
# The passed callback takes the following parameters:
|
201
|
+
# - [String] message: the received message. Messages that are not JSON are discarded.
|
202
|
+
# - [String] app_id: the app_id of the channel the message was sent on
|
203
|
+
# - [String] run_id: the run_id of the channel the message was sent on
|
204
|
+
# - [Hash] from: the sender's identifiers (run_id, app_id, component_id and optionally resource_id)
|
205
|
+
def self.subscribe_to_all_runs( channel, callback )
|
206
|
+
# Check the passed callback has the right number of arguments
|
207
|
+
raise 'You need to pass a callback with 4 parameters (payload, app_id, run_id, from) when subscribing to all runs!' if callback.parameters.length!=4
|
208
|
+
# Pad channel
|
209
|
+
padded_channel = Nutella::Net.pad_channel(channel, '+', '+')
|
210
|
+
mqtt_cb = lambda do |mqtt_message, mqtt_channel|
|
211
|
+
begin
|
212
|
+
type, from, payload, _ = Nutella::Net.extract_fields_from_message mqtt_message
|
213
|
+
app_id, run_id = self.extract_run_id_and_app_id mqtt_channel
|
214
|
+
callback.call(payload, app_id, run_id, from) if type=='publish'
|
215
|
+
rescue JSON::ParserError
|
216
|
+
# Make sure the message is JSON, if not drop the message
|
217
|
+
return
|
218
|
+
rescue
|
219
|
+
# Check the passed callback has the right number of arguments
|
220
|
+
STDERR.puts "The callback you passed to subscribe has the #{$!}: it needs 'payload', 'app_id', 'run_id' and 'from'"
|
221
|
+
end
|
222
|
+
end
|
223
|
+
# Add to subscriptions, save mqtt callback and subscribe
|
224
|
+
Nutella::Net.subscriptions.push padded_channel
|
225
|
+
Nutella::Net.callbacks.push mqtt_cb
|
226
|
+
Nutella.mqtt.subscribe( padded_channel, mqtt_cb )
|
227
|
+
end
|
228
|
+
|
229
|
+
|
230
|
+
# Allows framework-level APIs to unsubscribe from a run-level channel *for ALL runs*
|
231
|
+
#
|
232
|
+
# @param [String] channel the run-level channel we want to unsubscribe from. Can contain wildcard(s).
|
233
|
+
def self.unsubscribe_from_all_runs( channel )
|
234
|
+
Nutella::Net.unsubscribe_to(channel, '+', '+')
|
235
|
+
end
|
236
|
+
|
237
|
+
|
238
|
+
# Allows framework-level APIs to publish a message to a run-level channel *for ALL runs*
|
239
|
+
#
|
240
|
+
# @param [String] channel the run-level channel we want to publish the message to. *CANNOT* contain wildcard(s)!
|
241
|
+
# @param [Object] message the message we are publishing. This can be,
|
242
|
+
# nil/empty (default), a string, a hash and, in general, anything with a .to_json method.
|
243
|
+
def self.publish_to_all_runs( channel, message )
|
244
|
+
Nutella.runlist.all_runs.each do |app_id, _|
|
245
|
+
Nutella.runlist.runs_for_app(app_id).each do |run_id|
|
246
|
+
Nutella::Net.publish_to(channel, message, app_id, run_id)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
|
252
|
+
# Allows framework-level APIs to send a request to a run-level channel *for ALL runs*
|
253
|
+
#
|
254
|
+
# @param [String] channel the run-level channel we want to make the request to. *CANNOT* contain wildcard(s)!
|
255
|
+
# @param [Object] request the body of request. This can be,
|
256
|
+
# nil/empty (default), a string, a hash and, in general, anything with a .to_json method.
|
257
|
+
# @param [Proc] callback the callback that is fired whenever a response is received. It takes one parameter (response).
|
258
|
+
def self.async_request_to_all_runs(channel, request, callback)
|
259
|
+
Nutella.runlist.all_runs.each do |app_id, _|
|
260
|
+
Nutella.runlist.runs_for_app(app_id).each do |run_id|
|
261
|
+
Nutella::Net.async_request_to(channel, request, callback, app_id, run_id)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
|
267
|
+
# Allows framework-level APIs to handle requests to a run-level channel *for ALL runs*
|
268
|
+
#
|
269
|
+
# @param [String] channel tha run-level channel we want to listen for requests on. Can contain wildcard(s).
|
270
|
+
# @param [Proc] callback a lambda expression that is fired whenever a message is received.
|
271
|
+
# The passed callback takes the following parameters:
|
272
|
+
# - [String] the received message (request). Messages that are not JSON are discarded.
|
273
|
+
# - [String] app_id: the app_id of the channel the request was sent on
|
274
|
+
# - [String] run_id: the run_id of the channel the request was sent on
|
275
|
+
# - [Hash] the sender's identifiers (from containing, run_id, app_id, component_id and optionally resource_id)
|
276
|
+
# - [*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.
|
277
|
+
def self.handle_requests_on_all_runs(channel, callback)
|
278
|
+
# Check the passed callback has the right number of arguments
|
279
|
+
raise 'You need to pass a callback with 4 parameters (request, run_id, from) when handling requests!' if callback.parameters.length!=4
|
280
|
+
# Pad channel
|
281
|
+
padded_channel = Nutella::Net.pad_channel(channel, '+', '+')
|
282
|
+
mqtt_cb = lambda do |request, mqtt_channel|
|
283
|
+
begin
|
284
|
+
# Extract nutella fields
|
285
|
+
type, from, payload, id = Nutella::Net.extract_fields_from_message request
|
286
|
+
app_id, run_id = self.extract_run_id_and_app_id mqtt_channel
|
287
|
+
# Only handle requests that have proper id set
|
288
|
+
return if type!='request' || id.nil?
|
289
|
+
# Execute callback and send response
|
290
|
+
m = Net.prepare_message_for_response( callback.call( payload, app_id, run_id, from), id )
|
291
|
+
Nutella.mqtt.publish( mqtt_channel, m )
|
292
|
+
rescue JSON::ParserError
|
293
|
+
# Make sure that request contains JSON, if not drop the message
|
294
|
+
return
|
295
|
+
rescue
|
296
|
+
# Check the passed callback has the right number of arguments
|
297
|
+
STDERR.puts "The callback you passed to subscribe has the #{$!}: it needs 'request', 'app_id', 'run_id' and 'from'"
|
298
|
+
end
|
299
|
+
end
|
300
|
+
# Subscribe to the channel
|
301
|
+
Nutella.mqtt.subscribe( padded_channel, mqtt_cb )
|
302
|
+
end
|
303
|
+
|
304
|
+
# @!endgroup
|
305
|
+
|
306
|
+
# ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
307
|
+
|
308
|
+
# @!group Framework-level APIs to communicate at the application-level
|
309
|
+
|
310
|
+
# TODO
|
311
|
+
|
312
|
+
# @!endgroup
|
313
|
+
|
314
|
+
# ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
315
|
+
|
316
|
+
# @!group Framework-level APIs to communicate at the application-level (broadcast)
|
317
|
+
|
318
|
+
# TODO
|
319
|
+
|
320
|
+
# @!endgroup
|
321
|
+
|
322
|
+
private
|
323
|
+
|
324
|
+
def self.extract_run_id_and_app_id( mqtt_channel )
|
325
|
+
sp = mqtt_channel.sub('/nutella/apps/', '').split('/')
|
326
|
+
return sp[0], sp[2]
|
327
|
+
end
|
328
|
+
|
329
|
+
|
330
|
+
end
|
331
|
+
|
332
|
+
end
|
333
|
+
end
|
data/lib/tmux/tmux.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
module Nutella
|
2
|
+
|
3
|
+
class Tmux
|
4
|
+
|
5
|
+
def initialize( app_id, run_id )
|
6
|
+
@app_id = app_id
|
7
|
+
@run_id = run_id
|
8
|
+
end
|
9
|
+
|
10
|
+
# Creates a new window (and session if necessary) to start a run-level bot
|
11
|
+
def new_bot_window( bot )
|
12
|
+
# Create session name
|
13
|
+
sn = Tmux.session_name(@app_id, @run_id)
|
14
|
+
# Create session
|
15
|
+
create_tmux_window(sn, bot)
|
16
|
+
# Start bot
|
17
|
+
`tmux send-keys "cd bots/#{bot};./startup #{Nutella.config['broker']} #{@app_id} #{@run_id}" C-m`
|
18
|
+
end
|
19
|
+
|
20
|
+
# Creates a new window (and session if necessary) to start an app-level bot
|
21
|
+
def new_app_bot_window( bot )
|
22
|
+
# Create session name
|
23
|
+
sn = Tmux.app_bot_session_name(@app_id)
|
24
|
+
# Create session
|
25
|
+
create_tmux_window(sn, bot)
|
26
|
+
# Start bot
|
27
|
+
`tmux send-keys "cd bots/#{bot};./startup #{Nutella.config['broker']} #{@app_id}" C-m`
|
28
|
+
end
|
29
|
+
|
30
|
+
# Removes a run-level session associated to a particular run
|
31
|
+
def self.kill_run_session( app_id, run_id )
|
32
|
+
`tmux kill-session -t #{session_name(app_id, run_id)} &> /dev/null`
|
33
|
+
end
|
34
|
+
|
35
|
+
# Removes the app-level session associated to a particular application
|
36
|
+
def self.kill_app_session( app_id )
|
37
|
+
`tmux kill-session -t #{app_bot_session_name( app_id )} &> /dev/null`
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns true if a tmux session with a certain id exists
|
41
|
+
def self.session_exist?( session_id )
|
42
|
+
system( "tmux has-session -t #{session_id} &> /dev/null" )
|
43
|
+
end
|
44
|
+
|
45
|
+
# Builds a session name for run-level session
|
46
|
+
def self.session_name( app_id, run_id )
|
47
|
+
"#{app_id}/#{run_id}"
|
48
|
+
end
|
49
|
+
|
50
|
+
# Builds a session name for an app-level session
|
51
|
+
def self.app_bot_session_name( app_id )
|
52
|
+
"#{app_id}-app-bots"
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def create_tmux_window( session_name, bot )
|
59
|
+
# If a session already exists, simply create a new window (-n) for 'bot'.
|
60
|
+
# -k destroys the window if it can't be created
|
61
|
+
# -P prints info about creation of window
|
62
|
+
# If there is no sessions, let's create one (-s) and, at the same time, create a new window for the bot
|
63
|
+
if defined? @windows
|
64
|
+
`tmux new-window -kP -n #{bot} &> /dev/null`
|
65
|
+
@windows.push bot
|
66
|
+
else
|
67
|
+
`tmux new-session -d -s #{session_name} -n #{bot} &> /dev/null`
|
68
|
+
@windows = [bot]
|
69
|
+
end
|
70
|
+
# Select the last window we launched
|
71
|
+
`tmux select-window -t #{session_name}:#{@windows.length-1} &> /dev/null`
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
data/nutella_framework.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_framework 0.
|
5
|
+
# stub: nutella_framework 0.4.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "nutella_framework"
|
9
|
-
s.version = "0.
|
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-
|
14
|
+
s.date = "2015-03-13"
|
15
15
|
s.description = "Nutella is a framework to create and run classroom narratives"
|
16
16
|
s.email = "tebemis@gmail.com"
|
17
17
|
s.executables = ["nutella"]
|
@@ -27,46 +27,53 @@ Gem::Specification.new do |s|
|
|
27
27
|
"README.md",
|
28
28
|
"Rakefile",
|
29
29
|
"VERSION",
|
30
|
-
"actors/main_interface/main_interface_bot.rb",
|
31
|
-
"actors/main_interface/public/index.html",
|
32
|
-
"actors/main_interface/startup",
|
33
|
-
"actors/main_interface/views/index.erb",
|
34
|
-
"actors/main_interface/views/not_found_404.erb",
|
35
30
|
"bin/nutella",
|
36
31
|
"data/index.html",
|
37
32
|
"data/startup",
|
38
|
-
"
|
33
|
+
"framework_components/example_framework_interface/dandelion-flowers-card.jpg",
|
34
|
+
"framework_components/example_framework_interface/index.html",
|
35
|
+
"framework_components/main_interface/main_interface_bot.rb",
|
36
|
+
"framework_components/main_interface/public/index.html",
|
37
|
+
"framework_components/main_interface/startup",
|
38
|
+
"framework_components/main_interface/views/index.erb",
|
39
|
+
"framework_components/main_interface/views/not_found_404.erb",
|
40
|
+
"framework_components/order.json.example",
|
41
|
+
"framework_components/runs_list_bot/app_runs_list_bot.rb",
|
42
|
+
"lib/commands/broker.rb",
|
43
|
+
"lib/commands/checkup.rb",
|
44
|
+
"lib/commands/compile.rb",
|
45
|
+
"lib/commands/dependencies.rb",
|
46
|
+
"lib/commands/help.rb",
|
47
|
+
"lib/commands/install.rb",
|
48
|
+
"lib/commands/meta/command.rb",
|
49
|
+
"lib/commands/meta/run_command.rb",
|
50
|
+
"lib/commands/meta/template_command.rb",
|
51
|
+
"lib/commands/new.rb",
|
52
|
+
"lib/commands/runs.rb",
|
53
|
+
"lib/commands/start.rb",
|
54
|
+
"lib/commands/stop.rb",
|
55
|
+
"lib/commands/template.rb",
|
39
56
|
"lib/config/config.rb",
|
40
|
-
"lib/config/
|
57
|
+
"lib/config/current_app_utils.rb",
|
41
58
|
"lib/config/persisted_hash.rb",
|
42
59
|
"lib/config/runlist.rb",
|
43
|
-
"lib/core/
|
44
|
-
"lib/core/commands/broker.rb",
|
45
|
-
"lib/core/commands/checkup.rb",
|
46
|
-
"lib/core/commands/compile.rb",
|
47
|
-
"lib/core/commands/dependencies.rb",
|
48
|
-
"lib/core/commands/help.rb",
|
49
|
-
"lib/core/commands/install.rb",
|
50
|
-
"lib/core/commands/new.rb",
|
51
|
-
"lib/core/commands/runs.rb",
|
52
|
-
"lib/core/commands/start.rb",
|
53
|
-
"lib/core/commands/stop.rb",
|
54
|
-
"lib/core/commands/template.rb",
|
60
|
+
"lib/core/nutella_cli.rb",
|
55
61
|
"lib/core/nutella_core.rb",
|
56
|
-
"lib/core/run_command.rb",
|
57
|
-
"lib/core/template_command.rb",
|
58
|
-
"lib/core/tmux.rb",
|
59
62
|
"lib/logging/nutella_logger-remote.rb",
|
60
63
|
"lib/logging/nutella_logger.rb",
|
61
64
|
"lib/logging/nutella_logging.rb",
|
62
65
|
"lib/nutella_framework.rb",
|
66
|
+
"lib/nutella_lib_framework/api.rb",
|
67
|
+
"lib/tmux/tmux.rb",
|
63
68
|
"nutella_framework.gemspec",
|
64
|
-
"test/
|
65
|
-
"test/
|
69
|
+
"test/commands/test_cmd_cli_params_parsing.rb",
|
70
|
+
"test/commands/test_command_template.rb",
|
71
|
+
"test/config/test_current_app_utils.rb",
|
72
|
+
"test/config/test_persisted_hash.rb",
|
66
73
|
"test/config/test_runlist.rb",
|
74
|
+
"test/framework_apis/test_framework_api.rb",
|
67
75
|
"test/helper.rb",
|
68
|
-
"test/logging/test_logging.rb"
|
69
|
-
"test/test_run_command.rb"
|
76
|
+
"test/logging/test_logging.rb"
|
70
77
|
]
|
71
78
|
s.homepage = "https://github.com/nutella-framework/nutella_framework"
|
72
79
|
s.licenses = ["MIT"]
|
@@ -82,8 +89,10 @@ Gem::Specification.new do |s|
|
|
82
89
|
s.add_runtime_dependency(%q<logging>, [">= 1.8.2", "~> 1.8"])
|
83
90
|
s.add_runtime_dependency(%q<git>, [">= 1.2.8", "~> 1.2"])
|
84
91
|
s.add_runtime_dependency(%q<sinatra>, [">= 1.4.5", "~> 1.4.5"])
|
92
|
+
s.add_runtime_dependency(%q<thin>, [">= 1.6.3", "~> 1.6.3"])
|
85
93
|
s.add_runtime_dependency(%q<nokogiri>, [">= 1.6.3", "~> 1.6.3"])
|
86
94
|
s.add_runtime_dependency(%q<slop>, [">= 4.0.0", "~> 4.0.0"])
|
95
|
+
s.add_runtime_dependency(%q<nutella_lib>, [">= 0.4.0", "~> 0.4.0"])
|
87
96
|
s.add_development_dependency(%q<shoulda>, [">= 3", "~> 3"])
|
88
97
|
s.add_development_dependency(%q<yard>, [">= 0.8.7", "~> 0.8"])
|
89
98
|
s.add_development_dependency(%q<rdoc>, [">= 4.0", "~> 4.0"])
|
@@ -96,8 +105,10 @@ Gem::Specification.new do |s|
|
|
96
105
|
s.add_dependency(%q<logging>, [">= 1.8.2", "~> 1.8"])
|
97
106
|
s.add_dependency(%q<git>, [">= 1.2.8", "~> 1.2"])
|
98
107
|
s.add_dependency(%q<sinatra>, [">= 1.4.5", "~> 1.4.5"])
|
108
|
+
s.add_dependency(%q<thin>, [">= 1.6.3", "~> 1.6.3"])
|
99
109
|
s.add_dependency(%q<nokogiri>, [">= 1.6.3", "~> 1.6.3"])
|
100
110
|
s.add_dependency(%q<slop>, [">= 4.0.0", "~> 4.0.0"])
|
111
|
+
s.add_dependency(%q<nutella_lib>, [">= 0.4.0", "~> 0.4.0"])
|
101
112
|
s.add_dependency(%q<shoulda>, [">= 3", "~> 3"])
|
102
113
|
s.add_dependency(%q<yard>, [">= 0.8.7", "~> 0.8"])
|
103
114
|
s.add_dependency(%q<rdoc>, [">= 4.0", "~> 4.0"])
|
@@ -111,8 +122,10 @@ Gem::Specification.new do |s|
|
|
111
122
|
s.add_dependency(%q<logging>, [">= 1.8.2", "~> 1.8"])
|
112
123
|
s.add_dependency(%q<git>, [">= 1.2.8", "~> 1.2"])
|
113
124
|
s.add_dependency(%q<sinatra>, [">= 1.4.5", "~> 1.4.5"])
|
125
|
+
s.add_dependency(%q<thin>, [">= 1.6.3", "~> 1.6.3"])
|
114
126
|
s.add_dependency(%q<nokogiri>, [">= 1.6.3", "~> 1.6.3"])
|
115
127
|
s.add_dependency(%q<slop>, [">= 4.0.0", "~> 4.0.0"])
|
128
|
+
s.add_dependency(%q<nutella_lib>, [">= 0.4.0", "~> 0.4.0"])
|
116
129
|
s.add_dependency(%q<shoulda>, [">= 3", "~> 3"])
|
117
130
|
s.add_dependency(%q<yard>, [">= 0.8.7", "~> 0.8"])
|
118
131
|
s.add_dependency(%q<rdoc>, [">= 4.0", "~> 4.0"])
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Nutella
|
4
|
+
|
5
|
+
class TestCmdCLIParamsParsing < MiniTest::Test
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@run_cmd = RunCommand.new
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
should 'parse app long argument' do
|
13
|
+
params = @run_cmd.parse_cli_parameters ['--with=bot1,bot2,bot3']
|
14
|
+
assert_equal %w(bot1 bot2 bot3), params[:with]
|
15
|
+
end
|
16
|
+
|
17
|
+
should 'parse without long argument' do
|
18
|
+
params = @run_cmd.parse_cli_parameters ['--without=botA,botB,botC']
|
19
|
+
assert_equal %w(botA botB botC), params[:without]
|
20
|
+
end
|
21
|
+
|
22
|
+
should 'parse with long argument' do
|
23
|
+
params = @run_cmd.parse_cli_parameters ['--with=botX,botY,botZ']
|
24
|
+
assert_equal %w(botX botY botZ), params[:with]
|
25
|
+
end
|
26
|
+
|
27
|
+
should 'parse one short argument' do
|
28
|
+
params = @run_cmd.parse_cli_parameters ['-w=bot1,bot2,bot3']
|
29
|
+
assert_equal %w(bot1 bot2 bot3), params[:with]
|
30
|
+
end
|
31
|
+
|
32
|
+
should 'parse two long arguments' do
|
33
|
+
params = @run_cmd.parse_cli_parameters %w(--with=bot1,bot2,bot3 --without=botA,botB,botC)
|
34
|
+
assert_equal %w(bot1 bot2 bot3), params[:with]
|
35
|
+
assert_equal %w(botA botB botC), params[:without]
|
36
|
+
end
|
37
|
+
|
38
|
+
should 'parse two short arguments' do
|
39
|
+
params = @run_cmd.parse_cli_parameters %w(-wo=bot1,bot2,bot3 -w=botA,botB,botC)
|
40
|
+
assert_equal %w(bot1 bot2 bot3), params[:without]
|
41
|
+
assert_equal %w(botA botB botC), params[:with]
|
42
|
+
end
|
43
|
+
|
44
|
+
should 'parse one short and one long argument' do
|
45
|
+
params = @run_cmd.parse_cli_parameters %w(--with=bot1,bot2,bot3 -wo=botA,botB,botC)
|
46
|
+
assert_equal %w(bot1 bot2 bot3), params[:with]
|
47
|
+
assert_equal %w(botA botB botC), params[:without]
|
48
|
+
end
|
49
|
+
|
50
|
+
should 'raise an exception when trying to parse params that do not exist' do
|
51
|
+
assert_raises (StandardError) { @run_cmd.parse_cli_parameters %w(--wit=bot1,bot2,bot3 -o=botA,botB,botC) }
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Nutella
|
4
|
+
|
5
|
+
class TestCommandTemplate < MiniTest::Test
|
6
|
+
|
7
|
+
# 1. Create a fake app
|
8
|
+
# New.new.run ['test_app']
|
9
|
+
# Dir.chdir 'test_app'
|
10
|
+
# Nutella::Install.new.run ['basic-ruby-bot', 'bot1']
|
11
|
+
# Dir.chdir '..'
|
12
|
+
|
13
|
+
# 2. Perform tests
|
14
|
+
# should 'this is my first test' do
|
15
|
+
# Dir.chdir 'test_app'
|
16
|
+
# # Test code here
|
17
|
+
# Dir.chdir '..'
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# should 'this is my second test' do
|
21
|
+
# Dir.chdir 'test_app'
|
22
|
+
# # Test code here
|
23
|
+
# Dir.chdir '..'
|
24
|
+
# end
|
25
|
+
|
26
|
+
# 3. Cleanup
|
27
|
+
# MiniTest.after_run { FileUtils.rm_r 'test_app' }
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Nutella
|
4
|
+
|
5
|
+
class TestCurrentAppUtils < MiniTest::Test
|
6
|
+
|
7
|
+
# def setup
|
8
|
+
# Dir.chdir NUTELLA_HOME
|
9
|
+
# Nutella.execute_command( 'new', ['test_app'] )
|
10
|
+
# Dir.chdir "#{NUTELLA_HOME}test_app"
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
#
|
14
|
+
# should 'return true if the dir is a nutella app' do
|
15
|
+
# assert Nutella.current_app.exist?
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# should 'return false if the dir is not a nutella app' do
|
19
|
+
# Dir.chdir NUTELLA_HOME
|
20
|
+
# refute Nutella.current_app.exist?
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# should 'return the correct version of nutella as read from the app configuration file' do
|
24
|
+
# assert_equal File.open("#{NUTELLA_HOME}VERSION", "rb").read, Nutella.current_app.config['nutella_version']
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
#
|
28
|
+
# def teardown
|
29
|
+
# FileUtils.rm_rf "#{NUTELLA_HOME}test_app"
|
30
|
+
# Dir.chdir NUTELLA_HOME
|
31
|
+
# end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|