activerecord 4.2.0

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 (221) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1372 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +218 -0
  5. data/examples/performance.rb +184 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record.rb +173 -0
  8. data/lib/active_record/aggregations.rb +266 -0
  9. data/lib/active_record/association_relation.rb +22 -0
  10. data/lib/active_record/associations.rb +1724 -0
  11. data/lib/active_record/associations/alias_tracker.rb +87 -0
  12. data/lib/active_record/associations/association.rb +253 -0
  13. data/lib/active_record/associations/association_scope.rb +194 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +111 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
  16. data/lib/active_record/associations/builder/association.rb +149 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +116 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +91 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +124 -0
  20. data/lib/active_record/associations/builder/has_many.rb +15 -0
  21. data/lib/active_record/associations/builder/has_one.rb +23 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +38 -0
  23. data/lib/active_record/associations/collection_association.rb +634 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1027 -0
  25. data/lib/active_record/associations/has_many_association.rb +184 -0
  26. data/lib/active_record/associations/has_many_through_association.rb +238 -0
  27. data/lib/active_record/associations/has_one_association.rb +105 -0
  28. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  29. data/lib/active_record/associations/join_dependency.rb +282 -0
  30. data/lib/active_record/associations/join_dependency/join_association.rb +122 -0
  31. data/lib/active_record/associations/join_dependency/join_base.rb +22 -0
  32. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  33. data/lib/active_record/associations/preloader.rb +203 -0
  34. data/lib/active_record/associations/preloader/association.rb +162 -0
  35. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  36. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  37. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  38. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  39. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  40. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  41. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  42. data/lib/active_record/associations/preloader/through_association.rb +96 -0
  43. data/lib/active_record/associations/singular_association.rb +86 -0
  44. data/lib/active_record/associations/through_association.rb +96 -0
  45. data/lib/active_record/attribute.rb +149 -0
  46. data/lib/active_record/attribute_assignment.rb +212 -0
  47. data/lib/active_record/attribute_decorators.rb +66 -0
  48. data/lib/active_record/attribute_methods.rb +439 -0
  49. data/lib/active_record/attribute_methods/before_type_cast.rb +71 -0
  50. data/lib/active_record/attribute_methods/dirty.rb +181 -0
  51. data/lib/active_record/attribute_methods/primary_key.rb +128 -0
  52. data/lib/active_record/attribute_methods/query.rb +40 -0
  53. data/lib/active_record/attribute_methods/read.rb +103 -0
  54. data/lib/active_record/attribute_methods/serialization.rb +70 -0
  55. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -0
  56. data/lib/active_record/attribute_methods/write.rb +83 -0
  57. data/lib/active_record/attribute_set.rb +77 -0
  58. data/lib/active_record/attribute_set/builder.rb +86 -0
  59. data/lib/active_record/attributes.rb +139 -0
  60. data/lib/active_record/autosave_association.rb +439 -0
  61. data/lib/active_record/base.rb +317 -0
  62. data/lib/active_record/callbacks.rb +313 -0
  63. data/lib/active_record/coders/json.rb +13 -0
  64. data/lib/active_record/coders/yaml_column.rb +38 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +659 -0
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +373 -0
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +133 -0
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +574 -0
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +991 -0
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +219 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -0
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +883 -0
  78. data/lib/active_record/connection_adapters/column.rb +82 -0
  79. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +282 -0
  81. data/lib/active_record/connection_adapters/mysql_adapter.rb +491 -0
  82. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  111. data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
  112. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +588 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +754 -0
  117. data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +628 -0
  119. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  120. data/lib/active_record/connection_handling.rb +132 -0
  121. data/lib/active_record/core.rb +566 -0
  122. data/lib/active_record/counter_cache.rb +175 -0
  123. data/lib/active_record/dynamic_matchers.rb +140 -0
  124. data/lib/active_record/enum.rb +198 -0
  125. data/lib/active_record/errors.rb +252 -0
  126. data/lib/active_record/explain.rb +38 -0
  127. data/lib/active_record/explain_registry.rb +30 -0
  128. data/lib/active_record/explain_subscriber.rb +29 -0
  129. data/lib/active_record/fixture_set/file.rb +56 -0
  130. data/lib/active_record/fixtures.rb +1007 -0
  131. data/lib/active_record/gem_version.rb +15 -0
  132. data/lib/active_record/inheritance.rb +247 -0
  133. data/lib/active_record/integration.rb +113 -0
  134. data/lib/active_record/locale/en.yml +47 -0
  135. data/lib/active_record/locking/optimistic.rb +204 -0
  136. data/lib/active_record/locking/pessimistic.rb +77 -0
  137. data/lib/active_record/log_subscriber.rb +75 -0
  138. data/lib/active_record/migration.rb +1051 -0
  139. data/lib/active_record/migration/command_recorder.rb +197 -0
  140. data/lib/active_record/migration/join_table.rb +15 -0
  141. data/lib/active_record/model_schema.rb +340 -0
  142. data/lib/active_record/nested_attributes.rb +548 -0
  143. data/lib/active_record/no_touching.rb +52 -0
  144. data/lib/active_record/null_relation.rb +81 -0
  145. data/lib/active_record/persistence.rb +532 -0
  146. data/lib/active_record/query_cache.rb +56 -0
  147. data/lib/active_record/querying.rb +68 -0
  148. data/lib/active_record/railtie.rb +162 -0
  149. data/lib/active_record/railties/console_sandbox.rb +5 -0
  150. data/lib/active_record/railties/controller_runtime.rb +50 -0
  151. data/lib/active_record/railties/databases.rake +391 -0
  152. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  153. data/lib/active_record/readonly_attributes.rb +23 -0
  154. data/lib/active_record/reflection.rb +881 -0
  155. data/lib/active_record/relation.rb +681 -0
  156. data/lib/active_record/relation/batches.rb +138 -0
  157. data/lib/active_record/relation/calculations.rb +403 -0
  158. data/lib/active_record/relation/delegation.rb +140 -0
  159. data/lib/active_record/relation/finder_methods.rb +528 -0
  160. data/lib/active_record/relation/merger.rb +170 -0
  161. data/lib/active_record/relation/predicate_builder.rb +126 -0
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +47 -0
  163. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  164. data/lib/active_record/relation/query_methods.rb +1176 -0
  165. data/lib/active_record/relation/spawn_methods.rb +75 -0
  166. data/lib/active_record/result.rb +131 -0
  167. data/lib/active_record/runtime_registry.rb +22 -0
  168. data/lib/active_record/sanitization.rb +191 -0
  169. data/lib/active_record/schema.rb +64 -0
  170. data/lib/active_record/schema_dumper.rb +251 -0
  171. data/lib/active_record/schema_migration.rb +56 -0
  172. data/lib/active_record/scoping.rb +87 -0
  173. data/lib/active_record/scoping/default.rb +134 -0
  174. data/lib/active_record/scoping/named.rb +164 -0
  175. data/lib/active_record/serialization.rb +22 -0
  176. data/lib/active_record/serializers/xml_serializer.rb +193 -0
  177. data/lib/active_record/statement_cache.rb +111 -0
  178. data/lib/active_record/store.rb +205 -0
  179. data/lib/active_record/tasks/database_tasks.rb +296 -0
  180. data/lib/active_record/tasks/mysql_database_tasks.rb +145 -0
  181. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  182. data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
  183. data/lib/active_record/timestamp.rb +121 -0
  184. data/lib/active_record/transactions.rb +417 -0
  185. data/lib/active_record/translation.rb +22 -0
  186. data/lib/active_record/type.rb +23 -0
  187. data/lib/active_record/type/big_integer.rb +13 -0
  188. data/lib/active_record/type/binary.rb +50 -0
  189. data/lib/active_record/type/boolean.rb +30 -0
  190. data/lib/active_record/type/date.rb +46 -0
  191. data/lib/active_record/type/date_time.rb +43 -0
  192. data/lib/active_record/type/decimal.rb +40 -0
  193. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  194. data/lib/active_record/type/decorator.rb +14 -0
  195. data/lib/active_record/type/float.rb +19 -0
  196. data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
  197. data/lib/active_record/type/integer.rb +55 -0
  198. data/lib/active_record/type/mutable.rb +16 -0
  199. data/lib/active_record/type/numeric.rb +36 -0
  200. data/lib/active_record/type/serialized.rb +56 -0
  201. data/lib/active_record/type/string.rb +36 -0
  202. data/lib/active_record/type/text.rb +11 -0
  203. data/lib/active_record/type/time.rb +26 -0
  204. data/lib/active_record/type/time_value.rb +38 -0
  205. data/lib/active_record/type/type_map.rb +64 -0
  206. data/lib/active_record/type/unsigned_integer.rb +15 -0
  207. data/lib/active_record/type/value.rb +101 -0
  208. data/lib/active_record/validations.rb +90 -0
  209. data/lib/active_record/validations/associated.rb +51 -0
  210. data/lib/active_record/validations/presence.rb +67 -0
  211. data/lib/active_record/validations/uniqueness.rb +229 -0
  212. data/lib/active_record/version.rb +8 -0
  213. data/lib/rails/generators/active_record.rb +17 -0
  214. data/lib/rails/generators/active_record/migration.rb +18 -0
  215. data/lib/rails/generators/active_record/migration/migration_generator.rb +70 -0
  216. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +22 -0
  217. data/lib/rails/generators/active_record/migration/templates/migration.rb +45 -0
  218. data/lib/rails/generators/active_record/model/model_generator.rb +52 -0
  219. data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
  220. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  221. metadata +309 -0
@@ -0,0 +1,77 @@
1
+ module ActiveRecord
2
+ module Locking
3
+ # Locking::Pessimistic provides support for row-level locking using
4
+ # SELECT ... FOR UPDATE and other lock types.
5
+ #
6
+ # Chain <tt>ActiveRecord::Base#find</tt> to <tt>ActiveRecord::QueryMethods#lock</tt> to obtain an exclusive
7
+ # lock on the selected rows:
8
+ # # select * from accounts where id=1 for update
9
+ # Account.lock.find(1)
10
+ #
11
+ # Call <tt>lock('some locking clause')</tt> to use a database-specific locking clause
12
+ # of your own such as 'LOCK IN SHARE MODE' or 'FOR UPDATE NOWAIT'. Example:
13
+ #
14
+ # Account.transaction do
15
+ # # select * from accounts where name = 'shugo' limit 1 for update
16
+ # shugo = Account.where("name = 'shugo'").lock(true).first
17
+ # yuko = Account.where("name = 'yuko'").lock(true).first
18
+ # shugo.balance -= 100
19
+ # shugo.save!
20
+ # yuko.balance += 100
21
+ # yuko.save!
22
+ # end
23
+ #
24
+ # You can also use <tt>ActiveRecord::Base#lock!</tt> method to lock one record by id.
25
+ # This may be better if you don't need to lock every row. Example:
26
+ #
27
+ # Account.transaction do
28
+ # # select * from accounts where ...
29
+ # accounts = Account.where(...)
30
+ # account1 = accounts.detect { |account| ... }
31
+ # account2 = accounts.detect { |account| ... }
32
+ # # select * from accounts where id=? for update
33
+ # account1.lock!
34
+ # account2.lock!
35
+ # account1.balance -= 100
36
+ # account1.save!
37
+ # account2.balance += 100
38
+ # account2.save!
39
+ # end
40
+ #
41
+ # You can start a transaction and acquire the lock in one go by calling
42
+ # <tt>with_lock</tt> with a block. The block is called from within
43
+ # a transaction, the object is already locked. Example:
44
+ #
45
+ # account = Account.first
46
+ # account.with_lock do
47
+ # # This block is called within a transaction,
48
+ # # account is already locked.
49
+ # account.balance -= 100
50
+ # account.save!
51
+ # end
52
+ #
53
+ # Database-specific information on row locking:
54
+ # MySQL: http://dev.mysql.com/doc/refman/5.1/en/innodb-locking-reads.html
55
+ # PostgreSQL: http://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
56
+ module Pessimistic
57
+ # Obtain a row lock on this record. Reloads the record to obtain the requested
58
+ # lock. Pass an SQL locking clause to append the end of the SELECT statement
59
+ # or pass true for "FOR UPDATE" (the default, an exclusive row lock). Returns
60
+ # the locked record.
61
+ def lock!(lock = true)
62
+ reload(:lock => lock) if persisted?
63
+ self
64
+ end
65
+
66
+ # Wraps the passed block in a transaction, locking the object
67
+ # before yielding. You can pass the SQL locking clause
68
+ # as argument (see <tt>lock!</tt>).
69
+ def with_lock(lock = true)
70
+ transaction do
71
+ lock!(lock)
72
+ yield
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,75 @@
1
+ module ActiveRecord
2
+ class LogSubscriber < ActiveSupport::LogSubscriber
3
+ IGNORE_PAYLOAD_NAMES = ["SCHEMA", "EXPLAIN"]
4
+
5
+ def self.runtime=(value)
6
+ ActiveRecord::RuntimeRegistry.sql_runtime = value
7
+ end
8
+
9
+ def self.runtime
10
+ ActiveRecord::RuntimeRegistry.sql_runtime ||= 0
11
+ end
12
+
13
+ def self.reset_runtime
14
+ rt, self.runtime = runtime, 0
15
+ rt
16
+ end
17
+
18
+ def initialize
19
+ super
20
+ @odd = false
21
+ end
22
+
23
+ def render_bind(column, value)
24
+ if column
25
+ if column.binary?
26
+ # This specifically deals with the PG adapter that casts bytea columns into a Hash.
27
+ value = value[:value] if value.is_a?(Hash)
28
+ value = value ? "<#{value.bytesize} bytes of binary data>" : "<NULL binary data>"
29
+ end
30
+
31
+ [column.name, value]
32
+ else
33
+ [nil, value]
34
+ end
35
+ end
36
+
37
+ def sql(event)
38
+ self.class.runtime += event.duration
39
+ return unless logger.debug?
40
+
41
+ payload = event.payload
42
+
43
+ return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
44
+
45
+ name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
46
+ sql = payload[:sql]
47
+ binds = nil
48
+
49
+ unless (payload[:binds] || []).empty?
50
+ binds = " " + payload[:binds].map { |col,v|
51
+ render_bind(col, v)
52
+ }.inspect
53
+ end
54
+
55
+ if odd?
56
+ name = color(name, CYAN, true)
57
+ sql = color(sql, nil, true)
58
+ else
59
+ name = color(name, MAGENTA, true)
60
+ end
61
+
62
+ debug " #{name} #{sql}#{binds}"
63
+ end
64
+
65
+ def odd?
66
+ @odd = !@odd
67
+ end
68
+
69
+ def logger
70
+ ActiveRecord::Base.logger
71
+ end
72
+ end
73
+ end
74
+
75
+ ActiveRecord::LogSubscriber.attach_to :active_record
@@ -0,0 +1,1051 @@
1
+ require "active_support/core_ext/module/attribute_accessors"
2
+ require 'set'
3
+
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
+
12
+ # Exception that can be raised to stop migrations from going backwards.
13
+ class IrreversibleMigration < MigrationError
14
+ end
15
+
16
+ class DuplicateMigrationVersionError < MigrationError#:nodoc:
17
+ def initialize(version)
18
+ super("Multiple migrations have the version number #{version}")
19
+ end
20
+ end
21
+
22
+ class DuplicateMigrationNameError < MigrationError#:nodoc:
23
+ def initialize(name)
24
+ super("Multiple migrations have the name #{name}")
25
+ end
26
+ end
27
+
28
+ class UnknownMigrationVersionError < MigrationError #:nodoc:
29
+ def initialize(version)
30
+ super("No migration with version number #{version}")
31
+ end
32
+ end
33
+
34
+ class IllegalMigrationNameError < MigrationError#:nodoc:
35
+ def initialize(name)
36
+ super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed)")
37
+ end
38
+ end
39
+
40
+ class PendingMigrationError < MigrationError#:nodoc:
41
+ def initialize
42
+ if defined?(Rails)
43
+ super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rake db:migrate RAILS_ENV=#{::Rails.env}")
44
+ else
45
+ super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rake db:migrate")
46
+ end
47
+ end
48
+ end
49
+
50
+ # = Active Record Migrations
51
+ #
52
+ # Migrations can manage the evolution of a schema used by several physical
53
+ # databases. It's a solution to the common problem of adding a field to make
54
+ # a new feature work in your local database, but being unsure of how to
55
+ # push that change to other developers and to the production server. With
56
+ # migrations, you can describe the transformations in self-contained classes
57
+ # that can be checked into version control systems and executed against
58
+ # another database that might be one, two, or five versions behind.
59
+ #
60
+ # Example of a simple migration:
61
+ #
62
+ # class AddSsl < ActiveRecord::Migration
63
+ # def up
64
+ # add_column :accounts, :ssl_enabled, :boolean, default: true
65
+ # end
66
+ #
67
+ # def down
68
+ # remove_column :accounts, :ssl_enabled
69
+ # end
70
+ # end
71
+ #
72
+ # This migration will add a boolean flag to the accounts table and remove it
73
+ # if you're backing out of the migration. It shows how all migrations have
74
+ # two methods +up+ and +down+ that describes the transformations
75
+ # required to implement or remove the migration. These methods can consist
76
+ # of both the migration specific methods like +add_column+ and +remove_column+,
77
+ # but may also contain regular Ruby code for generating data needed for the
78
+ # transformations.
79
+ #
80
+ # Example of a more complex migration that also needs to initialize data:
81
+ #
82
+ # class AddSystemSettings < ActiveRecord::Migration
83
+ # def up
84
+ # create_table :system_settings do |t|
85
+ # t.string :name
86
+ # t.string :label
87
+ # t.text :value
88
+ # t.string :type
89
+ # t.integer :position
90
+ # end
91
+ #
92
+ # SystemSetting.create name: 'notice',
93
+ # label: 'Use notice?',
94
+ # value: 1
95
+ # end
96
+ #
97
+ # def down
98
+ # drop_table :system_settings
99
+ # end
100
+ # end
101
+ #
102
+ # This migration first adds the +system_settings+ table, then creates the very
103
+ # first row in it using the Active Record model that relies on the table. It
104
+ # also uses the more advanced +create_table+ syntax where you can specify a
105
+ # complete table schema in one block call.
106
+ #
107
+ # == Available transformations
108
+ #
109
+ # * <tt>create_table(name, options)</tt>: Creates a table called +name+ and
110
+ # makes the table object available to a block that can then add columns to it,
111
+ # following the same format as +add_column+. See example above. The options hash
112
+ # is for fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create
113
+ # table definition.
114
+ # * <tt>drop_table(name)</tt>: Drops the table called +name+.
115
+ # * <tt>change_table(name, options)</tt>: Allows to make column alterations to
116
+ # the table called +name+. It makes the table object available to a block that
117
+ # can then add/remove columns, indexes or foreign keys to it.
118
+ # * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
119
+ # to +new_name+.
120
+ # * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column
121
+ # to the table called +table_name+
122
+ # named +column_name+ specified to be one of the following types:
123
+ # <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>,
124
+ # <tt>:decimal</tt>, <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
125
+ # <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>. A default value can be
126
+ # specified by passing an +options+ hash like <tt>{ default: 11 }</tt>.
127
+ # Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g.
128
+ # <tt>{ limit: 50, null: false }</tt>) -- see
129
+ # ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
130
+ # * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames
131
+ # a column but keeps the type and content.
132
+ # * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
133
+ # the column to a different type using the same parameters as add_column.
134
+ # * <tt>remove_column(table_name, column_name, type, options)</tt>: Removes the column
135
+ # named +column_name+ from the table called +table_name+.
136
+ # * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
137
+ # with the name of the column. Other options include
138
+ # <tt>:name</tt>, <tt>:unique</tt> (e.g.
139
+ # <tt>{ name: 'users_name_index', unique: true }</tt>) and <tt>:order</tt>
140
+ # (e.g. <tt>{ order: { name: :desc } }</tt>).
141
+ # * <tt>remove_index(table_name, column: column_name)</tt>: Removes the index
142
+ # specified by +column_name+.
143
+ # * <tt>remove_index(table_name, name: index_name)</tt>: Removes the index
144
+ # specified by +index_name+.
145
+ #
146
+ # == Irreversible transformations
147
+ #
148
+ # Some transformations are destructive in a manner that cannot be reversed.
149
+ # Migrations of that kind should raise an <tt>ActiveRecord::IrreversibleMigration</tt>
150
+ # exception in their +down+ method.
151
+ #
152
+ # == Running migrations from within Rails
153
+ #
154
+ # The Rails package has several tools to help create and apply migrations.
155
+ #
156
+ # To generate a new migration, you can use
157
+ # rails generate migration MyNewMigration
158
+ #
159
+ # where MyNewMigration is the name of your migration. The generator will
160
+ # create an empty migration file <tt>timestamp_my_new_migration.rb</tt>
161
+ # in the <tt>db/migrate/</tt> directory where <tt>timestamp</tt> is the
162
+ # UTC formatted date and time that the migration was generated.
163
+ #
164
+ # There is a special syntactic shortcut to generate migrations that add fields to a table.
165
+ #
166
+ # rails generate migration add_fieldname_to_tablename fieldname:string
167
+ #
168
+ # This will generate the file <tt>timestamp_add_fieldname_to_tablename</tt>, which will look like this:
169
+ # class AddFieldnameToTablename < ActiveRecord::Migration
170
+ # def change
171
+ # add_column :tablenames, :field, :string
172
+ # end
173
+ # end
174
+ #
175
+ # To run migrations against the currently configured database, use
176
+ # <tt>rake db:migrate</tt>. This will update the database by running all of the
177
+ # pending migrations, creating the <tt>schema_migrations</tt> table
178
+ # (see "About the schema_migrations table" section below) if missing. It will also
179
+ # invoke the db:schema:dump task, which will update your db/schema.rb file
180
+ # to match the structure of your database.
181
+ #
182
+ # To roll the database back to a previous migration version, use
183
+ # <tt>rake db:migrate VERSION=X</tt> where <tt>X</tt> is the version to which
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.
190
+ #
191
+ # == Database support
192
+ #
193
+ # Migrations are currently supported in MySQL, PostgreSQL, SQLite,
194
+ # SQL Server, and Oracle (all supported databases except DB2).
195
+ #
196
+ # == More examples
197
+ #
198
+ # Not all migrations change the schema. Some just fix the data:
199
+ #
200
+ # class RemoveEmptyTags < ActiveRecord::Migration
201
+ # def up
202
+ # Tag.all.each { |tag| tag.destroy if tag.pages.empty? }
203
+ # end
204
+ #
205
+ # def down
206
+ # # not much we can do to restore deleted data
207
+ # raise ActiveRecord::IrreversibleMigration, "Can't recover the deleted tags"
208
+ # end
209
+ # end
210
+ #
211
+ # Others remove columns when they migrate up instead of down:
212
+ #
213
+ # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration
214
+ # def up
215
+ # remove_column :items, :incomplete_items_count
216
+ # remove_column :items, :completed_items_count
217
+ # end
218
+ #
219
+ # def down
220
+ # add_column :items, :incomplete_items_count
221
+ # add_column :items, :completed_items_count
222
+ # end
223
+ # end
224
+ #
225
+ # And sometimes you need to do something in SQL not abstracted directly by migrations:
226
+ #
227
+ # class MakeJoinUnique < ActiveRecord::Migration
228
+ # def up
229
+ # execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
230
+ # end
231
+ #
232
+ # def down
233
+ # execute "ALTER TABLE `pages_linked_pages` DROP INDEX `page_id_linked_page_id`"
234
+ # end
235
+ # end
236
+ #
237
+ # == Using a model after changing its table
238
+ #
239
+ # Sometimes you'll want to add a column in a migration and populate it
240
+ # immediately after. In that case, you'll need to make a call to
241
+ # <tt>Base#reset_column_information</tt> in order to ensure that the model has the
242
+ # latest column data from after the new column was added. Example:
243
+ #
244
+ # class AddPeopleSalary < ActiveRecord::Migration
245
+ # def up
246
+ # add_column :people, :salary, :integer
247
+ # Person.reset_column_information
248
+ # Person.all.each do |p|
249
+ # p.update_attribute :salary, SalaryCalculator.compute(p)
250
+ # end
251
+ # end
252
+ # end
253
+ #
254
+ # == Controlling verbosity
255
+ #
256
+ # By default, migrations will describe the actions they are taking, writing
257
+ # them to the console as they happen, along with benchmarks describing how
258
+ # long each step took.
259
+ #
260
+ # You can quiet them down by setting ActiveRecord::Migration.verbose = false.
261
+ #
262
+ # You can also insert your own messages and benchmarks by using the +say_with_time+
263
+ # method:
264
+ #
265
+ # def up
266
+ # ...
267
+ # say_with_time "Updating salaries..." do
268
+ # Person.all.each do |p|
269
+ # p.update_attribute :salary, SalaryCalculator.compute(p)
270
+ # end
271
+ # end
272
+ # ...
273
+ # end
274
+ #
275
+ # The phrase "Updating salaries..." would then be printed, along with the
276
+ # benchmark for the block when the block completes.
277
+ #
278
+ # == About the schema_migrations table
279
+ #
280
+ # Rails versions 2.0 and prior used to create a table called
281
+ # <tt>schema_info</tt> when using migrations. This table contained the
282
+ # version of the schema as of the last applied migration.
283
+ #
284
+ # Starting with Rails 2.1, the <tt>schema_info</tt> table is
285
+ # (automatically) replaced by the <tt>schema_migrations</tt> table, which
286
+ # contains the version numbers of all the migrations applied.
287
+ #
288
+ # As a result, it is now possible to add migration files that are numbered
289
+ # lower than the current schema version: when migrating up, those
290
+ # never-applied "interleaved" migrations will be automatically applied, and
291
+ # when migrating down, never-applied "interleaved" migrations will be skipped.
292
+ #
293
+ # == Timestamped Migrations
294
+ #
295
+ # By default, Rails generates migrations that look like:
296
+ #
297
+ # 20080717013526_your_migration_name.rb
298
+ #
299
+ # The prefix is a generation timestamp (in UTC).
300
+ #
301
+ # If you'd prefer to use numeric prefixes, you can turn timestamped migrations
302
+ # off by setting:
303
+ #
304
+ # config.active_record.timestamped_migrations = false
305
+ #
306
+ # In application.rb.
307
+ #
308
+ # == Reversible Migrations
309
+ #
310
+ # Starting with Rails 3.1, you will be able to define reversible migrations.
311
+ # Reversible migrations are migrations that know how to go +down+ for you.
312
+ # You simply supply the +up+ logic, and the Migration system will figure out
313
+ # how to execute the down commands for you.
314
+ #
315
+ # To define a reversible migration, define the +change+ method in your
316
+ # migration like this:
317
+ #
318
+ # class TenderloveMigration < ActiveRecord::Migration
319
+ # def change
320
+ # create_table(:horses) do |t|
321
+ # t.column :content, :text
322
+ # t.column :remind_at, :datetime
323
+ # end
324
+ # end
325
+ # end
326
+ #
327
+ # This migration will create the horses table for you on the way up, and
328
+ # automatically figure out how to drop the table on the way down.
329
+ #
330
+ # Some commands like +remove_column+ cannot be reversed. If you care to
331
+ # define how to move up and down in these cases, you should define the +up+
332
+ # and +down+ methods as before.
333
+ #
334
+ # If a command cannot be reversed, an
335
+ # <tt>ActiveRecord::IrreversibleMigration</tt> exception will be raised when
336
+ # the migration is moving down.
337
+ #
338
+ # For a list of commands that are reversible, please see
339
+ # <tt>ActiveRecord::Migration::CommandRecorder</tt>.
340
+ #
341
+ # == Transactional Migrations
342
+ #
343
+ # If the database adapter supports DDL transactions, all migrations will
344
+ # automatically be wrapped in a transaction. There are queries that you
345
+ # can't execute inside a transaction though, and for these situations
346
+ # you can turn the automatic transactions off.
347
+ #
348
+ # class ChangeEnum < ActiveRecord::Migration
349
+ # disable_ddl_transaction!
350
+ #
351
+ # def up
352
+ # execute "ALTER TYPE model_size ADD VALUE 'new_value'"
353
+ # end
354
+ # end
355
+ #
356
+ # Remember that you can still open your own transactions, even if you
357
+ # are in a Migration with <tt>self.disable_ddl_transaction!</tt>.
358
+ class Migration
359
+ autoload :CommandRecorder, 'active_record/migration/command_recorder'
360
+
361
+
362
+ # This class is used to verify that all migrations have been run before
363
+ # loading a web page if config.active_record.migration_error is set to :page_load
364
+ class CheckPending
365
+ def initialize(app)
366
+ @app = app
367
+ @last_check = 0
368
+ end
369
+
370
+ def call(env)
371
+ if connection.supports_migrations?
372
+ mtime = ActiveRecord::Migrator.last_migration.mtime.to_i
373
+ if @last_check < mtime
374
+ ActiveRecord::Migration.check_pending!(connection)
375
+ @last_check = mtime
376
+ end
377
+ end
378
+ @app.call(env)
379
+ end
380
+
381
+ private
382
+
383
+ def connection
384
+ ActiveRecord::Base.connection
385
+ end
386
+ end
387
+
388
+ class << self
389
+ attr_accessor :delegate # :nodoc:
390
+ attr_accessor :disable_ddl_transaction # :nodoc:
391
+
392
+ def check_pending!(connection = Base.connection)
393
+ raise ActiveRecord::PendingMigrationError if ActiveRecord::Migrator.needs_migration?(connection)
394
+ end
395
+
396
+ def load_schema_if_pending!
397
+ if ActiveRecord::Migrator.needs_migration? || !ActiveRecord::Migrator.any_migrations?
398
+ # Roundrip to Rake to allow plugins to hook into database initialization.
399
+ FileUtils.cd Rails.root do
400
+ current_config = Base.connection_config
401
+ Base.clear_all_connections!
402
+ system("bin/rake db:test:prepare")
403
+ # Establish a new connection, the old database may be gone (db:test:prepare uses purge)
404
+ Base.establish_connection(current_config)
405
+ end
406
+ check_pending!
407
+ end
408
+ end
409
+
410
+ def maintain_test_schema! # :nodoc:
411
+ if ActiveRecord::Base.maintain_test_schema
412
+ suppress_messages { load_schema_if_pending! }
413
+ end
414
+ end
415
+
416
+ def method_missing(name, *args, &block) # :nodoc:
417
+ (delegate || superclass.delegate).send(name, *args, &block)
418
+ end
419
+
420
+ def migrate(direction)
421
+ new.migrate direction
422
+ end
423
+
424
+ # Disable DDL transactions for this migration.
425
+ def disable_ddl_transaction!
426
+ @disable_ddl_transaction = true
427
+ end
428
+ end
429
+
430
+ def disable_ddl_transaction # :nodoc:
431
+ self.class.disable_ddl_transaction
432
+ end
433
+
434
+ cattr_accessor :verbose
435
+ attr_accessor :name, :version
436
+
437
+ def initialize(name = self.class.name, version = nil)
438
+ @name = name
439
+ @version = version
440
+ @connection = nil
441
+ end
442
+
443
+ self.verbose = true
444
+ # instantiate the delegate object after initialize is defined
445
+ self.delegate = new
446
+
447
+ # Reverses the migration commands for the given block and
448
+ # the given migrations.
449
+ #
450
+ # The following migration will remove the table 'horses'
451
+ # and create the table 'apples' on the way up, and the reverse
452
+ # on the way down.
453
+ #
454
+ # class FixTLMigration < ActiveRecord::Migration
455
+ # def change
456
+ # revert do
457
+ # create_table(:horses) do |t|
458
+ # t.text :content
459
+ # t.datetime :remind_at
460
+ # end
461
+ # end
462
+ # create_table(:apples) do |t|
463
+ # t.string :variety
464
+ # end
465
+ # end
466
+ # end
467
+ #
468
+ # Or equivalently, if +TenderloveMigration+ is defined as in the
469
+ # documentation for Migration:
470
+ #
471
+ # require_relative '2012121212_tenderlove_migration'
472
+ #
473
+ # class FixupTLMigration < ActiveRecord::Migration
474
+ # def change
475
+ # revert TenderloveMigration
476
+ #
477
+ # create_table(:apples) do |t|
478
+ # t.string :variety
479
+ # end
480
+ # end
481
+ # end
482
+ #
483
+ # This command can be nested.
484
+ def revert(*migration_classes)
485
+ run(*migration_classes.reverse, revert: true) unless migration_classes.empty?
486
+ if block_given?
487
+ if @connection.respond_to? :revert
488
+ @connection.revert { yield }
489
+ else
490
+ recorder = CommandRecorder.new(@connection)
491
+ @connection = recorder
492
+ suppress_messages do
493
+ @connection.revert { yield }
494
+ end
495
+ @connection = recorder.delegate
496
+ recorder.commands.each do |cmd, args, block|
497
+ send(cmd, *args, &block)
498
+ end
499
+ end
500
+ end
501
+ end
502
+
503
+ def reverting?
504
+ @connection.respond_to?(:reverting) && @connection.reverting
505
+ end
506
+
507
+ class ReversibleBlockHelper < Struct.new(:reverting) # :nodoc:
508
+ def up
509
+ yield unless reverting
510
+ end
511
+
512
+ def down
513
+ yield if reverting
514
+ end
515
+ end
516
+
517
+ # Used to specify an operation that can be run in one direction or another.
518
+ # Call the methods +up+ and +down+ of the yielded object to run a block
519
+ # only in one given direction.
520
+ # The whole block will be called in the right order within the migration.
521
+ #
522
+ # In the following example, the looping on users will always be done
523
+ # when the three columns 'first_name', 'last_name' and 'full_name' exist,
524
+ # even when migrating down:
525
+ #
526
+ # class SplitNameMigration < ActiveRecord::Migration
527
+ # def change
528
+ # add_column :users, :first_name, :string
529
+ # add_column :users, :last_name, :string
530
+ #
531
+ # reversible do |dir|
532
+ # User.reset_column_information
533
+ # User.all.each do |u|
534
+ # dir.up { u.first_name, u.last_name = u.full_name.split(' ') }
535
+ # dir.down { u.full_name = "#{u.first_name} #{u.last_name}" }
536
+ # u.save
537
+ # end
538
+ # end
539
+ #
540
+ # revert { add_column :users, :full_name, :string }
541
+ # end
542
+ # end
543
+ def reversible
544
+ helper = ReversibleBlockHelper.new(reverting?)
545
+ execute_block{ yield helper }
546
+ end
547
+
548
+ # Runs the given migration classes.
549
+ # Last argument can specify options:
550
+ # - :direction (default is :up)
551
+ # - :revert (default is false)
552
+ def run(*migration_classes)
553
+ opts = migration_classes.extract_options!
554
+ dir = opts[:direction] || :up
555
+ dir = (dir == :down ? :up : :down) if opts[:revert]
556
+ if reverting?
557
+ # If in revert and going :up, say, we want to execute :down without reverting, so
558
+ revert { run(*migration_classes, direction: dir, revert: true) }
559
+ else
560
+ migration_classes.each do |migration_class|
561
+ migration_class.new.exec_migration(@connection, dir)
562
+ end
563
+ end
564
+ end
565
+
566
+ def up
567
+ self.class.delegate = self
568
+ return unless self.class.respond_to?(:up)
569
+ self.class.up
570
+ end
571
+
572
+ def down
573
+ self.class.delegate = self
574
+ return unless self.class.respond_to?(:down)
575
+ self.class.down
576
+ end
577
+
578
+ # Execute this migration in the named direction
579
+ def migrate(direction)
580
+ return unless respond_to?(direction)
581
+
582
+ case direction
583
+ when :up then announce "migrating"
584
+ when :down then announce "reverting"
585
+ end
586
+
587
+ time = nil
588
+ ActiveRecord::Base.connection_pool.with_connection do |conn|
589
+ time = Benchmark.measure do
590
+ exec_migration(conn, direction)
591
+ end
592
+ end
593
+
594
+ case direction
595
+ when :up then announce "migrated (%.4fs)" % time.real; write
596
+ when :down then announce "reverted (%.4fs)" % time.real; write
597
+ end
598
+ end
599
+
600
+ def exec_migration(conn, direction)
601
+ @connection = conn
602
+ if respond_to?(:change)
603
+ if direction == :down
604
+ revert { change }
605
+ else
606
+ change
607
+ end
608
+ else
609
+ send(direction)
610
+ end
611
+ ensure
612
+ @connection = nil
613
+ end
614
+
615
+ def write(text="")
616
+ puts(text) if verbose
617
+ end
618
+
619
+ def announce(message)
620
+ text = "#{version} #{name}: #{message}"
621
+ length = [0, 75 - text.length].max
622
+ write "== %s %s" % [text, "=" * length]
623
+ end
624
+
625
+ def say(message, subitem=false)
626
+ write "#{subitem ? " ->" : "--"} #{message}"
627
+ end
628
+
629
+ def say_with_time(message)
630
+ say(message)
631
+ result = nil
632
+ time = Benchmark.measure { result = yield }
633
+ say "%.4fs" % time.real, :subitem
634
+ say("#{result} rows", :subitem) if result.is_a?(Integer)
635
+ result
636
+ end
637
+
638
+ def suppress_messages
639
+ save, self.verbose = verbose, false
640
+ yield
641
+ ensure
642
+ self.verbose = save
643
+ end
644
+
645
+ def connection
646
+ @connection || ActiveRecord::Base.connection
647
+ end
648
+
649
+ def method_missing(method, *arguments, &block)
650
+ arg_list = arguments.map{ |a| a.inspect } * ', '
651
+
652
+ say_with_time "#{method}(#{arg_list})" do
653
+ unless @connection.respond_to? :revert
654
+ unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
655
+ arguments[0] = proper_table_name(arguments.first, table_name_options)
656
+ if [:rename_table, :add_foreign_key].include?(method)
657
+ arguments[1] = proper_table_name(arguments.second, table_name_options)
658
+ end
659
+ end
660
+ end
661
+ return super unless connection.respond_to?(method)
662
+ connection.send(method, *arguments, &block)
663
+ end
664
+ end
665
+
666
+ def copy(destination, sources, options = {})
667
+ copied = []
668
+
669
+ FileUtils.mkdir_p(destination) unless File.exist?(destination)
670
+
671
+ destination_migrations = ActiveRecord::Migrator.migrations(destination)
672
+ last = destination_migrations.last
673
+ sources.each do |scope, path|
674
+ source_migrations = ActiveRecord::Migrator.migrations(path)
675
+
676
+ source_migrations.each do |migration|
677
+ source = File.binread(migration.filename)
678
+ inserted_comment = "# This migration comes from #{scope} (originally #{migration.version})\n"
679
+ if /\A#.*\b(?:en)?coding:\s*\S+/ =~ source
680
+ # If we have a magic comment in the original migration,
681
+ # insert our comment after the first newline(end of the magic comment line)
682
+ # so the magic keep working.
683
+ # Note that magic comments must be at the first line(except sh-bang).
684
+ source[/\n/] = "\n#{inserted_comment}"
685
+ else
686
+ source = "#{inserted_comment}#{source}"
687
+ end
688
+
689
+ if duplicate = destination_migrations.detect { |m| m.name == migration.name }
690
+ if options[:on_skip] && duplicate.scope != scope.to_s
691
+ options[:on_skip].call(scope, migration)
692
+ end
693
+ next
694
+ end
695
+
696
+ migration.version = next_migration_number(last ? last.version + 1 : 0).to_i
697
+ new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.#{scope}.rb")
698
+ old_path, migration.filename = migration.filename, new_path
699
+ last = migration
700
+
701
+ File.binwrite(migration.filename, source)
702
+ copied << migration
703
+ options[:on_copy].call(scope, migration, old_path) if options[:on_copy]
704
+ destination_migrations << migration
705
+ end
706
+ end
707
+
708
+ copied
709
+ end
710
+
711
+ # Finds the correct table name given an Active Record object.
712
+ # Uses the Active Record object's own table_name, or pre/suffix from the
713
+ # options passed in.
714
+ def proper_table_name(name, options = {})
715
+ if name.respond_to? :table_name
716
+ name.table_name
717
+ else
718
+ "#{options[:table_name_prefix]}#{name}#{options[:table_name_suffix]}"
719
+ end
720
+ end
721
+
722
+ # Determines the version number of the next migration.
723
+ def next_migration_number(number)
724
+ if ActiveRecord::Base.timestamped_migrations
725
+ [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % number].max
726
+ else
727
+ SchemaMigration.normalize_migration_number(number)
728
+ end
729
+ end
730
+
731
+ def table_name_options(config = ActiveRecord::Base)
732
+ {
733
+ table_name_prefix: config.table_name_prefix,
734
+ table_name_suffix: config.table_name_suffix
735
+ }
736
+ end
737
+
738
+ private
739
+ def execute_block
740
+ if connection.respond_to? :execute_block
741
+ super # use normal delegation to record the block
742
+ else
743
+ yield
744
+ end
745
+ end
746
+ end
747
+
748
+ # MigrationProxy is used to defer loading of the actual migration classes
749
+ # until they are needed
750
+ class MigrationProxy < Struct.new(:name, :version, :filename, :scope)
751
+
752
+ def initialize(name, version, filename, scope)
753
+ super
754
+ @migration = nil
755
+ end
756
+
757
+ def basename
758
+ File.basename(filename)
759
+ end
760
+
761
+ def mtime
762
+ File.mtime filename
763
+ end
764
+
765
+ delegate :migrate, :announce, :write, :disable_ddl_transaction, to: :migration
766
+
767
+ private
768
+
769
+ def migration
770
+ @migration ||= load_migration
771
+ end
772
+
773
+ def load_migration
774
+ require(File.expand_path(filename))
775
+ name.constantize.new(name, version)
776
+ end
777
+
778
+ end
779
+
780
+ class NullMigration < MigrationProxy #:nodoc:
781
+ def initialize
782
+ super(nil, 0, nil, nil)
783
+ end
784
+
785
+ def mtime
786
+ 0
787
+ end
788
+ end
789
+
790
+ class Migrator#:nodoc:
791
+ class << self
792
+ attr_writer :migrations_paths
793
+ alias :migrations_path= :migrations_paths=
794
+
795
+ def migrate(migrations_paths, target_version = nil, &block)
796
+ case
797
+ when target_version.nil?
798
+ up(migrations_paths, target_version, &block)
799
+ when current_version == 0 && target_version == 0
800
+ []
801
+ when current_version > target_version
802
+ down(migrations_paths, target_version, &block)
803
+ else
804
+ up(migrations_paths, target_version, &block)
805
+ end
806
+ end
807
+
808
+ def rollback(migrations_paths, steps=1)
809
+ move(:down, migrations_paths, steps)
810
+ end
811
+
812
+ def forward(migrations_paths, steps=1)
813
+ move(:up, migrations_paths, steps)
814
+ end
815
+
816
+ def up(migrations_paths, target_version = nil)
817
+ migrations = migrations(migrations_paths)
818
+ migrations.select! { |m| yield m } if block_given?
819
+
820
+ new(:up, migrations, target_version).migrate
821
+ end
822
+
823
+ def down(migrations_paths, target_version = nil, &block)
824
+ migrations = migrations(migrations_paths)
825
+ migrations.select! { |m| yield m } if block_given?
826
+
827
+ new(:down, migrations, target_version).migrate
828
+ end
829
+
830
+ def run(direction, migrations_paths, target_version)
831
+ new(direction, migrations(migrations_paths), target_version).run
832
+ end
833
+
834
+ def open(migrations_paths)
835
+ new(:up, migrations(migrations_paths), nil)
836
+ end
837
+
838
+ def schema_migrations_table_name
839
+ SchemaMigration.table_name
840
+ end
841
+
842
+ def get_all_versions(connection = Base.connection)
843
+ if connection.table_exists?(schema_migrations_table_name)
844
+ SchemaMigration.all.map { |x| x.version.to_i }.sort
845
+ else
846
+ []
847
+ end
848
+ end
849
+
850
+ def current_version(connection = Base.connection)
851
+ get_all_versions(connection).max || 0
852
+ end
853
+
854
+ def needs_migration?(connection = Base.connection)
855
+ (migrations(migrations_paths).collect(&:version) - get_all_versions(connection)).size > 0
856
+ end
857
+
858
+ def any_migrations?
859
+ migrations(migrations_paths).any?
860
+ end
861
+
862
+ def last_version
863
+ last_migration.version
864
+ end
865
+
866
+ def last_migration #:nodoc:
867
+ migrations(migrations_paths).last || NullMigration.new
868
+ end
869
+
870
+ def migrations_paths
871
+ @migrations_paths ||= ['db/migrate']
872
+ # just to not break things if someone uses: migration_path = some_string
873
+ Array(@migrations_paths)
874
+ end
875
+
876
+ def migrations_path
877
+ migrations_paths.first
878
+ end
879
+
880
+ def migrations(paths)
881
+ paths = Array(paths)
882
+
883
+ files = Dir[*paths.map { |p| "#{p}/**/[0-9]*_*.rb" }]
884
+
885
+ migrations = files.map do |file|
886
+ version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
887
+
888
+ raise IllegalMigrationNameError.new(file) unless version
889
+ version = version.to_i
890
+ name = name.camelize
891
+
892
+ MigrationProxy.new(name, version, file, scope)
893
+ end
894
+
895
+ migrations.sort_by(&:version)
896
+ end
897
+
898
+ private
899
+
900
+ def move(direction, migrations_paths, steps)
901
+ migrator = new(direction, migrations(migrations_paths))
902
+ start_index = migrator.migrations.index(migrator.current_migration)
903
+
904
+ if start_index
905
+ finish = migrator.migrations[start_index + steps]
906
+ version = finish ? finish.version : 0
907
+ send(direction, migrations_paths, version)
908
+ end
909
+ end
910
+ end
911
+
912
+ def initialize(direction, migrations, target_version = nil)
913
+ raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?
914
+
915
+ @direction = direction
916
+ @target_version = target_version
917
+ @migrated_versions = nil
918
+ @migrations = migrations
919
+
920
+ validate(@migrations)
921
+
922
+ Base.connection.initialize_schema_migrations_table
923
+ end
924
+
925
+ def current_version
926
+ migrated.max || 0
927
+ end
928
+
929
+ def current_migration
930
+ migrations.detect { |m| m.version == current_version }
931
+ end
932
+ alias :current :current_migration
933
+
934
+ def run
935
+ migration = migrations.detect { |m| m.version == @target_version }
936
+ raise UnknownMigrationVersionError.new(@target_version) if migration.nil?
937
+ unless (up? && migrated.include?(migration.version.to_i)) || (down? && !migrated.include?(migration.version.to_i))
938
+ begin
939
+ execute_migration_in_transaction(migration, @direction)
940
+ rescue => e
941
+ canceled_msg = use_transaction?(migration) ? ", this migration was canceled" : ""
942
+ raise StandardError, "An error has occurred#{canceled_msg}:\n\n#{e}", e.backtrace
943
+ end
944
+ end
945
+ end
946
+
947
+ def migrate
948
+ if !target && @target_version && @target_version > 0
949
+ raise UnknownMigrationVersionError.new(@target_version)
950
+ end
951
+
952
+ runnable.each do |migration|
953
+ Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
954
+
955
+ begin
956
+ execute_migration_in_transaction(migration, @direction)
957
+ rescue => e
958
+ canceled_msg = use_transaction?(migration) ? "this and " : ""
959
+ raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace
960
+ end
961
+ end
962
+ end
963
+
964
+ def runnable
965
+ runnable = migrations[start..finish]
966
+ if up?
967
+ runnable.reject { |m| ran?(m) }
968
+ else
969
+ # skip the last migration if we're headed down, but not ALL the way down
970
+ runnable.pop if target
971
+ runnable.find_all { |m| ran?(m) }
972
+ end
973
+ end
974
+
975
+ def migrations
976
+ down? ? @migrations.reverse : @migrations.sort_by(&:version)
977
+ end
978
+
979
+ def pending_migrations
980
+ already_migrated = migrated
981
+ migrations.reject { |m| already_migrated.include?(m.version) }
982
+ end
983
+
984
+ def migrated
985
+ @migrated_versions ||= Set.new(self.class.get_all_versions)
986
+ end
987
+
988
+ private
989
+ def ran?(migration)
990
+ migrated.include?(migration.version.to_i)
991
+ end
992
+
993
+ def execute_migration_in_transaction(migration, direction)
994
+ ddl_transaction(migration) do
995
+ migration.migrate(direction)
996
+ record_version_state_after_migrating(migration.version)
997
+ end
998
+ end
999
+
1000
+ def target
1001
+ migrations.detect { |m| m.version == @target_version }
1002
+ end
1003
+
1004
+ def finish
1005
+ migrations.index(target) || migrations.size - 1
1006
+ end
1007
+
1008
+ def start
1009
+ up? ? 0 : (migrations.index(current) || 0)
1010
+ end
1011
+
1012
+ def validate(migrations)
1013
+ name ,= migrations.group_by(&:name).find { |_,v| v.length > 1 }
1014
+ raise DuplicateMigrationNameError.new(name) if name
1015
+
1016
+ version ,= migrations.group_by(&:version).find { |_,v| v.length > 1 }
1017
+ raise DuplicateMigrationVersionError.new(version) if version
1018
+ end
1019
+
1020
+ def record_version_state_after_migrating(version)
1021
+ if down?
1022
+ migrated.delete(version)
1023
+ ActiveRecord::SchemaMigration.where(:version => version.to_s).delete_all
1024
+ else
1025
+ migrated << version
1026
+ ActiveRecord::SchemaMigration.create!(:version => version.to_s)
1027
+ end
1028
+ end
1029
+
1030
+ def up?
1031
+ @direction == :up
1032
+ end
1033
+
1034
+ def down?
1035
+ @direction == :down
1036
+ end
1037
+
1038
+ # Wrap the migration in a transaction only if supported by the adapter.
1039
+ def ddl_transaction(migration)
1040
+ if use_transaction?(migration)
1041
+ Base.transaction { yield }
1042
+ else
1043
+ yield
1044
+ end
1045
+ end
1046
+
1047
+ def use_transaction?(migration)
1048
+ !migration.disable_ddl_transaction && Base.connection.supports_ddl_transactions?
1049
+ end
1050
+ end
1051
+ end