1 module Radiant
2 class ExtensionMigrator < ActiveRecord::Migrator
3 class << self
4 attr_accessor :extension
5
6 def migrate(target_version = nil)
7 super extension.migrations_path, target_version
8 end
9
10 def migrate_extensions
11 Radiant.configuration.enabled_extensions.each do |ext|
12 to_migrate = Extension.descendants.detect{|descendant| descendant.name == "#{ext.to_s.camelize}Extension" }
13 to_migrate.migrator.migrate
14 end
15 end
16
17 def get_all_versions
18 ActiveRecord::Base.connection.select_values("SELECT version FROM #{schema_migrations_table_name}").
19 select { |version| version.starts_with?("#{@extension.extension_name}-")}.
20 map { |version| version.sub("#{@extension.extension_name}-", '').to_i }.sort
21 end
22 end
23
24 def initialize(direction, migrations_path, target_version = nil)
25 super
26 initialize_extension_schema_migrations
27 initialize_received_migrations
28 end
29
30 private
31 def quote(s)
32 ActiveRecord::Base.connection.quote(s)
33 end
34
35 def extension_name
36 self.class.extension.extension_name
37 end
38
39 def version_string(version)
40 "#{extension_name}-#{version}"
41 end
42
43 def initialize_extension_schema_migrations
44 current_version = ActiveRecord::Base.connection.select_value("SELECT schema_version FROM extension_meta WHERE name = #{quote(extension_name)}")
45 if current_version
46 assume_migrated_upto_version(current_version.to_i)
47 ActiveRecord::Base.connection.delete("DELETE FROM extension_meta WHERE name = #{quote(extension_name)}")
48 end
49 end
50
51 def initialize_received_migrations
52 if donors = self.class.extension.migrates_from
53 donors.each do |extension_name, until_migration|
54 replaced = ActiveRecord::Base.connection.select_values("SELECT version FROM #{ActiveRecord::Migrator.schema_migrations_table_name} WHERE version LIKE '#{extension_name}-%'").map{|v| v.sub(/^#{extension_name}\-/, '').to_i}
55 replaced.delete_if{|v| v > until_migration.to_i} if until_migration
56 assume_migrated_upto_version(replaced.max) if replaced.any?
57 end
58 end
59 end
60
61 def assume_migrated_upto_version(version)
62 version = version.to_i
63 sm_table = self.class.schema_migrations_table_name
64
65 migrated = self.class.get_all_versions
66 versions = Dir["#{@migrations_path}/[0-9]*_*.rb"].map do |filename|
67 filename.split('/').last.split('_').first.to_i
68 end
69
70 unless migrated.include?(version)
71 ActiveRecord::Base.connection.execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version_string(version))})"
72 end
73
74 inserted = Set.new
75 (versions - migrated).each do |v|
76 if inserted.include?(v)
77 raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict."
78 elsif v < version
79 ActiveRecord::Base.connection.execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version_string(v))})"
80 inserted << v
81 end
82 end
83 end
84
85 def record_version_state_after_migrating(version)
86 sm_table = self.class.schema_migrations_table_name
87
88 @migrated_versions ||= []
89 if down?
90 @migrated_versions.delete(version.to_i)
91 ActiveRecord::Base.connection.update("DELETE FROM #{sm_table} WHERE version = #{quote(version_string(version))}")
92 else
93 @migrated_versions.push(version.to_i).sort!
94 ActiveRecord::Base.connection.insert("INSERT INTO #{sm_table} (version) VALUES (#{quote(version_string(version))})")
95 end
96 end
97 end
98 end