activerecord 3.2.22.4 → 4.0.13

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 (173) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2799 -617
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +23 -32
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +40 -34
  7. data/lib/active_record/association_relation.rb +22 -0
  8. data/lib/active_record/associations/alias_tracker.rb +4 -2
  9. data/lib/active_record/associations/association.rb +60 -46
  10. data/lib/active_record/associations/association_scope.rb +46 -40
  11. data/lib/active_record/associations/belongs_to_association.rb +17 -4
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  13. data/lib/active_record/associations/builder/association.rb +81 -28
  14. data/lib/active_record/associations/builder/belongs_to.rb +73 -56
  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 +130 -96
  21. data/lib/active_record/associations/collection_proxy.rb +916 -63
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +15 -13
  23. data/lib/active_record/associations/has_many_association.rb +35 -8
  24. data/lib/active_record/associations/has_many_through_association.rb +37 -17
  25. data/lib/active_record/associations/has_one_association.rb +42 -19
  26. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency/join_association.rb +39 -22
  28. data/lib/active_record/associations/join_dependency/join_base.rb +2 -2
  29. data/lib/active_record/associations/join_dependency/join_part.rb +21 -8
  30. data/lib/active_record/associations/join_dependency.rb +30 -9
  31. data/lib/active_record/associations/join_helper.rb +1 -11
  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 +2 -2
  35. data/lib/active_record/associations/preloader/has_many_through.rb +6 -2
  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/preloader.rb +20 -43
  39. data/lib/active_record/associations/singular_association.rb +11 -11
  40. data/lib/active_record/associations/through_association.rb +3 -3
  41. data/lib/active_record/associations.rb +223 -282
  42. data/lib/active_record/attribute_assignment.rb +134 -154
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
  44. data/lib/active_record/attribute_methods/dirty.rb +36 -29
  45. data/lib/active_record/attribute_methods/primary_key.rb +45 -31
  46. data/lib/active_record/attribute_methods/query.rb +5 -4
  47. data/lib/active_record/attribute_methods/read.rb +67 -90
  48. data/lib/active_record/attribute_methods/serialization.rb +133 -70
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +51 -45
  50. data/lib/active_record/attribute_methods/write.rb +34 -39
  51. data/lib/active_record/attribute_methods.rb +268 -108
  52. data/lib/active_record/autosave_association.rb +80 -73
  53. data/lib/active_record/base.rb +54 -451
  54. data/lib/active_record/callbacks.rb +60 -22
  55. data/lib/active_record/coders/yaml_column.rb +18 -21
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +347 -197
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +146 -138
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -19
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +19 -3
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +151 -142
  62. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +499 -217
  64. data/lib/active_record/connection_adapters/abstract/transaction.rb +208 -0
  65. data/lib/active_record/connection_adapters/abstract_adapter.rb +209 -44
  66. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +169 -61
  67. data/lib/active_record/connection_adapters/column.rb +67 -36
  68. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +28 -29
  70. data/lib/active_record/connection_adapters/mysql_adapter.rb +200 -73
  71. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +98 -0
  72. data/lib/active_record/connection_adapters/postgresql/cast.rb +160 -0
  73. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +240 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid.rb +374 -0
  75. data/lib/active_record/connection_adapters/postgresql/quoting.rb +183 -0
  76. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  77. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +508 -0
  78. data/lib/active_record/connection_adapters/postgresql_adapter.rb +544 -899
  79. data/lib/active_record/connection_adapters/schema_cache.rb +76 -16
  80. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +595 -16
  81. data/lib/active_record/connection_handling.rb +98 -0
  82. data/lib/active_record/core.rb +472 -0
  83. data/lib/active_record/counter_cache.rb +107 -108
  84. data/lib/active_record/dynamic_matchers.rb +115 -63
  85. data/lib/active_record/errors.rb +36 -18
  86. data/lib/active_record/explain.rb +15 -63
  87. data/lib/active_record/explain_registry.rb +30 -0
  88. data/lib/active_record/explain_subscriber.rb +8 -4
  89. data/lib/active_record/fixture_set/file.rb +55 -0
  90. data/lib/active_record/fixtures.rb +159 -155
  91. data/lib/active_record/inheritance.rb +93 -59
  92. data/lib/active_record/integration.rb +8 -8
  93. data/lib/active_record/locale/en.yml +8 -1
  94. data/lib/active_record/locking/optimistic.rb +39 -43
  95. data/lib/active_record/locking/pessimistic.rb +4 -4
  96. data/lib/active_record/log_subscriber.rb +19 -9
  97. data/lib/active_record/migration/command_recorder.rb +102 -33
  98. data/lib/active_record/migration/join_table.rb +15 -0
  99. data/lib/active_record/migration.rb +411 -173
  100. data/lib/active_record/model_schema.rb +81 -94
  101. data/lib/active_record/nested_attributes.rb +173 -131
  102. data/lib/active_record/null_relation.rb +67 -0
  103. data/lib/active_record/persistence.rb +254 -106
  104. data/lib/active_record/query_cache.rb +18 -36
  105. data/lib/active_record/querying.rb +19 -15
  106. data/lib/active_record/railtie.rb +113 -38
  107. data/lib/active_record/railties/console_sandbox.rb +3 -4
  108. data/lib/active_record/railties/controller_runtime.rb +4 -3
  109. data/lib/active_record/railties/databases.rake +115 -368
  110. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  111. data/lib/active_record/readonly_attributes.rb +7 -3
  112. data/lib/active_record/reflection.rb +110 -61
  113. data/lib/active_record/relation/batches.rb +29 -29
  114. data/lib/active_record/relation/calculations.rb +155 -125
  115. data/lib/active_record/relation/delegation.rb +94 -18
  116. data/lib/active_record/relation/finder_methods.rb +151 -203
  117. data/lib/active_record/relation/merger.rb +188 -0
  118. data/lib/active_record/relation/predicate_builder.rb +85 -42
  119. data/lib/active_record/relation/query_methods.rb +793 -146
  120. data/lib/active_record/relation/spawn_methods.rb +43 -150
  121. data/lib/active_record/relation.rb +293 -173
  122. data/lib/active_record/result.rb +48 -7
  123. data/lib/active_record/runtime_registry.rb +17 -0
  124. data/lib/active_record/sanitization.rb +41 -54
  125. data/lib/active_record/schema.rb +19 -12
  126. data/lib/active_record/schema_dumper.rb +41 -41
  127. data/lib/active_record/schema_migration.rb +46 -0
  128. data/lib/active_record/scoping/default.rb +56 -52
  129. data/lib/active_record/scoping/named.rb +78 -103
  130. data/lib/active_record/scoping.rb +54 -124
  131. data/lib/active_record/serialization.rb +6 -2
  132. data/lib/active_record/serializers/xml_serializer.rb +9 -15
  133. data/lib/active_record/statement_cache.rb +26 -0
  134. data/lib/active_record/store.rb +131 -15
  135. data/lib/active_record/tasks/database_tasks.rb +204 -0
  136. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  137. data/lib/active_record/tasks/mysql_database_tasks.rb +144 -0
  138. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  139. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  140. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  141. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  142. data/lib/active_record/test_case.rb +67 -38
  143. data/lib/active_record/timestamp.rb +16 -11
  144. data/lib/active_record/transactions.rb +73 -51
  145. data/lib/active_record/validations/associated.rb +19 -13
  146. data/lib/active_record/validations/presence.rb +65 -0
  147. data/lib/active_record/validations/uniqueness.rb +110 -57
  148. data/lib/active_record/validations.rb +18 -17
  149. data/lib/active_record/version.rb +7 -6
  150. data/lib/active_record.rb +63 -45
  151. data/lib/rails/generators/active_record/migration/migration_generator.rb +45 -8
  152. data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +4 -0
  153. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  154. data/lib/rails/generators/active_record/model/model_generator.rb +5 -4
  155. data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
  156. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  157. data/lib/rails/generators/active_record.rb +3 -5
  158. metadata +43 -29
  159. data/examples/associations.png +0 -0
  160. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  161. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  162. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  163. data/lib/active_record/dynamic_finder_match.rb +0 -68
  164. data/lib/active_record/dynamic_scope_match.rb +0 -23
  165. data/lib/active_record/fixtures/file.rb +0 -65
  166. data/lib/active_record/identity_map.rb +0 -162
  167. data/lib/active_record/observer.rb +0 -121
  168. data/lib/active_record/session_store.rb +0 -360
  169. data/lib/rails/generators/active_record/migration.rb +0 -15
  170. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  171. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  172. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  173. 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,16 @@ module ActiveRecord
32
30
  end
33
31
  end
34
32
 
33
+ class PendingMigrationError < ActiveRecordError#:nodoc:
34
+ def initialize
35
+ if defined?(Rails)
36
+ super("Migrations are pending; run 'bin/rake db:migrate RAILS_ENV=#{::Rails.env}' to resolve this issue.")
37
+ else
38
+ super("Migrations are pending; run 'bin/rake db:migrate' to resolve this issue.")
39
+ end
40
+ end
41
+ end
42
+
35
43
  # = Active Record Migrations
36
44
  #
37
45
  # Migrations can manage the evolution of a schema used by several physical
@@ -46,7 +54,7 @@ module ActiveRecord
46
54
  #
47
55
  # class AddSsl < ActiveRecord::Migration
48
56
  # def up
49
- # add_column :accounts, :ssl_enabled, :boolean, :default => 1
57
+ # add_column :accounts, :ssl_enabled, :boolean, default: true
50
58
  # end
51
59
  #
52
60
  # def down
@@ -58,7 +66,7 @@ module ActiveRecord
58
66
  # if you're backing out of the migration. It shows how all migrations have
59
67
  # two methods +up+ and +down+ that describes the transformations
60
68
  # required to implement or remove the migration. These methods can consist
61
- # of both the migration specific methods like add_column and remove_column,
69
+ # of both the migration specific methods like +add_column+ and +remove_column+,
62
70
  # but may also contain regular Ruby code for generating data needed for the
63
71
  # transformations.
64
72
  #
@@ -74,9 +82,9 @@ module ActiveRecord
74
82
  # t.integer :position
75
83
  # end
76
84
  #
77
- # SystemSetting.create :name => "notice",
78
- # :label => "Use notice?",
79
- # :value => 1
85
+ # SystemSetting.create name: 'notice',
86
+ # label: 'Use notice?',
87
+ # value: 1
80
88
  # end
81
89
  #
82
90
  # def down
@@ -84,19 +92,22 @@ module ActiveRecord
84
92
  # end
85
93
  # end
86
94
  #
87
- # This migration first adds the system_settings table, then creates the very
95
+ # This migration first adds the +system_settings+ table, then creates the very
88
96
  # 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
97
+ # also uses the more advanced +create_table+ syntax where you can specify a
90
98
  # complete table schema in one block call.
91
99
  #
92
100
  # == Available transformations
93
101
  #
94
- # * <tt>create_table(name, options)</tt> Creates a table called +name+ and
102
+ # * <tt>create_table(name, options)</tt>: Creates a table called +name+ and
95
103
  # 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
104
+ # following the same format as +add_column+. See example above. The options hash
97
105
  # is for fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create
98
106
  # table definition.
99
107
  # * <tt>drop_table(name)</tt>: Drops the table called +name+.
108
+ # * <tt>change_table(name, options)</tt>: Allows to make column alterations to
109
+ # the table called +name+. It makes the table object available to a block that
110
+ # can then add/remove columns, indexes or foreign keys to it.
100
111
  # * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
101
112
  # to +new_name+.
102
113
  # * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column
@@ -105,24 +116,24 @@ module ActiveRecord
105
116
  # <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>,
106
117
  # <tt>:decimal</tt>, <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
107
118
  # <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>.
119
+ # specified by passing an +options+ hash like <tt>{ default: 11 }</tt>.
109
120
  # Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g.
110
- # <tt>{ :limit => 50, :null => false }</tt>) -- see
121
+ # <tt>{ limit: 50, null: false }</tt>) -- see
111
122
  # ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
112
123
  # * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames
113
124
  # a column but keeps the type and content.
114
125
  # * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
115
126
  # the column to a different type using the same parameters as add_column.
116
- # * <tt>remove_column(table_name, column_names)</tt>: Removes the column listed in
117
- # +column_names+ from the table called +table_name+.
127
+ # * <tt>remove_column(table_name, column_name, type, options)</tt>: Removes the column
128
+ # named +column_name+ from the table called +table_name+.
118
129
  # * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
119
130
  # with the name of the column. Other options include
120
131
  # <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
132
+ # <tt>{ name: 'users_name_index', unique: true }</tt>) and <tt>:order</tt>
133
+ # (e.g. <tt>{ order: { name: :desc } }</tt>).
134
+ # * <tt>remove_index(table_name, column: column_name)</tt>: Removes the index
124
135
  # specified by +column_name+.
125
- # * <tt>remove_index(table_name, :name => index_name)</tt>: Removes the index
136
+ # * <tt>remove_index(table_name, name: index_name)</tt>: Removes the index
126
137
  # specified by +index_name+.
127
138
  #
128
139
  # == Irreversible transformations
@@ -323,11 +334,53 @@ module ActiveRecord
323
334
  #
324
335
  # For a list of commands that are reversible, please see
325
336
  # <tt>ActiveRecord::Migration::CommandRecorder</tt>.
337
+ #
338
+ # == Transactional Migrations
339
+ #
340
+ # If the database adapter supports DDL transactions, all migrations will
341
+ # automatically be wrapped in a transaction. There are queries that you
342
+ # can't execute inside a transaction though, and for these situations
343
+ # you can turn the automatic transactions off.
344
+ #
345
+ # class ChangeEnum < ActiveRecord::Migration
346
+ # disable_ddl_transaction!
347
+ #
348
+ # def up
349
+ # execute "ALTER TYPE model_size ADD VALUE 'new_value'"
350
+ # end
351
+ # end
352
+ #
353
+ # Remember that you can still open your own transactions, even if you
354
+ # are in a Migration with <tt>self.disable_ddl_transaction!</tt>.
326
355
  class Migration
327
356
  autoload :CommandRecorder, 'active_record/migration/command_recorder'
328
357
 
358
+
359
+ # This class is used to verify that all migrations have been run before
360
+ # loading a web page if config.active_record.migration_error is set to :page_load
361
+ class CheckPending
362
+ def initialize(app)
363
+ @app = app
364
+ @last_check = 0
365
+ end
366
+
367
+ def call(env)
368
+ mtime = ActiveRecord::Migrator.last_migration.mtime.to_i
369
+ if @last_check < mtime
370
+ ActiveRecord::Migration.check_pending!
371
+ @last_check = mtime
372
+ end
373
+ @app.call(env)
374
+ end
375
+ end
376
+
329
377
  class << self
330
378
  attr_accessor :delegate # :nodoc:
379
+ attr_accessor :disable_ddl_transaction # :nodoc:
380
+ end
381
+
382
+ def self.check_pending!
383
+ raise ActiveRecord::PendingMigrationError if ActiveRecord::Migrator.needs_migration?
331
384
  end
332
385
 
333
386
  def self.method_missing(name, *args, &block) # :nodoc:
@@ -338,30 +391,145 @@ module ActiveRecord
338
391
  new.migrate direction
339
392
  end
340
393
 
341
- cattr_accessor :verbose
394
+ # Disable DDL transactions for this migration.
395
+ def self.disable_ddl_transaction!
396
+ @disable_ddl_transaction = true
397
+ end
398
+
399
+ def disable_ddl_transaction # :nodoc:
400
+ self.class.disable_ddl_transaction
401
+ end
342
402
 
403
+ cattr_accessor :verbose
343
404
  attr_accessor :name, :version
344
405
 
345
- def initialize
346
- @name = self.class.name
347
- @version = nil
406
+ def initialize(name = self.class.name, version = nil)
407
+ @name = name
408
+ @version = version
348
409
  @connection = nil
349
- @reverting = false
350
410
  end
351
411
 
412
+ self.verbose = true
352
413
  # instantiate the delegate object after initialize is defined
353
- self.verbose = true
354
414
  self.delegate = new
355
415
 
356
- def revert
357
- @reverting = true
358
- yield
359
- ensure
360
- @reverting = false
416
+ # Reverses the migration commands for the given block and
417
+ # the given migrations.
418
+ #
419
+ # The following migration will remove the table 'horses'
420
+ # and create the table 'apples' on the way up, and the reverse
421
+ # on the way down.
422
+ #
423
+ # class FixTLMigration < ActiveRecord::Migration
424
+ # def change
425
+ # revert do
426
+ # create_table(:horses) do |t|
427
+ # t.text :content
428
+ # t.datetime :remind_at
429
+ # end
430
+ # end
431
+ # create_table(:apples) do |t|
432
+ # t.string :variety
433
+ # end
434
+ # end
435
+ # end
436
+ #
437
+ # Or equivalently, if +TenderloveMigration+ is defined as in the
438
+ # documentation for Migration:
439
+ #
440
+ # require_relative '2012121212_tenderlove_migration'
441
+ #
442
+ # class FixupTLMigration < ActiveRecord::Migration
443
+ # def change
444
+ # revert TenderloveMigration
445
+ #
446
+ # create_table(:apples) do |t|
447
+ # t.string :variety
448
+ # end
449
+ # end
450
+ # end
451
+ #
452
+ # This command can be nested.
453
+ def revert(*migration_classes)
454
+ run(*migration_classes.reverse, revert: true) unless migration_classes.empty?
455
+ if block_given?
456
+ if @connection.respond_to? :revert
457
+ @connection.revert { yield }
458
+ else
459
+ recorder = CommandRecorder.new(@connection)
460
+ @connection = recorder
461
+ suppress_messages do
462
+ @connection.revert { yield }
463
+ end
464
+ @connection = recorder.delegate
465
+ recorder.commands.each do |cmd, args, block|
466
+ send(cmd, *args, &block)
467
+ end
468
+ end
469
+ end
361
470
  end
362
471
 
363
472
  def reverting?
364
- @reverting
473
+ @connection.respond_to?(:reverting) && @connection.reverting
474
+ end
475
+
476
+ class ReversibleBlockHelper < Struct.new(:reverting) # :nodoc:
477
+ def up
478
+ yield unless reverting
479
+ end
480
+
481
+ def down
482
+ yield if reverting
483
+ end
484
+ end
485
+
486
+ # Used to specify an operation that can be run in one direction or another.
487
+ # Call the methods +up+ and +down+ of the yielded object to run a block
488
+ # only in one given direction.
489
+ # The whole block will be called in the right order within the migration.
490
+ #
491
+ # In the following example, the looping on users will always be done
492
+ # when the three columns 'first_name', 'last_name' and 'full_name' exist,
493
+ # even when migrating down:
494
+ #
495
+ # class SplitNameMigration < ActiveRecord::Migration
496
+ # def change
497
+ # add_column :users, :first_name, :string
498
+ # add_column :users, :last_name, :string
499
+ #
500
+ # reversible do |dir|
501
+ # User.reset_column_information
502
+ # User.all.each do |u|
503
+ # dir.up { u.first_name, u.last_name = u.full_name.split(' ') }
504
+ # dir.down { u.full_name = "#{u.first_name} #{u.last_name}" }
505
+ # u.save
506
+ # end
507
+ # end
508
+ #
509
+ # revert { add_column :users, :full_name, :string }
510
+ # end
511
+ # end
512
+ def reversible
513
+ helper = ReversibleBlockHelper.new(reverting?)
514
+ execute_block{ yield helper }
515
+ end
516
+
517
+ # Runs the given migration classes.
518
+ # Last argument can specify options:
519
+ # - :direction (default is :up)
520
+ # - :revert (default is false)
521
+ def run(*migration_classes)
522
+ opts = migration_classes.extract_options!
523
+ dir = opts[:direction] || :up
524
+ dir = (dir == :down ? :up : :down) if opts[:revert]
525
+ if reverting?
526
+ # If in revert and going :up, say, we want to execute :down without reverting, so
527
+ revert { run(*migration_classes, direction: dir, revert: true) }
528
+ else
529
+ migration_classes.each do |migration_class|
530
+ migration_class.new.exec_migration(@connection, dir)
531
+ end
532
+ end
365
533
  end
366
534
 
367
535
  def up
@@ -387,29 +555,9 @@ module ActiveRecord
387
555
 
388
556
  time = nil
389
557
  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) }
558
+ time = Benchmark.measure do
559
+ exec_migration(conn, direction)
411
560
  end
412
- @connection = nil
413
561
  end
414
562
 
415
563
  case direction
@@ -418,6 +566,21 @@ module ActiveRecord
418
566
  end
419
567
  end
420
568
 
569
+ def exec_migration(conn, direction)
570
+ @connection = conn
571
+ if respond_to?(:change)
572
+ if direction == :down
573
+ revert { change }
574
+ else
575
+ change
576
+ end
577
+ else
578
+ send(direction)
579
+ end
580
+ ensure
581
+ @connection = nil
582
+ end
583
+
421
584
  def write(text="")
422
585
  puts(text) if verbose
423
586
  end
@@ -456,9 +619,9 @@ module ActiveRecord
456
619
  arg_list = arguments.map{ |a| a.inspect } * ', '
457
620
 
458
621
  say_with_time "#{method}(#{arg_list})" do
459
- unless reverting?
460
- unless arguments.empty? || method == :execute
461
- arguments[0] = Migrator.proper_table_name(arguments.first) unless method == :assume_migrated_upto_version
622
+ unless @connection.respond_to? :revert
623
+ unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
624
+ arguments[0] = Migrator.proper_table_name(arguments.first)
462
625
  arguments[1] = Migrator.proper_table_name(arguments.second) if method == :rename_table
463
626
  end
464
627
  end
@@ -470,7 +633,7 @@ module ActiveRecord
470
633
  def copy(destination, sources, options = {})
471
634
  copied = []
472
635
 
473
- FileUtils.mkdir_p(destination) unless File.exists?(destination)
636
+ FileUtils.mkdir_p(destination) unless File.exist?(destination)
474
637
 
475
638
  destination_migrations = ActiveRecord::Migrator.migrations(destination)
476
639
  last = destination_migrations.last
@@ -478,8 +641,17 @@ module ActiveRecord
478
641
  source_migrations = ActiveRecord::Migrator.migrations(path)
479
642
 
480
643
  source_migrations.each do |migration|
481
- source = File.read(migration.filename)
482
- source = "# This migration comes from #{scope} (originally #{migration.version})\n#{source}"
644
+ source = File.binread(migration.filename)
645
+ inserted_comment = "# This migration comes from #{scope} (originally #{migration.version})\n"
646
+ if /\A#.*\b(?:en)?coding:\s*\S+/ =~ source
647
+ # If we have a magic comment in the original migration,
648
+ # insert our comment after the first newline(end of the magic comment line)
649
+ # so the magic keep working.
650
+ # Note that magic comments must be at the first line(except sh-bang).
651
+ source[/\n/] = "\n#{inserted_comment}"
652
+ else
653
+ source = "#{inserted_comment}#{source}"
654
+ end
483
655
 
484
656
  if duplicate = destination_migrations.detect { |m| m.name == migration.name }
485
657
  if options[:on_skip] && duplicate.scope != scope.to_s
@@ -493,7 +665,7 @@ module ActiveRecord
493
665
  old_path, migration.filename = migration.filename, new_path
494
666
  last = migration
495
667
 
496
- File.open(migration.filename, "w") { |f| f.write source }
668
+ File.binwrite(migration.filename, source)
497
669
  copied << migration
498
670
  options[:on_copy].call(scope, migration, old_path) if options[:on_copy]
499
671
  destination_migrations << migration
@@ -507,7 +679,16 @@ module ActiveRecord
507
679
  if ActiveRecord::Base.timestamped_migrations
508
680
  [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % number].max
509
681
  else
510
- "%.3d" % number
682
+ SchemaMigration.normalize_migration_number(number)
683
+ end
684
+ end
685
+
686
+ private
687
+ def execute_block
688
+ if connection.respond_to? :execute_block
689
+ super # use normal delegation to record the block
690
+ else
691
+ yield
511
692
  end
512
693
  end
513
694
  end
@@ -525,7 +706,11 @@ module ActiveRecord
525
706
  File.basename(filename)
526
707
  end
527
708
 
528
- delegate :migrate, :announce, :write, :to => :migration
709
+ def mtime
710
+ File.mtime filename
711
+ end
712
+
713
+ delegate :migrate, :announce, :write, :disable_ddl_transaction, to: :migration
529
714
 
530
715
  private
531
716
 
@@ -535,11 +720,21 @@ module ActiveRecord
535
720
 
536
721
  def load_migration
537
722
  require(File.expand_path(filename))
538
- name.constantize.new
723
+ name.constantize.new(name, version)
539
724
  end
540
725
 
541
726
  end
542
727
 
728
+ class NullMigration < MigrationProxy #:nodoc:
729
+ def initialize
730
+ super(nil, 0, nil, nil)
731
+ end
732
+
733
+ def mtime
734
+ 0
735
+ end
736
+ end
737
+
543
738
  class Migrator#:nodoc:
544
739
  class << self
545
740
  attr_writer :migrations_paths
@@ -547,14 +742,14 @@ module ActiveRecord
547
742
 
548
743
  def migrate(migrations_paths, target_version = nil, &block)
549
744
  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)
745
+ when target_version.nil?
746
+ up(migrations_paths, target_version, &block)
747
+ when current_version == 0 && target_version == 0
748
+ []
749
+ when current_version > target_version
750
+ down(migrations_paths, target_version, &block)
751
+ else
752
+ up(migrations_paths, target_version, &block)
558
753
  end
559
754
  end
560
755
 
@@ -566,25 +761,34 @@ module ActiveRecord
566
761
  move(:up, migrations_paths, steps)
567
762
  end
568
763
 
569
- def up(migrations_paths, target_version = nil, &block)
570
- self.new(:up, migrations_paths, target_version).migrate(&block)
764
+ def up(migrations_paths, target_version = nil)
765
+ migrations = migrations(migrations_paths)
766
+ migrations.select! { |m| yield m } if block_given?
767
+
768
+ self.new(:up, migrations, target_version).migrate
571
769
  end
572
770
 
573
771
  def down(migrations_paths, target_version = nil, &block)
574
- self.new(:down, migrations_paths, target_version).migrate(&block)
772
+ migrations = migrations(migrations_paths)
773
+ migrations.select! { |m| yield m } if block_given?
774
+
775
+ self.new(:down, migrations, target_version).migrate
575
776
  end
576
777
 
577
778
  def run(direction, migrations_paths, target_version)
578
- self.new(direction, migrations_paths, target_version).run
779
+ self.new(direction, migrations(migrations_paths), target_version).run
780
+ end
781
+
782
+ def open(migrations_paths)
783
+ self.new(:up, migrations(migrations_paths), nil)
579
784
  end
580
785
 
581
786
  def schema_migrations_table_name
582
- Base.table_name_prefix + 'schema_migrations' + Base.table_name_suffix
787
+ SchemaMigration.table_name
583
788
  end
584
789
 
585
790
  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
791
+ SchemaMigration.all.map { |x| x.version.to_i }.sort
588
792
  end
589
793
 
590
794
  def current_version
@@ -596,35 +800,41 @@ module ActiveRecord
596
800
  end
597
801
  end
598
802
 
803
+ def needs_migration?
804
+ current_version < last_version
805
+ end
806
+
807
+ def last_version
808
+ last_migration.version
809
+ end
810
+
811
+ def last_migration #:nodoc:
812
+ migrations(migrations_paths).last || NullMigration.new
813
+ end
814
+
599
815
  def proper_table_name(name)
600
816
  # 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}"
817
+ if name.respond_to? :table_name
818
+ name.table_name
819
+ else
820
+ "#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}"
821
+ end
602
822
  end
603
823
 
604
824
  def migrations_paths
605
825
  @migrations_paths ||= ['db/migrate']
606
826
  # just to not break things if someone uses: migration_path = some_string
607
- Array.wrap(@migrations_paths)
827
+ Array(@migrations_paths)
608
828
  end
609
829
 
610
830
  def migrations_path
611
831
  migrations_paths.first
612
832
  end
613
833
 
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)
834
+ def migrations(paths)
835
+ paths = Array(paths)
623
836
 
624
- glob = subdirectories ? "**/" : ""
625
- files = Dir[*paths.map { |p| "#{p}/#{glob}[0-9]*_*.rb" }]
626
-
627
- seen = Hash.new false
837
+ files = Dir[*paths.map { |p| "#{p}/**/[0-9]*_*.rb" }]
628
838
 
629
839
  migrations = files.map do |file|
630
840
  version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
@@ -633,11 +843,6 @@ module ActiveRecord
633
843
  version = version.to_i
634
844
  name = name.camelize
635
845
 
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
846
  MigrationProxy.new(name, version, file, scope)
642
847
  end
643
848
 
@@ -647,7 +852,7 @@ module ActiveRecord
647
852
  private
648
853
 
649
854
  def move(direction, migrations_paths, steps)
650
- migrator = self.new(direction, migrations_paths)
855
+ migrator = self.new(direction, migrations(migrations_paths))
651
856
  start_index = migrator.migrations.index(migrator.current_migration)
652
857
 
653
858
  if start_index
@@ -658,124 +863,157 @@ module ActiveRecord
658
863
  end
659
864
  end
660
865
 
661
- def initialize(direction, migrations_paths, target_version = nil)
866
+ def initialize(direction, migrations, target_version = nil)
662
867
  raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?
868
+
869
+ @direction = direction
870
+ @target_version = target_version
871
+ @migrated_versions = nil
872
+
873
+ if Array(migrations).grep(String).empty?
874
+ @migrations = migrations
875
+ else
876
+ ActiveSupport::Deprecation.warn "instantiate this class with a list of migrations"
877
+ @migrations = self.class.migrations(migrations)
878
+ end
879
+
880
+ validate(@migrations)
881
+
663
882
  Base.connection.initialize_schema_migrations_table
664
- @direction, @migrations_paths, @target_version = direction, migrations_paths, target_version
665
883
  end
666
884
 
667
885
  def current_version
668
- migrated.last || 0
886
+ migrated.max || 0
669
887
  end
670
888
 
671
889
  def current_migration
672
890
  migrations.detect { |m| m.version == current_version }
673
891
  end
892
+ alias :current :current_migration
674
893
 
675
894
  def run
676
- target = migrations.detect { |m| m.version == @target_version }
677
- raise UnknownMigrationVersionError.new(@target_version) if target.nil?
678
- unless (up? && migrated.include?(target.version.to_i)) || (down? && !migrated.include?(target.version.to_i))
679
- target.migrate(@direction)
680
- record_version_state_after_migrating(target.version)
895
+ migration = migrations.detect { |m| m.version == @target_version }
896
+ raise UnknownMigrationVersionError.new(@target_version) if migration.nil?
897
+ unless (up? && migrated.include?(migration.version.to_i)) || (down? && !migrated.include?(migration.version.to_i))
898
+ begin
899
+ execute_migration_in_transaction(migration, @direction)
900
+ rescue => e
901
+ canceled_msg = use_transaction?(migration) ? ", this migration was canceled" : ""
902
+ raise StandardError, "An error has occurred#{canceled_msg}:\n\n#{e}", e.backtrace
903
+ end
681
904
  end
682
905
  end
683
906
 
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
907
+ def migrate
908
+ if !target && @target_version && @target_version > 0
689
909
  raise UnknownMigrationVersionError.new(@target_version)
690
910
  end
691
911
 
692
- start = up? ? 0 : (migrations.index(current) || 0)
693
- finish = migrations.index(target) || migrations.size - 1
694
- runnable = migrations[start..finish]
912
+ running = runnable
695
913
 
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
914
+ if block_given?
915
+ message = "block argument to migrate is deprecated, please filter migrations before constructing the migrator"
916
+ ActiveSupport::Deprecation.warn message
917
+ running.select! { |m| yield m }
918
+ end
704
919
 
920
+ running.each do |migration|
705
921
  Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
706
922
 
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
923
  begin
719
- ddl_transaction do
720
- migration.migrate(@direction)
721
- record_version_state_after_migrating(migration.version)
722
- end
723
- ran << migration
924
+ execute_migration_in_transaction(migration, @direction)
724
925
  rescue => e
725
- canceled_msg = Base.connection.supports_ddl_transactions? ? "this and " : ""
926
+ canceled_msg = use_transaction?(migration) ? "this and " : ""
726
927
  raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace
727
928
  end
728
929
  end
729
- ran
730
930
  end
731
931
 
732
- def migrations
733
- @migrations ||= begin
734
- migrations = self.class.migrations(@migrations_paths)
735
- down? ? migrations.reverse : migrations
932
+ def runnable
933
+ runnable = migrations[start..finish]
934
+ if up?
935
+ runnable.reject { |m| ran?(m) }
936
+ else
937
+ # skip the last migration if we're headed down, but not ALL the way down
938
+ runnable.pop if target
939
+ runnable.find_all { |m| ran?(m) }
736
940
  end
737
941
  end
738
942
 
943
+ def migrations
944
+ down? ? @migrations.reverse : @migrations.sort_by(&:version)
945
+ end
946
+
739
947
  def pending_migrations
740
948
  already_migrated = migrated
741
- migrations.reject { |m| already_migrated.include?(m.version.to_i) }
949
+ migrations.reject { |m| already_migrated.include?(m.version) }
742
950
  end
743
951
 
744
952
  def migrated
745
- @migrated_versions ||= self.class.get_all_versions
953
+ @migrated_versions ||= Set.new(self.class.get_all_versions)
746
954
  end
747
955
 
748
956
  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
957
+ def ran?(migration)
958
+ migrated.include?(migration.version.to_i)
959
+ end
763
960
 
764
- def up?
765
- @direction == :up
961
+ def execute_migration_in_transaction(migration, direction)
962
+ ddl_transaction(migration) do
963
+ migration.migrate(direction)
964
+ record_version_state_after_migrating(migration.version)
766
965
  end
966
+ end
967
+
968
+ def target
969
+ migrations.detect { |m| m.version == @target_version }
970
+ end
971
+
972
+ def finish
973
+ migrations.index(target) || migrations.size - 1
974
+ end
975
+
976
+ def start
977
+ up? ? 0 : (migrations.index(current) || 0)
978
+ end
767
979
 
768
- def down?
769
- @direction == :down
980
+ def validate(migrations)
981
+ name ,= migrations.group_by(&:name).find { |_,v| v.length > 1 }
982
+ raise DuplicateMigrationNameError.new(name) if name
983
+
984
+ version ,= migrations.group_by(&:version).find { |_,v| v.length > 1 }
985
+ raise DuplicateMigrationVersionError.new(version) if version
986
+ end
987
+
988
+ def record_version_state_after_migrating(version)
989
+ if down?
990
+ migrated.delete(version)
991
+ ActiveRecord::SchemaMigration.where(:version => version.to_s).delete_all
992
+ else
993
+ migrated << version
994
+ ActiveRecord::SchemaMigration.create!(:version => version.to_s)
770
995
  end
996
+ end
771
997
 
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
998
+ def up?
999
+ @direction == :up
1000
+ end
1001
+
1002
+ def down?
1003
+ @direction == :down
1004
+ end
1005
+
1006
+ # Wrap the migration in a transaction only if supported by the adapter.
1007
+ def ddl_transaction(migration)
1008
+ if use_transaction?(migration)
1009
+ Base.transaction { yield }
1010
+ else
1011
+ yield
779
1012
  end
1013
+ end
1014
+
1015
+ def use_transaction?(migration)
1016
+ !migration.disable_ddl_transaction && Base.connection.supports_ddl_transactions?
1017
+ end
780
1018
  end
781
1019
  end