aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorJosé Valim <jose.valim@gmail.com>2010-09-04 00:31:35 +0200
committerJosé Valim <jose.valim@gmail.com>2010-09-04 00:31:43 +0200
commit23a9455962f0362cf242ffa96db7a9e7fdb0804b (patch)
treec046d1285d078649db6833fa1b73a6c23f7f16ea /activerecord
parent63032c1162e96d3380168ca63ac42aa044dbebd6 (diff)
parentc3c1a1e14859e6716970283caeab0c4c3720862e (diff)
downloadrails-23a9455962f0362cf242ffa96db7a9e7fdb0804b.tar.gz
rails-23a9455962f0362cf242ffa96db7a9e7fdb0804b.tar.bz2
rails-23a9455962f0362cf242ffa96db7a9e7fdb0804b.zip
This commit merges most of the work done by Piotr Sarnacki in his Ruby Summer of Code project.
His work brings several capabilities from app to engines, as routes, middleware stack, asset handling and much more. Please check Rails::Engine documentation for more refenrences. Merge remote branch 'drogus/engines'
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/lib/active_record/migration.rb96
-rw-r--r--activerecord/lib/active_record/railties/databases.rake35
-rw-r--r--activerecord/lib/rails/generators/active_record.rb6
-rw-r--r--activerecord/test/cases/helper.rb18
-rw-r--r--activerecord/test/cases/migration_test.rb125
-rw-r--r--activerecord/test/migrations/empty/.gitkeep0
-rw-r--r--activerecord/test/migrations/to_copy/1_people_have_hobbies.rb9
-rw-r--r--activerecord/test/migrations/to_copy/2_people_have_descriptions.rb9
-rw-r--r--activerecord/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb9
-rw-r--r--activerecord/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb9
-rw-r--r--activerecord/test/migrations/valid_with_timestamps/20100101010101_people_have_last_names.rb9
-rw-r--r--activerecord/test/migrations/valid_with_timestamps/20100201010101_we_need_reminders.rb12
-rw-r--r--activerecord/test/migrations/valid_with_timestamps/20100301010101_innocent_jointable.rb12
13 files changed, 314 insertions, 35 deletions
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 198f0a18cb..e708b3fbcf 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -383,6 +383,37 @@ module ActiveRecord
connection.send(method, *arguments, &block)
end
end
+
+ def copy(destination, sources)
+ copied = []
+
+ sources.each do |scope, path|
+ destination_migrations = ActiveRecord::Migrator.migrations(destination)
+ source_migrations = ActiveRecord::Migrator.migrations(path)
+ last = destination_migrations.last
+
+ source_migrations.each do |migration|
+ next if destination_migrations.any? { |m| m.name == migration.name && m.scope == scope.to_s }
+
+ migration.version = next_migration_number(last ? last.version + 1 : 0).to_i
+ last = migration
+
+ new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.#{scope}.rb")
+ FileUtils.cp(migration.filename, new_path)
+ copied << new_path
+ end
+ end
+
+ copied
+ end
+
+ def next_migration_number(number)
+ if ActiveRecord::Base.timestamped_migrations
+ [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % number].max
+ else
+ "%.3d" % number
+ end
+ end
end
end
@@ -390,7 +421,7 @@ module ActiveRecord
# until they are needed
class MigrationProxy
- attr_accessor :name, :version, :filename
+ attr_accessor :name, :version, :filename, :scope
delegate :migrate, :announce, :write, :to=>:migration
@@ -409,6 +440,8 @@ module ActiveRecord
class Migrator#:nodoc:
class << self
+ attr_writer :migrations_path
+
def migrate(migrations_path, target_version = nil)
case
when target_version.nil?
@@ -441,10 +474,6 @@ module ActiveRecord
self.new(direction, migrations_path, target_version).run
end
- def migrations_path
- 'db/migrate'
- end
-
def schema_migrations_table_name
Base.table_name_prefix + 'schema_migrations' + Base.table_name_suffix
end
@@ -468,6 +497,38 @@ module ActiveRecord
name.table_name rescue "#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}"
end
+ def migrations_path
+ @migrations_path ||= 'db/migrate'
+ end
+
+ def migrations(path)
+ files = Dir["#{path}/[0-9]*_*.rb"]
+
+ migrations = files.inject([]) do |klasses, file|
+ version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?.rb/).first
+
+ raise IllegalMigrationNameError.new(file) unless version
+ version = version.to_i
+
+ if klasses.detect { |m| m.version == version }
+ raise DuplicateMigrationVersionError.new(version)
+ end
+
+ if klasses.detect { |m| m.name == name.camelize && m.scope == scope }
+ raise DuplicateMigrationNameError.new(name.camelize)
+ end
+
+ migration = MigrationProxy.new
+ migration.name = name.camelize
+ migration.version = version
+ migration.filename = file
+ migration.scope = scope
+ klasses << migration
+ end
+
+ migrations.sort_by(&:version)
+ end
+
private
def move(direction, migrations_path, steps)
@@ -546,30 +607,7 @@ module ActiveRecord
def migrations
@migrations ||= begin
- files = Dir["#{@migrations_path}/[0-9]*_*.rb"]
-
- migrations = files.inject([]) do |klasses, file|
- version, name = file.scan(/([0-9]+)_([_a-z0-9]*).rb/).first
-
- raise IllegalMigrationNameError.new(file) unless version
- version = version.to_i
-
- if klasses.detect { |m| m.version == version }
- raise DuplicateMigrationVersionError.new(version)
- end
-
- if klasses.detect { |m| m.name == name.camelize }
- raise DuplicateMigrationNameError.new(name.camelize)
- end
-
- migration = MigrationProxy.new
- migration.name = name.camelize
- migration.version = version
- migration.filename = file
- klasses << migration
- end
-
- migrations = migrations.sort_by { |m| m.version }
+ migrations = self.class.migrations(@migrations_path)
down? ? migrations.reverse : migrations
end
end
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index b1aad0d496..aedda26ba5 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -2,6 +2,29 @@ namespace :db do
task :load_config => :rails_env do
require 'active_record'
ActiveRecord::Base.configurations = Rails.application.config.database_configuration
+ ActiveRecord::Migrator.migrations_path = Rails.application.config.paths.db.migrate.to_a.first
+ end
+
+ desc "Copies missing migrations from Railties (e.g. plugins, engines). You can specify Railties to use with FROM=railtie1,railtie2"
+ task :copy_migrations => :load_config do
+ to_load = ENV["FROM"].blank? ? :all : ENV["FROM"].split(",").map {|n| n.strip }
+ railties = {}
+ Rails.application.railties.all do |railtie|
+ next unless to_load == :all || to_load.include?(railtie.railtie_name)
+
+ if railtie.config.respond_to?(:paths) && railtie.config.paths.db
+ railties[railtie.railtie_name] = railtie.config.paths.db.migrate.to_a.first
+ end
+ end
+
+ copied = ActiveRecord::Migration.copy(ActiveRecord::Migrator.migrations_path, railties)
+
+ if copied.blank?
+ puts "No migrations were copied, project is up to date."
+ else
+ puts "The following migrations were copied:"
+ puts copied.map{ |path| File.basename(path) }.join("\n")
+ end
end
namespace :create do
@@ -139,7 +162,7 @@ namespace :db do
desc "Migrate the database (options: VERSION=x, VERBOSE=false)."
task :migrate => :environment do
ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
- ActiveRecord::Migrator.migrate("db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
+ ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_path, ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
end
@@ -162,7 +185,7 @@ namespace :db do
task :up => :environment do
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
raise "VERSION is required" unless version
- ActiveRecord::Migrator.run(:up, "db/migrate/", version)
+ ActiveRecord::Migrator.run(:up, ActiveRecord::Migrator.migrations_path, version)
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
end
@@ -170,7 +193,7 @@ namespace :db do
task :down => :environment do
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
raise "VERSION is required" unless version
- ActiveRecord::Migrator.run(:down, "db/migrate/", version)
+ ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_path, version)
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
end
@@ -208,14 +231,14 @@ namespace :db do
desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).'
task :rollback => :environment do
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
- ActiveRecord::Migrator.rollback('db/migrate/', step)
+ ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_path, step)
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
end
# desc 'Pushes the schema to the next version (specify steps w/ STEP=n).'
task :forward => :environment do
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
- ActiveRecord::Migrator.forward('db/migrate/', step)
+ ActiveRecord::Migrator.forward(ActiveRecord::Migrator.migrations_path, step)
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
end
@@ -260,7 +283,7 @@ namespace :db do
# desc "Raises an error if there are pending migrations"
task :abort_if_pending_migrations => :environment do
if defined? ActiveRecord
- pending_migrations = ActiveRecord::Migrator.new(:up, 'db/migrate').pending_migrations
+ pending_migrations = ActiveRecord::Migrator.new(:up, ActiveRecord::Migrator.migrations_path).pending_migrations
if pending_migrations.any?
puts "You have #{pending_migrations.size} pending migrations:"
diff --git a/activerecord/lib/rails/generators/active_record.rb b/activerecord/lib/rails/generators/active_record.rb
index 26bc977e19..4b3d1db216 100644
--- a/activerecord/lib/rails/generators/active_record.rb
+++ b/activerecord/lib/rails/generators/active_record.rb
@@ -14,6 +14,12 @@ module ActiveRecord
def self.base_root
File.dirname(__FILE__)
end
+
+ # Implement the required interface for Rails::Generators::Migration.
+ def self.next_migration_number(dirname) #:nodoc:
+ next_migration_number = current_migration_number(dirname) + 1
+ ActiveRecord::Migration.next_migration_number(next_migration_number)
+ end
end
end
end
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 1fb59d3589..4bf3c25d28 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -83,3 +83,21 @@ begin
ensure
$stdout = original_stdout
end
+
+class << Time
+ unless method_defined? :now_before_time_travel
+ alias_method :now_before_time_travel, :now
+ end
+
+ def now
+ (@now ||= nil) || now_before_time_travel
+ end
+
+ def travel_to(time, &block)
+ @now = time
+ block.call
+ ensure
+ @now = nil
+ end
+end
+
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 96b97fdd8a..03a8cc90f6 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -1875,5 +1875,130 @@ if ActiveRecord::Base.connection.supports_migrations?
end
end
end
+
+ class CopyMigrationsTest < ActiveRecord::TestCase
+ def setup
+ end
+
+ def clear
+ ActiveRecord::Base.timestamped_migrations = true
+ to_delete = Dir[@migrations_path + "/*.rb"] - @existing_migrations
+ File.delete(*to_delete)
+ end
+
+ def test_copying_migrations_without_timestamps
+ ActiveRecord::Base.timestamped_migrations = false
+ @migrations_path = MIGRATIONS_ROOT + "/valid"
+ @existing_migrations = Dir[@migrations_path + "/*.rb"]
+
+ copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy"})
+ assert File.exists?(@migrations_path + "/4_people_have_hobbies.bukkits.rb")
+ assert File.exists?(@migrations_path + "/5_people_have_descriptions.bukkits.rb")
+ assert_equal [@migrations_path + "/4_people_have_hobbies.bukkits.rb", @migrations_path + "/5_people_have_descriptions.bukkits.rb"], copied
+
+ files_count = Dir[@migrations_path + "/*.rb"].length
+ copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy"})
+ assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
+ assert copied.empty?
+ ensure
+ clear
+ end
+
+ def test_copying_migrations_without_timestamps_from_2_sources
+ ActiveRecord::Base.timestamped_migrations = false
+ @migrations_path = MIGRATIONS_ROOT + "/valid"
+ @existing_migrations = Dir[@migrations_path + "/*.rb"]
+
+ sources = ActiveSupport::OrderedHash.new
+ sources[:bukkits] = sources[:omg] = MIGRATIONS_ROOT + "/to_copy"
+ ActiveRecord::Migration.copy(@migrations_path, sources)
+ assert File.exists?(@migrations_path + "/4_people_have_hobbies.omg.rb")
+ assert File.exists?(@migrations_path + "/5_people_have_descriptions.omg.rb")
+ assert File.exists?(@migrations_path + "/6_people_have_hobbies.bukkits.rb")
+ assert File.exists?(@migrations_path + "/7_people_have_descriptions.bukkits.rb")
+
+ files_count = Dir[@migrations_path + "/*.rb"].length
+ ActiveRecord::Migration.copy(@migrations_path, sources)
+ assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
+ ensure
+ clear
+ end
+
+ def test_copying_migrations_with_timestamps
+ @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
+ @existing_migrations = Dir[@migrations_path + "/*.rb"]
+
+ Time.travel_to(created_at = Time.utc(2010, 7, 26, 10, 10, 10)) do
+ copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
+ assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
+ assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
+ expected = [@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb",
+ @migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb"]
+ assert_equal expected, copied
+
+ files_count = Dir[@migrations_path + "/*.rb"].length
+ copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
+ assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
+ assert copied.empty?
+ end
+ ensure
+ clear
+ end
+
+ def test_copying_migrations_with_timestamps_from_2_sources
+ @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
+ @existing_migrations = Dir[@migrations_path + "/*.rb"]
+
+ sources = ActiveSupport::OrderedHash.new
+ sources[:bukkits] = sources[:omg] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
+
+ Time.travel_to(created_at = Time.utc(2010, 7, 26, 10, 10, 10)) do
+ copied = ActiveRecord::Migration.copy(@migrations_path, sources)
+ assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.omg.rb")
+ assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.omg.rb")
+ assert File.exists?(@migrations_path + "/20100726101012_people_have_hobbies.bukkits.rb")
+ assert File.exists?(@migrations_path + "/20100726101013_people_have_descriptions.bukkits.rb")
+ assert_equal 4, copied.length
+
+ files_count = Dir[@migrations_path + "/*.rb"].length
+ ActiveRecord::Migration.copy(@migrations_path, sources)
+ assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
+ end
+ ensure
+ clear
+ end
+
+ def test_copying_migrations_with_timestamps_to_destination_with_timestamps_in_future
+ @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
+ @existing_migrations = Dir[@migrations_path + "/*.rb"]
+
+ Time.travel_to(created_at = Time.utc(2010, 2, 20, 10, 10, 10)) do
+ ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
+ assert File.exists?(@migrations_path + "/20100301010102_people_have_hobbies.bukkits.rb")
+ assert File.exists?(@migrations_path + "/20100301010103_people_have_descriptions.bukkits.rb")
+
+ files_count = Dir[@migrations_path + "/*.rb"].length
+ copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
+ assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
+ assert copied.empty?
+ end
+ ensure
+ clear
+ end
+
+ def test_copying_migrations_to_empty_directory
+ @migrations_path = MIGRATIONS_ROOT + "/empty"
+ @existing_migrations = []
+
+ Time.travel_to(created_at = Time.utc(2010, 7, 26, 10, 10, 10)) do
+ copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
+ assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
+ assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
+ assert_equal 2, copied.length
+ end
+ ensure
+ clear
+ end
+ end
end
diff --git a/activerecord/test/migrations/empty/.gitkeep b/activerecord/test/migrations/empty/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/activerecord/test/migrations/empty/.gitkeep
diff --git a/activerecord/test/migrations/to_copy/1_people_have_hobbies.rb b/activerecord/test/migrations/to_copy/1_people_have_hobbies.rb
new file mode 100644
index 0000000000..639841f663
--- /dev/null
+++ b/activerecord/test/migrations/to_copy/1_people_have_hobbies.rb
@@ -0,0 +1,9 @@
+class PeopleHaveLastNames < ActiveRecord::Migration
+ def self.up
+ add_column "people", "hobbies", :text
+ end
+
+ def self.down
+ remove_column "people", "hobbies"
+ end
+end
diff --git a/activerecord/test/migrations/to_copy/2_people_have_descriptions.rb b/activerecord/test/migrations/to_copy/2_people_have_descriptions.rb
new file mode 100644
index 0000000000..b3d0b30640
--- /dev/null
+++ b/activerecord/test/migrations/to_copy/2_people_have_descriptions.rb
@@ -0,0 +1,9 @@
+class PeopleHaveLastNames < ActiveRecord::Migration
+ def self.up
+ add_column "people", "description", :text
+ end
+
+ def self.down
+ remove_column "people", "description"
+ end
+end
diff --git a/activerecord/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb b/activerecord/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb
new file mode 100644
index 0000000000..639841f663
--- /dev/null
+++ b/activerecord/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb
@@ -0,0 +1,9 @@
+class PeopleHaveLastNames < ActiveRecord::Migration
+ def self.up
+ add_column "people", "hobbies", :text
+ end
+
+ def self.down
+ remove_column "people", "hobbies"
+ end
+end
diff --git a/activerecord/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb b/activerecord/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb
new file mode 100644
index 0000000000..b3d0b30640
--- /dev/null
+++ b/activerecord/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb
@@ -0,0 +1,9 @@
+class PeopleHaveLastNames < ActiveRecord::Migration
+ def self.up
+ add_column "people", "description", :text
+ end
+
+ def self.down
+ remove_column "people", "description"
+ end
+end
diff --git a/activerecord/test/migrations/valid_with_timestamps/20100101010101_people_have_last_names.rb b/activerecord/test/migrations/valid_with_timestamps/20100101010101_people_have_last_names.rb
new file mode 100644
index 0000000000..81af5fef5e
--- /dev/null
+++ b/activerecord/test/migrations/valid_with_timestamps/20100101010101_people_have_last_names.rb
@@ -0,0 +1,9 @@
+class PeopleHaveLastNames < ActiveRecord::Migration
+ def self.up
+ add_column "people", "last_name", :string
+ end
+
+ def self.down
+ remove_column "people", "last_name"
+ end
+end \ No newline at end of file
diff --git a/activerecord/test/migrations/valid_with_timestamps/20100201010101_we_need_reminders.rb b/activerecord/test/migrations/valid_with_timestamps/20100201010101_we_need_reminders.rb
new file mode 100644
index 0000000000..d5e71ce8ef
--- /dev/null
+++ b/activerecord/test/migrations/valid_with_timestamps/20100201010101_we_need_reminders.rb
@@ -0,0 +1,12 @@
+class WeNeedReminders < ActiveRecord::Migration
+ def self.up
+ create_table("reminders") do |t|
+ t.column :content, :text
+ t.column :remind_at, :datetime
+ end
+ end
+
+ def self.down
+ drop_table "reminders"
+ end
+end \ No newline at end of file
diff --git a/activerecord/test/migrations/valid_with_timestamps/20100301010101_innocent_jointable.rb b/activerecord/test/migrations/valid_with_timestamps/20100301010101_innocent_jointable.rb
new file mode 100644
index 0000000000..21c9ca5328
--- /dev/null
+++ b/activerecord/test/migrations/valid_with_timestamps/20100301010101_innocent_jointable.rb
@@ -0,0 +1,12 @@
+class InnocentJointable < ActiveRecord::Migration
+ def self.up
+ create_table("people_reminders", :id => false) do |t|
+ t.column :reminder_id, :integer
+ t.column :person_id, :integer
+ end
+ end
+
+ def self.down
+ drop_table "people_reminders"
+ end
+end \ No newline at end of file