mongify 0.1.2 → 0.1.3
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/CHANGELOG.rdoc +9 -0
- data/Gemfile.lock +22 -24
- data/README.rdoc +20 -2
- data/lib/mongify.rb +2 -1
- data/lib/mongify/cli/application.rb +4 -1
- data/lib/mongify/cli/options.rb +2 -0
- data/lib/mongify/database/base_connection.rb +2 -2
- data/lib/mongify/database/column.rb +88 -13
- data/lib/mongify/database/no_sql_connection.rb +17 -0
- data/lib/mongify/exceptions.rb +18 -4
- data/lib/mongify/progressbar.rb +270 -0
- data/lib/mongify/status.rb +44 -11
- data/lib/mongify/translation/process.rb +54 -40
- data/lib/mongify/ui.rb +8 -0
- data/lib/mongify/version.rb +1 -1
- data/mongify.gemspec +1 -1
- data/spec/mongify/database/base_connection_spec.rb +2 -2
- data/spec/mongify/database/column_spec.rb +67 -6
- data/spec/mongify/database/no_sql_connection_spec.rb +14 -0
- data/spec/mongify/status_spec.rb +35 -0
- data/spec/mongify/translation/process_spec.rb +31 -4
- data/spec/mongify/ui_spec.rb +6 -0
- metadata +13 -9
data/CHANGELOG.rdoc
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
== 0.1.3 / 21 Feb 2011
|
2
|
+
* Made all exception stem from MongifyError and system always raises a child of MongifyError.
|
3
|
+
* Brought in the progress bar into source
|
4
|
+
* Changed behaviour on storing BigDecimal (now converts into String)
|
5
|
+
* Added ability to convert BigDecimal to integer via an :at => 'integer, :scale => 2 settings.
|
6
|
+
* Improved Progress Bar display, now it gives you a better feel for what's going on.
|
7
|
+
* Added an index on pre_mongified_id to speedup lookup times (Making it 42 times faster on import of embedded tables)
|
8
|
+
* Fixed bug in importing of polymorphic tables where associations are nil
|
9
|
+
* Fixed bug where pre_mongified_id would be a string and not an integer (causing no updates of ref_ids)
|
1
10
|
== 0.1.2 / 14 Feb 2011
|
2
11
|
* More Improvements to README and RDOC
|
3
12
|
* Added ability to :force => true on a mongodb_connection, forcing it to drop database before processing.
|
data/Gemfile.lock
CHANGED
@@ -1,31 +1,31 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
mongify (0.1.
|
4
|
+
mongify (0.1.3)
|
5
5
|
activerecord (>= 3.0.3)
|
6
6
|
activesupport (>= 3.0.3)
|
7
7
|
bson_ext (>= 1.1.5)
|
8
|
+
highline (>= 1.6.1)
|
8
9
|
mongo (>= 1.1.5)
|
9
10
|
mysql2
|
10
11
|
net-ssh (>= 2.0)
|
11
|
-
progressbar (>= 0.9)
|
12
12
|
|
13
13
|
GEM
|
14
14
|
remote: http://rubygems.org/
|
15
15
|
specs:
|
16
|
-
activemodel (3.0.
|
17
|
-
activesupport (= 3.0.
|
16
|
+
activemodel (3.0.4)
|
17
|
+
activesupport (= 3.0.4)
|
18
18
|
builder (~> 2.1.2)
|
19
19
|
i18n (~> 0.4)
|
20
|
-
activerecord (3.0.
|
21
|
-
activemodel (= 3.0.
|
22
|
-
activesupport (= 3.0.
|
20
|
+
activerecord (3.0.4)
|
21
|
+
activemodel (= 3.0.4)
|
22
|
+
activesupport (= 3.0.4)
|
23
23
|
arel (~> 2.0.2)
|
24
24
|
tzinfo (~> 0.3.23)
|
25
|
-
activesupport (3.0.
|
26
|
-
arel (2.0.
|
27
|
-
bson (1.2.
|
28
|
-
bson_ext (1.2.
|
25
|
+
activesupport (3.0.4)
|
26
|
+
arel (2.0.8)
|
27
|
+
bson (1.2.2)
|
28
|
+
bson_ext (1.2.2)
|
29
29
|
builder (2.1.2)
|
30
30
|
cucumber (0.10.0)
|
31
31
|
builder (>= 2.1.2)
|
@@ -36,26 +36,24 @@ GEM
|
|
36
36
|
diff-lcs (1.1.2)
|
37
37
|
gherkin (2.3.3)
|
38
38
|
json (~> 1.4.6)
|
39
|
+
highline (1.6.1)
|
39
40
|
i18n (0.5.0)
|
40
41
|
json (1.4.6)
|
41
|
-
mocha (0.9.
|
42
|
-
|
43
|
-
|
44
|
-
bson (>= 1.2.0)
|
42
|
+
mocha (0.9.12)
|
43
|
+
mongo (1.2.2)
|
44
|
+
bson (>= 1.2.2)
|
45
45
|
mysql (2.8.1)
|
46
46
|
mysql2 (0.2.6)
|
47
47
|
net-ssh (2.1.0)
|
48
|
-
progressbar (0.9.0)
|
49
|
-
rake (0.8.7)
|
50
48
|
rcov (0.9.9)
|
51
|
-
rspec (2.
|
52
|
-
rspec-core (~> 2.
|
53
|
-
rspec-expectations (~> 2.
|
54
|
-
rspec-mocks (~> 2.
|
55
|
-
rspec-core (2.
|
56
|
-
rspec-expectations (2.
|
49
|
+
rspec (2.5.0)
|
50
|
+
rspec-core (~> 2.5.0)
|
51
|
+
rspec-expectations (~> 2.5.0)
|
52
|
+
rspec-mocks (~> 2.5.0)
|
53
|
+
rspec-core (2.5.1)
|
54
|
+
rspec-expectations (2.5.0)
|
57
55
|
diff-lcs (~> 1.1.2)
|
58
|
-
rspec-mocks (2.
|
56
|
+
rspec-mocks (2.5.0)
|
59
57
|
sqlite3 (1.3.3)
|
60
58
|
sqlite3-ruby (1.3.3)
|
61
59
|
sqlite3 (>= 1.3.3)
|
data/README.rdoc
CHANGED
@@ -177,6 +177,8 @@ Table Options are as follow:
|
|
177
177
|
end # Moving records around, renaming records, changing values in row based on
|
178
178
|
end # some values! Checkout Mongify::Database::DataRow to learn more
|
179
179
|
|
180
|
+
More documentation can be found at {Mongify::Database::Table}
|
181
|
+
|
180
182
|
=== Columns
|
181
183
|
|
182
184
|
==== Structure
|
@@ -194,7 +196,7 @@ Before we cover the options, you need to know what types of columns are supporte
|
|
194
196
|
:key # Columns that are primary keys need to be marked as :key type
|
195
197
|
:integer # Will be converted to a integer
|
196
198
|
:float # Will be converted to a float
|
197
|
-
:decimal # Will be converted to a BigDecimal
|
199
|
+
:decimal # Will be converted to a string (due to MongoDB Ruby Drivers not supporting BigDecimal, read details in Mongify::Database::Column under Decimal Storage)
|
198
200
|
:string # Will be converted to a string
|
199
201
|
:text # Will be converted to a string
|
200
202
|
:datetime # Will be converted to a Time format (DateTime is not currently supported in the Mongo ruby driver)
|
@@ -215,6 +217,18 @@ Before we cover the options, you need to know what types of columns are supporte
|
|
215
217
|
column "surname",
|
216
218
|
:string,
|
217
219
|
:rename_to => 'last_name' # Rename_to allows you to rename the column
|
220
|
+
|
221
|
+
<em>For decimal columns you can specify a few options:</em>
|
222
|
+
column "total", # This is a default conversion setting.
|
223
|
+
:decimal,
|
224
|
+
:as => 'string'
|
225
|
+
|
226
|
+
column "total", # You can specify to convert your decimal to integer
|
227
|
+
:decimal, # specifying scale will define how many decimal places to keep
|
228
|
+
:as => 'integer', # Example: :scale => 2 will convert 123.4567 to 12346 before saving
|
229
|
+
:scale => 2
|
230
|
+
|
231
|
+
More documentation can be found at {Mongify::Database::Column}
|
218
232
|
|
219
233
|
== Notes
|
220
234
|
|
@@ -224,8 +238,12 @@ More testing will be done once the basic functionality is done
|
|
224
238
|
|
225
239
|
|
226
240
|
== TODO
|
241
|
+
* Allow deeper embedding
|
242
|
+
* Test in Ruby 1.9
|
243
|
+
* Test in different databases
|
227
244
|
|
228
|
-
|
245
|
+
== Known Issues
|
246
|
+
* Can't do anything to an embedded table
|
229
247
|
|
230
248
|
== Development
|
231
249
|
|
data/lib/mongify.rb
CHANGED
@@ -26,9 +26,12 @@ module Mongify
|
|
26
26
|
begin
|
27
27
|
cmd = @options.parse
|
28
28
|
cmd.execute(self)
|
29
|
-
rescue
|
29
|
+
rescue MongifyError => error
|
30
30
|
$stderr.puts "Error: #{error}"
|
31
31
|
report_error
|
32
|
+
rescue Exception => error
|
33
|
+
report_error
|
34
|
+
raise error
|
32
35
|
end
|
33
36
|
return @status
|
34
37
|
end
|
data/lib/mongify/cli/options.rb
CHANGED
@@ -87,6 +87,8 @@ EOB
|
|
87
87
|
# option parser, ensuring parse_options is only called once
|
88
88
|
def parse_options
|
89
89
|
@parsed = true && @parser.parse!(@argv) unless @parsed
|
90
|
+
rescue OptionParser::InvalidOption => er
|
91
|
+
raise Mongify::InvalidOption, er.message, er.backtrace
|
90
92
|
end
|
91
93
|
end
|
92
94
|
end
|
@@ -41,12 +41,12 @@ module Mongify
|
|
41
41
|
|
42
42
|
# Used to setup connection, Raises NotImplementedError because it needs to be setup in BaseConnection's children
|
43
43
|
def setup_connection_adapter
|
44
|
-
raise
|
44
|
+
raise NotImplementedMongifyError
|
45
45
|
end
|
46
46
|
|
47
47
|
# Used to test connection, Raises NotImplementedError because it needs to be setup in BaseConnection's children
|
48
48
|
def has_connection?
|
49
|
-
raise
|
49
|
+
raise NotImplementedMongifyError
|
50
50
|
end
|
51
51
|
|
52
52
|
|
@@ -19,7 +19,7 @@ module Mongify
|
|
19
19
|
# :key # Columns that are primary keys need to be marked as :key type
|
20
20
|
# :integer # Will be converted to a integer
|
21
21
|
# :float # Will be converted to a float
|
22
|
-
# :decimal # Will be converted to a
|
22
|
+
# :decimal # Will be converted to a string *(you can change default behaviour read below)
|
23
23
|
# :string # Will be converted to a string
|
24
24
|
# :text # Will be converted to a string
|
25
25
|
# :datetime # Will be converted to a Time format (DateTime is not currently supported in the Mongo ruby driver)
|
@@ -41,12 +41,46 @@ module Mongify
|
|
41
41
|
#
|
42
42
|
# column "post_id", :integer, :auto_detect => true # Will run auto detect and make this column a :references => 'posts', :on => 'post_id' for you
|
43
43
|
# # More used when reading a sql database, NOT recommended for use during processing of translation
|
44
|
+
#
|
45
|
+
# <em>For decimal columns you can specify a few options:</em>
|
46
|
+
# column "total", # This is a default conversion setting.
|
47
|
+
# :decimal,
|
48
|
+
# :as => 'string'
|
44
49
|
#
|
50
|
+
# column "total", # You can specify to convert your decimal to integer
|
51
|
+
# :decimal, # specifying scale will define how many decimal places to keep
|
52
|
+
# :as => 'integer', # Example: :scale => 2 will convert 123.4567 to 12346 in to mongodb
|
53
|
+
# :scale => 2
|
54
|
+
# ==== Decimal Storage
|
55
|
+
#
|
56
|
+
# Unfortunately MongoDB Ruby Drivers doesn't support BigDecimal, so to ensure all data is stored correctly (without losing information)
|
57
|
+
# I've chosen to store as String, however you can overwrite this functionality in one of two ways:
|
58
|
+
# <em>The reason you would want to do this, is to make this searchable via a query.</em>
|
59
|
+
#
|
60
|
+
# <b>1) You can specify :as => 'integer', :scale => 2</b>
|
61
|
+
# column "total", :decimal, :as => 'integer', :scale => 2
|
62
|
+
#
|
63
|
+
# #It would take a value of 123.456 and store it as an integer of value 12346
|
64
|
+
#
|
65
|
+
# <b>2) You can specify your own custom conversion by doing a {Mongify::Database::Table#before_save}
|
66
|
+
#
|
67
|
+
# Example:
|
68
|
+
# table "invoice" do
|
69
|
+
# column "name", :string
|
70
|
+
# column "total", :decimal
|
71
|
+
# before_save do |row|
|
72
|
+
# row.total = (BigDecimal.new(row.total) * 1000).round
|
73
|
+
# end
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
# This would take 123.456789 in the total column and convert it to an interger of value 123457 (and in your app you can convert it back to a decimal)
|
77
|
+
#
|
78
|
+
# *REMEMBER* there is a limit on how big of an integer you can store in BSON/MongoDB (http://bsonspec.org/#/specification)
|
45
79
|
class Column
|
46
80
|
attr_reader :sql_name, :type, :options
|
47
81
|
|
48
82
|
#List of available options for a column
|
49
|
-
AVAILABLE_OPTIONS = ['references', 'ignore', 'rename_to']
|
83
|
+
AVAILABLE_OPTIONS = ['references', 'ignore', 'rename_to', 'as', 'scale']
|
50
84
|
|
51
85
|
# Auto detects if a column is an :key column or is a reference column
|
52
86
|
def self.auto_detect(column)
|
@@ -88,7 +122,7 @@ module Mongify
|
|
88
122
|
return {} if ignored?
|
89
123
|
case type
|
90
124
|
when :key
|
91
|
-
{"pre_mongified_id" => value}
|
125
|
+
{"pre_mongified_id" => value.to_i}
|
92
126
|
else
|
93
127
|
{"#{name}" => type_cast(value)}
|
94
128
|
end
|
@@ -115,14 +149,20 @@ module Mongify
|
|
115
149
|
def method_missing(meth, *args, &blk)
|
116
150
|
method_name = meth.to_s.gsub("=", '')
|
117
151
|
if AVAILABLE_OPTIONS.include?(method_name)
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
152
|
+
unless self.class.method_defined?(method_name.to_sym)
|
153
|
+
class_eval <<-EOF
|
154
|
+
def #{method_name}=(value)
|
155
|
+
options['#{method_name}'] = value
|
156
|
+
end
|
157
|
+
EOF
|
158
|
+
end
|
159
|
+
unless self.class.method_defined?("#{method_name}=".to_sym)
|
160
|
+
class_eval <<-EOF
|
161
|
+
def #{method_name}
|
162
|
+
options['#{method_name}']
|
163
|
+
end
|
164
|
+
EOF
|
165
|
+
end
|
126
166
|
send(meth, *args, &blk)
|
127
167
|
else
|
128
168
|
super(meth, *args, &blk)
|
@@ -154,6 +194,34 @@ module Mongify
|
|
154
194
|
!!self.ignore
|
155
195
|
end
|
156
196
|
|
197
|
+
# Used when trying to figure out how to convert a decimal value
|
198
|
+
# @return [String] passed option['as'] or defaults to 'string'
|
199
|
+
def as
|
200
|
+
options['as'] ||= 'string'
|
201
|
+
end
|
202
|
+
# Sets option['as'] to either 'string' or 'integer', defults to 'string' for unknown values
|
203
|
+
# @param [String|Symbol] value, can be either 'string' or 'integer
|
204
|
+
def as=(value)
|
205
|
+
value = value.to_s.downcase
|
206
|
+
options['as'] = ['string', 'integer'].include?(value) ? value : 'string'
|
207
|
+
end
|
208
|
+
# Returns true if :as was passed as integer
|
209
|
+
def as_integer?
|
210
|
+
self.as == 'integer'
|
211
|
+
end
|
212
|
+
|
213
|
+
# Get the scale option for decimal to integer conversion
|
214
|
+
# column 'total', :decimal, :as => 'integer', :scale => 3
|
215
|
+
# @return [integer] passed option['scale'] or 0
|
216
|
+
def scale
|
217
|
+
options['scale'] ||= 0
|
218
|
+
end
|
219
|
+
# Set the scale option for decimal to integer conversion
|
220
|
+
# column 'total', :decimal, :as => 'integer', :scale => 3
|
221
|
+
# @param [Integer] number of decimal places to round off to
|
222
|
+
def scale=(value)
|
223
|
+
options['scale'] = value.to_i
|
224
|
+
end
|
157
225
|
#######
|
158
226
|
private
|
159
227
|
#######
|
@@ -164,9 +232,15 @@ module Mongify
|
|
164
232
|
case type
|
165
233
|
when :string then value
|
166
234
|
when :text then value
|
167
|
-
when :integer then value.to_i
|
235
|
+
when :integer then value.to_i
|
168
236
|
when :float then value.to_f
|
169
|
-
when :decimal
|
237
|
+
when :decimal
|
238
|
+
value = ActiveRecord::ConnectionAdapters::Column.value_to_decimal(value)
|
239
|
+
if as_integer?
|
240
|
+
(value * (10 ** self.scale)).round.to_i
|
241
|
+
else
|
242
|
+
value.to_s
|
243
|
+
end
|
170
244
|
when :datetime then ActiveRecord::ConnectionAdapters::Column.string_to_time(value)
|
171
245
|
when :timestamp then ActiveRecord::ConnectionAdapters::Column.string_to_time(value)
|
172
246
|
when :time then ActiveRecord::ConnectionAdapters::Column.string_to_dummy_time(value)
|
@@ -176,6 +250,7 @@ module Mongify
|
|
176
250
|
else value
|
177
251
|
end
|
178
252
|
end
|
253
|
+
|
179
254
|
|
180
255
|
|
181
256
|
# runs auto detect (see {Mongify::Database::Column.auto_detect})
|
@@ -27,6 +27,7 @@ module Mongify
|
|
27
27
|
class NoSqlConnection < Mongify::Database::BaseConnection
|
28
28
|
include Mongo
|
29
29
|
|
30
|
+
|
30
31
|
#Required fields for a no sql connection
|
31
32
|
REQUIRED_FIELDS = %w{host database}
|
32
33
|
|
@@ -114,10 +115,26 @@ module Mongify
|
|
114
115
|
|
115
116
|
# Removes pre_mongified_id from all records in a given collection
|
116
117
|
def remove_pre_mongified_ids(collection_name)
|
118
|
+
drop_mongified_index(collection_name)
|
117
119
|
db[collection_name].update({}, { '$unset' => { 'pre_mongified_id' => 1} }, :multi => true)
|
118
120
|
end
|
119
121
|
|
122
|
+
# Removes pre_mongified_id from collection
|
123
|
+
# @param [String] collection_name name of collection to remove the index from
|
124
|
+
# @return True if successful
|
125
|
+
# @raise MongoDBError if index isn't found
|
126
|
+
def drop_mongified_index(collection_name)
|
127
|
+
db[collection_name].drop_index('pre_mongified_id_1') if db[collection_name].index_information.keys.include?("pre_mongified_id_1")
|
128
|
+
end
|
129
|
+
|
130
|
+
# Creates a pre_mongified_id index to ensure
|
131
|
+
# speedy lookup for collections via the pre_mongified_id
|
132
|
+
def create_pre_mongified_id_index(collection_name)
|
133
|
+
db[collection_name].create_index([['pre_mongified_id', Mongo::ASCENDING]])
|
134
|
+
end
|
135
|
+
|
120
136
|
# Asks user permission to drop the database
|
137
|
+
# @return true or false depending on user's response
|
121
138
|
def ask_to_drop_database
|
122
139
|
if UI.ask("Are you sure you want to drop #{database} database?")
|
123
140
|
drop_database
|
data/lib/mongify/exceptions.rb
CHANGED
@@ -1,20 +1,34 @@
|
|
1
1
|
module Mongify
|
2
|
+
# Base Mongify Error
|
3
|
+
class MongifyError < RuntimeError; end
|
4
|
+
|
5
|
+
# Not Implemented Error from Mongify
|
6
|
+
class NotImplementedMongifyError < MongifyError; end
|
7
|
+
|
2
8
|
# File Not Found Exception
|
3
|
-
class FileNotFound <
|
9
|
+
class FileNotFound < MongifyError; end
|
4
10
|
# Raise when configuration file is missing
|
5
11
|
class ConfigurationFileNotFound < FileNotFound; end
|
6
12
|
# Raised when Translation file is missing
|
7
13
|
class TranslationFileNotFound < FileNotFound; end
|
8
14
|
|
9
15
|
# Basic Configuration Error Exception
|
10
|
-
class ConfigurationError <
|
16
|
+
class ConfigurationError < MongifyError; end
|
11
17
|
# Raise when a sqlConnection is required but not given
|
12
18
|
class SqlConnectionRequired < ConfigurationError; end
|
19
|
+
# Raised when a SqlConfiguration is invalid?
|
20
|
+
class SqlConnectionInvalid < ConfigurationError; end
|
13
21
|
# Raised when a noSqlConnection is required but not given
|
14
22
|
class NoSqlConnectionRequired < ConfigurationError; end
|
23
|
+
# Raised when a NoSqlConfiguration is invalid?
|
24
|
+
class NoSqlConnectionInvalid < ConfigurationError; end
|
25
|
+
|
15
26
|
# Raised when a Mongify::Database::Column is expected but not given
|
16
27
|
class DatabaseColumnExpected < ConfigurationError; end
|
17
|
-
|
28
|
+
|
18
29
|
# Raised when application has no root folder set
|
19
|
-
class RootMissing <
|
30
|
+
class RootMissing < MongifyError; end
|
31
|
+
|
32
|
+
# Raised when an invalid option is passed via CLI
|
33
|
+
class InvalidOption < MongifyError; end
|
20
34
|
end
|
@@ -0,0 +1,270 @@
|
|
1
|
+
#
|
2
|
+
# Ruby/ProgressBar - a text progress bar library
|
3
|
+
#
|
4
|
+
# Copyright (C) 2001-2005 Satoru Takabayashi <satoru@namazu.org>
|
5
|
+
# All rights reserved.
|
6
|
+
# This is free software with ABSOLUTELY NO WARRANTY.
|
7
|
+
#
|
8
|
+
# You can redistribute it and/or modify it under the terms
|
9
|
+
# of Ruby's license.
|
10
|
+
#
|
11
|
+
# This has been modified by
|
12
|
+
# Andrew Kalek
|
13
|
+
# Anlek Consulting
|
14
|
+
# http://anlek.com
|
15
|
+
|
16
|
+
module Mongify
|
17
|
+
# Progress bar used to display results
|
18
|
+
class ProgressBar
|
19
|
+
#Progress bar version
|
20
|
+
VERSION = "0.9.1"
|
21
|
+
|
22
|
+
def initialize (title, total, out = STDERR)
|
23
|
+
@title = title
|
24
|
+
@total = total
|
25
|
+
@out = out
|
26
|
+
@terminal_width = 80
|
27
|
+
@bar_mark = "o"
|
28
|
+
@current = 0
|
29
|
+
@previous = 0
|
30
|
+
@finished_p = false
|
31
|
+
@start_time = Time.now
|
32
|
+
@previous_time = @start_time
|
33
|
+
@title_width = 35
|
34
|
+
@format = "%-#{@title_width}s %s %3d%% %s %s"
|
35
|
+
@format_arguments = [:title, :count, :percentage, :bar, :stat]
|
36
|
+
clear
|
37
|
+
show
|
38
|
+
end
|
39
|
+
attr_reader :title
|
40
|
+
attr_reader :current
|
41
|
+
attr_reader :total
|
42
|
+
attr_accessor :start_time
|
43
|
+
|
44
|
+
#######
|
45
|
+
private
|
46
|
+
#######
|
47
|
+
|
48
|
+
# Formatting for the actual bar
|
49
|
+
def fmt_bar
|
50
|
+
bar_width = do_percentage * @terminal_width / 100
|
51
|
+
sprintf("|%s%s|",
|
52
|
+
@bar_mark * bar_width,
|
53
|
+
" " * (@terminal_width - bar_width))
|
54
|
+
end
|
55
|
+
|
56
|
+
# Formatting for the percentage
|
57
|
+
def fmt_percentage
|
58
|
+
do_percentage
|
59
|
+
end
|
60
|
+
|
61
|
+
# Formatting for the stat (time left or time taken to complete)
|
62
|
+
def fmt_stat
|
63
|
+
if @finished_p then elapsed else eta end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Formatting for file transfer
|
67
|
+
def fmt_stat_for_file_transfer
|
68
|
+
if @finished_p then
|
69
|
+
sprintf("%s %s %s", bytes, transfer_rate, elapsed)
|
70
|
+
else
|
71
|
+
sprintf("%s %s %s", bytes, transfer_rate, eta)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Formatting for title
|
76
|
+
def fmt_title
|
77
|
+
@title[0,(@title_width - 1)] + ":"
|
78
|
+
end
|
79
|
+
|
80
|
+
# Formatting for count (x/y)
|
81
|
+
def fmt_count
|
82
|
+
sprintf('%15s', "(#{@current}/#{@total})")
|
83
|
+
end
|
84
|
+
|
85
|
+
# Converts bytes to kb, mb or gb
|
86
|
+
def convert_bytes (bytes)
|
87
|
+
if bytes < 1024
|
88
|
+
sprintf("%6dB", bytes)
|
89
|
+
elsif bytes < 1024 * 1000 # 1000kb
|
90
|
+
sprintf("%5.1fKB", bytes.to_f / 1024)
|
91
|
+
elsif bytes < 1024 * 1024 * 1000 # 1000mb
|
92
|
+
sprintf("%5.1fMB", bytes.to_f / 1024 / 1024)
|
93
|
+
else
|
94
|
+
sprintf("%5.1fGB", bytes.to_f / 1024 / 1024 / 1024)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Returns the transfer rate
|
99
|
+
# works only with file transfer
|
100
|
+
def transfer_rate
|
101
|
+
bytes_per_second = @current.to_f / (Time.now - @start_time)
|
102
|
+
sprintf("%s/s", convert_bytes(bytes_per_second))
|
103
|
+
end
|
104
|
+
|
105
|
+
# Gets current byte count
|
106
|
+
def bytes
|
107
|
+
convert_bytes(@current)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Gets formatting for time
|
111
|
+
def format_time (t)
|
112
|
+
t = t.to_i
|
113
|
+
sec = t % 60
|
114
|
+
min = (t / 60) % 60
|
115
|
+
hour = t / 3600
|
116
|
+
sprintf("%02d:%02d:%02d", hour, min, sec);
|
117
|
+
end
|
118
|
+
|
119
|
+
# ETA stands for Estimated Time of Arrival.
|
120
|
+
def eta
|
121
|
+
if @current == 0
|
122
|
+
"ETA: --:--:--"
|
123
|
+
else
|
124
|
+
elapsed = Time.now - @start_time
|
125
|
+
eta = elapsed * @total / @current - elapsed;
|
126
|
+
sprintf("ETA: %s", format_time(eta))
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Returns elapsed time
|
131
|
+
def elapsed
|
132
|
+
elapsed = Time.now - @start_time
|
133
|
+
sprintf("Time: %s", format_time(elapsed))
|
134
|
+
end
|
135
|
+
|
136
|
+
# Returns end of line
|
137
|
+
# @return [String] "\n" or "\r"
|
138
|
+
def eol
|
139
|
+
if @finished_p then "\n" else "\r" end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Calculates percentage
|
143
|
+
# @return [Number] the percentage
|
144
|
+
def do_percentage
|
145
|
+
if @total.zero?
|
146
|
+
100
|
147
|
+
else
|
148
|
+
@current * 100 / @total
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Gets the width of the terminal window
|
153
|
+
def get_width
|
154
|
+
UI.terminal_helper.output_cols
|
155
|
+
end
|
156
|
+
|
157
|
+
# Draws the bar
|
158
|
+
def show
|
159
|
+
arguments = @format_arguments.map {|method|
|
160
|
+
method = sprintf("fmt_%s", method)
|
161
|
+
send(method)
|
162
|
+
}
|
163
|
+
line = sprintf(@format, *arguments)
|
164
|
+
|
165
|
+
width = get_width
|
166
|
+
if line.length == width - 1
|
167
|
+
@out.print(line + eol)
|
168
|
+
@out.flush
|
169
|
+
elsif line.length >= width
|
170
|
+
@terminal_width = [@terminal_width - (line.length - width + 1), 0].max
|
171
|
+
if @terminal_width == 0 then @out.print(line + eol) else show end
|
172
|
+
else # line.length < width - 1
|
173
|
+
@terminal_width += width - line.length + 1
|
174
|
+
show
|
175
|
+
end
|
176
|
+
@previous_time = Time.now
|
177
|
+
end
|
178
|
+
|
179
|
+
# Checks if it's needed, shows if it's so
|
180
|
+
def show_if_needed
|
181
|
+
if @total.zero?
|
182
|
+
cur_percentage = 100
|
183
|
+
prev_percentage = 0
|
184
|
+
else
|
185
|
+
cur_percentage = (@current * 100 / @total).to_i
|
186
|
+
prev_percentage = (@previous * 100 / @total).to_i
|
187
|
+
end
|
188
|
+
|
189
|
+
# Use "!=" instead of ">" to support negative changes
|
190
|
+
if cur_percentage != prev_percentage ||
|
191
|
+
Time.now - @previous_time >= 1 || @finished_p
|
192
|
+
show
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
public
|
197
|
+
# Clear's line
|
198
|
+
def clear
|
199
|
+
@out.print "\r"
|
200
|
+
@out.print(" " * (get_width - 1))
|
201
|
+
@out.print "\r"
|
202
|
+
end
|
203
|
+
|
204
|
+
# Marks finished
|
205
|
+
def finish
|
206
|
+
@current = @total
|
207
|
+
@finished_p = true
|
208
|
+
show
|
209
|
+
end
|
210
|
+
|
211
|
+
# Returns if the bar is finished
|
212
|
+
def finished?
|
213
|
+
@finished_p
|
214
|
+
end
|
215
|
+
|
216
|
+
# Sets bar to file trasfer mode
|
217
|
+
def file_transfer_mode
|
218
|
+
@format_arguments = [:title, :percentage, :bar, :stat_for_file_transfer]
|
219
|
+
end
|
220
|
+
|
221
|
+
# Allows format to be re/defined
|
222
|
+
def format= (format)
|
223
|
+
@format = format
|
224
|
+
end
|
225
|
+
|
226
|
+
# Allows to change the arguments of items that are shown
|
227
|
+
def format_arguments= (arguments)
|
228
|
+
@format_arguments = arguments
|
229
|
+
end
|
230
|
+
|
231
|
+
# halts drawing of bar
|
232
|
+
def halt
|
233
|
+
@finished_p = true
|
234
|
+
show
|
235
|
+
end
|
236
|
+
|
237
|
+
# Incremets bar
|
238
|
+
# @return [Integer] current bar frame
|
239
|
+
def inc (step = 1)
|
240
|
+
@current += step
|
241
|
+
@current = @total if @current > @total
|
242
|
+
show_if_needed
|
243
|
+
@previous = @current
|
244
|
+
end
|
245
|
+
|
246
|
+
# Allows you to set bar frame
|
247
|
+
# @return [Integer] current bar frame
|
248
|
+
def set (count)
|
249
|
+
if count < 0 || count > @total
|
250
|
+
raise "invalid count: #{count} (total: #{@total})"
|
251
|
+
end
|
252
|
+
@current = count
|
253
|
+
show_if_needed
|
254
|
+
@previous = @current
|
255
|
+
end
|
256
|
+
|
257
|
+
# Returns string representation of this object
|
258
|
+
def inspect
|
259
|
+
"#<ProgressBar:#{@current}/#{@total}>"
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
# Same as progress bar but this counts the progress backwards from 100% to 0
|
264
|
+
class ReversedProgressBar < ProgressBar
|
265
|
+
# Calculates the percentage
|
266
|
+
def do_percentage
|
267
|
+
100 - super
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|