squealer 1.2.0 → 2.1.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/.rvmrc +5 -2
- data/README.md +14 -0
- data/Rakefile +6 -1
- data/VERSION +1 -1
- data/bin/skewer +165 -0
- data/lib/.example_squeal.rb.swp +0 -0
- data/lib/example_squeal.rb +13 -5
- data/lib/squealer/database.rb +47 -1
- data/lib/squealer/progress_bar.rb +112 -0
- data/lib/squealer/target.rb +30 -8
- data/lib/squealer.rb +1 -0
- data/spec/spec_helper.rb +106 -0
- data/spec/squealer/database_spec.rb +116 -24
- data/spec/squealer/progress_bar_spec.rb +191 -0
- data/spec/squealer/target_spec.rb +29 -5
- data/squealer.gemspec +15 -5
- metadata +28 -16
data/.rvmrc
CHANGED
data/README.md
CHANGED
@@ -10,6 +10,20 @@ To run standalone, simply make your data squeal thusly:
|
|
10
10
|
where the squeal script includes a `require 'squealer'`.
|
11
11
|
|
12
12
|
## Release Notes
|
13
|
+
### v2.1
|
14
|
+
* Ruby 1.8.6 back-compatibility added. Using `eval "", binding, __FILE__, __LINE__` instead of `binding.eval`
|
15
|
+
* Target SQL script using backtick-quoted (mySQL) identifiers to avoid column-name / keyword conflict
|
16
|
+
* Automatically typecast Ruby `Boolean` (to integer), `Symbol` (to string), `Array` (to comma-seperated string)
|
17
|
+
* Improved handling and reporting of Target SQL errors
|
18
|
+
* Schaefer's Special "skewer" Script to reflect on Mongoid models and generate an initial squeal script and SQL schema DDL script. This tool is intended to build the _initial_ scripts only. It is extremely useful to get you going, but do think about the needs of the consumer of the export database, and adjust the scripts to suit. [How do you make something squeal? You skewer it!]
|
19
|
+
|
20
|
+
### v2.0
|
21
|
+
* `Object#import` now wraps a MongoDB cursor to provide counters and timings. Only `each` is supported for now, however `source` takes optional conditions.
|
22
|
+
* Progress bar and summary.
|
23
|
+
|
24
|
+
### v1.2.1
|
25
|
+
* `Object#import` syntax has changed. Now `import.source(collection).each` rather than `import.collection(collection).find({}).each`. `source` returns a MongoDB cursor like `find` does. See lib/example_squeal.rb for options.
|
26
|
+
|
13
27
|
### v1.2
|
14
28
|
* `Object#target` verifies there is a variable in scope with the same name as the `table_name` being targetted, it must be a `Hash` and must have an `_id` key
|
15
29
|
* Block to `Object#assign` not required, infers value from source scope
|
data/Rakefile
CHANGED
@@ -13,9 +13,14 @@ begin
|
|
13
13
|
gemspec.description = "Exports mongodb to mysql. More later."
|
14
14
|
gemspec.email = "joshua.graham@grahamis.com"
|
15
15
|
gemspec.homepage = "http://github.com/delitescere/squealer/"
|
16
|
-
gemspec.authors = ["Josh Graham", "Durran Jordan"]
|
16
|
+
gemspec.authors = ["Josh Graham", "Durran Jordan", "Matt Yoho", "Bernerd Schaefer"]
|
17
|
+
|
18
|
+
gemspec.default_executable = "skewer"
|
19
|
+
gemspec.executables = ["skewer"]
|
20
|
+
|
17
21
|
gemspec.add_dependency('mysql', '>= 2.8.1')
|
18
22
|
gemspec.add_dependency('mongo', '>= 0.18.3')
|
23
|
+
gemspec.add_dependency('bson_ext', '>= 1.0.1')
|
19
24
|
end
|
20
25
|
Jeweler::GemcutterTasks.new
|
21
26
|
rescue LoadError
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
2.1.0
|
data/bin/skewer
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
model = ARGV[0]
|
4
|
+
unless model
|
5
|
+
$stderr.puts "usage: skewer ModelName"
|
6
|
+
exit 1
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'config/boot'
|
10
|
+
require 'config/environment'
|
11
|
+
|
12
|
+
model = Object.const_get(model)
|
13
|
+
|
14
|
+
unless defined?(Mongoid) and Mongoid::Document > model
|
15
|
+
$stderr.puts "#{model} must be a Mongoid::Document"
|
16
|
+
exit 1
|
17
|
+
end
|
18
|
+
|
19
|
+
write_schema = true
|
20
|
+
write_squeal = true
|
21
|
+
|
22
|
+
schema_filename = "#{model.name.underscore}_schema.sql"
|
23
|
+
squeal_filename = "#{model.name.underscore}_squeal.rb"
|
24
|
+
|
25
|
+
schema_exists = File.exists?(schema_filename)
|
26
|
+
squeal_exists = File.exists?(squeal_filename)
|
27
|
+
|
28
|
+
if schema_exists || squeal_exists
|
29
|
+
$stdout.print "#{schema_filename} already exists, overwrite? [Y/n] "
|
30
|
+
write_schema = $stdin.gets.chomp != "n"
|
31
|
+
|
32
|
+
$stdout.print "#{squeal_filename} already exists, overwrite? [Y/n] "
|
33
|
+
write_squeal = $stdin.gets.chomp != "n"
|
34
|
+
end
|
35
|
+
|
36
|
+
fields = model.fields.values.sort_by(&:name)
|
37
|
+
associations = model.associations
|
38
|
+
|
39
|
+
# SQL #
|
40
|
+
def create_table(model, parent = nil)
|
41
|
+
fields = model.fields.values.sort_by(&:name)
|
42
|
+
columns = []
|
43
|
+
columns << "`#{parent.name.underscore}_id` CHAR(24)" if parent
|
44
|
+
|
45
|
+
fields.each do |field|
|
46
|
+
mysql_type = case field.type.name
|
47
|
+
when "Boolean"
|
48
|
+
"BOOLEAN"
|
49
|
+
when "Time"
|
50
|
+
"TIMESTAMP NULL DEFAULT NULL"
|
51
|
+
when "Date"
|
52
|
+
"DATE"
|
53
|
+
when "Float"
|
54
|
+
"FLOAT"
|
55
|
+
when "Integer"
|
56
|
+
"INT"
|
57
|
+
else
|
58
|
+
"TEXT"
|
59
|
+
end
|
60
|
+
columns << "`#{field.name[0..63]}` #{mysql_type}"
|
61
|
+
end
|
62
|
+
|
63
|
+
table_name = if parent
|
64
|
+
"#{parent.name.underscore}_#{model.name.underscore}"
|
65
|
+
else
|
66
|
+
"#{model.name.underscore}"
|
67
|
+
end
|
68
|
+
|
69
|
+
table_sql = []
|
70
|
+
table_sql << "DROP TABLE IF EXISTS `#{table_name}`;"
|
71
|
+
table_sql << "CREATE TABLE `#{table_name}` (`id` CHAR(24) PRIMARY KEY);"
|
72
|
+
columns.each do |column|
|
73
|
+
table_sql << "ALTER TABLE `#{table_name}` ADD COLUMN #{column};"
|
74
|
+
end
|
75
|
+
|
76
|
+
table_sql.join("\n") + "\n"
|
77
|
+
end
|
78
|
+
|
79
|
+
# SQUEAL #
|
80
|
+
def create_squeal(model, indent=false, parents = [])
|
81
|
+
fields = model.fields.values.sort_by(&:name)
|
82
|
+
|
83
|
+
parent = parents.last
|
84
|
+
table_name = if parent
|
85
|
+
"#{parent.name.underscore}_#{model.name.underscore}"
|
86
|
+
else
|
87
|
+
"#{model.name.underscore}"
|
88
|
+
end
|
89
|
+
|
90
|
+
squeal = if parent
|
91
|
+
"#{parent.name.underscore}.#{model.name.tableize}.each do |#{model.name.underscore}|\n" \
|
92
|
+
" #{table_name} = #{model.name.underscore}"
|
93
|
+
else
|
94
|
+
"import.source(\"#{model.name.tableize}\").each do |#{model.name.underscore}|"
|
95
|
+
end
|
96
|
+
|
97
|
+
schemas = [create_table(model, parent)]
|
98
|
+
|
99
|
+
squeal << <<-EOS
|
100
|
+
|
101
|
+
target(:#{table_name}) do
|
102
|
+
EOS
|
103
|
+
if parent
|
104
|
+
squeal << <<-EOS
|
105
|
+
assign(:#{parent.name.underscore}_id)
|
106
|
+
EOS
|
107
|
+
end
|
108
|
+
|
109
|
+
fields.each do |field|
|
110
|
+
field_name = field.name
|
111
|
+
case
|
112
|
+
when %w(type target).include?(field.name)
|
113
|
+
value = " { #{table_name}['#{field.name}'] }"
|
114
|
+
when field.name.size > 64
|
115
|
+
field_name = field.name[0..63]
|
116
|
+
value = " { #{table_name}.#{field.name} }"
|
117
|
+
when field.name =~ /(.*)_id$/
|
118
|
+
value = " { #{table_name}.#{$1} }"
|
119
|
+
end
|
120
|
+
squeal << <<-EOS
|
121
|
+
assign(:#{field_name})#{value}
|
122
|
+
EOS
|
123
|
+
end
|
124
|
+
|
125
|
+
model.associations.values.each do |association|
|
126
|
+
begin
|
127
|
+
if [Mongoid::Associations::HasMany, Mongoid::Associations::HasOne].include?(association.association)
|
128
|
+
unless parents.include?(association.klass)
|
129
|
+
ruby, sql = create_squeal(association.klass, true, parents | [model])
|
130
|
+
squeal << "\n" + ruby + "\n"
|
131
|
+
schemas |= sql
|
132
|
+
end
|
133
|
+
end
|
134
|
+
rescue NameError
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
squeal << <<-EOS
|
139
|
+
end
|
140
|
+
end # #{table_name}
|
141
|
+
EOS
|
142
|
+
squeal.gsub!(/^/, " ") if indent
|
143
|
+
return squeal, schemas
|
144
|
+
end
|
145
|
+
|
146
|
+
squeal, schema = create_squeal(model)
|
147
|
+
|
148
|
+
if write_squeal
|
149
|
+
File.open(squeal_filename, "w") do |file|
|
150
|
+
file.write <<-EOS
|
151
|
+
require 'squealer'
|
152
|
+
|
153
|
+
import('localhost', 27017, 'development') # <--- Change this as needed
|
154
|
+
export('localhost', 'root', '', 'export') # <--- Change this as needed
|
155
|
+
|
156
|
+
EOS
|
157
|
+
file.write(squeal)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
if write_schema
|
162
|
+
File.open(schema_filename, "w") do |file|
|
163
|
+
file.write(schema.join("\n"))
|
164
|
+
end
|
165
|
+
end
|
Binary file
|
data/lib/example_squeal.rb
CHANGED
@@ -7,7 +7,16 @@ import('localhost', 27017, 'development')
|
|
7
7
|
export('localhost', 'root', '', 'reporting_export')
|
8
8
|
|
9
9
|
# Here we extract, transform and load all documents in a collection...
|
10
|
-
|
10
|
+
|
11
|
+
#
|
12
|
+
# You don't want to use a find() on the MongoDB collection...
|
13
|
+
# import.source("users") { |users| users.find_one() }.each do |user|
|
14
|
+
#
|
15
|
+
# Also accepts optional conditions...
|
16
|
+
# import.source("users", "{disabled: 'false'}").each do |user|
|
17
|
+
#
|
18
|
+
# Defaults to find all...
|
19
|
+
import.source('users').each do |user|
|
11
20
|
# Insert or Update on table 'user' where 'id' is the column name of the primary key.
|
12
21
|
#
|
13
22
|
# The primary key value is taken from the '_id' field of the source document,
|
@@ -54,8 +63,7 @@ import.collection("users").find({}).each do |user|
|
|
54
63
|
#
|
55
64
|
# You can use an empty block to infer the value from the '_id' field
|
56
65
|
# of a parent document where the name of the parent collection matches
|
57
|
-
# a variable that is in scope
|
58
|
-
#
|
66
|
+
# a variable that is in scope...
|
59
67
|
assign(:user_id) #or# assign(:user_id) { user._id }
|
60
68
|
assign(:name) #or# assign(:name) { activity.name }
|
61
69
|
assign(:due_date) #or# assign(:due_date) { activity.due_date }
|
@@ -73,8 +81,8 @@ import.collection("users").find({}).each do |user|
|
|
73
81
|
end #collection("users")
|
74
82
|
|
75
83
|
# Here we use a procedural "join" on related collections to update a target...
|
76
|
-
import.
|
77
|
-
import.
|
84
|
+
import.source('organizations', {'disabled_date' => {'exists' => true}}).each do |organization|
|
85
|
+
import.source('users', {'organization_id' => organization.id}) do |user|
|
78
86
|
target(:user) do
|
79
87
|
#
|
80
88
|
# Source boolean values are converted to integer (0 or 1)...
|
data/lib/squealer/database.rb
CHANGED
@@ -8,6 +8,7 @@ module Squealer
|
|
8
8
|
|
9
9
|
def import_from(host, port, name)
|
10
10
|
@import_dbc = Mongo::Connection.new(host, port, :slave_ok => true).db(name)
|
11
|
+
@import_connection = Connection.new(@import_dbc)
|
11
12
|
end
|
12
13
|
|
13
14
|
def export_to(host, username, password, name)
|
@@ -15,12 +16,57 @@ module Squealer
|
|
15
16
|
end
|
16
17
|
|
17
18
|
def import
|
18
|
-
@
|
19
|
+
@import_connection
|
19
20
|
end
|
20
21
|
|
21
22
|
def export
|
22
23
|
@export_dbc
|
23
24
|
end
|
24
25
|
|
26
|
+
class Connection
|
27
|
+
attr_reader :collections
|
28
|
+
|
29
|
+
def initialize(dbc)
|
30
|
+
@dbc = dbc
|
31
|
+
@collections = {}
|
32
|
+
end
|
33
|
+
|
34
|
+
def source(collection, conditions = {}, &block)
|
35
|
+
source = Source.new(@dbc, collection)
|
36
|
+
@collections[collection] = source
|
37
|
+
source.source(conditions, &block)
|
38
|
+
end
|
39
|
+
|
40
|
+
def eval(string)
|
41
|
+
@dbc.eval(string)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Source
|
46
|
+
attr_reader :counts, :cursor
|
47
|
+
|
48
|
+
def initialize(dbc, collection)
|
49
|
+
@counts = {:exported => 0, :imported => 0}
|
50
|
+
@collection = dbc.collection(collection)
|
51
|
+
end
|
52
|
+
|
53
|
+
def source(conditions)
|
54
|
+
@cursor = block_given? ? yield(@collection) : @collection.find(conditions)
|
55
|
+
@counts[:total] = cursor.count
|
56
|
+
@progress_bar = Squealer::ProgressBar.new(cursor.count)
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def each
|
61
|
+
@progress_bar.start if @progress_bar
|
62
|
+
@cursor.each do |row|
|
63
|
+
@counts[:imported] += 1
|
64
|
+
yield row
|
65
|
+
@progress_bar.tick if @progress_bar
|
66
|
+
@counts[:exported] += 1
|
67
|
+
end
|
68
|
+
@progress_bar.finish if @progress_bar
|
69
|
+
end
|
70
|
+
end
|
25
71
|
end
|
26
72
|
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module Squealer
|
2
|
+
class ProgressBar
|
3
|
+
|
4
|
+
@@progress_bar = nil
|
5
|
+
|
6
|
+
def self.new(*args)
|
7
|
+
if @@progress_bar
|
8
|
+
nil
|
9
|
+
else
|
10
|
+
@@progress_bar = super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(total)
|
15
|
+
@total = total
|
16
|
+
@ticks = 0
|
17
|
+
|
18
|
+
@progress_bar_width = 50
|
19
|
+
@count_width = total.to_s.size
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
def start
|
24
|
+
@start_time = Time.new
|
25
|
+
@emitter = start_emitter if total > 0
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def finish
|
30
|
+
@end_time = Time.new
|
31
|
+
@emitter.wakeup.join if @emitter
|
32
|
+
@@progress_bar = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def tick
|
36
|
+
@ticks += 1
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def start_emitter
|
42
|
+
Thread.new do
|
43
|
+
emit
|
44
|
+
sleep(1) and emit until done?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def emit
|
49
|
+
format = "\r[%-#{progress_bar_width}s] %#{count_width}i/%i (%i%%)"
|
50
|
+
console.print format % [progress_markers, ticks, total, percentage]
|
51
|
+
emit_final if done?
|
52
|
+
end
|
53
|
+
|
54
|
+
def emit_final
|
55
|
+
console.puts
|
56
|
+
|
57
|
+
console.puts "Start: #{start_time}"
|
58
|
+
console.puts "End: #{end_time}"
|
59
|
+
console.puts "Duration: #{duration}"
|
60
|
+
end
|
61
|
+
|
62
|
+
def done?
|
63
|
+
ticks >= total || end_time
|
64
|
+
end
|
65
|
+
|
66
|
+
def start_time
|
67
|
+
@start_time
|
68
|
+
end
|
69
|
+
|
70
|
+
def end_time
|
71
|
+
@end_time
|
72
|
+
end
|
73
|
+
|
74
|
+
def ticks
|
75
|
+
@ticks
|
76
|
+
end
|
77
|
+
|
78
|
+
def total
|
79
|
+
@total
|
80
|
+
end
|
81
|
+
|
82
|
+
def percentage
|
83
|
+
((ticks.to_f / total) * 100).floor
|
84
|
+
end
|
85
|
+
|
86
|
+
def progress_markers
|
87
|
+
"=" * ((ticks.to_f / total) * progress_bar_width).floor
|
88
|
+
end
|
89
|
+
|
90
|
+
def console
|
91
|
+
$stderr
|
92
|
+
end
|
93
|
+
|
94
|
+
def progress_bar_width
|
95
|
+
@progress_bar_width
|
96
|
+
end
|
97
|
+
|
98
|
+
def count_width
|
99
|
+
@count_width
|
100
|
+
end
|
101
|
+
|
102
|
+
def total_time
|
103
|
+
@end_time - @start_time
|
104
|
+
end
|
105
|
+
|
106
|
+
def duration
|
107
|
+
duration = Time.at(total_time).utc
|
108
|
+
duration.strftime("%H:%M:%S.#{duration.usec}")
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
data/lib/squealer/target.rb
CHANGED
@@ -55,11 +55,11 @@ module Squealer
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def infer_row_id
|
58
|
-
|
58
|
+
eval "#{@table_name}._id", @binding, __FILE__, __LINE__
|
59
59
|
end
|
60
60
|
|
61
61
|
def verify_table_name_in_scope
|
62
|
-
table =
|
62
|
+
table = eval "#{@table_name}", @binding, __FILE__, __LINE__
|
63
63
|
raise ArgumentError, "The variable '#{@table_name}' is not a hashmap" unless table.is_a? Hash
|
64
64
|
raise ArgumentError, "The hashmap '#{@table_name}' must have an '_id' key" unless table.has_key? '_id'
|
65
65
|
rescue NameError
|
@@ -68,12 +68,12 @@ module Squealer
|
|
68
68
|
|
69
69
|
|
70
70
|
def infer_value(column_name, binding)
|
71
|
-
value =
|
71
|
+
value = eval "#{@table_name}.#{column_name}", binding, __FILE__, __LINE__
|
72
72
|
unless value
|
73
73
|
name = column_name.to_s
|
74
|
-
if name
|
74
|
+
if name =~ /_id$/
|
75
75
|
related = name[0..-4] #strip "_id"
|
76
|
-
value =
|
76
|
+
value = eval "#{related}._id", binding, __FILE__, __LINE__
|
77
77
|
end
|
78
78
|
end
|
79
79
|
value
|
@@ -103,8 +103,11 @@ module Squealer
|
|
103
103
|
|
104
104
|
def execute_sql(sql)
|
105
105
|
statement = Database.instance.export.prepare(sql)
|
106
|
-
values =
|
106
|
+
values = typecast_values * 2
|
107
|
+
|
107
108
|
statement.send(:execute, @row_id, *values) #expand values into distinct arguments
|
109
|
+
rescue Mysql::Error, TypeError
|
110
|
+
raise "Failed to execute statement: #{sql} with #{values.inspect}.\nOriginal Exception was: #{$!.to_s}"
|
108
111
|
end
|
109
112
|
|
110
113
|
def pk_name
|
@@ -113,7 +116,7 @@ module Squealer
|
|
113
116
|
|
114
117
|
def column_names
|
115
118
|
return if @column_names.size == 0
|
116
|
-
",#{@column_names.join(',')}"
|
119
|
+
",#{@column_names.map { |name| quote_identifier(name) }.join(',')}"
|
117
120
|
end
|
118
121
|
|
119
122
|
def column_values
|
@@ -130,10 +133,29 @@ module Squealer
|
|
130
133
|
def column_markers
|
131
134
|
return if @column_names.size == 0
|
132
135
|
result = ""
|
133
|
-
@column_names.each {|k| result << "#{k}=?," }
|
136
|
+
@column_names.each {|k| result << "#{quote_identifier(k)}=?," }
|
134
137
|
result.chop
|
135
138
|
end
|
136
139
|
|
140
|
+
def typecast_values
|
141
|
+
column_values.map do |value|
|
142
|
+
case value
|
143
|
+
when true, false
|
144
|
+
value.to_i
|
145
|
+
when Symbol
|
146
|
+
value.to_s
|
147
|
+
when Array
|
148
|
+
value.join(",")
|
149
|
+
else
|
150
|
+
value
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def quote_identifier(name)
|
156
|
+
"`#{name}`"
|
157
|
+
end
|
158
|
+
|
137
159
|
class Queue < DelegateClass(Array)
|
138
160
|
include Singleton
|
139
161
|
|
data/lib/squealer.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,112 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'time'
|
1
3
|
require 'rubygems'
|
2
4
|
|
3
5
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
4
6
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
5
7
|
|
6
8
|
require 'squealer'
|
9
|
+
|
10
|
+
Spec::Runner.configure do |config|
|
11
|
+
config.before(:suite) do
|
12
|
+
$db_name = "test_export_#{object_id}"
|
13
|
+
create_test_db($db_name)
|
14
|
+
end
|
15
|
+
|
16
|
+
config.after(:suite) do
|
17
|
+
drop_test_db($db_name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def create_test_db(name)
|
21
|
+
@my = Mysql.connect('localhost', 'root')
|
22
|
+
@my.query("DROP DATABASE IF EXISTS #{name}")
|
23
|
+
@my.query("CREATE DATABASE #{name}")
|
24
|
+
@my.query("USE #{name}")
|
25
|
+
@my.query("SET sql_mode='ANSI_QUOTES'")
|
26
|
+
|
27
|
+
create_export_tables
|
28
|
+
|
29
|
+
Squealer::Database.instance.import_from('localhost', 27017, $db_name)
|
30
|
+
@mongo = Squealer::Database.instance.import.send(:instance_variable_get, '@dbc')
|
31
|
+
drop_mongo
|
32
|
+
seed_import
|
33
|
+
end
|
34
|
+
|
35
|
+
def drop_test_db(name)
|
36
|
+
@my.query("DROP DATABASE IF EXISTS #{name}")
|
37
|
+
@my.close
|
38
|
+
|
39
|
+
drop_mongo
|
40
|
+
end
|
41
|
+
|
42
|
+
def drop_mongo
|
43
|
+
@mongo.eval('db.dropDatabase()') if @mongo
|
44
|
+
end
|
45
|
+
|
46
|
+
def seed_import
|
47
|
+
hashrocket = @mongo.collection('organizations').save({ :name => 'Hashrocket' })
|
48
|
+
zorganization = @mongo.collection('organizations').save({ :name => 'Zorganization', :disabled_date => as_time(Date.today) })
|
49
|
+
|
50
|
+
users = [
|
51
|
+
{ :name => 'Josh Graham', :dob => as_time(Date.parse('01-Jan-1971')), :gender => 'M',
|
52
|
+
:organization_id => hashrocket,
|
53
|
+
:activities => [
|
54
|
+
{ :name => 'Develop squealer', :due_date => as_time(Date.today + 1) },
|
55
|
+
{ :name => 'Organize speakerconf.com', :due_date => as_time(Date.today + 30) },
|
56
|
+
{ :name => 'Hashrocket party', :due_date => as_time(Date.today + 7) }
|
57
|
+
]
|
58
|
+
},
|
59
|
+
{ :name => 'Bernerd Schaefer', :dob => as_time(Date.parse('31-Dec-1985')), :gender => 'M',
|
60
|
+
:organization_id => hashrocket,
|
61
|
+
:activities => [
|
62
|
+
{ :name => 'Retype all of the code Josh wrote in squealer', :due_date => as_time(Date.today + 2) },
|
63
|
+
{ :name => 'Listen to rare Thelonius Monk EP', :due_date => as_time(Date.today) },
|
64
|
+
{ :name => 'Practice karaoke', :due_date => as_time(Date.today + 7) }
|
65
|
+
]
|
66
|
+
},
|
67
|
+
{ :name => 'Your momma', :dob => as_time(Date.parse('15-Jun-1955')), :gender => 'F',
|
68
|
+
:organization_id => zorganization,
|
69
|
+
:activities => [
|
70
|
+
{ :name => 'Cook me some pie', :due_date => as_time(Date.today) },
|
71
|
+
{ :name => 'Make me a sammich', :due_date => as_time(Date.today) }
|
72
|
+
]
|
73
|
+
}
|
74
|
+
]
|
75
|
+
|
76
|
+
users.each { |user| @mongo.collection('users').save user }
|
77
|
+
end
|
78
|
+
|
79
|
+
def create_export_tables
|
80
|
+
command = <<-COMMAND.gsub(/\n\s*/, " ")
|
81
|
+
CREATE TABLE "users" (
|
82
|
+
"id" INT NOT NULL AUTO_INCREMENT ,
|
83
|
+
"name" VARCHAR(255) NULL ,
|
84
|
+
"gender" CHAR(1) NULL ,
|
85
|
+
"dob" DATE NULL ,
|
86
|
+
PRIMARY KEY ("id") )
|
87
|
+
COMMAND
|
88
|
+
@my.query(command)
|
89
|
+
|
90
|
+
command = <<-COMMAND.gsub(/\n\s*/, " ")
|
91
|
+
CREATE TABLE "activity" (
|
92
|
+
"id" INT NOT NULL AUTO_INCREMENT ,
|
93
|
+
"user_id" INT NULL ,
|
94
|
+
"name" VARCHAR(255) NULL ,
|
95
|
+
"due_date" DATE NULL ,
|
96
|
+
PRIMARY KEY ("id") )
|
97
|
+
COMMAND
|
98
|
+
@my.query(command)
|
99
|
+
|
100
|
+
command = <<-COMMAND.gsub(/\n\s*/, " ")
|
101
|
+
CREATE TABLE "organizations" (
|
102
|
+
"id" INT NOT NULL AUTO_INCREMENT ,
|
103
|
+
"disabed_date" DATE NULL ,
|
104
|
+
PRIMARY KEY ("id") )
|
105
|
+
COMMAND
|
106
|
+
@my.query(command)
|
107
|
+
end
|
108
|
+
|
109
|
+
def as_time(date)
|
110
|
+
Time.parse(date.to_s)
|
111
|
+
end
|
112
|
+
end
|
@@ -3,39 +3,131 @@ require 'mongo'
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
describe Squealer::Database do
|
6
|
-
|
7
|
-
before(:all) do
|
8
|
-
@db_name = "test_export_#{object_id}"
|
9
|
-
create_test_db(@db_name)
|
10
|
-
end
|
11
|
-
|
12
|
-
after(:all) do
|
13
|
-
drop_test_db(@db_name)
|
14
|
-
end
|
15
|
-
|
16
6
|
it "is a singleton" do
|
17
7
|
Squealer::Database.respond_to?(:instance).should be_true
|
18
8
|
end
|
19
9
|
|
20
|
-
|
21
|
-
Squealer::Database.instance
|
22
|
-
|
23
|
-
|
10
|
+
describe "import" do
|
11
|
+
let(:databases) { Squealer::Database.instance }
|
12
|
+
|
13
|
+
|
14
|
+
it "takes an import database" do
|
15
|
+
databases.send(:instance_variable_get, '@import_dbc').should be_a_kind_of(Mongo::DB)
|
16
|
+
end
|
24
17
|
|
25
|
-
|
26
|
-
|
27
|
-
|
18
|
+
it "returns a squealer connection object" do
|
19
|
+
databases.import.should be_a_kind_of(Squealer::Database::Connection)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "delegates eval to Mongo" do
|
23
|
+
databases.send(:instance_variable_get, '@import_dbc').eval('db.getName()').should == $db_name
|
24
|
+
databases.import.eval('db.getName()').should == $db_name
|
25
|
+
end
|
28
26
|
end
|
29
27
|
|
30
|
-
|
28
|
+
describe "source" do
|
29
|
+
let(:databases) { Squealer::Database.instance }
|
30
|
+
|
31
|
+
before { databases.import_from('localhost', 27017, $db_name) }
|
32
|
+
|
33
|
+
it "returns a Source" do
|
34
|
+
databases.import.source('foo').should be_a_kind_of(Squealer::Database::Source)
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "Source::cursor" do
|
38
|
+
it "returns a databases cursor" do
|
39
|
+
databases.import.source('foo').cursor.should be_a_kind_of(Mongo::Cursor)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "an empty collection" do
|
44
|
+
subject { databases.import.source('foo') }
|
45
|
+
|
46
|
+
it "counts a total of zero" do
|
47
|
+
subject.counts[:total].should == 0
|
48
|
+
end
|
49
|
+
|
50
|
+
it "counts zero imported" do
|
51
|
+
subject.counts[:imported].should == 0
|
52
|
+
end
|
53
|
+
|
54
|
+
it "counts zero exported" do
|
55
|
+
subject.counts[:exported].should == 0
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "a collection with two documents" do
|
60
|
+
let(:mongo) { Squealer::Database.instance.import.send(:instance_variable_get, '@dbc') }
|
61
|
+
|
62
|
+
subject do
|
63
|
+
mongo.collection('foo').save({'name' => 'Bar'});
|
64
|
+
mongo.collection('foo').save({'name' => 'Baz'});
|
65
|
+
source = databases.import.source('foo') # activate the counter
|
66
|
+
source.send(:instance_variable_set, :@progress_bar, nil)
|
67
|
+
Squealer::ProgressBar.send(:class_variable_set, :@@progress_bar, nil)
|
68
|
+
source
|
69
|
+
end
|
70
|
+
|
71
|
+
after do
|
72
|
+
mongo.collection('foo').drop
|
73
|
+
end
|
74
|
+
|
75
|
+
it "returns a Source" do
|
76
|
+
subject.should be_a_kind_of(Squealer::Database::Source)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "counts a total of two" do
|
80
|
+
subject.counts[:total].should == 2
|
81
|
+
end
|
82
|
+
|
83
|
+
context "before iterating" do
|
84
|
+
it "counts zero imported" do
|
85
|
+
subject.counts[:imported].should == 0
|
86
|
+
end
|
87
|
+
|
88
|
+
it "counts zero exported" do
|
89
|
+
subject.counts[:exported].should == 0
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context "after iterating" do
|
94
|
+
before do
|
95
|
+
subject.each {}
|
96
|
+
end
|
97
|
+
|
98
|
+
it "counts two imported" do
|
99
|
+
subject.counts[:imported].should == 2
|
100
|
+
end
|
101
|
+
|
102
|
+
it "counts two exported" do
|
103
|
+
subject.counts[:exported].should == 2
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "real squeal" do
|
108
|
+
before { pending "interactive_view" }
|
109
|
+
subject do
|
110
|
+
source = databases.import.source("users")
|
111
|
+
end
|
112
|
+
|
113
|
+
it "^^^ you saw that progress bar right there" do
|
114
|
+
subject.each do
|
115
|
+
sleep (rand(2) + 1)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
31
121
|
|
32
|
-
def create_test_db(name)
|
33
|
-
@my = Mysql.connect('localhost', 'root')
|
34
|
-
@my.query("create database #{name}")
|
35
122
|
end
|
36
123
|
|
37
|
-
|
38
|
-
|
39
|
-
|
124
|
+
describe "export" do
|
125
|
+
let(:databases) { Squealer::Database.instance }
|
126
|
+
|
127
|
+
it "takes an export database" do
|
128
|
+
databases.export_to('localhost', 'root', '', $db_name)
|
129
|
+
databases.send(:instance_variable_get, '@export_dbc').should be_a_kind_of(Mysql)
|
130
|
+
end
|
40
131
|
end
|
132
|
+
|
41
133
|
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Squealer::ProgressBar do
|
4
|
+
let(:total) { 200 }
|
5
|
+
let(:progress_bar) do
|
6
|
+
testable_progress_bar = Class.new(Squealer::ProgressBar) do
|
7
|
+
attr_reader :emitter
|
8
|
+
public :total, :ticks, :percentage, :progress_markers, :emit,
|
9
|
+
:duration, :start_time, :end_time, :progress_bar_width
|
10
|
+
|
11
|
+
def console
|
12
|
+
@console ||= StringIO.new
|
13
|
+
end
|
14
|
+
|
15
|
+
alias real_start_emitter start_emitter
|
16
|
+
def start_emitter; end
|
17
|
+
public :real_start_emitter
|
18
|
+
|
19
|
+
end
|
20
|
+
testable_progress_bar.new(total).start
|
21
|
+
end
|
22
|
+
let(:console) { progress_bar.console }
|
23
|
+
let(:progress_bar_width) { progress_bar.progress_bar_width }
|
24
|
+
|
25
|
+
before { progress_bar.start }
|
26
|
+
after { progress_bar.finish }
|
27
|
+
|
28
|
+
it "allows only one progress bar at a time" do
|
29
|
+
Squealer::ProgressBar.new(0).should be_nil
|
30
|
+
end
|
31
|
+
|
32
|
+
it "records the starting time" do
|
33
|
+
progress_bar.start_time.should be_an_instance_of(Time)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "records the starting time" do
|
37
|
+
progress_bar.start_time.should be_an_instance_of(Time)
|
38
|
+
end
|
39
|
+
|
40
|
+
context "threaded" do
|
41
|
+
before { progress_bar.stub(:emitter).and_return(progress_bar.real_start_emitter) }
|
42
|
+
after { progress_bar.emitter.kill }
|
43
|
+
|
44
|
+
it "has an emitter" do
|
45
|
+
progress_bar.tick
|
46
|
+
progress_bar.emitter.should_not be_nil
|
47
|
+
end
|
48
|
+
|
49
|
+
it "emits at least once" do
|
50
|
+
progress_bar.tick
|
51
|
+
progress_bar.emitter.wakeup
|
52
|
+
sleep 0.1
|
53
|
+
console.string.split("\r").length.should > 0
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "no items completed" do
|
58
|
+
it "emits the total number provided" do
|
59
|
+
progress_bar.total.should == total
|
60
|
+
end
|
61
|
+
|
62
|
+
it "emits the number of ticks" do
|
63
|
+
progress_bar.ticks.should == 0
|
64
|
+
end
|
65
|
+
|
66
|
+
it "displays an empty bar" do
|
67
|
+
progress_bar.progress_markers.size.should == 0
|
68
|
+
end
|
69
|
+
|
70
|
+
it "prints a progress bar to the console" do
|
71
|
+
progress_bar.emit
|
72
|
+
console.string.should == "\r[#{' ' * progress_bar_width}] #{0}/#{total} (0%)"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "one item completed" do
|
77
|
+
before { progress_bar.tick }
|
78
|
+
it "emits the number of ticks" do
|
79
|
+
progress_bar.ticks.should == 1
|
80
|
+
end
|
81
|
+
|
82
|
+
it "emits the number of ticks as a percentage of the total number (rounded down)" do
|
83
|
+
progress_bar.percentage.should == 0
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context "1/nth complete (where n is the width of the progress bar)" do
|
88
|
+
let(:ticks) { (total / progress_bar_width) }
|
89
|
+
before { ticks.times { progress_bar.tick } }
|
90
|
+
it "emits the number of ticks" do
|
91
|
+
progress_bar.ticks.should == ticks
|
92
|
+
end
|
93
|
+
|
94
|
+
it "emits the number of ticks as a percentage of the total number (rounded down)" do
|
95
|
+
progress_bar.percentage.should == (ticks.to_f * 100 / total).floor
|
96
|
+
end
|
97
|
+
|
98
|
+
it "displays the first progress marker" do
|
99
|
+
progress_bar.progress_markers.size.should == 1
|
100
|
+
end
|
101
|
+
|
102
|
+
it "prints a progress bar to the console" do
|
103
|
+
progress_bar.emit
|
104
|
+
percentag = (ticks.to_f * 100 / total).floor
|
105
|
+
console.string.should == "\r[=#{' ' * (progress_bar_width - 1)}] #{ticks}/#{total} (#{percentag}%)"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context "all but one item completed" do
|
110
|
+
let(:ticks) { total - 1 }
|
111
|
+
before { ticks.times { progress_bar.tick } }
|
112
|
+
|
113
|
+
it "emits the number of ticks" do
|
114
|
+
progress_bar.ticks.should == ticks
|
115
|
+
end
|
116
|
+
|
117
|
+
it "emits the number of ticks as a percentage of the total number (rounded down)" do
|
118
|
+
progress_bar.percentage.should == 99
|
119
|
+
end
|
120
|
+
|
121
|
+
it "has not yet displayed the final progress marker" do
|
122
|
+
progress_bar.progress_markers.size.should == (progress_bar_width - 1)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "all items completed" do
|
127
|
+
let(:ticks) { total }
|
128
|
+
before { ticks.times { progress_bar.tick } }
|
129
|
+
|
130
|
+
it "emits the number of ticks" do
|
131
|
+
progress_bar.ticks.should == ticks
|
132
|
+
end
|
133
|
+
|
134
|
+
it "emits 100%" do
|
135
|
+
progress_bar.percentage.should == 100
|
136
|
+
end
|
137
|
+
|
138
|
+
it "fills the progress bar with progress markers" do
|
139
|
+
progress_bar.progress_markers.size.should == progress_bar_width
|
140
|
+
end
|
141
|
+
|
142
|
+
it "records the ending time when finished" do
|
143
|
+
progress_bar.finish
|
144
|
+
progress_bar.end_time.should be_an_instance_of(Time)
|
145
|
+
end
|
146
|
+
|
147
|
+
it "prints a progress bar to the console" do
|
148
|
+
progress_bar.finish
|
149
|
+
progress_bar.emit
|
150
|
+
console.string.split("\n").first.should == "\r[#{'=' * progress_bar_width}] #{ticks}/#{total} (100%)"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
context "multiple emits" do
|
155
|
+
let(:ticks) { total }
|
156
|
+
subject { console.string }
|
157
|
+
before do
|
158
|
+
progress_bar.emit
|
159
|
+
end
|
160
|
+
|
161
|
+
context "not done" do
|
162
|
+
it "emitted two lines with no final newline" do
|
163
|
+
progress_bar.emit
|
164
|
+
|
165
|
+
subject.split("\r").size.should == 3
|
166
|
+
subject[-1, 1].should_not == "\n"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
context "done" do
|
171
|
+
it "emitted two lines with a final newline" do
|
172
|
+
ticks.times { progress_bar.tick }
|
173
|
+
progress_bar.finish
|
174
|
+
progress_bar.emit
|
175
|
+
|
176
|
+
subject.split("\r").size.should == 3
|
177
|
+
subject[-1, 1].should == "\n"
|
178
|
+
end
|
179
|
+
|
180
|
+
it "emitted final timings" do
|
181
|
+
ticks.times { progress_bar.tick }
|
182
|
+
progress_bar.finish
|
183
|
+
progress_bar.emit
|
184
|
+
|
185
|
+
subject.should include("Start: #{progress_bar.start_time}\n")
|
186
|
+
subject.should include("End: #{progress_bar.end_time}\n")
|
187
|
+
subject.should include("Duration: #{progress_bar.duration}\n")
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -196,7 +196,6 @@ describe Squealer::Target do
|
|
196
196
|
end
|
197
197
|
end
|
198
198
|
end
|
199
|
-
|
200
199
|
end
|
201
200
|
end
|
202
201
|
end
|
@@ -210,6 +209,31 @@ describe Squealer::Target do
|
|
210
209
|
end
|
211
210
|
|
212
211
|
describe "#target" do
|
212
|
+
describe "#typecast_values" do
|
213
|
+
subject { target.send(:typecast_values) }
|
214
|
+
let(:target) { Squealer::Target.new(export_dbc, table_name) {} }
|
215
|
+
|
216
|
+
it "casts array to comma-separated string" do
|
217
|
+
target.assign(:colA) { ['1', '2'] }
|
218
|
+
subject.should == ['1,2']
|
219
|
+
end
|
220
|
+
|
221
|
+
it "casts false to 0 (for mysql TINYINT)" do
|
222
|
+
target.assign(:colA) { false }
|
223
|
+
subject.should == [0]
|
224
|
+
end
|
225
|
+
|
226
|
+
it "casts true to 1 (for mysql TINYINT)" do
|
227
|
+
target.assign(:colA) { true }
|
228
|
+
subject.should == [1]
|
229
|
+
end
|
230
|
+
|
231
|
+
it "casts symbol to string" do
|
232
|
+
target.assign(:colA) { :open }
|
233
|
+
subject.should == ['open']
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
213
237
|
context "generates SQL command strings" do
|
214
238
|
let(:target) { Squealer::Target.new(export_dbc, table_name) { nil } }
|
215
239
|
|
@@ -239,7 +263,7 @@ describe Squealer::Target do
|
|
239
263
|
end
|
240
264
|
|
241
265
|
it "includes the column name in the INSERT" do
|
242
|
-
target.sql.should =~ /\(id
|
266
|
+
target.sql.should =~ /\(id,`colA`\) VALUES/
|
243
267
|
end
|
244
268
|
|
245
269
|
it "includes the column value in the INSERT" do
|
@@ -249,7 +273,7 @@ describe Squealer::Target do
|
|
249
273
|
|
250
274
|
it "includes the column name and value in the UPDATE" do
|
251
275
|
# target.sql.should =~ /UPDATE colA='#{value_1}'/
|
252
|
-
target.sql.should =~ /UPDATE colA
|
276
|
+
target.sql.should =~ /UPDATE `colA`=\?/
|
253
277
|
end
|
254
278
|
|
255
279
|
end
|
@@ -265,7 +289,7 @@ describe Squealer::Target do
|
|
265
289
|
end
|
266
290
|
|
267
291
|
it "includes the column names in the INSERT" do
|
268
|
-
target.sql.should =~ /\(id
|
292
|
+
target.sql.should =~ /\(id,`colA`,`colB`\) VALUES/
|
269
293
|
end
|
270
294
|
|
271
295
|
it "includes the column values in the INSERT" do
|
@@ -275,7 +299,7 @@ describe Squealer::Target do
|
|
275
299
|
|
276
300
|
it "includes the column names and values in the UPDATE" do
|
277
301
|
# target.sql.should =~ /UPDATE colA='#{value_1}',colB='#{value_2}'/
|
278
|
-
target.sql.should =~ /UPDATE colA
|
302
|
+
target.sql.should =~ /UPDATE `colA`=\?,`colB`=\?/
|
279
303
|
end
|
280
304
|
end
|
281
305
|
end
|
data/squealer.gemspec
CHANGED
@@ -5,13 +5,15 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{squealer}
|
8
|
-
s.version = "1.
|
8
|
+
s.version = "2.1.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = ["Josh Graham", "Durran Jordan"]
|
12
|
-
s.date = %q{2010-05-
|
11
|
+
s.authors = ["Josh Graham", "Durran Jordan", "Matt Yoho", "Bernerd Schaefer"]
|
12
|
+
s.date = %q{2010-05-20}
|
13
|
+
s.default_executable = %q{skewer}
|
13
14
|
s.description = %q{Exports mongodb to mysql. More later.}
|
14
15
|
s.email = %q{joshua.graham@grahamis.com}
|
16
|
+
s.executables = ["skewer"]
|
15
17
|
s.extra_rdoc_files = [
|
16
18
|
"README.md"
|
17
19
|
]
|
@@ -22,12 +24,15 @@ Gem::Specification.new do |s|
|
|
22
24
|
"README.md",
|
23
25
|
"Rakefile",
|
24
26
|
"VERSION",
|
27
|
+
"bin/skewer",
|
28
|
+
"lib/.example_squeal.rb.swp",
|
25
29
|
"lib/example_squeal.rb",
|
26
30
|
"lib/squealer.rb",
|
27
31
|
"lib/squealer/boolean.rb",
|
28
32
|
"lib/squealer/database.rb",
|
29
33
|
"lib/squealer/hash.rb",
|
30
34
|
"lib/squealer/object.rb",
|
35
|
+
"lib/squealer/progress_bar.rb",
|
31
36
|
"lib/squealer/target.rb",
|
32
37
|
"lib/squealer/time.rb",
|
33
38
|
"lib/tasks/jeweler.rake",
|
@@ -37,6 +42,7 @@ Gem::Specification.new do |s|
|
|
37
42
|
"spec/squealer/database_spec.rb",
|
38
43
|
"spec/squealer/hash_spec.rb",
|
39
44
|
"spec/squealer/object_spec.rb",
|
45
|
+
"spec/squealer/progress_bar_spec.rb",
|
40
46
|
"spec/squealer/target_spec.rb",
|
41
47
|
"spec/squealer/time_spec.rb",
|
42
48
|
"squealer.gemspec"
|
@@ -44,7 +50,7 @@ Gem::Specification.new do |s|
|
|
44
50
|
s.homepage = %q{http://github.com/delitescere/squealer/}
|
45
51
|
s.rdoc_options = ["--charset=UTF-8"]
|
46
52
|
s.require_paths = ["lib"]
|
47
|
-
s.rubygems_version = %q{1.3.
|
53
|
+
s.rubygems_version = %q{1.3.6}
|
48
54
|
s.summary = %q{Document-oriented to Relational database exporter}
|
49
55
|
s.test_files = [
|
50
56
|
"spec/spec_helper.rb",
|
@@ -52,6 +58,7 @@ Gem::Specification.new do |s|
|
|
52
58
|
"spec/squealer/database_spec.rb",
|
53
59
|
"spec/squealer/hash_spec.rb",
|
54
60
|
"spec/squealer/object_spec.rb",
|
61
|
+
"spec/squealer/progress_bar_spec.rb",
|
55
62
|
"spec/squealer/target_spec.rb",
|
56
63
|
"spec/squealer/time_spec.rb"
|
57
64
|
]
|
@@ -60,16 +67,19 @@ Gem::Specification.new do |s|
|
|
60
67
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
61
68
|
s.specification_version = 3
|
62
69
|
|
63
|
-
if Gem::Version.new(Gem::
|
70
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
64
71
|
s.add_runtime_dependency(%q<mysql>, [">= 2.8.1"])
|
65
72
|
s.add_runtime_dependency(%q<mongo>, [">= 0.18.3"])
|
73
|
+
s.add_runtime_dependency(%q<bson_ext>, [">= 1.0.1"])
|
66
74
|
else
|
67
75
|
s.add_dependency(%q<mysql>, [">= 2.8.1"])
|
68
76
|
s.add_dependency(%q<mongo>, [">= 0.18.3"])
|
77
|
+
s.add_dependency(%q<bson_ext>, [">= 1.0.1"])
|
69
78
|
end
|
70
79
|
else
|
71
80
|
s.add_dependency(%q<mysql>, [">= 2.8.1"])
|
72
81
|
s.add_dependency(%q<mongo>, [">= 0.18.3"])
|
82
|
+
s.add_dependency(%q<bson_ext>, [">= 1.0.1"])
|
73
83
|
end
|
74
84
|
end
|
75
85
|
|
metadata
CHANGED
@@ -1,33 +1,32 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: squealer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash: 31
|
5
4
|
prerelease: false
|
6
5
|
segments:
|
7
|
-
- 1
|
8
6
|
- 2
|
7
|
+
- 1
|
9
8
|
- 0
|
10
|
-
version: 1.
|
9
|
+
version: 2.1.0
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- Josh Graham
|
14
13
|
- Durran Jordan
|
14
|
+
- Matt Yoho
|
15
|
+
- Bernerd Schaefer
|
15
16
|
autorequire:
|
16
17
|
bindir: bin
|
17
18
|
cert_chain: []
|
18
19
|
|
19
|
-
date: 2010-05-
|
20
|
-
default_executable:
|
20
|
+
date: 2010-05-20 00:00:00 -05:00
|
21
|
+
default_executable: skewer
|
21
22
|
dependencies:
|
22
23
|
- !ruby/object:Gem::Dependency
|
23
24
|
name: mysql
|
24
25
|
prerelease: false
|
25
26
|
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
-
none: false
|
27
27
|
requirements:
|
28
28
|
- - ">="
|
29
29
|
- !ruby/object:Gem::Version
|
30
|
-
hash: 45
|
31
30
|
segments:
|
32
31
|
- 2
|
33
32
|
- 8
|
@@ -39,11 +38,9 @@ dependencies:
|
|
39
38
|
name: mongo
|
40
39
|
prerelease: false
|
41
40
|
requirement: &id002 !ruby/object:Gem::Requirement
|
42
|
-
none: false
|
43
41
|
requirements:
|
44
42
|
- - ">="
|
45
43
|
- !ruby/object:Gem::Version
|
46
|
-
hash: 81
|
47
44
|
segments:
|
48
45
|
- 0
|
49
46
|
- 18
|
@@ -51,10 +48,24 @@ dependencies:
|
|
51
48
|
version: 0.18.3
|
52
49
|
type: :runtime
|
53
50
|
version_requirements: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: bson_ext
|
53
|
+
prerelease: false
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
segments:
|
59
|
+
- 1
|
60
|
+
- 0
|
61
|
+
- 1
|
62
|
+
version: 1.0.1
|
63
|
+
type: :runtime
|
64
|
+
version_requirements: *id003
|
54
65
|
description: Exports mongodb to mysql. More later.
|
55
66
|
email: joshua.graham@grahamis.com
|
56
|
-
executables:
|
57
|
-
|
67
|
+
executables:
|
68
|
+
- skewer
|
58
69
|
extensions: []
|
59
70
|
|
60
71
|
extra_rdoc_files:
|
@@ -66,12 +77,15 @@ files:
|
|
66
77
|
- README.md
|
67
78
|
- Rakefile
|
68
79
|
- VERSION
|
80
|
+
- bin/skewer
|
81
|
+
- lib/.example_squeal.rb.swp
|
69
82
|
- lib/example_squeal.rb
|
70
83
|
- lib/squealer.rb
|
71
84
|
- lib/squealer/boolean.rb
|
72
85
|
- lib/squealer/database.rb
|
73
86
|
- lib/squealer/hash.rb
|
74
87
|
- lib/squealer/object.rb
|
88
|
+
- lib/squealer/progress_bar.rb
|
75
89
|
- lib/squealer/target.rb
|
76
90
|
- lib/squealer/time.rb
|
77
91
|
- lib/tasks/jeweler.rake
|
@@ -81,6 +95,7 @@ files:
|
|
81
95
|
- spec/squealer/database_spec.rb
|
82
96
|
- spec/squealer/hash_spec.rb
|
83
97
|
- spec/squealer/object_spec.rb
|
98
|
+
- spec/squealer/progress_bar_spec.rb
|
84
99
|
- spec/squealer/target_spec.rb
|
85
100
|
- spec/squealer/time_spec.rb
|
86
101
|
- squealer.gemspec
|
@@ -94,27 +109,23 @@ rdoc_options:
|
|
94
109
|
require_paths:
|
95
110
|
- lib
|
96
111
|
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
-
none: false
|
98
112
|
requirements:
|
99
113
|
- - ">="
|
100
114
|
- !ruby/object:Gem::Version
|
101
|
-
hash: 3
|
102
115
|
segments:
|
103
116
|
- 0
|
104
117
|
version: "0"
|
105
118
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
|
-
none: false
|
107
119
|
requirements:
|
108
120
|
- - ">="
|
109
121
|
- !ruby/object:Gem::Version
|
110
|
-
hash: 3
|
111
122
|
segments:
|
112
123
|
- 0
|
113
124
|
version: "0"
|
114
125
|
requirements: []
|
115
126
|
|
116
127
|
rubyforge_project:
|
117
|
-
rubygems_version: 1.3.
|
128
|
+
rubygems_version: 1.3.6
|
118
129
|
signing_key:
|
119
130
|
specification_version: 3
|
120
131
|
summary: Document-oriented to Relational database exporter
|
@@ -124,5 +135,6 @@ test_files:
|
|
124
135
|
- spec/squealer/database_spec.rb
|
125
136
|
- spec/squealer/hash_spec.rb
|
126
137
|
- spec/squealer/object_spec.rb
|
138
|
+
- spec/squealer/progress_bar_spec.rb
|
127
139
|
- spec/squealer/target_spec.rb
|
128
140
|
- spec/squealer/time_spec.rb
|