cjbottaro / migration_izzle

Rails plugin to manage migrations when multiple developers are working on a shared database.

This URL has Read+Write access

migration_izzle / lib / migration_izzle.rb
100644 118 lines (95 sloc) 5.106 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
module ActiveRecord
  module ConnectionAdapters
    module SchemaStatements
    
      # This method overwritten by migration_izzle plugin.
      alias_method :initialize_schema_information__rails, :initialize_schema_information
      def initialize_schema_information
        
        # Call Rail's original version of this method.
        initialize_schema_information__rails
        
        # Now add our "histories" table.
        table_name = ActiveRecord::Migrator.schema_info_history_table_name
        unless self.tables.include?(table_name)
          create_table table_name do |t|
            t.column :version, :integer, :null => false
            t.column :name, :string, :null => false
            t.column :created_at, :timestamp
          end
        end
        
      end # def initialize_schema_information
 
    end # module SchemaStatements
  end # module ConnectionAdapters
  
  class Migrator
    class << self
    
      # This method added by migration_izzle plugin.
      def schema_info_history_table_name
        ActiveRecord::Migrator.schema_info_table_name + '_histories'
      end
    
      # This method overwritten by migration_izzle plugin.
      alias_method :migrate__rails, :migrate
      def migrate(migrations_path, target_version = nil)
      
        # Call Rail's original migrate method which does nothing except for call Base.connection.initialize_schema_information
        # if current_version == target_version which is the condition we want to do our stuff for.
        migrate__rails(migrations_path, target_version)
        
        # Do our stuff.
        up(migrations_path, target_version) if current_version == target_version
        
      end # def migrate
      
      # This method added by migration_izzle plugin.
      def force_migrate(migrations_path, file_name, direction)
        migrator = self.new(direction, migrations_path)
        migrator.force_migrate(file_name)
      end
      
    end # class << self
    
    # This method overwritten by migration_izzle plugin.
    # It's almost the same, but with one line removed and one line added.
    def migrate
      migration_classes.each do |(version, migration_class)|
        Base.logger.info("Reached target version: #{@target_version}") and break if reached_target_version?(version)
        #next if irrelevant_migration?(version) # this line removed
 
        Base.logger.info "Migrating to #{migration_class} (#{version})"
        migration_class.migrate(@direction)
        update_schema_history(version, migration_class.name) # this line added
        set_schema_version(version)
      end
    end
    
    # This method added by migration_izzle plugin.
    def force_migrate(file_name)
      migration_file = @migrations_path + file_name
      load(migration_file)
      version, name = migration_version_and_name(migration_file)
      migration_class(name, version.to_i).migrate(@direction)
      migration_class_name = migration_class(name, version.to_i).name
      if @direction == :up and !schema_history_exists(version, migration_class_name)
        update_schema_history(version, migration_class_name)
      elsif @direction == :down and schema_history_exists(version, migration_class_name)
        update_schema_history(version, migration_class_name)
      end
    end
    
    private
    
    # This method overwritten by migration_izzle plugin.
    def migration_classes
      sql = "SELECT * FROM #{ActiveRecord::Migrator.schema_info_history_table_name}"
      already_ran = Base.connection.select_all(sql).index_by{|row| row['version'] + row['name']}
      migrations = migration_files.inject([]) do |migrations, migration_file|
        load(migration_file)
        version, name = migration_version_and_name(migration_file)
        already_ran_key = version.to_i.to_s + migration_class(name, version.to_i).name
        migrations << [ version.to_i, migration_class(name, version.to_i) ] if (up? and !already_ran.has_key?(already_ran_key)) or \
                                                                               (down? and already_ran.has_key?(already_ran_key))
        migrations
      end
      down? ? migrations.sort_by{|e| [e[0], e[1].name]}.reverse : migrations.sort_by{|e| [e[0], e[1].name]}
    end # def migration_classes
    
    # This method added by migration_izzle plugin.
    def schema_history_exists(version, name)
      !Base.connection.select_one("SELECT * FROM #{ActiveRecord::Migrator.schema_info_history_table_name} WHERE version=#{version} AND name='#{name}'").blank?
    end # def schema_history_exists
    
    # This method added by migration_izzle plugin.
    def update_schema_history(version, name)
      if up?
        Base.connection.execute("INSERT INTO #{ActiveRecord::Migrator.schema_info_history_table_name} (version, name, created_at) values (#{version}, '#{name}', '#{Time.now}')")
      else
        Base.connection.execute("DELETE FROM #{ActiveRecord::Migrator.schema_info_history_table_name} WHERE version=#{version} AND name='#{name}'")
      end
    end # def set_schema_history
    
  end # class Migrator
  
end