activerecord 3.2.22.5 → 4.2.11.3

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 (236) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1632 -609
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +37 -41
  5. data/examples/performance.rb +31 -19
  6. data/examples/simple.rb +4 -4
  7. data/lib/active_record/aggregations.rb +56 -42
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -36
  10. data/lib/active_record/associations/association.rb +73 -55
  11. data/lib/active_record/associations/association_scope.rb +143 -82
  12. data/lib/active_record/associations/belongs_to_association.rb +65 -25
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
  14. data/lib/active_record/associations/builder/association.rb +125 -31
  15. data/lib/active_record/associations/builder/belongs_to.rb +89 -61
  16. data/lib/active_record/associations/builder/collection_association.rb +69 -49
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +113 -42
  18. data/lib/active_record/associations/builder/has_many.rb +8 -64
  19. data/lib/active_record/associations/builder/has_one.rb +12 -51
  20. data/lib/active_record/associations/builder/singular_association.rb +23 -17
  21. data/lib/active_record/associations/collection_association.rb +251 -177
  22. data/lib/active_record/associations/collection_proxy.rb +963 -63
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +113 -22
  25. data/lib/active_record/associations/has_many_through_association.rb +99 -39
  26. data/lib/active_record/associations/has_one_association.rb +43 -20
  27. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  28. data/lib/active_record/associations/join_dependency/join_association.rb +76 -107
  29. data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
  31. data/lib/active_record/associations/join_dependency.rb +230 -156
  32. data/lib/active_record/associations/preloader/association.rb +96 -55
  33. data/lib/active_record/associations/preloader/collection_association.rb +3 -3
  34. data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
  35. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  36. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  37. data/lib/active_record/associations/preloader/through_association.rb +62 -33
  38. data/lib/active_record/associations/preloader.rb +101 -79
  39. data/lib/active_record/associations/singular_association.rb +29 -13
  40. data/lib/active_record/associations/through_association.rb +30 -16
  41. data/lib/active_record/associations.rb +463 -345
  42. data/lib/active_record/attribute.rb +163 -0
  43. data/lib/active_record/attribute_assignment.rb +142 -151
  44. data/lib/active_record/attribute_decorators.rb +66 -0
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
  46. data/lib/active_record/attribute_methods/dirty.rb +137 -57
  47. data/lib/active_record/attribute_methods/primary_key.rb +50 -36
  48. data/lib/active_record/attribute_methods/query.rb +5 -4
  49. data/lib/active_record/attribute_methods/read.rb +73 -106
  50. data/lib/active_record/attribute_methods/serialization.rb +44 -94
  51. data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -45
  52. data/lib/active_record/attribute_methods/write.rb +57 -44
  53. data/lib/active_record/attribute_methods.rb +301 -141
  54. data/lib/active_record/attribute_set/builder.rb +106 -0
  55. data/lib/active_record/attribute_set.rb +81 -0
  56. data/lib/active_record/attributes.rb +147 -0
  57. data/lib/active_record/autosave_association.rb +246 -217
  58. data/lib/active_record/base.rb +70 -474
  59. data/lib/active_record/callbacks.rb +66 -28
  60. data/lib/active_record/coders/json.rb +13 -0
  61. data/lib/active_record/coders/yaml_column.rb +18 -21
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +396 -219
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -164
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +29 -24
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -55
  67. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  68. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
  69. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +261 -169
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +707 -259
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +298 -89
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +466 -196
  75. data/lib/active_record/connection_adapters/column.rb +31 -245
  76. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +45 -57
  78. data/lib/active_record/connection_adapters/mysql_adapter.rb +180 -123
  79. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
  80. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  81. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +596 -0
  112. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  113. data/lib/active_record/connection_adapters/postgresql_adapter.rb +430 -999
  114. data/lib/active_record/connection_adapters/schema_cache.rb +52 -27
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +579 -22
  116. data/lib/active_record/connection_handling.rb +132 -0
  117. data/lib/active_record/core.rb +579 -0
  118. data/lib/active_record/counter_cache.rb +157 -105
  119. data/lib/active_record/dynamic_matchers.rb +119 -63
  120. data/lib/active_record/enum.rb +197 -0
  121. data/lib/active_record/errors.rb +94 -36
  122. data/lib/active_record/explain.rb +15 -63
  123. data/lib/active_record/explain_registry.rb +30 -0
  124. data/lib/active_record/explain_subscriber.rb +9 -5
  125. data/lib/active_record/fixture_set/file.rb +56 -0
  126. data/lib/active_record/fixtures.rb +302 -215
  127. data/lib/active_record/gem_version.rb +15 -0
  128. data/lib/active_record/inheritance.rb +143 -70
  129. data/lib/active_record/integration.rb +65 -12
  130. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  131. data/lib/active_record/locale/en.yml +8 -1
  132. data/lib/active_record/locking/optimistic.rb +73 -52
  133. data/lib/active_record/locking/pessimistic.rb +5 -5
  134. data/lib/active_record/log_subscriber.rb +24 -21
  135. data/lib/active_record/migration/command_recorder.rb +124 -32
  136. data/lib/active_record/migration/join_table.rb +15 -0
  137. data/lib/active_record/migration.rb +511 -213
  138. data/lib/active_record/model_schema.rb +91 -117
  139. data/lib/active_record/nested_attributes.rb +184 -130
  140. data/lib/active_record/no_touching.rb +52 -0
  141. data/lib/active_record/null_relation.rb +81 -0
  142. data/lib/active_record/persistence.rb +276 -117
  143. data/lib/active_record/query_cache.rb +19 -37
  144. data/lib/active_record/querying.rb +28 -18
  145. data/lib/active_record/railtie.rb +73 -40
  146. data/lib/active_record/railties/console_sandbox.rb +3 -4
  147. data/lib/active_record/railties/controller_runtime.rb +4 -3
  148. data/lib/active_record/railties/databases.rake +141 -416
  149. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  150. data/lib/active_record/readonly_attributes.rb +1 -4
  151. data/lib/active_record/reflection.rb +513 -154
  152. data/lib/active_record/relation/batches.rb +91 -43
  153. data/lib/active_record/relation/calculations.rb +199 -161
  154. data/lib/active_record/relation/delegation.rb +116 -25
  155. data/lib/active_record/relation/finder_methods.rb +362 -248
  156. data/lib/active_record/relation/merger.rb +193 -0
  157. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  158. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  159. data/lib/active_record/relation/predicate_builder.rb +135 -43
  160. data/lib/active_record/relation/query_methods.rb +928 -167
  161. data/lib/active_record/relation/spawn_methods.rb +48 -149
  162. data/lib/active_record/relation.rb +352 -207
  163. data/lib/active_record/result.rb +101 -10
  164. data/lib/active_record/runtime_registry.rb +22 -0
  165. data/lib/active_record/sanitization.rb +56 -59
  166. data/lib/active_record/schema.rb +19 -13
  167. data/lib/active_record/schema_dumper.rb +106 -63
  168. data/lib/active_record/schema_migration.rb +53 -0
  169. data/lib/active_record/scoping/default.rb +50 -57
  170. data/lib/active_record/scoping/named.rb +73 -109
  171. data/lib/active_record/scoping.rb +58 -123
  172. data/lib/active_record/serialization.rb +6 -2
  173. data/lib/active_record/serializers/xml_serializer.rb +12 -22
  174. data/lib/active_record/statement_cache.rb +111 -0
  175. data/lib/active_record/store.rb +168 -15
  176. data/lib/active_record/tasks/database_tasks.rb +299 -0
  177. data/lib/active_record/tasks/mysql_database_tasks.rb +159 -0
  178. data/lib/active_record/tasks/postgresql_database_tasks.rb +101 -0
  179. data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
  180. data/lib/active_record/timestamp.rb +23 -16
  181. data/lib/active_record/transactions.rb +125 -79
  182. data/lib/active_record/type/big_integer.rb +13 -0
  183. data/lib/active_record/type/binary.rb +50 -0
  184. data/lib/active_record/type/boolean.rb +31 -0
  185. data/lib/active_record/type/date.rb +50 -0
  186. data/lib/active_record/type/date_time.rb +54 -0
  187. data/lib/active_record/type/decimal.rb +64 -0
  188. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  189. data/lib/active_record/type/decorator.rb +14 -0
  190. data/lib/active_record/type/float.rb +19 -0
  191. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  192. data/lib/active_record/type/integer.rb +59 -0
  193. data/lib/active_record/type/mutable.rb +16 -0
  194. data/lib/active_record/type/numeric.rb +36 -0
  195. data/lib/active_record/type/serialized.rb +62 -0
  196. data/lib/active_record/type/string.rb +40 -0
  197. data/lib/active_record/type/text.rb +11 -0
  198. data/lib/active_record/type/time.rb +26 -0
  199. data/lib/active_record/type/time_value.rb +38 -0
  200. data/lib/active_record/type/type_map.rb +64 -0
  201. data/lib/active_record/type/unsigned_integer.rb +15 -0
  202. data/lib/active_record/type/value.rb +110 -0
  203. data/lib/active_record/type.rb +23 -0
  204. data/lib/active_record/validations/associated.rb +24 -16
  205. data/lib/active_record/validations/presence.rb +67 -0
  206. data/lib/active_record/validations/uniqueness.rb +123 -64
  207. data/lib/active_record/validations.rb +36 -29
  208. data/lib/active_record/version.rb +5 -7
  209. data/lib/active_record.rb +66 -46
  210. data/lib/rails/generators/active_record/migration/migration_generator.rb +53 -8
  211. data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +5 -1
  212. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  213. data/lib/rails/generators/active_record/migration.rb +11 -8
  214. data/lib/rails/generators/active_record/model/model_generator.rb +9 -4
  215. data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
  216. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  217. data/lib/rails/generators/active_record.rb +3 -11
  218. metadata +101 -45
  219. data/examples/associations.png +0 -0
  220. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  221. data/lib/active_record/associations/join_helper.rb +0 -55
  222. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  223. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  224. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  225. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  226. data/lib/active_record/dynamic_finder_match.rb +0 -68
  227. data/lib/active_record/dynamic_scope_match.rb +0 -23
  228. data/lib/active_record/fixtures/file.rb +0 -65
  229. data/lib/active_record/identity_map.rb +0 -162
  230. data/lib/active_record/observer.rb +0 -121
  231. data/lib/active_record/session_store.rb +0 -360
  232. data/lib/active_record/test_case.rb +0 -73
  233. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  234. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  235. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  236. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,37 +1,52 @@
1
- require "active_support/core_ext/module/delegation"
2
- require "active_support/core_ext/class/attribute_accessors"
3
- require "active_support/core_ext/array/wrap"
4
- require 'active_support/deprecation'
1
+ require "active_support/core_ext/module/attribute_accessors"
2
+ require 'set'
5
3
 
6
4
  module ActiveRecord
5
+ class MigrationError < ActiveRecordError#:nodoc:
6
+ def initialize(message = nil)
7
+ message = "\n\n#{message}\n\n" if message
8
+ super
9
+ end
10
+ end
11
+
7
12
  # Exception that can be raised to stop migrations from going backwards.
8
- class IrreversibleMigration < ActiveRecordError
13
+ class IrreversibleMigration < MigrationError
9
14
  end
10
15
 
11
- class DuplicateMigrationVersionError < ActiveRecordError#:nodoc:
16
+ class DuplicateMigrationVersionError < MigrationError#:nodoc:
12
17
  def initialize(version)
13
18
  super("Multiple migrations have the version number #{version}")
14
19
  end
15
20
  end
16
21
 
17
- class DuplicateMigrationNameError < ActiveRecordError#:nodoc:
22
+ class DuplicateMigrationNameError < MigrationError#:nodoc:
18
23
  def initialize(name)
19
24
  super("Multiple migrations have the name #{name}")
20
25
  end
21
26
  end
22
27
 
23
- class UnknownMigrationVersionError < ActiveRecordError #:nodoc:
28
+ class UnknownMigrationVersionError < MigrationError #:nodoc:
24
29
  def initialize(version)
25
30
  super("No migration with version number #{version}")
26
31
  end
27
32
  end
28
33
 
29
- class IllegalMigrationNameError < ActiveRecordError#:nodoc:
34
+ class IllegalMigrationNameError < MigrationError#:nodoc:
30
35
  def initialize(name)
31
36
  super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed)")
32
37
  end
33
38
  end
34
39
 
40
+ class PendingMigrationError < MigrationError#:nodoc:
41
+ def initialize
42
+ if defined?(Rails.env)
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
+
35
50
  # = Active Record Migrations
36
51
  #
37
52
  # Migrations can manage the evolution of a schema used by several physical
@@ -46,7 +61,7 @@ module ActiveRecord
46
61
  #
47
62
  # class AddSsl < ActiveRecord::Migration
48
63
  # def up
49
- # add_column :accounts, :ssl_enabled, :boolean, :default => 1
64
+ # add_column :accounts, :ssl_enabled, :boolean, default: true
50
65
  # end
51
66
  #
52
67
  # def down
@@ -58,7 +73,7 @@ module ActiveRecord
58
73
  # if you're backing out of the migration. It shows how all migrations have
59
74
  # two methods +up+ and +down+ that describes the transformations
60
75
  # required to implement or remove the migration. These methods can consist
61
- # of both the migration specific methods like add_column and remove_column,
76
+ # of both the migration specific methods like +add_column+ and +remove_column+,
62
77
  # but may also contain regular Ruby code for generating data needed for the
63
78
  # transformations.
64
79
  #
@@ -74,9 +89,9 @@ module ActiveRecord
74
89
  # t.integer :position
75
90
  # end
76
91
  #
77
- # SystemSetting.create :name => "notice",
78
- # :label => "Use notice?",
79
- # :value => 1
92
+ # SystemSetting.create name: 'notice',
93
+ # label: 'Use notice?',
94
+ # value: 1
80
95
  # end
81
96
  #
82
97
  # def down
@@ -84,19 +99,22 @@ module ActiveRecord
84
99
  # end
85
100
  # end
86
101
  #
87
- # This migration first adds the system_settings table, then creates the very
102
+ # This migration first adds the +system_settings+ table, then creates the very
88
103
  # 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
104
+ # also uses the more advanced +create_table+ syntax where you can specify a
90
105
  # complete table schema in one block call.
91
106
  #
92
107
  # == Available transformations
93
108
  #
94
- # * <tt>create_table(name, options)</tt> Creates a table called +name+ and
109
+ # * <tt>create_table(name, options)</tt>: Creates a table called +name+ and
95
110
  # 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
111
+ # following the same format as +add_column+. See example above. The options hash
97
112
  # is for fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create
98
113
  # table definition.
99
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.
100
118
  # * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
101
119
  # to +new_name+.
102
120
  # * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column
@@ -105,24 +123,24 @@ module ActiveRecord
105
123
  # <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>,
106
124
  # <tt>:decimal</tt>, <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
107
125
  # <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>.
126
+ # specified by passing an +options+ hash like <tt>{ default: 11 }</tt>.
109
127
  # Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g.
110
- # <tt>{ :limit => 50, :null => false }</tt>) -- see
128
+ # <tt>{ limit: 50, null: false }</tt>) -- see
111
129
  # ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
112
130
  # * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames
113
131
  # a column but keeps the type and content.
114
132
  # * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
115
133
  # 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+.
134
+ # * <tt>remove_column(table_name, column_name, type, options)</tt>: Removes the column
135
+ # named +column_name+ from the table called +table_name+.
118
136
  # * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
119
137
  # with the name of the column. Other options include
120
138
  # <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
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
124
142
  # specified by +column_name+.
125
- # * <tt>remove_index(table_name, :name => index_name)</tt>: Removes the index
143
+ # * <tt>remove_index(table_name, name: index_name)</tt>: Removes the index
126
144
  # specified by +index_name+.
127
145
  #
128
146
  # == Irreversible transformations
@@ -143,21 +161,14 @@ module ActiveRecord
143
161
  # in the <tt>db/migrate/</tt> directory where <tt>timestamp</tt> is the
144
162
  # UTC formatted date and time that the migration was generated.
145
163
  #
146
- # You may then edit the <tt>up</tt> and <tt>down</tt> methods of
147
- # MyNewMigration.
148
- #
149
164
  # There is a special syntactic shortcut to generate migrations that add fields to a table.
150
165
  #
151
166
  # rails generate migration add_fieldname_to_tablename fieldname:string
152
167
  #
153
168
  # This will generate the file <tt>timestamp_add_fieldname_to_tablename</tt>, which will look like this:
154
169
  # class AddFieldnameToTablename < ActiveRecord::Migration
155
- # def up
156
- # add_column :tablenames, :fieldname, :string
157
- # end
158
- #
159
- # def down
160
- # remove_column :tablenames, :fieldname
170
+ # def change
171
+ # add_column :tablenames, :field, :string
161
172
  # end
162
173
  # end
163
174
  #
@@ -170,14 +181,17 @@ module ActiveRecord
170
181
  #
171
182
  # To roll the database back to a previous migration version, use
172
183
  # <tt>rake db:migrate VERSION=X</tt> where <tt>X</tt> is the version to which
173
- # you wish to downgrade. If any of the migrations throw an
174
- # <tt>ActiveRecord::IrreversibleMigration</tt> exception, that step will fail and you'll
175
- # have some manual work to do.
184
+ # you wish to downgrade. Alternatively, you can also use the STEP option if you
185
+ # wish to rollback last few migrations. <tt>rake db:migrate STEP=2</tt> will rollback
186
+ # the latest two migrations.
187
+ #
188
+ # If any of the migrations throw an <tt>ActiveRecord::IrreversibleMigration</tt> exception,
189
+ # that step will fail and you'll have some manual work to do.
176
190
  #
177
191
  # == Database support
178
192
  #
179
193
  # Migrations are currently supported in MySQL, PostgreSQL, SQLite,
180
- # SQL Server, Sybase, and Oracle (all supported databases except DB2).
194
+ # SQL Server, and Oracle (all supported databases except DB2).
181
195
  #
182
196
  # == More examples
183
197
  #
@@ -293,9 +307,8 @@ module ActiveRecord
293
307
  #
294
308
  # == Reversible Migrations
295
309
  #
296
- # Starting with Rails 3.1, you will be able to define reversible migrations.
297
310
  # Reversible migrations are migrations that know how to go +down+ for you.
298
- # You simply supply the +up+ logic, and the Migration system will figure out
311
+ # You simply supply the +up+ logic, and the Migration system figures out
299
312
  # how to execute the down commands for you.
300
313
  #
301
314
  # To define a reversible migration, define the +change+ method in your
@@ -323,45 +336,233 @@ module ActiveRecord
323
336
  #
324
337
  # For a list of commands that are reversible, please see
325
338
  # <tt>ActiveRecord::Migration::CommandRecorder</tt>.
339
+ #
340
+ # == Transactional Migrations
341
+ #
342
+ # If the database adapter supports DDL transactions, all migrations will
343
+ # automatically be wrapped in a transaction. There are queries that you
344
+ # can't execute inside a transaction though, and for these situations
345
+ # you can turn the automatic transactions off.
346
+ #
347
+ # class ChangeEnum < ActiveRecord::Migration
348
+ # disable_ddl_transaction!
349
+ #
350
+ # def up
351
+ # execute "ALTER TYPE model_size ADD VALUE 'new_value'"
352
+ # end
353
+ # end
354
+ #
355
+ # Remember that you can still open your own transactions, even if you
356
+ # are in a Migration with <tt>self.disable_ddl_transaction!</tt>.
326
357
  class Migration
327
358
  autoload :CommandRecorder, 'active_record/migration/command_recorder'
328
359
 
360
+
361
+ # This class is used to verify that all migrations have been run before
362
+ # loading a web page if config.active_record.migration_error is set to :page_load
363
+ class CheckPending
364
+ def initialize(app)
365
+ @app = app
366
+ @last_check = 0
367
+ end
368
+
369
+ def call(env)
370
+ if connection.supports_migrations?
371
+ mtime = ActiveRecord::Migrator.last_migration.mtime.to_i
372
+ if @last_check < mtime
373
+ ActiveRecord::Migration.check_pending!(connection)
374
+ @last_check = mtime
375
+ end
376
+ end
377
+ @app.call(env)
378
+ end
379
+
380
+ private
381
+
382
+ def connection
383
+ ActiveRecord::Base.connection
384
+ end
385
+ end
386
+
329
387
  class << self
330
388
  attr_accessor :delegate # :nodoc:
331
- end
389
+ attr_accessor :disable_ddl_transaction # :nodoc:
390
+
391
+ def check_pending!(connection = Base.connection)
392
+ raise ActiveRecord::PendingMigrationError if ActiveRecord::Migrator.needs_migration?(connection)
393
+ end
394
+
395
+ def load_schema_if_pending!
396
+ if ActiveRecord::Migrator.needs_migration? || !ActiveRecord::Migrator.any_migrations?
397
+ # Roundrip to Rake to allow plugins to hook into database initialization.
398
+ FileUtils.cd Rails.root do
399
+ current_config = Base.connection_config
400
+ Base.clear_all_connections!
401
+ system("bin/rake db:test:prepare")
402
+ # Establish a new connection, the old database may be gone (db:test:prepare uses purge)
403
+ Base.establish_connection(current_config)
404
+ end
405
+ check_pending!
406
+ end
407
+ end
408
+
409
+ def maintain_test_schema! # :nodoc:
410
+ if ActiveRecord::Base.maintain_test_schema
411
+ suppress_messages { load_schema_if_pending! }
412
+ end
413
+ end
332
414
 
333
- def self.method_missing(name, *args, &block) # :nodoc:
334
- (delegate || superclass.delegate).send(name, *args, &block)
415
+ def method_missing(name, *args, &block) # :nodoc:
416
+ (delegate || superclass.delegate).send(name, *args, &block)
417
+ end
418
+
419
+ def migrate(direction)
420
+ new.migrate direction
421
+ end
422
+
423
+ # Disable the transaction wrapping this migration.
424
+ # You can still create your own transactions even after calling #disable_ddl_transaction!
425
+ #
426
+ # For more details read the {"Transactional Migrations" section above}[rdoc-ref:Migration].
427
+ def disable_ddl_transaction!
428
+ @disable_ddl_transaction = true
429
+ end
335
430
  end
336
431
 
337
- def self.migrate(direction)
338
- new.migrate direction
432
+ def disable_ddl_transaction # :nodoc:
433
+ self.class.disable_ddl_transaction
339
434
  end
340
435
 
341
436
  cattr_accessor :verbose
342
-
343
437
  attr_accessor :name, :version
344
438
 
345
- def initialize
346
- @name = self.class.name
347
- @version = nil
439
+ def initialize(name = self.class.name, version = nil)
440
+ @name = name
441
+ @version = version
348
442
  @connection = nil
349
- @reverting = false
350
443
  end
351
444
 
445
+ self.verbose = true
352
446
  # instantiate the delegate object after initialize is defined
353
- self.verbose = true
354
447
  self.delegate = new
355
448
 
356
- def revert
357
- @reverting = true
358
- yield
359
- ensure
360
- @reverting = false
449
+ # Reverses the migration commands for the given block and
450
+ # the given migrations.
451
+ #
452
+ # The following migration will remove the table 'horses'
453
+ # and create the table 'apples' on the way up, and the reverse
454
+ # on the way down.
455
+ #
456
+ # class FixTLMigration < ActiveRecord::Migration
457
+ # def change
458
+ # revert do
459
+ # create_table(:horses) do |t|
460
+ # t.text :content
461
+ # t.datetime :remind_at
462
+ # end
463
+ # end
464
+ # create_table(:apples) do |t|
465
+ # t.string :variety
466
+ # end
467
+ # end
468
+ # end
469
+ #
470
+ # Or equivalently, if +TenderloveMigration+ is defined as in the
471
+ # documentation for Migration:
472
+ #
473
+ # require_relative '2012121212_tenderlove_migration'
474
+ #
475
+ # class FixupTLMigration < ActiveRecord::Migration
476
+ # def change
477
+ # revert TenderloveMigration
478
+ #
479
+ # create_table(:apples) do |t|
480
+ # t.string :variety
481
+ # end
482
+ # end
483
+ # end
484
+ #
485
+ # This command can be nested.
486
+ def revert(*migration_classes)
487
+ run(*migration_classes.reverse, revert: true) unless migration_classes.empty?
488
+ if block_given?
489
+ if @connection.respond_to? :revert
490
+ @connection.revert { yield }
491
+ else
492
+ recorder = CommandRecorder.new(@connection)
493
+ @connection = recorder
494
+ suppress_messages do
495
+ @connection.revert { yield }
496
+ end
497
+ @connection = recorder.delegate
498
+ recorder.commands.each do |cmd, args, block|
499
+ send(cmd, *args, &block)
500
+ end
501
+ end
502
+ end
361
503
  end
362
504
 
363
505
  def reverting?
364
- @reverting
506
+ @connection.respond_to?(:reverting) && @connection.reverting
507
+ end
508
+
509
+ class ReversibleBlockHelper < Struct.new(:reverting) # :nodoc:
510
+ def up
511
+ yield unless reverting
512
+ end
513
+
514
+ def down
515
+ yield if reverting
516
+ end
517
+ end
518
+
519
+ # Used to specify an operation that can be run in one direction or another.
520
+ # Call the methods +up+ and +down+ of the yielded object to run a block
521
+ # only in one given direction.
522
+ # The whole block will be called in the right order within the migration.
523
+ #
524
+ # In the following example, the looping on users will always be done
525
+ # when the three columns 'first_name', 'last_name' and 'full_name' exist,
526
+ # even when migrating down:
527
+ #
528
+ # class SplitNameMigration < ActiveRecord::Migration
529
+ # def change
530
+ # add_column :users, :first_name, :string
531
+ # add_column :users, :last_name, :string
532
+ #
533
+ # reversible do |dir|
534
+ # User.reset_column_information
535
+ # User.all.each do |u|
536
+ # dir.up { u.first_name, u.last_name = u.full_name.split(' ') }
537
+ # dir.down { u.full_name = "#{u.first_name} #{u.last_name}" }
538
+ # u.save
539
+ # end
540
+ # end
541
+ #
542
+ # revert { add_column :users, :full_name, :string }
543
+ # end
544
+ # end
545
+ def reversible
546
+ helper = ReversibleBlockHelper.new(reverting?)
547
+ execute_block{ yield helper }
548
+ end
549
+
550
+ # Runs the given migration classes.
551
+ # Last argument can specify options:
552
+ # - :direction (default is :up)
553
+ # - :revert (default is false)
554
+ def run(*migration_classes)
555
+ opts = migration_classes.extract_options!
556
+ dir = opts[:direction] || :up
557
+ dir = (dir == :down ? :up : :down) if opts[:revert]
558
+ if reverting?
559
+ # If in revert and going :up, say, we want to execute :down without reverting, so
560
+ revert { run(*migration_classes, direction: dir, revert: true) }
561
+ else
562
+ migration_classes.each do |migration_class|
563
+ migration_class.new.exec_migration(@connection, dir)
564
+ end
565
+ end
365
566
  end
366
567
 
367
568
  def up
@@ -387,29 +588,9 @@ module ActiveRecord
387
588
 
388
589
  time = nil
389
590
  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) }
591
+ time = Benchmark.measure do
592
+ exec_migration(conn, direction)
411
593
  end
412
- @connection = nil
413
594
  end
414
595
 
415
596
  case direction
@@ -418,6 +599,21 @@ module ActiveRecord
418
599
  end
419
600
  end
420
601
 
602
+ def exec_migration(conn, direction)
603
+ @connection = conn
604
+ if respond_to?(:change)
605
+ if direction == :down
606
+ revert { change }
607
+ else
608
+ change
609
+ end
610
+ else
611
+ send(direction)
612
+ end
613
+ ensure
614
+ @connection = nil
615
+ end
616
+
421
617
  def write(text="")
422
618
  puts(text) if verbose
423
619
  end
@@ -456,10 +652,13 @@ module ActiveRecord
456
652
  arg_list = arguments.map{ |a| a.inspect } * ', '
457
653
 
458
654
  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
462
- arguments[1] = Migrator.proper_table_name(arguments.second) if method == :rename_table
655
+ unless @connection.respond_to? :revert
656
+ unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
657
+ arguments[0] = proper_table_name(arguments.first, table_name_options)
658
+ if [:rename_table, :add_foreign_key].include?(method) ||
659
+ (method == :remove_foreign_key && !arguments.second.is_a?(Hash))
660
+ arguments[1] = proper_table_name(arguments.second, table_name_options)
661
+ end
463
662
  end
464
663
  end
465
664
  return super unless connection.respond_to?(method)
@@ -470,7 +669,7 @@ module ActiveRecord
470
669
  def copy(destination, sources, options = {})
471
670
  copied = []
472
671
 
473
- FileUtils.mkdir_p(destination) unless File.exists?(destination)
672
+ FileUtils.mkdir_p(destination) unless File.exist?(destination)
474
673
 
475
674
  destination_migrations = ActiveRecord::Migrator.migrations(destination)
476
675
  last = destination_migrations.last
@@ -478,8 +677,17 @@ module ActiveRecord
478
677
  source_migrations = ActiveRecord::Migrator.migrations(path)
479
678
 
480
679
  source_migrations.each do |migration|
481
- source = File.read(migration.filename)
482
- source = "# This migration comes from #{scope} (originally #{migration.version})\n#{source}"
680
+ source = File.binread(migration.filename)
681
+ inserted_comment = "# This migration comes from #{scope} (originally #{migration.version})\n"
682
+ if /\A#.*\b(?:en)?coding:\s*\S+/ =~ source
683
+ # If we have a magic comment in the original migration,
684
+ # insert our comment after the first newline(end of the magic comment line)
685
+ # so the magic keep working.
686
+ # Note that magic comments must be at the first line(except sh-bang).
687
+ source[/\n/] = "\n#{inserted_comment}"
688
+ else
689
+ source = "#{inserted_comment}#{source}"
690
+ end
483
691
 
484
692
  if duplicate = destination_migrations.detect { |m| m.name == migration.name }
485
693
  if options[:on_skip] && duplicate.scope != scope.to_s
@@ -493,7 +701,7 @@ module ActiveRecord
493
701
  old_path, migration.filename = migration.filename, new_path
494
702
  last = migration
495
703
 
496
- File.open(migration.filename, "w") { |f| f.write source }
704
+ File.binwrite(migration.filename, source)
497
705
  copied << migration
498
706
  options[:on_copy].call(scope, migration, old_path) if options[:on_copy]
499
707
  destination_migrations << migration
@@ -503,11 +711,39 @@ module ActiveRecord
503
711
  copied
504
712
  end
505
713
 
714
+ # Finds the correct table name given an Active Record object.
715
+ # Uses the Active Record object's own table_name, or pre/suffix from the
716
+ # options passed in.
717
+ def proper_table_name(name, options = {})
718
+ if name.respond_to? :table_name
719
+ name.table_name
720
+ else
721
+ "#{options[:table_name_prefix]}#{name}#{options[:table_name_suffix]}"
722
+ end
723
+ end
724
+
725
+ # Determines the version number of the next migration.
506
726
  def next_migration_number(number)
507
727
  if ActiveRecord::Base.timestamped_migrations
508
728
  [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % number].max
509
729
  else
510
- "%.3d" % number
730
+ SchemaMigration.normalize_migration_number(number)
731
+ end
732
+ end
733
+
734
+ def table_name_options(config = ActiveRecord::Base)
735
+ {
736
+ table_name_prefix: config.table_name_prefix,
737
+ table_name_suffix: config.table_name_suffix
738
+ }
739
+ end
740
+
741
+ private
742
+ def execute_block
743
+ if connection.respond_to? :execute_block
744
+ super # use normal delegation to record the block
745
+ else
746
+ yield
511
747
  end
512
748
  end
513
749
  end
@@ -525,7 +761,11 @@ module ActiveRecord
525
761
  File.basename(filename)
526
762
  end
527
763
 
528
- delegate :migrate, :announce, :write, :to => :migration
764
+ def mtime
765
+ File.mtime filename
766
+ end
767
+
768
+ delegate :migrate, :announce, :write, :disable_ddl_transaction, to: :migration
529
769
 
530
770
  private
531
771
 
@@ -535,11 +775,21 @@ module ActiveRecord
535
775
 
536
776
  def load_migration
537
777
  require(File.expand_path(filename))
538
- name.constantize.new
778
+ name.constantize.new(name, version)
539
779
  end
540
780
 
541
781
  end
542
782
 
783
+ class NullMigration < MigrationProxy #:nodoc:
784
+ def initialize
785
+ super(nil, 0, nil, nil)
786
+ end
787
+
788
+ def mtime
789
+ 0
790
+ end
791
+ end
792
+
543
793
  class Migrator#:nodoc:
544
794
  class << self
545
795
  attr_writer :migrations_paths
@@ -547,14 +797,14 @@ module ActiveRecord
547
797
 
548
798
  def migrate(migrations_paths, target_version = nil, &block)
549
799
  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)
800
+ when target_version.nil?
801
+ up(migrations_paths, target_version, &block)
802
+ when current_version == 0 && target_version == 0
803
+ []
804
+ when current_version > target_version
805
+ down(migrations_paths, target_version, &block)
806
+ else
807
+ up(migrations_paths, target_version, &block)
558
808
  end
559
809
  end
560
810
 
@@ -566,88 +816,117 @@ module ActiveRecord
566
816
  move(:up, migrations_paths, steps)
567
817
  end
568
818
 
569
- def up(migrations_paths, target_version = nil, &block)
570
- self.new(:up, migrations_paths, target_version).migrate(&block)
819
+ def up(migrations_paths, target_version = nil)
820
+ migrations = migrations(migrations_paths)
821
+ migrations.select! { |m| yield m } if block_given?
822
+
823
+ new(:up, migrations, target_version).migrate
571
824
  end
572
825
 
573
826
  def down(migrations_paths, target_version = nil, &block)
574
- self.new(:down, migrations_paths, target_version).migrate(&block)
827
+ migrations = migrations(migrations_paths)
828
+ migrations.select! { |m| yield m } if block_given?
829
+
830
+ new(:down, migrations, target_version).migrate
575
831
  end
576
832
 
577
833
  def run(direction, migrations_paths, target_version)
578
- self.new(direction, migrations_paths, target_version).run
834
+ new(direction, migrations(migrations_paths), target_version).run
579
835
  end
580
836
 
581
- def schema_migrations_table_name
582
- Base.table_name_prefix + 'schema_migrations' + Base.table_name_suffix
837
+ def open(migrations_paths)
838
+ new(:up, migrations(migrations_paths), nil)
583
839
  end
584
840
 
585
- 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
841
+ def schema_migrations_table_name
842
+ SchemaMigration.table_name
588
843
  end
589
844
 
590
- def current_version
591
- sm_table = schema_migrations_table_name
592
- if Base.connection.table_exists?(sm_table)
593
- get_all_versions.max || 0
845
+ def get_all_versions(connection = Base.connection)
846
+ if connection.table_exists?(schema_migrations_table_name)
847
+ SchemaMigration.all.map { |x| x.version.to_i }.sort
594
848
  else
595
- 0
849
+ []
596
850
  end
597
851
  end
598
852
 
599
- def proper_table_name(name)
600
- # 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}"
853
+ def current_version(connection = Base.connection)
854
+ get_all_versions(connection).max || 0
855
+ end
856
+
857
+ def needs_migration?(connection = Base.connection)
858
+ (migrations(migrations_paths).collect(&:version) - get_all_versions(connection)).size > 0
859
+ end
860
+
861
+ def any_migrations?
862
+ migrations(migrations_paths).any?
863
+ end
864
+
865
+ def last_version
866
+ last_migration.version
867
+ end
868
+
869
+ def last_migration #:nodoc:
870
+ migrations(migrations_paths).last || NullMigration.new
602
871
  end
603
872
 
604
873
  def migrations_paths
605
874
  @migrations_paths ||= ['db/migrate']
606
875
  # just to not break things if someone uses: migration_path = some_string
607
- Array.wrap(@migrations_paths)
876
+ Array(@migrations_paths)
608
877
  end
609
878
 
610
879
  def migrations_path
611
880
  migrations_paths.first
612
881
  end
613
882
 
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" }]
626
-
627
- seen = Hash.new false
883
+ def parse_migration_filename(filename) # :nodoc:
884
+ File.basename(filename).scan(/\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
885
+ end
628
886
 
629
- migrations = files.map do |file|
630
- version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
887
+ def migrations(paths)
888
+ paths = Array(paths)
631
889
 
890
+ migrations = migration_files(paths).map do |file|
891
+ version, name, scope = parse_migration_filename(file)
632
892
  raise IllegalMigrationNameError.new(file) unless version
633
893
  version = version.to_i
634
894
  name = name.camelize
635
895
 
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
896
  MigrationProxy.new(name, version, file, scope)
642
897
  end
643
898
 
644
899
  migrations.sort_by(&:version)
645
900
  end
646
901
 
902
+ def migrations_status(paths)
903
+ paths = Array(paths)
904
+
905
+ db_list = ActiveRecord::SchemaMigration.normalized_versions
906
+
907
+ file_list = migration_files(paths).map do |file|
908
+ version, name, scope = parse_migration_filename(file)
909
+ raise IllegalMigrationNameError.new(file) unless version
910
+ version = ActiveRecord::SchemaMigration.normalize_migration_number(version)
911
+ status = db_list.delete(version) ? "up" : "down"
912
+ [status, version, (name + scope).humanize]
913
+ end.compact
914
+
915
+ db_list.map! do |version|
916
+ ["up", version, "********** NO FILE **********"]
917
+ end
918
+
919
+ (db_list + file_list).sort_by { |_, version, _| version }
920
+ end
921
+
922
+ def migration_files(paths)
923
+ Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
924
+ end
925
+
647
926
  private
648
927
 
649
928
  def move(direction, migrations_paths, steps)
650
- migrator = self.new(direction, migrations_paths)
929
+ migrator = new(direction, migrations(migrations_paths))
651
930
  start_index = migrator.migrations.index(migrator.current_migration)
652
931
 
653
932
  if start_index
@@ -658,124 +937,143 @@ module ActiveRecord
658
937
  end
659
938
  end
660
939
 
661
- def initialize(direction, migrations_paths, target_version = nil)
940
+ def initialize(direction, migrations, target_version = nil)
662
941
  raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?
942
+
943
+ @direction = direction
944
+ @target_version = target_version
945
+ @migrated_versions = nil
946
+ @migrations = migrations
947
+
948
+ validate(@migrations)
949
+
663
950
  Base.connection.initialize_schema_migrations_table
664
- @direction, @migrations_paths, @target_version = direction, migrations_paths, target_version
665
951
  end
666
952
 
667
953
  def current_version
668
- migrated.last || 0
954
+ migrated.max || 0
669
955
  end
670
956
 
671
957
  def current_migration
672
958
  migrations.detect { |m| m.version == current_version }
673
959
  end
960
+ alias :current :current_migration
674
961
 
675
962
  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)
963
+ migration = migrations.detect { |m| m.version == @target_version }
964
+ raise UnknownMigrationVersionError.new(@target_version) if migration.nil?
965
+ unless (up? && migrated.include?(migration.version.to_i)) || (down? && !migrated.include?(migration.version.to_i))
966
+ begin
967
+ execute_migration_in_transaction(migration, @direction)
968
+ rescue => e
969
+ canceled_msg = use_transaction?(migration) ? ", this migration was canceled" : ""
970
+ raise StandardError, "An error has occurred#{canceled_msg}:\n\n#{e}", e.backtrace
971
+ end
681
972
  end
682
973
  end
683
974
 
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
975
+ def migrate
976
+ if !target && @target_version && @target_version > 0
689
977
  raise UnknownMigrationVersionError.new(@target_version)
690
978
  end
691
979
 
692
- start = up? ? 0 : (migrations.index(current) || 0)
693
- finish = migrations.index(target) || migrations.size - 1
694
- runnable = migrations[start..finish]
695
-
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
980
  runnable.each do |migration|
701
- if block && !block.call(migration)
702
- next
703
- end
704
-
705
981
  Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
706
982
 
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
983
  begin
719
- ddl_transaction do
720
- migration.migrate(@direction)
721
- record_version_state_after_migrating(migration.version)
722
- end
723
- ran << migration
984
+ execute_migration_in_transaction(migration, @direction)
724
985
  rescue => e
725
- canceled_msg = Base.connection.supports_ddl_transactions? ? "this and " : ""
986
+ canceled_msg = use_transaction?(migration) ? "this and " : ""
726
987
  raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace
727
988
  end
728
989
  end
729
- ran
730
990
  end
731
991
 
732
- def migrations
733
- @migrations ||= begin
734
- migrations = self.class.migrations(@migrations_paths)
735
- down? ? migrations.reverse : migrations
992
+ def runnable
993
+ runnable = migrations[start..finish]
994
+ if up?
995
+ runnable.reject { |m| ran?(m) }
996
+ else
997
+ # skip the last migration if we're headed down, but not ALL the way down
998
+ runnable.pop if target
999
+ runnable.find_all { |m| ran?(m) }
736
1000
  end
737
1001
  end
738
1002
 
1003
+ def migrations
1004
+ down? ? @migrations.reverse : @migrations.sort_by(&:version)
1005
+ end
1006
+
739
1007
  def pending_migrations
740
1008
  already_migrated = migrated
741
- migrations.reject { |m| already_migrated.include?(m.version.to_i) }
1009
+ migrations.reject { |m| already_migrated.include?(m.version) }
742
1010
  end
743
1011
 
744
1012
  def migrated
745
- @migrated_versions ||= self.class.get_all_versions
1013
+ @migrated_versions ||= Set.new(self.class.get_all_versions)
746
1014
  end
747
1015
 
748
1016
  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
1017
+ def ran?(migration)
1018
+ migrated.include?(migration.version.to_i)
1019
+ end
763
1020
 
764
- def up?
765
- @direction == :up
1021
+ def execute_migration_in_transaction(migration, direction)
1022
+ ddl_transaction(migration) do
1023
+ migration.migrate(direction)
1024
+ record_version_state_after_migrating(migration.version)
766
1025
  end
1026
+ end
1027
+
1028
+ def target
1029
+ migrations.detect { |m| m.version == @target_version }
1030
+ end
1031
+
1032
+ def finish
1033
+ migrations.index(target) || migrations.size - 1
1034
+ end
767
1035
 
768
- def down?
769
- @direction == :down
1036
+ def start
1037
+ up? ? 0 : (migrations.index(current) || 0)
1038
+ end
1039
+
1040
+ def validate(migrations)
1041
+ name ,= migrations.group_by(&:name).find { |_,v| v.length > 1 }
1042
+ raise DuplicateMigrationNameError.new(name) if name
1043
+
1044
+ version ,= migrations.group_by(&:version).find { |_,v| v.length > 1 }
1045
+ raise DuplicateMigrationVersionError.new(version) if version
1046
+ end
1047
+
1048
+ def record_version_state_after_migrating(version)
1049
+ if down?
1050
+ migrated.delete(version)
1051
+ ActiveRecord::SchemaMigration.where(:version => version.to_s).delete_all
1052
+ else
1053
+ migrated << version
1054
+ ActiveRecord::SchemaMigration.create!(:version => version.to_s)
770
1055
  end
1056
+ end
771
1057
 
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
1058
+ def up?
1059
+ @direction == :up
1060
+ end
1061
+
1062
+ def down?
1063
+ @direction == :down
1064
+ end
1065
+
1066
+ # Wrap the migration in a transaction only if supported by the adapter.
1067
+ def ddl_transaction(migration)
1068
+ if use_transaction?(migration)
1069
+ Base.transaction { yield }
1070
+ else
1071
+ yield
779
1072
  end
1073
+ end
1074
+
1075
+ def use_transaction?(migration)
1076
+ !migration.disable_ddl_transaction && Base.connection.supports_ddl_transactions?
1077
+ end
780
1078
  end
781
1079
  end