3 require 'active_record'
7 def self.dump(filename)
9 YamlDb::Dump.dump(File.new(filename, "w"))
13 def self.load(filename)
15 YamlDb::Load.load(File.new(filename, "r"))
19 def self.disable_logger
20 @@old_logger = ActiveRecord::Base.logger
21 ActiveRecord::Base.logger = nil
24 def self.reenable_logger
25 ActiveRecord::Base.logger = @@old_logger
31 def self.chunk_records(records)
32 yaml = [ records ].to_yaml
33 yaml.sub!("--- \n", "")
34 yaml.sub!('- - -', ' - -')
38 def self.unhash(hash, keys)
39 keys.map { |key| hash[key] }
42 def self.unhash_records(records, keys)
43 records.each_with_index do |record, index|
44 records[index] = unhash(record, keys)
50 def self.convert_booleans(records, columns)
51 records.each do |record|
52 columns.each do |column|
53 next if is_boolean(record[column])
54 record[column] = (record[column] == 't' or record[column] == '1')
60 def self.boolean_columns(table)
61 columns = ActiveRecord::Base.connection.columns(table).reject { |c| c.type != :boolean }
62 columns.map { |c| c.name }
65 def self.is_boolean(value)
66 value.kind_of?(TrueClass) or value.kind_of?(FalseClass)
69 def self.quote_table(table)
70 ActiveRecord::Base.connection.quote_table_name(table)
77 tables.each do |table|
83 ActiveRecord::Base.connection.tables.reject { |table| ['schema_info', 'schema_migrations'].include?(table) }
86 def self.dump_table(io, table)
87 return if table_record_count(table).zero?
89 dump_table_columns(io, table)
90 dump_table_records(io, table)
93 def self.dump_table_columns(io, table)
95 io.write({ table => { 'columns' => table_column_names(table) } }.to_yaml)
98 def self.dump_table_records(io, table)
99 table_record_header(io)
101 column_names = table_column_names(table)
103 each_table_page(table) do |records|
104 rows = YamlDb::Utils.unhash_records(records, column_names)
105 io.write(YamlDb::Utils.chunk_records(records))
109 def self.table_record_header(io)
110 io.write(" records: \n")
113 def self.table_column_names(table)
114 ActiveRecord::Base.connection.columns(table).map { |c| c.name }
117 def self.each_table_page(table, records_per_page=1000)
118 total_count = table_record_count(table)
119 pages = (total_count.to_f / records_per_page).ceil - 1
120 id = table_column_names(table).first
121 boolean_columns = YamlDb::Utils.boolean_columns(table)
122 quoted_table_name = YamlDb::Utils.quote_table(table)
124 (0..pages).to_a.each do |page|
125 sql = ActiveRecord::Base.connection.add_limit_offset!("SELECT * FROM #{quoted_table_name} ORDER BY #{id}",
126 :limit => records_per_page, :offset => records_per_page * page
128 records = ActiveRecord::Base.connection.select_all(sql)
129 records = YamlDb::Utils.convert_booleans(records, boolean_columns)
134 def self.table_record_count(table)
135 ActiveRecord::Base.connection.select_one("SELECT COUNT(*) FROM #{YamlDb::Utils.quote_table(table)}").values.first.to_i
142 ActiveRecord::Base.connection.transaction do
143 YAML.load_documents(io) do |ydoc|
144 ydoc.keys.each do |table_name|
145 next if ydoc[table_name].nil?
146 load_table(table_name, ydoc[table_name])
152 def self.truncate_table(table)
154 ActiveRecord::Base.connection.execute("TRUNCATE #{YamlDb::Utils.quote_table(table)}")
156 ActiveRecord::Base.connection.execute("DELETE FROM #{YamlDb::Utils.quote_table(table)}")
160 def self.load_table(table, data)
161 column_names = data['columns']
162 truncate_table(table)
163 load_records(table, column_names, data['records'])
164 reset_pk_sequence!(table)
167 def self.load_records(table, column_names, records)
168 quoted_column_names = column_names.map { |column| ActiveRecord::Base.connection.quote_column_name(column) }.join(',')
169 quoted_table_name = YamlDb::Utils.quote_table(table)
170 records.each do |record|
171 ActiveRecord::Base.connection.execute("INSERT INTO #{quoted_table_name} (#{quoted_column_names}) VALUES (#{record.map { |r| ActiveRecord::Base.connection.quote(r) }.join(',')})")
175 def self.reset_pk_sequence!(table_name)
176 if ActiveRecord::Base.connection.respond_to?(:reset_pk_sequence!)
177 ActiveRecord::Base.connection.reset_pk_sequence!(table_name)