taps-jruby 0.3.14
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/LICENSE +20 -0
- data/README.rdoc +51 -0
- data/Rakefile +70 -0
- data/TODO +1 -0
- data/VERSION.yml +5 -0
- data/bin/schema +54 -0
- data/bin/schema.cmd +6 -0
- data/bin/taps +6 -0
- data/lib/taps/cli.rb +193 -0
- data/lib/taps/config.rb +47 -0
- data/lib/taps/data_stream.rb +333 -0
- data/lib/taps/db_session.rb +20 -0
- data/lib/taps/errors.rb +15 -0
- data/lib/taps/log.rb +15 -0
- data/lib/taps/monkey.rb +21 -0
- data/lib/taps/multipart.rb +73 -0
- data/lib/taps/operation.rb +557 -0
- data/lib/taps/progress_bar.rb +236 -0
- data/lib/taps/schema.rb +83 -0
- data/lib/taps/server.rb +188 -0
- data/lib/taps/utils.rb +199 -0
- data/spec/base.rb +26 -0
- data/spec/cli_spec.rb +10 -0
- data/spec/data_stream_spec.rb +23 -0
- data/spec/operation_spec.rb +32 -0
- data/spec/server_spec.rb +35 -0
- data/spec/utils_spec.rb +61 -0
- metadata +194 -0
data/lib/taps/utils.rb
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
require 'zlib'
|
2
|
+
require 'stringio'
|
3
|
+
require 'time'
|
4
|
+
require 'tempfile'
|
5
|
+
|
6
|
+
require 'taps/errors'
|
7
|
+
|
8
|
+
module Taps
|
9
|
+
module Utils
|
10
|
+
extend self
|
11
|
+
|
12
|
+
def windows?
|
13
|
+
return @windows if defined?(@windows)
|
14
|
+
require 'rbconfig'
|
15
|
+
@windows = !!(::Config::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] =~ /^\w+\((\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 Taps::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
|
+
chunksize = old_chunksize
|
97
|
+
|
98
|
+
retries = 0
|
99
|
+
time_in_db = 0
|
100
|
+
begin
|
101
|
+
t1 = Time.now
|
102
|
+
time_in_db = yield chunksize
|
103
|
+
time_in_db = time_in_db.to_f rescue 0
|
104
|
+
rescue Errno::EPIPE, RestClient::RequestFailed, RestClient::RequestTimeout
|
105
|
+
retries += 1
|
106
|
+
raise if retries > 2
|
107
|
+
|
108
|
+
# we got disconnected, the chunksize could be too large
|
109
|
+
# on first retry change to 10, on successive retries go down to 1
|
110
|
+
chunksize = (retries == 1) ? 10 : 1
|
111
|
+
|
112
|
+
retry
|
113
|
+
end
|
114
|
+
|
115
|
+
t2 = Time.now
|
116
|
+
|
117
|
+
diff = t2 - t1 - time_in_db
|
118
|
+
|
119
|
+
new_chunksize = if retries > 0
|
120
|
+
chunksize
|
121
|
+
elsif diff > 3.0
|
122
|
+
(chunksize / 3).ceil
|
123
|
+
elsif diff > 1.1
|
124
|
+
chunksize - 100
|
125
|
+
elsif diff < 0.8
|
126
|
+
chunksize * 2
|
127
|
+
else
|
128
|
+
chunksize + 100
|
129
|
+
end
|
130
|
+
new_chunksize = 1 if new_chunksize < 1
|
131
|
+
new_chunksize
|
132
|
+
end
|
133
|
+
|
134
|
+
def load_schema(database_url, schema_data)
|
135
|
+
Tempfile.open('taps') do |tmp|
|
136
|
+
File.open(tmp.path, 'w') { |f| f.write(schema_data) }
|
137
|
+
schema_bin(:load, database_url, tmp.path)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def load_indexes(database_url, index_data)
|
142
|
+
Tempfile.open('taps') do |tmp|
|
143
|
+
File.open(tmp.path, 'w') { |f| f.write(index_data) }
|
144
|
+
schema_bin(:load_indexes, database_url, tmp.path)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def schema_bin(*args)
|
149
|
+
bin_path = File.expand_path("#{File.dirname(__FILE__)}/../../bin/#{bin('schema')}")
|
150
|
+
`"#{bin_path}" #{args.map { |a| "'#{a}'" }.join(' ')}`
|
151
|
+
end
|
152
|
+
|
153
|
+
def primary_key(db, table)
|
154
|
+
db.schema(table).select { |c| c[1][:primary_key] }.map { |c| c[0] }
|
155
|
+
end
|
156
|
+
|
157
|
+
def single_integer_primary_key(db, table)
|
158
|
+
table = table.to_sym.identifier unless table.kind_of?(Sequel::SQL::Identifier)
|
159
|
+
keys = db.schema(table).select { |c| c[1][:primary_key] and c[1][:type] == :integer }
|
160
|
+
not keys.nil? and keys.size == 1
|
161
|
+
end
|
162
|
+
|
163
|
+
def order_by(db, table)
|
164
|
+
pkey = primary_key(db, table)
|
165
|
+
if pkey
|
166
|
+
pkey.kind_of?(Array) ? pkey : [pkey.to_sym]
|
167
|
+
else
|
168
|
+
table = table.to_sym.identifier unless table.kind_of?(Sequel::SQL::Identifier)
|
169
|
+
db[table].columns
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
# try to detect server side errors to
|
175
|
+
# give the client a more useful error message
|
176
|
+
def server_error_handling(&blk)
|
177
|
+
begin
|
178
|
+
blk.call
|
179
|
+
rescue Sequel::DatabaseError => e
|
180
|
+
if e.message =~ /duplicate key value/i
|
181
|
+
raise Taps::DuplicatePrimaryKeyError, e.message
|
182
|
+
else
|
183
|
+
raise
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def reraise_server_exception(e)
|
189
|
+
if e.kind_of?(RestClient::Exception)
|
190
|
+
if e.respond_to?(:response) && e.response.headers[:content_type] == 'application/json'
|
191
|
+
json = JSON.parse(e.response.to_s)
|
192
|
+
klass = eval(json['error_class']) rescue nil
|
193
|
+
raise klass.new(json['error_message'], :backtrace => json['error_backtrace']) if klass
|
194
|
+
end
|
195
|
+
end
|
196
|
+
raise e
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
data/spec/base.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bacon'
|
3
|
+
require 'mocha'
|
4
|
+
require 'rack/test'
|
5
|
+
require 'tempfile'
|
6
|
+
|
7
|
+
$:.unshift File.dirname(__FILE__) + "/../lib"
|
8
|
+
|
9
|
+
class Bacon::Context
|
10
|
+
include Mocha::Standalone
|
11
|
+
include Rack::Test::Methods
|
12
|
+
|
13
|
+
alias_method :old_it, :it
|
14
|
+
def it(description)
|
15
|
+
old_it(description) do
|
16
|
+
mocha_setup
|
17
|
+
yield
|
18
|
+
mocha_verify
|
19
|
+
mocha_teardown
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'taps/config'
|
25
|
+
Taps::Config.taps_database_url = "jdbc:sqlite://#{Tempfile.new('test.db').path}"
|
26
|
+
Sequel.connect(Taps::Config.taps_database_url)
|
data/spec/cli_spec.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/base'
|
2
|
+
require 'taps/cli'
|
3
|
+
|
4
|
+
describe Taps::Cli do
|
5
|
+
it "translates a list of tables into a regex that can be used in table_filter" do
|
6
|
+
@cli = Taps::Cli.new(["-t", "mytable1,logs", "jdbc:sqlite://tmp.db", "http://x:y@localhost:5000"])
|
7
|
+
opts = @cli.clientoptparse(:pull)
|
8
|
+
opts[:table_filter].should == "(^mytable1$|^logs$)"
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/base'
|
2
|
+
require 'taps/data_stream'
|
3
|
+
|
4
|
+
describe Taps::DataStream do
|
5
|
+
before do
|
6
|
+
@db = mock('db')
|
7
|
+
end
|
8
|
+
|
9
|
+
it "increments the offset" do
|
10
|
+
stream = Taps::DataStream.new(@db, :table_name => 'test_table', :chunksize => 100)
|
11
|
+
stream.state[:offset].should == 0
|
12
|
+
stream.increment(100)
|
13
|
+
stream.state[:offset].should == 100
|
14
|
+
end
|
15
|
+
|
16
|
+
it "marks the stream complete if no rows are fetched" do
|
17
|
+
stream = Taps::DataStream.new(@db, :table_name => 'test_table', :chunksize => 100)
|
18
|
+
stream.stubs(:fetch_rows).returns({})
|
19
|
+
stream.complete?.should.be.false
|
20
|
+
stream.fetch
|
21
|
+
stream.complete?.should.be.true
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/base'
|
2
|
+
require 'taps/operation'
|
3
|
+
|
4
|
+
describe Taps::Operation do
|
5
|
+
before do
|
6
|
+
@op = Taps::Operation.new('dummy://localhost', 'http://x:y@localhost:5000')
|
7
|
+
end
|
8
|
+
|
9
|
+
it "returns an array of tables that match the regex table_filter" do
|
10
|
+
@op = Taps::Operation.new('dummy://localhost', 'http://x:y@localhost:5000', :table_filter => 'abc')
|
11
|
+
@op.apply_table_filter(['abc', 'def']).should == ['abc']
|
12
|
+
end
|
13
|
+
|
14
|
+
it "returns a hash of tables that match the regex table_filter" do
|
15
|
+
@op = Taps::Operation.new('dummy://localhost', 'http://x:y@localhost:5000', :table_filter => 'abc')
|
16
|
+
@op.apply_table_filter({ 'abc' => 1, 'def' => 2 }).should == { 'abc' => 1 }
|
17
|
+
end
|
18
|
+
|
19
|
+
it "masks a url's password" do
|
20
|
+
@op.safe_url("mysql://root:password@localhost/mydb").should == "mysql://root:[hidden]@localhost/mydb"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "returns http headers with compression enabled" do
|
24
|
+
@op.http_headers.should == { :taps_version => Taps.version, :accept_encoding => "gzip, deflate" }
|
25
|
+
end
|
26
|
+
|
27
|
+
it "returns http headers with compression disabled" do
|
28
|
+
@op.stubs(:compression_disabled?).returns(true)
|
29
|
+
@op.http_headers.should == { :taps_version => Taps.version, :accept_encoding => "" }
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
data/spec/server_spec.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/base'
|
2
|
+
|
3
|
+
require 'taps/server'
|
4
|
+
|
5
|
+
require 'pp'
|
6
|
+
|
7
|
+
describe Taps::Server do
|
8
|
+
def app
|
9
|
+
Taps::Server.new
|
10
|
+
end
|
11
|
+
|
12
|
+
before do
|
13
|
+
Taps::Config.login = 'taps'
|
14
|
+
Taps::Config.password = 'tpass'
|
15
|
+
|
16
|
+
@app = Taps::Server
|
17
|
+
@auth_header = "Basic " + ["taps:tpass"].pack("m*")
|
18
|
+
end
|
19
|
+
|
20
|
+
it "asks for http basic authentication" do
|
21
|
+
get '/'
|
22
|
+
last_response.status.should == 401
|
23
|
+
end
|
24
|
+
|
25
|
+
it "verifies the client taps version" do
|
26
|
+
get('/', { }, { 'HTTP_AUTHORIZATION' => @auth_header, 'HTTP_TAPS_VERSION' => Taps.version })
|
27
|
+
last_response.status.should == 200
|
28
|
+
end
|
29
|
+
|
30
|
+
it "yells loudly if the client taps version doesn't match" do
|
31
|
+
get('/', { }, { 'HTTP_AUTHORIZATION' => @auth_header, 'HTTP_TAPS_VERSION' => '0.0.1' })
|
32
|
+
last_response.status.should == 417
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
data/spec/utils_spec.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/base'
|
2
|
+
require 'taps/utils'
|
3
|
+
|
4
|
+
describe Taps::Utils do
|
5
|
+
it "generates a checksum using crc32" do
|
6
|
+
Taps::Utils.checksum("hello world").should == Zlib.crc32("hello world")
|
7
|
+
end
|
8
|
+
|
9
|
+
it "formats a data hash into one hash that contains an array of headers and an array of array of data" do
|
10
|
+
first_row = { :x => 1, :y => 1 }
|
11
|
+
first_row.stubs(:keys).returns([:x, :y])
|
12
|
+
Taps::Utils.format_data([ first_row, { :x => 2, :y => 2 } ]).should == { :header => [ :x, :y ], :data => [ [1, 1], [2, 2] ] }
|
13
|
+
end
|
14
|
+
|
15
|
+
it "enforces length limitations on columns" do
|
16
|
+
data = [ { :a => "aaabbbccc" } ]
|
17
|
+
schema = [ [ :a, { :db_type => "varchar(3)" }]]
|
18
|
+
lambda { Taps::Utils.format_data(data, :schema => schema) }.should.raise(Taps::InvalidData)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "scales chunksize down slowly when the time delta of the block is just over a second" do
|
22
|
+
Time.stubs(:now).returns(10.0).returns(11.5)
|
23
|
+
Taps::Utils.calculate_chunksize(1000) { }.should == 900
|
24
|
+
end
|
25
|
+
|
26
|
+
it "scales chunksize down fast when the time delta of the block is over 3 seconds" do
|
27
|
+
Time.stubs(:now).returns(10.0).returns(15.0)
|
28
|
+
Taps::Utils.calculate_chunksize(3000) { }.should == 1000
|
29
|
+
end
|
30
|
+
|
31
|
+
it "scales up chunksize fast when the time delta of the block is under 0.8 seconds" do
|
32
|
+
Time.stubs(:now).returns(10.0).returns(10.7)
|
33
|
+
Taps::Utils.calculate_chunksize(1000) { }.should == 2000
|
34
|
+
end
|
35
|
+
|
36
|
+
it "scales up chunksize slow when the time delta of the block is between 0.8 and 1.1 seconds" do
|
37
|
+
Time.stubs(:now).returns(10.0).returns(10.8)
|
38
|
+
Taps::Utils.calculate_chunksize(1000) { }.should == 1100
|
39
|
+
|
40
|
+
Time.stubs(:now).returns(10.0).returns(11.1)
|
41
|
+
Taps::Utils.calculate_chunksize(1000) { }.should == 1100
|
42
|
+
end
|
43
|
+
|
44
|
+
it "will reset the chunksize to a small value if we got a broken pipe exception" do
|
45
|
+
Taps::Utils.calculate_chunksize(1000) { |c| raise Errno::EPIPE if c == 1000; c.should == 10 }.should == 10
|
46
|
+
end
|
47
|
+
|
48
|
+
it "will reset the chunksize to a small value if we got a broken pipe exception a second time" do
|
49
|
+
Taps::Utils.calculate_chunksize(1000) { |c| raise Errno::EPIPE if c == 1000 || c == 10; c.should == 1 }.should == 1
|
50
|
+
end
|
51
|
+
|
52
|
+
it "returns a list of columns that are text fields if the database is mysql" do
|
53
|
+
@db = mock("db", :url => "mysql://localhost/mydb")
|
54
|
+
@db.stubs(:schema).with(:mytable).returns([
|
55
|
+
[:id, { :db_type => "int" }],
|
56
|
+
[:mytext, { :db_type => "text" }]
|
57
|
+
])
|
58
|
+
Taps::Utils.incorrect_blobs(@db, :mytable).should == [:mytext]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
metadata
ADDED
@@ -0,0 +1,194 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: taps-jruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 3
|
8
|
+
- 14
|
9
|
+
version: 0.3.14
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Ricardo Chimal, Jr.
|
13
|
+
- Rob Heittman
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-11-19 00:00:00 -05:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: json_pure
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 1
|
30
|
+
- 2
|
31
|
+
- 0
|
32
|
+
version: 1.2.0
|
33
|
+
- - <
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
segments:
|
36
|
+
- 1
|
37
|
+
- 5
|
38
|
+
- 0
|
39
|
+
version: 1.5.0
|
40
|
+
type: :runtime
|
41
|
+
version_requirements: *id001
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: sinatra
|
44
|
+
prerelease: false
|
45
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ~>
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
segments:
|
50
|
+
- 1
|
51
|
+
- 0
|
52
|
+
- 0
|
53
|
+
version: 1.0.0
|
54
|
+
type: :runtime
|
55
|
+
version_requirements: *id002
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rest-client
|
58
|
+
prerelease: false
|
59
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
segments:
|
64
|
+
- 1
|
65
|
+
- 4
|
66
|
+
- 0
|
67
|
+
version: 1.4.0
|
68
|
+
- - <
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
segments:
|
71
|
+
- 1
|
72
|
+
- 7
|
73
|
+
- 0
|
74
|
+
version: 1.7.0
|
75
|
+
type: :runtime
|
76
|
+
version_requirements: *id003
|
77
|
+
- !ruby/object:Gem::Dependency
|
78
|
+
name: sequel
|
79
|
+
prerelease: false
|
80
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ~>
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
segments:
|
85
|
+
- 3
|
86
|
+
- 17
|
87
|
+
- 0
|
88
|
+
version: 3.17.0
|
89
|
+
type: :runtime
|
90
|
+
version_requirements: *id004
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: activerecord-jdbcsqlite3-adapter
|
93
|
+
prerelease: false
|
94
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
segments:
|
99
|
+
- 1
|
100
|
+
- 0
|
101
|
+
- 2
|
102
|
+
version: 1.0.2
|
103
|
+
type: :runtime
|
104
|
+
version_requirements: *id005
|
105
|
+
- !ruby/object:Gem::Dependency
|
106
|
+
name: rack
|
107
|
+
prerelease: false
|
108
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
segments:
|
113
|
+
- 1
|
114
|
+
- 0
|
115
|
+
- 1
|
116
|
+
version: 1.0.1
|
117
|
+
type: :runtime
|
118
|
+
version_requirements: *id006
|
119
|
+
description: A simple database agnostic import/export app to transfer data to/from a remote database. (JRuby version)
|
120
|
+
email: heittman.rob@gmail.com
|
121
|
+
executables:
|
122
|
+
- taps
|
123
|
+
- schema
|
124
|
+
extensions: []
|
125
|
+
|
126
|
+
extra_rdoc_files:
|
127
|
+
- LICENSE
|
128
|
+
- README.rdoc
|
129
|
+
- TODO
|
130
|
+
files:
|
131
|
+
- LICENSE
|
132
|
+
- README.rdoc
|
133
|
+
- Rakefile
|
134
|
+
- VERSION.yml
|
135
|
+
- bin/schema
|
136
|
+
- bin/schema.cmd
|
137
|
+
- bin/taps
|
138
|
+
- lib/taps/cli.rb
|
139
|
+
- lib/taps/config.rb
|
140
|
+
- lib/taps/data_stream.rb
|
141
|
+
- lib/taps/db_session.rb
|
142
|
+
- lib/taps/errors.rb
|
143
|
+
- lib/taps/log.rb
|
144
|
+
- lib/taps/monkey.rb
|
145
|
+
- lib/taps/multipart.rb
|
146
|
+
- lib/taps/operation.rb
|
147
|
+
- lib/taps/progress_bar.rb
|
148
|
+
- lib/taps/schema.rb
|
149
|
+
- lib/taps/server.rb
|
150
|
+
- lib/taps/utils.rb
|
151
|
+
- spec/base.rb
|
152
|
+
- spec/cli_spec.rb
|
153
|
+
- spec/data_stream_spec.rb
|
154
|
+
- spec/operation_spec.rb
|
155
|
+
- spec/server_spec.rb
|
156
|
+
- spec/utils_spec.rb
|
157
|
+
- TODO
|
158
|
+
has_rdoc: true
|
159
|
+
homepage: http://github.com/rfc2616/taps-jruby
|
160
|
+
licenses: []
|
161
|
+
|
162
|
+
post_install_message:
|
163
|
+
rdoc_options: []
|
164
|
+
|
165
|
+
require_paths:
|
166
|
+
- lib
|
167
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
168
|
+
requirements:
|
169
|
+
- - ">="
|
170
|
+
- !ruby/object:Gem::Version
|
171
|
+
segments:
|
172
|
+
- 0
|
173
|
+
version: "0"
|
174
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
175
|
+
requirements:
|
176
|
+
- - ">="
|
177
|
+
- !ruby/object:Gem::Version
|
178
|
+
segments:
|
179
|
+
- 0
|
180
|
+
version: "0"
|
181
|
+
requirements: []
|
182
|
+
|
183
|
+
rubyforge_project:
|
184
|
+
rubygems_version: 1.3.6
|
185
|
+
signing_key:
|
186
|
+
specification_version: 3
|
187
|
+
summary: simple database import/export app - jruby version
|
188
|
+
test_files:
|
189
|
+
- spec/base.rb
|
190
|
+
- spec/cli_spec.rb
|
191
|
+
- spec/data_stream_spec.rb
|
192
|
+
- spec/operation_spec.rb
|
193
|
+
- spec/server_spec.rb
|
194
|
+
- spec/utils_spec.rb
|