squealer 2.2.1 → 2.2.2
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/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
|