seed_data_migrations 0.0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +15 -0
  3. data/Rakefile +31 -0
  4. data/db/migrate/20140220214847_add_seed_migrations_table.rb +11 -0
  5. data/lib/data_migration.rb +935 -0
  6. data/lib/data_migrations/version.rb +3 -0
  7. data/lib/data_migrations.rb +2 -0
  8. data/lib/generators/data_migrations/install/data_migrations_generator.rb +24 -0
  9. data/lib/generators/data_migrations/templates/data_migrations_migration.rb +11 -0
  10. data/lib/schema_data_migration.rb +50 -0
  11. data/lib/tasks/data_migrations_tasks.rake +54 -0
  12. data/test/data_migrations_test.rb +7 -0
  13. data/test/dummy/README.rdoc +28 -0
  14. data/test/dummy/Rakefile +6 -0
  15. data/test/dummy/app/assets/javascripts/application.js +13 -0
  16. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  17. data/test/dummy/app/controllers/application_controller.rb +5 -0
  18. data/test/dummy/app/helpers/application_helper.rb +2 -0
  19. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  20. data/test/dummy/bin/bundle +3 -0
  21. data/test/dummy/bin/rails +4 -0
  22. data/test/dummy/bin/rake +4 -0
  23. data/test/dummy/config/application.rb +23 -0
  24. data/test/dummy/config/boot.rb +5 -0
  25. data/test/dummy/config/database.yml +25 -0
  26. data/test/dummy/config/environment.rb +5 -0
  27. data/test/dummy/config/environments/development.rb +29 -0
  28. data/test/dummy/config/environments/production.rb +80 -0
  29. data/test/dummy/config/environments/test.rb +36 -0
  30. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  31. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  32. data/test/dummy/config/initializers/inflections.rb +16 -0
  33. data/test/dummy/config/initializers/mime_types.rb +5 -0
  34. data/test/dummy/config/initializers/secret_token.rb +12 -0
  35. data/test/dummy/config/initializers/session_store.rb +3 -0
  36. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  37. data/test/dummy/config/locales/en.yml +23 -0
  38. data/test/dummy/config/routes.rb +56 -0
  39. data/test/dummy/config.ru +4 -0
  40. data/test/dummy/public/404.html +58 -0
  41. data/test/dummy/public/422.html +58 -0
  42. data/test/dummy/public/500.html +57 -0
  43. data/test/dummy/public/favicon.ico +0 -0
  44. data/test/test_helper.rb +15 -0
  45. metadata +157 -0
@@ -0,0 +1,935 @@
1
+ require "active_support/core_ext/module/attribute_accessors"
2
+ require 'set'
3
+
4
+ module ActiveRecord
5
+ class DataMigrationError < ActiveRecordError#:nodoc:
6
+ def initialize(message = nil)
7
+ message = "\n\n#{message}\n\n" if message
8
+ super
9
+ end
10
+ end
11
+
12
+ # Exception that can be raised to stop migrations from going backwards.
13
+ class IrreversibleDataMigration < MigrationError
14
+ end
15
+
16
+ class DuplicateDataMigrationVersionError < MigrationError#:nodoc:
17
+ def initialize(version)
18
+ super("Multiple migrations have the version number #{version}")
19
+ end
20
+ end
21
+
22
+ class DuplicateDataMigrationNameError < MigrationError#:nodoc:
23
+ def initialize(name)
24
+ super("Multiple migrations have the name #{name}")
25
+ end
26
+ end
27
+
28
+ class UnknownDataMigrationVersionError < MigrationError #:nodoc:
29
+ def initialize(version)
30
+ super("No migration with version number #{version}")
31
+ end
32
+ end
33
+
34
+ class IllegalDataMigrationNameError < MigrationError#:nodoc:
35
+ def initialize(name)
36
+ super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed)")
37
+ end
38
+ end
39
+
40
+ class PendingDataMigrationError < MigrationError#:nodoc:
41
+ def initialize
42
+ if defined?(Rails)
43
+ super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rake db:migrate RAILS_ENV=#{::Rails.env}")
44
+ else
45
+ super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rake db:migrate")
46
+ end
47
+ end
48
+ end
49
+
50
+ # = Active Record Migrations
51
+ #
52
+ # Migrations can manage the evolution of a schema used by several physical
53
+ # databases. It's a solution to the common problem of adding a field to make
54
+ # a new feature work in your local database, but being unsure of how to
55
+ # push that change to other developers and to the production server. With
56
+ # migrations, you can describe the transformations in self-contained classes
57
+ # that can be checked into version control systems and executed against
58
+ # another database that might be one, two, or five versions behind.
59
+ #
60
+ # Example of a simple migration:
61
+ #
62
+ # class AddSsl < ActiveRecord::Migration
63
+ # def up
64
+ # add_column :accounts, :ssl_enabled, :boolean, default: true
65
+ # end
66
+ #
67
+ # def down
68
+ # remove_column :accounts, :ssl_enabled
69
+ # end
70
+ # end
71
+ #
72
+ # This migration will add a boolean flag to the accounts table and remove it
73
+ # if you're backing out of the migration. It shows how all migrations have
74
+ # two methods +up+ and +down+ that describes the transformations
75
+ # required to implement or remove the migration. These methods can consist
76
+ # of both the migration specific methods like +add_column+ and +remove_column+,
77
+ # but may also contain regular Ruby code for generating data needed for the
78
+ # transformations.
79
+ #
80
+ # Example of a more complex migration that also needs to initialize data:
81
+ #
82
+ # class AddSystemSettings < ActiveRecord::Migration
83
+ # def up
84
+ # create_table :system_settings do |t|
85
+ # t.string :name
86
+ # t.string :label
87
+ # t.text :value
88
+ # t.string :type
89
+ # t.integer :position
90
+ # end
91
+ #
92
+ # SystemSetting.create name: 'notice',
93
+ # label: 'Use notice?',
94
+ # value: 1
95
+ # end
96
+ #
97
+ # def down
98
+ # drop_table :system_settings
99
+ # end
100
+ # end
101
+ #
102
+ # This migration first adds the +system_settings+ table, then creates the very
103
+ # first row in it using the Active Record model that relies on the table. It
104
+ # also uses the more advanced +create_table+ syntax where you can specify a
105
+ # complete table schema in one block call.
106
+ #
107
+ # == Available transformations
108
+ #
109
+ # * <tt>create_table(name, options)</tt>: Creates a table called +name+ and
110
+ # makes the table object available to a block that can then add columns to it,
111
+ # following the same format as +add_column+. See example above. The options hash
112
+ # is for fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create
113
+ # table definition.
114
+ # * <tt>drop_table(name)</tt>: Drops the table called +name+.
115
+ # * <tt>change_table(name, options)</tt>: Allows to make column alterations to
116
+ # the table called +name+. It makes the table object available to a block that
117
+ # can then add/remove columns, indexes or foreign keys to it.
118
+ # * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
119
+ # to +new_name+.
120
+ # * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column
121
+ # to the table called +table_name+
122
+ # named +column_name+ specified to be one of the following types:
123
+ # <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>,
124
+ # <tt>:decimal</tt>, <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
125
+ # <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>. A default value can be
126
+ # specified by passing an +options+ hash like <tt>{ default: 11 }</tt>.
127
+ # Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g.
128
+ # <tt>{ limit: 50, null: false }</tt>) -- see
129
+ # ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
130
+ # * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames
131
+ # a column but keeps the type and content.
132
+ # * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
133
+ # the column to a different type using the same parameters as add_column.
134
+ # * <tt>remove_column(table_name, column_name, type, options)</tt>: Removes the column
135
+ # named +column_name+ from the table called +table_name+.
136
+ # * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
137
+ # with the name of the column. Other options include
138
+ # <tt>:name</tt>, <tt>:unique</tt> (e.g.
139
+ # <tt>{ name: 'users_name_index', unique: true }</tt>) and <tt>:order</tt>
140
+ # (e.g. <tt>{ order: { name: :desc } }</tt>).
141
+ # * <tt>remove_index(table_name, column: column_name)</tt>: Removes the index
142
+ # specified by +column_name+.
143
+ # * <tt>remove_index(table_name, name: index_name)</tt>: Removes the index
144
+ # specified by +index_name+.
145
+ #
146
+ # == Irreversible transformations
147
+ #
148
+ # Some transformations are destructive in a manner that cannot be reversed.
149
+ # Migrations of that kind should raise an <tt>ActiveRecord::IrreversibleMigration</tt>
150
+ # exception in their +down+ method.
151
+ #
152
+ # == Running migrations from within Rails
153
+ #
154
+ # The Rails package has several tools to help create and apply migrations.
155
+ #
156
+ # To generate a new migration, you can use
157
+ # rails generate migration MyNewMigration
158
+ #
159
+ # where MyNewMigration is the name of your migration. The generator will
160
+ # create an empty migration file <tt>timestamp_my_new_migration.rb</tt>
161
+ # in the <tt>db/migrate/</tt> directory where <tt>timestamp</tt> is the
162
+ # UTC formatted date and time that the migration was generated.
163
+ #
164
+ # You may then edit the <tt>up</tt> and <tt>down</tt> methods of
165
+ # MyNewMigration.
166
+ #
167
+ # There is a special syntactic shortcut to generate migrations that add fields to a table.
168
+ #
169
+ # rails generate migration add_fieldname_to_tablename fieldname:string
170
+ #
171
+ # This will generate the file <tt>timestamp_add_fieldname_to_tablename</tt>, which will look like this:
172
+ # class AddFieldnameToTablename < ActiveRecord::Migration
173
+ # def up
174
+ # add_column :tablenames, :fieldname, :string
175
+ # end
176
+ #
177
+ # def down
178
+ # remove_column :tablenames, :fieldname
179
+ # end
180
+ # end
181
+ #
182
+ # To run migrations against the currently configured database, use
183
+ # <tt>rake db:migrate</tt>. This will update the database by running all of the
184
+ # pending migrations, creating the <tt>schema_migrations</tt> table
185
+ # (see "About the schema_migrations table" section below) if missing. It will also
186
+ # invoke the db:schema:dump task, which will update your db/schema.rb file
187
+ # to match the structure of your database.
188
+ #
189
+ # To roll the database back to a previous migration version, use
190
+ # <tt>rake db:migrate VERSION=X</tt> where <tt>X</tt> is the version to which
191
+ # you wish to downgrade. If any of the migrations throw an
192
+ # <tt>ActiveRecord::IrreversibleMigration</tt> exception, that step will fail and you'll
193
+ # have some manual work to do.
194
+ #
195
+ # == Database support
196
+ #
197
+ # Migrations are currently supported in MySQL, PostgreSQL, SQLite,
198
+ # SQL Server, Sybase, and Oracle (all supported databases except DB2).
199
+ #
200
+ # == More examples
201
+ #
202
+ # Not all migrations change the schema. Some just fix the data:
203
+ #
204
+ # class RemoveEmptyTags < ActiveRecord::Migration
205
+ # def up
206
+ # Tag.all.each { |tag| tag.destroy if tag.pages.empty? }
207
+ # end
208
+ #
209
+ # def down
210
+ # # not much we can do to restore deleted data
211
+ # raise ActiveRecord::IrreversibleMigration, "Can't recover the deleted tags"
212
+ # end
213
+ # end
214
+ #
215
+ # Others remove columns when they migrate up instead of down:
216
+ #
217
+ # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration
218
+ # def up
219
+ # remove_column :items, :incomplete_items_count
220
+ # remove_column :items, :completed_items_count
221
+ # end
222
+ #
223
+ # def down
224
+ # add_column :items, :incomplete_items_count
225
+ # add_column :items, :completed_items_count
226
+ # end
227
+ # end
228
+ #
229
+ # And sometimes you need to do something in SQL not abstracted directly by migrations:
230
+ #
231
+ # class MakeJoinUnique < ActiveRecord::Migration
232
+ # def up
233
+ # execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
234
+ # end
235
+ #
236
+ # def down
237
+ # execute "ALTER TABLE `pages_linked_pages` DROP INDEX `page_id_linked_page_id`"
238
+ # end
239
+ # end
240
+ #
241
+ # == Using a model after changing its table
242
+ #
243
+ # Sometimes you'll want to add a column in a migration and populate it
244
+ # immediately after. In that case, you'll need to make a call to
245
+ # <tt>Base#reset_column_information</tt> in order to ensure that the model has the
246
+ # latest column data from after the new column was added. Example:
247
+ #
248
+ # class AddPeopleSalary < ActiveRecord::Migration
249
+ # def up
250
+ # add_column :people, :salary, :integer
251
+ # Person.reset_column_information
252
+ # Person.all.each do |p|
253
+ # p.update_attribute :salary, SalaryCalculator.compute(p)
254
+ # end
255
+ # end
256
+ # end
257
+ #
258
+ # == Controlling verbosity
259
+ #
260
+ # By default, migrations will describe the actions they are taking, writing
261
+ # them to the console as they happen, along with benchmarks describing how
262
+ # long each step took.
263
+ #
264
+ # You can quiet them down by setting ActiveRecord::Migration.verbose = false.
265
+ #
266
+ # You can also insert your own messages and benchmarks by using the +say_with_time+
267
+ # method:
268
+ #
269
+ # def up
270
+ # ...
271
+ # say_with_time "Updating salaries..." do
272
+ # Person.all.each do |p|
273
+ # p.update_attribute :salary, SalaryCalculator.compute(p)
274
+ # end
275
+ # end
276
+ # ...
277
+ # end
278
+ #
279
+ # The phrase "Updating salaries..." would then be printed, along with the
280
+ # benchmark for the block when the block completes.
281
+ #
282
+ # == About the schema_migrations table
283
+ #
284
+ # Rails versions 2.0 and prior used to create a table called
285
+ # <tt>schema_info</tt> when using migrations. This table contained the
286
+ # version of the schema as of the last applied migration.
287
+ #
288
+ # Starting with Rails 2.1, the <tt>schema_info</tt> table is
289
+ # (automatically) replaced by the <tt>schema_migrations</tt> table, which
290
+ # contains the version numbers of all the migrations applied.
291
+ #
292
+ # As a result, it is now possible to add migration files that are numbered
293
+ # lower than the current schema version: when migrating up, those
294
+ # never-applied "interleaved" migrations will be automatically applied, and
295
+ # when migrating down, never-applied "interleaved" migrations will be skipped.
296
+ #
297
+ # == Timestamped Migrations
298
+ #
299
+ # By default, Rails generates migrations that look like:
300
+ #
301
+ # 20080717013526_your_migration_name.rb
302
+ #
303
+ # The prefix is a generation timestamp (in UTC).
304
+ #
305
+ # If you'd prefer to use numeric prefixes, you can turn timestamped migrations
306
+ # off by setting:
307
+ #
308
+ # config.active_record.timestamped_migrations = false
309
+ #
310
+ # In application.rb.
311
+ #
312
+ # == Reversible Migrations
313
+ #
314
+ # Starting with Rails 3.1, you will be able to define reversible migrations.
315
+ # Reversible migrations are migrations that know how to go +down+ for you.
316
+ # You simply supply the +up+ logic, and the Migration system will figure out
317
+ # how to execute the down commands for you.
318
+ #
319
+ # To define a reversible migration, define the +change+ method in your
320
+ # migration like this:
321
+ #
322
+ # class TenderloveMigration < ActiveRecord::Migration
323
+ # def change
324
+ # create_table(:horses) do |t|
325
+ # t.column :content, :text
326
+ # t.column :remind_at, :datetime
327
+ # end
328
+ # end
329
+ # end
330
+ #
331
+ # This migration will create the horses table for you on the way up, and
332
+ # automatically figure out how to drop the table on the way down.
333
+ #
334
+ # Some commands like +remove_column+ cannot be reversed. If you care to
335
+ # define how to move up and down in these cases, you should define the +up+
336
+ # and +down+ methods as before.
337
+ #
338
+ # If a command cannot be reversed, an
339
+ # <tt>ActiveRecord::IrreversibleMigration</tt> exception will be raised when
340
+ # the migration is moving down.
341
+ #
342
+ # For a list of commands that are reversible, please see
343
+ # <tt>ActiveRecord::Migration::CommandRecorder</tt>.
344
+ #
345
+ # == Transactional Migrations
346
+ #
347
+ # If the database adapter supports DDL transactions, all migrations will
348
+ # automatically be wrapped in a transaction. There are queries that you
349
+ # can't execute inside a transaction though, and for these situations
350
+ # you can turn the automatic transactions off.
351
+ #
352
+ # class ChangeEnum < ActiveRecord::Migration
353
+ # disable_ddl_transaction!
354
+ #
355
+ # def up
356
+ # execute "ALTER TYPE model_size ADD VALUE 'new_value'"
357
+ # end
358
+ # end
359
+ #
360
+ # Remember that you can still open your own transactions, even if you
361
+ # are in a Migration with <tt>self.disable_ddl_transaction!</tt>.
362
+ class DataMigration
363
+ autoload :CommandRecorder, 'active_record/migration/command_recorder'
364
+
365
+
366
+ # This class is used to verify that all migrations have been run before
367
+ # loading a web page if config.active_record.migration_error is set to :page_load
368
+ class CheckPending
369
+ def initialize(app)
370
+ @app = app
371
+ @last_check = 0
372
+ end
373
+
374
+ def call(env)
375
+ mtime = ActiveRecord::Migrator.last_migration.mtime.to_i
376
+ if @last_check < mtime
377
+ ActiveRecord::DataMigration.check_pending!
378
+ @last_check = mtime
379
+ end
380
+ @app.call(env)
381
+ end
382
+ end
383
+
384
+ class << self
385
+ attr_accessor :delegate # :nodoc:
386
+ attr_accessor :disable_ddl_transaction # :nodoc:
387
+
388
+ def check_pending!(connection = Base.connection)
389
+ raise ActiveRecord::PendingDataMigrationError if ActiveRecord::Migrator.needs_migration?(connection)
390
+ end
391
+
392
+ def load_schema_if_pending!
393
+ if ActiveRecord::DataMigrator.needs_migration?
394
+ ActiveRecord::Tasks::DatabaseTasks.load_schema
395
+ check_pending!
396
+ end
397
+ end
398
+
399
+ def maintain_test_schema! # :nodoc:
400
+ if ActiveRecord::Base.maintain_test_schema
401
+ suppress_messages { load_schema_if_pending! }
402
+ end
403
+ end
404
+
405
+ def method_missing(name, *args, &block) # :nodoc:
406
+ (delegate || superclass.delegate).send(name, *args, &block)
407
+ end
408
+
409
+ def migrate(direction)
410
+ new.migrate :up
411
+ end
412
+
413
+ # Disable DDL transactions for this migration.
414
+ def disable_ddl_transaction!
415
+ @disable_ddl_transaction = true
416
+ end
417
+ end
418
+
419
+ def disable_ddl_transaction # :nodoc:
420
+ self.class.disable_ddl_transaction
421
+ end
422
+
423
+ cattr_accessor :verbose
424
+ attr_accessor :name, :version
425
+
426
+ def initialize(name = self.class.name, version = nil)
427
+ @name = name
428
+ @version = version
429
+ @connection = nil
430
+ end
431
+
432
+ self.verbose = true
433
+ # instantiate the delegate object after initialize is defined
434
+ self.delegate = new
435
+
436
+ class ReversibleBlockHelper < Struct.new(:reverting) # :nodoc:
437
+ def up
438
+ yield unless reverting
439
+ end
440
+
441
+ def down
442
+ yield if reverting
443
+ end
444
+ end
445
+
446
+ # Used to specify an operation that can be run in one direction or another.
447
+ # Call the methods +up+ and +down+ of the yielded object to run a block
448
+ # only in one given direction.
449
+ # The whole block will be called in the right order within the migration.
450
+ #
451
+ # In the following example, the looping on users will always be done
452
+ # when the three columns 'first_name', 'last_name' and 'full_name' exist,
453
+ # even when migrating down:
454
+ #
455
+ # class SplitNameMigration < ActiveRecord::Migration
456
+ # def change
457
+ # add_column :users, :first_name, :string
458
+ # add_column :users, :last_name, :string
459
+ #
460
+ # reversible do |dir|
461
+ # User.reset_column_information
462
+ # User.all.each do |u|
463
+ # dir.up { u.first_name, u.last_name = u.full_name.split(' ') }
464
+ # dir.down { u.full_name = "#{u.first_name} #{u.last_name}" }
465
+ # u.save
466
+ # end
467
+ # end
468
+ #
469
+ # revert { add_column :users, :full_name, :string }
470
+ # end
471
+ # end
472
+ def reversible
473
+ helper = ReversibleBlockHelper.new(reverting?)
474
+ execute_block{ yield helper }
475
+ end
476
+
477
+ # Runs the given migration classes.
478
+ # Last argument can specify options:
479
+ def run(*migration_classes)
480
+ migration_classes.each do |migration_class|
481
+ migration_class.new.exec_migration(@connection, :up)
482
+ end
483
+ end
484
+
485
+ def up
486
+ self.class.delegate = self
487
+ return unless self.class.respond_to?(:up)
488
+ self.class.up
489
+ end
490
+
491
+ def down
492
+ self.class.delegate = self
493
+ return unless self.class.respond_to?(:down)
494
+ self.class.down
495
+ end
496
+
497
+ # Execute this migration in the named direction
498
+ def migrate(direction)
499
+ return unless respond_to?(direction)
500
+
501
+ announce "migrating seeds"
502
+
503
+ time = nil
504
+ ActiveRecord::Base.connection_pool.with_connection do |conn|
505
+ time = Benchmark.measure do
506
+ exec_migration(conn, direction)
507
+ end
508
+ end
509
+
510
+ case direction
511
+ when :up then announce "migrated (%.4fs)" % time.real; write
512
+ when :down then announce "reverted (%.4fs)" % time.real; write
513
+ end
514
+ end
515
+
516
+ def exec_migration(conn, direction)
517
+ @connection = conn
518
+ self.up
519
+ ensure
520
+ @connection = nil
521
+ end
522
+
523
+ def write(text="")
524
+ puts(text) if verbose
525
+ end
526
+
527
+ def announce(message)
528
+ text = "#{version} #{name}: #{message}"
529
+ length = [0, 75 - text.length].max
530
+ write "== %s %s" % [text, "=" * length]
531
+ end
532
+
533
+ def say(message, subitem=false)
534
+ write "#{subitem ? " ->" : "--"} #{message}"
535
+ end
536
+
537
+ def say_with_time(message)
538
+ say(message)
539
+ result = nil
540
+ time = Benchmark.measure { result = yield }
541
+ say "%.4fs" % time.real, :subitem
542
+ say("#{result} rows", :subitem) if result.is_a?(Integer)
543
+ result
544
+ end
545
+
546
+ def suppress_messages
547
+ save, self.verbose = verbose, false
548
+ yield
549
+ ensure
550
+ self.verbose = save
551
+ end
552
+
553
+ def connection
554
+ @connection || ActiveRecord::Base.connection
555
+ end
556
+
557
+ def method_missing(method, *arguments, &block)
558
+ arg_list = arguments.map{ |a| a.inspect } * ', '
559
+
560
+ say_with_time "#{method}(#{arg_list})" do
561
+ unless @connection.respond_to? :revert
562
+ unless arguments.empty? || method == :execute
563
+ arguments[0] = proper_table_name(arguments.first, table_name_options)
564
+ arguments[1] = proper_table_name(arguments.second, table_name_options) if method == :rename_table
565
+ end
566
+ end
567
+ return super unless connection.respond_to?(method)
568
+ connection.send(method, *arguments, &block)
569
+ end
570
+ end
571
+
572
+ def copy(destination, sources, options = {})
573
+ copied = []
574
+
575
+ FileUtils.mkdir_p(destination) unless File.exist?(destination)
576
+
577
+ destination_migrations = ActiveRecord::Migrator.migrations(destination)
578
+ last = destination_migrations.last
579
+ sources.each do |scope, path|
580
+ source_migrations = ActiveRecord::Migrator.migrations(path)
581
+
582
+ source_migrations.each do |migration|
583
+ source = File.binread(migration.filename)
584
+ inserted_comment = "# This migration comes from #{scope} (originally #{migration.version})\n"
585
+ if /\A#.*\b(?:en)?coding:\s*\S+/ =~ source
586
+ # If we have a magic comment in the original migration,
587
+ # insert our comment after the first newline(end of the magic comment line)
588
+ # so the magic keep working.
589
+ # Note that magic comments must be at the first line(except sh-bang).
590
+ source[/\n/] = "\n#{inserted_comment}"
591
+ else
592
+ source = "#{inserted_comment}#{source}"
593
+ end
594
+
595
+ if duplicate = destination_migrations.detect { |m| m.name == migration.name }
596
+ if options[:on_skip] && duplicate.scope != scope.to_s
597
+ options[:on_skip].call(scope, migration)
598
+ end
599
+ next
600
+ end
601
+
602
+ migration.version = next_migration_number(last ? last.version + 1 : 0).to_i
603
+ new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.#{scope}.rb")
604
+ old_path, migration.filename = migration.filename, new_path
605
+ last = migration
606
+
607
+ File.binwrite(migration.filename, source)
608
+ copied << migration
609
+ options[:on_copy].call(scope, migration, old_path) if options[:on_copy]
610
+ destination_migrations << migration
611
+ end
612
+ end
613
+
614
+ copied
615
+ end
616
+
617
+ # Finds the correct table name given an Active Record object.
618
+ # Uses the Active Record object's own table_name, or pre/suffix from the
619
+ # options passed in.
620
+ def proper_table_name(name, options = {})
621
+ if name.respond_to? :table_name
622
+ name.table_name
623
+ else
624
+ "#{options[:table_name_prefix]}#{name}#{options[:table_name_suffix]}"
625
+ end
626
+ end
627
+
628
+ # Determines the version number of the next migration.
629
+ def next_migration_number(number)
630
+ if ActiveRecord::Base.timestamped_migrations
631
+ [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % number].max
632
+ else
633
+ "%.3d" % number
634
+ end
635
+ end
636
+
637
+ def table_name_options(config = ActiveRecord::Base)
638
+ {
639
+ table_name_prefix: config.table_name_prefix,
640
+ table_name_suffix: config.table_name_suffix
641
+ }
642
+ end
643
+
644
+ private
645
+ def execute_block
646
+ if connection.respond_to? :execute_block
647
+ super # use normal delegation to record the block
648
+ else
649
+ yield
650
+ end
651
+ end
652
+ end
653
+
654
+ class NullDataMigration < MigrationProxy #:nodoc:
655
+ def initialize
656
+ super(nil, 0, nil, nil)
657
+ end
658
+
659
+ def mtime
660
+ 0
661
+ end
662
+ end
663
+
664
+ class DataMigrator#:nodoc:
665
+ class << self
666
+ attr_writer :migrations_paths
667
+ alias :migrations_path= :migrations_paths=
668
+
669
+ def migrate(migrations_paths, target_version = nil, &block)
670
+ case
671
+ when target_version.nil?
672
+ up(migrations_paths, target_version, &block)
673
+ when current_version == 0 && target_version == 0
674
+ []
675
+ when current_version > target_version
676
+ down(migrations_paths, target_version, &block)
677
+ else
678
+ up(migrations_paths, target_version, &block)
679
+ end
680
+ end
681
+
682
+ def rollback(migrations_paths, steps=1)
683
+ move(:down, migrations_paths, steps)
684
+ end
685
+
686
+ def forward(migrations_paths, steps=1)
687
+ move(:up, migrations_paths, steps)
688
+ end
689
+
690
+ def up(migrations_paths, target_version = nil)
691
+ migrations = migrations(migrations_paths)
692
+ migrations.select! { |m| yield m } if block_given?
693
+
694
+ self.new(:up, migrations, target_version).migrate
695
+ end
696
+
697
+ def down(migrations_paths, target_version = nil, &block)
698
+ migrations = migrations(migrations_paths)
699
+ migrations.select! { |m| yield m } if block_given?
700
+
701
+ self.new(:down, migrations, target_version).migrate
702
+ end
703
+
704
+ def run(direction, migrations_paths, target_version)
705
+ self.new(direction, migrations(migrations_paths), target_version).run
706
+ end
707
+
708
+ def open(migrations_paths)
709
+ self.new(:up, migrations(migrations_paths), nil)
710
+ end
711
+
712
+ def schema_migrations_table_name
713
+ SchemaDataMigration.table_name
714
+ end
715
+
716
+ def get_all_versions
717
+ SchemaDataMigration.all.map { |x| x.version.to_i }.sort
718
+ end
719
+
720
+ def current_version(connection = Base.connection)
721
+ sm_table = schema_migrations_table_name
722
+ if connection.table_exists?(sm_table)
723
+ get_all_versions.max || 0
724
+ else
725
+ 0
726
+ end
727
+ end
728
+
729
+ def needs_migration?(connection = Base.connection)
730
+ current_version(connection) < last_version
731
+ end
732
+
733
+ def last_version
734
+ last_migration.version
735
+ end
736
+
737
+ def last_migration #:nodoc:
738
+ migrations(migrations_paths).last || NullDataMigration.new
739
+ end
740
+
741
+ def proper_table_name(name, options = {})
742
+ ActiveSupport::Deprecation.warn "ActiveRecord::Migrator.proper_table_name is deprecated and will be removed in Rails 4.2. Use the proper_table_name instance method on ActiveRecord::Migration instead"
743
+ options = {
744
+ table_name_prefix: ActiveRecord::Base.table_name_prefix,
745
+ table_name_suffix: ActiveRecord::Base.table_name_suffix
746
+ }.merge(options)
747
+ if name.respond_to? :table_name
748
+ name.table_name
749
+ else
750
+ "#{options[:table_name_prefix]}#{name}#{options[:table_name_suffix]}"
751
+ end
752
+ end
753
+
754
+ def migrations_paths
755
+ @migrations_paths ||= ['db/seeds']
756
+ # just to not break things if someone uses: migration_path = some_string
757
+ Array(@migrations_paths)
758
+ end
759
+
760
+ def migrations_path
761
+ migrations_paths.first
762
+ end
763
+
764
+ def migrations(paths)
765
+ paths = Array(paths)
766
+
767
+ files = Dir[*paths.map { |p| "#{p}/**/[0-9]*_*.rb" }]
768
+
769
+ migrations = files.map do |file|
770
+ version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
771
+
772
+ raise IllegalMigrationNameError.new(file) unless version
773
+ version = version.to_i
774
+ name = name.camelize
775
+
776
+ MigrationProxy.new(name, version, file, scope)
777
+ end
778
+
779
+ migrations.sort_by(&:version)
780
+ end
781
+
782
+ private
783
+
784
+ def move(direction, migrations_paths, steps)
785
+ migrator = self.new(direction, migrations(migrations_paths))
786
+ start_index = migrator.migrations.index(migrator.current_migration)
787
+
788
+ if start_index
789
+ finish = migrator.migrations[start_index + steps]
790
+ version = finish ? finish.version : 0
791
+ send(direction, migrations_paths, version)
792
+ end
793
+ end
794
+ end
795
+
796
+ def initialize(direction, migrations, target_version = nil)
797
+ raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?
798
+
799
+ @direction = direction
800
+ @target_version = target_version
801
+ @migrated_versions = nil
802
+ @migrations = migrations
803
+
804
+ validate(@migrations)
805
+
806
+ Base.connection.initialize_schema_migrations_table
807
+ end
808
+
809
+ def current_version
810
+ migrated.max || 0
811
+ end
812
+
813
+ def current_migration
814
+ migrations.detect { |m| m.version == current_version }
815
+ end
816
+ alias :current :current_migration
817
+
818
+ def run
819
+ migration = migrations.detect { |m| m.version == @target_version }
820
+ raise UnknownMigrationVersionError.new(@target_version) if migration.nil?
821
+ unless (up? && migrated.include?(migration.version.to_i)) || (down? && !migrated.include?(migration.version.to_i))
822
+ begin
823
+ execute_migration_in_transaction(migration, @direction)
824
+ rescue => e
825
+ canceled_msg = use_transaction?(migration) ? ", this migration was canceled" : ""
826
+ raise StandardError, "An error has occurred#{canceled_msg}:\n\n#{e}", e.backtrace
827
+ end
828
+ end
829
+ end
830
+
831
+ def migrate
832
+ if !target && @target_version && @target_version > 0
833
+ raise UnknownMigrationVersionError.new(@target_version)
834
+ end
835
+
836
+ runnable.each do |migration|
837
+ Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
838
+
839
+ begin
840
+ execute_migration_in_transaction(migration, @direction)
841
+ rescue => e
842
+ canceled_msg = use_transaction?(migration) ? "this and " : ""
843
+ raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace
844
+ end
845
+ end
846
+ end
847
+
848
+ def runnable
849
+ runnable = migrations[start..finish]
850
+ if up?
851
+ runnable.reject { |m| ran?(m) }
852
+ else
853
+ # skip the last migration if we're headed down, but not ALL the way down
854
+ runnable.pop if target
855
+ runnable.find_all { |m| ran?(m) }
856
+ end
857
+ end
858
+
859
+ def migrations
860
+ down? ? @migrations.reverse : @migrations.sort_by(&:version)
861
+ end
862
+
863
+ def pending_migrations
864
+ already_migrated = migrated
865
+ migrations.reject { |m| already_migrated.include?(m.version) }
866
+ end
867
+
868
+ def migrated
869
+ @migrated_versions ||= Set.new(self.class.get_all_versions)
870
+ end
871
+
872
+ private
873
+ def ran?(migration)
874
+ migrated.include?(migration.version.to_i)
875
+ end
876
+
877
+ def execute_migration_in_transaction(migration, direction)
878
+ ddl_transaction(migration) do
879
+ migration.migrate(direction)
880
+ record_version_state_after_migrating(migration.version)
881
+ end
882
+ end
883
+
884
+ def target
885
+ migrations.detect { |m| m.version == @target_version }
886
+ end
887
+
888
+ def finish
889
+ migrations.index(target) || migrations.size - 1
890
+ end
891
+
892
+ def start
893
+ up? ? 0 : (migrations.index(current) || 0)
894
+ end
895
+
896
+ def validate(migrations)
897
+ name ,= migrations.group_by(&:name).find { |_,v| v.length > 1 }
898
+ raise DuplicateMigrationNameError.new(name) if name
899
+
900
+ version ,= migrations.group_by(&:version).find { |_,v| v.length > 1 }
901
+ raise DuplicateMigrationVersionError.new(version) if version
902
+ end
903
+
904
+ def record_version_state_after_migrating(version)
905
+ if down?
906
+ migrated.delete(version)
907
+ ActiveRecord::SchemaDataMigration.where(:version => version.to_s).delete_all
908
+ else
909
+ migrated << version
910
+ ActiveRecord::SchemaDataMigration.create!(:version => version.to_s)
911
+ end
912
+ end
913
+
914
+ def up?
915
+ @direction == :up
916
+ end
917
+
918
+ def down?
919
+ @direction == :down
920
+ end
921
+
922
+ # Wrap the migration in a transaction only if supported by the adapter.
923
+ def ddl_transaction(migration)
924
+ if use_transaction?(migration)
925
+ Base.transaction { yield }
926
+ else
927
+ yield
928
+ end
929
+ end
930
+
931
+ def use_transaction?(migration)
932
+ !migration.disable_ddl_transaction && Base.connection.supports_ddl_transactions?
933
+ end
934
+ end
935
+ end