nutella_framework 0.2.1 → 0.3.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 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