nutella_framework 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +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
|