seed_data_migrations 0.0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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