squealer 2.2.1 → 2.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +2 -2
- data/VERSION +1 -1
- data/lib/squealer/database.rb +13 -15
- data/lib/squealer/target.rb +1 -1
- data/spec/integration/export_a_record_spec.rb +3 -15
- data/spec/integration/imports_from_mongodb.rb +166 -0
- data/spec/integration/spec_helper_dbms.rb +111 -0
- data/spec/{spec_helper_dbms_mysql.rb → integration/spec_helper_dbms_mysql.rb} +9 -16
- data/spec/{spec_helper_dbms_postgres.rb → integration/spec_helper_dbms_postgres.rb} +9 -16
- data/spec/spec_helper.rb +6 -82
- data/spec/squealer/database_spec.rb +0 -159
- data/spec/squealer/object_spec.rb +2 -5
- data/spec/squealer/target_spec.rb +5 -0
- data/squealer.gemspec +12 -8
- metadata +13 -9
data/Rakefile
CHANGED
@@ -10,8 +10,8 @@ begin
|
|
10
10
|
require 'jeweler'
|
11
11
|
Jeweler::Tasks.new do |gemspec|
|
12
12
|
gemspec.name = "squealer"
|
13
|
-
gemspec.summary = "
|
14
|
-
gemspec.description = "
|
13
|
+
gemspec.summary = "Export document-oriented database to RDBMS"
|
14
|
+
gemspec.description = "A Ruby DSL for exporting MongoDB to MySQL or PostgreSQL. You don't need to install both, just one. Use EXPORT_DBMS=[mysql|postgres] environment variable to specify the appropriate adapter."
|
15
15
|
gemspec.email = "joshua.graham@grahamis.com"
|
16
16
|
gemspec.homepage = "http://github.com/delitescere/squealer/"
|
17
17
|
gemspec.authors = ["Josh Graham", "Durran Jordan", "Matt Yoho", "Bernerd Schaefer"]
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.2.
|
1
|
+
2.2.2
|
data/lib/squealer/database.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
require 'mongo'
|
2
|
-
require 'data_objects'
|
3
|
-
|
4
1
|
require 'singleton'
|
5
2
|
|
3
|
+
require 'mongo'
|
4
|
+
# data_objects required under export_to dependant on adapter requested in EXPORT_DBMS
|
5
|
+
|
6
6
|
module Squealer
|
7
7
|
class Database
|
8
8
|
include Singleton
|
@@ -15,11 +15,14 @@ module Squealer
|
|
15
15
|
def export_to(adapter, host, username, password, name)
|
16
16
|
require "do_#{adapter}"
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
@export_do.release if @export_do
|
19
|
+
creds = ""
|
20
|
+
creds << username if username
|
21
|
+
creds << ":#{password}" if password
|
22
|
+
at_host = ""
|
23
|
+
at_host << "#{creds}@" unless creds.empty?
|
24
|
+
at_host << host
|
25
|
+
@export_do = DataObjects::Connection.new("#{adapter}://#{at_host}/#{name}")
|
23
26
|
end
|
24
27
|
|
25
28
|
def import
|
@@ -51,7 +54,7 @@ module Squealer
|
|
51
54
|
def eval(string)
|
52
55
|
@dbc.eval(string)
|
53
56
|
end
|
54
|
-
end
|
57
|
+
end # Connection
|
55
58
|
|
56
59
|
class Source
|
57
60
|
attr_reader :counts, :cursor
|
@@ -78,12 +81,7 @@ module Squealer
|
|
78
81
|
end
|
79
82
|
@progress_bar.finish if @progress_bar
|
80
83
|
end
|
81
|
-
end
|
84
|
+
end # Source
|
82
85
|
|
83
|
-
private
|
84
|
-
|
85
|
-
def dispose_all_connections
|
86
|
-
@@all_export_connections.each {|c| c.dispose if c} if defined?(@@all_export_connections)
|
87
|
-
end
|
88
86
|
end
|
89
87
|
end
|
data/lib/squealer/target.rb
CHANGED
@@ -49,7 +49,7 @@ module Squealer
|
|
49
49
|
(eval "#{@table_name}['_id']", @binding, __FILE__, __LINE__)
|
50
50
|
).to_s
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
53
|
def verify_table_name_in_scope
|
54
54
|
table = eval "#{@table_name}", @binding, __FILE__, __LINE__
|
55
55
|
raise ArgumentError, "The variable '#{@table_name}' is not a hashmap" unless table.is_a? Hash
|
@@ -1,17 +1,9 @@
|
|
1
|
-
require '
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/./spec_helper_dbms')
|
2
2
|
|
3
3
|
describe "Exporting" do
|
4
|
-
before do
|
5
|
-
truncate_export_tables
|
6
|
-
end
|
7
|
-
|
8
4
|
let(:databases) { Squealer::Database.instance }
|
9
5
|
let(:today) { Date.today }
|
10
6
|
|
11
|
-
def prepare_export_database
|
12
|
-
databases.export_to($db_adapter, 'localhost', $db_user, '', $db_name)
|
13
|
-
end
|
14
|
-
|
15
7
|
def squeal_basic_users_document(user=users_document)
|
16
8
|
target(:user) do
|
17
9
|
assign(:name)
|
@@ -61,7 +53,7 @@ describe "Exporting" do
|
|
61
53
|
end
|
62
54
|
|
63
55
|
let :first_users_record do
|
64
|
-
dbc = databases.
|
56
|
+
dbc = databases.export
|
65
57
|
reader = dbc.create_command(%{SELECT * FROM "user"}).execute_reader
|
66
58
|
result = reader.each { |x| break x }
|
67
59
|
reader.close
|
@@ -69,7 +61,7 @@ describe "Exporting" do
|
|
69
61
|
end
|
70
62
|
|
71
63
|
let :first_activity_record do
|
72
|
-
dbc = databases.
|
64
|
+
dbc = databases.export
|
73
65
|
reader = dbc.create_command(%{SELECT * FROM "activity"}).execute_reader
|
74
66
|
result = reader.each { |x| break x }
|
75
67
|
reader.close
|
@@ -78,7 +70,6 @@ describe "Exporting" do
|
|
78
70
|
|
79
71
|
context "a new record" do
|
80
72
|
it "saves the data correctly" do
|
81
|
-
prepare_export_database
|
82
73
|
squeal_basic_users_document
|
83
74
|
result = first_users_record
|
84
75
|
|
@@ -99,7 +90,6 @@ describe "Exporting" do
|
|
99
90
|
end
|
100
91
|
|
101
92
|
it "saves embedded documents correctly" do
|
102
|
-
prepare_export_database
|
103
93
|
squeal_users_document_with_activities
|
104
94
|
result = first_activity_record
|
105
95
|
|
@@ -112,7 +102,6 @@ describe "Exporting" do
|
|
112
102
|
|
113
103
|
context "an existing record" do
|
114
104
|
it "updates the data correctly" do
|
115
|
-
prepare_export_database
|
116
105
|
squeal_basic_users_document
|
117
106
|
squeal_basic_users_document(users_document.merge('foreign' => false, 'gender' => 'F'))
|
118
107
|
|
@@ -135,7 +124,6 @@ describe "Exporting" do
|
|
135
124
|
end
|
136
125
|
|
137
126
|
it "updates the child record correctly" do
|
138
|
-
prepare_export_database
|
139
127
|
squeal_users_document_with_activities(users_document.merge('activities' => [{ :_id => 'a1', 'name' => 'Be expansionist', 'due_date' => as_time(today + 1) }]))
|
140
128
|
result = first_activity_record
|
141
129
|
|
@@ -0,0 +1,166 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/./spec_helper_dbms')
|
2
|
+
|
3
|
+
describe Squealer::Database do
|
4
|
+
it "is a singleton" do
|
5
|
+
Squealer::Database.respond_to?(:instance).should be_true
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "import" do
|
9
|
+
let(:databases) { Squealer::Database.instance }
|
10
|
+
|
11
|
+
|
12
|
+
it "takes an import database" do
|
13
|
+
databases.send(:instance_variable_get, '@import_dbc').should be_a_kind_of(Mongo::DB)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "returns a squealer connection object" do
|
17
|
+
databases.import.should be_a_kind_of(Squealer::Database::Connection)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "delegates eval to Mongo" do
|
21
|
+
databases.send(:instance_variable_get, '@import_dbc').eval('db.getName()').should == $db_name
|
22
|
+
databases.import.eval('db.getName()').should == $db_name
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "source" do
|
27
|
+
let(:databases) { Squealer::Database.instance }
|
28
|
+
|
29
|
+
before { databases.import_from('localhost', 27017, $db_name) }
|
30
|
+
|
31
|
+
it "returns a Source" do
|
32
|
+
databases.import.source('foo').should be_a_kind_of(Squealer::Database::Source)
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "Source::cursor" do
|
36
|
+
it "returns a databases cursor" do
|
37
|
+
databases.import.source('foo').cursor.should be_a_kind_of(Mongo::Cursor)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "an empty collection" do
|
42
|
+
subject { databases.import.source('foo') }
|
43
|
+
|
44
|
+
it "counts a total of zero" do
|
45
|
+
subject.counts[:total].should == 0
|
46
|
+
end
|
47
|
+
|
48
|
+
it "counts zero imported" do
|
49
|
+
subject.counts[:imported].should == 0
|
50
|
+
end
|
51
|
+
|
52
|
+
it "counts zero exported" do
|
53
|
+
subject.counts[:exported].should == 0
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "a collection with two documents" do
|
58
|
+
let(:mongo) { Squealer::Database.instance.import.send(:instance_variable_get, '@dbc') }
|
59
|
+
|
60
|
+
subject do
|
61
|
+
mongo.collection('foo').save({'name' => 'Bar'});
|
62
|
+
mongo.collection('foo').save({'name' => 'Baz'});
|
63
|
+
source = databases.import.source('foo') # activate the counter
|
64
|
+
source.send(:instance_variable_set, :@progress_bar, nil)
|
65
|
+
Squealer::ProgressBar.send(:class_variable_set, :@@progress_bar, nil)
|
66
|
+
source
|
67
|
+
end
|
68
|
+
|
69
|
+
after do
|
70
|
+
mongo.collection('foo').drop
|
71
|
+
end
|
72
|
+
|
73
|
+
it "returns a Source" do
|
74
|
+
subject.should be_a_kind_of(Squealer::Database::Source)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "counts a total of two" do
|
78
|
+
subject.counts[:total].should == 2
|
79
|
+
end
|
80
|
+
|
81
|
+
context "before iterating" do
|
82
|
+
it "counts zero imported" do
|
83
|
+
subject.counts[:imported].should == 0
|
84
|
+
end
|
85
|
+
|
86
|
+
it "counts zero exported" do
|
87
|
+
subject.counts[:exported].should == 0
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "after iterating" do
|
92
|
+
before do
|
93
|
+
subject.each {}
|
94
|
+
end
|
95
|
+
|
96
|
+
it "counts two imported" do
|
97
|
+
subject.counts[:imported].should == 2
|
98
|
+
end
|
99
|
+
|
100
|
+
it "counts two exported" do
|
101
|
+
subject.counts[:exported].should == 2
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context "real squeal" do
|
106
|
+
# before { pending "interactive_view" }
|
107
|
+
it "exports that stuff to SQL" do
|
108
|
+
databases.export_to($do_adapter, 'localhost', $do_user, $do_pass, $db_name)
|
109
|
+
databases.import.source("users").each do |user|
|
110
|
+
target(:user) do |target|
|
111
|
+
target.instance_variable_get('@row_id').should == user['_id'].to_s
|
112
|
+
assign(:organization_id)
|
113
|
+
assign(:name)
|
114
|
+
|
115
|
+
#TODO: Update README to highlight that all embedded docs should have an _id
|
116
|
+
# as all Ruby mappers for MongoDB make one. (according to Durran)
|
117
|
+
user.activities.each do |activity|
|
118
|
+
target(:activity) do |target|
|
119
|
+
assign(:user_id)
|
120
|
+
assign(:name)
|
121
|
+
assign(:due_date)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
describe "export" do
|
134
|
+
let(:databases) { Squealer::Database.instance }
|
135
|
+
|
136
|
+
it "creates a DataObjects connection to the export database" do
|
137
|
+
databases.export_to($do_adapter, 'localhost', $do_user, $do_pass, $db_name)
|
138
|
+
databases.instance_variable_get('@export_do').should be_a_kind_of DataObjects::Connection
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
describe "upsertable?" do
|
143
|
+
subject { Squealer::Database.instance }
|
144
|
+
|
145
|
+
if defined?(DataObjects::Mysql)
|
146
|
+
context "mysql connection" do
|
147
|
+
before do
|
148
|
+
subject.export_to('mysql', 'localhost', $do_user, $do_pass, 'mysql')
|
149
|
+
end
|
150
|
+
|
151
|
+
it { should be_upsertable }
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
if defined?(DataObjects::Postgres)
|
156
|
+
context "postgres connection" do
|
157
|
+
before do
|
158
|
+
subject.export_to('postgres', 'localhost', $do_user, $do_pass, 'postgres')
|
159
|
+
end
|
160
|
+
|
161
|
+
it { should_not be_upsertable }
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + "/./spec_helper_dbms_#{ENV['EXPORT_DBMS']||'mysql'}")
|
3
|
+
|
4
|
+
Spec::Runner.configure do |config|
|
5
|
+
config.before(:suite) do
|
6
|
+
$db_name = "squealer_test_export_#{object_id}"
|
7
|
+
create_export_db($db_name)
|
8
|
+
create_import_db($db_name)
|
9
|
+
end
|
10
|
+
|
11
|
+
config.after(:suite) do
|
12
|
+
DataObjects::Pooling.pools.each {|pool| pool.flush!}
|
13
|
+
drop_export_test_db($db_name)
|
14
|
+
|
15
|
+
drop_mongo
|
16
|
+
end
|
17
|
+
|
18
|
+
config.before(:each) do
|
19
|
+
if self.class.example_implementations.first.first.location =~ %r{/spec/integration/}
|
20
|
+
Squealer::Database.instance.export_to($do_adapter, $do_host, $do_user, $do_pass, $db_name)
|
21
|
+
truncate_export_tables(Squealer::Database.instance.export)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
config.after(:each) do
|
25
|
+
if self.class.example_implementations.first.first.location =~ %r{/spec/integration/}
|
26
|
+
Squealer::Database.instance.export.release
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_import_db(name)
|
32
|
+
Squealer::Database.instance.import_from('localhost', 27017, name)
|
33
|
+
@mongo = Squealer::Database.instance.import.instance_variable_get('@dbc')
|
34
|
+
drop_mongo
|
35
|
+
seed_import
|
36
|
+
end
|
37
|
+
|
38
|
+
def drop_mongo
|
39
|
+
@mongo.eval('db.dropDatabase()') if @mongo
|
40
|
+
end
|
41
|
+
|
42
|
+
def seed_import
|
43
|
+
hashrocket = @mongo.collection('organizations').save({ :name => 'Hashrocket' })
|
44
|
+
zorganization = @mongo.collection('organizations').save({ :name => 'Zorganization', :disabled_date => as_time(Date.today) })
|
45
|
+
|
46
|
+
users = [
|
47
|
+
{ :name => 'Josh Graham', :dob => as_time(Date.parse('01-Jan-1971')), :gender => 'M',
|
48
|
+
:organization_id => hashrocket,
|
49
|
+
:activities => [
|
50
|
+
{ :_id => id, :name => 'Develop squealer', :due_date => as_time(Date.today + 1) },
|
51
|
+
{ :_id => id, :name => 'Organize speakerconf.com', :due_date => as_time(Date.today + 30) },
|
52
|
+
{ :_id => id, :name => 'Hashrocket party', :due_date => as_time(Date.today + 7) }
|
53
|
+
]
|
54
|
+
},
|
55
|
+
{ :name => 'Bernerd Schaefer', :dob => as_time(Date.parse('31-Dec-1985')), :gender => 'M',
|
56
|
+
:organization_id => hashrocket,
|
57
|
+
:activities => [
|
58
|
+
{ :_id => id, :name => 'Retype all of the code Josh wrote in squealer', :due_date => as_time(Date.today + 2) },
|
59
|
+
{ :_id => id, :name => 'Listen to rare Thelonius Monk EP', :due_date => as_time(Date.today) },
|
60
|
+
{ :_id => id, :name => 'Practice karaoke', :due_date => as_time(Date.today + 7) }
|
61
|
+
]
|
62
|
+
},
|
63
|
+
{ :name => 'Your momma', :dob => as_time(Date.parse('15-Jun-1955')), :gender => 'F',
|
64
|
+
:organization_id => zorganization,
|
65
|
+
:activities => [
|
66
|
+
{ :_id => id, :name => 'Cook me some pie', :due_date => as_time(Date.today) },
|
67
|
+
{ :_id => id, :name => 'Make me a sammich', :due_date => as_time(Date.today) }
|
68
|
+
]
|
69
|
+
}
|
70
|
+
]
|
71
|
+
|
72
|
+
users.each { |user| @mongo.collection('users').save user }
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
def truncate_export_tables(dbc)
|
77
|
+
%w{user activity organization}.each do |t|
|
78
|
+
text = %{TRUNCATE TABLE "#{t}"}
|
79
|
+
dbc.create_command(text).execute_non_query
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def non_query(text)
|
84
|
+
dbc = do_conn
|
85
|
+
dbc.create_command(text).execute_non_query
|
86
|
+
dbc.release
|
87
|
+
end
|
88
|
+
|
89
|
+
def drop_export_test_db(name)
|
90
|
+
dbc = do_conn_default
|
91
|
+
dbc.create_command("DROP DATABASE IF EXISTS #{name}").execute_non_query
|
92
|
+
dbc.release
|
93
|
+
end
|
94
|
+
|
95
|
+
def do_conn
|
96
|
+
DataObjects::Connection.new("#{$do_adapter}://#{at_host}/#{$db_name}")
|
97
|
+
end
|
98
|
+
|
99
|
+
def do_conn_default
|
100
|
+
DataObjects::Connection.new("#{$do_adapter}://#{at_host}/#{$do_adapter}")
|
101
|
+
end
|
102
|
+
|
103
|
+
def at_host
|
104
|
+
creds = ""
|
105
|
+
creds << $do_user if $do_user
|
106
|
+
creds << ":#{$do_pass}" if $do_pass
|
107
|
+
|
108
|
+
at_host = ""
|
109
|
+
at_host << "#{creds}@" unless creds.empty?
|
110
|
+
at_host << $do_host
|
111
|
+
end
|
@@ -1,19 +1,17 @@
|
|
1
|
+
require 'do_mysql'
|
2
|
+
|
3
|
+
$do_adapter = 'mysql'
|
4
|
+
$do_host = 'localhost'
|
5
|
+
$do_user = 'root'
|
6
|
+
$do_pass = ''
|
7
|
+
|
1
8
|
def create_export_db(name)
|
2
|
-
|
3
|
-
$db_adapter = 'mysql'
|
4
|
-
dbc = DataObjects::Connection.new("mysql://root@localhost/mysql")
|
9
|
+
dbc = do_conn_default
|
5
10
|
dbc.create_command("DROP DATABASE IF EXISTS #{name}").execute_non_query
|
6
11
|
dbc.create_command("CREATE DATABASE #{name}").execute_non_query
|
7
12
|
dbc.create_command("SET sql_mode='ANSI_QUOTES'").execute_non_query
|
13
|
+
dbc.release
|
8
14
|
create_export_tables
|
9
|
-
dbc.dispose
|
10
|
-
end
|
11
|
-
|
12
|
-
def drop_export_test_db(name)
|
13
|
-
@export_dbc.dispose if @export_dbc
|
14
|
-
dbc = DataObjects::Connection.new("mysql://root@localhost/mysql")
|
15
|
-
dbc.create_command("DROP DATABASE IF EXISTS #{name}").execute_non_query
|
16
|
-
dbc.dispose
|
17
15
|
end
|
18
16
|
|
19
17
|
def create_export_tables
|
@@ -50,8 +48,3 @@ def create_export_tables
|
|
50
48
|
COMMAND
|
51
49
|
non_query(command)
|
52
50
|
end
|
53
|
-
|
54
|
-
def export_dbc
|
55
|
-
$db_user ||= 'root'
|
56
|
-
@export_dbc ||= DataObjects::Connection.new("mysql://root@localhost/#{$db_name}")
|
57
|
-
end
|
@@ -1,18 +1,16 @@
|
|
1
|
+
require 'do_postgres'
|
2
|
+
|
3
|
+
$do_adapter = 'postgres'
|
4
|
+
$do_host = 'localhost'
|
5
|
+
$do_user = nil
|
6
|
+
$do_pass = nil
|
7
|
+
|
1
8
|
def create_export_db(name)
|
2
|
-
|
3
|
-
$db_adapter = 'postgres'
|
4
|
-
dbc = DataObjects::Connection.new("postgres://localhost/postgres")
|
9
|
+
dbc = do_conn_default
|
5
10
|
dbc.create_command("DROP DATABASE IF EXISTS #{name}").execute_non_query
|
6
11
|
dbc.create_command("CREATE DATABASE #{name}").execute_non_query
|
12
|
+
dbc.release
|
7
13
|
create_export_tables
|
8
|
-
dbc.dispose
|
9
|
-
end
|
10
|
-
|
11
|
-
def drop_export_test_db(name)
|
12
|
-
@export_dbc.dispose if @export_dbc
|
13
|
-
dbc = DataObjects::Connection.new("postgres://localhost/postgres")
|
14
|
-
dbc.create_command("DROP DATABASE IF EXISTS #{name}").execute_non_query
|
15
|
-
dbc.dispose
|
16
14
|
end
|
17
15
|
|
18
16
|
def create_export_tables
|
@@ -49,8 +47,3 @@ def create_export_tables
|
|
49
47
|
COMMAND
|
50
48
|
non_query(command)
|
51
49
|
end
|
52
|
-
|
53
|
-
def export_dbc
|
54
|
-
$db_user ||= ''
|
55
|
-
@export_dbc ||= DataObjects::Connection.new("postgres://localhost/#{$db_name}")
|
56
|
-
end
|
data/spec/spec_helper.rb
CHANGED
@@ -4,89 +4,13 @@ require 'rubygems'
|
|
4
4
|
|
5
5
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
6
6
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
7
|
-
|
8
7
|
require 'squealer'
|
9
|
-
require "spec_helper_dbms_#{ENV['EXPORT_DBMS']||'mysql'}"
|
10
|
-
|
11
|
-
Spec::Runner.configure do |config|
|
12
|
-
config.before(:suite) do
|
13
|
-
$db_name = "test_export_#{object_id}"
|
14
|
-
create_export_db($db_name)
|
15
|
-
create_import_db($db_name)
|
16
|
-
end
|
17
|
-
|
18
|
-
config.after(:suite) do
|
19
|
-
Squealer::Database.instance.send(:dispose_all_connections)
|
20
|
-
drop_export_test_db($db_name)
|
21
|
-
|
22
|
-
drop_mongo
|
23
|
-
end
|
24
|
-
|
25
|
-
config.after(:each) do
|
26
|
-
@export_dbc.dispose if @export_dbc
|
27
|
-
end
|
28
|
-
|
29
|
-
def create_import_db(name)
|
30
|
-
Squealer::Database.instance.import_from('localhost', 27017, name)
|
31
|
-
@mongo = Squealer::Database.instance.import.instance_variable_get('@dbc')
|
32
|
-
drop_mongo
|
33
|
-
seed_import
|
34
|
-
end
|
35
|
-
|
36
|
-
def drop_mongo
|
37
|
-
@mongo.eval('db.dropDatabase()') if @mongo
|
38
|
-
end
|
39
8
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
users = [
|
45
|
-
{ :name => 'Josh Graham', :dob => as_time(Date.parse('01-Jan-1971')), :gender => 'M',
|
46
|
-
:organization_id => hashrocket,
|
47
|
-
:activities => [
|
48
|
-
{ :_id => id, :name => 'Develop squealer', :due_date => as_time(Date.today + 1) },
|
49
|
-
{ :_id => id, :name => 'Organize speakerconf.com', :due_date => as_time(Date.today + 30) },
|
50
|
-
{ :_id => id, :name => 'Hashrocket party', :due_date => as_time(Date.today + 7) }
|
51
|
-
]
|
52
|
-
},
|
53
|
-
{ :name => 'Bernerd Schaefer', :dob => as_time(Date.parse('31-Dec-1985')), :gender => 'M',
|
54
|
-
:organization_id => hashrocket,
|
55
|
-
:activities => [
|
56
|
-
{ :_id => id, :name => 'Retype all of the code Josh wrote in squealer', :due_date => as_time(Date.today + 2) },
|
57
|
-
{ :_id => id, :name => 'Listen to rare Thelonius Monk EP', :due_date => as_time(Date.today) },
|
58
|
-
{ :_id => id, :name => 'Practice karaoke', :due_date => as_time(Date.today + 7) }
|
59
|
-
]
|
60
|
-
},
|
61
|
-
{ :name => 'Your momma', :dob => as_time(Date.parse('15-Jun-1955')), :gender => 'F',
|
62
|
-
:organization_id => zorganization,
|
63
|
-
:activities => [
|
64
|
-
{ :_id => id, :name => 'Cook me some pie', :due_date => as_time(Date.today) },
|
65
|
-
{ :_id => id, :name => 'Make me a sammich', :due_date => as_time(Date.today) }
|
66
|
-
]
|
67
|
-
}
|
68
|
-
]
|
69
|
-
|
70
|
-
users.each { |user| @mongo.collection('users').save user }
|
71
|
-
end
|
72
|
-
|
73
|
-
|
74
|
-
def truncate_export_tables
|
75
|
-
non_query('TRUNCATE TABLE "user"')
|
76
|
-
non_query('TRUNCATE TABLE "activity"')
|
77
|
-
non_query('TRUNCATE TABLE "organization"')
|
78
|
-
end
|
79
|
-
|
80
|
-
def as_time(date)
|
81
|
-
Time.parse(date.to_s)
|
82
|
-
end
|
83
|
-
|
84
|
-
def non_query(text)
|
85
|
-
export_dbc.create_command(text).execute_non_query
|
86
|
-
end
|
9
|
+
def as_time(date)
|
10
|
+
Time.parse(date.to_s)
|
11
|
+
end
|
87
12
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
end
|
13
|
+
def id
|
14
|
+
require 'digest/sha1'
|
15
|
+
(Digest::SHA1.hexdigest rand.to_s)[0,24]
|
92
16
|
end
|
@@ -1,163 +1,4 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'mongo'
|
3
2
|
|
4
3
|
describe Squealer::Database do
|
5
|
-
it "is a singleton" do
|
6
|
-
Squealer::Database.respond_to?(:instance).should be_true
|
7
|
-
end
|
8
|
-
|
9
|
-
describe "import" do
|
10
|
-
let(:databases) { Squealer::Database.instance }
|
11
|
-
|
12
|
-
|
13
|
-
it "takes an import database" do
|
14
|
-
databases.send(:instance_variable_get, '@import_dbc').should be_a_kind_of(Mongo::DB)
|
15
|
-
end
|
16
|
-
|
17
|
-
it "returns a squealer connection object" do
|
18
|
-
databases.import.should be_a_kind_of(Squealer::Database::Connection)
|
19
|
-
end
|
20
|
-
|
21
|
-
it "delegates eval to Mongo" do
|
22
|
-
databases.send(:instance_variable_get, '@import_dbc').eval('db.getName()').should == $db_name
|
23
|
-
databases.import.eval('db.getName()').should == $db_name
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
describe "source" do
|
28
|
-
let(:databases) { Squealer::Database.instance }
|
29
|
-
|
30
|
-
before { databases.import_from('localhost', 27017, $db_name) }
|
31
|
-
|
32
|
-
it "returns a Source" do
|
33
|
-
databases.import.source('foo').should be_a_kind_of(Squealer::Database::Source)
|
34
|
-
end
|
35
|
-
|
36
|
-
describe "Source::cursor" do
|
37
|
-
it "returns a databases cursor" do
|
38
|
-
databases.import.source('foo').cursor.should be_a_kind_of(Mongo::Cursor)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
context "an empty collection" do
|
43
|
-
subject { databases.import.source('foo') }
|
44
|
-
|
45
|
-
it "counts a total of zero" do
|
46
|
-
subject.counts[:total].should == 0
|
47
|
-
end
|
48
|
-
|
49
|
-
it "counts zero imported" do
|
50
|
-
subject.counts[:imported].should == 0
|
51
|
-
end
|
52
|
-
|
53
|
-
it "counts zero exported" do
|
54
|
-
subject.counts[:exported].should == 0
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
context "a collection with two documents" do
|
59
|
-
let(:mongo) { Squealer::Database.instance.import.send(:instance_variable_get, '@dbc') }
|
60
|
-
|
61
|
-
subject do
|
62
|
-
mongo.collection('foo').save({'name' => 'Bar'});
|
63
|
-
mongo.collection('foo').save({'name' => 'Baz'});
|
64
|
-
source = databases.import.source('foo') # activate the counter
|
65
|
-
source.send(:instance_variable_set, :@progress_bar, nil)
|
66
|
-
Squealer::ProgressBar.send(:class_variable_set, :@@progress_bar, nil)
|
67
|
-
source
|
68
|
-
end
|
69
|
-
|
70
|
-
after do
|
71
|
-
mongo.collection('foo').drop
|
72
|
-
end
|
73
|
-
|
74
|
-
it "returns a Source" do
|
75
|
-
subject.should be_a_kind_of(Squealer::Database::Source)
|
76
|
-
end
|
77
|
-
|
78
|
-
it "counts a total of two" do
|
79
|
-
subject.counts[:total].should == 2
|
80
|
-
end
|
81
|
-
|
82
|
-
context "before iterating" do
|
83
|
-
it "counts zero imported" do
|
84
|
-
subject.counts[:imported].should == 0
|
85
|
-
end
|
86
|
-
|
87
|
-
it "counts zero exported" do
|
88
|
-
subject.counts[:exported].should == 0
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
context "after iterating" do
|
93
|
-
before do
|
94
|
-
subject.each {}
|
95
|
-
end
|
96
|
-
|
97
|
-
it "counts two imported" do
|
98
|
-
subject.counts[:imported].should == 2
|
99
|
-
end
|
100
|
-
|
101
|
-
it "counts two exported" do
|
102
|
-
subject.counts[:exported].should == 2
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
context "real squeal" do
|
107
|
-
# before { pending "interactive_view" }
|
108
|
-
it "exports that stuff to SQL" do
|
109
|
-
databases.export_to($db_adapter, 'localhost', $db_user, '', $db_name)
|
110
|
-
databases.import.source("users").each do |user|
|
111
|
-
target(:user) do |target|
|
112
|
-
target.instance_variable_get('@row_id').should == user['_id'].to_s
|
113
|
-
assign(:organization_id)
|
114
|
-
assign(:name)
|
115
|
-
|
116
|
-
#TODO: Update README to highlight that all embedded docs should have an _id
|
117
|
-
# as all Ruby mappers for MongoDB make one. (according to Durran)
|
118
|
-
user.activities.each do |activity|
|
119
|
-
target(:activity) do |target|
|
120
|
-
assign(:user_id)
|
121
|
-
assign(:name)
|
122
|
-
assign(:due_date)
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
end
|
131
|
-
|
132
|
-
end
|
133
|
-
|
134
|
-
describe "export" do
|
135
|
-
let(:databases) { Squealer::Database.instance }
|
136
|
-
|
137
|
-
it "takes an export database" do
|
138
|
-
databases.export_to($db_adapter, 'localhost', $db_user, '', $db_name)
|
139
|
-
databases.instance_variable_get('@export_do').should_not be_nil
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
describe "upsertable?" do
|
144
|
-
subject { Squealer::Database.instance }
|
145
|
-
|
146
|
-
context "mysql connection" do
|
147
|
-
before do
|
148
|
-
subject.export_to('mysql', 'localhost', 'root', '', 'mysql')
|
149
|
-
end
|
150
|
-
|
151
|
-
it { should be_upsertable }
|
152
|
-
end
|
153
|
-
|
154
|
-
context "postgres connection" do
|
155
|
-
before do
|
156
|
-
subject.export_to('postgres', 'localhost', '', '', 'postgres')
|
157
|
-
end
|
158
|
-
|
159
|
-
it { should_not be_upsertable }
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
4
|
end
|
@@ -30,11 +30,6 @@ describe Object do
|
|
30
30
|
target(:test_table) { nil }
|
31
31
|
end
|
32
32
|
|
33
|
-
it "uses the export database connection" do
|
34
|
-
mock_mysql
|
35
|
-
target(:test_table) { nil }
|
36
|
-
end
|
37
|
-
|
38
33
|
end
|
39
34
|
|
40
35
|
describe "#assign" do
|
@@ -112,6 +107,8 @@ describe Object do
|
|
112
107
|
end
|
113
108
|
|
114
109
|
def mock_mysql
|
110
|
+
require 'data_objects'
|
111
|
+
|
115
112
|
my = mock(DataObjects::Connection)
|
116
113
|
comm = mock(DataObjects::Command)
|
117
114
|
Squealer::Database.instance.should_receive(:export).at_least(:once).and_return(my)
|
data/squealer.gemspec
CHANGED
@@ -5,13 +5,13 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{squealer}
|
8
|
-
s.version = "2.2.
|
8
|
+
s.version = "2.2.2"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Josh Graham", "Durran Jordan", "Matt Yoho", "Bernerd Schaefer"]
|
12
|
-
s.date = %q{2010-06-
|
12
|
+
s.date = %q{2010-06-29}
|
13
13
|
s.default_executable = %q{skewer}
|
14
|
-
s.description = %q{
|
14
|
+
s.description = %q{A Ruby DSL for exporting MongoDB to MySQL or PostgreSQL. You don't need to install both, just one. Use EXPORT_DBMS=[mysql|postgres] environment variable to specify the appropriate adapter.}
|
15
15
|
s.email = %q{joshua.graham@grahamis.com}
|
16
16
|
s.executables = ["skewer"]
|
17
17
|
s.extra_rdoc_files = [
|
@@ -36,10 +36,12 @@ Gem::Specification.new do |s|
|
|
36
36
|
"lib/squealer/target.rb",
|
37
37
|
"lib/tasks/jeweler.rake",
|
38
38
|
"spec/integration/export_a_record_spec.rb",
|
39
|
+
"spec/integration/imports_from_mongodb.rb",
|
40
|
+
"spec/integration/spec_helper_dbms.rb",
|
41
|
+
"spec/integration/spec_helper_dbms_mysql.rb",
|
42
|
+
"spec/integration/spec_helper_dbms_postgres.rb",
|
39
43
|
"spec/spec.opts",
|
40
44
|
"spec/spec_helper.rb",
|
41
|
-
"spec/spec_helper_dbms_mysql.rb",
|
42
|
-
"spec/spec_helper_dbms_postgres.rb",
|
43
45
|
"spec/squealer/database_spec.rb",
|
44
46
|
"spec/squealer/hash_spec.rb",
|
45
47
|
"spec/squealer/object_spec.rb",
|
@@ -51,12 +53,14 @@ Gem::Specification.new do |s|
|
|
51
53
|
s.rdoc_options = ["--charset=UTF-8"]
|
52
54
|
s.require_paths = ["lib"]
|
53
55
|
s.rubygems_version = %q{1.3.6}
|
54
|
-
s.summary = %q{
|
56
|
+
s.summary = %q{Export document-oriented database to RDBMS}
|
55
57
|
s.test_files = [
|
56
58
|
"spec/integration/export_a_record_spec.rb",
|
59
|
+
"spec/integration/imports_from_mongodb.rb",
|
60
|
+
"spec/integration/spec_helper_dbms.rb",
|
61
|
+
"spec/integration/spec_helper_dbms_mysql.rb",
|
62
|
+
"spec/integration/spec_helper_dbms_postgres.rb",
|
57
63
|
"spec/spec_helper.rb",
|
58
|
-
"spec/spec_helper_dbms_mysql.rb",
|
59
|
-
"spec/spec_helper_dbms_postgres.rb",
|
60
64
|
"spec/squealer/database_spec.rb",
|
61
65
|
"spec/squealer/hash_spec.rb",
|
62
66
|
"spec/squealer/object_spec.rb",
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 2
|
7
7
|
- 2
|
8
|
-
-
|
9
|
-
version: 2.2.
|
8
|
+
- 2
|
9
|
+
version: 2.2.2
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Josh Graham
|
@@ -17,7 +17,7 @@ autorequire:
|
|
17
17
|
bindir: bin
|
18
18
|
cert_chain: []
|
19
19
|
|
20
|
-
date: 2010-06-
|
20
|
+
date: 2010-06-29 00:00:00 -05:00
|
21
21
|
default_executable: skewer
|
22
22
|
dependencies:
|
23
23
|
- !ruby/object:Gem::Dependency
|
@@ -104,7 +104,7 @@ dependencies:
|
|
104
104
|
version: 1.3.0
|
105
105
|
type: :development
|
106
106
|
version_requirements: *id006
|
107
|
-
description:
|
107
|
+
description: A Ruby DSL for exporting MongoDB to MySQL or PostgreSQL. You don't need to install both, just one. Use EXPORT_DBMS=[mysql|postgres] environment variable to specify the appropriate adapter.
|
108
108
|
email: joshua.graham@grahamis.com
|
109
109
|
executables:
|
110
110
|
- skewer
|
@@ -131,10 +131,12 @@ files:
|
|
131
131
|
- lib/squealer/target.rb
|
132
132
|
- lib/tasks/jeweler.rake
|
133
133
|
- spec/integration/export_a_record_spec.rb
|
134
|
+
- spec/integration/imports_from_mongodb.rb
|
135
|
+
- spec/integration/spec_helper_dbms.rb
|
136
|
+
- spec/integration/spec_helper_dbms_mysql.rb
|
137
|
+
- spec/integration/spec_helper_dbms_postgres.rb
|
134
138
|
- spec/spec.opts
|
135
139
|
- spec/spec_helper.rb
|
136
|
-
- spec/spec_helper_dbms_mysql.rb
|
137
|
-
- spec/spec_helper_dbms_postgres.rb
|
138
140
|
- spec/squealer/database_spec.rb
|
139
141
|
- spec/squealer/hash_spec.rb
|
140
142
|
- spec/squealer/object_spec.rb
|
@@ -170,12 +172,14 @@ rubyforge_project:
|
|
170
172
|
rubygems_version: 1.3.6
|
171
173
|
signing_key:
|
172
174
|
specification_version: 3
|
173
|
-
summary:
|
175
|
+
summary: Export document-oriented database to RDBMS
|
174
176
|
test_files:
|
175
177
|
- spec/integration/export_a_record_spec.rb
|
178
|
+
- spec/integration/imports_from_mongodb.rb
|
179
|
+
- spec/integration/spec_helper_dbms.rb
|
180
|
+
- spec/integration/spec_helper_dbms_mysql.rb
|
181
|
+
- spec/integration/spec_helper_dbms_postgres.rb
|
176
182
|
- spec/spec_helper.rb
|
177
|
-
- spec/spec_helper_dbms_mysql.rb
|
178
|
-
- spec/spec_helper_dbms_postgres.rb
|
179
183
|
- spec/squealer/database_spec.rb
|
180
184
|
- spec/squealer/hash_spec.rb
|
181
185
|
- spec/squealer/object_spec.rb
|