molo 0.5.0 → 0.6.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/Gemfile.lock +2 -2
- data/Molo.gemspec +15 -3
- data/VERSION +1 -1
- data/lib/tasks/molo.rb +48 -3
- data/vendor/yaml_db/init.rb +1 -0
- data/vendor/yaml_db/lib/csv_db.rb +78 -0
- data/vendor/yaml_db/lib/serialization_helper.rb +195 -0
- data/vendor/yaml_db/lib/yaml_db.rb +66 -0
- data/vendor/yaml_db/spec/base.rb +9 -0
- data/vendor/yaml_db/spec/serialization_helper_base_spec.rb +53 -0
- data/vendor/yaml_db/spec/serialization_helper_dump_spec.rb +65 -0
- data/vendor/yaml_db/spec/serialization_helper_load_spec.rb +77 -0
- data/vendor/yaml_db/spec/serialization_utils_spec.rb +31 -0
- data/vendor/yaml_db/spec/yaml_dump_spec.rb +55 -0
- data/vendor/yaml_db/spec/yaml_load_spec.rb +33 -0
- data/vendor/yaml_db/spec/yaml_utils_spec.rb +21 -0
- metadata +16 -4
data/Gemfile.lock
CHANGED
data/Molo.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{molo}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.6.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Joel Moss", "Todd Huss", "Michael Grosser"]
|
12
|
-
s.date = %q{2010-12-
|
12
|
+
s.date = %q{2010-12-14}
|
13
13
|
s.email = %q{joel@developwithstyle.com}
|
14
14
|
s.extra_rdoc_files = [
|
15
15
|
"README.markdown"
|
@@ -27,7 +27,19 @@ Gem::Specification.new do |s|
|
|
27
27
|
"vendor/migration_helpers/MIT-LICENSE",
|
28
28
|
"vendor/migration_helpers/README.markdown",
|
29
29
|
"vendor/migration_helpers/init.rb",
|
30
|
-
"vendor/migration_helpers/lib/migration_helper.rb"
|
30
|
+
"vendor/migration_helpers/lib/migration_helper.rb",
|
31
|
+
"vendor/yaml_db/init.rb",
|
32
|
+
"vendor/yaml_db/lib/csv_db.rb",
|
33
|
+
"vendor/yaml_db/lib/serialization_helper.rb",
|
34
|
+
"vendor/yaml_db/lib/yaml_db.rb",
|
35
|
+
"vendor/yaml_db/spec/base.rb",
|
36
|
+
"vendor/yaml_db/spec/serialization_helper_base_spec.rb",
|
37
|
+
"vendor/yaml_db/spec/serialization_helper_dump_spec.rb",
|
38
|
+
"vendor/yaml_db/spec/serialization_helper_load_spec.rb",
|
39
|
+
"vendor/yaml_db/spec/serialization_utils_spec.rb",
|
40
|
+
"vendor/yaml_db/spec/yaml_dump_spec.rb",
|
41
|
+
"vendor/yaml_db/spec/yaml_load_spec.rb",
|
42
|
+
"vendor/yaml_db/spec/yaml_utils_spec.rb"
|
31
43
|
]
|
32
44
|
s.homepage = %q{http://codaset.com/joelmoss/molo}
|
33
45
|
s.rdoc_options = ["--charset=UTF-8"]
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.6.0
|
data/lib/tasks/molo.rb
CHANGED
@@ -33,10 +33,11 @@ class MigratorTasks < ::Rake::TaskLib
|
|
33
33
|
namespace :db do
|
34
34
|
task :ar_init do
|
35
35
|
require 'active_record'
|
36
|
-
ENV[@env] ||= @default_env
|
37
|
-
|
38
36
|
require 'erb'
|
37
|
+
require 'yaml_db'
|
39
38
|
|
39
|
+
ENV[@env] ||= @default_env
|
40
|
+
|
40
41
|
if @config.is_a?(Hash)
|
41
42
|
ActiveRecord::Base.configurations = @config
|
42
43
|
else
|
@@ -171,7 +172,7 @@ class MigratorTasks < ::Rake::TaskLib
|
|
171
172
|
puts "Current version: #{ActiveRecord::Migrator.current_version}"
|
172
173
|
end
|
173
174
|
|
174
|
-
desc "Raises an error if there are pending migrations"
|
175
|
+
# desc "Raises an error if there are pending migrations"
|
175
176
|
task :abort_if_pending_migrations => :ar_init do
|
176
177
|
@migrations.each do |path|
|
177
178
|
pending_migrations = ActiveRecord::Migrator.new(:up, path).pending_migrations
|
@@ -204,6 +205,50 @@ class MigratorTasks < ::Rake::TaskLib
|
|
204
205
|
end
|
205
206
|
end
|
206
207
|
|
208
|
+
desc "Dump schema and data to db/schema.rb and db/data.yml"
|
209
|
+
task(:dump => [ "db:schema:dump", "db:data:dump" ])
|
210
|
+
|
211
|
+
desc "Load schema and data from db/schema.rb and db/data.yml"
|
212
|
+
task(:load => [ "db:schema:load", "db:data:load" ])
|
213
|
+
|
214
|
+
namespace :data do
|
215
|
+
def db_dump_data_file (extension = "yml")
|
216
|
+
"#{dump_dir}/data.#{extension}"
|
217
|
+
end
|
218
|
+
|
219
|
+
def dump_dir(dir = "")
|
220
|
+
"#{base}/db#{dir}"
|
221
|
+
end
|
222
|
+
|
223
|
+
desc "Dump contents of database to db/data.extension (defaults to yaml)"
|
224
|
+
task :dump => :ar_init do
|
225
|
+
format_class = ENV['class'] || "YamlDb::Helper"
|
226
|
+
helper = format_class.constantize
|
227
|
+
SerializationHelper::Base.new(helper).dump db_dump_data_file helper.extension
|
228
|
+
end
|
229
|
+
|
230
|
+
desc "Dump contents of database to db/data/tablename.extension (defaults to yaml)"
|
231
|
+
task :dump_dir => :ar_init do
|
232
|
+
format_class = ENV['class'] || "YamlDb::Helper"
|
233
|
+
dir = ENV['dir'] || "data"
|
234
|
+
SerializationHelper::Base.new(format_class.constantize).dump_to_dir dump_dir("/#{dir}")
|
235
|
+
end
|
236
|
+
|
237
|
+
desc "Load contents of db/data.extension (defaults to yaml) into database"
|
238
|
+
task :load => :ar_init do
|
239
|
+
format_class = ENV['class'] || "YamlDb::Helper"
|
240
|
+
helper = format_class.constantize
|
241
|
+
SerializationHelper::Base.new(helper).load(db_dump_data_file helper.extension)
|
242
|
+
end
|
243
|
+
|
244
|
+
desc "Load contents of db/data/* into database"
|
245
|
+
task :load_dir => :ar_init do
|
246
|
+
dir = ENV['dir'] || "data"
|
247
|
+
format_class = ENV['class'] || "YamlDb::Helper"
|
248
|
+
SerializationHelper::Base.new(format_class.constantize).load_from_dir dump_dir("/#{dir}")
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
207
252
|
namespace :test do
|
208
253
|
desc "Recreate the test database from the current schema.rb"
|
209
254
|
task :load => ['db:ar_init', 'db:test:purge'] do
|
@@ -0,0 +1 @@
|
|
1
|
+
require "yaml_db"
|
@@ -0,0 +1,78 @@
|
|
1
|
+
#require 'FasterCSV'
|
2
|
+
module CsvDb
|
3
|
+
module Helper
|
4
|
+
def self.loader
|
5
|
+
Load
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.dumper
|
9
|
+
Dump
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.extension
|
13
|
+
"csv"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Load < SerializationHelper::Load
|
18
|
+
def self.load_documents(io, truncate = true)
|
19
|
+
tables = {}
|
20
|
+
curr_table = nil
|
21
|
+
io.each do |line|
|
22
|
+
if /BEGIN_CSV_TABLE_DECLARATION(.+)END_CSV_TABLE_DECLARATION/ =~ line
|
23
|
+
curr_table = $1
|
24
|
+
tables[curr_table] = {}
|
25
|
+
else
|
26
|
+
if tables[curr_table]["columns"]
|
27
|
+
tables[curr_table]["records"] << FasterCSV.parse(line)[0]
|
28
|
+
else
|
29
|
+
tables[curr_table]["columns"] = FasterCSV.parse(line)[0]
|
30
|
+
tables[curr_table]["records"] = []
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
tables.each_pair do |table_name, contents|
|
36
|
+
load_table(table_name, contents, truncate)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Dump < SerializationHelper::Dump
|
42
|
+
|
43
|
+
def self.before_table(io,table)
|
44
|
+
io.write "BEGIN_CSV_TABLE_DECLARATION#{table}END_CSV_TABLE_DECLARATION\n"
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.dump(io)
|
48
|
+
tables.each do |table|
|
49
|
+
before_table(io, table)
|
50
|
+
dump_table(io, table)
|
51
|
+
after_table(io, table)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.after_table(io,table)
|
56
|
+
io.write ""
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.dump_table_columns(io, table)
|
60
|
+
io.write(table_column_names(table).to_csv)
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.dump_table_records(io, table)
|
64
|
+
|
65
|
+
column_names = table_column_names(table)
|
66
|
+
|
67
|
+
each_table_page(table) do |records|
|
68
|
+
rows = SerializationHelper::Utils.unhash_records(records, column_names)
|
69
|
+
records.each do |record|
|
70
|
+
io.write(record.to_csv)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
module SerializationHelper
|
2
|
+
|
3
|
+
class Base
|
4
|
+
attr_reader :extension
|
5
|
+
|
6
|
+
def initialize(helper)
|
7
|
+
@dumper = helper.dumper
|
8
|
+
@loader = helper.loader
|
9
|
+
@extension = helper.extension
|
10
|
+
end
|
11
|
+
|
12
|
+
def dump(filename)
|
13
|
+
disable_logger
|
14
|
+
@dumper.dump(File.new(filename, "w"))
|
15
|
+
reenable_logger
|
16
|
+
end
|
17
|
+
|
18
|
+
def dump_to_dir(dirname)
|
19
|
+
Dir.mkdir(dirname)
|
20
|
+
tables = @dumper.tables
|
21
|
+
tables.each do |table|
|
22
|
+
io = File.new "#{dirname}/#{table}.#{@extension}", "w"
|
23
|
+
@dumper.before_table(io, table)
|
24
|
+
@dumper.dump_table io, table
|
25
|
+
@dumper.after_table(io, table)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def load(filename, truncate = true)
|
30
|
+
disable_logger
|
31
|
+
@loader.load(File.new(filename, "r"), truncate)
|
32
|
+
reenable_logger
|
33
|
+
end
|
34
|
+
|
35
|
+
def load_from_dir(dirname, truncate = true)
|
36
|
+
Dir.entries(dirname).each do |filename|
|
37
|
+
if filename =~ /^[.]/
|
38
|
+
next
|
39
|
+
end
|
40
|
+
@loader.load(File.new("#{dirname}/#{filename}", "r"), truncate)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def disable_logger
|
45
|
+
@@old_logger = ActiveRecord::Base.logger
|
46
|
+
ActiveRecord::Base.logger = nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def reenable_logger
|
50
|
+
ActiveRecord::Base.logger = @@old_logger
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Load
|
55
|
+
def self.load(io, truncate = true)
|
56
|
+
ActiveRecord::Base.connection.transaction do
|
57
|
+
load_documents(io, truncate)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.truncate_table(table)
|
62
|
+
begin
|
63
|
+
ActiveRecord::Base.connection.execute("TRUNCATE #{SerializationHelper::Utils.quote_table(table)}")
|
64
|
+
rescue Exception
|
65
|
+
ActiveRecord::Base.connection.execute("DELETE FROM #{SerializationHelper::Utils.quote_table(table)}")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.load_table(table, data, truncate = true)
|
70
|
+
column_names = data['columns']
|
71
|
+
if truncate
|
72
|
+
truncate_table(table)
|
73
|
+
end
|
74
|
+
load_records(table, column_names, data['records'])
|
75
|
+
reset_pk_sequence!(table)
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.load_records(table, column_names, records)
|
79
|
+
if column_names.nil?
|
80
|
+
return
|
81
|
+
end
|
82
|
+
columns = column_names.map{|cn| ActiveRecord::Base.connection.columns(table).detect{|c| c.name == cn}}
|
83
|
+
quoted_column_names = column_names.map { |column| ActiveRecord::Base.connection.quote_column_name(column) }.join(',')
|
84
|
+
quoted_table_name = SerializationHelper::Utils.quote_table(table)
|
85
|
+
records.each do |record|
|
86
|
+
quoted_values = record.zip(columns).map{|c| ActiveRecord::Base.connection.quote(c.first, c.last)}.join(',')
|
87
|
+
ActiveRecord::Base.connection.execute("INSERT INTO #{quoted_table_name} (#{quoted_column_names}) VALUES (#{quoted_values})")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.reset_pk_sequence!(table_name)
|
92
|
+
if ActiveRecord::Base.connection.respond_to?(:reset_pk_sequence!)
|
93
|
+
ActiveRecord::Base.connection.reset_pk_sequence!(table_name)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
module Utils
|
101
|
+
|
102
|
+
def self.unhash(hash, keys)
|
103
|
+
keys.map { |key| hash[key] }
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.unhash_records(records, keys)
|
107
|
+
records.each_with_index do |record, index|
|
108
|
+
records[index] = unhash(record, keys)
|
109
|
+
end
|
110
|
+
|
111
|
+
records
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.convert_booleans(records, columns)
|
115
|
+
records.each do |record|
|
116
|
+
columns.each do |column|
|
117
|
+
next if is_boolean(record[column])
|
118
|
+
record[column] = (record[column] == 't' or record[column] == '1')
|
119
|
+
end
|
120
|
+
end
|
121
|
+
records
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.boolean_columns(table)
|
125
|
+
columns = ActiveRecord::Base.connection.columns(table).reject { |c| silence_warnings { c.type != :boolean } }
|
126
|
+
columns.map { |c| c.name }
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.is_boolean(value)
|
130
|
+
value.kind_of?(TrueClass) or value.kind_of?(FalseClass)
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.quote_table(table)
|
134
|
+
ActiveRecord::Base.connection.quote_table_name(table)
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
class Dump
|
140
|
+
def self.before_table(io, table)
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.dump(io)
|
145
|
+
tables.each do |table|
|
146
|
+
before_table(io, table)
|
147
|
+
dump_table(io, table)
|
148
|
+
after_table(io, table)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.after_table(io, table)
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
def self.tables
|
157
|
+
ActiveRecord::Base.connection.tables.reject { |table| ['schema_info', 'schema_migrations'].include?(table) }
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.dump_table(io, table)
|
161
|
+
return if table_record_count(table).zero?
|
162
|
+
|
163
|
+
dump_table_columns(io, table)
|
164
|
+
dump_table_records(io, table)
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.table_column_names(table)
|
168
|
+
ActiveRecord::Base.connection.columns(table).map { |c| c.name }
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
def self.each_table_page(table, records_per_page=1000)
|
173
|
+
total_count = table_record_count(table)
|
174
|
+
pages = (total_count.to_f / records_per_page).ceil - 1
|
175
|
+
id = table_column_names(table).first
|
176
|
+
boolean_columns = SerializationHelper::Utils.boolean_columns(table)
|
177
|
+
quoted_table_name = SerializationHelper::Utils.quote_table(table)
|
178
|
+
|
179
|
+
(0..pages).to_a.each do |page|
|
180
|
+
sql = ActiveRecord::Base.connection.add_limit_offset!("SELECT * FROM #{quoted_table_name} ORDER BY #{id}",
|
181
|
+
:limit => records_per_page, :offset => records_per_page * page
|
182
|
+
)
|
183
|
+
records = ActiveRecord::Base.connection.select_all(sql)
|
184
|
+
records = SerializationHelper::Utils.convert_booleans(records, boolean_columns)
|
185
|
+
yield records
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def self.table_record_count(table)
|
190
|
+
ActiveRecord::Base.connection.select_one("SELECT COUNT(*) FROM #{SerializationHelper::Utils.quote_table(table)}").values.first.to_i
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'serialization_helper'
|
3
|
+
require 'active_support/core_ext/kernel/reporting'
|
4
|
+
|
5
|
+
module YamlDb
|
6
|
+
module Helper
|
7
|
+
def self.loader
|
8
|
+
YamlDb::Load
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.dumper
|
12
|
+
YamlDb::Dump
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.extension
|
16
|
+
"yml"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
module Utils
|
22
|
+
def self.chunk_records(records)
|
23
|
+
yaml = [ records ].to_yaml
|
24
|
+
yaml.sub!("--- \n", "")
|
25
|
+
yaml.sub!('- - -', ' - -')
|
26
|
+
yaml
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
class Dump < SerializationHelper::Dump
|
32
|
+
|
33
|
+
def self.dump_table_columns(io, table)
|
34
|
+
io.write("\n")
|
35
|
+
io.write({ table => { 'columns' => table_column_names(table) } }.to_yaml)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.dump_table_records(io, table)
|
39
|
+
table_record_header(io)
|
40
|
+
|
41
|
+
column_names = table_column_names(table)
|
42
|
+
|
43
|
+
each_table_page(table) do |records|
|
44
|
+
rows = SerializationHelper::Utils.unhash_records(records, column_names)
|
45
|
+
io.write(YamlDb::Utils.chunk_records(records))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.table_record_header(io)
|
50
|
+
io.write(" records: \n")
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
class Load < SerializationHelper::Load
|
56
|
+
def self.load_documents(io, truncate = true)
|
57
|
+
YAML.load_documents(io) do |ydoc|
|
58
|
+
ydoc.keys.each do |table_name|
|
59
|
+
next if ydoc[table_name].nil?
|
60
|
+
load_table(table_name, ydoc[table_name], truncate)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/base'
|
2
|
+
|
3
|
+
describe SerializationHelper::Base do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@io = StringIO.new
|
7
|
+
silence_warnings { ActiveRecord::Base = mock('ActiveRecord::Base', :null_object => true) }
|
8
|
+
ActiveRecord::Base.connection = mock('connection')
|
9
|
+
ActiveRecord::Base.connection.stub!(:tables).and_return([ 'mytable', 'schema_info', 'schema_migrations' ])
|
10
|
+
end
|
11
|
+
|
12
|
+
def stub_helper!
|
13
|
+
@helper = mock("MyHelper")
|
14
|
+
@dumper = mock("MyDumper");
|
15
|
+
@loader = mock("MyLoader");
|
16
|
+
@helper.stub!(:dumper).and_return(@dumper)
|
17
|
+
@helper.stub!(:loader).and_return(@loader)
|
18
|
+
@helper.stub!(:extension).and_return("yml")
|
19
|
+
@dumper.stub!(:tables).and_return([ActiveRecord::Base.connection.tables[0]])
|
20
|
+
@dumper.stub!(:before_table).and_return(nil)
|
21
|
+
@dumper.stub!(:after_table).and_return(nil)
|
22
|
+
end
|
23
|
+
|
24
|
+
context "for multi-file dumps" do
|
25
|
+
before do
|
26
|
+
File.should_receive(:new).once.with("dir_name/mytable.yml", "w").and_return(@io)
|
27
|
+
Dir.should_receive(:mkdir).once.with("dir_name")
|
28
|
+
stub_helper!
|
29
|
+
@dumper.should_receive(:dump_table).once.with(@io, "mytable")
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should create the number of files that there are tables" do
|
33
|
+
SerializationHelper::Base.new(@helper).dump_to_dir "dir_name"
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
context "for multi-file loads" do
|
39
|
+
|
40
|
+
before do
|
41
|
+
stub_helper!
|
42
|
+
@loader.should_receive(:load).once.with(@io, true)
|
43
|
+
File.should_receive(:new).once.with("dir_name/mytable.yml", "r").and_return(@io)
|
44
|
+
Dir.stub!(:entries).and_return(["mytable.yml"])
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should insert into then umber of tables that there are files" do
|
48
|
+
SerializationHelper::Base.new(@helper).load_from_dir "dir_name"
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/base'
|
2
|
+
|
3
|
+
describe SerializationHelper::Dump do
|
4
|
+
|
5
|
+
before do
|
6
|
+
silence_warnings { ActiveRecord::Base = mock('ActiveRecord::Base', :null_object => true) }
|
7
|
+
ActiveRecord::Base.connection = mock('connection')
|
8
|
+
ActiveRecord::Base.connection.stub!(:tables).and_return([ 'mytable', 'schema_info', 'schema_migrations' ])
|
9
|
+
ActiveRecord::Base.connection.stub!(:columns).with('mytable').and_return([ mock('a',:name => 'a'), mock('b', :name => 'b') ])
|
10
|
+
ActiveRecord::Base.connection.stub!(:select_one).and_return({"count"=>"2"})
|
11
|
+
ActiveRecord::Base.connection.stub!(:select_all).and_return([ { 'a' => 1, 'b' => 2 }, { 'a' => 3, 'b' => 4 } ])
|
12
|
+
SerializationHelper::Utils.stub!(:quote_table).with('mytable').and_return('mytable')
|
13
|
+
end
|
14
|
+
|
15
|
+
before(:each) do
|
16
|
+
File.stub!(:new).with('dump.yml', 'w').and_return(StringIO.new)
|
17
|
+
@io = StringIO.new
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should return a list of column names" do
|
21
|
+
SerializationHelper::Dump.table_column_names('mytable').should == [ 'a', 'b' ]
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should return a list of tables without the rails schema table" do
|
25
|
+
SerializationHelper::Dump.tables.should == ['mytable']
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should return the total number of records in a table" do
|
29
|
+
SerializationHelper::Dump.table_record_count('mytable').should == 2
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should return all records from the database and return them when there is only 1 page" do
|
33
|
+
SerializationHelper::Dump.each_table_page('mytable') do |records|
|
34
|
+
records.should == [ { 'a' => 1, 'b' => 2 }, { 'a' => 3, 'b' => 4 } ]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should paginate records from the database and return them" do
|
39
|
+
ActiveRecord::Base.connection.stub!(:select_all).and_return([ { 'a' => 1, 'b' => 2 } ], [ { 'a' => 3, 'b' => 4 } ])
|
40
|
+
|
41
|
+
records = [ ]
|
42
|
+
SerializationHelper::Dump.each_table_page('mytable', 1) do |page|
|
43
|
+
page.size.should == 1
|
44
|
+
records.concat(page)
|
45
|
+
end
|
46
|
+
|
47
|
+
records.should == [ { 'a' => 1, 'b' => 2 }, { 'a' => 3, 'b' => 4 } ]
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should dump a table's contents to yaml" do
|
51
|
+
SerializationHelper::Dump.should_receive(:dump_table_columns)
|
52
|
+
SerializationHelper::Dump.should_receive(:dump_table_records)
|
53
|
+
SerializationHelper::Dump.dump_table(@io, 'mytable')
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should not dump a table's contents when the record count is zero" do
|
57
|
+
SerializationHelper::Dump.stub!(:table_record_count).with('mytable').and_return(0)
|
58
|
+
SerializationHelper::Dump.should_not_receive(:dump_table_columns)
|
59
|
+
SerializationHelper::Dump.should_not_receive(:dump_table_records)
|
60
|
+
SerializationHelper::Dump.dump_table(@io, 'mytable')
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/base'
|
2
|
+
|
3
|
+
describe SerializationHelper::Load do
|
4
|
+
before do
|
5
|
+
SerializationHelper::Utils.stub!(:quote_table).with('mytable').and_return('mytable')
|
6
|
+
|
7
|
+
silence_warnings { ActiveRecord::Base = mock('ActiveRecord::Base', :null_object => true) }
|
8
|
+
ActiveRecord::Base.connection = mock('connection')
|
9
|
+
ActiveRecord::Base.connection.stub!(:transaction).and_yield
|
10
|
+
@io = StringIO.new
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should truncate the table" do
|
14
|
+
ActiveRecord::Base.connection.stub!(:execute).with("TRUNCATE mytable").and_return(true)
|
15
|
+
ActiveRecord::Base.connection.should_not_receive(:execute).with("DELETE FROM mytable")
|
16
|
+
SerializationHelper::Load.truncate_table('mytable')
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should delete the table if truncate throws an exception" do
|
20
|
+
ActiveRecord::Base.connection.should_receive(:execute).with("TRUNCATE mytable").and_raise()
|
21
|
+
ActiveRecord::Base.connection.should_receive(:execute).with("DELETE FROM mytable").and_return(true)
|
22
|
+
SerializationHelper::Load.truncate_table('mytable')
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
it "should call reset pk sequence if the connection adapter is postgres" do
|
27
|
+
ActiveRecord::Base.connection.should_receive(:respond_to?).with(:reset_pk_sequence!).and_return(true)
|
28
|
+
ActiveRecord::Base.connection.should_receive(:reset_pk_sequence!).with('mytable')
|
29
|
+
SerializationHelper::Load.reset_pk_sequence!('mytable')
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should not call reset pk sequence for other adapters" do
|
33
|
+
ActiveRecord::Base.connection.should_receive(:respond_to?).with(:reset_pk_sequence!).and_return(false)
|
34
|
+
ActiveRecord::Base.connection.should_not_receive(:reset_pk_sequence!)
|
35
|
+
SerializationHelper::Load.reset_pk_sequence!('mytable')
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should insert records into a table" do
|
39
|
+
mca = mock('a',:name => 'a')
|
40
|
+
mcb = mock('b', :name => 'b')
|
41
|
+
ActiveRecord::Base.connection.stub!(:columns).with('mytable').and_return([mca , mcb ])
|
42
|
+
ActiveRecord::Base.connection.stub!(:quote_column_name).with('a').and_return('a')
|
43
|
+
ActiveRecord::Base.connection.stub!(:quote_column_name).with('b').and_return('b')
|
44
|
+
ActiveRecord::Base.connection.stub!(:quote).with(1, mca).and_return("'1'")
|
45
|
+
ActiveRecord::Base.connection.stub!(:quote).with(2, mcb).and_return("'2'")
|
46
|
+
ActiveRecord::Base.connection.stub!(:quote).with(3, mca).and_return("'3'")
|
47
|
+
ActiveRecord::Base.connection.stub!(:quote).with(4, mcb).and_return("'4'")
|
48
|
+
ActiveRecord::Base.connection.should_receive(:execute).with("INSERT INTO mytable (a,b) VALUES ('1','2')")
|
49
|
+
ActiveRecord::Base.connection.should_receive(:execute).with("INSERT INTO mytable (a,b) VALUES ('3','4')")
|
50
|
+
|
51
|
+
SerializationHelper::Load.load_records('mytable', ['a', 'b'], [[1, 2], [3, 4]])
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should quote column names that correspond to sql keywords" do
|
55
|
+
mca = mock('a',:name => 'a')
|
56
|
+
mccount = mock('count', :name => 'count')
|
57
|
+
ActiveRecord::Base.connection.stub!(:columns).with('mytable').and_return([mca , mccount ])
|
58
|
+
ActiveRecord::Base.connection.stub!(:quote_column_name).with('a').and_return('a')
|
59
|
+
ActiveRecord::Base.connection.stub!(:quote_column_name).with('count').and_return('"count"')
|
60
|
+
ActiveRecord::Base.connection.stub!(:quote).with(1, mca).and_return("'1'")
|
61
|
+
ActiveRecord::Base.connection.stub!(:quote).with(2, mccount).and_return("'2'")
|
62
|
+
ActiveRecord::Base.connection.stub!(:quote).with(3, mca).and_return("'3'")
|
63
|
+
ActiveRecord::Base.connection.stub!(:quote).with(4, mccount).and_return("'4'")
|
64
|
+
ActiveRecord::Base.connection.should_receive(:execute).with("INSERT INTO mytable (a,\"count\") VALUES ('1','2')")
|
65
|
+
ActiveRecord::Base.connection.should_receive(:execute).with("INSERT INTO mytable (a,\"count\") VALUES ('3','4')")
|
66
|
+
|
67
|
+
SerializationHelper::Load.load_records('mytable', ['a', 'count'], [[1, 2], [3, 4]])
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should truncate the table and then load the records into the table" do
|
71
|
+
SerializationHelper::Load.should_receive(:truncate_table).with('mytable')
|
72
|
+
SerializationHelper::Load.should_receive(:load_records).with('mytable', ['a', 'b'], [[1, 2], [3, 4]])
|
73
|
+
SerializationHelper::Load.should_receive(:reset_pk_sequence!).with('mytable')
|
74
|
+
|
75
|
+
SerializationHelper::Load.load_table('mytable', { 'columns' => [ 'a', 'b' ], 'records' => [[1, 2], [3, 4]] })
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/base'
|
2
|
+
|
3
|
+
describe SerializationHelper::Utils, " convert records utility method" do
|
4
|
+
before do
|
5
|
+
silence_warnings { ActiveRecord::Base = mock('ActiveRecord::Base', :null_object => true) }
|
6
|
+
ActiveRecord::Base.connection = mock('connection')
|
7
|
+
end
|
8
|
+
|
9
|
+
it "returns an array of hash values using an array of ordered keys" do
|
10
|
+
SerializationHelper::Utils.unhash({ 'a' => 1, 'b' => 2 }, [ 'b', 'a' ]).should == [ 2, 1 ]
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should unhash each hash an array using an array of ordered keys" do
|
14
|
+
SerializationHelper::Utils.unhash_records([ { 'a' => 1, 'b' => 2 }, { 'a' => 3, 'b' => 4 } ], [ 'b', 'a' ]).should == [ [ 2, 1 ], [ 4, 3 ] ]
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should return true if it is a boolean type" do
|
18
|
+
SerializationHelper::Utils.is_boolean(true).should == true
|
19
|
+
SerializationHelper::Utils.is_boolean('true').should_not == true
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should return an array of boolean columns" do
|
23
|
+
ActiveRecord::Base.connection.stub!(:columns).with('mytable').and_return([ mock('a',:name => 'a',:type => :string), mock('b', :name => 'b',:type => :boolean) ])
|
24
|
+
SerializationHelper::Utils.boolean_columns('mytable').should == ['b']
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should quote the table name" do
|
28
|
+
ActiveRecord::Base.connection.should_receive(:quote_table_name).with('values').and_return('`values`')
|
29
|
+
SerializationHelper::Utils.quote_table('values').should == '`values`'
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/base'
|
2
|
+
|
3
|
+
describe YamlDb::Dump do
|
4
|
+
|
5
|
+
before do
|
6
|
+
silence_warnings { ActiveRecord::Base = mock('ActiveRecord::Base', :null_object => true) }
|
7
|
+
ActiveRecord::Base.connection = mock('connection')
|
8
|
+
ActiveRecord::Base.connection.stub!(:tables).and_return([ 'mytable', 'schema_info', 'schema_migrations' ])
|
9
|
+
ActiveRecord::Base.connection.stub!(:columns).with('mytable').and_return([ mock('a',:name => 'a'), mock('b', :name => 'b') ])
|
10
|
+
ActiveRecord::Base.connection.stub!(:select_one).and_return({"count"=>"2"})
|
11
|
+
ActiveRecord::Base.connection.stub!(:select_all).and_return([ { 'a' => 1, 'b' => 2 }, { 'a' => 3, 'b' => 4 } ])
|
12
|
+
YamlDb::Utils.stub!(:quote_table).with('mytable').and_return('mytable')
|
13
|
+
end
|
14
|
+
|
15
|
+
before(:each) do
|
16
|
+
File.stub!(:new).with('dump.yml', 'w').and_return(StringIO.new)
|
17
|
+
@io = StringIO.new
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should return a formatted string" do
|
21
|
+
YamlDb::Dump.table_record_header(@io)
|
22
|
+
@io.rewind
|
23
|
+
@io.read.should == " records: \n"
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
it "should return a yaml string that contains a table header and column names" do
|
28
|
+
YamlDb::Dump.stub!(:table_column_names).with('mytable').and_return([ 'a', 'b' ])
|
29
|
+
YamlDb::Dump.dump_table_columns(@io, 'mytable')
|
30
|
+
@io.rewind
|
31
|
+
@io.read.should == <<EOYAML
|
32
|
+
|
33
|
+
---
|
34
|
+
mytable:
|
35
|
+
columns:
|
36
|
+
- a
|
37
|
+
- b
|
38
|
+
EOYAML
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should return dump the records for a table in yaml to a given io stream" do
|
42
|
+
YamlDb::Dump.dump_table_records(@io, 'mytable')
|
43
|
+
@io.rewind
|
44
|
+
@io.read.should == <<EOYAML
|
45
|
+
records:
|
46
|
+
- - 1
|
47
|
+
- 2
|
48
|
+
- - 3
|
49
|
+
- 4
|
50
|
+
EOYAML
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/base'
|
2
|
+
require 'active_support/core_ext/kernel/debugger'
|
3
|
+
|
4
|
+
describe YamlDb::Load do
|
5
|
+
before do
|
6
|
+
SerializationHelper::Utils.stub!(:quote_table).with('mytable').and_return('mytable')
|
7
|
+
|
8
|
+
silence_warnings { ActiveRecord::Base = mock('ActiveRecord::Base', :null_object => true) }
|
9
|
+
ActiveRecord::Base.connection = mock('connection')
|
10
|
+
ActiveRecord::Base.connection.stub!(:transaction).and_yield
|
11
|
+
end
|
12
|
+
|
13
|
+
before(:each) do
|
14
|
+
@io = StringIO.new
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
it "should call load structure for each document in the file" do
|
19
|
+
YAML.should_receive(:load_documents).with(@io).and_yield({ 'mytable' => {
|
20
|
+
'columns' => [ 'a', 'b' ],
|
21
|
+
'records' => [[1, 2], [3, 4]]
|
22
|
+
} } )
|
23
|
+
YamlDb::Load.should_receive(:load_table).with('mytable', { 'columns' => [ 'a', 'b' ], 'records' => [[1, 2], [3, 4]] },true)
|
24
|
+
YamlDb::Load.load(@io)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should not call load structure when the document in the file contains no records" do
|
28
|
+
YAML.should_receive(:load_documents).with(@io).and_yield({ 'mytable' => nil })
|
29
|
+
YamlDb::Load.should_not_receive(:load_table)
|
30
|
+
YamlDb::Load.load(@io)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/base'
|
2
|
+
|
3
|
+
describe YamlDb::Utils, " convert records utility method" do
|
4
|
+
|
5
|
+
it "turns an array with one record into a yaml chunk" do
|
6
|
+
YamlDb::Utils.chunk_records([ %w(a b) ]).should == <<EOYAML
|
7
|
+
- - a
|
8
|
+
- b
|
9
|
+
EOYAML
|
10
|
+
end
|
11
|
+
|
12
|
+
it "turns an array with two records into a yaml chunk" do
|
13
|
+
YamlDb::Utils.chunk_records([ %w(a b), %w(x y) ]).should == <<EOYAML
|
14
|
+
- - a
|
15
|
+
- b
|
16
|
+
- - x
|
17
|
+
- y
|
18
|
+
EOYAML
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: molo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 7
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 6
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.6.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Joel Moss
|
@@ -17,7 +17,7 @@ autorequire:
|
|
17
17
|
bindir: bin
|
18
18
|
cert_chain: []
|
19
19
|
|
20
|
-
date: 2010-12-
|
20
|
+
date: 2010-12-14 00:00:00 +00:00
|
21
21
|
default_executable:
|
22
22
|
dependencies:
|
23
23
|
- !ruby/object:Gem::Dependency
|
@@ -73,6 +73,18 @@ files:
|
|
73
73
|
- vendor/migration_helpers/README.markdown
|
74
74
|
- vendor/migration_helpers/init.rb
|
75
75
|
- vendor/migration_helpers/lib/migration_helper.rb
|
76
|
+
- vendor/yaml_db/init.rb
|
77
|
+
- vendor/yaml_db/lib/csv_db.rb
|
78
|
+
- vendor/yaml_db/lib/serialization_helper.rb
|
79
|
+
- vendor/yaml_db/lib/yaml_db.rb
|
80
|
+
- vendor/yaml_db/spec/base.rb
|
81
|
+
- vendor/yaml_db/spec/serialization_helper_base_spec.rb
|
82
|
+
- vendor/yaml_db/spec/serialization_helper_dump_spec.rb
|
83
|
+
- vendor/yaml_db/spec/serialization_helper_load_spec.rb
|
84
|
+
- vendor/yaml_db/spec/serialization_utils_spec.rb
|
85
|
+
- vendor/yaml_db/spec/yaml_dump_spec.rb
|
86
|
+
- vendor/yaml_db/spec/yaml_load_spec.rb
|
87
|
+
- vendor/yaml_db/spec/yaml_utils_spec.rb
|
76
88
|
has_rdoc: true
|
77
89
|
homepage: http://codaset.com/joelmoss/molo
|
78
90
|
licenses: []
|