Upgraded Rails and RSpec
[monkeycharger.git] / vendor / rails / activerecord / lib / active_record / migration.rb
blobfa9dc83255a3955827abad7edc363e0571bab1c2
1 module ActiveRecord
2   class IrreversibleMigration < ActiveRecordError#:nodoc:
3   end
5   class DuplicateMigrationVersionError < ActiveRecordError#:nodoc:
6     def initialize(version)
7       super("Multiple migrations have the version number #{version}")
8     end
9   end
11   class IllegalMigrationNameError < ActiveRecordError#:nodoc:
12     def initialize(name)
13       super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed)")
14     end
15   end
17   # Migrations can manage the evolution of a schema used by several physical databases. It's a solution
18   # to the common problem of adding a field to make a new feature work in your local database, but being unsure of how to
19   # push that change to other developers and to the production server. With migrations, you can describe the transformations
20   # in self-contained classes that can be checked into version control systems and executed against another database that
21   # might be one, two, or five versions behind.
22   #
23   # Example of a simple migration:
24   #
25   #   class AddSsl < ActiveRecord::Migration
26   #     def self.up
27   #       add_column :accounts, :ssl_enabled, :boolean, :default => 1
28   #     end
29   #
30   #     def self.down
31   #       remove_column :accounts, :ssl_enabled
32   #     end
33   #   end
34   #
35   # This migration will add a boolean flag to the accounts table and remove it if you're backing out of the migration.
36   # It shows how all migrations have two class methods +up+ and +down+ that describes the transformations required to implement
37   # or remove the migration. These methods can consist of both the migration specific methods like add_column and remove_column,
38   # but may also contain regular Ruby code for generating data needed for the transformations.
39   #
40   # Example of a more complex migration that also needs to initialize data:
41   #
42   #   class AddSystemSettings < ActiveRecord::Migration
43   #     def self.up
44   #       create_table :system_settings do |t|
45   #         t.string  :name
46   #         t.string  :label
47   #         t.text  :value
48   #         t.string  :type
49   #         t.integer  :position
50   #       end
51   #
52   #       SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1
53   #     end
54   #
55   #     def self.down
56   #       drop_table :system_settings
57   #     end
58   #   end
59   #
60   # This migration first adds the system_settings table, then creates the very first row in it using the Active Record model
61   # that relies on the table. It also uses the more advanced create_table syntax where you can specify a complete table schema
62   # in one block call.
63   #
64   # == Available transformations
65   #
66   # * <tt>create_table(name, options)</tt> Creates a table called +name+ and makes the table object available to a block
67   #   that can then add columns to it, following the same format as add_column. See example above. The options hash is for
68   #   fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create table definition.
69   # * <tt>drop_table(name)</tt>: Drops the table called +name+.
70   # * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+ to +new_name+.
71   # * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column to the table called +table_name+
72   #   named +column_name+ specified to be one of the following types:
73   #   :string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time,
74   #   :date, :binary, :boolean. A default value can be specified by passing an
75   #   +options+ hash like { :default => 11 }. Other options include :limit and :null (e.g. { :limit => 50, :null => false })
76   #   -- see ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
77   # * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames a column but keeps the type and content.
78   # * <tt>change_column(table_name, column_name, type, options)</tt>:  Changes the column to a different type using the same
79   #   parameters as add_column.
80   # * <tt>remove_column(table_name, column_name)</tt>: Removes the column named +column_name+ from the table called +table_name+.
81   # * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index with the name of the column. Other options include
82   #   :name and :unique (e.g. { :name => "users_name_index", :unique => true }).
83   # * <tt>remove_index(table_name, index_name)</tt>: Removes the index specified by +index_name+.
84   #
85   # == Irreversible transformations
86   #
87   # Some transformations are destructive in a manner that cannot be reversed. Migrations of that kind should raise
88   # an <tt>ActiveRecord::IrreversibleMigration</tt> exception in their +down+ method.
89   #
90   # == Running migrations from within Rails
91   #
92   # The Rails package has several tools to help create and apply migrations.
93   #
94   # To generate a new migration, use <tt>script/generate migration MyNewMigration</tt>
95   # where MyNewMigration is the name of your migration. The generator will
96   # create a file <tt>nnn_my_new_migration.rb</tt> in the <tt>db/migrate/</tt>
97   # directory where <tt>nnn</tt> is the next largest migration number.
98   # You may then edit the <tt>self.up</tt> and <tt>self.down</tt> methods of
99   # MyNewMigration.
100   #
101   # To run migrations against the currently configured database, use
102   # <tt>rake db:migrate</tt>. This will update the database by running all of the
103   # pending migrations, creating the <tt>schema_info</tt> table if missing.
104   #
105   # To roll the database back to a previous migration version, use
106   # <tt>rake db:migrate VERSION=X</tt> where <tt>X</tt> is the version to which
107   # you wish to downgrade. If any of the migrations throw an
108   # <tt>ActiveRecord::IrreversibleMigration</tt> exception, that step will fail and you'll
109   # have some manual work to do.
110   #
111   # == Database support
112   #
113   # Migrations are currently supported in MySQL, PostgreSQL, SQLite,
114   # SQL Server, Sybase, and Oracle (all supported databases except DB2).
115   #
116   # == More examples
117   #
118   # Not all migrations change the schema. Some just fix the data:
119   #
120   #   class RemoveEmptyTags < ActiveRecord::Migration
121   #     def self.up
122   #       Tag.find(:all).each { |tag| tag.destroy if tag.pages.empty? }
123   #     end
124   #
125   #     def self.down
126   #       # not much we can do to restore deleted data
127   #       raise ActiveRecord::IrreversibleMigration, "Can't recover the deleted tags"
128   #     end
129   #   end
130   #
131   # Others remove columns when they migrate up instead of down:
132   #
133   #   class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration
134   #     def self.up
135   #       remove_column :items, :incomplete_items_count
136   #       remove_column :items, :completed_items_count
137   #     end
138   #
139   #     def self.down
140   #       add_column :items, :incomplete_items_count
141   #       add_column :items, :completed_items_count
142   #     end
143   #   end
144   #
145   # And sometimes you need to do something in SQL not abstracted directly by migrations:
146   #
147   #   class MakeJoinUnique < ActiveRecord::Migration
148   #     def self.up
149   #       execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
150   #     end
151   #
152   #     def self.down
153   #       execute "ALTER TABLE `pages_linked_pages` DROP INDEX `page_id_linked_page_id`"
154   #     end
155   #   end
156   #
157   # == Using a model after changing its table
158   #
159   # Sometimes you'll want to add a column in a migration and populate it immediately after. In that case, you'll need
160   # to make a call to Base#reset_column_information in order to ensure that the model has the latest column data from
161   # after the new column was added. Example:
162   #
163   #   class AddPeopleSalary < ActiveRecord::Migration
164   #     def self.up
165   #       add_column :people, :salary, :integer
166   #       Person.reset_column_information
167   #       Person.find(:all).each do |p|
168   #         p.update_attribute :salary, SalaryCalculator.compute(p)
169   #       end
170   #     end
171   #   end
172   #
173   # == Controlling verbosity
174   #
175   # By default, migrations will describe the actions they are taking, writing
176   # them to the console as they happen, along with benchmarks describing how
177   # long each step took.
178   #
179   # You can quiet them down by setting ActiveRecord::Migration.verbose = false.
180   #
181   # You can also insert your own messages and benchmarks by using the #say_with_time
182   # method:
183   #
184   #   def self.up
185   #     ...
186   #     say_with_time "Updating salaries..." do
187   #       Person.find(:all).each do |p|
188   #         p.update_attribute :salary, SalaryCalculator.compute(p)
189   #       end
190   #     end
191   #     ...
192   #   end
193   #
194   # The phrase "Updating salaries..." would then be printed, along with the
195   # benchmark for the block when the block completes.
196   class Migration
197     @@verbose = true
198     cattr_accessor :verbose
200     class << self
201       def up_with_benchmarks #:nodoc:
202         migrate(:up)
203       end
205       def down_with_benchmarks #:nodoc:
206         migrate(:down)
207       end
209       # Execute this migration in the named direction
210       def migrate(direction)
211         return unless respond_to?(direction)
213         case direction
214           when :up   then announce "migrating"
215           when :down then announce "reverting"
216         end
218         result = nil
219         time = Benchmark.measure { result = send("#{direction}_without_benchmarks") }
221         case direction
222           when :up   then announce "migrated (%.4fs)" % time.real; write
223           when :down then announce "reverted (%.4fs)" % time.real; write
224         end
226         result
227       end
229       # Because the method added may do an alias_method, it can be invoked
230       # recursively. We use @ignore_new_methods as a guard to indicate whether
231       # it is safe for the call to proceed.
232       def singleton_method_added(sym) #:nodoc:
233         return if @ignore_new_methods
235         begin
236           @ignore_new_methods = true
238           case sym
239             when :up, :down
240               klass = (class << self; self; end)
241               klass.send(:alias_method_chain, sym, "benchmarks")
242           end
243         ensure
244           @ignore_new_methods = false
245         end
246       end
248       def write(text="")
249         puts(text) if verbose
250       end
252       def announce(message)
253         text = "#{@version} #{name}: #{message}"
254         length = [0, 75 - text.length].max
255         write "== %s %s" % [text, "=" * length]
256       end
258       def say(message, subitem=false)
259         write "#{subitem ? "   ->" : "--"} #{message}"
260       end
262       def say_with_time(message)
263         say(message)
264         result = nil
265         time = Benchmark.measure { result = yield }
266         say "%.4fs" % time.real, :subitem
267         say("#{result} rows", :subitem) if result.is_a?(Integer)
268         result
269       end
271       def suppress_messages
272         save, self.verbose = verbose, false
273         yield
274       ensure
275         self.verbose = save
276       end
278       def method_missing(method, *arguments, &block)
279         arg_list = arguments.map(&:inspect) * ', '
281         say_with_time "#{method}(#{arg_list})" do
282           unless arguments.empty? || method == :execute
283             arguments[0] = Migrator.proper_table_name(arguments.first)
284           end
285           ActiveRecord::Base.connection.send(method, *arguments, &block)
286         end
287       end
288     end
289   end
291   class Migrator#:nodoc:
292     class << self
293       def migrate(migrations_path, target_version = nil)
294         Base.connection.initialize_schema_information
296         case
297           when target_version.nil?, current_version < target_version
298             up(migrations_path, target_version)
299           when current_version > target_version
300             down(migrations_path, target_version)
301           when current_version == target_version
302             return # You're on the right version
303         end
304       end
306       def up(migrations_path, target_version = nil)
307         self.new(:up, migrations_path, target_version).migrate
308       end
310       def down(migrations_path, target_version = nil)
311         self.new(:down, migrations_path, target_version).migrate
312       end
314       def schema_info_table_name
315         Base.table_name_prefix + "schema_info" + Base.table_name_suffix
316       end
318       def current_version
319         Base.connection.select_value("SELECT version FROM #{schema_info_table_name}").to_i
320       end
322       def proper_table_name(name)
323         # Use the ActiveRecord objects own table_name, or pre/suffix from ActiveRecord::Base if name is a symbol/string
324         name.table_name rescue "#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}"
325       end
326     end
328     def initialize(direction, migrations_path, target_version = nil)
329       raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?
330       @direction, @migrations_path, @target_version = direction, migrations_path, target_version
331       Base.connection.initialize_schema_information
332     end
334     def current_version
335       self.class.current_version
336     end
338     def migrate
339       migration_classes.each do |migration_class|
340         if reached_target_version?(migration_class.version)
341           Base.logger.info("Reached target version: #{@target_version}")
342           break
343         end
345         next if irrelevant_migration?(migration_class.version)
347         Base.logger.info "Migrating to #{migration_class} (#{migration_class.version})"
348         migration_class.migrate(@direction)
349         set_schema_version(migration_class.version)
350       end
351     end
353     private
354       def migration_classes
355         migrations = migration_files.inject([]) do |migrations, migration_file|
356           load(migration_file)
357           version, name = migration_version_and_name(migration_file)
358           assert_unique_migration_version(migrations, version.to_i)
359           migrations << migration_class(name, version.to_i)
360         end
362         sorted = migrations.sort_by { |m| m.version }
363         down? ? sorted.reverse : sorted
364       end
366       def assert_unique_migration_version(migrations, version)
367         if !migrations.empty? && migrations.find { |m| m.version == version }
368           raise DuplicateMigrationVersionError.new(version)
369         end
370       end
372       def migration_files
373         files = Dir["#{@migrations_path}/[0-9]*_*.rb"].sort_by do |f|
374           m = migration_version_and_name(f)
375           raise IllegalMigrationNameError.new(f) unless m
376           m.first.to_i
377         end
378         down? ? files.reverse : files
379       end
381       def migration_class(migration_name, version)
382         klass = migration_name.camelize.constantize
383         class << klass; attr_accessor :version end
384         klass.version = version
385         klass
386       end
388       def migration_version_and_name(migration_file)
389         return *migration_file.scan(/([0-9]+)_([_a-z0-9]*).rb/).first
390       end
392       def set_schema_version(version)
393         Base.connection.update("UPDATE #{self.class.schema_info_table_name} SET version = #{down? ? version.to_i - 1 : version.to_i}")
394       end
396       def up?
397         @direction == :up
398       end
400       def down?
401         @direction == :down
402       end
404       def reached_target_version?(version)
405         return false if @target_version == nil
406         (up? && version.to_i - 1 >= @target_version) || (down? && version.to_i <= @target_version)
407       end
409       def irrelevant_migration?(version)
410         (up? && version.to_i <= current_version) || (down? && version.to_i > current_version)
411       end
412   end