liri 0.2.1 → 0.4.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.
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