activerecord 3.2.22.5 → 4.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (162) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1024 -543
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +20 -29
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +55 -44
  7. data/lib/active_record/aggregations.rb +40 -34
  8. data/lib/active_record/associations.rb +204 -276
  9. data/lib/active_record/associations/alias_tracker.rb +1 -1
  10. data/lib/active_record/associations/association.rb +30 -35
  11. data/lib/active_record/associations/association_scope.rb +40 -40
  12. data/lib/active_record/associations/belongs_to_association.rb +15 -2
  13. data/lib/active_record/associations/builder/association.rb +81 -28
  14. data/lib/active_record/associations/builder/belongs_to.rb +35 -57
  15. data/lib/active_record/associations/builder/collection_association.rb +54 -40
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -64
  18. data/lib/active_record/associations/builder/has_one.rb +13 -50
  19. data/lib/active_record/associations/builder/singular_association.rb +13 -13
  20. data/lib/active_record/associations/collection_association.rb +92 -88
  21. data/lib/active_record/associations/collection_proxy.rb +913 -63
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -10
  23. data/lib/active_record/associations/has_many_association.rb +35 -9
  24. data/lib/active_record/associations/has_many_through_association.rb +24 -14
  25. data/lib/active_record/associations/has_one_association.rb +33 -13
  26. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +2 -2
  28. data/lib/active_record/associations/join_dependency/join_association.rb +17 -22
  29. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  30. data/lib/active_record/associations/join_helper.rb +1 -11
  31. data/lib/active_record/associations/preloader.rb +14 -17
  32. data/lib/active_record/associations/preloader/association.rb +29 -33
  33. data/lib/active_record/associations/preloader/collection_association.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +1 -1
  35. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  36. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  37. data/lib/active_record/associations/preloader/through_association.rb +13 -17
  38. data/lib/active_record/associations/singular_association.rb +11 -11
  39. data/lib/active_record/associations/through_association.rb +2 -2
  40. data/lib/active_record/attribute_assignment.rb +133 -153
  41. data/lib/active_record/attribute_methods.rb +196 -93
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
  43. data/lib/active_record/attribute_methods/dirty.rb +31 -28
  44. data/lib/active_record/attribute_methods/primary_key.rb +38 -30
  45. data/lib/active_record/attribute_methods/query.rb +5 -4
  46. data/lib/active_record/attribute_methods/read.rb +62 -91
  47. data/lib/active_record/attribute_methods/serialization.rb +97 -66
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -45
  49. data/lib/active_record/attribute_methods/write.rb +32 -39
  50. data/lib/active_record/autosave_association.rb +56 -70
  51. data/lib/active_record/base.rb +53 -450
  52. data/lib/active_record/callbacks.rb +53 -18
  53. data/lib/active_record/coders/yaml_column.rb +11 -9
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +353 -197
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -131
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -19
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -3
  59. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +101 -91
  60. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +59 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +225 -96
  62. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +99 -46
  64. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +114 -36
  65. data/lib/active_record/connection_adapters/column.rb +46 -24
  66. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  67. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  68. data/lib/active_record/connection_adapters/mysql_adapter.rb +181 -64
  69. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  70. data/lib/active_record/connection_adapters/postgresql/cast.rb +132 -0
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid.rb +347 -0
  73. data/lib/active_record/connection_adapters/postgresql/quoting.rb +158 -0
  74. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  75. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +448 -0
  76. data/lib/active_record/connection_adapters/postgresql_adapter.rb +454 -885
  77. data/lib/active_record/connection_adapters/schema_cache.rb +48 -16
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +574 -13
  79. data/lib/active_record/connection_handling.rb +98 -0
  80. data/lib/active_record/core.rb +428 -0
  81. data/lib/active_record/counter_cache.rb +106 -108
  82. data/lib/active_record/dynamic_matchers.rb +110 -63
  83. data/lib/active_record/errors.rb +25 -8
  84. data/lib/active_record/explain.rb +8 -58
  85. data/lib/active_record/explain_subscriber.rb +6 -3
  86. data/lib/active_record/fixture_set/file.rb +56 -0
  87. data/lib/active_record/fixtures.rb +146 -148
  88. data/lib/active_record/inheritance.rb +77 -59
  89. data/lib/active_record/integration.rb +5 -5
  90. data/lib/active_record/locale/en.yml +8 -1
  91. data/lib/active_record/locking/optimistic.rb +38 -42
  92. data/lib/active_record/locking/pessimistic.rb +4 -4
  93. data/lib/active_record/log_subscriber.rb +19 -9
  94. data/lib/active_record/migration.rb +318 -153
  95. data/lib/active_record/migration/command_recorder.rb +90 -31
  96. data/lib/active_record/migration/join_table.rb +15 -0
  97. data/lib/active_record/model_schema.rb +69 -92
  98. data/lib/active_record/nested_attributes.rb +113 -148
  99. data/lib/active_record/null_relation.rb +65 -0
  100. data/lib/active_record/persistence.rb +188 -97
  101. data/lib/active_record/query_cache.rb +18 -36
  102. data/lib/active_record/querying.rb +19 -15
  103. data/lib/active_record/railtie.rb +91 -36
  104. data/lib/active_record/railties/console_sandbox.rb +0 -2
  105. data/lib/active_record/railties/controller_runtime.rb +2 -2
  106. data/lib/active_record/railties/databases.rake +90 -309
  107. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  108. data/lib/active_record/readonly_attributes.rb +7 -3
  109. data/lib/active_record/reflection.rb +72 -56
  110. data/lib/active_record/relation.rb +241 -157
  111. data/lib/active_record/relation/batches.rb +25 -22
  112. data/lib/active_record/relation/calculations.rb +143 -121
  113. data/lib/active_record/relation/delegation.rb +96 -18
  114. data/lib/active_record/relation/finder_methods.rb +117 -183
  115. data/lib/active_record/relation/merger.rb +133 -0
  116. data/lib/active_record/relation/predicate_builder.rb +90 -42
  117. data/lib/active_record/relation/query_methods.rb +666 -136
  118. data/lib/active_record/relation/spawn_methods.rb +43 -150
  119. data/lib/active_record/result.rb +33 -6
  120. data/lib/active_record/sanitization.rb +24 -50
  121. data/lib/active_record/schema.rb +19 -12
  122. data/lib/active_record/schema_dumper.rb +31 -39
  123. data/lib/active_record/schema_migration.rb +36 -0
  124. data/lib/active_record/scoping.rb +0 -124
  125. data/lib/active_record/scoping/default.rb +48 -45
  126. data/lib/active_record/scoping/named.rb +74 -103
  127. data/lib/active_record/serialization.rb +6 -2
  128. data/lib/active_record/serializers/xml_serializer.rb +9 -15
  129. data/lib/active_record/store.rb +119 -15
  130. data/lib/active_record/tasks/database_tasks.rb +158 -0
  131. data/lib/active_record/tasks/mysql_database_tasks.rb +138 -0
  132. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  133. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  134. data/lib/active_record/test_case.rb +61 -38
  135. data/lib/active_record/timestamp.rb +8 -9
  136. data/lib/active_record/transactions.rb +65 -51
  137. data/lib/active_record/validations.rb +17 -15
  138. data/lib/active_record/validations/associated.rb +20 -14
  139. data/lib/active_record/validations/presence.rb +65 -0
  140. data/lib/active_record/validations/uniqueness.rb +93 -52
  141. data/lib/active_record/version.rb +4 -4
  142. data/lib/rails/generators/active_record.rb +3 -5
  143. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -7
  144. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  145. data/lib/rails/generators/active_record/model/model_generator.rb +4 -3
  146. data/lib/rails/generators/active_record/model/templates/model.rb +1 -6
  147. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  148. metadata +53 -46
  149. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  150. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  151. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  152. data/lib/active_record/dynamic_finder_match.rb +0 -68
  153. data/lib/active_record/dynamic_scope_match.rb +0 -23
  154. data/lib/active_record/fixtures/file.rb +0 -65
  155. data/lib/active_record/identity_map.rb +0 -162
  156. data/lib/active_record/observer.rb +0 -121
  157. data/lib/active_record/session_store.rb +0 -360
  158. data/lib/rails/generators/active_record/migration.rb +0 -15
  159. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  160. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  161. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  162. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,7 +1,5 @@
1
- require "active_support/core_ext/module/delegation"
2
1
  require "active_support/core_ext/class/attribute_accessors"
3
- require "active_support/core_ext/array/wrap"
4
- require 'active_support/deprecation'
2
+ require 'set'
5
3
 
6
4
  module ActiveRecord
7
5
  # Exception that can be raised to stop migrations from going backwards.
@@ -32,6 +30,12 @@ module ActiveRecord
32
30
  end
33
31
  end
34
32
 
33
+ class PendingMigrationError < ActiveRecordError#:nodoc:
34
+ def initialize
35
+ super("Migrations are pending; run 'rake db:migrate RAILS_ENV=#{Rails.env}' to resolve this issue.")
36
+ end
37
+ end
38
+
35
39
  # = Active Record Migrations
36
40
  #
37
41
  # Migrations can manage the evolution of a schema used by several physical
@@ -46,7 +50,7 @@ module ActiveRecord
46
50
  #
47
51
  # class AddSsl < ActiveRecord::Migration
48
52
  # def up
49
- # add_column :accounts, :ssl_enabled, :boolean, :default => 1
53
+ # add_column :accounts, :ssl_enabled, :boolean, default: true
50
54
  # end
51
55
  #
52
56
  # def down
@@ -58,7 +62,7 @@ module ActiveRecord
58
62
  # if you're backing out of the migration. It shows how all migrations have
59
63
  # two methods +up+ and +down+ that describes the transformations
60
64
  # required to implement or remove the migration. These methods can consist
61
- # of both the migration specific methods like add_column and remove_column,
65
+ # of both the migration specific methods like +add_column+ and +remove_column+,
62
66
  # but may also contain regular Ruby code for generating data needed for the
63
67
  # transformations.
64
68
  #
@@ -74,9 +78,9 @@ module ActiveRecord
74
78
  # t.integer :position
75
79
  # end
76
80
  #
77
- # SystemSetting.create :name => "notice",
78
- # :label => "Use notice?",
79
- # :value => 1
81
+ # SystemSetting.create name: 'notice',
82
+ # label: 'Use notice?',
83
+ # value: 1
80
84
  # end
81
85
  #
82
86
  # def down
@@ -84,19 +88,22 @@ module ActiveRecord
84
88
  # end
85
89
  # end
86
90
  #
87
- # This migration first adds the system_settings table, then creates the very
91
+ # This migration first adds the +system_settings+ table, then creates the very
88
92
  # first row in it using the Active Record model that relies on the table. It
89
- # also uses the more advanced create_table syntax where you can specify a
93
+ # also uses the more advanced +create_table+ syntax where you can specify a
90
94
  # complete table schema in one block call.
91
95
  #
92
96
  # == Available transformations
93
97
  #
94
- # * <tt>create_table(name, options)</tt> Creates a table called +name+ and
98
+ # * <tt>create_table(name, options)</tt>: Creates a table called +name+ and
95
99
  # makes the table object available to a block that can then add columns to it,
96
- # following the same format as add_column. See example above. The options hash
100
+ # following the same format as +add_column+. See example above. The options hash
97
101
  # is for fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create
98
102
  # table definition.
99
103
  # * <tt>drop_table(name)</tt>: Drops the table called +name+.
104
+ # * <tt>change_table(name, options)</tt>: Allows to make column alterations to
105
+ # the table called +name+. It makes the table object availabe to a block that
106
+ # can then add/remove columns, indexes or foreign keys to it.
100
107
  # * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
101
108
  # to +new_name+.
102
109
  # * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column
@@ -105,9 +112,9 @@ module ActiveRecord
105
112
  # <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>,
106
113
  # <tt>:decimal</tt>, <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
107
114
  # <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>. A default value can be
108
- # specified by passing an +options+ hash like <tt>{ :default => 11 }</tt>.
115
+ # specified by passing an +options+ hash like <tt>{ default: 11 }</tt>.
109
116
  # Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g.
110
- # <tt>{ :limit => 50, :null => false }</tt>) -- see
117
+ # <tt>{ limit: 50, null: false }</tt>) -- see
111
118
  # ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
112
119
  # * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames
113
120
  # a column but keeps the type and content.
@@ -118,11 +125,11 @@ module ActiveRecord
118
125
  # * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
119
126
  # with the name of the column. Other options include
120
127
  # <tt>:name</tt>, <tt>:unique</tt> (e.g.
121
- # <tt>{ :name => "users_name_index", :unique => true }</tt>) and <tt>:order</tt>
122
- # (e.g. { :order => {:name => :desc} }</tt>).
123
- # * <tt>remove_index(table_name, :column => column_name)</tt>: Removes the index
128
+ # <tt>{ name: 'users_name_index', unique: true }</tt>) and <tt>:order</tt>
129
+ # (e.g. <tt>{ order: { name: :desc } }</tt>).
130
+ # * <tt>remove_index(table_name, column: column_name)</tt>: Removes the index
124
131
  # specified by +column_name+.
125
- # * <tt>remove_index(table_name, :name => index_name)</tt>: Removes the index
132
+ # * <tt>remove_index(table_name, name: index_name)</tt>: Removes the index
126
133
  # specified by +index_name+.
127
134
  #
128
135
  # == Irreversible transformations
@@ -326,10 +333,30 @@ module ActiveRecord
326
333
  class Migration
327
334
  autoload :CommandRecorder, 'active_record/migration/command_recorder'
328
335
 
336
+
337
+ # This class is used to verify that all migrations have been run before
338
+ # loading a web page if config.active_record.migration_error is set to :page_load
339
+ class CheckPending
340
+ def initialize(app)
341
+ @app = app
342
+ end
343
+
344
+ def call(env)
345
+ ActiveRecord::Base.logger.silence do
346
+ ActiveRecord::Migration.check_pending!
347
+ end
348
+ @app.call(env)
349
+ end
350
+ end
351
+
329
352
  class << self
330
353
  attr_accessor :delegate # :nodoc:
331
354
  end
332
355
 
356
+ def self.check_pending!
357
+ raise ActiveRecord::PendingMigrationError if ActiveRecord::Migrator.needs_migration?
358
+ end
359
+
333
360
  def self.method_missing(name, *args, &block) # :nodoc:
334
361
  (delegate || superclass.delegate).send(name, *args, &block)
335
362
  end
@@ -342,26 +369,133 @@ module ActiveRecord
342
369
 
343
370
  attr_accessor :name, :version
344
371
 
345
- def initialize
346
- @name = self.class.name
347
- @version = nil
372
+ def initialize(name = self.class.name, version = nil)
373
+ @name = name
374
+ @version = version
348
375
  @connection = nil
349
- @reverting = false
350
376
  end
351
377
 
352
378
  # instantiate the delegate object after initialize is defined
353
379
  self.verbose = true
354
380
  self.delegate = new
355
381
 
356
- def revert
357
- @reverting = true
358
- yield
359
- ensure
360
- @reverting = false
382
+ # Reverses the migration commands for the given block and
383
+ # the given migrations.
384
+ #
385
+ # The following migration will remove the table 'horses'
386
+ # and create the table 'apples' on the way up, and the reverse
387
+ # on the way down.
388
+ #
389
+ # class FixTLMigration < ActiveRecord::Migration
390
+ # def change
391
+ # revert do
392
+ # create_table(:horses) do |t|
393
+ # t.text :content
394
+ # t.datetime :remind_at
395
+ # end
396
+ # end
397
+ # create_table(:apples) do |t|
398
+ # t.string :variety
399
+ # end
400
+ # end
401
+ # end
402
+ #
403
+ # Or equivalently, if +TenderloveMigration+ is defined as in the
404
+ # documentation for Migration:
405
+ #
406
+ # require_relative '2012121212_tenderlove_migration'
407
+ #
408
+ # class FixupTLMigration < ActiveRecord::Migration
409
+ # def change
410
+ # revert TenderloveMigration
411
+ #
412
+ # create_table(:apples) do |t|
413
+ # t.string :variety
414
+ # end
415
+ # end
416
+ # end
417
+ #
418
+ # This command can be nested.
419
+ def revert(*migration_classes)
420
+ run(*migration_classes.reverse, revert: true) unless migration_classes.empty?
421
+ if block_given?
422
+ if @connection.respond_to? :revert
423
+ @connection.revert { yield }
424
+ else
425
+ recorder = CommandRecorder.new(@connection)
426
+ @connection = recorder
427
+ suppress_messages do
428
+ @connection.revert { yield }
429
+ end
430
+ @connection = recorder.delegate
431
+ recorder.commands.each do |cmd, args, block|
432
+ send(cmd, *args, &block)
433
+ end
434
+ end
435
+ end
361
436
  end
362
437
 
363
438
  def reverting?
364
- @reverting
439
+ @connection.respond_to?(:reverting) && @connection.reverting
440
+ end
441
+
442
+ class ReversibleBlockHelper < Struct.new(:reverting)
443
+ def up
444
+ yield unless reverting
445
+ end
446
+
447
+ def down
448
+ yield if reverting
449
+ end
450
+ end
451
+
452
+ # Used to specify an operation that can be run in one direction or another.
453
+ # Call the methods +up+ and +down+ of the yielded object to run a block
454
+ # only in one given direction.
455
+ # The whole block will be called in the right order within the migration.
456
+ #
457
+ # In the following example, the looping on users will always be done
458
+ # when the three columns 'first_name', 'last_name' and 'full_name' exist,
459
+ # even when migrating down:
460
+ #
461
+ # class SplitNameMigration < ActiveRecord::Migration
462
+ # def change
463
+ # add_column :users, :first_name, :string
464
+ # add_column :users, :last_name, :string
465
+ #
466
+ # reversible do |dir|
467
+ # User.reset_column_information
468
+ # User.all.each do |u|
469
+ # dir.up { u.first_name, u.last_name = u.full_name.split(' ') }
470
+ # dir.down { u.full_name = "#{u.first_name} #{u.last_name}" }
471
+ # u.save
472
+ # end
473
+ # end
474
+ #
475
+ # revert { add_column :users, :full_name, :string }
476
+ # end
477
+ # end
478
+ def reversible
479
+ helper = ReversibleBlockHelper.new(reverting?)
480
+ execute_block{ yield helper }
481
+ end
482
+
483
+ # Runs the given migration classes.
484
+ # Last argument can specify options:
485
+ # - :direction (default is :up)
486
+ # - :revert (default is false)
487
+ def run(*migration_classes)
488
+ opts = migration_classes.extract_options!
489
+ dir = opts[:direction] || :up
490
+ dir = (dir == :down ? :up : :down) if opts[:revert]
491
+ if reverting?
492
+ # If in revert and going :up, say, we want to execute :down without reverting, so
493
+ revert { run(*migration_classes, direction: dir, revert: true) }
494
+ else
495
+ migration_classes.each do |migration_class|
496
+ migration_class.new.exec_migration(@connection, dir)
497
+ end
498
+ end
365
499
  end
366
500
 
367
501
  def up
@@ -387,29 +521,9 @@ module ActiveRecord
387
521
 
388
522
  time = nil
389
523
  ActiveRecord::Base.connection_pool.with_connection do |conn|
390
- @connection = conn
391
- if respond_to?(:change)
392
- if direction == :down
393
- recorder = CommandRecorder.new(@connection)
394
- suppress_messages do
395
- @connection = recorder
396
- change
397
- end
398
- @connection = conn
399
- time = Benchmark.measure {
400
- self.revert {
401
- recorder.inverse.each do |cmd, args|
402
- send(cmd, *args)
403
- end
404
- }
405
- }
406
- else
407
- time = Benchmark.measure { change }
408
- end
409
- else
410
- time = Benchmark.measure { send(direction) }
524
+ time = Benchmark.measure do
525
+ exec_migration(conn, direction)
411
526
  end
412
- @connection = nil
413
527
  end
414
528
 
415
529
  case direction
@@ -418,6 +532,21 @@ module ActiveRecord
418
532
  end
419
533
  end
420
534
 
535
+ def exec_migration(conn, direction)
536
+ @connection = conn
537
+ if respond_to?(:change)
538
+ if direction == :down
539
+ revert { change }
540
+ else
541
+ change
542
+ end
543
+ else
544
+ send(direction)
545
+ end
546
+ ensure
547
+ @connection = nil
548
+ end
549
+
421
550
  def write(text="")
422
551
  puts(text) if verbose
423
552
  end
@@ -456,9 +585,9 @@ module ActiveRecord
456
585
  arg_list = arguments.map{ |a| a.inspect } * ', '
457
586
 
458
587
  say_with_time "#{method}(#{arg_list})" do
459
- unless reverting?
588
+ unless @connection.respond_to? :revert
460
589
  unless arguments.empty? || method == :execute
461
- arguments[0] = Migrator.proper_table_name(arguments.first) unless method == :assume_migrated_upto_version
590
+ arguments[0] = Migrator.proper_table_name(arguments.first)
462
591
  arguments[1] = Migrator.proper_table_name(arguments.second) if method == :rename_table
463
592
  end
464
593
  end
@@ -510,6 +639,15 @@ module ActiveRecord
510
639
  "%.3d" % number
511
640
  end
512
641
  end
642
+
643
+ private
644
+ def execute_block
645
+ if connection.respond_to? :execute_block
646
+ super # use normal delegation to record the block
647
+ else
648
+ yield
649
+ end
650
+ end
513
651
  end
514
652
 
515
653
  # MigrationProxy is used to defer loading of the actual migration classes
@@ -547,14 +685,14 @@ module ActiveRecord
547
685
 
548
686
  def migrate(migrations_paths, target_version = nil, &block)
549
687
  case
550
- when target_version.nil?
551
- up(migrations_paths, target_version, &block)
552
- when current_version == 0 && target_version == 0
553
- []
554
- when current_version > target_version
555
- down(migrations_paths, target_version, &block)
556
- else
557
- up(migrations_paths, target_version, &block)
688
+ when target_version.nil?
689
+ up(migrations_paths, target_version, &block)
690
+ when current_version == 0 && target_version == 0
691
+ []
692
+ when current_version > target_version
693
+ down(migrations_paths, target_version, &block)
694
+ else
695
+ up(migrations_paths, target_version, &block)
558
696
  end
559
697
  end
560
698
 
@@ -566,25 +704,34 @@ module ActiveRecord
566
704
  move(:up, migrations_paths, steps)
567
705
  end
568
706
 
569
- def up(migrations_paths, target_version = nil, &block)
570
- self.new(:up, migrations_paths, target_version).migrate(&block)
707
+ def up(migrations_paths, target_version = nil)
708
+ migrations = migrations(migrations_paths)
709
+ migrations.select! { |m| yield m } if block_given?
710
+
711
+ self.new(:up, migrations, target_version).migrate
571
712
  end
572
713
 
573
714
  def down(migrations_paths, target_version = nil, &block)
574
- self.new(:down, migrations_paths, target_version).migrate(&block)
715
+ migrations = migrations(migrations_paths)
716
+ migrations.select! { |m| yield m } if block_given?
717
+
718
+ self.new(:down, migrations, target_version).migrate
575
719
  end
576
720
 
577
721
  def run(direction, migrations_paths, target_version)
578
- self.new(direction, migrations_paths, target_version).run
722
+ self.new(direction, migrations(migrations_paths), target_version).run
723
+ end
724
+
725
+ def open(migrations_paths)
726
+ self.new(:up, migrations(migrations_paths), nil)
579
727
  end
580
728
 
581
729
  def schema_migrations_table_name
582
- Base.table_name_prefix + 'schema_migrations' + Base.table_name_suffix
730
+ SchemaMigration.table_name
583
731
  end
584
732
 
585
733
  def get_all_versions
586
- table = Arel::Table.new(schema_migrations_table_name)
587
- Base.connection.select_values(table.project(table['version'])).map{ |v| v.to_i }.sort
734
+ SchemaMigration.all.map { |x| x.version.to_i }.sort
588
735
  end
589
736
 
590
737
  def current_version
@@ -596,35 +743,37 @@ module ActiveRecord
596
743
  end
597
744
  end
598
745
 
746
+ def needs_migration?
747
+ current_version < last_version
748
+ end
749
+
750
+ def last_version
751
+ migrations(migrations_paths).last.try(:version)||0
752
+ end
753
+
599
754
  def proper_table_name(name)
600
755
  # Use the Active Record objects own table_name, or pre/suffix from ActiveRecord::Base if name is a symbol/string
601
- name.table_name rescue "#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}"
756
+ if name.respond_to? :table_name
757
+ name.table_name
758
+ else
759
+ "#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}"
760
+ end
602
761
  end
603
762
 
604
763
  def migrations_paths
605
764
  @migrations_paths ||= ['db/migrate']
606
765
  # just to not break things if someone uses: migration_path = some_string
607
- Array.wrap(@migrations_paths)
766
+ Array(@migrations_paths)
608
767
  end
609
768
 
610
769
  def migrations_path
611
770
  migrations_paths.first
612
771
  end
613
772
 
614
- def migrations(paths, *args)
615
- if args.empty?
616
- subdirectories = true
617
- else
618
- subdirectories = args.first
619
- ActiveSupport::Deprecation.warn "The `subdirectories` argument to `migrations` is deprecated"
620
- end
621
-
622
- paths = Array.wrap(paths)
623
-
624
- glob = subdirectories ? "**/" : ""
625
- files = Dir[*paths.map { |p| "#{p}/#{glob}[0-9]*_*.rb" }]
773
+ def migrations(paths)
774
+ paths = Array(paths)
626
775
 
627
- seen = Hash.new false
776
+ files = Dir[*paths.map { |p| "#{p}/**/[0-9]*_*.rb" }]
628
777
 
629
778
  migrations = files.map do |file|
630
779
  version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
@@ -633,11 +782,6 @@ module ActiveRecord
633
782
  version = version.to_i
634
783
  name = name.camelize
635
784
 
636
- raise DuplicateMigrationVersionError.new(version) if seen[version]
637
- raise DuplicateMigrationNameError.new(name) if seen[name]
638
-
639
- seen[version] = seen[name] = true
640
-
641
785
  MigrationProxy.new(name, version, file, scope)
642
786
  end
643
787
 
@@ -647,7 +791,7 @@ module ActiveRecord
647
791
  private
648
792
 
649
793
  def move(direction, migrations_paths, steps)
650
- migrator = self.new(direction, migrations_paths)
794
+ migrator = self.new(direction, migrations(migrations_paths))
651
795
  start_index = migrator.migrations.index(migrator.current_migration)
652
796
 
653
797
  if start_index
@@ -658,19 +802,33 @@ module ActiveRecord
658
802
  end
659
803
  end
660
804
 
661
- def initialize(direction, migrations_paths, target_version = nil)
805
+ def initialize(direction, migrations, target_version = nil)
662
806
  raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?
663
- Base.connection.initialize_schema_migrations_table
664
- @direction, @migrations_paths, @target_version = direction, migrations_paths, target_version
807
+
808
+ @direction = direction
809
+ @target_version = target_version
810
+ @migrated_versions = nil
811
+
812
+ if Array(migrations).grep(String).empty?
813
+ @migrations = migrations
814
+ else
815
+ ActiveSupport::Deprecation.warn "instantiate this class with a list of migrations"
816
+ @migrations = self.class.migrations(migrations)
817
+ end
818
+
819
+ validate(@migrations)
820
+
821
+ ActiveRecord::SchemaMigration.create_table
665
822
  end
666
823
 
667
824
  def current_version
668
- migrated.last || 0
825
+ migrated.sort.last || 0
669
826
  end
670
827
 
671
828
  def current_migration
672
829
  migrations.detect { |m| m.version == current_version }
673
830
  end
831
+ alias :current :current_migration
674
832
 
675
833
  def run
676
834
  target = migrations.detect { |m| m.version == @target_version }
@@ -681,101 +839,108 @@ module ActiveRecord
681
839
  end
682
840
  end
683
841
 
684
- def migrate(&block)
685
- current = migrations.detect { |m| m.version == current_version }
686
- target = migrations.detect { |m| m.version == @target_version }
687
-
688
- if target.nil? && @target_version && @target_version > 0
842
+ def migrate
843
+ if !target && @target_version && @target_version > 0
689
844
  raise UnknownMigrationVersionError.new(@target_version)
690
845
  end
691
846
 
692
- start = up? ? 0 : (migrations.index(current) || 0)
693
- finish = migrations.index(target) || migrations.size - 1
694
- runnable = migrations[start..finish]
847
+ running = runnable
695
848
 
696
- # skip the last migration if we're headed down, but not ALL the way down
697
- runnable.pop if down? && target
698
-
699
- ran = []
700
- runnable.each do |migration|
701
- if block && !block.call(migration)
702
- next
703
- end
849
+ if block_given?
850
+ message = "block argument to migrate is deprecated, please filter migrations before constructing the migrator"
851
+ ActiveSupport::Deprecation.warn message
852
+ running.select! { |m| yield m }
853
+ end
704
854
 
855
+ running.each do |migration|
705
856
  Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
706
857
 
707
- seen = migrated.include?(migration.version.to_i)
708
-
709
- # On our way up, we skip migrating the ones we've already migrated
710
- next if up? && seen
711
-
712
- # On our way down, we skip reverting the ones we've never migrated
713
- if down? && !seen
714
- migration.announce 'never migrated, skipping'; migration.write
715
- next
716
- end
717
-
718
858
  begin
719
859
  ddl_transaction do
720
860
  migration.migrate(@direction)
721
861
  record_version_state_after_migrating(migration.version)
722
862
  end
723
- ran << migration
724
863
  rescue => e
725
864
  canceled_msg = Base.connection.supports_ddl_transactions? ? "this and " : ""
726
865
  raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace
727
866
  end
728
867
  end
729
- ran
730
868
  end
731
869
 
732
- def migrations
733
- @migrations ||= begin
734
- migrations = self.class.migrations(@migrations_paths)
735
- down? ? migrations.reverse : migrations
870
+ def runnable
871
+ runnable = migrations[start..finish]
872
+ if up?
873
+ runnable.reject { |m| ran?(m) }
874
+ else
875
+ # skip the last migration if we're headed down, but not ALL the way down
876
+ runnable.pop if target
877
+ runnable.find_all { |m| ran?(m) }
736
878
  end
737
879
  end
738
880
 
881
+ def migrations
882
+ down? ? @migrations.reverse : @migrations.sort_by(&:version)
883
+ end
884
+
739
885
  def pending_migrations
740
886
  already_migrated = migrated
741
- migrations.reject { |m| already_migrated.include?(m.version.to_i) }
887
+ migrations.reject { |m| already_migrated.include?(m.version) }
742
888
  end
743
889
 
744
890
  def migrated
745
- @migrated_versions ||= self.class.get_all_versions
891
+ @migrated_versions ||= Set.new(self.class.get_all_versions)
746
892
  end
747
893
 
748
894
  private
749
- def record_version_state_after_migrating(version)
750
- table = Arel::Table.new(self.class.schema_migrations_table_name)
751
-
752
- @migrated_versions ||= []
753
- if down?
754
- @migrated_versions.delete(version)
755
- stmt = table.where(table["version"].eq(version.to_s)).compile_delete
756
- Base.connection.delete stmt
757
- else
758
- @migrated_versions.push(version).sort!
759
- stmt = table.compile_insert table["version"] => version.to_s
760
- Base.connection.insert stmt
761
- end
762
- end
895
+ def ran?(migration)
896
+ migrated.include?(migration.version.to_i)
897
+ end
763
898
 
764
- def up?
765
- @direction == :up
766
- end
899
+ def target
900
+ migrations.detect { |m| m.version == @target_version }
901
+ end
902
+
903
+ def finish
904
+ migrations.index(target) || migrations.size - 1
905
+ end
906
+
907
+ def start
908
+ up? ? 0 : (migrations.index(current) || 0)
909
+ end
767
910
 
768
- def down?
769
- @direction == :down
911
+ def validate(migrations)
912
+ name ,= migrations.group_by(&:name).find { |_,v| v.length > 1 }
913
+ raise DuplicateMigrationNameError.new(name) if name
914
+
915
+ version ,= migrations.group_by(&:version).find { |_,v| v.length > 1 }
916
+ raise DuplicateMigrationVersionError.new(version) if version
917
+ end
918
+
919
+ def record_version_state_after_migrating(version)
920
+ if down?
921
+ migrated.delete(version)
922
+ ActiveRecord::SchemaMigration.where(:version => version.to_s).delete_all
923
+ else
924
+ migrated << version
925
+ ActiveRecord::SchemaMigration.create!(:version => version.to_s)
770
926
  end
927
+ end
771
928
 
772
- # Wrap the migration in a transaction only if supported by the adapter.
773
- def ddl_transaction(&block)
774
- if Base.connection.supports_ddl_transactions?
775
- Base.transaction { block.call }
776
- else
777
- block.call
778
- end
929
+ def up?
930
+ @direction == :up
931
+ end
932
+
933
+ def down?
934
+ @direction == :down
935
+ end
936
+
937
+ # Wrap the migration in a transaction only if supported by the adapter.
938
+ def ddl_transaction
939
+ if Base.connection.supports_ddl_transactions?
940
+ Base.transaction { yield }
941
+ else
942
+ yield
779
943
  end
944
+ end
780
945
  end
781
946
  end