nutella_framework 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4fa6667c9ae02d2334bae79b81176c8297cb4d60
4
- data.tar.gz: 8a7cbbb915d58d5a7c1de1e4010647a7ef618cd0
3
+ metadata.gz: 25372f33a5d356902733e9c5c56e632cf7d03f00
4
+ data.tar.gz: 615a93c90ef6cba3206ee4f10e819bd3179c58fe
5
5
  SHA512:
6
- metadata.gz: 5fcc6cab7b5665c59329d0e5ed75265094ec763ad7ed20578411696424e90f89ad1a2be1bfffddcaa6366370c6b69109c0ceb381f094d651eb3841696e5f8e87
7
- data.tar.gz: 42b82c3abefeb8145934b03729b4f93fdce1df48e0a325503c02bab44e04e8b914b0bf04cf1ce5bcbf72215d325292edc4459d65d6ebd38978d735fb2f77eb95
6
+ metadata.gz: 12dd8ec3456123428114c3c9b1c32eb611ff51c46346a7e1d2cb834ce76bc9fcbcd0efa3b65423c73fe2ba9f4359a2e58df056cd0454a1254a6368771e210495
7
+ data.tar.gz: 605229b6c2f71386d74b7930d23b80ab89e55e52d0eb35293348029c036923d71f196cf4c4bb60166d272607bed4cfa9c96a22d8d0fb5a3bb819603836e5a2a7
data/Gemfile CHANGED
@@ -6,6 +6,7 @@ gem 'logging', '~> 1.8', '>=1.8.2'
6
6
  gem 'git', '~> 1.2', '>=1.2.8'
7
7
  gem 'sinatra', '~>1.4.5', '>=1.4.5'
8
8
  gem 'nokogiri', '~>1.6.3', '>=1.6.3'
9
+ gem 'slop', '~>4.0.0', '>=4.0.0'
9
10
 
10
11
 
11
12
  group :development do
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1
1
+ 0.3.0
@@ -28,8 +28,14 @@ get '/' do
28
28
  end
29
29
 
30
30
 
31
+ # Redirect if there is no slash after the run_id
32
+ get '/:run_id' do
33
+ redirect "#{request.url}/"
34
+ end
35
+
36
+
31
37
  # Renders the run template
32
- get '/:run_id/?' do
38
+ get '/:run_id/' do
33
39
 
34
40
  # Parse the run_id from URL and extract the run path from runlist.json
35
41
  @run_id = params[:run_id]
@@ -53,6 +59,12 @@ get '/:run_id/?' do
53
59
  end
54
60
 
55
61
 
62
+ # Redirect if there is a slash after the interface
63
+ get '/:run_id/:interface/' do
64
+ redirect "#{request.url[0..-2]}"
65
+ end
66
+
67
+
56
68
  # Serves the index.html file for each individual interface augmented with nutella query string parameters
57
69
  get '/:run_id/:interface' do
58
70
 
@@ -128,7 +140,7 @@ def extract_interface_info( interfaces_path, iface_dir )
128
140
 
129
141
  unless File.exist? index_path
130
142
  iface_props[:name] = iface_dir
131
- iface_props[:description] = 'My designer was a lazy ass and didn\'t include an index.html file in the main interface directory'
143
+ iface_props[:description] = 'My designer was a bit lazy and didn\'t include an index.html file in the main interface directory :('
132
144
  return iface_props
133
145
  end
134
146
 
@@ -138,14 +150,14 @@ def extract_interface_info( interfaces_path, iface_dir )
138
150
  f.close
139
151
  iface_props[:name] = doc.css('title').empty? ? iface_dir : doc.css('title').text
140
152
  if doc.css("meta[name='description']").empty?
141
- iface_props[:description] = 'My designer was a lazy ass and didn\'t include a <meta name="description" content="Description of this interface"> tag in the index.html file'
153
+ iface_props[:description] = 'My designer was a bit lazy and didn\'t include a <meta name="description" content="Description of this interface"> tag in the index.html file :('
142
154
  else
143
155
  if doc.css("meta[name='description']").attribute('content').nil?
144
- iface_props[:description] = 'There was no attribute content in <meta name="description" content="Description of this interface"> tag in the index.html file'
156
+ iface_props[:description] = 'There was no attribute content in <meta name="description" content="Description of this interface"> tag in the index.html file :('
145
157
  else
146
158
  iface_props[:description] = doc.css("meta[name='description']").attribute('content').text
147
159
  end
148
160
  end
149
- iface_props[:url] = "#{@run_id}/#{iface_dir}"
161
+ iface_props[:url] = "#{iface_dir}"
150
162
  iface_props
151
163
  end
@@ -40,7 +40,11 @@
40
40
  <script>
41
41
  el = document.getElementById('run_id_input');
42
42
  el.addEventListener('change', function() {
43
- window.location = '/' + this.value
43
+ url = location.href
44
+ if (url.substr(url.length - 1)=="/")
45
+ window.location = url + this.value + '/';
46
+ else
47
+ window.location = url + '/' + this.value + '/';
44
48
  });
45
49
  </script>
46
50
  </body>
data/data/index.html ADDED
@@ -0,0 +1,21 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <!-- Your interface name here-->
6
+ <title></title>
7
+ <!-- The description of this interfaces here -->
8
+ <meta name="description" content="">
9
+
10
+ <!-- Your CSS here -->
11
+ </head>
12
+
13
+ <body>
14
+
15
+ <!-- Your markup here -->
16
+
17
+
18
+ <!-- Your scripts here -->
19
+
20
+ </body>
21
+ </html>
data/data/startup ADDED
@@ -0,0 +1,13 @@
1
+ #!/bin/sh
2
+
3
+ # Extract the path of the current directory.
4
+ # From now on you can refer to any file in the bot directory as:
5
+ # $BASEDIR/<my_file>
6
+ BASEDIR=$(dirname $0)
7
+
8
+
9
+ # Remember that nutella passes the run_id and the broker name as
10
+ # command line parameters that you can access via $1 and $2 respectively.
11
+
12
+ # Start your bot here with something like (example in ruby):
13
+ # ruby $BASEDIR/my_bot.rb $1 $2
@@ -0,0 +1,21 @@
1
+ require 'core/run_command'
2
+ require 'core/tmux'
3
+
4
+ module Nutella
5
+ class Compile < RunCommand
6
+ @description = 'Compiles all the actors that need compilation in the project'
7
+
8
+ def run(args=nil)
9
+
10
+ # If the current directory is not a nutella project, return
11
+ return unless Nutella.current_project.exist?
12
+
13
+ # Compile all actors
14
+ return unless prepare_bot( Nutella.current_project.dir, 'compile', 'Compiling' )
15
+
16
+ # Output success message
17
+ console.success "All actors compiled for #{Nutella.current_project.config['name']}"
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ require 'core/run_command'
2
+ require 'core/tmux'
3
+
4
+ module Nutella
5
+ class Dependencies < RunCommand
6
+ @description = 'Installs the dependencies for all actors in the project'
7
+
8
+ def run(args=nil)
9
+
10
+ # If the current directory is not a nutella project, return
11
+ return unless Nutella.current_project.exist?
12
+
13
+ # Install all dependencies
14
+ return unless prepare_bot( Nutella.current_project.dir, 'dependencies', 'Installing dependencies for' )
15
+
16
+ # Output success message
17
+ console.success "All dependencies installed for #{Nutella.current_project.config['name']}"
18
+ end
19
+
20
+ end
21
+ end
@@ -8,11 +8,17 @@ module Nutella
8
8
  def run(args=nil)
9
9
  message=''
10
10
  Dir["#{File.dirname(__FILE__)}/*.rb"].each do |file|
11
- message += "#{File.basename(file, File.extname(file))}\t\t"
12
- message += Object::const_get("Nutella::#{File.basename(file, File.extname(file)).capitalize}").description
11
+ command = File.basename(file, File.extname(file))
12
+ if command.length > 7
13
+ message += "#{command}\t"
14
+ else
15
+ message += "#{command}\t\t"
16
+ end
17
+ message += Object::const_get("Nutella::#{command.capitalize}").description
13
18
  message += "\n"
14
19
  end
15
- console.info message, 200
20
+ console.info message
21
+ console.info 'For more details on individual commands, see https://github.com/nutella-framework/nutella_framework/wiki/Nutella-Command-Line-Interface'
16
22
  end
17
23
 
18
24
  end
@@ -1,11 +1,10 @@
1
- require 'core/command'
2
- require 'json'
1
+ require 'core/template_command'
3
2
  require 'git'
4
3
  require 'net/http'
5
4
 
6
5
  module Nutella
7
6
 
8
- class Install < Command
7
+ class Install < TemplateCommand
9
8
  @description = 'Copies an arbitrary template (from central DB, directory or URL) into the current project'
10
9
 
11
10
  def run(args=nil)
@@ -118,27 +117,6 @@ module Nutella
118
117
  Dir.mkdir Nutella.config['tmp_dir'] unless Dir.exists? Nutella.config['tmp_dir']
119
118
  Git.clone(template, dest_dir, :path => Nutella.config['tmp_dir'])
120
119
  end
121
-
122
-
123
- def validate_template( dir )
124
- # Parse the template's nutella.json file
125
- begin
126
- template_nutella_file_json = JSON.parse(IO.read("#{dir}/nutella.json"))
127
- rescue
128
- return false
129
- end
130
- # If template is a bot, perform the appropriate checks
131
- if template_nutella_file_json['type']=='bot'
132
- # Is there a mandatory 'startup' script and is it executable
133
- return false unless File.executable? "#{dir}/startup"
134
- end
135
- # If template is an interface, perform the appropriate checks
136
- if template_nutella_file_json['type']=='interface'
137
- # Is there the mandatory index.html file
138
- return false unless File.exist? "#{dir}/index.html"
139
- end
140
- true
141
- end
142
120
 
143
121
 
144
122
  def extract_remote_repo_url( template_name )
@@ -8,7 +8,7 @@ module Nutella
8
8
  def run(args=nil)
9
9
 
10
10
  # If invoked with "all" it will show all the runs under this instance of nutella
11
- if args[0]=='all'
11
+ if args[0]=='--all' || args[0]=='-a'
12
12
  display_all_runs
13
13
  else
14
14
  # If current dir is not a nutella project, return
@@ -38,10 +38,10 @@ module Nutella
38
38
  runs.each do |run|
39
39
  run_id = run.dup
40
40
  run_id.slice! "#{project_name}_"
41
- if run_id.empty?
42
- console.info " #{project_name}"
41
+ if run==project_name
42
+ console.info " #{project_name} (default instance)"
43
43
  else
44
- console.info " #{run}"
44
+ console.info " #{run_id}"
45
45
  end
46
46
  end
47
47
  end
@@ -12,9 +12,37 @@ module Nutella
12
12
 
13
13
  # Extract run (passed run name) and run_id
14
14
  run, run_id = extract_names args
15
- # Extract project directory and run_id
15
+
16
+ # Check that the run name is different than the project name
17
+ if run==Nutella.current_project.config['name']
18
+ console.warn 'Your run name should be different than your project name'
19
+ return
20
+ end
21
+
22
+ # Extract parameters
23
+ begin
24
+ params = extract_parameters args
25
+ rescue
26
+ console.warn 'The only supported parameters are --with (-w) and --without (-wo)'
27
+ return
28
+ end
29
+
30
+ # Check that we are not using 'with' and 'without' options at the same time
31
+ unless params[:with].empty? || params[:without].empty?
32
+ console.warn 'You can\'t use both --with and --without at the same time'
33
+ return
34
+ end
35
+
36
+ # Extract project directory
16
37
  cur_prj_dir = Nutella.current_project.dir
17
38
 
39
+ # Check that there is at least a regular bot that will be started,
40
+ # otherwise it makes no sense to create a run
41
+ if run_bots_list_minus_project_bots(cur_prj_dir, params).empty? && project_bots_started?
42
+ console.warn "Run #{run} not created: your project bots are already started and you specified no regular bots exclusively for this run"
43
+ return
44
+ end
45
+
18
46
  # Check that the run_id is unique and add it to the list of runs
19
47
  # If it's not, return (without adding the run_id to the list of course)
20
48
  return unless add_to_run_list( run_id, cur_prj_dir )
@@ -27,24 +55,49 @@ module Nutella
27
55
  # Start all nutella internal actors, if needed
28
56
  return unless start_nutella_actors
29
57
 
30
- # Install dependencies, compile and start all bots
31
- return unless prepare_bot( cur_prj_dir, 'dependencies', 'Installing dependencies for' )
32
- return unless prepare_bot( cur_prj_dir, 'compile', 'Compiling' )
33
- return unless start_bots( cur_prj_dir, run_id )
58
+ # Start all project level actors, if any
59
+ return unless start_project_bots cur_prj_dir
60
+
61
+ # Start all bots
62
+ return unless start_bots( cur_prj_dir, run_id, params )
34
63
 
35
64
  # Output success message
36
65
  output_success_message( run_id, run, 'started' )
37
- output_monitoring_details run_id
66
+ output_monitoring_details( run_id, cur_prj_dir, params)
38
67
  end
39
68
 
40
69
 
41
70
  private
42
71
 
43
72
 
73
+ def run_bots_list_minus_project_bots( cur_prj_dir, params )
74
+ # Fetch the list of project bots
75
+ project_bots_list = Nutella.current_project.config['project_bots']
76
+ run_bots_list = run_actors_list("#{cur_prj_dir}/bots/")
77
+ # Depending on the mode we are in we want to start only some bots, exclude only some bots, start all bots
78
+ unless params[:with].empty?
79
+ return project_bots_list.nil? ? params[:with] : params[:with] - project_bots_list
80
+ end
81
+ unless params[:without].empty?
82
+ return project_bots_list.nil? ? run_bots_list - params[:without] : run_bots_list - params[:without] - project_bots_list
83
+ end
84
+ if params[:with].empty? && params[:without].empty?
85
+ return project_bots_list.nil? ? run_bots_list : run_bots_list - project_bots_list
86
+ end
87
+ end
88
+
89
+
90
+ def project_bots_started?
91
+ project_name = Nutella.current_project.config['name']
92
+ tmux_session_name = "#{project_name}-project-bots"
93
+ return Tmux.session_exist? tmux_session_name
94
+ end
95
+
96
+
44
97
  def add_to_run_list(run_id, prj_dir)
45
98
  unless Nutella.runlist.add?( run_id, prj_dir )
46
99
  # If the run_id is already in the list, check that it's actually live
47
- if Tmux.session_exists? run_id
100
+ if Tmux.session_exist? run_id
48
101
  console.error 'Impossible to start project: an instance of this project with the same run_id is already running!'
49
102
  console.error "You might want to kill it with 'nutella stop #{run_id}'"
50
103
  return false
@@ -126,6 +179,7 @@ module Nutella
126
179
  true
127
180
  end
128
181
 
182
+
129
183
  def start_nutella_actor( actor_dir )
130
184
  pid_file_path = "#{actor_dir}/.pid"
131
185
  return true if sanitize_pid_file pid_file_path
@@ -145,43 +199,69 @@ module Nutella
145
199
  end
146
200
 
147
201
 
148
- def prepare_bot( cur_prj_dir, script, message )
149
- for_each_actor_in_dir cur_prj_dir do |bot|
150
- # Skip bot if there is no '' script
151
- next unless File.exist? "#{cur_prj_dir}/bots/#{bot}/#{script}"
152
- # Output message
153
- console.info "#{message} bot #{bot}."
154
- # Execute 'dependencies' script
155
- cur_dir = Dir.pwd
156
- Dir.chdir "#{cur_prj_dir}/bots/#{bot}"
157
- system "./#{script}"
158
- Dir.chdir cur_dir
202
+ def start_project_bots( cur_prj_dir )
203
+ project_bots_list = Nutella.current_project.config['project_bots']
204
+ project_name = Nutella.current_project.config['name']
205
+ tmux_session_name = "#{project_name}-project-bots"
206
+ bots_dir = "#{cur_prj_dir}/bots/"
207
+ # If project bots have been started already, then do nothing
208
+ unless Tmux.session_exist? tmux_session_name
209
+ # Start all project bots in the list into a new tmux session
210
+ tmux = Tmux.new tmux_session_name
211
+ for_each_actor_in_dir bots_dir do |bot|
212
+ unless project_bots_list.nil? || !project_bots_list.include?( bot )
213
+ # If there is no 'startup' script output a warning (because
214
+ # startup is mandatory) and skip the bot
215
+ unless File.exist?("#{bots_dir}#{bot}/startup")
216
+ console.warn "Impossible to start bot #{bot}. Couldn't locate 'startup' script."
217
+ next
218
+ end
219
+ # Create a new window in the session for this run
220
+ tmux.new_bot_window bot
221
+ end
222
+ end
159
223
  end
160
224
  true
161
225
  end
162
226
 
163
227
 
164
- def start_bots( cur_prj_dir, run_id )
165
- bots_dir = "#{cur_prj_dir}/bots/"
228
+ def start_bots( cur_prj_dir, run_id, params )
166
229
  # Create a new tmux instance for this run
167
230
  tmux = Tmux.new run_id
168
- for_each_actor_in_dir bots_dir do |bot|
169
- # If there is no 'startup' script output a warning (because
170
- # startup is mandatory) and skip the bot
171
- unless File.exist?("#{bots_dir}#{bot}/startup")
172
- console.warn "Impossible to start bot #{bot}. Couldn't locate 'startup' script."
173
- next
174
- end
175
- # Create a new window in the session for this run
176
- tmux.new_bot_window bot
177
- end
231
+ # Fetch bots dir
232
+ bots_dir = "#{cur_prj_dir}/bots/"
233
+ # Start the appropriate bots
234
+ run_bots_list_minus_project_bots( cur_prj_dir, params ).each { |bot| start_bot(bots_dir, bot, tmux) }
178
235
  true
179
236
  end
180
237
 
181
238
 
182
- def output_monitoring_details( run_id )
183
- console.success "Do `tmux attach-session -t #{run_id}` to monitor your bots."
239
+ def start_bot( bots_dir, bot, tmux )
240
+ # If there is no 'startup' script output a warning (because
241
+ # startup is mandatory) and skip the bot
242
+ unless File.exist?("#{bots_dir}#{bot}/startup")
243
+ console.warn "Impossible to start bot #{bot}. Couldn't locate 'startup' script."
244
+ return
245
+ end
246
+ # Create a new window in the session for this run
247
+ tmux.new_bot_window bot
248
+ end
249
+
250
+
251
+ def output_monitoring_details( run_id, cur_prj_dir, params )
252
+ project_bots_list = Nutella.current_project.config['project_bots']
253
+ project_name = Nutella.current_project.config['name']
254
+ tmux_session_name = "#{project_name}-project-bots"
255
+ unless project_bots_list.nil? || project_bots_list.empty?
256
+ console.success "Do `tmux attach-session -t #{tmux_session_name}` to monitor your project bots."
257
+ end
258
+ if run_bots_list_minus_project_bots(cur_prj_dir, params).empty?
259
+ console.success 'No tmux session was created for this run because you specified no regular bots exclusively for this run'
260
+ else
261
+ console.success "Do `tmux attach-session -t #{run_id}` to monitor your bots."
262
+ end
184
263
  console.success "Go to http://localhost:#{Nutella.config['main_interface_port']}/#{run_id} to access your interfaces"
264
+
185
265
  end
186
266
 
187
267
 
@@ -20,6 +20,9 @@ module Nutella
20
20
  # Stops all the bots
21
21
  Tmux.kill_session run_id
22
22
 
23
+ # Stop all project level actors (if any) if needed
24
+ stop_project_bots
25
+
23
26
  # Stop all nutella internal actors, if needed
24
27
  if Nutella.runlist.empty?
25
28
  stop_nutella_actors
@@ -47,6 +50,19 @@ module Nutella
47
50
  end
48
51
 
49
52
 
53
+ def stop_project_bots
54
+ project_name = Nutella.current_project.config['name']
55
+ tmux_session_name = "#{project_name}-project-bots"
56
+ if Tmux.session_exist? tmux_session_name
57
+ # Are there any runs of this project hinging on the project bots?
58
+ if Nutella.runlist.runs_by_project(project_name).empty?
59
+ Tmux.kill_session tmux_session_name
60
+ end
61
+ end
62
+ true
63
+ end
64
+
65
+
50
66
  def stop_nutella_actors
51
67
  nutella_actors_dir = "#{Nutella.config['nutella_home']}actors"
52
68
  for_each_actor_in_dir nutella_actors_dir do |actor|
@@ -0,0 +1,132 @@
1
+ require 'core/template_command'
2
+
3
+ module Nutella
4
+
5
+ class Template < TemplateCommand
6
+ @description = 'Helps create and validate templates'
7
+
8
+ def run(args=nil)
9
+
10
+ # If no argument then we just display info about the command
11
+ if args==nil || args.length < 2
12
+ display_help
13
+ return
14
+ end
15
+
16
+ # Extract sub command
17
+ sub_command = args[0]
18
+ param = args[1]
19
+
20
+ if sub_command == 'validate'
21
+ validate_template_sub_command param
22
+ end
23
+
24
+ if sub_command == 'create'
25
+ create_template_sub_command param
26
+ end
27
+
28
+ end
29
+
30
+ private
31
+
32
+ def display_help
33
+ console.info 'You need to specify a sub command.'
34
+ console.info 'create <template_name> creates a template skeleton in the folder with the same name'
35
+ console.info 'validate <temple_dir> validates a template that already exists'
36
+ end
37
+
38
+
39
+ def validate_template_sub_command( dir )
40
+ if validate_template dir
41
+ console.success 'This directory appears to be a valid nutella template'
42
+ else
43
+ console.warn 'Looks like this directory is not a valid nutella template'
44
+ end
45
+ end
46
+
47
+
48
+ def create_template_sub_command( name_d )
49
+ ver_d = '0.1.0'
50
+ type_d = 'bot'
51
+
52
+ # Name
53
+ puts 'What is the name of your template?'
54
+ print "(#{name_d}) "
55
+ c = $stdin.gets.chomp!
56
+ name = c.empty? ? name_d : c
57
+ # Version
58
+ puts 'What is the version of your template?'
59
+ print "(#{ver_d}) "
60
+ c = $stdin.gets.chomp!
61
+ version = c.empty? ? ver_d : c
62
+ # Type
63
+ puts 'Are you creating a template for a "bot" or "interface"?'
64
+ print "(#{type_d}) "
65
+ c = $stdin.gets.chomp!
66
+ type = c.empty? ? type_d : c
67
+ # Description
68
+ puts "Do you want to provide a short description for your #{type} template?"
69
+ print '(optional, hit enter if no description) '
70
+ description = $stdin.gets.chomp!
71
+ # Repo
72
+ puts 'Do you want to provide a git repository for your template?'
73
+ print '(optional, hit enter if no repo) '
74
+ repo = $stdin.gets.chomp!
75
+
76
+ # Build JSON and show confirmation
77
+ puts 'Looks good?'
78
+ json = build_nutella_json( name, version, type, description, repo )
79
+ puts JSON.pretty_generate json
80
+ print '(yes/no) '
81
+
82
+ # If user confirms, create template
83
+ confirm = $stdin.gets.chomp!
84
+ if confirm=='yes'
85
+ create_template_files json
86
+ else
87
+ console.warn 'Template creation aborted'
88
+ end
89
+
90
+ end
91
+
92
+
93
+ def create_template_files( json )
94
+ # First validate the JSON
95
+ unless validate_nutella_file_json json
96
+ console.error 'Something was wrong with your nutella.json file. Template creation aborted'
97
+ return
98
+ end
99
+ # Assemble destination directory
100
+ template_dir = File.join( Dir.pwd, json['name'] )
101
+ # Check that the directory doesn't exist already
102
+ if File.directory?(template_dir)
103
+ console.error("The directory #{template_dir} already exists! Can't create template #{json[:name]}")
104
+ return
105
+ end
106
+ # Create directory
107
+ Dir.mkdir template_dir
108
+ # Create nutella.json file
109
+ File.open("#{template_dir}/nutella.json", 'w') { |f| f.write(JSON.pretty_generate json) }
110
+ # Add bot/interface specific files
111
+ bot_specific_file = nil
112
+ bot_specific_file = File.join( Nutella.config['nutella_home'], 'data/startup' ) if json['type']=='bot'
113
+ bot_specific_file = File.join( Nutella.config['nutella_home'], 'data/index.html' ) if json['type']=='interface'
114
+ FileUtils.copy( bot_specific_file, template_dir )
115
+ console.success "Template #{json['name']} created successfully!"
116
+ end
117
+
118
+
119
+ def build_nutella_json( name, version, type, description, repo )
120
+ json = { 'name' => name, 'version' => version, 'type' => type}
121
+ unless description.empty?
122
+ json['description'] = description
123
+ end
124
+ unless repo.empty?
125
+ json['repo'] = repo
126
+ end
127
+ json
128
+ end
129
+
130
+ end
131
+
132
+ end
@@ -1,4 +1,5 @@
1
1
  require 'core/command'
2
+ require 'slop'
2
3
 
3
4
  module Nutella
4
5
  # This class describes a run command which can be either start or stop.
@@ -9,22 +10,59 @@ module Nutella
9
10
  console.error 'Running generic RunCommand!!! WAT?'
10
11
  end
11
12
 
13
+
12
14
  # Extracts run name and run_id
13
15
  # @param [Array<String>] args command line arguments passed to the command
14
16
  # @return [String, String ] the run name (cleaned of nils) and the run_id
15
17
  def extract_names( args )
16
- # Check that the run name passed as parameter is not nil
17
- run = args.nil? ? nil : args[0]
18
- # Extract run_id
19
- run_id = args.nil? ? Nutella.runlist.extract_run_id( '' ) : Nutella.runlist.extract_run_id( args[0] )
18
+
19
+ # Simple `nutella start/stop`
20
+ if args.nil? || args.empty?
21
+ run = nil
22
+ run_id = Nutella.runlist.extract_run_id( '' )
23
+ return run, run_id
24
+ end
25
+
26
+ # Check if the first argument is a parameter or a run name
27
+ if args[0].start_with? '-'
28
+ run = nil
29
+ run_id = Nutella.runlist.extract_run_id( '' )
30
+ else
31
+ # If it's a run name, store the run name and shift so we are left with only
32
+ # the parameters in args
33
+ run = args[0]
34
+ run_id = Nutella.runlist.extract_run_id( args[0] )
35
+ args.shift
36
+ end
37
+
20
38
  return run, run_id
21
39
  end
22
40
 
41
+
42
+ # Extracts the command line parameters
43
+ # @param [Array<String>] args command line arguments passed to the command
44
+ # @return [Hash] an hash containing the parameters
45
+ def extract_parameters( args )
46
+ opts = Slop::Options.new
47
+ opts.array '-wo', '--without', 'A list of actors NOT to start'
48
+ opts.array '-w', '--with', 'A list of actors that needs to be started'
49
+ parser = Slop::Parser.new(opts)
50
+ result = parser.parse(args)
51
+ result.to_hash
52
+ end
53
+
54
+
55
+ # Returns all the actors in a certain directory
56
+ def run_actors_list( actors_dir )
57
+ Dir.entries(actors_dir).select {|entry| File.directory?(File.join(actors_dir, entry)) && !(entry =='.' || entry == '..') }
58
+ end
59
+
60
+
23
61
  # Executes a code block for each actor in a certain directory
24
62
  # @param [String] actors_dir directory where we are iterating
25
63
  # @yield [actor_dir] Gives the actor directory to the block
26
64
  def for_each_actor_in_dir( actors_dir, &block )
27
- Dir.entries(actors_dir).select {|entry| File.directory?(File.join(actors_dir, entry)) && !(entry =='.' || entry == '..') }.each do |actor_dir|
65
+ run_actors_list(actors_dir).each do |actor_dir|
28
66
  block.call actor_dir
29
67
  end
30
68
  end
@@ -37,6 +75,22 @@ module Nutella
37
75
  console.success "Project #{Nutella.current_project.config['name']}, run #{run} #{action}!"
38
76
  end
39
77
  end
78
+
79
+
80
+ def prepare_bot( cur_prj_dir, script, message )
81
+ for_each_actor_in_dir cur_prj_dir do |bot|
82
+ # Skip bot if there is no script
83
+ next unless File.exist? "#{cur_prj_dir}/bots/#{bot}/#{script}"
84
+ # Output message
85
+ console.info "#{message} bot #{bot}."
86
+ # Execute 'script' script
87
+ cur_dir = Dir.pwd
88
+ Dir.chdir "#{cur_prj_dir}/bots/#{bot}"
89
+ system "./#{script}"
90
+ Dir.chdir cur_dir
91
+ end
92
+ true
93
+ end
40
94
 
41
95
 
42
96
  end
@@ -0,0 +1,46 @@
1
+ require 'core/command'
2
+ require 'json'
3
+
4
+ module Nutella
5
+ # This class describes a template command which can be either install or template
6
+ # It is mostly a commodity class for code reuse.
7
+
8
+ class TemplateCommand < Command
9
+
10
+ def run (args=nil)
11
+ console.error 'Running generic TemplateCommand!!! WAT?'
12
+ end
13
+
14
+
15
+ # Validates a template in a certain folder
16
+ # @param [String] dir the directory where the template is stored
17
+ def validate_template( dir )
18
+ # Parse and validate the template's nutella.json file
19
+ begin
20
+ template_nutella_file_json = JSON.parse(IO.read("#{dir}/nutella.json"))
21
+ rescue
22
+ return false
23
+ end
24
+ return false unless validate_nutella_file_json template_nutella_file_json
25
+ # If template is a bot, perform the appropriate checks
26
+ if template_nutella_file_json['type']=='bot'
27
+ # Is there a mandatory 'startup' script and is it executable
28
+ return false unless File.executable? "#{dir}/startup"
29
+ end
30
+ # If template is an interface, perform the appropriate checks
31
+ if template_nutella_file_json['type']=='interface'
32
+ # Is there the mandatory index.html file
33
+ return false unless File.exist? "#{dir}/index.html"
34
+ end
35
+ true
36
+ end
37
+
38
+
39
+ def validate_nutella_file_json( json )
40
+ !json['name'].nil? && !json['version'].nil? && !json['type'].nil? && (json['type']=='bot' || json['type']=='interface')
41
+ end
42
+
43
+
44
+ end
45
+
46
+ end
data/lib/core/tmux.rb CHANGED
@@ -29,7 +29,7 @@ module Nutella
29
29
  `tmux kill-session -t #{run_id} &> /dev/null`
30
30
  end
31
31
 
32
- def self.session_exists?( run_id )
32
+ def self.session_exist?( run_id )
33
33
  system( "tmux has-session -t #{run_id} &> /dev/null" )
34
34
  end
35
35
 
@@ -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.2.1 ruby lib
5
+ # stub: nutella_framework 0.3.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "nutella_framework"
9
- s.version = "0.2.1"
9
+ s.version = "0.3.0"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
13
13
  s.authors = ["Alessandro Gnoli"]
14
- s.date = "2015-01-20"
14
+ s.date = "2015-01-29"
15
15
  s.description = "Nutella is a framework to build and run \"Internet of Things\"-like learning applications"
16
16
  s.email = "tebemis@gmail.com"
17
17
  s.executables = ["nutella"]
@@ -33,6 +33,8 @@ Gem::Specification.new do |s|
33
33
  "actors/main_interface/views/index.erb",
34
34
  "actors/main_interface/views/not_found_404.erb",
35
35
  "bin/nutella",
36
+ "data/index.html",
37
+ "data/startup",
36
38
  "lib/cli/nutella_cli.rb",
37
39
  "lib/config/config.rb",
38
40
  "lib/config/current_project.rb",
@@ -41,14 +43,18 @@ Gem::Specification.new do |s|
41
43
  "lib/core/command.rb",
42
44
  "lib/core/commands/broker.rb",
43
45
  "lib/core/commands/checkup.rb",
46
+ "lib/core/commands/compile.rb",
47
+ "lib/core/commands/dependencies.rb",
44
48
  "lib/core/commands/help.rb",
45
49
  "lib/core/commands/install.rb",
46
50
  "lib/core/commands/new.rb",
47
51
  "lib/core/commands/runs.rb",
48
52
  "lib/core/commands/start.rb",
49
53
  "lib/core/commands/stop.rb",
54
+ "lib/core/commands/template.rb",
50
55
  "lib/core/nutella_core.rb",
51
56
  "lib/core/run_command.rb",
57
+ "lib/core/template_command.rb",
52
58
  "lib/core/tmux.rb",
53
59
  "lib/logging/nutella_logger-remote.rb",
54
60
  "lib/logging/nutella_logger.rb",
@@ -59,7 +65,8 @@ Gem::Specification.new do |s|
59
65
  "test/config/test_project.rb",
60
66
  "test/config/test_runlist.rb",
61
67
  "test/helper.rb",
62
- "test/logging/test_logging.rb"
68
+ "test/logging/test_logging.rb",
69
+ "test/test_run_command.rb"
63
70
  ]
64
71
  s.homepage = "https://github.com/nutella-framework/nutella_framework"
65
72
  s.licenses = ["MIT"]
@@ -76,6 +83,7 @@ Gem::Specification.new do |s|
76
83
  s.add_runtime_dependency(%q<git>, [">= 1.2.8", "~> 1.2"])
77
84
  s.add_runtime_dependency(%q<sinatra>, [">= 1.4.5", "~> 1.4.5"])
78
85
  s.add_runtime_dependency(%q<nokogiri>, [">= 1.6.3", "~> 1.6.3"])
86
+ s.add_runtime_dependency(%q<slop>, [">= 4.0.0", "~> 4.0.0"])
79
87
  s.add_development_dependency(%q<shoulda>, [">= 3", "~> 3"])
80
88
  s.add_development_dependency(%q<yard>, [">= 0.8.7", "~> 0.8"])
81
89
  s.add_development_dependency(%q<rdoc>, [">= 4.0", "~> 4.0"])
@@ -89,6 +97,7 @@ Gem::Specification.new do |s|
89
97
  s.add_dependency(%q<git>, [">= 1.2.8", "~> 1.2"])
90
98
  s.add_dependency(%q<sinatra>, [">= 1.4.5", "~> 1.4.5"])
91
99
  s.add_dependency(%q<nokogiri>, [">= 1.6.3", "~> 1.6.3"])
100
+ s.add_dependency(%q<slop>, [">= 4.0.0", "~> 4.0.0"])
92
101
  s.add_dependency(%q<shoulda>, [">= 3", "~> 3"])
93
102
  s.add_dependency(%q<yard>, [">= 0.8.7", "~> 0.8"])
94
103
  s.add_dependency(%q<rdoc>, [">= 4.0", "~> 4.0"])
@@ -103,6 +112,7 @@ Gem::Specification.new do |s|
103
112
  s.add_dependency(%q<git>, [">= 1.2.8", "~> 1.2"])
104
113
  s.add_dependency(%q<sinatra>, [">= 1.4.5", "~> 1.4.5"])
105
114
  s.add_dependency(%q<nokogiri>, [">= 1.6.3", "~> 1.6.3"])
115
+ s.add_dependency(%q<slop>, [">= 4.0.0", "~> 4.0.0"])
106
116
  s.add_dependency(%q<shoulda>, [">= 3", "~> 3"])
107
117
  s.add_dependency(%q<yard>, [">= 0.8.7", "~> 0.8"])
108
118
  s.add_dependency(%q<rdoc>, [">= 4.0", "~> 4.0"])
@@ -0,0 +1,54 @@
1
+ require 'helper'
2
+
3
+ module Nutella
4
+
5
+ class TestRunCommand < MiniTest::Test
6
+
7
+ @@run_cmd = RunCommand.new
8
+
9
+
10
+ should 'parse app long argument' do
11
+ params = @@run_cmd.send( :extract_parameters, ['--with=bot1,bot2,bot3'] )
12
+ assert_equal ['bot1', 'bot2', 'bot3'], params[:with]
13
+ end
14
+
15
+ should 'parse without long argument' do
16
+ params = @@run_cmd.send( :extract_parameters, ['--without=botA,botB,botC'] )
17
+ assert_equal ['botA', 'botB', 'botC'], params[:without]
18
+ end
19
+
20
+ should 'parse with long argument' do
21
+ params = @@run_cmd.send( :extract_parameters, ['--with=botX,botY,botZ'] )
22
+ assert_equal ['botX', 'botY', 'botZ'], params[:with]
23
+ end
24
+
25
+ should 'parse one short argument' do
26
+ params = @@run_cmd.send( :extract_parameters, ['-w=bot1,bot2,bot3'] )
27
+ assert_equal ['bot1', 'bot2', 'bot3'], params[:with]
28
+ end
29
+
30
+ should 'parse two long arguments' do
31
+ params = @@run_cmd.send( :extract_parameters, ['--with=bot1,bot2,bot3', '--without=botA,botB,botC'] )
32
+ assert_equal ['bot1', 'bot2', 'bot3'], params[:with]
33
+ assert_equal ['botA', 'botB', 'botC'], params[:without]
34
+ end
35
+
36
+ should 'parse two short arguments' do
37
+ params = @@run_cmd.send( :extract_parameters, ['-wo=bot1,bot2,bot3', '-w=botA,botB,botC'] )
38
+ assert_equal ['bot1', 'bot2', 'bot3'], params[:without]
39
+ assert_equal ['botA', 'botB', 'botC'], params[:with]
40
+ end
41
+
42
+ should 'parse one short and one long argument' do
43
+ params = @@run_cmd.send( :extract_parameters, ['--with=bot1,bot2,bot3', '-wo=botA,botB,botC'] )
44
+ assert_equal ['bot1', 'bot2', 'bot3'], params[:with]
45
+ assert_equal ['botA', 'botB', 'botC'], params[:without]
46
+ end
47
+
48
+ should 'raise an exception when trying to parse params that do not exist' do
49
+ assert_raises (Slop::UnknownOption) { @@run_cmd.send( :extract_parameters, ['--wit=bot1,bot2,bot3', '-o=botA,botB,botC'] ) }
50
+ end
51
+
52
+ end
53
+
54
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nutella_framework
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alessandro Gnoli
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-20 00:00:00.000000000 Z
11
+ date: 2015-01-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ansi
@@ -130,6 +130,26 @@ dependencies:
130
130
  - - "~>"
131
131
  - !ruby/object:Gem::Version
132
132
  version: 1.6.3
133
+ - !ruby/object:Gem::Dependency
134
+ name: slop
135
+ requirement: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: 4.0.0
140
+ - - "~>"
141
+ - !ruby/object:Gem::Version
142
+ version: 4.0.0
143
+ type: :runtime
144
+ prerelease: false
145
+ version_requirements: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: 4.0.0
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: 4.0.0
133
153
  - !ruby/object:Gem::Dependency
134
154
  name: shoulda
135
155
  requirement: !ruby/object:Gem::Requirement
@@ -273,6 +293,8 @@ files:
273
293
  - actors/main_interface/views/index.erb
274
294
  - actors/main_interface/views/not_found_404.erb
275
295
  - bin/nutella
296
+ - data/index.html
297
+ - data/startup
276
298
  - lib/cli/nutella_cli.rb
277
299
  - lib/config/config.rb
278
300
  - lib/config/current_project.rb
@@ -281,14 +303,18 @@ files:
281
303
  - lib/core/command.rb
282
304
  - lib/core/commands/broker.rb
283
305
  - lib/core/commands/checkup.rb
306
+ - lib/core/commands/compile.rb
307
+ - lib/core/commands/dependencies.rb
284
308
  - lib/core/commands/help.rb
285
309
  - lib/core/commands/install.rb
286
310
  - lib/core/commands/new.rb
287
311
  - lib/core/commands/runs.rb
288
312
  - lib/core/commands/start.rb
289
313
  - lib/core/commands/stop.rb
314
+ - lib/core/commands/template.rb
290
315
  - lib/core/nutella_core.rb
291
316
  - lib/core/run_command.rb
317
+ - lib/core/template_command.rb
292
318
  - lib/core/tmux.rb
293
319
  - lib/logging/nutella_logger-remote.rb
294
320
  - lib/logging/nutella_logger.rb
@@ -300,6 +326,7 @@ files:
300
326
  - test/config/test_runlist.rb
301
327
  - test/helper.rb
302
328
  - test/logging/test_logging.rb
329
+ - test/test_run_command.rb
303
330
  homepage: https://github.com/nutella-framework/nutella_framework
304
331
  licenses:
305
332
  - MIT