nutella_framework 0.1.2 → 0.2.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: b06d936ef60bf41dac3eb872609ba9906a50cecd
4
- data.tar.gz: e7a51bdbff02a0fa306eaf5267f80168c3563e30
3
+ metadata.gz: 2e2c581262282b014fb5dc0911616b442534da8c
4
+ data.tar.gz: 3908173709fc9ee02ba4cd339615dde180e4776c
5
5
  SHA512:
6
- metadata.gz: 23c35cb1768d1469bd29bd27b3eb4b8686c911082ec233556e9526188b544beee945bc2ff8005f588d64f0ed25f97fb41a4e3bafbd8b8c2be22a71f97003f67b
7
- data.tar.gz: 23aa14c916de0d105faf807bf548ea9f6c1249868609a80756d4ba7a53b578a77f330c08080408a6bda34eaccaf920a8687514a052462ea5d8cd593ddaa351a6
6
+ metadata.gz: 5715373d4edeac20b922d507261b496bb961bc71c3d812e3d5ffd74ddf5ed0904cf096b8929e10788bdae78750e39b40427af224ca65bf46386e3956c8aa0872
7
+ data.tar.gz: d7e8d4b278eb8df7275f209cc21828bad2e678ee0116c3d45556245713b8d835256285df2522ded4487607ec0cbf14f22ff95409b2270c4b04bb343861728d11
data/Gemfile CHANGED
@@ -6,11 +6,12 @@ gem 'logging', '~> 1.8', '>=1.8.2'
6
6
  gem 'git', '~> 1.2', '>=1.2.8'
7
7
  gem 'thin', '~> 1.6.3', '>= 1.6.3'
8
8
  gem 'sinatra', '~>1.4.5', '>=1.4.5'
9
- gem 'erubis', '~>2.7.0', '>=2.7.0'
9
+ gem 'nokogiri', '~>1.6.3', '>=1.6.3'
10
+
10
11
 
11
12
  group :development do
12
13
  gem 'shoulda', '~> 3', '>= 3'
13
- gem "yard", '~> 0.8', '>= 0.8.7'
14
+ gem 'yard', '~> 0.8', '>= 0.8.7'
14
15
  gem 'rdoc', '~> 4.0', '>= 4.0'
15
16
  gem 'bundler', '~> 1.0', '>= 1.0'
16
17
  gem 'jeweler', '~> 2.0.1', '>= 2.0.1'
@@ -18,5 +19,5 @@ group :development do
18
19
  end
19
20
 
20
21
  group :test do
21
- gem "rake"
22
+ gem 'rake'
22
23
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.2
1
+ 0.2.0
@@ -0,0 +1,148 @@
1
+ require 'json'
2
+ require 'sinatra'
3
+ require 'nokogiri'
4
+
5
+ # Configuration file and runlist (runlist as global)
6
+ config_file = ARGV[0]
7
+ $runlist_file = ARGV[1]
8
+
9
+ # Try to parse the both config file and runlist and terminate if we can't
10
+ begin
11
+ config_h = JSON.parse(IO.read(config_file))
12
+ JSON.parse(IO.read($runlist_file))
13
+ rescue
14
+ # something went wrong
15
+ abort 'Impossible to parse configuration and/or runlist files!'
16
+ end
17
+
18
+ # Set Sinatra's port to nutella's main_interface_port
19
+ set :port, config_h['main_interface_port']
20
+
21
+
22
+ # Display the form to input the run_id
23
+ get '/' do
24
+ send_file File.join("#{config_h['nutella_home']}/actors/main_interface/public", 'index.html')
25
+ end
26
+
27
+
28
+ # Renders the run template
29
+ get '/:run_id' do
30
+
31
+ # Parse the run_id from URL and extract the run path from runlist.json
32
+ @run_id = params[:run_id]
33
+ @run_path = get_run_path @run_id
34
+ @project_name = @run_path[@run_path.rindex('/')+1..@run_path.length] unless @run_path.nil?
35
+
36
+ # If there is no run with this name, render error page
37
+ return erb( :not_found_404, :locals => {:not_found_type => 'run'} ) if @run_path.nil?
38
+
39
+ # If there is an 'index.erb' file inside the 'interfaces' folder, render it
40
+ custom_index_file = "#{@run_path}/interfaces/index.erb"
41
+ return erb(File.read( custom_index_file )) if File.exist? custom_index_file
42
+
43
+ # If no 'index.erb' is provided we need to generate one
44
+ # In order to do so we need to load a bunch of details
45
+ # (folder, title/name, description) for each interface
46
+ @interfaces = load_interfaces_details
47
+
48
+ # Finally render the interfaces summary page
49
+ erb :index
50
+ end
51
+
52
+
53
+ # Serves the index.html file for each individual interface augmented with nutella query string parameters
54
+ get '/:run_id/:interface' do
55
+
56
+ # Parse the run_id and the interface name from URL
57
+ run_id = params[:run_id]
58
+ interface = params[:interface]
59
+
60
+ # Extract the run path from runlist.json
61
+ run_path = get_run_path run_id
62
+
63
+ # Compose the path of interface index file
64
+ index_file_path = "#{run_path}/interfaces/#{interface}/index.html"
65
+
66
+ # If the index file doesn't exist, render error page
67
+ return erb( :not_found_404, :locals => {:not_found_type => 'idx'} ) unless File.exist? index_file_path
68
+
69
+ # If the index file exists, compose query string and redirect
70
+ index_with_query_url = "#{request.path}/index.html?run_id=#{run_id}&broker=#{config_h['broker']}"
71
+ redirect index_with_query_url
72
+ end
73
+
74
+
75
+ # Serves the files contained in each interface folder
76
+ get '/:run_id/:interface/*' do
77
+
78
+ # Parse the run_id, the interface name and the file_path from URL
79
+ run_id = params[:run_id]
80
+ interface = params[:interface]
81
+ relative_file_path = params[:splat][0]
82
+
83
+ # Extract the run path from runlist.json
84
+ run_path = get_run_path run_id
85
+
86
+ # Compose the path of the file we are trying to serve
87
+ file_path = "#{run_path}/interfaces/#{interface}/#{relative_file_path}"
88
+
89
+ # If the file we are trying to serve doesn't exist, render error page
90
+ return erb( :not_found_404, :locals => {:not_found_type => 'file'} ) unless File.exist? file_path
91
+
92
+ # If the file exists, render it
93
+ send_file file_path
94
+ end
95
+
96
+
97
+ # Utility function:
98
+ # Gets the path associated with a certain run
99
+ def get_run_path (run_id)
100
+ begin
101
+ runs_h = JSON.parse(IO.read($runlist_file))
102
+ runs_h[run_id]
103
+ rescue
104
+ nil
105
+ end
106
+ end
107
+
108
+ # Utility function:
109
+ # Loads all the details for all interfaces and stores them into an array of hashes
110
+ def load_interfaces_details
111
+ interfaces = Array.new
112
+ interfaces_path = "#{@run_path}/interfaces/"
113
+ Dir.entries(interfaces_path).select {|entry| File.directory?(File.join(interfaces_path, entry)) && !(entry =='.' || entry == '..') }.each do |iface_dir|
114
+ interfaces.push extract_interface_info( interfaces_path, iface_dir )
115
+ end
116
+ interfaces
117
+ end
118
+
119
+ # Utility function:
120
+ # Extracts name, description and folder for a single interface
121
+ def extract_interface_info( interfaces_path, iface_dir )
122
+ iface_props = Hash.new
123
+
124
+ index_path = "#{interfaces_path}#{iface_dir}/index.html"
125
+
126
+ unless File.exist? index_path
127
+ iface_props[:name] = iface_dir
128
+ iface_props[:description] = 'My designer was a lazy ass and didn\'t include an index.html file in the main interface directory'
129
+ return iface_props
130
+ end
131
+
132
+ # If file exists, parse it and extract info
133
+ f = File.open index_path
134
+ doc = Nokogiri::HTML f
135
+ f.close
136
+ iface_props[:name] = doc.css('title').empty? ? iface_dir : doc.css('title').text
137
+ if doc.css("meta[name='description']").empty?
138
+ 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'
139
+ else
140
+ if doc.css("meta[name='description']").attribute('content').nil?
141
+ iface_props[:description] = 'There was no attribute content in <meta name="description" content="Description of this interface"> tag in the index.html file'
142
+ else
143
+ iface_props[:description] = doc.css("meta[name='description']").attribute('content').text
144
+ end
145
+ end
146
+ iface_props[:url] = "#{@run_id}/#{iface_dir}"
147
+ iface_props
148
+ end
@@ -0,0 +1,47 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+ <title>Welcome to nutella</title>
8
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
9
+
10
+ <style type="text/css">
11
+ div {
12
+ margin-top: 4em;
13
+ text-align: center;
14
+ font-size: 3em;
15
+ }
16
+ .form-control {
17
+ height: 50px;
18
+ width: 40%;
19
+ text-align: center;
20
+ font-size: .7em;
21
+ display: inline-block;
22
+ }
23
+ .center {
24
+ text-align:center;
25
+ }
26
+ .center form {
27
+ display:inline-block;
28
+ }
29
+ </style>
30
+ </head>
31
+ <body>
32
+
33
+ <!-- Content -->
34
+ <div>
35
+ <p>What is your run id?</p>
36
+ <input id="run_id_input" type="text" value="" class="form-control" autofocus>
37
+ </div>
38
+
39
+ <!-- Scripts -->
40
+ <script>
41
+ el = document.getElementById('run_id_input');
42
+ el.addEventListener('change', function() {
43
+ window.location = '/' + this.value
44
+ });
45
+ </script>
46
+ </body>
47
+ </html>
@@ -0,0 +1,5 @@
1
+ #!/bin/sh
2
+
3
+ BASEDIR=$(dirname $0)
4
+ ruby $BASEDIR/main_interface_bot.rb $1 $2 &>/dev/null &
5
+ echo $! > $BASEDIR/.pid
@@ -0,0 +1,45 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+ <title><%= @run_id %> interfaces</title>
8
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
9
+
10
+ <style type="text/css">
11
+ .row {
12
+ margin-bottom: 20px;
13
+ }
14
+ </style>
15
+ </head>
16
+ <body>
17
+
18
+ <!-- Content -->
19
+ <div class="container">
20
+ <div class="page-header">
21
+ <% if @run_id == @project_name %>
22
+ <h1>Welcome to <em><%= @project_name %></em></h1>
23
+ <% else %>
24
+ <h1>Welcome to <em><%= @project_name %></em>, run <em><%= @run_id %></em></h1>
25
+ <% end %>
26
+ <p class="lead">Click on the buttons to launch the intereface</p>
27
+ </div>
28
+
29
+ <% @interfaces.each do |i| %>
30
+ <div class="row">
31
+ <div class="col-xs-1 col-sm-1 col-lg-1"><a href="<%= i[:url] %>" target="_blank" type="button" class="btn btn-danger">Launch!</a></div>
32
+ <div class="col-xs-2 col-sm-2 col-lg-2 text-center"><strong><%= i[:name] %></strong></div>
33
+ <div class="col-xs-9 col-sm-9 col-lg-9"><%= i[:description] %></div>
34
+ </div>
35
+
36
+ <% end %>
37
+
38
+ </div>
39
+
40
+ <!-- Scripts -->
41
+ <script>
42
+
43
+ </script>
44
+ </body>
45
+ </html>
@@ -0,0 +1,37 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+ <title>Run not found</title>
8
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
9
+
10
+ <style type="text/css">
11
+ div {
12
+ margin-top: 4em;
13
+ text-align: center;
14
+ font-size: 3em;
15
+ }
16
+ </style>
17
+ </head>
18
+ <body>
19
+
20
+ <!-- Content -->
21
+ <div>
22
+ <% if locals[:not_found_type] == 'run' %>
23
+ <p>Sorry, we don't have that run ID!</p>
24
+ <p>(?_?)</p>
25
+ <% elsif locals[:not_found_type] == 'idx' %>
26
+ <p>Sorry, this interface doesn't seem to exist for this application</p>
27
+ <p>(゜_゜)</p>
28
+ <% elsif locals[:not_found_type] == 'file' %>
29
+ <p>The page you are looking for doesn't seem to exist</p>
30
+ <p>(;_;)</p>
31
+ <% end %>
32
+
33
+
34
+ </div>
35
+
36
+ </body>
37
+ </html>
@@ -10,33 +10,42 @@ module Nutella
10
10
  |_| |_|\\__,_|\\__\\___|_|_|\\__,_|
11
11
  "
12
12
 
13
- # Reads the parameters and executes commands
13
+ # Nutella entry point. Every time the "nutella" command is invoked this is
14
+ # the method that gets called.
15
+ # It reads the command line parameters and it invokes the right sub-command
14
16
  def self.run
15
17
  # Read parameters
16
18
  args = ARGV.dup
17
19
  args.shift
18
- # Check that the command is not empty, if so, print the prompt
20
+
21
+ # Check that the command is not empty, if so, simply print the nutella logo
19
22
  command = ARGV.first
20
23
  if command == nil
21
- printPrompt
24
+ print_nutella_logo
22
25
  exit 0
23
26
  end
24
- # Prepend warning if nutella is not ready
25
- if (Nutella.config["ready"].nil? && command!="checkup")
26
- console.warn "Looks like this is a fresh installation of nutella. Please run `nutella checkup` to check all dependencies are installed."
27
+
28
+ # If nutella is not ready to be used (i.e. nobody has invoked the "nutella checkup" command yet),
29
+ # append warning/reminder message
30
+ if Nutella.config['ready'].nil? && command!='checkup'
31
+ console.warn 'Looks like this is a fresh installation of nutella. Please run \'nutella checkup\' to check all dependencies are installed.'
27
32
  end
28
- Nutella.executeCommand command, args
33
+
34
+ # Execute the appropriate command
35
+ Nutella.execute_command command, args
29
36
  exit 0
30
37
  end
31
-
32
- # Print Nutella logo
33
- def self.printPrompt
38
+
39
+
40
+ # Print nutella logo
41
+ def self.print_nutella_logo
34
42
  console.info(NUTELLA_LOGO)
35
- nutella_version = File.open(NUTELLA_HOME+"VERSION", "rb").read
36
- console.info("Welcome to nutella version #{nutella_version}! For a complete lists of available commands type `nutella help`\n")
37
- # Append warning if nutella is not ready
38
- if (Nutella.config["ready"].nil?)
39
- console.warn "Looks like this is a fresh installation of nutella. Please run `nutella checkup` to check all dependencies are installed."
43
+ nutella_version = File.open("#{Nutella.config['nutella_home']}VERSION", 'rb').read
44
+ console.info("Welcome to nutella version #{nutella_version}! For a complete lists of available commands type 'nutella help'\n")
45
+ # If nutella is not ready to be used (i.e. nobody has invoked the "nutella checkup" command yet),
46
+ # append warning/reminder message
47
+ if Nutella.config['ready'].nil?
48
+ console.warn 'Looks like this is a fresh installation of nutella. Please run \'nutella checkup\' to check all dependencies are installed.'
40
49
  end
41
50
  end
42
51
  end
data/lib/config/config.rb CHANGED
@@ -1,73 +1,11 @@
1
- # This handles the configuration files of Nutella
2
- # It's basically a hash overload that stores into a file
3
- require 'json'
1
+ require 'config/persisted_hash'
4
2
 
5
3
  module Nutella
6
-
7
- class ConfigHash
8
-
9
- def initialize(file)
10
- @config_file=file
11
- end
12
-
13
-
14
- def []=(key,val)
15
- hash = loadConfig
16
- hash[key]=val
17
- storeConfig hash
18
- end
19
-
20
- def [](key)
21
- hash = loadConfig
22
- hash[key]
23
- end
24
-
25
- def empty?
26
- hash = loadConfig
27
- hash.empty?
28
- end
29
-
30
- def has_key?(key)
31
- hash = loadConfig
32
- hash.has_key? key
33
- end
34
-
35
- def to_s
36
- hash = loadConfig
37
- hash.to_s
38
- end
39
-
40
- def to_h
41
- hash = loadConfig
42
- hash
43
- end
44
-
45
- private
46
-
47
- def storeConfig(hash)
48
- File.open(@config_file, "w+") do |f|
49
- f.write(JSON.pretty_generate(hash))
50
- end
51
- File.chmod(0777,@config_file)
52
- end
53
-
54
- def loadConfig
55
- begin
56
- return JSON.parse IO.read @config_file
57
- rescue
58
- # File doesn't exist... do nothing
59
- Hash.new
60
- end
61
- end
62
-
63
- def removeConfigFile
64
- File.delete(@config_file) if File.exist?(@config_file)
65
- end
66
-
67
- end
68
-
4
+
5
+ # Calling this method (Nutella.config) simply returns and instance of
6
+ # PersistedHash linked to file config.json in nutella home directory
69
7
  def Nutella.config
70
- ConfigHash.new(File.dirname(__FILE__)+"/../../config.json")
8
+ PersistedHash.new("#{File.dirname(__FILE__)}/../../config.json")
71
9
  end
72
10
 
73
11
  end
@@ -0,0 +1,58 @@
1
+ # handles current project files
2
+ require 'json'
3
+
4
+ module Nutella
5
+
6
+ module CurrentProjectUtils
7
+
8
+ # Checks that the current directory is actually a nutella project
9
+ # @return [Boolean] true if the current directory is a nutella project, false otherwise
10
+ def CurrentProjectUtils.exist?
11
+ cur_prj_dir = Dir.pwd
12
+ nutella_json_file = "#{cur_prj_dir}/nutella.json"
13
+ # Check that there is a nutella.json file in the main directory of the project
14
+ if File.exist? nutella_json_file
15
+ conf = JSON.parse( IO.read(nutella_json_file) )
16
+ if conf['nutella_version'].nil?
17
+ console.warn 'The current directory is not a nutella project: nutella_version unspecified in nutella.json file'
18
+ return false
19
+ end
20
+ else
21
+ console.warn 'The current directory is not a nutella project: impossible to read nutella.json file'
22
+ return false
23
+ end
24
+ true
25
+ end
26
+
27
+ # Builds a PersistedHash of the project nutella.json file and returns it.
28
+ # This method is used to ease access to the project nutella.json file.
29
+ # @return [PersistedHash] the PersistedHash of the project nutella.json file
30
+ def CurrentProjectUtils.config
31
+ cur_prj_dir = Dir.pwd
32
+ nutella_json_file = "#{cur_prj_dir}/nutella.json"
33
+ if File.exist? nutella_json_file
34
+ return PersistedHash.new nutella_json_file
35
+ else
36
+ console.error 'The current directory is not a nutella project: impossible to read nutella.json file'
37
+ end
38
+ end
39
+
40
+ # Retrieves the current project directory
41
+ # @return [String] the current project home
42
+ def CurrentProjectUtils.dir
43
+ Dir.pwd
44
+ end
45
+
46
+ end
47
+
48
+
49
+ # Calling this method (Nutella.current_project) simply returns
50
+ # a reference to the CurrentProjectUtils module
51
+ def Nutella.current_project
52
+ CurrentProjectUtils
53
+ end
54
+
55
+ end
56
+
57
+
58
+
@@ -0,0 +1,114 @@
1
+ require 'json'
2
+
3
+ module Nutella
4
+
5
+ # This class behaves *similarly* to a regular Hash but it persists every operation
6
+ # to the json file passed in the constructor. Not all Hash operations are supported
7
+ # and we added some of our own.
8
+ class PersistedHash
9
+
10
+ def initialize(file)
11
+ @config_file=file
12
+ end
13
+
14
+ def []( key )
15
+ hash = load_hash
16
+ hash[key]
17
+ end
18
+
19
+ def []=( key, val )
20
+ hash = load_hash
21
+ hash[key]=val
22
+ store_hash hash
23
+ end
24
+
25
+ def delete( key )
26
+ hash = load_hash
27
+ return_value = hash.delete key
28
+ store_hash hash
29
+ return_value
30
+ end
31
+
32
+ def empty?
33
+ hash = load_hash
34
+ hash.empty?
35
+ end
36
+
37
+ def has_key?( key )
38
+ hash = load_hash
39
+ hash.has_key? key
40
+ end
41
+
42
+ def include?( key )
43
+ has_key? key
44
+ end
45
+
46
+ def to_s
47
+ hash = load_hash
48
+ hash.to_s
49
+ end
50
+
51
+ def to_h
52
+ load_hash
53
+ end
54
+
55
+ def keys
56
+ hash = load_hash
57
+ hash.keys
58
+ end
59
+
60
+ def length
61
+ hash = load_hash
62
+ hash.length
63
+ end
64
+
65
+ # PersistedHash-only public methods
66
+
67
+ # Adds a <key, value> pair to the PersistedHash _only if_
68
+ # there is currently no value associated with the specified key.
69
+ # @return [Boolean] false if the key already exists, true if the
70
+ # <key, value> pair was added successfully
71
+ def add?(key, val)
72
+ hash = load_hash
73
+ return false if hash.key? key
74
+ hash[key] = val
75
+ store_hash hash
76
+ true
77
+ end
78
+
79
+ # Removes a <key, value> pair from the PersistedHash _only if_
80
+ # there is currently a value associated with the specified key.
81
+ # @return [Boolean] false if there is no value associated with
82
+ # the specified key, true otherwise
83
+ def delete?( key )
84
+ hash = load_hash
85
+ return false if hash.delete(key).nil?
86
+ store_hash hash
87
+ true
88
+ end
89
+
90
+ private
91
+
92
+ def store_hash(hash)
93
+ File.open(@config_file, 'w+') do |f|
94
+ f.write(JSON.pretty_generate(hash))
95
+ end
96
+ File.chmod(0777, @config_file)
97
+ end
98
+
99
+ def load_hash
100
+ begin
101
+ return JSON.parse IO.read @config_file
102
+ rescue
103
+ # File doesn't exist, return new empty Hash
104
+ Hash.new
105
+ end
106
+ end
107
+
108
+ def remove_file
109
+ File.delete(@config_file) if File.exist?(@config_file)
110
+ end
111
+
112
+ end
113
+
114
+ end