tapsoob 0.1.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,236 @@
1
+ # -*- encoding : utf-8 -*-
2
+ #
3
+ # Ruby/ProgressBar - a text progress bar library
4
+ #
5
+ # Copyright (C) 2001-2005 Satoru Takabayashi <satoru@namazu.org>
6
+ # All rights reserved.
7
+ # This is free software with ABSOLUTELY NO WARRANTY.
8
+ #
9
+ # You can redistribute it and/or modify it under the terms
10
+ # of Ruby's license.
11
+ #
12
+
13
+ class ProgressBar
14
+ VERSION = "0.9"
15
+
16
+ def initialize (title, total, out = STDERR)
17
+ @title = title
18
+ @total = total
19
+ @out = out
20
+ @terminal_width = 80
21
+ @bar_mark = "="
22
+ @current = 0
23
+ @previous = 0
24
+ @finished_p = false
25
+ @start_time = Time.now
26
+ @previous_time = @start_time
27
+ @title_width = 14
28
+ @format = "%-#{@title_width}s %3d%% %s %s"
29
+ @format_arguments = [:title, :percentage, :bar, :stat]
30
+ clear
31
+ show
32
+ end
33
+ attr_reader :title
34
+ attr_reader :current
35
+ attr_reader :total
36
+ attr_accessor :start_time
37
+
38
+ private
39
+ def fmt_bar
40
+ bar_width = do_percentage * @terminal_width / 100
41
+ sprintf("|%s%s|",
42
+ @bar_mark * bar_width,
43
+ " " * (@terminal_width - bar_width))
44
+ end
45
+
46
+ def fmt_percentage
47
+ do_percentage
48
+ end
49
+
50
+ def fmt_stat
51
+ if @finished_p then elapsed else eta end
52
+ end
53
+
54
+ def fmt_stat_for_file_transfer
55
+ if @finished_p then
56
+ sprintf("%s %s %s", bytes, transfer_rate, elapsed)
57
+ else
58
+ sprintf("%s %s %s", bytes, transfer_rate, eta)
59
+ end
60
+ end
61
+
62
+ def fmt_title
63
+ @title[0,(@title_width - 1)] + ":"
64
+ end
65
+
66
+ def convert_bytes (bytes)
67
+ if bytes < 1024
68
+ sprintf("%6dB", bytes)
69
+ elsif bytes < 1024 * 1000 # 1000kb
70
+ sprintf("%5.1fKB", bytes.to_f / 1024)
71
+ elsif bytes < 1024 * 1024 * 1000 # 1000mb
72
+ sprintf("%5.1fMB", bytes.to_f / 1024 / 1024)
73
+ else
74
+ sprintf("%5.1fGB", bytes.to_f / 1024 / 1024 / 1024)
75
+ end
76
+ end
77
+
78
+ def transfer_rate
79
+ bytes_per_second = @current.to_f / (Time.now - @start_time)
80
+ sprintf("%s/s", convert_bytes(bytes_per_second))
81
+ end
82
+
83
+ def bytes
84
+ convert_bytes(@current)
85
+ end
86
+
87
+ def format_time (t)
88
+ t = t.to_i
89
+ sec = t % 60
90
+ min = (t / 60) % 60
91
+ hour = t / 3600
92
+ sprintf("%02d:%02d:%02d", hour, min, sec);
93
+ end
94
+
95
+ # ETA stands for Estimated Time of Arrival.
96
+ def eta
97
+ if @current == 0
98
+ "ETA: --:--:--"
99
+ else
100
+ elapsed = Time.now - @start_time
101
+ eta = elapsed * @total / @current - elapsed;
102
+ sprintf("ETA: %s", format_time(eta))
103
+ end
104
+ end
105
+
106
+ def elapsed
107
+ elapsed = Time.now - @start_time
108
+ sprintf("Time: %s", format_time(elapsed))
109
+ end
110
+
111
+ def eol
112
+ if @finished_p then "\n" else "\r" end
113
+ end
114
+
115
+ def do_percentage
116
+ if @total.zero?
117
+ 100
118
+ else
119
+ @current * 100 / @total
120
+ end
121
+ end
122
+
123
+ def get_width
124
+ # FIXME: I don't know how portable it is.
125
+ default_width = 80
126
+ begin
127
+ tiocgwinsz = 0x5413
128
+ data = [0, 0, 0, 0].pack("SSSS")
129
+ if @out.ioctl(tiocgwinsz, data) >= 0 then
130
+ rows, cols, xpixels, ypixels = data.unpack("SSSS")
131
+ if cols > 0 then cols else default_width end
132
+ else
133
+ default_width
134
+ end
135
+ rescue Exception
136
+ default_width
137
+ end
138
+ end
139
+
140
+ def show
141
+ arguments = @format_arguments.map {|method|
142
+ method = sprintf("fmt_%s", method)
143
+ send(method)
144
+ }
145
+ line = sprintf(@format, *arguments)
146
+
147
+ width = get_width
148
+ if line.length == width - 1
149
+ @out.print(line + eol)
150
+ @out.flush
151
+ elsif line.length >= width
152
+ @terminal_width = [@terminal_width - (line.length - width + 1), 0].max
153
+ if @terminal_width == 0 then @out.print(line + eol) else show end
154
+ else # line.length < width - 1
155
+ @terminal_width += width - line.length + 1
156
+ show
157
+ end
158
+ @previous_time = Time.now
159
+ end
160
+
161
+ def show_if_needed
162
+ if @total.zero?
163
+ cur_percentage = 100
164
+ prev_percentage = 0
165
+ else
166
+ cur_percentage = (@current * 100 / @total).to_i
167
+ prev_percentage = (@previous * 100 / @total).to_i
168
+ end
169
+
170
+ # Use "!=" instead of ">" to support negative changes
171
+ if cur_percentage != prev_percentage ||
172
+ Time.now - @previous_time >= 1 || @finished_p
173
+ show
174
+ end
175
+ end
176
+
177
+ public
178
+ def clear
179
+ @out.print "\r"
180
+ @out.print(" " * (get_width - 1))
181
+ @out.print "\r"
182
+ end
183
+
184
+ def finish
185
+ @current = @total
186
+ @finished_p = true
187
+ show
188
+ end
189
+
190
+ def finished?
191
+ @finished_p
192
+ end
193
+
194
+ def file_transfer_mode
195
+ @format_arguments = [:title, :percentage, :bar, :stat_for_file_transfer]
196
+ end
197
+
198
+ def format= (format)
199
+ @format = format
200
+ end
201
+
202
+ def format_arguments= (arguments)
203
+ @format_arguments = arguments
204
+ end
205
+
206
+ def halt
207
+ @finished_p = true
208
+ show
209
+ end
210
+
211
+ def inc (step = 1)
212
+ @current += step
213
+ @current = @total if @current > @total
214
+ show_if_needed
215
+ @previous = @current
216
+ end
217
+
218
+ def set (count)
219
+ if count < 0 || count > @total
220
+ raise "invalid count: #{count} (total: #{@total})"
221
+ end
222
+ @current = count
223
+ show_if_needed
224
+ @previous = @current
225
+ end
226
+
227
+ def inspect
228
+ "#<ProgressBar:#{@current}/#{@total}>"
229
+ end
230
+ end
231
+
232
+ class ReversedProgressBar < ProgressBar
233
+ def do_percentage
234
+ 100 - super
235
+ end
236
+ end
@@ -0,0 +1,11 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'tapsoob'
3
+ require 'rails'
4
+
5
+ module Tapsoob
6
+ class Railtie < Rails::Railtie
7
+ rake_tasks do
8
+ load "tasks/tapsoob.rake"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,83 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'sequel'
3
+ require 'sequel/extensions/schema_dumper'
4
+ require 'sequel/extensions/migration'
5
+ require 'json'
6
+
7
+ module Tapsoob
8
+ module Schema
9
+ extend self
10
+
11
+ def dump(database_url)
12
+ db = Sequel.connect(database_url)
13
+ db.dump_schema_migration(:indexes => false)
14
+ end
15
+
16
+ def dump_table(database_url, table)
17
+ table = table.to_sym
18
+ Sequel.connect(database_url) do |db|
19
+ <<END_MIG
20
+ Class.new(Sequel::Migration) do
21
+ def up
22
+ #{db.dump_table_schema(table.identifier, :indexes => false)}
23
+ end
24
+
25
+ def down
26
+ drop_table("#{table}") if @db.table_exists?("#{table}")
27
+ end
28
+ end
29
+ END_MIG
30
+ end
31
+ end
32
+
33
+ def indexes(database_url)
34
+ db = Sequel.connect(database_url)
35
+ db.dump_indexes_migration
36
+ end
37
+
38
+ def indexes_individual(database_url)
39
+ idxs = {}
40
+ Sequel.connect(database_url) do |db|
41
+ tables = db.tables
42
+ tables.each do |table|
43
+ idxs[table] = db.send(:dump_table_indexes, table, :add_index, {}).split("\n")
44
+ end
45
+ end
46
+
47
+ idxs.each do |table, indexes|
48
+ idxs[table] = indexes.map do |idx|
49
+ <<END_MIG
50
+ Class.new(Sequel::Migration) do
51
+ def up
52
+ #{idx}
53
+ end
54
+ end
55
+ END_MIG
56
+ end
57
+ end
58
+ JSON.generate(idxs)
59
+ end
60
+
61
+ def load(database_url, schema)
62
+ Sequel.connect(database_url) do |db|
63
+ klass = eval(schema)
64
+ klass.apply(db, :down)
65
+ klass.apply(db, :up)
66
+ end
67
+ end
68
+
69
+ def load_indexes(database_url, indexes)
70
+ Sequel.connect(database_url) do |db|
71
+ eval(indexes).apply(db, :up)
72
+ end
73
+ end
74
+
75
+ def reset_db_sequences(database_url)
76
+ db = Sequel.connect(database_url)
77
+ return unless db.respond_to?(:reset_primary_key_sequence)
78
+ db.tables.each do |table|
79
+ db.reset_primary_key_sequence(table)
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,179 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'zlib'
3
+
4
+ require 'tapsoob/errors'
5
+ require 'tapsoob/chunksize'
6
+ require 'tapsoob/schema'
7
+
8
+ module Tapsoob
9
+ module Utils
10
+ extend self
11
+
12
+ def windows?
13
+ return @windows if defined?(@windows)
14
+ require 'rbconfig'
15
+ @windows = !!(::RbConfig::CONFIG['host_os'] =~ /mswin|mingw/)
16
+ end
17
+
18
+ def bin(cmd)
19
+ cmd = "#{cmd}.cmd" if windows?
20
+ cmd
21
+ end
22
+
23
+ def checksum(data)
24
+ Zlib.crc32(data)
25
+ end
26
+
27
+ def valid_data?(data, crc32)
28
+ Zlib.crc32(data) == crc32.to_i
29
+ end
30
+
31
+ def base64encode(data)
32
+ [data].pack("m")
33
+ end
34
+
35
+ def base64decode(data)
36
+ data.unpack("m").first
37
+ end
38
+
39
+ def format_data(data, opts = {})
40
+ return {} if data.size == 0
41
+ string_columns = opts[:string_columns] || []
42
+ schema = opts[:schema] || []
43
+ table = opts[:table]
44
+
45
+ max_lengths = schema.inject({}) do |hash, (column, meta)|
46
+ if meta[:db_type] =~ /^varchar\((\d+)\)/
47
+ hash.update(column => $1.to_i)
48
+ end
49
+ hash
50
+ end
51
+
52
+ header = data[0].keys
53
+ only_data = data.collect do |row|
54
+ row = blobs_to_string(row, string_columns)
55
+ row.each do |column, data|
56
+ if data.to_s.length > (max_lengths[column] || data.to_s.length)
57
+ raise Tapsoob::InvalidData.new(<<-ERROR)
58
+ Detected data that exceeds the length limitation of its column. This is
59
+ generally due to the fact that SQLite does not enforce length restrictions.
60
+
61
+ Table : #{table}
62
+ Column : #{column}
63
+ Type : #{schema.detect{|s| s.first == column}.last[:db_type]}
64
+ Data : #{data}
65
+ ERROR
66
+ end
67
+ end
68
+ header.collect { |h| row[h] }
69
+ end
70
+ { :header => header, :data => only_data }
71
+ end
72
+
73
+ # mysql text and blobs fields are handled the same way internally
74
+ # this is not true for other databases so we must check if the field is
75
+ # actually text and manually convert it back to a string
76
+ def incorrect_blobs(db, table)
77
+ return [] if (db.url =~ /mysql:\/\//).nil?
78
+
79
+ columns = []
80
+ db.schema(table).each do |data|
81
+ column, cdata = data
82
+ columns << column if cdata[:db_type] =~ /text/
83
+ end
84
+ columns
85
+ end
86
+
87
+ def blobs_to_string(row, columns)
88
+ return row if columns.size == 0
89
+ columns.each do |c|
90
+ row[c] = row[c].to_s if row[c].kind_of?(Sequel::SQL::Blob)
91
+ end
92
+ row
93
+ end
94
+
95
+ def calculate_chunksize(old_chunksize)
96
+ c = Tapsoob::Chunksize.new(old_chunksize)
97
+
98
+ begin
99
+ c.start_time = Time.now
100
+ c.time_in_db = yield c
101
+ rescue Errno::EPIPE
102
+ c.retries += 1
103
+ raise if c.retries > 2
104
+
105
+ # we got disconnected, the chunksize could be too large
106
+ # reset the chunksize based on the number of retries
107
+ c.reset_chunksize
108
+ retry
109
+ end
110
+
111
+ c.end_time = Time.now
112
+ c.calc_new_chunksize
113
+ end
114
+
115
+ def export_schema(dump_path, table, schema_data)
116
+ File.open(File.join(dump_path, "schemas", "#{table}.rb"), 'w') do |file|
117
+ file.write(schema_data)
118
+ end
119
+ end
120
+
121
+ def export_indexes(dump_path, table, index_data)
122
+ data = [index_data]
123
+ if File.exists?(File.join(dump_path, "indexes", "#{table}.json"))
124
+ previous_data = JSON.parse(File.read(File.join(dump_path, "indexes", "#{table}.json")))
125
+ data = data + previous_data
126
+ end
127
+
128
+ File.open(File.join(dump_path, "indexes", "#{table}.json"), 'w') do |file|
129
+ file.write(JSON.generate(data))
130
+ end
131
+ end
132
+
133
+ def export_rows(dump_path, table, row_data)
134
+ data = row_data
135
+ if File.exists?(File.join(dump_path, "data", "#{table}.json"))
136
+ previous_data = JSON.parse(File.read(File.join(dump_path, "data", "#{table}.json")))
137
+ data[:data] = previous_data["data"] + row_data[:data]
138
+ end
139
+
140
+ File.open(File.join(dump_path, "data", "#{table}.json"), 'w') do |file|
141
+ file.write(JSON.generate(data))
142
+ end
143
+ end
144
+
145
+ def load_schema(dump_path, database_url, table)
146
+ schema = File.join(dump_path, "schemas", "#{table}.rb")
147
+ schema_bin(:load, database_url, schema.to_s)
148
+ end
149
+
150
+ def load_indexes(database_url, index)
151
+ Tapsoob::Schema.load_indexes(database_url, index)
152
+ end
153
+
154
+ def schema_bin(*args)
155
+ bin_path = File.expand_path("#{File.dirname(__FILE__)}/../../bin/#{bin('schema')}")
156
+ `"#{bin_path}" #{args.map { |a| "'#{a}'" }.join(' ')}`
157
+ end
158
+
159
+ def primary_key(db, table)
160
+ db.schema(table).select { |c| c[1][:primary_key] }.map { |c| c[0] }
161
+ end
162
+
163
+ def single_integer_primary_key(db, table)
164
+ table = table.to_sym.identifier unless table.kind_of?(Sequel::SQL::Identifier)
165
+ keys = db.schema(table).select { |c| c[1][:primary_key] and c[1][:type] == :integer }
166
+ not keys.nil? and keys.size == 1
167
+ end
168
+
169
+ def order_by(db, table)
170
+ pkey = primary_key(db, table)
171
+ if pkey
172
+ pkey.kind_of?(Array) ? pkey : [pkey.to_sym]
173
+ else
174
+ table = table.to_sym.identifier unless table.kind_of?(Sequel::SQL::Identifier)
175
+ db[table].columns
176
+ end
177
+ end
178
+ end
179
+ end