schema_transformer 0.2.0 → 0.3.0
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/README.markdown +19 -0
- data/TODO +0 -1
- data/lib/schema_transformer.rb +3 -1
- data/lib/schema_transformer/analyze.rb +52 -0
- data/lib/schema_transformer/base.rb +2 -247
- data/lib/schema_transformer/cli.rb +12 -12
- data/lib/schema_transformer/transform.rb +252 -0
- data/lib/schema_transformer/version.rb +1 -1
- data/test/fake_app/config/schema_transformations/books.json +1 -1
- data/test/fake_app/log/schema_transformer.log +2143 -57778
- data/test/schema_transformer_test.rb +141 -193
- data/test/test_helper.rb +75 -0
- metadata +7 -5
- data/test/fake_app/config/schema_transformations/users.json +0 -1
@@ -1,209 +1,157 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
require 'rubygems'
|
6
|
-
require 'test/unit'
|
7
|
-
require 'pp'
|
8
|
-
require File.expand_path("../../lib/schema_transformer", __FILE__)
|
9
|
-
|
10
|
-
# open to mock out methods
|
11
|
-
$testing_books = false # im being lazy, should use mocks
|
12
|
-
module SchemaTransformer
|
13
|
-
class Base
|
14
|
-
def ask(msg)
|
15
|
-
nil
|
16
|
-
end
|
17
|
-
def gets(name = nil)
|
18
|
-
case name
|
19
|
-
when :table
|
20
|
-
if $testing_books
|
21
|
-
out = "books"
|
22
|
-
else
|
23
|
-
out = "users"
|
24
|
-
end
|
25
|
-
when :mod
|
26
|
-
if $testing_books
|
27
|
-
out = "ADD COLUMN active tinyint(1) DEFAULT '0'"
|
28
|
-
else
|
29
|
-
out = "ADD COLUMN active tinyint(1) DEFAULT '0',
|
30
|
-
ADD COLUMN title varchar(255) DEFAULT 'Mr',
|
31
|
-
DROP COLUMN about_me"
|
32
|
-
end
|
33
|
-
else
|
34
|
-
raise "gets method: need to mock out #{name}"
|
35
|
-
end
|
36
|
-
out
|
37
|
-
end
|
38
|
-
def help(msg)
|
39
|
-
nil
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def setup_fixtures
|
45
|
-
ActiveRecord::Base.connection.drop_table(:users_st_temp, :force => true) rescue nil
|
46
|
-
ActiveRecord::Base.connection.drop_table(:users_st_trash, :force => true) rescue nil
|
47
|
-
ActiveRecord::Base.connection.create_table :users, :force => true do |table|
|
48
|
-
table.column :name, :string
|
49
|
-
table.column :about_me, :string
|
50
|
-
table.column :updated_at, :datetime
|
51
|
-
table.column :created_at, :datetime
|
52
|
-
end
|
53
|
-
Object.send(:remove_const, "User") rescue nil
|
54
|
-
Object.const_set("User", Class.new(ActiveRecord::Base))
|
55
|
-
35.times do |i|
|
56
|
-
User.create(:name => "name_#{i}")
|
57
|
-
end
|
58
|
-
Object.send(:remove_const, "User") rescue nil
|
59
|
-
|
60
|
-
ActiveRecord::Base.connection.drop_table(:books_st_temp, :force => true) rescue nil
|
61
|
-
ActiveRecord::Base.connection.drop_table(:books_st_trash, :force => true) rescue nil
|
62
|
-
# no timestamp
|
63
|
-
ActiveRecord::Base.connection.create_table :books, :force => true do |table|
|
64
|
-
table.column :title, :string
|
65
|
-
table.column :author, :string
|
66
|
-
end
|
67
|
-
Object.send(:remove_const, "Book") rescue nil
|
68
|
-
Object.const_set("Book", Class.new(ActiveRecord::Base))
|
69
|
-
4.times do |i|
|
70
|
-
Book.create(:title => "title_#{i}")
|
71
|
-
end
|
72
|
-
Object.send(:remove_const, "Book") rescue nil
|
73
|
-
end
|
3
|
+
require File.expand_path("../test_helper", __FILE__)
|
74
4
|
|
75
5
|
class SchemaTransformerTest < Test::Unit::TestCase
|
76
|
-
def count(table)
|
77
|
-
@conn = ActiveRecord::Base.connection
|
78
|
-
res = @conn.execute("SELECT count(*) AS c FROM #{table}")
|
79
|
-
c = res.fetch_row[0].to_i # nil case is okay: [nil][0].to_i => 0
|
80
|
-
end
|
81
|
-
|
82
6
|
def setup
|
83
7
|
@base = File.expand_path("../fake_app", __FILE__)
|
8
|
+
@transformer = SchemaTransformer::Transform.new(@base, :batch_size => 10, :stagger => 0)
|
9
|
+
setup_fixtures
|
10
|
+
setup_stubs
|
11
|
+
|
84
12
|
@transform_file = @base+"/config/schema_transformations/users.json"
|
85
13
|
File.delete(@transform_file) if File.exist?(@transform_file)
|
86
|
-
@transformer = SchemaTransformer::Base.new(@base, :batch_size => 10, :stagger => 0)
|
87
|
-
@conn = ActiveRecord::Base.connection
|
88
|
-
setup_fixtures
|
89
14
|
end
|
90
15
|
|
91
|
-
def
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
16
|
+
# def test_books_no_updated_at_no_data
|
17
|
+
# @transformer = SchemaTransformer::Transform.new(@base, :batch_size => 10, :stagger => 0)
|
18
|
+
# @conn.execute("delete from books")
|
19
|
+
#
|
20
|
+
# @transformer.expects(:gets).with(:table).returns("books")
|
21
|
+
# @transformer.expects(:gets).with(:mod).returns("ADD COLUMN active tinyint(1) DEFAULT '0'")
|
22
|
+
#
|
23
|
+
# @transformer.generate
|
24
|
+
# @transformer.gather_info("books")
|
25
|
+
#
|
26
|
+
# assert_table_exist("books")
|
27
|
+
# assert_table_not_exist("books_st_temp")
|
28
|
+
# @transformer.create
|
29
|
+
# assert_table_exist("books_st_temp")
|
30
|
+
#
|
31
|
+
# @transformer.final_sync
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# def test_books_no_updated_at_with_data
|
35
|
+
# @transformer = SchemaTransformer::Transform.new(@base, :batch_size => 10, :stagger => 0)
|
36
|
+
# @transformer.expects(:gets).with(:table).returns("books")
|
37
|
+
# @transformer.expects(:gets).with(:mod).returns("ADD COLUMN active tinyint(1) DEFAULT '0'")
|
38
|
+
#
|
39
|
+
# @transformer.generate
|
40
|
+
# @transformer.gather_info("books")
|
41
|
+
#
|
42
|
+
# assert_table_exist("books")
|
43
|
+
# assert_table_not_exist("books_st_temp")
|
44
|
+
# @transformer.create
|
45
|
+
# assert_table_exist("books_st_temp")
|
46
|
+
#
|
47
|
+
# @transformer.final_sync
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# def test_find_in_batches
|
51
|
+
# i = 0
|
52
|
+
# bounds = [[8, 17], [18, 27], [28,35]]
|
53
|
+
# @transformer.find_in_batches("users", :start => 8, :batch_size => 10) do |batch|
|
54
|
+
# # puts "batch #{batch.inspect}"
|
55
|
+
# lower = batch.first
|
56
|
+
# upper = batch.last
|
57
|
+
# assert_equal bounds[i][0], lower
|
58
|
+
# assert_equal bounds[i][1], upper
|
59
|
+
# # puts("syncing over records #{lower} to #{upper}...")
|
60
|
+
# i += 1
|
61
|
+
# end
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# def test_run_sync_black_box
|
65
|
+
# @transformer.generate
|
66
|
+
# c1 = count("users")
|
67
|
+
# SchemaTransformer::Transform.run(:base => @base, :action => ["sync", "users"])
|
68
|
+
# c2 = count("users_st_temp")
|
69
|
+
# assert_equal c1, c2
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# def test_run_sync_black_box_repeatedly
|
73
|
+
# @transformer.generate
|
74
|
+
# c1 = count("users")
|
75
|
+
# # first run
|
76
|
+
# SchemaTransformer::Transform.run(:base => @base, :action => ["sync", "users"])
|
77
|
+
# assert_equal c1, count("users_st_temp")
|
78
|
+
# @conn.execute("delete from users_st_temp order by id desc limit 10")
|
79
|
+
# assert_equal c1, count("users_st_temp") + 10
|
80
|
+
# # second run
|
81
|
+
# SchemaTransformer::Transform.run(:base => @base, :action => ["sync", "users"])
|
82
|
+
# assert_equal c1, count("users_st_temp")
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# def test_run_switch_black_box
|
86
|
+
# @transformer.generate
|
87
|
+
# c1 = count("users")
|
88
|
+
# SchemaTransformer::Transform.run(:base => @base, :action => ["sync", "users"])
|
89
|
+
# c2 = count("users_st_temp")
|
90
|
+
# assert_equal c1, c2
|
91
|
+
# @conn.execute("delete from users_st_temp order by id desc limit 10")
|
92
|
+
# assert_equal c1, count("users_st_temp") + 10
|
93
|
+
#
|
94
|
+
# # This is what Im testing
|
95
|
+
# col1 = User.columns.size
|
96
|
+
# SchemaTransformer::Transform.run(:base => @base, :action => ["switch", "users"])
|
97
|
+
# User.reset_column_information
|
98
|
+
# assert_equal col1 + 1, User.columns.size
|
99
|
+
# assert_equal c1, count("users") # this is the new table
|
100
|
+
# end
|
101
|
+
#
|
102
|
+
# def test_run_tranformations_white_box
|
103
|
+
# @transformer.generate
|
104
|
+
# @transformer.gather_info("users")
|
105
|
+
#
|
106
|
+
# assert_table_exist("users")
|
107
|
+
# assert_table_not_exist("users_st_temp")
|
108
|
+
# @transformer.create
|
109
|
+
# assert_table_exist("users_st_temp")
|
110
|
+
#
|
111
|
+
# assert_equal 0, UsersStTemp.count
|
112
|
+
# @transformer.sync
|
113
|
+
# assert_equal User.count, UsersStTemp.count
|
114
|
+
#
|
115
|
+
# assert_table_exist("users")
|
116
|
+
# @transformer.switch
|
117
|
+
# assert_table_exist("users_st_trash")
|
118
|
+
# assert_table_not_exist("users_st_temp")
|
119
|
+
#
|
120
|
+
# @transformer.cleanup
|
121
|
+
# assert_table_exist("users")
|
122
|
+
# assert_table_not_exist("users_st_trash")
|
123
|
+
# assert_table_not_exist("users_st_temp")
|
124
|
+
# end
|
125
|
+
#
|
126
|
+
# def test_generate_transformations
|
127
|
+
# assert !File.exist?(@transform_file)
|
128
|
+
# @transformer.generate
|
129
|
+
# assert File.exist?(@transform_file)
|
130
|
+
# data = JSON.parse(IO.read(@transform_file))
|
131
|
+
# assert_equal "users", data["table"]
|
132
|
+
# assert_match /ADD COLUMN/, data["mod"]
|
133
|
+
#
|
134
|
+
# @transformer.gather_info("users")
|
135
|
+
# assert_equal "users", @transformer.instance_variable_get(:@table)
|
136
|
+
# assert_match /ADD COLUMN/, @transformer.instance_variable_get(:@mod)
|
137
|
+
# end
|
96
138
|
|
97
|
-
|
98
|
-
|
99
|
-
@
|
100
|
-
assert
|
101
|
-
|
102
|
-
@
|
103
|
-
|
139
|
+
def test_analyze_timestamps
|
140
|
+
@analyze = SchemaTransformer::Analyze.new(@base)
|
141
|
+
tables = @analyze.timestamps
|
142
|
+
assert tables.include?("users") # has updated_at timestamp
|
143
|
+
assert !tables.include?("books") # does not have updated_at timestamp
|
144
|
+
tables = @analyze.no_timestamps
|
145
|
+
assert !tables.include?("users") # has updated_at timestamp
|
146
|
+
assert tables.include?("books") # does not have updated_at timestamp
|
104
147
|
end
|
105
148
|
|
106
|
-
def
|
107
|
-
|
108
|
-
@
|
109
|
-
|
110
|
-
|
111
|
-
assert
|
112
|
-
assert !@conn.tables.include?("books_st_temp")
|
113
|
-
@transformer.create
|
114
|
-
assert @conn.tables.include?("books_st_temp")
|
115
|
-
|
116
|
-
@transformer.final_sync
|
117
|
-
$testing_books = false
|
118
|
-
end
|
119
|
-
|
120
|
-
def test_find_in_batches
|
121
|
-
i = 0
|
122
|
-
bounds = [[8, 17], [18, 27], [28,35]]
|
123
|
-
@transformer.find_in_batches("users", :start => 8, :batch_size => 10) do |batch|
|
124
|
-
# puts "batch #{batch.inspect}"
|
125
|
-
lower = batch.first
|
126
|
-
upper = batch.last
|
127
|
-
assert_equal bounds[i][0], lower
|
128
|
-
assert_equal bounds[i][1], upper
|
129
|
-
# puts("syncing over records #{lower} to #{upper}...")
|
130
|
-
i += 1
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
def test_run_sync_black_box
|
135
|
-
@transformer.generate
|
136
|
-
c1 = count("users")
|
137
|
-
SchemaTransformer::Base.run(:base => @base, :action => ["sync", "users"])
|
138
|
-
c2 = count("users_st_temp")
|
139
|
-
assert_equal c1, c2
|
140
|
-
end
|
141
|
-
|
142
|
-
def test_run_sync_black_box_repeatedly
|
143
|
-
@transformer.generate
|
144
|
-
c1 = count("users")
|
145
|
-
# first run
|
146
|
-
SchemaTransformer::Base.run(:base => @base, :action => ["sync", "users"])
|
147
|
-
assert_equal c1, count("users_st_temp")
|
148
|
-
@conn.execute("delete from users_st_temp order by id desc limit 10")
|
149
|
-
assert_equal c1, count("users_st_temp") + 10
|
150
|
-
# second run
|
151
|
-
SchemaTransformer::Base.run(:base => @base, :action => ["sync", "users"])
|
152
|
-
assert_equal c1, count("users_st_temp")
|
153
|
-
end
|
154
|
-
|
155
|
-
def test_run_switch_black_box
|
156
|
-
@transformer.generate
|
157
|
-
c1 = count("users")
|
158
|
-
SchemaTransformer::Base.run(:base => @base, :action => ["sync", "users"])
|
159
|
-
c2 = count("users_st_temp")
|
160
|
-
assert_equal c1, c2
|
161
|
-
@conn.execute("delete from users_st_temp order by id desc limit 10")
|
162
|
-
assert_equal c1, count("users_st_temp") + 10
|
163
|
-
|
164
|
-
# This is what Im testing
|
165
|
-
col1 = User.columns.size
|
166
|
-
SchemaTransformer::Base.run(:base => @base, :action => ["switch", "users"])
|
167
|
-
User.reset_column_information
|
168
|
-
assert_equal col1 + 1, User.columns.size
|
169
|
-
assert_equal c1, count("users") # this is the new table
|
170
|
-
end
|
171
|
-
|
172
|
-
def test_run_tranformations_white_box
|
173
|
-
@transformer.generate
|
174
|
-
@transformer.gather_info("users")
|
175
|
-
|
176
|
-
assert @conn.tables.include?("users")
|
177
|
-
assert !@conn.tables.include?("users_st_temp")
|
178
|
-
@transformer.create
|
179
|
-
assert @conn.tables.include?("users_st_temp")
|
180
|
-
|
181
|
-
assert_equal 0, UsersStTemp.count
|
182
|
-
@transformer.sync
|
183
|
-
assert_equal User.count, UsersStTemp.count
|
184
|
-
|
185
|
-
assert @conn.tables.include?("users")
|
186
|
-
@transformer.switch
|
187
|
-
assert @conn.tables.include?("users_st_trash")
|
188
|
-
assert !@conn.tables.include?("users_st_temp")
|
189
|
-
|
190
|
-
@transformer.cleanup
|
191
|
-
assert @conn.tables.include?("users")
|
192
|
-
assert !@conn.tables.include?("users_st_trash")
|
193
|
-
assert !@conn.tables.include?("users_st_temp")
|
194
|
-
end
|
195
|
-
|
196
|
-
def test_generate_transformations
|
197
|
-
assert !File.exist?(@transform_file)
|
198
|
-
@transformer.generate
|
199
|
-
assert File.exist?(@transform_file)
|
200
|
-
data = JSON.parse(IO.read(@transform_file))
|
201
|
-
assert_equal "users", data["table"]
|
202
|
-
assert_match /ADD COLUMN/, data["mod"]
|
203
|
-
|
204
|
-
@transformer.gather_info("users")
|
205
|
-
assert_equal "users", @transformer.instance_variable_get(:@table)
|
206
|
-
assert_match /ADD COLUMN/, @transformer.instance_variable_get(:@mod)
|
149
|
+
def test_analyze_indexes
|
150
|
+
@analyze = SchemaTransformer::Analyze.new(@base)
|
151
|
+
tables = @analyze.indexes
|
152
|
+
assert_equal [], tables
|
153
|
+
tables = @analyze.no_indexes
|
154
|
+
assert tables.include?("users") # has updated_at timestamp, but index
|
207
155
|
end
|
208
156
|
end
|
209
157
|
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
ENV['RAILS_ENV'] = 'test'
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'test/unit'
|
7
|
+
require 'mocha'
|
8
|
+
require 'pp'
|
9
|
+
require File.expand_path("../../lib/schema_transformer", __FILE__)
|
10
|
+
|
11
|
+
module TestExtensions
|
12
|
+
def setup_fixtures
|
13
|
+
@conn = ActiveRecord::Base.connection # shortcut to connection
|
14
|
+
|
15
|
+
# cleanup in case tests fail half way
|
16
|
+
@conn.drop_table(:users_st_temp, :force => true) rescue nil
|
17
|
+
@conn.drop_table(:users_st_trash, :force => true) rescue nil
|
18
|
+
|
19
|
+
@conn.create_table :users, :force => true do |table|
|
20
|
+
table.column :name, :string
|
21
|
+
table.column :about_me, :string
|
22
|
+
table.column :updated_at, :datetime
|
23
|
+
table.column :created_at, :datetime
|
24
|
+
end
|
25
|
+
Object.send(:remove_const, "User") rescue nil
|
26
|
+
Object.const_set("User", Class.new(ActiveRecord::Base))
|
27
|
+
35.times do |i|
|
28
|
+
User.create(:name => "name_#{i}")
|
29
|
+
end
|
30
|
+
Object.send(:remove_const, "User") rescue nil
|
31
|
+
|
32
|
+
# cleanup in case tests fail half way
|
33
|
+
@conn.drop_table(:books_st_temp, :force => true) rescue nil
|
34
|
+
@conn.drop_table(:books_st_trash, :force => true) rescue nil
|
35
|
+
# no timestamp
|
36
|
+
@conn.create_table :books, :force => true do |table|
|
37
|
+
table.column :title, :string
|
38
|
+
table.column :author, :string
|
39
|
+
end
|
40
|
+
Object.send(:remove_const, "Book") rescue nil
|
41
|
+
Object.const_set("Book", Class.new(ActiveRecord::Base))
|
42
|
+
4.times do |i|
|
43
|
+
Book.create(:title => "title_#{i}")
|
44
|
+
end
|
45
|
+
Object.send(:remove_const, "Book") rescue nil
|
46
|
+
end
|
47
|
+
|
48
|
+
def setup_stubs
|
49
|
+
SchemaTransformer::Transform.any_instance.stubs(:gets).with(:table).returns("users")
|
50
|
+
SchemaTransformer::Transform.any_instance.stubs(:gets).with(:mod).returns(
|
51
|
+
"ADD COLUMN active tinyint(1) DEFAULT '0',
|
52
|
+
ADD COLUMN title varchar(255) DEFAULT 'Mr',
|
53
|
+
DROP COLUMN about_me"
|
54
|
+
)
|
55
|
+
SchemaTransformer::Transform.any_instance.stubs(:ask).returns(nil)
|
56
|
+
SchemaTransformer::Transform.any_instance.stubs(:help).returns(nil)
|
57
|
+
end
|
58
|
+
|
59
|
+
def count(table)
|
60
|
+
res = @conn.execute("SELECT count(*) AS c FROM #{table}")
|
61
|
+
c = res.fetch_row[0].to_i # nil case is okay: [nil][0].to_i => 0
|
62
|
+
end
|
63
|
+
|
64
|
+
def assert_table_exist(table)
|
65
|
+
assert @conn.tables.include?(table)
|
66
|
+
end
|
67
|
+
|
68
|
+
def assert_table_not_exist(table)
|
69
|
+
assert !@conn.tables.include?(table)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class Test::Unit::TestCase
|
74
|
+
include TestExtensions
|
75
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: schema_transformer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 3
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.3.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Tung Nguyen
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-10-
|
18
|
+
date: 2010-10-24 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|
@@ -30,18 +30,20 @@ extra_rdoc_files:
|
|
30
30
|
files:
|
31
31
|
- bin/schema_transformer
|
32
32
|
- gemspec.rb
|
33
|
+
- lib/schema_transformer/analyze.rb
|
33
34
|
- lib/schema_transformer/base.rb
|
34
35
|
- lib/schema_transformer/cli.rb
|
35
36
|
- lib/schema_transformer/help.rb
|
37
|
+
- lib/schema_transformer/transform.rb
|
36
38
|
- lib/schema_transformer/version.rb
|
37
39
|
- lib/schema_transformer.rb
|
38
40
|
- Rakefile
|
39
41
|
- README.markdown
|
40
42
|
- test/fake_app/config/database.yml
|
41
43
|
- test/fake_app/config/schema_transformations/books.json
|
42
|
-
- test/fake_app/config/schema_transformations/users.json
|
43
44
|
- test/fake_app/log/schema_transformer.log
|
44
45
|
- test/schema_transformer_test.rb
|
46
|
+
- test/test_helper.rb
|
45
47
|
- TODO
|
46
48
|
has_rdoc: true
|
47
49
|
homepage: http://github.com/tongueroo/schema_transformer
|