liri 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rakeTasks +7 -0
- data/.rspec +8 -0
- data/.rubocop.yml +15 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +32 -0
- data/Gemfile.lock +76 -0
- data/LICENSE.txt +21 -0
- data/README.md +161 -0
- data/Rakefile +61 -0
- data/bin/bundle +114 -0
- data/bin/coderay +29 -0
- data/bin/commander +29 -0
- data/bin/htmldiff +29 -0
- data/bin/ldiff +29 -0
- data/bin/liri +29 -0
- data/bin/rake +29 -0
- data/bin/rspec +29 -0
- data/bin/rubocop +29 -0
- data/bin/ruby-parse +29 -0
- data/bin/ruby-rewrite +29 -0
- data/bin/yard +29 -0
- data/bin/yardoc +29 -0
- data/bin/yri +29 -0
- data/config/locales/to_duration_es.yml +25 -0
- data/exe/liri +73 -0
- data/lib/agent/agent.rb +235 -0
- data/lib/agent/runner.rb +19 -0
- data/lib/all_libraries.rb +30 -0
- data/lib/common/benchmarking.rb +36 -0
- data/lib/common/compressor/zip.rb +70 -0
- data/lib/common/log.rb +121 -0
- data/lib/common/source_code.rb +53 -0
- data/lib/common/unit_test/rspec.rb +91 -0
- data/lib/exe/agent.rb +5 -0
- data/lib/exe/manager.rb +5 -0
- data/lib/hash_extend.rb +57 -0
- data/lib/liri.rb +129 -0
- data/lib/manager/credential.rb +47 -0
- data/lib/manager/manager.rb +258 -0
- data/lib/manager/setup.rb +70 -0
- data/lib/manager/test_result.rb +38 -0
- data/lib/task.rb +15 -0
- data/liri.gemspec +58 -0
- data/template/liri-config.yml +28 -0
- metadata +182 -0
data/lib/agent/runner.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
=begin
|
2
|
+
Esta clase se encarga de ejecutar las pruebas unitarias recibidas del Manager
|
3
|
+
=end
|
4
|
+
|
5
|
+
module Liri
|
6
|
+
class Agent
|
7
|
+
class Runner
|
8
|
+
def initialize(unit_test_class, source_code_folder_path)
|
9
|
+
@unit_test = Object.const_get(unit_test_class).new(source_code_folder_path)
|
10
|
+
end
|
11
|
+
|
12
|
+
def run_tests(tests)
|
13
|
+
result_hash = @unit_test.run_tests(tests.values)
|
14
|
+
result_hash[:test_keys] = tests.keys
|
15
|
+
result_hash
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# = all_libraries.rb
|
2
|
+
#
|
3
|
+
# Autor:: Rodrigo Fernández
|
4
|
+
# Web:: http://www.something.com
|
5
|
+
#
|
6
|
+
# Este archivo se encarga de importar todas las librerías a utilizar
|
7
|
+
|
8
|
+
# Se utiliza la librería *socket* de Ruby para realizar una conexión udp
|
9
|
+
require 'socket'
|
10
|
+
require 'net/ssh'
|
11
|
+
require 'net/scp'
|
12
|
+
|
13
|
+
# El archivo *hash_extend* extiende la clase Hash de Ruby para agregarle más funcionalidades
|
14
|
+
require 'hash_extend'
|
15
|
+
|
16
|
+
require 'task'
|
17
|
+
|
18
|
+
require 'agent/agent'
|
19
|
+
require 'agent/runner'
|
20
|
+
|
21
|
+
require 'common/benchmarking'
|
22
|
+
require 'common/log'
|
23
|
+
require 'common/source_code'
|
24
|
+
require 'common/compressor/zip'
|
25
|
+
require 'common/unit_test/rspec'
|
26
|
+
|
27
|
+
require 'manager/manager'
|
28
|
+
require 'manager/setup'
|
29
|
+
require 'manager/credential'
|
30
|
+
require 'manager/test_result'
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# = benchmarking.rb
|
2
|
+
#
|
3
|
+
# @author Rodrigo Fernández
|
4
|
+
#
|
5
|
+
# == Módulo Benchmarking
|
6
|
+
# Este módulo se encarga de medir el tiempo de ejecución de algunos bloques de código
|
7
|
+
|
8
|
+
require 'benchmark'
|
9
|
+
require 'i18n' # requirimiento de la gema to_duration
|
10
|
+
require 'to_duration'
|
11
|
+
|
12
|
+
# Se configura la ubicación del archivo de internacionalización de la gema to_duration
|
13
|
+
I18n.load_path << Dir[File.join(File.dirname(File.dirname(File.dirname(__FILE__))), 'config/locales') + "/*.yml"]
|
14
|
+
I18n.default_locale = :es
|
15
|
+
|
16
|
+
module Liri
|
17
|
+
module Common
|
18
|
+
module Benchmarking
|
19
|
+
class << self
|
20
|
+
def start(start_msg: nil, end_msg: 'Duración: ')
|
21
|
+
print start_msg
|
22
|
+
seconds = Benchmark.realtime do
|
23
|
+
yield
|
24
|
+
end
|
25
|
+
print_result(end_msg, seconds)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def print_result(msg, seconds)
|
31
|
+
print "#{msg}#{seconds.to_duration}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
=begin
|
2
|
+
Esta clase se encarga de comprimir y descomprimir archivos
|
3
|
+
=end
|
4
|
+
require 'zip'
|
5
|
+
|
6
|
+
module Liri
|
7
|
+
module Common
|
8
|
+
module Compressor
|
9
|
+
class Zip
|
10
|
+
# Inicializa la carpeta a comprimir y la ubicación en donde se guardará el archivo comprimido
|
11
|
+
def initialize(input_dir, output_file)
|
12
|
+
@input_dir = input_dir
|
13
|
+
@output_file = output_file
|
14
|
+
end
|
15
|
+
|
16
|
+
# Comprime el directorio de entrada @input_dir en un archivo con extensión zip.
|
17
|
+
def compress
|
18
|
+
clear_output_file
|
19
|
+
entries = Dir.entries(@input_dir) - %w[. ..]
|
20
|
+
|
21
|
+
::Zip::File.open(@output_file, ::Zip::File::CREATE) do |zipfile|
|
22
|
+
write_entries(entries, '', zipfile)
|
23
|
+
end
|
24
|
+
true
|
25
|
+
end
|
26
|
+
|
27
|
+
def decompress(zip_dir, dir_destination)
|
28
|
+
FileUtils.mkdir_p(dir_destination)
|
29
|
+
::Zip::File.open(File.expand_path(zip_dir)) do |zip_file|
|
30
|
+
zip_file.each do |f|
|
31
|
+
fpath = File.join(dir_destination, f.name)
|
32
|
+
FileUtils.mkdir_p(File.dirname(fpath))
|
33
|
+
zip_file.extract(f, fpath) unless File.exist?(fpath)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def clear_output_file
|
41
|
+
File.delete(@output_file) if File.exist?(@output_file)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Un método de ayuda que hace que la recursión funcione
|
45
|
+
def write_entries(entries, path, zipfile)
|
46
|
+
entries.each do |e|
|
47
|
+
zipfile_path = path == '' ? e : File.join(path, e)
|
48
|
+
disk_file_path = File.join(@input_dir, zipfile_path)
|
49
|
+
|
50
|
+
if File.directory? disk_file_path
|
51
|
+
recursively_deflate_directory(disk_file_path, zipfile, zipfile_path)
|
52
|
+
else
|
53
|
+
put_into_archive(disk_file_path, zipfile, zipfile_path)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def recursively_deflate_directory(disk_file_path, zipfile, zipfile_path)
|
59
|
+
zipfile.mkdir(zipfile_path)
|
60
|
+
subdir = Dir.entries(disk_file_path) - %w[. ..]
|
61
|
+
write_entries(subdir, zipfile_path, zipfile)
|
62
|
+
end
|
63
|
+
|
64
|
+
def put_into_archive(disk_file_path, zipfile, zipfile_path)
|
65
|
+
zipfile.add(zipfile_path, disk_file_path)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/common/log.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
# = log.rb
|
2
|
+
#
|
3
|
+
# @author Rodrigo Fernández
|
4
|
+
#
|
5
|
+
# == Clase Log
|
6
|
+
# Esta clase se encarga de manejar el logging
|
7
|
+
|
8
|
+
require 'logger'
|
9
|
+
|
10
|
+
module Liri
|
11
|
+
module Common
|
12
|
+
class Log
|
13
|
+
FOLDER_NAME = 'logs'
|
14
|
+
FOLDER_PATH = File.join(Dir.pwd, "/#{FOLDER_NAME}")
|
15
|
+
FILE_NAME = 'liri.log'
|
16
|
+
|
17
|
+
def initialize(shift_age, folder_path:, file_name:, stdout: true)
|
18
|
+
@stdout = stdout
|
19
|
+
@shift_age = shift_age
|
20
|
+
@folder_path = folder_path || FOLDER_PATH
|
21
|
+
@file_name = file_name || FILE_NAME
|
22
|
+
@file_path = File.join(@folder_path, '/', @file_name)
|
23
|
+
|
24
|
+
create_stdout_logger if @stdout
|
25
|
+
create_log_folder
|
26
|
+
create_file_logger
|
27
|
+
end
|
28
|
+
|
29
|
+
def debug(text)
|
30
|
+
@stdout_logger.debug(text) if @stdout
|
31
|
+
@file_logger.debug(text)
|
32
|
+
end
|
33
|
+
|
34
|
+
def info(text)
|
35
|
+
@stdout_logger.info(text) if @stdout
|
36
|
+
@file_logger.info(text)
|
37
|
+
end
|
38
|
+
|
39
|
+
def warn(text)
|
40
|
+
@stdout_logger.warn(text) if @stdout
|
41
|
+
@file_logger.warn(text)
|
42
|
+
end
|
43
|
+
|
44
|
+
def error(text)
|
45
|
+
@stdout_logger.error(text) if @stdout
|
46
|
+
@file_logger.error(text)
|
47
|
+
end
|
48
|
+
|
49
|
+
def fatal(text)
|
50
|
+
@stdout_logger.fatal(text) if @stdout
|
51
|
+
@file_logger.fatal(text)
|
52
|
+
end
|
53
|
+
|
54
|
+
def unknown(text)
|
55
|
+
@stdout_logger.unknown(text) if @stdout
|
56
|
+
@file_logger.unknown(text)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
def create_stdout_logger
|
61
|
+
@stdout_logger = Logger.new(STDOUT, @shift_age)
|
62
|
+
@stdout_logger.formatter = Liri::Common::LogFormatter.colorize(Liri.setup.log.stdout.colorize)
|
63
|
+
end
|
64
|
+
|
65
|
+
def create_file_logger
|
66
|
+
@file_logger = Logger.new(@file_path, @shift_age)
|
67
|
+
@file_logger.formatter = Liri::Common::LogFormatter.colorize(Liri.setup.log.file.colorize)
|
68
|
+
end
|
69
|
+
|
70
|
+
def create_log_folder
|
71
|
+
Dir.mkdir(@folder_path) unless Dir.exist?(@folder_path)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class LogFormatter
|
76
|
+
DATETIME_FORMAT = "%d-%m-%Y %H:%M"
|
77
|
+
|
78
|
+
SEVERITY_COLORS = {
|
79
|
+
DEBUG: '0;36', # cyan
|
80
|
+
ERROR: '0;31', # red
|
81
|
+
INFO: '0;32', # green
|
82
|
+
WARN: '0;33', # orange
|
83
|
+
FATAL: '0;35', # pink
|
84
|
+
ANY: '0;36', # cyan
|
85
|
+
DEFAULT: '1;0' # white
|
86
|
+
}
|
87
|
+
|
88
|
+
class << self
|
89
|
+
def colorize(type)
|
90
|
+
proc do |severity, datetime, progname, msg|
|
91
|
+
formatted_date = datetime.strftime(DATETIME_FORMAT)
|
92
|
+
severity_abb_block = "#{severity.slice(0)}"
|
93
|
+
date_block = "[#{formatted_date}##{Process.pid}]"
|
94
|
+
severity_block = "#{severity} -- :"
|
95
|
+
msg_block = "#{msg}\n"
|
96
|
+
|
97
|
+
case type
|
98
|
+
when 'severity' then
|
99
|
+
info_block = "#{colorize_according_severity(severity, severity_abb_block)} #{date_block} #{colorize_according_severity(severity, severity_block)}"
|
100
|
+
when 'severity_date' then
|
101
|
+
info_block = colorize_according_severity(severity, "#{severity_abb_block} #{date_block} #{severity_block}")
|
102
|
+
when 'full' then
|
103
|
+
info_block = colorize_according_severity(severity, "#{severity_abb_block} #{date_block} #{severity_block}")
|
104
|
+
msg_block = colorize_according_severity(severity, msg_block)
|
105
|
+
else
|
106
|
+
info_block = "#{severity_abb_block} #{date_block} #{severity_block}"
|
107
|
+
end
|
108
|
+
|
109
|
+
"#{info_block} #{msg_block}"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
def colorize_according_severity(severity, line)
|
115
|
+
color = SEVERITY_COLORS[severity.to_sym] || SEVERITY_COLORS[:DEFAULT]
|
116
|
+
"\e[#{color}m#{line}\e[0m"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
=begin
|
2
|
+
Este clase se encarga de manejar lo relativo al código fuente
|
3
|
+
=end
|
4
|
+
|
5
|
+
module Liri
|
6
|
+
module Common
|
7
|
+
class SourceCode
|
8
|
+
FOLDER_PATH = Dir.pwd
|
9
|
+
FOLDER_NAME = FOLDER_PATH.split('/').last
|
10
|
+
DECOMPRESSED_FOLDER_NAME = 'decompressed'
|
11
|
+
attr_reader :compressed_file_folder_path, :compressed_file_path, :decompressed_file_folder_path
|
12
|
+
|
13
|
+
def initialize(compressed_file_folder_path, compression_class, unit_test_class)
|
14
|
+
@compressed_file_folder_path = compressed_file_folder_path
|
15
|
+
@decompressed_file_folder_path = File.join(@compressed_file_folder_path, '/', DECOMPRESSED_FOLDER_NAME)
|
16
|
+
@compressed_file_path = File.join(@compressed_file_folder_path, '/', "#{FOLDER_NAME}.zip")
|
17
|
+
# Inicializa un compresor acorde a compression_class, la siguiente línea en realidad hace lo siguiente:
|
18
|
+
# @compressor = Liri::Common::Compressor::Zip.new(input_dir, output_file)
|
19
|
+
# compression_class en este caso es Zip pero podría ser otro si existiera la implementación, por ejemplo Rar
|
20
|
+
@compressor = Object.const_get(compression_class).new(FOLDER_PATH, @compressed_file_path)
|
21
|
+
# Inicializa un ejecutor de pruebas acorde a unit_test_class, la siguiente línea en realidad hace lo siguiente:
|
22
|
+
# @unit_test = Liri::Common::UnitTest::Rspec.new(source_code_folder_path)
|
23
|
+
# unit_test_class en este caso es Rspec pero podría ser otro si existiera la implementación, por ejemplo UnitTest
|
24
|
+
@unit_test = Object.const_get(unit_test_class).new(FOLDER_PATH)
|
25
|
+
end
|
26
|
+
|
27
|
+
def compress_folder
|
28
|
+
@compressor.compress
|
29
|
+
end
|
30
|
+
|
31
|
+
def decompress_file(compressed_file_path = @compressed_file_path)
|
32
|
+
@compressor.decompress(compressed_file_path, @decompressed_file_folder_path)
|
33
|
+
end
|
34
|
+
|
35
|
+
def delete_compressed_file
|
36
|
+
if File.exist?(@compressed_file_path)
|
37
|
+
File.delete(@compressed_file_path)
|
38
|
+
true
|
39
|
+
else
|
40
|
+
false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def all_tests
|
45
|
+
@unit_test.all_tests
|
46
|
+
end
|
47
|
+
|
48
|
+
def folder_path
|
49
|
+
FOLDER_PATH
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module Liri
|
2
|
+
module Common
|
3
|
+
module UnitTest
|
4
|
+
class Rspec
|
5
|
+
TESTS_FOLDER_NAME = 'spec'
|
6
|
+
attr_reader :tests_folder_path
|
7
|
+
|
8
|
+
def initialize(source_code_folder_path)
|
9
|
+
@source_code_folder_path = source_code_folder_path
|
10
|
+
@tests_folder_path = File.join(source_code_folder_path, TESTS_FOLDER_NAME)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Retorna un hash con todos los tests. Ex.: {1=>"spec/hash_spec.rb:2", 2=>"spec/hash_spec.rb:13", 3=>"spec/hash_spec.rb:24", ..., 29=>"spec/liri_spec.rb:62"}
|
14
|
+
def all_tests
|
15
|
+
tests_count = 1
|
16
|
+
tests_hash = {}
|
17
|
+
test_files.each do |test_file|
|
18
|
+
File.open(test_file) do |file|
|
19
|
+
file.each_with_index do |line, index|
|
20
|
+
if line.strip.start_with?('it')
|
21
|
+
absolute_file_path = file.to_path
|
22
|
+
relative_file_path = absolute_file_path.sub(@source_code_folder_path + '/', '')
|
23
|
+
|
24
|
+
test_line = relative_file_path + ":#{index + 1}"
|
25
|
+
tests_hash[tests_count] = test_line
|
26
|
+
tests_count += 1
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
tests_hash
|
32
|
+
end
|
33
|
+
|
34
|
+
# Recibe un arreglo de rutas a las pruebas unitarias
|
35
|
+
# Ejecuta las pruebas unitarias y retorna el resultado como un hash
|
36
|
+
def run_tests(tests)
|
37
|
+
# Se puede ejecutar comandos en líneas de comandos usando system(cli_command) o %x|cli_command|
|
38
|
+
# system devuelve true, false o nil, %x devuelve la salida del comando ejecutado
|
39
|
+
# From:
|
40
|
+
# https://www.rubyguides.com/2018/12/ruby-system/
|
41
|
+
|
42
|
+
#system("bundle exec rspec #{tests_paths} --format progress --out rspec_result.txt --no-color")
|
43
|
+
# El comando chdir hace que el directorio de trabajo se mueva a la carpeta en donde se descomprimió el código fuente para poder ejecutar las pruebas
|
44
|
+
Dir.chdir(@source_code_folder_path) do
|
45
|
+
# Descomentar para la depuración en entorno de desarrollo (Creo que aún así no se puede depurar)
|
46
|
+
# raw_tests_result = %x|bundle exec rspec #{tests.join(' ')} --format progress|
|
47
|
+
# Descomentar para el entorno de producción
|
48
|
+
raw_tests_result = ''
|
49
|
+
Liri::Common::Benchmarking.start(start_msg: "Ejecutando conjunto de pruebas. Espere... ") do
|
50
|
+
raw_tests_result = %x|bash -lc 'rvm use #{Liri.current_folder_ruby_and_gemset}; rspec #{tests.join(' ')} --format progress'|
|
51
|
+
end
|
52
|
+
|
53
|
+
hash_tests_result = process_tests_result(raw_tests_result)
|
54
|
+
hash_tests_result
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
def test_files
|
60
|
+
Dir[@tests_folder_path + "/**/*spec.rb"]
|
61
|
+
end
|
62
|
+
|
63
|
+
# Recibe el resultado crudo de las pruebas unitarias
|
64
|
+
# Procesa el resultado y lo devuelve en formato hash manejable
|
65
|
+
# Ejemplo del hash retornado:
|
66
|
+
# {example_quantity: 2, failure_quantity: 1}
|
67
|
+
def process_tests_result(raw_test_results)
|
68
|
+
result_hash = {example_quantity: 0, failure_quantity: 0}
|
69
|
+
flag = ''
|
70
|
+
raw_test_results.each_line do |line|
|
71
|
+
if line.strip.start_with?('Finished')
|
72
|
+
flag = 'Finished'
|
73
|
+
next
|
74
|
+
end
|
75
|
+
|
76
|
+
if flag == 'Finished'
|
77
|
+
puts ''
|
78
|
+
Liri.logger.info(line)
|
79
|
+
values = line.to_s.match(/([\d]+) example.?, ([\d]+) failure.?/)
|
80
|
+
result_hash[:example_quantity] = values[1].to_i
|
81
|
+
result_hash[:failure_quantity] = values[2].to_i
|
82
|
+
flag = ''
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
result_hash
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/lib/exe/agent.rb
ADDED
data/lib/exe/manager.rb
ADDED
data/lib/hash_extend.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# = hash_extend.rb
|
2
|
+
#
|
3
|
+
# @author Rodrigo Fernández
|
4
|
+
#
|
5
|
+
# == Clase Hash
|
6
|
+
# Esta clase extiende las funcionalidades de la clase Hash de Ruby
|
7
|
+
class Hash
|
8
|
+
# Retorna un hash con elementos aleatorios del hash original.
|
9
|
+
# @param quantity [Integer] la cantidad de elementos del nuevo hash retornado.
|
10
|
+
# @return [Hash] un hash con elementos aleatorios del hash original.
|
11
|
+
# @example
|
12
|
+
# hash = {uno: 'uno', dos: 'dos', tres: 'tres'}
|
13
|
+
# hash.sample
|
14
|
+
# => {dos: 'dos'}
|
15
|
+
# hash.sample(2)
|
16
|
+
# => {uno: 'uno', tres: 'tres'}
|
17
|
+
def sample(quantity=1)
|
18
|
+
sample_keys = self.keys.sample(quantity)
|
19
|
+
sample_values = {}
|
20
|
+
sample_keys.each do |sample_key|
|
21
|
+
sample_values[sample_key] = self[sample_key]
|
22
|
+
end
|
23
|
+
sample_values
|
24
|
+
end
|
25
|
+
|
26
|
+
# Retorna un hash con elementos aleatorios del hash original y borra estos elementos del hash.
|
27
|
+
# @param quantity [Integer] la cantidad de elementos del nuevo hash retornado.
|
28
|
+
# @return [Hash] un hash con elementos aleatorios del hash original.
|
29
|
+
# @example
|
30
|
+
# hash = {uno: 'uno', dos: 'dos', tres: 'tres'}
|
31
|
+
# hash.sample
|
32
|
+
# => {dos: 'dos'}
|
33
|
+
# hash.sample(2)
|
34
|
+
# => {uno: 'uno', tres: 'tres'}
|
35
|
+
def sample!(quantity=1)
|
36
|
+
samples = self.sample(quantity)
|
37
|
+
remove!(samples.keys)
|
38
|
+
samples
|
39
|
+
end
|
40
|
+
|
41
|
+
# Borra elementos del hash.
|
42
|
+
# @note Los elementos son borrados del hash original
|
43
|
+
# @param keys [Array] las claves a remover separadas por comas o en un arreglo.
|
44
|
+
# @return [Hash] un hash sin los elementos indicados.
|
45
|
+
# @example
|
46
|
+
# hash = {uno: 'uno', dos: 'dos', tres: 'tres'}
|
47
|
+
# hash.remove!(:uno)
|
48
|
+
# => {:dos=>"dos", :tres=>"tres"}
|
49
|
+
# hash.remove!(:uno, :dos, :tres)
|
50
|
+
# => {}
|
51
|
+
# hash
|
52
|
+
# => {}
|
53
|
+
def remove!(*keys)
|
54
|
+
keys.flatten.each{|key| self.delete(key) }
|
55
|
+
self
|
56
|
+
end
|
57
|
+
end
|
data/lib/liri.rb
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Este modulo contiene datos del programa que son reutilizados en otras partes de la aplicacion
|
4
|
+
module Liri
|
5
|
+
NAME = 'liri' # El gemspec requiere que el nombre este en minusculas
|
6
|
+
VERSION = '0.1.0'
|
7
|
+
SETUP_FOLDER_NAME = 'liri'
|
8
|
+
SETUP_FOLDER_PATH = File.join(Dir.pwd, '/', SETUP_FOLDER_NAME)
|
9
|
+
LOGS_FOLDER_NAME = 'logs'
|
10
|
+
MANAGER_LOGS_FOLDER_PATH = File.join(SETUP_FOLDER_PATH, '/', LOGS_FOLDER_NAME)
|
11
|
+
AGENT_LOGS_FOLDER_PATH = MANAGER_LOGS_FOLDER_PATH
|
12
|
+
AGENT_FOLDER_NAME = 'agent'
|
13
|
+
AGENT_FOLDER_PATH = File.join(SETUP_FOLDER_PATH, '/', AGENT_FOLDER_NAME)
|
14
|
+
MANAGER_FOLDER_NAME = 'manager'
|
15
|
+
MANAGER_FOLDER_PATH = File.join(SETUP_FOLDER_PATH, '/', MANAGER_FOLDER_NAME)
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def setup
|
19
|
+
@setup ||= load_setup
|
20
|
+
end
|
21
|
+
|
22
|
+
def logger
|
23
|
+
@logger ||= load_logger
|
24
|
+
end
|
25
|
+
|
26
|
+
def set_logger(folder_path, file_name)
|
27
|
+
@logger = load_logger(folder_path, file_name)
|
28
|
+
end
|
29
|
+
|
30
|
+
def clear_setup
|
31
|
+
if @setup
|
32
|
+
@setup = nil
|
33
|
+
true
|
34
|
+
else
|
35
|
+
false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_folders(program)
|
40
|
+
Dir.mkdir(SETUP_FOLDER_PATH) unless Dir.exist?(SETUP_FOLDER_PATH)
|
41
|
+
|
42
|
+
case program
|
43
|
+
when 'manager'
|
44
|
+
Dir.mkdir(MANAGER_LOGS_FOLDER_PATH) unless Dir.exist?(MANAGER_LOGS_FOLDER_PATH)
|
45
|
+
Dir.mkdir(MANAGER_FOLDER_PATH) unless Dir.exist?(MANAGER_FOLDER_PATH)
|
46
|
+
when 'agent'
|
47
|
+
Dir.mkdir(AGENT_FOLDER_PATH) unless Dir.exist?(AGENT_FOLDER_PATH)
|
48
|
+
Dir.mkdir(AGENT_LOGS_FOLDER_PATH) unless Dir.exist?(AGENT_LOGS_FOLDER_PATH)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def clean_folder(folder_path)
|
53
|
+
FileUtils.rm_rf(Dir.glob(folder_path + '/*')) if Dir.exist?(folder_path)
|
54
|
+
end
|
55
|
+
|
56
|
+
def reload_setup
|
57
|
+
@setup = load_setup
|
58
|
+
end
|
59
|
+
|
60
|
+
def delete_setup
|
61
|
+
liri_setup = Liri::Manager::Setup.new(SETUP_FOLDER_PATH)
|
62
|
+
liri_setup.delete
|
63
|
+
end
|
64
|
+
|
65
|
+
def init_exit(stop, threads, program)
|
66
|
+
threads = threads.compact
|
67
|
+
kill(threads) if stop
|
68
|
+
|
69
|
+
# Con la siguiente línea se asegura que los hilos no mueran antes de que finalize el programa principal
|
70
|
+
# Fuente: https://underc0de.org/foro/ruby/hilos-en-ruby/
|
71
|
+
threads.each{|thread| thread.join}
|
72
|
+
#rescue SignalException => e
|
73
|
+
#puts "\nEjecución del #{program} terminada manualmente\n"
|
74
|
+
#kill(threads)
|
75
|
+
end
|
76
|
+
|
77
|
+
def kill(threads)
|
78
|
+
threads.each{|thread| Thread.kill(thread)}
|
79
|
+
end
|
80
|
+
|
81
|
+
def current_host_ip_address
|
82
|
+
addr = Socket.ip_address_list.select(&:ipv4?).detect{|addr| addr.ip_address != '127.0.0.1'}
|
83
|
+
addr.ip_address
|
84
|
+
end
|
85
|
+
|
86
|
+
def compression_class
|
87
|
+
"Liri::Common::Compressor::#{setup.library.compression}"
|
88
|
+
end
|
89
|
+
|
90
|
+
def unit_test_class
|
91
|
+
"Liri::Common::UnitTest::#{setup.library.unit_test}"
|
92
|
+
end
|
93
|
+
|
94
|
+
def udp_port
|
95
|
+
setup.ports.udp
|
96
|
+
end
|
97
|
+
|
98
|
+
def tcp_port
|
99
|
+
setup.ports.tcp
|
100
|
+
end
|
101
|
+
|
102
|
+
def current_folder_ruby_and_gemset
|
103
|
+
"#{File.read('.ruby-version').strip}@#{File.read('.ruby-gemset').strip}"
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
# Carga las configuraciones en memoria desde un archivo de configuracion
|
109
|
+
def load_setup
|
110
|
+
liri_setup = Liri::Manager::Setup.new(SETUP_FOLDER_PATH)
|
111
|
+
liri_setup.create unless File.exist?(liri_setup.path)
|
112
|
+
liri_setup.load
|
113
|
+
end
|
114
|
+
|
115
|
+
# Inicializa y configura la librería encargada de loguear
|
116
|
+
def load_logger(folder_path = nil, file_name = nil)
|
117
|
+
log = Liri::Common::Log.new('daily', folder_path: folder_path, file_name: file_name, stdout: Liri.setup.log.stdout.show)
|
118
|
+
log
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# EXCEPTIONS
|
123
|
+
class FileNotFoundError < StandardError
|
124
|
+
def initialize(file_path)
|
125
|
+
msg = "No se encuentra el archivo #{file_path}"
|
126
|
+
super(msg)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'highline/import'
|
2
|
+
|
3
|
+
module Liri
|
4
|
+
class Manager
|
5
|
+
class Credential
|
6
|
+
FILE_NAME = 'liri-credentials.yml'
|
7
|
+
def initialize(folder_path)
|
8
|
+
@folder_path = folder_path
|
9
|
+
@file_path = File.join(@folder_path, '/', FILE_NAME)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Obtiene ususario y contraseña del sistema en el que se ejecuta el programa
|
13
|
+
def get
|
14
|
+
user, password = get_credentials
|
15
|
+
unless user || password
|
16
|
+
user, password = ask_credentials
|
17
|
+
save_credentials(user, password)
|
18
|
+
end
|
19
|
+
return user, password
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def get_local_user
|
24
|
+
%x[whoami].delete!("\n")
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_credentials
|
28
|
+
if File.exist?(@file_path)
|
29
|
+
data = YAML.load(File.read(@file_path))
|
30
|
+
return data['user'], data['password']
|
31
|
+
else
|
32
|
+
return nil, nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def ask_credentials
|
37
|
+
local_user = get_local_user
|
38
|
+
password = ask("Ingrese contraseña del usuario #{local_user}: ") { |q| q.echo = "*" }
|
39
|
+
return local_user, password
|
40
|
+
end
|
41
|
+
|
42
|
+
def save_credentials(user, password)
|
43
|
+
File.write(@file_path, "user: #{user}\npassword: #{password}")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|