Progress bar для скриптов на Ruby

December 8, 2010, revised January 1, 2011 bundler data scraping gem rake rdoc Ruby ruby-progressbar rubygems правило бойскаутов

Знакома такая ситуация - пишешь задачу для Rake, понимаешь, что выполняться она будет долго, а сделать какую-нибудь индикацию процесса облом?

Ну или если уж приходится делать, знаком такой вот код?

count=0
some_long_data_array.each do |data|
  data.process
  count+=1
  puts "#{count}/#{some_long_data_array.length}" if (count%1000)==0
end

Посчитай - тут половина строчек занимается выводом статистики! Да еще и не очень полезной. Вот мне и захотелось решить это проблему “сухим” и универсальным способом.

Надо сказать, что ее решили еще в 2005 году, и этот скрипт для вывода индикатора выполнения практически без изменений используется и до сих пор в виде гема ruby-progressbar. С ним можно написать вот так:

require 'progressbar'
pbar = ProgressBar.new('data processing', some_long_data_array.length)
some_long_data_array.each do |data|
  data.process
  pbar.inc
end
pbar.finish

Строчек не поубавилось, зато благодаря ruby-progressbar мы получаем красивенький индикатор выполнения:

data processing:           67% |oooooooooooooooooooooooooo              | ETA:  00:00:03

Как видишь, тут даже показывается ориентировочное время до окончания операции. Удобно!

Естественно, библиотека, написанная в 2005, не использует современных идиом Ruby (да она даже RDoc не использует), что я и постарался изменить.

Итак, мой форк гема ruby-progressbar, помимо вышеописанного, поддерживает блочную форму вызова:

require 'progressbar'
ProgressBar.block('data processing', some_long_data_array.length) do |pbar|
  some_long_data_array.each do |data|
    data.process
    pbar.inc
  end
end

Но и это еще не все! В 90% случаев обработка данных происходит в виде одного из методов-итераторов Enumerable - почему бы тогда высушить код еще дальше?

require 'progressbar'
some_long_data_array.each_with_progressbar('data processing') do |data|
  data.process
end
# or map_with_progressbar, or select_with_progressbar, etc

Сложно сделать этот код еще короче, не так ли?

Кроме того, я, следуя правилу бойскаутов, задокументировал код посредством RDoc и выполнил рефакторинг.

Установка

Предполагая, что ты используешь bundler:

# Gemfile
gem 'ruby-progressbar', :git => 'git://github.com/leonid-shevtsov/ruby-progressbar.git'

И - вперед, к наглядным Rake-таскам и аккуратному коду!

Buy Me a Coffee at ko-fi.com