提交 f02d2185 编写于 作者: J Josh Susser

Add migration history to schema.rb dump

上级 0a5afa22
## Rails 4.0.0 (unreleased) ##
* Add migration history to schema.rb dump.
Loading schema.rb with full migration history
restores the exact list of migrations that created
that schema (including names and fingerprints). This
avoids possible mistakes caused by assuming all
migrations with a lower version have been run when
loading schema.rb. Old schema.rb files without migration
history but with the :version setting still work as before.
*Josh Susser*
* Add metadata columns to schema_migrations table.
New columns are: migrated_at (timestamp),
fingerprint (md5 hash of migration source), and
......
......@@ -490,8 +490,8 @@ def dump_schema_information #:nodoc:
sm_table = ActiveRecord::Migrator.schema_migrations_table_name
ActiveRecord::SchemaMigration.order('version').map { |sm|
"INSERT INTO #{sm_table} (version, migrated_at, name) VALUES ('#{sm.version}',LOCALTIMESTAMP,'#{sm.name}');"
}.join "\n\n"
"INSERT INTO #{sm_table} (version, migrated_at, fingerprint, name) VALUES ('#{sm.version}',LOCALTIMESTAMP,'#{sm.fingerprint}','#{sm.name}');"
}.join("\n\n")
end
# Should not be called normally, but this operation is non-destructive.
......
......@@ -34,27 +34,45 @@ def migrations_paths
end
def define(info, &block)
@using_deprecated_version_setting = info[:version].present?
SchemaMigration.drop_table
initialize_schema_migrations_table
instance_eval(&block)
unless info[:version].blank?
initialize_schema_migrations_table
assume_migrated_upto_version(info[:version], migrations_paths)
end
# handle files from pre-4.0 that used :version option instead of dumping migration table
assume_migrated_upto_version(info[:version], migrations_paths) if @using_deprecated_version_setting
end
# Eval the given block. All methods available to the current connection
# adapter are available within the block, so you can easily use the
# database definition DSL to build up your schema (+create_table+,
# +add_index+, etc.).
#
# The +info+ hash is optional, and if given is used to define metadata
# about the current schema (currently, only the schema's version):
#
# ActiveRecord::Schema.define(version: 20380119000001) do
# ...
# end
def self.define(info={}, &block)
new.define(info, &block)
end
# Create schema migration history. Include migration statements in a block to this method.
#
# migrations do
# migration 20121128235959, "44f1397e3b92442ca7488a029068a5ad", "add_horn_color_to_unicorns"
# migration 20121129235959, "4a1eb3965d94406b00002b370854eae8", "add_magic_power_to_unicorns"
# end
def migrations
raise(ArgumentError, "Can't set migrations while using :version option") if @using_deprecated_version_setting
yield
end
# Add a migration to the ActiveRecord::SchemaMigration table.
#
# The +version+ argument is an integer.
# The +fingerprint+ and +name+ arguments are required but may be empty strings.
# The migration's +migrated_at+ attribute is set to the current time,
# instead of being set explicitly as an argument to the method.
#
# migration 20121129235959, "4a1eb3965d94406b00002b370854eae8", "add_magic_power_to_unicorns"
def migration(version, fingerprint, name)
SchemaMigration.create!(version: version, migrated_at: Time.now, fingerprint: fingerprint, name: name)
end
end
end
......@@ -24,6 +24,7 @@ def self.dump(connection=ActiveRecord::Base.connection, stream=STDOUT)
def dump(stream)
header(stream)
migrations(stream)
tables(stream)
trailer(stream)
stream
......@@ -44,7 +45,7 @@ def header(stream)
stream.puts "# encoding: #{stream.external_encoding.name}"
end
stream.puts <<HEADER
header_text = <<HEADER_RUBY
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
......@@ -59,13 +60,25 @@ def header(stream)
ActiveRecord::Schema.define(#{define_params}) do
HEADER
HEADER_RUBY
stream.puts header_text
end
def trailer(stream)
stream.puts "end"
end
def migrations(stream)
all_migrations = ActiveRecord::SchemaMigration.all.to_a
if all_migrations.any?
stream.puts(" migrations do")
all_migrations.each do |migration|
stream.puts(migration.schema_line(" "))
end
stream.puts(" end")
end
end
def tables(stream)
@connection.tables.sort.each do |tbl|
next if ['schema_migrations', ignore_tables].flatten.any? do |ignored|
......
......@@ -53,5 +53,17 @@ def self.drop_table
def version
super.to_i
end
# Construct ruby source to include in schema.rb dump for this migration.
# Pass a string of spaces as +indent+ to allow calling code to control how deeply indented the line is.
# The generated line includes the migration version, fingerprint, and name. Either fingerprint or name
# can be an empty string.
#
# Example output:
#
# migration 20121129235959, "ee4be703f9e6e2fc0f4baddebe6eb8f7", "add_magic_power_to_unicorns"
def schema_line(indent)
%Q(#{indent}migration %s, "%s", "%s") % [version, fingerprint, name]
end
end
end
......@@ -46,4 +46,55 @@ def test_schema_subclass
end
end
class ActiveRecordSchemaMigrationsTest < ActiveRecordSchemaTest
def setup
super
ActiveRecord::SchemaMigration.delete_all
end
def test_migration_adds_row_to_migrations_table
schema = ActiveRecord::Schema.new
schema.migration(1001, "", "")
schema.migration(1002, "123456789012345678901234567890ab", "add_magic_power_to_unicorns")
migrations = ActiveRecord::SchemaMigration.all.to_a
assert_equal 2, migrations.length
assert_equal 1001, migrations[0].version
assert_match %r{^2\d\d\d-}, migrations[0].migrated_at.to_s(:db)
assert_equal "", migrations[0].fingerprint
assert_equal "", migrations[0].name
assert_equal 1002, migrations[1].version
assert_match %r{^2\d\d\d-}, migrations[1].migrated_at.to_s(:db)
assert_equal "123456789012345678901234567890ab", migrations[1].fingerprint
assert_equal "add_magic_power_to_unicorns", migrations[1].name
end
def test_define_clears_schema_migrations
assert_nothing_raised do
ActiveRecord::Schema.define do
migrations do
migration(123001, "", "")
end
end
ActiveRecord::Schema.define do
migrations do
migration(123001, "", "")
end
end
end
end
def test_define_raises_if_both_version_and_explicit_migrations
assert_raise(ArgumentError) do
ActiveRecord::Schema.define(version: 123001) do
migrations do
migration(123001, "", "")
end
end
end
end
end
end
require "cases/helper"
# require "cases/migration/helper"
class SchemaDumperTest < ActiveRecord::TestCase
def setup
......@@ -18,11 +18,15 @@ def standard_dump
def test_dump_schema_information_outputs_lexically_ordered_versions
versions = %w{ 20100101010101 20100201010101 20100301010101 }
versions.reverse.each do |v|
ActiveRecord::SchemaMigration.create!(:version => v, :name => "anon", :migrated_at => Time.now)
ActiveRecord::SchemaMigration.create!(
:version => v, :migrated_at => Time.now,
:fingerprint => "123456789012345678901234567890ab", :name => "anon")
end
schema_info = ActiveRecord::Base.connection.dump_schema_information
assert_match(/20100201010101.*20100301010101/m, schema_info)
target_line = %q{INSERT INTO schema_migrations (version, migrated_at, fingerprint, name) VALUES ('20100101010101',LOCALTIMESTAMP,'123456789012345678901234567890ab','anon');}
assert_match target_line, schema_info
end
def test_magic_comment
......@@ -36,6 +40,16 @@ def test_schema_dump
assert_no_match %r{create_table "schema_migrations"}, output
end
def test_schema_dump_includes_migrations
ActiveRecord::SchemaMigration.delete_all
ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/always_safe")
output = standard_dump
assert_match %r{migrations do}, output, "Missing migrations block"
assert_match %r{migration 1001, "[0-9a-f]{32}", "always_safe"}, output, "Missing migration line"
assert_match %r{migration 1002, "[0-9a-f]{32}", "still_safe"}, output, "Missing migration line"
end
def test_schema_dump_excludes_sqlite_sequence
output = standard_dump
assert_no_match %r{create_table "sqlite_sequence"}, output
......
class AlwaysSafe < ActiveRecord::Migration
def change
# do nothing to avoid side-effect conflicts from running multiple times
end
end
class StillSafe < ActiveRecord::Migration
def change
# do nothing to avoid side-effect conflicts from running multiple times
end
end
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册