reviser 0.0.1.1.pre.beta
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 +7 -0
- data/bin/reviser +6 -0
- data/config.yml +80 -0
- data/ext/html_validator.rb +21 -0
- data/ext/valgrind.rb +24 -0
- data/labels.yml +8 -0
- data/lang/C.yml +22 -0
- data/lang/HTML.yml +49 -0
- data/lang/Java.yml +15 -0
- data/lang/Ruby.yml +26 -0
- data/lib/component.rb +53 -0
- data/lib/components/archiver.rb +118 -0
- data/lib/components/checker.rb +78 -0
- data/lib/components/extractors.rb +139 -0
- data/lib/components/generator.rb +47 -0
- data/lib/components/generators.rb +130 -0
- data/lib/components/organiser.rb +181 -0
- data/lib/config.rb +48 -0
- data/lib/exec.rb +136 -0
- data/lib/helpers/code_analysis.rb +64 -0
- data/lib/helpers/compilation.rb +40 -0
- data/lib/helpers/criteria.rb +186 -0
- data/lib/helpers/execution.rb +83 -0
- data/lib/helpers/git.rb +37 -0
- data/lib/helpers/project.rb +39 -0
- data/lib/helpers/system.rb +56 -0
- data/lib/loggers/logger.rb +41 -0
- data/lib/loggers/modes.rb +74 -0
- data/lib/project.rb +155 -0
- data/lib/reviser.rb +68 -0
- data/res/css/component.css +115 -0
- data/res/css/normalize.css +1 -0
- data/res/js/jquery.stickyheader.js +149 -0
- data/type/CProject.yml +32 -0
- data/type/HelloWorldRuby.yml +2 -0
- data/type/HtmlASRALL.yml +1 -0
- data/type/Labyrinthe.yml +42 -0
- metadata +83 -0
@@ -0,0 +1,186 @@
|
|
1
|
+
require_relative '../config'
|
2
|
+
|
3
|
+
# Manage criteria and labels.
|
4
|
+
|
5
|
+
# @example Call a criterion (in the config File):
|
6
|
+
# criteria:
|
7
|
+
# - :count_lines: Number of lines
|
8
|
+
# - :list_files: List of all files
|
9
|
+
# - :<method>: <label of method>
|
10
|
+
#
|
11
|
+
# @author Yann Prono
|
12
|
+
# @author Renan Strauss
|
13
|
+
#
|
14
|
+
module Helpers
|
15
|
+
# This module enables to
|
16
|
+
# imports automaticlly all modules for the analysis
|
17
|
+
#
|
18
|
+
# Convention over configuration !
|
19
|
+
# A analysis module contains the word 'tool' in its filename.
|
20
|
+
# You also have the possibility to put code in the ext folder.
|
21
|
+
#
|
22
|
+
# @example Call a criterion during analysis (in the config File):
|
23
|
+
# criteria:
|
24
|
+
# - :count_lines
|
25
|
+
# - :list_files
|
26
|
+
# - :<method>: <custom label>
|
27
|
+
#
|
28
|
+
# In the last item of the list, the custom label will overwrite the label
|
29
|
+
# in labels.yml if it exist.
|
30
|
+
#
|
31
|
+
module Criteria
|
32
|
+
|
33
|
+
# Where I am ?
|
34
|
+
PWD = File.dirname __FILE__
|
35
|
+
# Path of extensions
|
36
|
+
EXT = File.join File.dirname(File.dirname(PWD)), 'ext'
|
37
|
+
|
38
|
+
attr_reader :criteria
|
39
|
+
attr_reader :output
|
40
|
+
|
41
|
+
# All criterias available.
|
42
|
+
# :criterion => Name of the module
|
43
|
+
@criteria
|
44
|
+
|
45
|
+
# :criterion => label of criterion
|
46
|
+
@output
|
47
|
+
|
48
|
+
# Enable to call a specified method.
|
49
|
+
# @param meth [String] Method to call.
|
50
|
+
# @return results of the method.
|
51
|
+
def call meth
|
52
|
+
if @criteria.key? meth
|
53
|
+
@logger.h1(Logger::INFO, "Include methods of #{@criteria[meth]}") unless respond_to? meth
|
54
|
+
self.class.send(:include, @criteria[meth]) unless respond_to? meth
|
55
|
+
|
56
|
+
send meth
|
57
|
+
else
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
# Get all criteria which can be used.
|
66
|
+
# @return [Array] all criteria
|
67
|
+
def all
|
68
|
+
@criteria.keys.map &:to_sym
|
69
|
+
end
|
70
|
+
|
71
|
+
# from Cfg file to symbols
|
72
|
+
# @param criterion The criteria
|
73
|
+
# @param module_name The name of the module.
|
74
|
+
def populate criterion, module_name
|
75
|
+
raise "Criterion '#{criterion}' is already defined in #{@criteria[criterion.to_sym]} (#{criterion}/#{module_name}).\nPlease change the name of the method in one of modules." if @criteria.has_key? criterion.to_sym
|
76
|
+
@criteria[criterion.to_sym] = module_name
|
77
|
+
end
|
78
|
+
|
79
|
+
# Load all of modules available for the analysis
|
80
|
+
# @param directory Directory where search of modules is done.
|
81
|
+
# @param regex regex to find name of modules.
|
82
|
+
def load directory, regex = '*'
|
83
|
+
@logger.h2 Logger::INFO, "Modules of #{directory}"
|
84
|
+
modules = Dir[File.join(directory, regex)]
|
85
|
+
|
86
|
+
namespace = directory == EXT && 'Extensions' || 'Helpers'
|
87
|
+
modules.each do |m|
|
88
|
+
next if m =~ /(criteria)/
|
89
|
+
|
90
|
+
require_relative m
|
91
|
+
ext = File.extname m
|
92
|
+
module_name = Object.const_get "#{namespace}::#{camelize(File.basename(m,ext))}", false
|
93
|
+
@logger.h3 Logger::INFO, "Load #{module_name}"
|
94
|
+
methods = module_name.instance_methods false
|
95
|
+
methods.each { |method| populate(method, module_name) }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Gets the name of module
|
100
|
+
# @param file_module Name of the file module.
|
101
|
+
def camelize file_module
|
102
|
+
file_module.split('_').each {|s| s.capitalize! }.join('')
|
103
|
+
end
|
104
|
+
|
105
|
+
# Load labels given by the user.
|
106
|
+
# If the label doesn't exist, it will created with the name of the method.
|
107
|
+
# @param key Key of criteria in config file
|
108
|
+
def load_labels key
|
109
|
+
labels = Labels.load
|
110
|
+
|
111
|
+
if Cfg.has_key?(key) && Cfg[key].respond_to?('each')
|
112
|
+
Cfg[key].each do |meth|
|
113
|
+
if meth.respond_to?('each') && meth.respond_to?('[]')
|
114
|
+
label = meth[meth.keys[0]]
|
115
|
+
@logger.h2(Logger::ERROR, "Undefined label for #{meth.keys[0]}, check your config file") if label == nil
|
116
|
+
label = create_label(meth.keys[0]) if label == nil
|
117
|
+
@output[meth.keys[0].to_sym] = label
|
118
|
+
else
|
119
|
+
label = (labels.respond_to?('[]') && labels.key?(meth.to_sym)) ? labels[meth.to_sym] : create_label(meth)
|
120
|
+
@output[meth.to_sym] = label
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Create label for a method.
|
127
|
+
# @param meth [String] method linked to the label
|
128
|
+
# @return [String] Renamed Label inspired of the name of the method
|
129
|
+
def create_label meth
|
130
|
+
@logger.h2 Logger::ERROR, "Create label for #{meth}. You should custom your label (see 'reviser add')"
|
131
|
+
meth.to_s.split('_').each {|s| s.capitalize! }.join(' ')
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
# Manage all actions for adding, updating or getting labels of Reviser.
|
137
|
+
# A label is a a group of words, describing the associated criterion (method).
|
138
|
+
#
|
139
|
+
# @example
|
140
|
+
# criterion => label
|
141
|
+
# all_files => all files of project
|
142
|
+
#
|
143
|
+
# known Labels are in the labels.yml file.
|
144
|
+
#
|
145
|
+
# @author Yann Prono
|
146
|
+
class Labels
|
147
|
+
|
148
|
+
# Current directory of this file
|
149
|
+
PWD = File.dirname __FILE__
|
150
|
+
|
151
|
+
# Path of label.yml file
|
152
|
+
LABELS = File.join(File.dirname(File.dirname(PWD)), 'labels.yml')
|
153
|
+
|
154
|
+
#
|
155
|
+
# Enable to associate a label to a criterion (method).
|
156
|
+
# The label will be saved in the 'labels.yml' file
|
157
|
+
# @param meth Method to link.
|
158
|
+
# @param label Label to link with the method.
|
159
|
+
def self.add meth, label
|
160
|
+
res = "Create"
|
161
|
+
labels = YAML.load File.open(LABELS)
|
162
|
+
if labels.respond_to? '[]'
|
163
|
+
res = "Update" if labels.key? meth
|
164
|
+
labels[meth] = label
|
165
|
+
File.open(LABELS, 'w') { |f| f.write labels.to_yaml }
|
166
|
+
end
|
167
|
+
res
|
168
|
+
end
|
169
|
+
|
170
|
+
# @return Hash all known labels by reviser.
|
171
|
+
# :criterion => label
|
172
|
+
def self.load
|
173
|
+
Labels.populate(YAML.load(File.open(LABELS)))
|
174
|
+
end
|
175
|
+
|
176
|
+
def self.populate hash
|
177
|
+
labels = {}
|
178
|
+
if hash.respond_to?('each')
|
179
|
+
hash.each do |meth, label|
|
180
|
+
labels[meth.to_sym] = label
|
181
|
+
end
|
182
|
+
end
|
183
|
+
labels
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
#
|
2
|
+
# @Author Renan Strauss
|
3
|
+
#
|
4
|
+
# Needed stuff for Checker
|
5
|
+
# when it comes to executing
|
6
|
+
# both compiled and interpreted
|
7
|
+
# languages
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'timeout'
|
11
|
+
|
12
|
+
module Helpers
|
13
|
+
module Execution
|
14
|
+
#
|
15
|
+
# Determines how to execute the program
|
16
|
+
# thanks to Cfg, then returns its exec
|
17
|
+
# status(es)
|
18
|
+
#
|
19
|
+
def execute
|
20
|
+
outputs = []
|
21
|
+
if Cfg.has_key? :execution_value
|
22
|
+
if Cfg[:execution_value].respond_to? 'each'
|
23
|
+
Cfg[:execution_value].each do |v|
|
24
|
+
outputs << exec(v)
|
25
|
+
end
|
26
|
+
else
|
27
|
+
if Cfg.has_key? :execution_count
|
28
|
+
outputs[Cfg[:execution_value]] = []
|
29
|
+
Cfg[:execution_count].times do
|
30
|
+
outputs << exec(Cfg[:execution_value])
|
31
|
+
end
|
32
|
+
else
|
33
|
+
outputs << exec(Cfg[:execution_value])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
else
|
37
|
+
if Cfg.has_key? :execution_count
|
38
|
+
Cfg[:execution_count].times do
|
39
|
+
outputs << exec
|
40
|
+
end
|
41
|
+
else
|
42
|
+
return exec
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
outputs.join("\r")
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
#
|
52
|
+
# The method that actually
|
53
|
+
# executes the program.
|
54
|
+
# If no program name is specified
|
55
|
+
# in the Cfg, it executes the
|
56
|
+
# first executable found.
|
57
|
+
# It helps with C (a.out) when no
|
58
|
+
# Makefile is avalaible, but it
|
59
|
+
# might not be a good idea regarding
|
60
|
+
# security
|
61
|
+
#
|
62
|
+
def exec(param = nil)
|
63
|
+
program = (Cfg.has_key? :program_name) && Cfg[:program_name] || find_executable
|
64
|
+
|
65
|
+
return 'Program not found' unless program != nil
|
66
|
+
|
67
|
+
program = "#{Cfg[:program_prefix]}#{program}"
|
68
|
+
argument = (param == nil) && '' || param
|
69
|
+
|
70
|
+
cmd = "#{(Cfg.has_key? :execute_command) && Cfg[:execute_command] || ''} #{program} #{argument}"
|
71
|
+
out = exec_with_timeout cmd
|
72
|
+
|
73
|
+
"$ #{cmd}\r#{out[:stdout]}\r#{out[:stderr]}"
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# @return the first executable found
|
78
|
+
#
|
79
|
+
def find_executable
|
80
|
+
Dir.glob('*').select {|f| File.executable?(f) && !File.directory?(f)}.first
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/helpers/git.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# Class which organizes all directories for use them with git.
|
2
|
+
# Its another component of the project
|
3
|
+
#
|
4
|
+
# @author Romain Ruez
|
5
|
+
# @author Anthony Cerf
|
6
|
+
#
|
7
|
+
require 'git'
|
8
|
+
|
9
|
+
module Helpers
|
10
|
+
module Git
|
11
|
+
# method which initialize a git repository
|
12
|
+
def git_init
|
13
|
+
@git = ::Git.init
|
14
|
+
end
|
15
|
+
|
16
|
+
# method which allows the user to add something on the repository
|
17
|
+
def git_add
|
18
|
+
@git.add(:all=>true)
|
19
|
+
end
|
20
|
+
|
21
|
+
# method for displaying a message when the repository is configured
|
22
|
+
def git_commit
|
23
|
+
@git.commit_all('initialization of git repertory')
|
24
|
+
end
|
25
|
+
|
26
|
+
def git_push
|
27
|
+
@git.push
|
28
|
+
end
|
29
|
+
|
30
|
+
# method which allows the user to see the differences between two last commits
|
31
|
+
# I have to know the current commit and the last but how ?
|
32
|
+
# and do a diff between these 2 commits.
|
33
|
+
def git_diff
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
#
|
2
|
+
# Provide important methods
|
3
|
+
# for compilation or something like that.
|
4
|
+
#
|
5
|
+
# @author Renan Strauss
|
6
|
+
# @author Yann Prono
|
7
|
+
#
|
8
|
+
module Helpers
|
9
|
+
module Project
|
10
|
+
#
|
11
|
+
# For interpreted languages
|
12
|
+
# We only check for missing files
|
13
|
+
#
|
14
|
+
def prepare
|
15
|
+
missing_files.empty? && 'None' || res
|
16
|
+
end
|
17
|
+
|
18
|
+
# Check if the project has all files needed
|
19
|
+
def missing_files
|
20
|
+
return [] unless Cfg =~ :required_files
|
21
|
+
|
22
|
+
dir = Dir['*']
|
23
|
+
|
24
|
+
#
|
25
|
+
# Check if there is any regexp
|
26
|
+
# If it's the case, if any file
|
27
|
+
# matches, we delete the entry
|
28
|
+
# for diff to work properly
|
29
|
+
#
|
30
|
+
Cfg[:required_files].each_with_index do |e, i|
|
31
|
+
if dir.any? { |f| (e.respond_to?(:match)) && (e =~ f) }
|
32
|
+
Cfg[:required_files].delete_at i
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
Cfg[:required_files] - dir
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
#
|
2
|
+
# @author Renan Strauss
|
3
|
+
#
|
4
|
+
|
5
|
+
module Helpers
|
6
|
+
module System
|
7
|
+
#
|
8
|
+
# Executes the given command
|
9
|
+
# and kills it if its execution
|
10
|
+
# time > timeout
|
11
|
+
# @returns stdout, stderr & process_status
|
12
|
+
#
|
13
|
+
def exec_with_timeout(cmd, timeout = Cfg[:timeout])
|
14
|
+
stdin, stdout, stderr, wait_thr = Open3.popen3(cmd)
|
15
|
+
process_status = -1
|
16
|
+
|
17
|
+
stdin.close
|
18
|
+
#
|
19
|
+
# We try to wait for the thread to join
|
20
|
+
# during the given timeout.
|
21
|
+
# When the thread has joined, process_status
|
22
|
+
# will be an object, so we can check and
|
23
|
+
# return at the end if it failed to complete
|
24
|
+
# before time runs out.
|
25
|
+
#
|
26
|
+
begin
|
27
|
+
Timeout.timeout(timeout) do
|
28
|
+
process_status = wait_thr.value
|
29
|
+
end
|
30
|
+
rescue Timeout::Error
|
31
|
+
#
|
32
|
+
# Then whether it suceeded or not,
|
33
|
+
# we kill the process
|
34
|
+
#
|
35
|
+
begin
|
36
|
+
Process.kill('KILL', wait_thr[:pid])
|
37
|
+
rescue Object => e
|
38
|
+
$stderr << "Unable to kill process : #{e.to_s}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
result = {
|
43
|
+
:stdout => process_status == -1 && 'Timeout' || stdout.read,
|
44
|
+
:stderr => process_status == -1 && 'Timeout' || stderr.read,
|
45
|
+
:process_status => process_status == -1 && 'Timeout' || process_status
|
46
|
+
}
|
47
|
+
|
48
|
+
result.delete :process_status unless process_status != -1
|
49
|
+
|
50
|
+
stdout.close
|
51
|
+
stderr.close
|
52
|
+
|
53
|
+
result
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require_relative 'modes'
|
3
|
+
|
4
|
+
# Custom logger of Reviser.
|
5
|
+
# This class is a adapter.
|
6
|
+
# We used the standard Logger included in Ruby.
|
7
|
+
#
|
8
|
+
# @author Yann Prono
|
9
|
+
#
|
10
|
+
module Loggers
|
11
|
+
class Logger
|
12
|
+
|
13
|
+
# Create logger.
|
14
|
+
# The extension determines the mode to use (logger mode).
|
15
|
+
# @filename
|
16
|
+
def initialize filename
|
17
|
+
|
18
|
+
ext = File.extname(filename).delete '.'
|
19
|
+
# Include mode aksed by user (config file)
|
20
|
+
begin
|
21
|
+
self.class.send :include, Modes.const_get("#{ext.downcase.capitalize}")
|
22
|
+
rescue => e
|
23
|
+
self.class.send :include, Modes::Txt
|
24
|
+
end
|
25
|
+
|
26
|
+
@logger = ::Logger.new filename
|
27
|
+
@logger.level = ::Logger::DEBUG
|
28
|
+
end
|
29
|
+
|
30
|
+
# Close the logger
|
31
|
+
def close
|
32
|
+
@logger.close
|
33
|
+
end
|
34
|
+
|
35
|
+
# In case of someone want to use methods of standard Logger ...
|
36
|
+
def method_missing(m, *args, &block)
|
37
|
+
@logger.send(m,*args, &block)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
#
|
4
|
+
# Modules containing all methods to custom logger.
|
5
|
+
#
|
6
|
+
# There are 3 main level of logger (as in HTML)
|
7
|
+
# => h1
|
8
|
+
# => h2
|
9
|
+
# => h3
|
10
|
+
|
11
|
+
# @author Yann Prono
|
12
|
+
# @author Anthony Cerf
|
13
|
+
module Loggers
|
14
|
+
module Modes
|
15
|
+
|
16
|
+
module Txt
|
17
|
+
|
18
|
+
include Modes
|
19
|
+
|
20
|
+
def h1 severity, msg
|
21
|
+
change_formatter ''
|
22
|
+
@logger.add severity, msg
|
23
|
+
end
|
24
|
+
|
25
|
+
def h2 severity, msg
|
26
|
+
change_formatter "\t\t"
|
27
|
+
@logger.add severity, msg
|
28
|
+
end
|
29
|
+
|
30
|
+
def h3 severity, msg
|
31
|
+
change_formatter "\t\t\t"
|
32
|
+
@logger.add severity, msg
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module Org
|
37
|
+
|
38
|
+
include Modes
|
39
|
+
|
40
|
+
def h1 severity, msg
|
41
|
+
change_formatter '*'
|
42
|
+
@logger.add severity, msg
|
43
|
+
end
|
44
|
+
|
45
|
+
def h2 severity, msg
|
46
|
+
change_formatter "**"
|
47
|
+
@logger.add severity, msg
|
48
|
+
end
|
49
|
+
|
50
|
+
def h3 severity, msg
|
51
|
+
change_formatter "***"
|
52
|
+
@logger.add severity, msg
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
# Change formatter
|
58
|
+
# @param prefix Prefix to put before all content
|
59
|
+
def change_formatter prefix
|
60
|
+
@logger.formatter = proc do |severity, datetime, progname, msg|
|
61
|
+
"\n#{prefix} #{severity} #{msg}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Create new line
|
66
|
+
def newline
|
67
|
+
@logger.formatter = proc do |severity, datetime, progname, msg|
|
68
|
+
"\n#{msg}"
|
69
|
+
end
|
70
|
+
@logger.add(nil,"\n")
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
data/lib/project.rb
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
# This modules is used to scan the name of project
|
2
|
+
# in order to get all students who worked.
|
3
|
+
# This analysis uses regex of convention given by teachers (config file).
|
4
|
+
#
|
5
|
+
# @author Yann Prono
|
6
|
+
#
|
7
|
+
module Project
|
8
|
+
|
9
|
+
# Dictionnary for regex in config file
|
10
|
+
SYMBOLS = {
|
11
|
+
:group => 'GROUP',
|
12
|
+
:firstname => 'FIRSTN',
|
13
|
+
:name => 'NAME',
|
14
|
+
:user => 'USER',
|
15
|
+
:lambda => 'LAMBDA'
|
16
|
+
}
|
17
|
+
|
18
|
+
# Regex to associate, depending the used word in Cfg
|
19
|
+
REGEX = {
|
20
|
+
:group => '([A-Za-z0-9]+)',
|
21
|
+
:firstname => '([A-Za-z\-]+)',
|
22
|
+
:name => '([A-Za-z]+)',
|
23
|
+
:user => '([^_]*)',
|
24
|
+
:lambda => '[a-zA-Z0-9 _]*'
|
25
|
+
}
|
26
|
+
|
27
|
+
|
28
|
+
# Get formatter written in the config file
|
29
|
+
# and count occurences of each word in the dictionnary SYMBOLS.
|
30
|
+
# @return [Hash] sym => count.
|
31
|
+
#
|
32
|
+
def analyze_formatter
|
33
|
+
regex = Cfg[:projects_names]
|
34
|
+
# Foreach known symbols
|
35
|
+
SYMBOLS.each do |k, _|
|
36
|
+
# Get numbers of occurences of the word k in regex
|
37
|
+
matches = regex.scan(SYMBOLS[k]).size
|
38
|
+
# the word K => number of occurences
|
39
|
+
@count_patterns[k] = matches if matches > 0
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
# Analyze et get all informations
|
45
|
+
# that could be useful in the name of the
|
46
|
+
# directory project.
|
47
|
+
# @param entry [String] name of directory to analysis.
|
48
|
+
#
|
49
|
+
def format entry
|
50
|
+
ext = File.extname entry
|
51
|
+
entry = File.basename entry, ext
|
52
|
+
|
53
|
+
analyze_formatter if @count_patterns.empty?
|
54
|
+
|
55
|
+
group = check_entry_name entry
|
56
|
+
generate_label group
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
# Generate new name of project.
|
61
|
+
# @param infos [Hash] All informations used for generate a name for the directory.
|
62
|
+
# @return [String] the formatted name for directory project
|
63
|
+
#
|
64
|
+
def generate_label infos
|
65
|
+
unless infos.empty?
|
66
|
+
label = ''
|
67
|
+
infos.reject { |k| k == :group }.each { |_, v|
|
68
|
+
if v.respond_to?('each')
|
69
|
+
v.each { |data|
|
70
|
+
label += data +' ' }
|
71
|
+
else
|
72
|
+
label += v + ' '
|
73
|
+
end
|
74
|
+
}
|
75
|
+
# Inject group of project before name : group/name
|
76
|
+
label = infos.key?(:group) && File.join(infos[:group], label) || label
|
77
|
+
label
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# I'm not pround of this method ...
|
82
|
+
# associate to a symbol, his position in the regex
|
83
|
+
# @example NAME_FIRSTN
|
84
|
+
# will give : {
|
85
|
+
# 1 => :name,
|
86
|
+
# 2 => :firstname
|
87
|
+
#}
|
88
|
+
def get_position regex
|
89
|
+
res = {}
|
90
|
+
SYMBOLS.each do |k,v|
|
91
|
+
regex.scan(v) do |_|
|
92
|
+
res[$~.offset(0)[0]] = k
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
res = (res.sort_by { |k, _| k }).to_h
|
97
|
+
tmp = {}
|
98
|
+
|
99
|
+
index = 1
|
100
|
+
res.each do |_,v|
|
101
|
+
tmp[index] = v
|
102
|
+
index += 1
|
103
|
+
end
|
104
|
+
tmp
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
# Apply regex of user on the entry name
|
109
|
+
# and try to get all interested matched values.
|
110
|
+
def check_entry_name entry
|
111
|
+
regex = Cfg[:projects_names]
|
112
|
+
# who work on the current project (entry) ?
|
113
|
+
position = get_position regex
|
114
|
+
|
115
|
+
@count_patterns.each do |k, _|
|
116
|
+
regex = regex.gsub SYMBOLS[k], REGEX[k]
|
117
|
+
end
|
118
|
+
|
119
|
+
# Apply created regex
|
120
|
+
entry.match Regexp.new(regex)
|
121
|
+
pos = 1
|
122
|
+
infos = {}
|
123
|
+
|
124
|
+
# Get matched values
|
125
|
+
begin
|
126
|
+
tmp = eval "$#{pos}"
|
127
|
+
if tmp != nil && tmp != ''
|
128
|
+
tmp = tmp.delete '_'
|
129
|
+
infos.has_key?(position[pos]) && infos[position[pos]] << tmp || infos[position[pos]] = [tmp]
|
130
|
+
else
|
131
|
+
ask entry
|
132
|
+
end
|
133
|
+
pos += 1
|
134
|
+
end while pos <= position.size
|
135
|
+
|
136
|
+
sort_infos infos
|
137
|
+
infos
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
# Put all datas found in respective variables (students, groups, teams ...).
|
142
|
+
# @param infos [Hash] Informations found by regex.
|
143
|
+
def sort_infos infos
|
144
|
+
infos[:name].respond_to?('each') && infos[:name].each { |n| @students << n } || @students << infos[:name]
|
145
|
+
infos[:group] = infos[:group][0].upcase if infos.key? :group
|
146
|
+
@groups << infos[:group] if infos.has_key?(:group) && !@groups.include?(infos[:group])
|
147
|
+
@binoms << infos[:name]
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
def ask entry
|
152
|
+
# TODO, ask to user if the entry is correctly written
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|