class ActiveRecord::SchemaDumper
:nodoc:
output format (i.e., ActiveRecord::Schema).
This class is used to dump the database schema for some connection to some
def self.dump(connection=ActiveRecord::Base.connection, stream=STDOUT)
def self.dump(connection=ActiveRecord::Base.connection, stream=STDOUT) new(connection).dump(stream) stream end
def default_string(value)
def default_string(value) case value when BigDecimal value.to_s when Date, DateTime, Time "'" + value.to_s(:db) + "'" else value.inspect end end
def dump(stream)
def dump(stream) header(stream) tables(stream) trailer(stream) stream end
def header(stream)
def header(stream) define_params = @version ? ":version => #{@version}" : "" stream.puts <<HEADER file is auto-generated from the current state of the database. Instead of editing this file, se use the migrations feature of Active Record to incrementally modify your database, and regenerate this schema definition. that this schema.rb definition is the authoritative source for your database schema. If you need reate the application database on another system, you should be using db:schema:load, not running the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations ll amass, the slower it'll run and the greater likelihood for issues). strongly recommended to check this file into your version control system. Record::Schema.define(#{define_params}) do end
def indexes(table, stream)
def indexes(table, stream) if (indexes = @connection.indexes(table)).any? add_index_statements = indexes.map do |index| statment_parts = [ ('add_index ' + index.table.inspect) ] statment_parts << index.columns.inspect statment_parts << (':name => ' + index.name.inspect) statment_parts << ':unique => true' if index.unique index_lengths = index.lengths.compact if index.lengths.is_a?(Array) statment_parts << (':length => ' + Hash[*index.columns.zip(index.lengths).flatten].inspect) if index_lengths.present? ' ' + statment_parts.join(', ') end stream.puts add_index_statements.sort.join("\n") stream.puts end end
def initialize(connection)
def initialize(connection) @connection = connection @types = @connection.native_database_types @version = Migrator::current_version rescue nil end
def table(table, stream)
def table(table, stream) columns = @connection.columns(table) begin tbl = StringIO.new # first dump primary key column if @connection.respond_to?(:pk_and_sequence_for) pk, pk_seq = @connection.pk_and_sequence_for(table) elsif @connection.respond_to?(:primary_key) pk = @connection.primary_key(table) end tbl.print " create_table #{table.inspect}" if columns.detect { |c| c.name == pk } if pk != 'id' tbl.print %Q(, :primary_key => "#{pk}") end else tbl.print ", :id => false" end tbl.print ", :force => true" tbl.puts " do |t|" # then dump all non-primary key columns column_specs = columns.map do |column| raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil? next if column.name == pk spec = {} spec[:name] = column.name.inspect spec[:type] = column.type.to_s spec[:limit] = column.limit.inspect if column.limit != @types[column.type][:limit] && column.type != :decimal spec[:precision] = column.precision.inspect if !column.precision.nil? spec[:scale] = column.scale.inspect if !column.scale.nil? spec[:null] = 'false' if !column.null spec[:default] = default_string(column.default) if column.has_default? (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.inspect} => ")} spec end.compact # find all migration keys used in this table keys = [:name, :limit, :precision, :scale, :default, :null] & column_specs.map(&:keys).flatten # figure out the lengths for each column based on above keys lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max } # the string we're going to sprintf our values against, with standardized column widths format_string = lengths.map{ |len| "%-#{len}s" } # find the max length for the 'type' column, which is special type_length = column_specs.map{ |column| column[:type].length }.max # add column type definition to our format string format_string.unshift " t.%-#{type_length}s " format_string *= '' column_specs.each do |colspec| values = keys.zip(lengths).map{ |key, len| colspec.key?(key) ? colspec[key] + ", " : " " * len } values.unshift colspec[:type] tbl.print((format_string % values).gsub(/,\s*$/, '')) tbl.puts end tbl.puts " end" tbl.puts indexes(table, tbl) tbl.rewind stream.print tbl.read rescue => e stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}" stream.puts "# #{e.message}" stream.puts end stream end
def tables(stream)
def tables(stream) @connection.tables.sort.each do |tbl| next if ['schema_migrations', ignore_tables].flatten.any? do |ignored| case ignored when String; tbl == ignored when Regexp; tbl =~ ignored else raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.' end end table(tbl, stream) end end
def trailer(stream)
def trailer(stream) stream.puts "end" end