added yaml_db plugin
[selectricity-live] / vendor / plugins / yaml_db / lib / yaml_db.rb
diff --git a/vendor/plugins/yaml_db/lib/yaml_db.rb b/vendor/plugins/yaml_db/lib/yaml_db.rb
new file mode 100644 (file)
index 0000000..1cd485e
--- /dev/null
@@ -0,0 +1,180 @@
+require 'rubygems'
+require 'yaml'
+require 'active_record'
+
+
+module YamlDb
+       def self.dump(filename)
+               disable_logger
+               YamlDb::Dump.dump(File.new(filename, "w"))
+               reenable_logger
+       end
+
+       def self.load(filename)
+               disable_logger
+               YamlDb::Load.load(File.new(filename, "r"))
+               reenable_logger
+       end
+
+       def self.disable_logger
+               @@old_logger = ActiveRecord::Base.logger
+               ActiveRecord::Base.logger = nil
+       end
+
+       def self.reenable_logger
+               ActiveRecord::Base.logger = @@old_logger
+       end
+end
+
+
+module YamlDb::Utils
+       def self.chunk_records(records)
+               yaml = [ records ].to_yaml
+               yaml.sub!("--- \n", "")
+               yaml.sub!('- - -', '  - -')
+               yaml
+       end
+
+       def self.unhash(hash, keys)
+               keys.map { |key| hash[key] }
+       end
+
+       def self.unhash_records(records, keys)
+               records.each_with_index do |record, index|
+                       records[index] = unhash(record, keys)   
+               end
+               
+               records
+       end
+
+       def self.convert_booleans(records, columns)
+               records.each do |record|
+                       columns.each do |column|
+                               next if is_boolean(record[column])
+                               record[column] = (record[column] == 't' or record[column] == '1')
+                       end
+               end
+               records
+       end
+
+       def self.boolean_columns(table)
+               columns = ActiveRecord::Base.connection.columns(table).reject { |c| c.type != :boolean }
+               columns.map { |c| c.name }
+       end
+
+       def self.is_boolean(value)
+               value.kind_of?(TrueClass) or value.kind_of?(FalseClass)
+       end
+
+       def self.quote_table(table)
+               ActiveRecord::Base.connection.quote_table_name(table)
+       end
+end
+
+
+module YamlDb::Dump
+       def self.dump(io)
+               tables.each do |table|
+                       dump_table(io, table)
+               end
+       end
+
+       def self.tables
+               ActiveRecord::Base.connection.tables.reject { |table| ['schema_info', 'schema_migrations'].include?(table) }
+       end
+
+       def self.dump_table(io, table)
+               return if table_record_count(table).zero?
+
+               dump_table_columns(io, table)
+               dump_table_records(io, table)
+       end
+
+       def self.dump_table_columns(io, table)
+               io.write("\n")
+               io.write({ table => { 'columns' => table_column_names(table) } }.to_yaml)
+       end
+
+       def self.dump_table_records(io, table)
+               table_record_header(io) 
+       
+               column_names = table_column_names(table)
+
+               each_table_page(table) do |records|
+                       rows = YamlDb::Utils.unhash_records(records, column_names)
+                       io.write(YamlDb::Utils.chunk_records(records))
+               end
+       end
+
+       def self.table_record_header(io)
+               io.write("  records: \n")
+       end
+
+       def self.table_column_names(table)
+               ActiveRecord::Base.connection.columns(table).map { |c| c.name }
+       end
+
+       def self.each_table_page(table, records_per_page=1000)
+               total_count = table_record_count(table)
+               pages = (total_count.to_f / records_per_page).ceil - 1
+               id = table_column_names(table).first
+               boolean_columns = YamlDb::Utils.boolean_columns(table)
+               quoted_table_name = YamlDb::Utils.quote_table(table)
+               
+               (0..pages).to_a.each do |page|
+                       sql = ActiveRecord::Base.connection.add_limit_offset!("SELECT * FROM #{quoted_table_name} ORDER BY #{id}",
+                               :limit => records_per_page, :offset => records_per_page * page
+                       )
+                       records = ActiveRecord::Base.connection.select_all(sql)
+                       records = YamlDb::Utils.convert_booleans(records, boolean_columns)
+                       yield records
+               end
+       end
+
+       def self.table_record_count(table)
+               ActiveRecord::Base.connection.select_one("SELECT COUNT(*) FROM #{YamlDb::Utils.quote_table(table)}").values.first.to_i
+       end
+end
+
+
+module YamlDb::Load
+       def self.load(io)
+               ActiveRecord::Base.connection.transaction do
+                       YAML.load_documents(io) do |ydoc|
+                               ydoc.keys.each do |table_name|
+                                       next if ydoc[table_name].nil?
+                                       load_table(table_name, ydoc[table_name])
+                               end
+                       end
+               end
+       end
+
+       def self.truncate_table(table)
+               begin
+                       ActiveRecord::Base.connection.execute("TRUNCATE #{YamlDb::Utils.quote_table(table)}")
+               rescue Exception
+                       ActiveRecord::Base.connection.execute("DELETE FROM #{YamlDb::Utils.quote_table(table)}")
+               end
+       end
+
+       def self.load_table(table, data)
+               column_names = data['columns']
+               truncate_table(table)
+               load_records(table, column_names, data['records'])
+               reset_pk_sequence!(table)
+       end
+
+       def self.load_records(table, column_names, records)
+               quoted_column_names = column_names.map { |column| ActiveRecord::Base.connection.quote_column_name(column) }.join(',')
+               quoted_table_name = YamlDb::Utils.quote_table(table)
+               records.each do |record|
+                       ActiveRecord::Base.connection.execute("INSERT INTO #{quoted_table_name} (#{quoted_column_names}) VALUES (#{record.map { |r| ActiveRecord::Base.connection.quote(r) }.join(',')})")
+               end
+       end
+
+       def self.reset_pk_sequence!(table_name)
+               if ActiveRecord::Base.connection.respond_to?(:reset_pk_sequence!)
+                       ActiveRecord::Base.connection.reset_pk_sequence!(table_name)
+               end
+       end
+end

Benjamin Mako Hill || Want to submit a patch?