icebox 0.0.2 → 0.0.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/Gemfile +1 -0
- data/Rakefile +2 -2
- data/VERSION +1 -1
- data/icebox.gemspec +12 -8
- data/lib/icebox.rb +70 -19
- data/test/test_icebox.rb +80 -19
- metadata +77 -60
data/Gemfile
CHANGED
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.3
|
data/icebox.gemspec
CHANGED
@@ -4,14 +4,15 @@
|
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
|
-
s.name =
|
8
|
-
s.version = "0.0.
|
7
|
+
s.name = %q{icebox}
|
8
|
+
s.version = "0.0.3"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Riley Goodside"]
|
12
|
-
s.date =
|
13
|
-
s.
|
14
|
-
s.
|
12
|
+
s.date = %q{2011-10-25}
|
13
|
+
s.default_executable = %q{icebox}
|
14
|
+
s.description = %q{Ruby library and command-line utility for working with Infobright Community Edition}
|
15
|
+
s.email = %q{riley.goodside@gmail.com}
|
15
16
|
s.executables = ["icebox"]
|
16
17
|
s.extra_rdoc_files = [
|
17
18
|
"LICENSE.txt",
|
@@ -32,11 +33,11 @@ Gem::Specification.new do |s|
|
|
32
33
|
"test/helper.rb",
|
33
34
|
"test/test_icebox.rb"
|
34
35
|
]
|
35
|
-
s.homepage =
|
36
|
+
s.homepage = %q{http://github.com/goodside/icebox}
|
36
37
|
s.licenses = ["MIT"]
|
37
38
|
s.require_paths = ["lib"]
|
38
|
-
s.rubygems_version =
|
39
|
-
s.summary =
|
39
|
+
s.rubygems_version = %q{1.6.2}
|
40
|
+
s.summary = %q{Multitool for working with Infobright Community Edition}
|
40
41
|
|
41
42
|
if s.respond_to? :specification_version then
|
42
43
|
s.specification_version = 3
|
@@ -47,6 +48,7 @@ Gem::Specification.new do |s|
|
|
47
48
|
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
48
49
|
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
49
50
|
s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
|
51
|
+
s.add_development_dependency(%q<rdoc>, ["~> 3.11"])
|
50
52
|
s.add_development_dependency(%q<rcov>, [">= 0"])
|
51
53
|
else
|
52
54
|
s.add_dependency(%q<mysql>, ["~> 2.8.1"])
|
@@ -54,6 +56,7 @@ Gem::Specification.new do |s|
|
|
54
56
|
s.add_dependency(%q<shoulda>, [">= 0"])
|
55
57
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
56
58
|
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
59
|
+
s.add_dependency(%q<rdoc>, ["~> 3.11"])
|
57
60
|
s.add_dependency(%q<rcov>, [">= 0"])
|
58
61
|
end
|
59
62
|
else
|
@@ -62,6 +65,7 @@ Gem::Specification.new do |s|
|
|
62
65
|
s.add_dependency(%q<shoulda>, [">= 0"])
|
63
66
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
64
67
|
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
68
|
+
s.add_dependency(%q<rdoc>, ["~> 3.11"])
|
65
69
|
s.add_dependency(%q<rcov>, [">= 0"])
|
66
70
|
end
|
67
71
|
end
|
data/lib/icebox.rb
CHANGED
@@ -1,37 +1,88 @@
|
|
1
1
|
require 'sequel'
|
2
|
+
require 'yaml'
|
2
3
|
|
3
4
|
module Icebox
|
5
|
+
def self.new(*args, &block)
|
6
|
+
Icebox.new(*args, &block)
|
7
|
+
end
|
8
|
+
|
4
9
|
class Icebox
|
5
|
-
|
6
|
-
|
10
|
+
attr_accessor :db
|
11
|
+
|
12
|
+
def initialize(db = nil)
|
13
|
+
# Icebox is configurable using a YAML file in /etc/icebox.conf
|
14
|
+
# The root-level key :connections should be an array of option sets to be
|
15
|
+
# passed to the Sequel#mysql method. Right now, only the first is used.
|
16
|
+
|
17
|
+
@db = db
|
18
|
+
if @db.nil?
|
19
|
+
begin
|
20
|
+
opts = YAML.load(File.open("/etc/icebox.conf"))[:connections][0]
|
21
|
+
rescue
|
22
|
+
opts = {database: 'test', socket: '/tmp/mysql-ib.sock'}
|
23
|
+
end
|
24
|
+
@db = Sequel.mysql(opts)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def load_data_infile(table, path=nil, opts={})
|
29
|
+
# If a handle object is given, start writing it out into a new FIFO pipe
|
30
|
+
# from within a forked subprocess before we start loading:
|
31
|
+
if opts[:handle]
|
32
|
+
pipe = "/tmp/icebox_pipe_for_#{table}_at_#{Time.now.to_i}"
|
33
|
+
system "mkfifo #{pipe}"
|
34
|
+
opts[:fifo] = pipe
|
35
|
+
pid = fork do
|
36
|
+
File.open(pipe, 'a') do |p|
|
37
|
+
p.write opts[:handle].read
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
path = path || opts[:path] || opts[:fifo]
|
43
|
+
raise "load_data_infile called with no input source" unless path
|
44
|
+
|
45
|
+
load_sql = <<-SQL
|
46
|
+
#{"SET @bh_pipemode = 'server';" if opts[:fifo]}
|
47
|
+
LOAD DATA INFILE '#{path}' INTO TABLE #{table}
|
48
|
+
FIELDS TERMINATED BY ',' ENCLOSED BY '"' ESCAPED BY '\\\\';
|
49
|
+
SQL
|
50
|
+
# Force value retrieval to delay function return until query finishes:
|
51
|
+
@db[load_sql].all
|
52
|
+
Process.waitpid(pid) if pid # Wait for writer too if it exists
|
53
|
+
end
|
54
|
+
|
55
|
+
def insert_into(table, sql)
|
56
|
+
pipe = "/tmp/icebox_pipe_for_#{table}"
|
57
|
+
sql.sub! /\s*;\s*/, '' # Chop off semicolon if present
|
58
|
+
|
59
|
+
# Simultaneously export and re-import CSV through FIFO pipe:
|
60
|
+
@db.disconnect # Needed for fork; Reconnects automatically
|
61
|
+
pid1 = fork do
|
62
|
+
@db << <<-SQL
|
63
|
+
SET @bh_pipemode = 'client';
|
64
|
+
#{sql} INTO OUTFILE '#{pipe}'
|
65
|
+
FIELDS TERMINATED BY ',' ENCLOSED BY '"' ESCAPED BY '\\\\'
|
66
|
+
SQL
|
67
|
+
end
|
68
|
+
pid2 = fork do
|
69
|
+
load_data_infile table, pipe, fifo: true
|
70
|
+
end
|
71
|
+
Process.waitpid(pid1)
|
72
|
+
Process.waitpid(pid2)
|
7
73
|
end
|
8
74
|
|
9
75
|
def create_table_as(table, sql)
|
10
76
|
view = "icebox_view_for_#{table}"
|
11
|
-
pipe = "/tmp/icebox_pipe_for_#{table}"
|
12
77
|
|
13
78
|
# Create view from SQL, extract layout, and make new table from it:
|
14
79
|
@db.create_or_replace_view view, sql
|
15
80
|
fields_definition = @db.schema(view).map do |col|
|
16
81
|
"#{col[0]} #{col[1][:db_type]}"
|
17
82
|
end.join(',')
|
18
|
-
@db
|
19
|
-
|
20
|
-
# Simultaneously export and re-import CSV through FIFO pipe:
|
21
|
-
@db.disconnect # Needed for fork; Reconnects automatically
|
22
|
-
fork do
|
23
|
-
@db.run <<-SQL
|
24
|
-
SET @bh_pipemode = 'server';
|
25
|
-
LOAD DATA INFILE '#{pipe}' INTO TABLE #{table}
|
26
|
-
FIELDS TERMINATED BY ',' ENCLOSED BY '"' ESCAPED BY '\\\\';
|
27
|
-
SQL
|
28
|
-
end
|
29
|
-
@db.run <<-SQL
|
30
|
-
SET @bh_pipemode = 'client';
|
31
|
-
SELECT * FROM #{view} INTO OUTFILE '#{pipe}'
|
32
|
-
FIELDS TERMINATED BY ',' ENCLOSED BY '"' ESCAPED BY '\\\\'
|
33
|
-
SQL
|
83
|
+
@db << "CREATE TABLE #{table} (#{fields_definition})"
|
34
84
|
|
85
|
+
insert_into table, "SELECT * FROM #{view}"
|
35
86
|
@db.drop_view view
|
36
87
|
end
|
37
88
|
end
|
data/test/test_icebox.rb
CHANGED
@@ -1,29 +1,90 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
3
|
class TestIcebox < Test::Unit::TestCase
|
4
|
-
context "
|
4
|
+
context "A clean, default Icebox instance" do
|
5
5
|
setup do
|
6
|
-
@
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
6
|
+
@box = Icebox.new
|
7
|
+
end
|
8
|
+
should "have an internal Sequel MySQL DB accessible via #db" do
|
9
|
+
assert_equal @box.db.class, Sequel::MySQL::Database
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context "Given a CSV file," do
|
14
|
+
setup do
|
15
|
+
File.open('/tmp/test_table.csv', 'w') do |f|
|
16
|
+
f.write <<-CSV.gsub(/^\s*/, '')
|
17
|
+
-127,-127,-32767,-8388608,-2147483647,-9223372036854775806,-3.402823466E+38,-1.7976931348623157E+308,-8675309.8675309,"Negative"
|
18
|
+
127,127,32767,8388608,2147483647,9223372036854775806,3.402823466E+38,1.7976931348623157E+308,8675309.8675309,"Positive"
|
19
|
+
0,0,0,0,0,0,0.0,0.0,0.0,"Zero"
|
20
|
+
CSV
|
21
|
+
end
|
22
|
+
@box = Icebox.new
|
23
|
+
@db = @box.db
|
24
|
+
@db << <<-SQL
|
25
|
+
CREATE TABLE test_table (
|
26
|
+
ti TINYINT,
|
27
|
+
b BOOLEAN,
|
28
|
+
si SMALLINT,
|
29
|
+
mi MEDIUMINT,
|
30
|
+
i INTEGER,
|
31
|
+
bi BIGINT,
|
32
|
+
f FLOAT,
|
33
|
+
dp DOUBLE PRECISION,
|
34
|
+
dc DEC(14,7),
|
35
|
+
tx TEXT
|
36
|
+
)
|
18
37
|
SQL
|
19
38
|
end
|
20
39
|
|
21
|
-
should "copy
|
22
|
-
box
|
23
|
-
@
|
24
|
-
|
25
|
-
|
26
|
-
assert_equal
|
40
|
+
should "load data from CSV and copy it with #create_table_as" do
|
41
|
+
@box.load_data_infile 'test_table', '/tmp/test_table.csv'
|
42
|
+
@box.create_table_as 'test_ctas_output', 'SELECT * FROM test_table'
|
43
|
+
|
44
|
+
t = @db[:test_ctas_output]
|
45
|
+
assert_equal 3, t.count
|
46
|
+
assert_equal -127, t.order(:ti).get(:ti)
|
47
|
+
assert_equal "Negative", t.order(:si).get(:tx)
|
48
|
+
assert_equal BigDecimal.new("8675309.8675309"), t.order(:dc.desc).get(:dc)
|
49
|
+
end
|
50
|
+
|
51
|
+
should "append loaded CSV data to an existing table" do
|
52
|
+
@box.load_data_infile 'test_table', '/tmp/test_table.csv'
|
53
|
+
count_1 = @db[:test_table].count
|
54
|
+
@box.load_data_infile 'test_table', '/tmp/test_table.csv'
|
55
|
+
count_2 = @db[:test_table].count
|
56
|
+
|
57
|
+
assert_equal 3, count_1
|
58
|
+
assert_equal 6, count_2
|
59
|
+
end
|
60
|
+
|
61
|
+
should "append a query result onto existing data via #insert_into" do
|
62
|
+
@box.load_data_infile 'test_table', '/tmp/test_table.csv'
|
63
|
+
count_1 = @db[:test_table].count
|
64
|
+
@box.insert_into 'test_table', 'SELECT * FROM test_table'
|
65
|
+
count_2 = @db[:test_table].count
|
66
|
+
|
67
|
+
assert_equal 3, count_1
|
68
|
+
assert_equal 6, count_2
|
69
|
+
end
|
70
|
+
|
71
|
+
should "not have race conditions when #insert_into is called rapidly" do
|
72
|
+
@box.load_data_infile 'test_table', '/tmp/test_table.csv'
|
73
|
+
count_1 = @db[:test_table].count
|
74
|
+
5.times do
|
75
|
+
@box.insert_into 'test_table', 'SELECT * FROM test_table'
|
76
|
+
end
|
77
|
+
count_2 = @db[:test_table].count
|
78
|
+
|
79
|
+
assert_equal 3, count_1
|
80
|
+
assert_equal 3 * (2**5), count_2
|
81
|
+
end
|
82
|
+
|
83
|
+
teardown do
|
84
|
+
File.delete '/tmp/test_table.csv'
|
85
|
+
@db.drop_table 'test_table'
|
86
|
+
@db.drop_table 'test_ctas_output' rescue Sequel
|
27
87
|
end
|
28
88
|
end
|
89
|
+
|
29
90
|
end
|
metadata
CHANGED
@@ -1,92 +1,105 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: icebox
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.2
|
3
|
+
version: !ruby/object:Gem::Version
|
5
4
|
prerelease:
|
5
|
+
version: 0.0.3
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
7
|
+
authors:
|
8
8
|
- Riley Goodside
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
|
13
|
+
date: 2011-10-25 00:00:00 -04:00
|
14
|
+
default_executable: icebox
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
15
17
|
name: mysql
|
16
|
-
requirement: &
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
17
19
|
none: false
|
18
|
-
requirements:
|
20
|
+
requirements:
|
19
21
|
- - ~>
|
20
|
-
- !ruby/object:Gem::Version
|
22
|
+
- !ruby/object:Gem::Version
|
21
23
|
version: 2.8.1
|
22
24
|
type: :development
|
23
25
|
prerelease: false
|
24
|
-
version_requirements: *
|
25
|
-
- !ruby/object:Gem::Dependency
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
26
28
|
name: sequel
|
27
|
-
requirement: &
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
28
30
|
none: false
|
29
|
-
requirements:
|
31
|
+
requirements:
|
30
32
|
- - ~>
|
31
|
-
- !ruby/object:Gem::Version
|
33
|
+
- !ruby/object:Gem::Version
|
32
34
|
version: 3.27.0
|
33
35
|
type: :development
|
34
36
|
prerelease: false
|
35
|
-
version_requirements: *
|
36
|
-
- !ruby/object:Gem::Dependency
|
37
|
+
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
37
39
|
name: shoulda
|
38
|
-
requirement: &
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
39
41
|
none: false
|
40
|
-
requirements:
|
41
|
-
- -
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
version:
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
44
46
|
type: :development
|
45
47
|
prerelease: false
|
46
|
-
version_requirements: *
|
47
|
-
- !ruby/object:Gem::Dependency
|
48
|
+
version_requirements: *id003
|
49
|
+
- !ruby/object:Gem::Dependency
|
48
50
|
name: bundler
|
49
|
-
requirement: &
|
51
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
50
52
|
none: false
|
51
|
-
requirements:
|
53
|
+
requirements:
|
52
54
|
- - ~>
|
53
|
-
- !ruby/object:Gem::Version
|
55
|
+
- !ruby/object:Gem::Version
|
54
56
|
version: 1.0.0
|
55
57
|
type: :development
|
56
58
|
prerelease: false
|
57
|
-
version_requirements: *
|
58
|
-
- !ruby/object:Gem::Dependency
|
59
|
+
version_requirements: *id004
|
60
|
+
- !ruby/object:Gem::Dependency
|
59
61
|
name: jeweler
|
60
|
-
requirement: &
|
62
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
61
63
|
none: false
|
62
|
-
requirements:
|
64
|
+
requirements:
|
63
65
|
- - ~>
|
64
|
-
- !ruby/object:Gem::Version
|
66
|
+
- !ruby/object:Gem::Version
|
65
67
|
version: 1.6.4
|
66
68
|
type: :development
|
67
69
|
prerelease: false
|
68
|
-
version_requirements: *
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
+
version_requirements: *id005
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: rdoc
|
73
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ~>
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: "3.11"
|
79
|
+
type: :development
|
80
|
+
prerelease: false
|
81
|
+
version_requirements: *id006
|
82
|
+
- !ruby/object:Gem::Dependency
|
70
83
|
name: rcov
|
71
|
-
requirement: &
|
84
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
72
85
|
none: false
|
73
|
-
requirements:
|
74
|
-
- -
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
version:
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: "0"
|
77
90
|
type: :development
|
78
91
|
prerelease: false
|
79
|
-
version_requirements: *
|
80
|
-
description: Ruby library and command-line utility for working with Infobright Community
|
81
|
-
Edition
|
92
|
+
version_requirements: *id007
|
93
|
+
description: Ruby library and command-line utility for working with Infobright Community Edition
|
82
94
|
email: riley.goodside@gmail.com
|
83
|
-
executables:
|
95
|
+
executables:
|
84
96
|
- icebox
|
85
97
|
extensions: []
|
86
|
-
|
98
|
+
|
99
|
+
extra_rdoc_files:
|
87
100
|
- LICENSE.txt
|
88
101
|
- README.rdoc
|
89
|
-
files:
|
102
|
+
files:
|
90
103
|
- .document
|
91
104
|
- Gemfile
|
92
105
|
- LICENSE.txt
|
@@ -100,32 +113,36 @@ files:
|
|
100
113
|
- tasks/scratch.rb
|
101
114
|
- test/helper.rb
|
102
115
|
- test/test_icebox.rb
|
116
|
+
has_rdoc: true
|
103
117
|
homepage: http://github.com/goodside/icebox
|
104
|
-
licenses:
|
118
|
+
licenses:
|
105
119
|
- MIT
|
106
120
|
post_install_message:
|
107
121
|
rdoc_options: []
|
108
|
-
|
122
|
+
|
123
|
+
require_paths:
|
109
124
|
- lib
|
110
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
125
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
111
126
|
none: false
|
112
|
-
requirements:
|
113
|
-
- -
|
114
|
-
- !ruby/object:Gem::Version
|
115
|
-
|
116
|
-
segments:
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
hash: -170128870566158022
|
131
|
+
segments:
|
117
132
|
- 0
|
118
|
-
|
119
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
|
+
version: "0"
|
134
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
135
|
none: false
|
121
|
-
requirements:
|
122
|
-
- -
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version:
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: "0"
|
125
140
|
requirements: []
|
141
|
+
|
126
142
|
rubyforge_project:
|
127
|
-
rubygems_version: 1.
|
143
|
+
rubygems_version: 1.6.2
|
128
144
|
signing_key:
|
129
145
|
specification_version: 3
|
130
146
|
summary: Multitool for working with Infobright Community Edition
|
131
147
|
test_files: []
|
148
|
+
|