liri 0.2.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/all_libraries.rb CHANGED
@@ -19,14 +19,19 @@ require 'agent/agent'
19
19
  require 'agent/runner'
20
20
 
21
21
  require 'common/benchmarking'
22
+ require 'common/duration'
23
+ require 'common/hardware'
22
24
  require 'common/log'
23
25
  require 'common/manager_data'
24
26
  require 'common/progressbar'
27
+ require 'common/tty_progressbar'
25
28
  require 'common/setup'
26
29
  require 'common/source_code'
27
30
  require 'common/compressor/zip'
28
31
  require 'common/unit_test/rspec'
32
+ require 'common/unit_test/rspec_result_parser'
29
33
  require 'common/tests_result'
34
+ require 'common/text_time_parser'
30
35
 
31
36
  require 'manager/manager'
32
37
  require 'manager/credential'
@@ -1,31 +1,26 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # = benchmarking.rb
2
4
  #
3
5
  # @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' # requerimiento de la gema to_duration
10
- require 'to_duration'
11
6
 
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
7
+ require "benchmark"
15
8
 
16
9
  module Liri
17
10
  module Common
11
+ # == Módulo Benchmarking
12
+ # Este módulo se encarga de medir el tiempo de ejecución de algunos bloques de código
18
13
  module Benchmarking
19
14
  class << self
20
- def start(start_msg: nil, end_msg: 'Duración: ', stdout: true)
15
+ def start(start_msg: nil, end_msg: 'Duration: ', stdout: true, &block)
21
16
  Liri.logger.info(start_msg, stdout)
22
- seconds = Benchmark.realtime do
23
- yield
24
- end
25
- Liri.logger.info("#{end_msg}#{seconds.to_duration}", stdout)
17
+
18
+ seconds = Benchmark.realtime(&block)
19
+
20
+ Liri.logger.info("#{end_msg}#{Duration.humanize(seconds, times_round: Liri.times_round, times_round_type: Liri.times_round_type)}", stdout)
26
21
  seconds
27
22
  end
28
23
  end
29
24
  end
30
25
  end
31
- end
26
+ end
@@ -8,15 +8,16 @@ module Liri
8
8
  module Compressor
9
9
  class Zip
10
10
  # Inicializa la carpeta a comprimir y la ubicación en donde se guardará el archivo comprimido
11
- def initialize(input_dir, output_file)
11
+ def initialize(input_dir, output_file, ignored_folders)
12
12
  @input_dir = input_dir
13
13
  @output_file = output_file
14
+ @ignored_folders = ignored_folders.split(",")
14
15
  end
15
16
 
16
17
  # Comprime el directorio de entrada @input_dir en un archivo con extensión zip.
17
18
  def compress
18
19
  clear_output_file
19
- entries = Dir.entries(@input_dir) - %w[. ..]
20
+ entries = Dir.entries(@input_dir) - (%w[. ..] + @ignored_folders)
20
21
 
21
22
  ::Zip::File.open(@output_file, ::Zip::File::CREATE) do |zipfile|
22
23
  write_entries(entries, '', zipfile)
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # = duration.rb
4
+ #
5
+ # @author Rodrigo Fernández
6
+
7
+ require "chronic_duration"
8
+
9
+ module Liri
10
+ module Common
11
+ # == Módulo Duration
12
+ # Este módulo se encarga de convertir el tiempo en segundos a un formato legible
13
+ module Duration
14
+ class << self
15
+ def humanize(time, times_round:, times_round_type:)
16
+ # El time puede ser un BigDecimal y aunque se redondee puede responder con un formato 0.744e2, por eso
17
+ # es imporantes hacerle un to_f para convertirlo a 74.4 antes de proceder a humanizarlo
18
+ time = time.to_f
19
+ case times_round_type
20
+ when :floor then ChronicDuration.output(time.truncate(times_round), format: :short, keep_zero: true)
21
+ when :roof then ChronicDuration.output(time.round(times_round), format: :short, keep_zero: true)
22
+ else raise "Invalid times_round_type. Expected: floor or roof. Received: #{times_round_type}"
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,29 @@
1
+ # = hardware.rb
2
+ #
3
+ # @author Rodrigo Fernández
4
+
5
+ module Liri
6
+ module Common
7
+ # == Módulo Hardware
8
+ # Este módulo se encarga de obtener información del hardware
9
+ module Hardware
10
+ class << self
11
+ def cpu
12
+ cpu = %x|inxi -C|
13
+ cpu = cpu.to_s.match(/model(.+)bits/)
14
+ cpu = cpu[1].gsub("12", "")
15
+ cpu = cpu.gsub(":", "")
16
+ cpu.strip
17
+ rescue Errno::ENOENT
18
+ raise InxiCommandNotFoundError.new
19
+ end
20
+
21
+ def memory
22
+ memory = %x|grep MemTotal /proc/meminfo|
23
+ memory = memory.to_s.match(/(\d+)/)
24
+ (memory[1].to_i * 0.000001).to_i
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
data/lib/common/log.rb CHANGED
@@ -14,6 +14,8 @@ module Liri
14
14
  FOLDER_PATH = File.join(Dir.pwd, "/#{FOLDER_NAME}")
15
15
  FILE_NAME = 'liri.log'
16
16
 
17
+ attr_reader :folder_path
18
+
17
19
  def initialize(shift_age, folder_path:, file_name:, stdout: true)
18
20
  @stdout = stdout
19
21
  @shift_age = shift_age
@@ -27,37 +29,37 @@ module Liri
27
29
  end
28
30
 
29
31
  def debug(text, stdout = false)
30
- puts(text) if stdout
32
+ puts(ColorizeText.debug(text)) if stdout
31
33
  @stdout_logger.debug(text) if @stdout
32
34
  @file_logger.debug(text)
33
35
  end
34
36
 
35
37
  def info(text, stdout = false)
36
- puts(text) if stdout
38
+ puts(ColorizeText.default(text)) if stdout
37
39
  @stdout_logger.info(text) if @stdout
38
40
  @file_logger.info(text)
39
41
  end
40
42
 
41
43
  def warn(text, stdout = false)
42
- puts(text) if stdout
44
+ puts(ColorizeText.warn(text)) if stdout
43
45
  @stdout_logger.warn(text) if @stdout
44
46
  @file_logger.warn(text)
45
47
  end
46
48
 
47
49
  def error(text, stdout = false)
48
- puts(text) if stdout
50
+ puts(ColorizeText.error(text)) if stdout
49
51
  @stdout_logger.error(text) if @stdout
50
52
  @file_logger.error(text)
51
53
  end
52
54
 
53
55
  def fatal(text, stdout = false)
54
- puts(text) if stdout
56
+ puts(ColorizeText.fatal(text)) if stdout
55
57
  @stdout_logger.fatal(text) if @stdout
56
58
  @file_logger.fatal(text)
57
59
  end
58
60
 
59
61
  def unknown(text, stdout = false)
60
- puts(text) if stdout
62
+ puts(ColorizeText.unknown(text)) if stdout
61
63
  @stdout_logger.unknown(text) if @stdout
62
64
  @file_logger.unknown(text)
63
65
  end
@@ -65,12 +67,12 @@ module Liri
65
67
  private
66
68
  def create_stdout_logger
67
69
  @stdout_logger = Logger.new(STDOUT, @shift_age)
68
- @stdout_logger.formatter = Liri::Common::LogFormatter.colorize(Liri.setup.log.stdout.colorize)
70
+ @stdout_logger.formatter = Liri::Common::LogFormatter.colorize(Liri.setup.general.log.stdout.colorize)
69
71
  end
70
72
 
71
73
  def create_file_logger
72
74
  @file_logger = Logger.new(@file_path, @shift_age)
73
- @file_logger.formatter = Liri::Common::LogFormatter.colorize(Liri.setup.log.file.colorize)
75
+ @file_logger.formatter = Liri::Common::LogFormatter.colorize(Liri.setup.general.log.file.colorize)
74
76
  end
75
77
 
76
78
  def create_log_folder
@@ -78,18 +80,58 @@ module Liri
78
80
  end
79
81
  end
80
82
 
83
+ class ColorizeText
84
+ COLORS = {
85
+ DEBUG: '0;36', # cyan
86
+ ERROR: '0;31', # red
87
+ INFO: '0;32', # green
88
+ WARN: '0;33', # orange
89
+ FATAL: '0;35', # pink
90
+ ANY: '0;36', # cyan
91
+ DEFAULT: '1;0' # white
92
+ }
93
+
94
+ class << self
95
+ def default(text)
96
+ colorize(text, :DEFAULT)
97
+ end
98
+
99
+ def debug(text)
100
+ colorize(text, :DEBUG)
101
+ end
102
+
103
+ def info(text)
104
+ colorize(text, :INFO)
105
+ end
106
+
107
+ def warn(text)
108
+ colorize(text, :WARN)
109
+ end
110
+
111
+ def error(text)
112
+ colorize(text, :ERROR)
113
+ end
114
+
115
+ def fatal(text)
116
+ colorize(text, :FATAL)
117
+ end
118
+
119
+ def unknown(text)
120
+ colorize(text, :ANY)
121
+ end
122
+
123
+ private
124
+
125
+ def colorize(text, color)
126
+ "\e[#{COLORS[color]}m#{text}\e[0m"
127
+ end
128
+ end
129
+ end
130
+
81
131
  class LogFormatter
82
132
  DATETIME_FORMAT = "%d-%m-%Y %H:%M"
83
133
 
84
- SEVERITY_COLORS = {
85
- DEBUG: '0;36', # cyan
86
- ERROR: '0;31', # red
87
- INFO: '0;32', # green
88
- WARN: '0;33', # orange
89
- FATAL: '0;35', # pink
90
- ANY: '0;36', # cyan
91
- DEFAULT: '1;0' # white
92
- }
134
+ SEVERITY_COLORS = ColorizeText::COLORS
93
135
 
94
136
  class << self
95
137
  def colorize(type)
@@ -8,10 +8,10 @@ module Liri
8
8
  module Common
9
9
  # Esta clase guarda los datos del Manager
10
10
  class ManagerData
11
- attr_accessor :folder_path, :compressed_file_path, :user, :password
11
+ attr_accessor :tests_results_folder_path, :compressed_file_path, :user, :password
12
12
 
13
- def initialize(folder_path:, compressed_file_path:, user:, password:)
14
- @folder_path = folder_path
13
+ def initialize(tests_results_folder_path:, compressed_file_path:, user:, password:)
14
+ @tests_results_folder_path = tests_results_folder_path
15
15
  @compressed_file_path = compressed_file_path
16
16
  @user = user
17
17
  @password = password
@@ -19,7 +19,7 @@ module Liri
19
19
 
20
20
  def to_h
21
21
  {
22
- folder_path: @folder_path,
22
+ tests_results_folder_path: @tests_results_folder_path,
23
23
  compressed_file_path: @compressed_file_path,
24
24
  user: @user,
25
25
  password: @password
@@ -12,16 +12,16 @@ module Liri
12
12
  module Progressbar
13
13
  class << self
14
14
  def start(params = {})
15
- @compressing = true
15
+ @progressing = true
16
16
  progressbar = ProgressBar.create(params)
17
17
  Thread.new do
18
- while @compressing
18
+ while @progressing
19
19
  progressbar.increment
20
20
  sleep(0.1)
21
21
  end
22
22
  end
23
23
  yield
24
- @compressing = false
24
+ @progressing = false
25
25
  end
26
26
  end
27
27
  end
data/lib/common/setup.rb CHANGED
@@ -14,21 +14,31 @@ module Liri
14
14
  MANAGER_FOLDER_NAME = 'manager'
15
15
  AGENT_FOLDER_NAME = 'agent'
16
16
 
17
- attr_reader :setup_folder_path, :setup_file_path, :logs_folder_path, :manager_folder_path, :agent_folder_path
17
+ attr_reader :setup_folder_path, :setup_file_path, :logs_folder_path, :manager_folder_path,
18
+ :manager_tests_results_folder_path, :agent_folder_path
18
19
 
19
- def initialize(destination_folder_path)
20
+ def initialize(destination_folder_path, program, manager_tests_results_folder_time: nil)
20
21
  @setup_folder_path = File.join(destination_folder_path, '/', SETUP_FOLDER_NAME)
21
22
  @setup_file_path = File.join(@setup_folder_path, '/', SETUP_FILE_NAME)
22
23
  @logs_folder_path = File.join(@setup_folder_path, '/', LOGS_FOLDER_NAME)
23
24
  @manager_folder_path = File.join(@setup_folder_path, '/', MANAGER_FOLDER_NAME)
25
+ @manager_tests_results_folder_path = File.join(@manager_folder_path, '/', "#{manager_tests_results_folder_time}_tests_results") if manager_tests_results_folder_time
24
26
  @agent_folder_path = File.join(@setup_folder_path, '/', AGENT_FOLDER_NAME)
27
+ @program = program
25
28
  end
26
29
 
27
30
  def init
28
31
  create_folder(@setup_folder_path)
29
32
  create_folder(@logs_folder_path)
30
- create_folder(@manager_folder_path)
31
- create_folder(@agent_folder_path)
33
+
34
+ case @program
35
+ when :manager
36
+ create_folder(@manager_folder_path)
37
+ create_folder(@manager_tests_results_folder_path) if @manager_tests_results_folder_path
38
+ when :agent
39
+ create_folder(@agent_folder_path)
40
+ end
41
+
32
42
  create_setup_file
33
43
  end
34
44
 
@@ -8,7 +8,7 @@ module Liri
8
8
  DECOMPRESSED_FOLDER_NAME = 'decompressed'
9
9
  attr_reader :folder_path, :compressed_file_folder_path, :compressed_file_path, :decompressed_file_folder_path
10
10
 
11
- def initialize(folder_path, compressed_file_folder_path, compression_class, unit_test_class)
11
+ def initialize(folder_path, compressed_file_folder_path, ignored_folders, compression_class, unit_test_class)
12
12
  @folder_path = folder_path
13
13
  @folder_name = @folder_path.split('/').last
14
14
  @compressed_file_folder_path = compressed_file_folder_path
@@ -17,7 +17,7 @@ module Liri
17
17
  # Inicializa un compresor acorde a compression_class, la siguiente línea en realidad hace lo siguiente:
18
18
  # @compressor = Liri::Common::Compressor::Zip.new(input_dir, output_file)
19
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)
20
+ @compressor = Object.const_get(compression_class).new(@folder_path, @compressed_file_path, ignored_folders)
21
21
  # Inicializa un ejecutor de pruebas acorde a unit_test_class, la siguiente línea en realidad hace lo siguiente:
22
22
  # @unit_test = Liri::Common::UnitTest::Rspec.new(source_code_folder_path)
23
23
  # unit_test_class en este caso es Rspec pero podría ser otro si existiera la implementación, por ejemplo UnitTest
@@ -8,12 +8,19 @@ module Liri
8
8
  module Common
9
9
  # Esta clase se encarga de guardar y procesar el archivo de resultados
10
10
  class TestsResult
11
+ attr_reader :examples, :failures, :pending, :passed
12
+
11
13
  def initialize(folder_path)
12
14
  @folder_path = folder_path
13
- @example_quantity = 0
14
- @failure_quantity = 0
15
- @passed_quantity = 0
16
- @failures = ''
15
+ @examples = 0
16
+ @failures = 0
17
+ @pending = 0
18
+ @passed = 0
19
+ @finish_in = 0
20
+ @files_load = 0
21
+ @failures_list = ''
22
+ @failed_examples = ''
23
+ @failed_files = ''
17
24
  end
18
25
 
19
26
  def save(file_name, raw_tests_result)
@@ -22,28 +29,36 @@ module Liri
22
29
  file_path
23
30
  end
24
31
 
25
- def build_file_name(agent_ip_address, tests_batch_number)
26
- "batch_#{tests_batch_number}_agent_#{agent_ip_address}_tests_results"
32
+ def build_file_name(agent_ip_address, batch_num)
33
+ "batch_#{batch_num}_agent_#{agent_ip_address}_tests_results"
27
34
  end
28
35
 
29
36
  # Procesa el resultado crudo de las pruebas unitarias y lo devuelve en formato hash manejable
30
37
  # Ejemplo del hash retornado:
31
- # {example_quantity: 2, failure_quantity: 1}
38
+ # { examples: 0, failures: 0, pending: 0, passed: 0, finish_in: 0, files_load: 0,
39
+ # failures_list: '', failed_examples: '' }
32
40
  def process(tests_result_file_name)
33
41
  file_path = File.join(@folder_path, '/', tests_result_file_name)
42
+ # A veces no se encuentra el archivo de resultados, la siguiente condicional es para evitar errores relativos a esto
43
+ return {} unless File.exist?(file_path)
44
+
34
45
  result_hash = process_tests_result_file(file_path)
35
46
  update_partial_result(result_hash)
36
- #print_partial_result(result_hash)
37
47
  result_hash
38
48
  end
39
49
 
40
50
  def print_summary
41
- puts "\n#{@example_quantity} examples, #{@failure_quantity} failures\n"
51
+ Liri.logger.info("\n#{@examples} examples, #{@passed} passed, #{@failures} failures\n", true)
52
+ end
53
+
54
+ def print_detailed_failures
55
+ Liri.logger.info("\nFailures: ", true) unless @failures_list.empty?
56
+ Liri.logger.info(@failures_list, true)
42
57
  end
43
58
 
44
- def print_failures
45
- puts "\nFailures: " if !@failures.empty?
46
- puts @failures
59
+ def print_summary_failures
60
+ Liri.logger.info("\nFailed examples: ", true) unless @failed_examples.empty?
61
+ Liri.logger.info(@failed_examples, true)
47
62
  end
48
63
 
49
64
  private
@@ -51,10 +66,12 @@ module Liri
51
66
  # Recibe el resultado crudo de las pruebas unitarias
52
67
  # Procesa el archivo con los resultados crudos y lo devuelve en formato hash manejable
53
68
  # Ejemplo del hash retornado:
54
- # {result: '.F', failures: '', example_quantity: 2, failure_quantity: 1, failed_examples: ''}
69
+ # {result: '.F', failures: '', examples: 2, failures: 1, failed_examples: ''}
55
70
  def process_tests_result_file(file_path)
56
- result_hash = {failures: '', example_quantity: 0, failure_quantity: 0, passed_quantity: 0, failed_examples: ''}
71
+ result_hash = { examples: 0, failures: 0, pending: 0, passed: 0, finish_in: 0, files_load: 0,
72
+ failures_list: '', failed_examples: '', failed_files: '' }
57
73
  flag = ''
74
+ @failures_lists_count = @failures
58
75
  File.foreach(file_path) do |line|
59
76
  if flag == '' && line.strip.start_with?('Randomized')
60
77
  flag = 'Randomized'
@@ -67,6 +84,9 @@ module Liri
67
84
  end
68
85
 
69
86
  if ['Randomized', 'Failures', ''].include?(flag) && line.strip.start_with?('Finished')
87
+ values = finish_in_values(line)
88
+ result_hash[:finish_in] = values[:finish_in]
89
+ result_hash[:files_load] = values[:files_load]
70
90
  flag = 'Finished'
71
91
  next
72
92
  end
@@ -78,15 +98,20 @@ module Liri
78
98
 
79
99
  case flag
80
100
  when 'Failures'
81
- result_hash[:failures] << line
101
+ line = fix_failure_number(line)
102
+ result_hash[:failures_list] << line
82
103
  when 'Finished'
83
- values = line.to_s.match(/([\d]+) example.?, ([\d]+) failure.?/)
84
- result_hash[:example_quantity] = values[1].to_i
85
- result_hash[:failure_quantity] = values[2].to_i
86
- result_hash[:passed_quantity] = result_hash[:example_quantity] - result_hash[:failure_quantity]
104
+ values = finished_summary_values(line)
105
+ result_hash[:examples] = values[:examples]
106
+ result_hash[:failures] = values[:failures]
107
+ result_hash[:passed] = result_hash[:examples] - result_hash[:failures]
108
+ result_hash[:pending] = values[:pending]
87
109
  flag = ''
88
110
  when 'Failed'
89
- result_hash[:failed_examples] << line
111
+ if line.strip.start_with?('rspec')
112
+ result_hash[:failed_examples] << line
113
+ result_hash[:failed_files] << "#{failed_example(line)}\n"
114
+ end
90
115
  end
91
116
  end
92
117
 
@@ -94,22 +119,38 @@ module Liri
94
119
  end
95
120
 
96
121
  def update_partial_result(hash_result)
97
- @example_quantity += hash_result[:example_quantity]
98
- @failure_quantity += hash_result[:failure_quantity]
99
- @passed_quantity += hash_result[:passed_quantity]
100
- @failures << hash_result[:failures]
122
+ @examples += hash_result[:examples]
123
+ @failures += hash_result[:failures]
124
+ @pending += hash_result[:pending]
125
+ @passed += hash_result[:passed]
126
+ @failures_list << hash_result[:failures_list]
127
+ @failed_examples << hash_result[:failed_examples]
128
+ @failed_files << hash_result[:failed_files]
101
129
  end
102
130
 
103
- def print_partial_result(result_hash)
104
- result_hash[:passed_quantity].times do
105
- print '.'
106
- end
131
+ def finish_in_values(line)
132
+ UnitTest::RspecResultParser.finish_in_values(line)
133
+ end
107
134
 
108
- result_hash[:failure_quantity].times do
109
- print 'F'
110
- end
135
+ def finished_summary_values(line)
136
+ UnitTest::RspecResultParser.finished_summary_values(line)
137
+ end
138
+
139
+ def failed_example(line)
140
+ # get string like this "/spec/failed_spec.rb:4"
141
+ failed_example = UnitTest::RspecResultParser.failed_example(line)
142
+ # return "failed_spec.rb:4"
143
+ failed_example.split("/").last
111
144
  end
112
145
 
146
+ def fix_failure_number(line)
147
+ line_number_regex = /(\d+\))/
148
+ if line.strip.start_with?(line_number_regex)
149
+ @failures_lists_count += 1
150
+ line.gsub!(line_number_regex, "#{@failures_lists_count})")
151
+ end
152
+ line
153
+ end
113
154
  end
114
155
  end
115
156
  end
@@ -0,0 +1,51 @@
1
+ # = text_time_parser.rb
2
+ #
3
+ # @author Rodrigo Fernández
4
+ #
5
+ # == Clase TextTimeParser
6
+
7
+ require 'bigdecimal'
8
+
9
+ module Liri
10
+ module Common
11
+ # Esta clase parsea texto en horas, minutos y segundos a un valor decimal en segundos
12
+ class TextTimeParser
13
+ class << self
14
+ def to_seconds(text_time)
15
+ values = text_time.split(' ')
16
+ case values.size
17
+ when 2 # cuando se tiene por ejemplo '15 minutes'
18
+ text_time_to_seconds(values[0], values[1])
19
+ when 4 # cuando se tiene por ejemplo '1 minute 5 seconds'
20
+ text_time_to_seconds(values[0], values[1]) + text_time_to_seconds(values[2], values[3])
21
+ when 6 # cuando se tiene por ejemplo '1 hour 30 minutes 25 seconds'
22
+ text_time_to_seconds(values[0], values[1]) +
23
+ text_time_to_seconds(values[2], values[3]) +
24
+ text_time_to_seconds(values[4], values[5])
25
+ end
26
+ # queda pendiente agregar el caso de dias, horas, minutos y segundos, además hace falta verificar como lo muestra Rspec
27
+ end
28
+
29
+ private
30
+
31
+ def text_time_to_seconds(number, text)
32
+ # Se usa BigDecimal porque
33
+ # En una multiplicación normal: (203.033*3600).to_f = 730918.7999999999
34
+ # Con BigDecimal: (BigDecimal('203.033') * 3600).to_f = 730918.8
35
+ time = BigDecimal(number)
36
+ time_in_seconds = case text
37
+ when 'second', 'seconds' then time
38
+ when 'minute', 'minutes' then time * 60
39
+ when 'hour', 'hours' then time * 3600
40
+ when 'day', 'days' then time * 86_400
41
+ end
42
+
43
+ # time in seconds puede contener valores BigDecimal como este 0.744e2 que al hacerle to_f devuelve 74.4
44
+ # Aún así conviene mantener el valor en BigDecimal para las operaciones matematicas y solo cuando se va a mostrar
45
+ # los datos en pantalla se debe hacer el to_f
46
+ time_in_seconds
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,70 @@
1
+ # = tty_progressbar.rb
2
+ #
3
+ # @author Rodrigo Fernández
4
+ #
5
+ # == Módulo TtyProgressbar
6
+ require "tty-progressbar"
7
+
8
+ module Liri
9
+ module Common
10
+ # Este módulo se encarga de mostrar una barra de progreso
11
+ module TtyProgressbar
12
+ ANIMATION = [
13
+ "■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□",
14
+ "□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■",
15
+ "□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□",
16
+ "□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□□■□□",
17
+ ]
18
+
19
+ ANIMATION2 = [
20
+ "■□□□■□□□■□□□■□□□■□□",
21
+ "□■□□□■□□□■□□□■□□□■□",
22
+ "□□■□□□■□□□■□□□■□□□■",
23
+ "□□□■□□□■□□□■□□□■□□□",
24
+ ]
25
+ class << self
26
+ # Example:
27
+ # Common::TtyProgressbar.start("Compressing source code |:bar| Time: :elapsed", total: nil, width: 80) do
28
+ # ...code
29
+ # end
30
+ def start(format, params = {})
31
+ params[:unknown] = ANIMATION[0]
32
+ progressbar = TTY::ProgressBar.new(format, params)
33
+ # Es importante iniciar la barra porque TimeFormatter.call accede a su start_time y si no se inició la barra
34
+ # entonces ocurre un error
35
+ progressbar.start
36
+ progressbar.use(Common::TtyProgressbar::TimeFormatter)
37
+
38
+ Thread.new do
39
+ animation_count = 0
40
+ while !progressbar.stopped?
41
+ progressbar.advance
42
+
43
+ progressbar.update(unknown: ANIMATION[animation_count])
44
+ animation_count += 1
45
+ animation_count = 0 if animation_count == 3
46
+
47
+ sleep(0.1)
48
+ end
49
+ end
50
+ yield
51
+ progressbar.update(total: 1) # Esto hace que la barra cambie a al estilo completado con un porcentaje del 100%
52
+ progressbar.stop
53
+ rescue TypeError
54
+ # Se captura la excepción solo para evitar un error en start_time mas abajo
55
+ end
56
+ end
57
+
58
+ # From
59
+ class TimeFormatter
60
+ include TTY::ProgressBar::Formatter[/:time/i]
61
+
62
+ def call(value) # specify how display string is formatted
63
+ # access current progress bar instance to read start time
64
+ elapsed = Duration.humanize(Time.now - progress.start_time, times_round: Liri.times_round, times_round_type: Liri.times_round_type)
65
+ value.gsub(matcher, elapsed) # replace :time token with a value
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end