activerecord 2.3.18 → 3.2.22

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 (454) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1014 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +222 -0
  5. data/examples/performance.rb +100 -126
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record/aggregations.rb +93 -99
  8. data/lib/active_record/associations/alias_tracker.rb +76 -0
  9. data/lib/active_record/associations/association.rb +247 -0
  10. data/lib/active_record/associations/association_scope.rb +134 -0
  11. data/lib/active_record/associations/belongs_to_association.rb +54 -61
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +17 -59
  13. data/lib/active_record/associations/builder/association.rb +55 -0
  14. data/lib/active_record/associations/builder/belongs_to.rb +88 -0
  15. data/lib/active_record/associations/builder/collection_association.rb +75 -0
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +57 -0
  17. data/lib/active_record/associations/builder/has_many.rb +71 -0
  18. data/lib/active_record/associations/builder/has_one.rb +62 -0
  19. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  20. data/lib/active_record/associations/collection_association.rb +580 -0
  21. data/lib/active_record/associations/collection_proxy.rb +133 -0
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +39 -119
  23. data/lib/active_record/associations/has_many_association.rb +60 -79
  24. data/lib/active_record/associations/has_many_through_association.rb +127 -206
  25. data/lib/active_record/associations/has_one_association.rb +55 -114
  26. data/lib/active_record/associations/has_one_through_association.rb +25 -26
  27. data/lib/active_record/associations/join_dependency/join_association.rb +159 -0
  28. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  29. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  30. data/lib/active_record/associations/join_dependency.rb +214 -0
  31. data/lib/active_record/associations/join_helper.rb +55 -0
  32. data/lib/active_record/associations/preloader/association.rb +125 -0
  33. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  34. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  35. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  36. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  37. data/lib/active_record/associations/preloader/has_many_through.rb +15 -0
  38. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  39. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  40. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  41. data/lib/active_record/associations/preloader/through_association.rb +67 -0
  42. data/lib/active_record/associations/preloader.rb +181 -0
  43. data/lib/active_record/associations/singular_association.rb +64 -0
  44. data/lib/active_record/associations/through_association.rb +87 -0
  45. data/lib/active_record/associations.rb +693 -1337
  46. data/lib/active_record/attribute_assignment.rb +221 -0
  47. data/lib/active_record/attribute_methods/before_type_cast.rb +31 -0
  48. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
  49. data/lib/active_record/attribute_methods/dirty.rb +111 -0
  50. data/lib/active_record/attribute_methods/primary_key.rb +114 -0
  51. data/lib/active_record/attribute_methods/query.rb +39 -0
  52. data/lib/active_record/attribute_methods/read.rb +136 -0
  53. data/lib/active_record/attribute_methods/serialization.rb +120 -0
  54. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -0
  55. data/lib/active_record/attribute_methods/write.rb +70 -0
  56. data/lib/active_record/attribute_methods.rb +211 -339
  57. data/lib/active_record/autosave_association.rb +179 -149
  58. data/lib/active_record/base.rb +401 -2907
  59. data/lib/active_record/callbacks.rb +91 -176
  60. data/lib/active_record/coders/yaml_column.rb +41 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +236 -119
  62. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +110 -58
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +12 -11
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +175 -74
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -35
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +71 -21
  67. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +81 -311
  68. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +194 -78
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +130 -83
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +676 -0
  71. data/lib/active_record/connection_adapters/column.rb +296 -0
  72. data/lib/active_record/connection_adapters/mysql2_adapter.rb +280 -0
  73. data/lib/active_record/connection_adapters/mysql_adapter.rb +272 -493
  74. data/lib/active_record/connection_adapters/postgresql_adapter.rb +650 -405
  75. data/lib/active_record/connection_adapters/schema_cache.rb +69 -0
  76. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +30 -9
  77. data/lib/active_record/connection_adapters/sqlite_adapter.rb +276 -147
  78. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  79. data/lib/active_record/counter_cache.rb +123 -0
  80. data/lib/active_record/dynamic_finder_match.rb +41 -14
  81. data/lib/active_record/dynamic_matchers.rb +84 -0
  82. data/lib/active_record/dynamic_scope_match.rb +13 -15
  83. data/lib/active_record/errors.rb +195 -0
  84. data/lib/active_record/explain.rb +86 -0
  85. data/lib/active_record/explain_subscriber.rb +25 -0
  86. data/lib/active_record/fixtures/file.rb +65 -0
  87. data/lib/active_record/fixtures.rb +695 -770
  88. data/lib/active_record/identity_map.rb +162 -0
  89. data/lib/active_record/inheritance.rb +174 -0
  90. data/lib/active_record/integration.rb +60 -0
  91. data/lib/active_record/locale/en.yml +9 -27
  92. data/lib/active_record/locking/optimistic.rb +76 -73
  93. data/lib/active_record/locking/pessimistic.rb +32 -10
  94. data/lib/active_record/log_subscriber.rb +72 -0
  95. data/lib/active_record/migration/command_recorder.rb +105 -0
  96. data/lib/active_record/migration.rb +415 -205
  97. data/lib/active_record/model_schema.rb +368 -0
  98. data/lib/active_record/nested_attributes.rb +153 -63
  99. data/lib/active_record/observer.rb +27 -103
  100. data/lib/active_record/persistence.rb +376 -0
  101. data/lib/active_record/query_cache.rb +49 -8
  102. data/lib/active_record/querying.rb +58 -0
  103. data/lib/active_record/railtie.rb +131 -0
  104. data/lib/active_record/railties/console_sandbox.rb +6 -0
  105. data/lib/active_record/railties/controller_runtime.rb +49 -0
  106. data/lib/active_record/railties/databases.rake +659 -0
  107. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  108. data/lib/active_record/readonly_attributes.rb +26 -0
  109. data/lib/active_record/reflection.rb +269 -120
  110. data/lib/active_record/relation/batches.rb +90 -0
  111. data/lib/active_record/relation/calculations.rb +372 -0
  112. data/lib/active_record/relation/delegation.rb +49 -0
  113. data/lib/active_record/relation/finder_methods.rb +402 -0
  114. data/lib/active_record/relation/predicate_builder.rb +63 -0
  115. data/lib/active_record/relation/query_methods.rb +417 -0
  116. data/lib/active_record/relation/spawn_methods.rb +180 -0
  117. data/lib/active_record/relation.rb +537 -0
  118. data/lib/active_record/result.rb +40 -0
  119. data/lib/active_record/sanitization.rb +194 -0
  120. data/lib/active_record/schema.rb +9 -6
  121. data/lib/active_record/schema_dumper.rb +55 -32
  122. data/lib/active_record/scoping/default.rb +142 -0
  123. data/lib/active_record/scoping/named.rb +200 -0
  124. data/lib/active_record/scoping.rb +152 -0
  125. data/lib/active_record/serialization.rb +8 -91
  126. data/lib/active_record/serializers/xml_serializer.rb +43 -197
  127. data/lib/active_record/session_store.rb +129 -103
  128. data/lib/active_record/store.rb +52 -0
  129. data/lib/active_record/test_case.rb +30 -23
  130. data/lib/active_record/timestamp.rb +95 -52
  131. data/lib/active_record/transactions.rb +212 -66
  132. data/lib/active_record/translation.rb +22 -0
  133. data/lib/active_record/validations/associated.rb +43 -0
  134. data/lib/active_record/validations/uniqueness.rb +180 -0
  135. data/lib/active_record/validations.rb +43 -1106
  136. data/lib/active_record/version.rb +5 -4
  137. data/lib/active_record.rb +121 -48
  138. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  139. data/lib/rails/generators/active_record/migration/templates/migration.rb +34 -0
  140. data/lib/rails/generators/active_record/migration.rb +15 -0
  141. data/lib/rails/generators/active_record/model/model_generator.rb +47 -0
  142. data/lib/rails/generators/active_record/model/templates/migration.rb +15 -0
  143. data/lib/rails/generators/active_record/model/templates/model.rb +12 -0
  144. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  145. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  146. data/lib/rails/generators/active_record/observer/templates/observer.rb +4 -0
  147. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +25 -0
  148. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +12 -0
  149. data/lib/rails/generators/active_record.rb +25 -0
  150. metadata +187 -363
  151. data/CHANGELOG +0 -5904
  152. data/README +0 -351
  153. data/RUNNING_UNIT_TESTS +0 -36
  154. data/Rakefile +0 -268
  155. data/install.rb +0 -30
  156. data/lib/active_record/association_preload.rb +0 -406
  157. data/lib/active_record/associations/association_collection.rb +0 -533
  158. data/lib/active_record/associations/association_proxy.rb +0 -288
  159. data/lib/active_record/batches.rb +0 -85
  160. data/lib/active_record/calculations.rb +0 -321
  161. data/lib/active_record/dirty.rb +0 -183
  162. data/lib/active_record/named_scope.rb +0 -197
  163. data/lib/active_record/serializers/json_serializer.rb +0 -91
  164. data/lib/activerecord.rb +0 -2
  165. data/test/assets/example.log +0 -1
  166. data/test/assets/flowers.jpg +0 -0
  167. data/test/cases/aaa_create_tables_test.rb +0 -24
  168. data/test/cases/active_schema_test_mysql.rb +0 -122
  169. data/test/cases/active_schema_test_postgresql.rb +0 -24
  170. data/test/cases/adapter_test.rb +0 -144
  171. data/test/cases/aggregations_test.rb +0 -167
  172. data/test/cases/ar_schema_test.rb +0 -32
  173. data/test/cases/associations/belongs_to_associations_test.rb +0 -438
  174. data/test/cases/associations/callbacks_test.rb +0 -161
  175. data/test/cases/associations/cascaded_eager_loading_test.rb +0 -131
  176. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +0 -36
  177. data/test/cases/associations/eager_load_nested_include_test.rb +0 -131
  178. data/test/cases/associations/eager_load_nested_polymorphic_include.rb +0 -19
  179. data/test/cases/associations/eager_singularization_test.rb +0 -145
  180. data/test/cases/associations/eager_test.rb +0 -852
  181. data/test/cases/associations/extension_test.rb +0 -62
  182. data/test/cases/associations/habtm_join_table_test.rb +0 -56
  183. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +0 -827
  184. data/test/cases/associations/has_many_associations_test.rb +0 -1273
  185. data/test/cases/associations/has_many_through_associations_test.rb +0 -360
  186. data/test/cases/associations/has_one_associations_test.rb +0 -330
  187. data/test/cases/associations/has_one_through_associations_test.rb +0 -209
  188. data/test/cases/associations/inner_join_association_test.rb +0 -93
  189. data/test/cases/associations/inverse_associations_test.rb +0 -566
  190. data/test/cases/associations/join_model_test.rb +0 -712
  191. data/test/cases/associations_test.rb +0 -282
  192. data/test/cases/attribute_methods_test.rb +0 -305
  193. data/test/cases/autosave_association_test.rb +0 -1218
  194. data/test/cases/base_test.rb +0 -2166
  195. data/test/cases/batches_test.rb +0 -81
  196. data/test/cases/binary_test.rb +0 -30
  197. data/test/cases/calculations_test.rb +0 -360
  198. data/test/cases/callbacks_observers_test.rb +0 -38
  199. data/test/cases/callbacks_test.rb +0 -438
  200. data/test/cases/class_inheritable_attributes_test.rb +0 -32
  201. data/test/cases/column_alias_test.rb +0 -17
  202. data/test/cases/column_definition_test.rb +0 -70
  203. data/test/cases/connection_pool_test.rb +0 -25
  204. data/test/cases/connection_test_firebird.rb +0 -8
  205. data/test/cases/connection_test_mysql.rb +0 -65
  206. data/test/cases/copy_table_test_sqlite.rb +0 -80
  207. data/test/cases/counter_cache_test.rb +0 -84
  208. data/test/cases/database_statements_test.rb +0 -12
  209. data/test/cases/datatype_test_postgresql.rb +0 -204
  210. data/test/cases/date_time_test.rb +0 -37
  211. data/test/cases/default_test_firebird.rb +0 -16
  212. data/test/cases/defaults_test.rb +0 -111
  213. data/test/cases/deprecated_finder_test.rb +0 -30
  214. data/test/cases/dirty_test.rb +0 -316
  215. data/test/cases/finder_respond_to_test.rb +0 -76
  216. data/test/cases/finder_test.rb +0 -1098
  217. data/test/cases/fixtures_test.rb +0 -661
  218. data/test/cases/helper.rb +0 -68
  219. data/test/cases/i18n_test.rb +0 -46
  220. data/test/cases/inheritance_test.rb +0 -262
  221. data/test/cases/invalid_date_test.rb +0 -24
  222. data/test/cases/json_serialization_test.rb +0 -219
  223. data/test/cases/lifecycle_test.rb +0 -193
  224. data/test/cases/locking_test.rb +0 -350
  225. data/test/cases/method_scoping_test.rb +0 -704
  226. data/test/cases/migration_test.rb +0 -1649
  227. data/test/cases/migration_test_firebird.rb +0 -124
  228. data/test/cases/mixin_test.rb +0 -96
  229. data/test/cases/modules_test.rb +0 -109
  230. data/test/cases/multiple_db_test.rb +0 -85
  231. data/test/cases/named_scope_test.rb +0 -372
  232. data/test/cases/nested_attributes_test.rb +0 -840
  233. data/test/cases/pk_test.rb +0 -119
  234. data/test/cases/pooled_connections_test.rb +0 -103
  235. data/test/cases/query_cache_test.rb +0 -129
  236. data/test/cases/readonly_test.rb +0 -107
  237. data/test/cases/reflection_test.rb +0 -234
  238. data/test/cases/reload_models_test.rb +0 -22
  239. data/test/cases/repair_helper.rb +0 -50
  240. data/test/cases/reserved_word_test_mysql.rb +0 -176
  241. data/test/cases/sanitize_test.rb +0 -25
  242. data/test/cases/schema_authorization_test_postgresql.rb +0 -75
  243. data/test/cases/schema_dumper_test.rb +0 -211
  244. data/test/cases/schema_test_postgresql.rb +0 -178
  245. data/test/cases/serialization_test.rb +0 -47
  246. data/test/cases/sp_test_mysql.rb +0 -16
  247. data/test/cases/synonym_test_oracle.rb +0 -17
  248. data/test/cases/timestamp_test.rb +0 -75
  249. data/test/cases/transactions_test.rb +0 -543
  250. data/test/cases/unconnected_test.rb +0 -32
  251. data/test/cases/validations_i18n_test.rb +0 -925
  252. data/test/cases/validations_test.rb +0 -1684
  253. data/test/cases/xml_serialization_test.rb +0 -240
  254. data/test/cases/yaml_serialization_test.rb +0 -11
  255. data/test/config.rb +0 -5
  256. data/test/connections/jdbc_jdbcderby/connection.rb +0 -18
  257. data/test/connections/jdbc_jdbch2/connection.rb +0 -18
  258. data/test/connections/jdbc_jdbchsqldb/connection.rb +0 -18
  259. data/test/connections/jdbc_jdbcmysql/connection.rb +0 -26
  260. data/test/connections/jdbc_jdbcpostgresql/connection.rb +0 -26
  261. data/test/connections/jdbc_jdbcsqlite3/connection.rb +0 -25
  262. data/test/connections/native_db2/connection.rb +0 -25
  263. data/test/connections/native_firebird/connection.rb +0 -26
  264. data/test/connections/native_frontbase/connection.rb +0 -27
  265. data/test/connections/native_mysql/connection.rb +0 -25
  266. data/test/connections/native_openbase/connection.rb +0 -21
  267. data/test/connections/native_oracle/connection.rb +0 -27
  268. data/test/connections/native_postgresql/connection.rb +0 -21
  269. data/test/connections/native_sqlite/connection.rb +0 -25
  270. data/test/connections/native_sqlite3/connection.rb +0 -25
  271. data/test/connections/native_sqlite3/in_memory_connection.rb +0 -18
  272. data/test/connections/native_sybase/connection.rb +0 -23
  273. data/test/fixtures/accounts.yml +0 -29
  274. data/test/fixtures/all/developers.yml +0 -0
  275. data/test/fixtures/all/people.csv +0 -0
  276. data/test/fixtures/all/tasks.yml +0 -0
  277. data/test/fixtures/author_addresses.yml +0 -5
  278. data/test/fixtures/author_favorites.yml +0 -4
  279. data/test/fixtures/authors.yml +0 -9
  280. data/test/fixtures/binaries.yml +0 -132
  281. data/test/fixtures/books.yml +0 -7
  282. data/test/fixtures/categories/special_categories.yml +0 -9
  283. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +0 -4
  284. data/test/fixtures/categories.yml +0 -14
  285. data/test/fixtures/categories_ordered.yml +0 -7
  286. data/test/fixtures/categories_posts.yml +0 -23
  287. data/test/fixtures/categorizations.yml +0 -17
  288. data/test/fixtures/clubs.yml +0 -6
  289. data/test/fixtures/comments.yml +0 -59
  290. data/test/fixtures/companies.yml +0 -56
  291. data/test/fixtures/computers.yml +0 -4
  292. data/test/fixtures/courses.yml +0 -7
  293. data/test/fixtures/customers.yml +0 -26
  294. data/test/fixtures/developers.yml +0 -21
  295. data/test/fixtures/developers_projects.yml +0 -17
  296. data/test/fixtures/edges.yml +0 -6
  297. data/test/fixtures/entrants.yml +0 -14
  298. data/test/fixtures/faces.yml +0 -11
  299. data/test/fixtures/fk_test_has_fk.yml +0 -3
  300. data/test/fixtures/fk_test_has_pk.yml +0 -2
  301. data/test/fixtures/funny_jokes.yml +0 -10
  302. data/test/fixtures/interests.yml +0 -33
  303. data/test/fixtures/items.yml +0 -4
  304. data/test/fixtures/jobs.yml +0 -7
  305. data/test/fixtures/legacy_things.yml +0 -3
  306. data/test/fixtures/mateys.yml +0 -4
  307. data/test/fixtures/member_types.yml +0 -6
  308. data/test/fixtures/members.yml +0 -6
  309. data/test/fixtures/memberships.yml +0 -20
  310. data/test/fixtures/men.yml +0 -5
  311. data/test/fixtures/minimalistics.yml +0 -2
  312. data/test/fixtures/mixed_case_monkeys.yml +0 -6
  313. data/test/fixtures/mixins.yml +0 -29
  314. data/test/fixtures/movies.yml +0 -7
  315. data/test/fixtures/naked/csv/accounts.csv +0 -1
  316. data/test/fixtures/naked/yml/accounts.yml +0 -1
  317. data/test/fixtures/naked/yml/companies.yml +0 -1
  318. data/test/fixtures/naked/yml/courses.yml +0 -1
  319. data/test/fixtures/organizations.yml +0 -5
  320. data/test/fixtures/owners.yml +0 -7
  321. data/test/fixtures/parrots.yml +0 -27
  322. data/test/fixtures/parrots_pirates.yml +0 -7
  323. data/test/fixtures/people.yml +0 -15
  324. data/test/fixtures/pets.yml +0 -14
  325. data/test/fixtures/pirates.yml +0 -9
  326. data/test/fixtures/polymorphic_designs.yml +0 -19
  327. data/test/fixtures/polymorphic_prices.yml +0 -19
  328. data/test/fixtures/posts.yml +0 -52
  329. data/test/fixtures/price_estimates.yml +0 -7
  330. data/test/fixtures/projects.yml +0 -7
  331. data/test/fixtures/readers.yml +0 -9
  332. data/test/fixtures/references.yml +0 -17
  333. data/test/fixtures/reserved_words/distinct.yml +0 -5
  334. data/test/fixtures/reserved_words/distincts_selects.yml +0 -11
  335. data/test/fixtures/reserved_words/group.yml +0 -14
  336. data/test/fixtures/reserved_words/select.yml +0 -8
  337. data/test/fixtures/reserved_words/values.yml +0 -7
  338. data/test/fixtures/ships.yml +0 -5
  339. data/test/fixtures/sponsors.yml +0 -9
  340. data/test/fixtures/subscribers.yml +0 -7
  341. data/test/fixtures/subscriptions.yml +0 -12
  342. data/test/fixtures/taggings.yml +0 -28
  343. data/test/fixtures/tags.yml +0 -7
  344. data/test/fixtures/tasks.yml +0 -7
  345. data/test/fixtures/tees.yml +0 -4
  346. data/test/fixtures/ties.yml +0 -4
  347. data/test/fixtures/topics.yml +0 -42
  348. data/test/fixtures/toys.yml +0 -4
  349. data/test/fixtures/treasures.yml +0 -10
  350. data/test/fixtures/vertices.yml +0 -4
  351. data/test/fixtures/warehouse-things.yml +0 -3
  352. data/test/fixtures/zines.yml +0 -5
  353. data/test/migrations/broken/100_migration_that_raises_exception.rb +0 -10
  354. data/test/migrations/decimal/1_give_me_big_numbers.rb +0 -15
  355. data/test/migrations/duplicate/1_people_have_last_names.rb +0 -9
  356. data/test/migrations/duplicate/2_we_need_reminders.rb +0 -12
  357. data/test/migrations/duplicate/3_foo.rb +0 -7
  358. data/test/migrations/duplicate/3_innocent_jointable.rb +0 -12
  359. data/test/migrations/duplicate_names/20080507052938_chunky.rb +0 -7
  360. data/test/migrations/duplicate_names/20080507053028_chunky.rb +0 -7
  361. data/test/migrations/interleaved/pass_1/3_innocent_jointable.rb +0 -12
  362. data/test/migrations/interleaved/pass_2/1_people_have_last_names.rb +0 -9
  363. data/test/migrations/interleaved/pass_2/3_innocent_jointable.rb +0 -12
  364. data/test/migrations/interleaved/pass_3/1_people_have_last_names.rb +0 -9
  365. data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +0 -8
  366. data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +0 -12
  367. data/test/migrations/missing/1000_people_have_middle_names.rb +0 -9
  368. data/test/migrations/missing/1_people_have_last_names.rb +0 -9
  369. data/test/migrations/missing/3_we_need_reminders.rb +0 -12
  370. data/test/migrations/missing/4_innocent_jointable.rb +0 -12
  371. data/test/migrations/valid/1_people_have_last_names.rb +0 -9
  372. data/test/migrations/valid/2_we_need_reminders.rb +0 -12
  373. data/test/migrations/valid/3_innocent_jointable.rb +0 -12
  374. data/test/models/author.rb +0 -151
  375. data/test/models/auto_id.rb +0 -4
  376. data/test/models/binary.rb +0 -2
  377. data/test/models/bird.rb +0 -9
  378. data/test/models/book.rb +0 -4
  379. data/test/models/categorization.rb +0 -5
  380. data/test/models/category.rb +0 -34
  381. data/test/models/citation.rb +0 -6
  382. data/test/models/club.rb +0 -13
  383. data/test/models/column_name.rb +0 -3
  384. data/test/models/comment.rb +0 -29
  385. data/test/models/company.rb +0 -173
  386. data/test/models/company_in_module.rb +0 -78
  387. data/test/models/computer.rb +0 -3
  388. data/test/models/contact.rb +0 -16
  389. data/test/models/contract.rb +0 -5
  390. data/test/models/course.rb +0 -3
  391. data/test/models/customer.rb +0 -73
  392. data/test/models/default.rb +0 -2
  393. data/test/models/developer.rb +0 -101
  394. data/test/models/edge.rb +0 -5
  395. data/test/models/entrant.rb +0 -3
  396. data/test/models/essay.rb +0 -3
  397. data/test/models/event.rb +0 -3
  398. data/test/models/event_author.rb +0 -8
  399. data/test/models/face.rb +0 -7
  400. data/test/models/guid.rb +0 -2
  401. data/test/models/interest.rb +0 -5
  402. data/test/models/invoice.rb +0 -4
  403. data/test/models/item.rb +0 -7
  404. data/test/models/job.rb +0 -5
  405. data/test/models/joke.rb +0 -3
  406. data/test/models/keyboard.rb +0 -3
  407. data/test/models/legacy_thing.rb +0 -3
  408. data/test/models/line_item.rb +0 -3
  409. data/test/models/man.rb +0 -9
  410. data/test/models/matey.rb +0 -4
  411. data/test/models/member.rb +0 -12
  412. data/test/models/member_detail.rb +0 -5
  413. data/test/models/member_type.rb +0 -3
  414. data/test/models/membership.rb +0 -9
  415. data/test/models/minimalistic.rb +0 -2
  416. data/test/models/mixed_case_monkey.rb +0 -3
  417. data/test/models/movie.rb +0 -5
  418. data/test/models/order.rb +0 -4
  419. data/test/models/organization.rb +0 -6
  420. data/test/models/owner.rb +0 -5
  421. data/test/models/parrot.rb +0 -22
  422. data/test/models/person.rb +0 -16
  423. data/test/models/pet.rb +0 -5
  424. data/test/models/pirate.rb +0 -80
  425. data/test/models/polymorphic_design.rb +0 -3
  426. data/test/models/polymorphic_price.rb +0 -3
  427. data/test/models/post.rb +0 -102
  428. data/test/models/price_estimate.rb +0 -3
  429. data/test/models/project.rb +0 -30
  430. data/test/models/reader.rb +0 -4
  431. data/test/models/reference.rb +0 -4
  432. data/test/models/reply.rb +0 -46
  433. data/test/models/ship.rb +0 -19
  434. data/test/models/ship_part.rb +0 -7
  435. data/test/models/sponsor.rb +0 -4
  436. data/test/models/subject.rb +0 -4
  437. data/test/models/subscriber.rb +0 -8
  438. data/test/models/subscription.rb +0 -4
  439. data/test/models/tag.rb +0 -7
  440. data/test/models/tagging.rb +0 -10
  441. data/test/models/task.rb +0 -3
  442. data/test/models/tee.rb +0 -4
  443. data/test/models/tie.rb +0 -4
  444. data/test/models/topic.rb +0 -80
  445. data/test/models/toy.rb +0 -6
  446. data/test/models/treasure.rb +0 -8
  447. data/test/models/vertex.rb +0 -9
  448. data/test/models/warehouse_thing.rb +0 -5
  449. data/test/models/zine.rb +0 -3
  450. data/test/schema/mysql_specific_schema.rb +0 -31
  451. data/test/schema/postgresql_specific_schema.rb +0 -114
  452. data/test/schema/schema.rb +0 -550
  453. data/test/schema/schema2.rb +0 -6
  454. data/test/schema/sqlite_specific_schema.rb +0 -25
@@ -1,5 +1,11 @@
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'
5
+
1
6
  module ActiveRecord
2
- class IrreversibleMigration < ActiveRecordError#:nodoc:
7
+ # Exception that can be raised to stop migrations from going backwards.
8
+ class IrreversibleMigration < ActiveRecordError
3
9
  end
4
10
 
5
11
  class DuplicateMigrationVersionError < ActiveRecordError#:nodoc:
@@ -26,111 +32,139 @@ module ActiveRecord
26
32
  end
27
33
  end
28
34
 
29
- # Migrations can manage the evolution of a schema used by several physical databases. It's a solution
30
- # to the common problem of adding a field to make a new feature work in your local database, but being unsure of how to
31
- # push that change to other developers and to the production server. With migrations, you can describe the transformations
32
- # in self-contained classes that can be checked into version control systems and executed against another database that
33
- # might be one, two, or five versions behind.
35
+ # = Active Record Migrations
36
+ #
37
+ # Migrations can manage the evolution of a schema used by several physical
38
+ # databases. It's a solution to the common problem of adding a field to make
39
+ # a new feature work in your local database, but being unsure of how to
40
+ # push that change to other developers and to the production server. With
41
+ # migrations, you can describe the transformations in self-contained classes
42
+ # that can be checked into version control systems and executed against
43
+ # another database that might be one, two, or five versions behind.
34
44
  #
35
45
  # Example of a simple migration:
36
46
  #
37
47
  # class AddSsl < ActiveRecord::Migration
38
- # def self.up
48
+ # def up
39
49
  # add_column :accounts, :ssl_enabled, :boolean, :default => 1
40
50
  # end
41
51
  #
42
- # def self.down
52
+ # def down
43
53
  # remove_column :accounts, :ssl_enabled
44
54
  # end
45
55
  # end
46
56
  #
47
- # This migration will add a boolean flag to the accounts table and remove it if you're backing out of the migration.
48
- # It shows how all migrations have two class methods +up+ and +down+ that describes the transformations required to implement
49
- # or remove the migration. These methods can consist of both the migration specific methods like add_column and remove_column,
50
- # but may also contain regular Ruby code for generating data needed for the transformations.
57
+ # This migration will add a boolean flag to the accounts table and remove it
58
+ # if you're backing out of the migration. It shows how all migrations have
59
+ # two methods +up+ and +down+ that describes the transformations
60
+ # required to implement or remove the migration. These methods can consist
61
+ # of both the migration specific methods like add_column and remove_column,
62
+ # but may also contain regular Ruby code for generating data needed for the
63
+ # transformations.
51
64
  #
52
65
  # Example of a more complex migration that also needs to initialize data:
53
66
  #
54
67
  # class AddSystemSettings < ActiveRecord::Migration
55
- # def self.up
68
+ # def up
56
69
  # create_table :system_settings do |t|
57
70
  # t.string :name
58
71
  # t.string :label
59
- # t.text :value
72
+ # t.text :value
60
73
  # t.string :type
61
- # t.integer :position
74
+ # t.integer :position
62
75
  # end
63
76
  #
64
- # SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1
77
+ # SystemSetting.create :name => "notice",
78
+ # :label => "Use notice?",
79
+ # :value => 1
65
80
  # end
66
81
  #
67
- # def self.down
82
+ # def down
68
83
  # drop_table :system_settings
69
84
  # end
70
85
  # end
71
86
  #
72
- # This migration first adds the system_settings table, then creates the very first row in it using the Active Record model
73
- # that relies on the table. It also uses the more advanced create_table syntax where you can specify a complete table schema
74
- # in one block call.
87
+ # This migration first adds the system_settings table, then creates the very
88
+ # 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
90
+ # complete table schema in one block call.
75
91
  #
76
92
  # == Available transformations
77
93
  #
78
- # * <tt>create_table(name, options)</tt> Creates a table called +name+ and makes the table object available to a block
79
- # that can then add columns to it, following the same format as add_column. See example above. The options hash is for
80
- # fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create table definition.
94
+ # * <tt>create_table(name, options)</tt> Creates a table called +name+ and
95
+ # 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
97
+ # is for fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create
98
+ # table definition.
81
99
  # * <tt>drop_table(name)</tt>: Drops the table called +name+.
82
- # * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+ to +new_name+.
83
- # * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column to the table called +table_name+
100
+ # * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
101
+ # to +new_name+.
102
+ # * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column
103
+ # to the table called +table_name+
84
104
  # named +column_name+ specified to be one of the following types:
85
- # <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
86
- # <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>. A default value can be specified by passing an
87
- # +options+ hash like <tt>{ :default => 11 }</tt>. Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g. <tt>{ :limit => 50, :null => false }</tt>)
88
- # -- see ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
89
- # * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames a column but keeps the type and content.
90
- # * <tt>change_column(table_name, column_name, type, options)</tt>: Changes the column to a different type using the same
91
- # parameters as add_column.
92
- # * <tt>remove_column(table_name, column_name)</tt>: Removes the column named +column_name+ from the table called +table_name+.
93
- # * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index with the name of the column. Other options include
94
- # <tt>:name</tt> and <tt>:unique</tt> (e.g. <tt>{ :name => "users_name_index", :unique => true }</tt>).
95
- # * <tt>remove_index(table_name, index_name)</tt>: Removes the index specified by +index_name+.
105
+ # <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>,
106
+ # <tt>:decimal</tt>, <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
107
+ # <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>.
109
+ # Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g.
110
+ # <tt>{ :limit => 50, :null => false }</tt>) -- see
111
+ # ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
112
+ # * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames
113
+ # a column but keeps the type and content.
114
+ # * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
115
+ # 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+.
118
+ # * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
119
+ # with the name of the column. Other options include
120
+ # <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
124
+ # specified by +column_name+.
125
+ # * <tt>remove_index(table_name, :name => index_name)</tt>: Removes the index
126
+ # specified by +index_name+.
96
127
  #
97
128
  # == Irreversible transformations
98
129
  #
99
- # Some transformations are destructive in a manner that cannot be reversed. Migrations of that kind should raise
100
- # an <tt>ActiveRecord::IrreversibleMigration</tt> exception in their +down+ method.
130
+ # Some transformations are destructive in a manner that cannot be reversed.
131
+ # Migrations of that kind should raise an <tt>ActiveRecord::IrreversibleMigration</tt>
132
+ # exception in their +down+ method.
101
133
  #
102
134
  # == Running migrations from within Rails
103
135
  #
104
136
  # The Rails package has several tools to help create and apply migrations.
105
137
  #
106
- # To generate a new migration, you can use
107
- # script/generate migration MyNewMigration
138
+ # To generate a new migration, you can use
139
+ # rails generate migration MyNewMigration
108
140
  #
109
141
  # where MyNewMigration is the name of your migration. The generator will
110
- # create an empty migration file <tt>nnn_my_new_migration.rb</tt> in the <tt>db/migrate/</tt>
111
- # directory where <tt>nnn</tt> is the next largest migration number.
142
+ # create an empty migration file <tt>timestamp_my_new_migration.rb</tt>
143
+ # in the <tt>db/migrate/</tt> directory where <tt>timestamp</tt> is the
144
+ # UTC formatted date and time that the migration was generated.
112
145
  #
113
- # You may then edit the <tt>self.up</tt> and <tt>self.down</tt> methods of
146
+ # You may then edit the <tt>up</tt> and <tt>down</tt> methods of
114
147
  # MyNewMigration.
115
148
  #
116
149
  # There is a special syntactic shortcut to generate migrations that add fields to a table.
117
- # script/generate migration add_fieldname_to_tablename fieldname:string
118
150
  #
119
- # This will generate the file <tt>nnn_add_fieldname_to_tablename</tt>, which will look like this:
151
+ # rails generate migration add_fieldname_to_tablename fieldname:string
152
+ #
153
+ # This will generate the file <tt>timestamp_add_fieldname_to_tablename</tt>, which will look like this:
120
154
  # class AddFieldnameToTablename < ActiveRecord::Migration
121
- # def self.up
155
+ # def up
122
156
  # add_column :tablenames, :fieldname, :string
123
157
  # end
124
- #
125
- # def self.down
158
+ #
159
+ # def down
126
160
  # remove_column :tablenames, :fieldname
127
161
  # end
128
162
  # end
129
- #
163
+ #
130
164
  # To run migrations against the currently configured database, use
131
165
  # <tt>rake db:migrate</tt>. This will update the database by running all of the
132
166
  # pending migrations, creating the <tt>schema_migrations</tt> table
133
- # (see "About the schema_migrations table" section below) if missing. It will also
167
+ # (see "About the schema_migrations table" section below) if missing. It will also
134
168
  # invoke the db:schema:dump task, which will update your db/schema.rb file
135
169
  # to match the structure of your database.
136
170
  #
@@ -150,11 +184,11 @@ module ActiveRecord
150
184
  # Not all migrations change the schema. Some just fix the data:
151
185
  #
152
186
  # class RemoveEmptyTags < ActiveRecord::Migration
153
- # def self.up
154
- # Tag.find(:all).each { |tag| tag.destroy if tag.pages.empty? }
187
+ # def up
188
+ # Tag.all.each { |tag| tag.destroy if tag.pages.empty? }
155
189
  # end
156
190
  #
157
- # def self.down
191
+ # def down
158
192
  # # not much we can do to restore deleted data
159
193
  # raise ActiveRecord::IrreversibleMigration, "Can't recover the deleted tags"
160
194
  # end
@@ -163,12 +197,12 @@ module ActiveRecord
163
197
  # Others remove columns when they migrate up instead of down:
164
198
  #
165
199
  # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration
166
- # def self.up
200
+ # def up
167
201
  # remove_column :items, :incomplete_items_count
168
202
  # remove_column :items, :completed_items_count
169
203
  # end
170
204
  #
171
- # def self.down
205
+ # def down
172
206
  # add_column :items, :incomplete_items_count
173
207
  # add_column :items, :completed_items_count
174
208
  # end
@@ -177,26 +211,27 @@ module ActiveRecord
177
211
  # And sometimes you need to do something in SQL not abstracted directly by migrations:
178
212
  #
179
213
  # class MakeJoinUnique < ActiveRecord::Migration
180
- # def self.up
214
+ # def up
181
215
  # execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
182
216
  # end
183
217
  #
184
- # def self.down
218
+ # def down
185
219
  # execute "ALTER TABLE `pages_linked_pages` DROP INDEX `page_id_linked_page_id`"
186
220
  # end
187
221
  # end
188
222
  #
189
223
  # == Using a model after changing its table
190
224
  #
191
- # Sometimes you'll want to add a column in a migration and populate it immediately after. In that case, you'll need
192
- # to make a call to Base#reset_column_information in order to ensure that the model has the latest column data from
193
- # after the new column was added. Example:
225
+ # Sometimes you'll want to add a column in a migration and populate it
226
+ # immediately after. In that case, you'll need to make a call to
227
+ # <tt>Base#reset_column_information</tt> in order to ensure that the model has the
228
+ # latest column data from after the new column was added. Example:
194
229
  #
195
230
  # class AddPeopleSalary < ActiveRecord::Migration
196
- # def self.up
231
+ # def up
197
232
  # add_column :people, :salary, :integer
198
233
  # Person.reset_column_information
199
- # Person.find(:all).each do |p|
234
+ # Person.all.each do |p|
200
235
  # p.update_attribute :salary, SalaryCalculator.compute(p)
201
236
  # end
202
237
  # end
@@ -213,10 +248,10 @@ module ActiveRecord
213
248
  # You can also insert your own messages and benchmarks by using the +say_with_time+
214
249
  # method:
215
250
  #
216
- # def self.up
251
+ # def up
217
252
  # ...
218
253
  # say_with_time "Updating salaries..." do
219
- # Person.find(:all).each do |p|
254
+ # Person.all.each do |p|
220
255
  # p.update_attribute :salary, SalaryCalculator.compute(p)
221
256
  # end
222
257
  # end
@@ -240,7 +275,7 @@ module ActiveRecord
240
275
  # lower than the current schema version: when migrating up, those
241
276
  # never-applied "interleaved" migrations will be automatically applied, and
242
277
  # when migrating down, never-applied "interleaved" migrations will be skipped.
243
- #
278
+ #
244
279
  # == Timestamped Migrations
245
280
  #
246
281
  # By default, Rails generates migrations that look like:
@@ -253,115 +288,244 @@ module ActiveRecord
253
288
  # off by setting:
254
289
  #
255
290
  # config.active_record.timestamped_migrations = false
256
- #
257
- # In environment.rb.
258
291
  #
292
+ # In application.rb.
293
+ #
294
+ # == Reversible Migrations
295
+ #
296
+ # Starting with Rails 3.1, you will be able to define reversible migrations.
297
+ # 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
299
+ # how to execute the down commands for you.
300
+ #
301
+ # To define a reversible migration, define the +change+ method in your
302
+ # migration like this:
303
+ #
304
+ # class TenderloveMigration < ActiveRecord::Migration
305
+ # def change
306
+ # create_table(:horses) do |t|
307
+ # t.column :content, :text
308
+ # t.column :remind_at, :datetime
309
+ # end
310
+ # end
311
+ # end
312
+ #
313
+ # This migration will create the horses table for you on the way up, and
314
+ # automatically figure out how to drop the table on the way down.
315
+ #
316
+ # Some commands like +remove_column+ cannot be reversed. If you care to
317
+ # define how to move up and down in these cases, you should define the +up+
318
+ # and +down+ methods as before.
319
+ #
320
+ # If a command cannot be reversed, an
321
+ # <tt>ActiveRecord::IrreversibleMigration</tt> exception will be raised when
322
+ # the migration is moving down.
323
+ #
324
+ # For a list of commands that are reversible, please see
325
+ # <tt>ActiveRecord::Migration::CommandRecorder</tt>.
259
326
  class Migration
260
- @@verbose = true
261
- cattr_accessor :verbose
327
+ autoload :CommandRecorder, 'active_record/migration/command_recorder'
262
328
 
263
329
  class << self
264
- def up_with_benchmarks #:nodoc:
265
- migrate(:up)
266
- end
330
+ attr_accessor :delegate # :nodoc:
331
+ end
267
332
 
268
- def down_with_benchmarks #:nodoc:
269
- migrate(:down)
270
- end
333
+ def self.method_missing(name, *args, &block) # :nodoc:
334
+ (delegate || superclass.delegate).send(name, *args, &block)
335
+ end
271
336
 
272
- # Execute this migration in the named direction
273
- def migrate(direction)
274
- return unless respond_to?(direction)
337
+ def self.migrate(direction)
338
+ new.migrate direction
339
+ end
275
340
 
276
- case direction
277
- when :up then announce "migrating"
278
- when :down then announce "reverting"
279
- end
341
+ cattr_accessor :verbose
280
342
 
281
- result = nil
282
- time = Benchmark.measure { result = send("#{direction}_without_benchmarks") }
343
+ attr_accessor :name, :version
283
344
 
284
- case direction
285
- when :up then announce "migrated (%.4fs)" % time.real; write
286
- when :down then announce "reverted (%.4fs)" % time.real; write
287
- end
345
+ def initialize
346
+ @name = self.class.name
347
+ @version = nil
348
+ @connection = nil
349
+ @reverting = false
350
+ end
288
351
 
289
- result
290
- end
352
+ # instantiate the delegate object after initialize is defined
353
+ self.verbose = true
354
+ self.delegate = new
291
355
 
292
- # Because the method added may do an alias_method, it can be invoked
293
- # recursively. We use @ignore_new_methods as a guard to indicate whether
294
- # it is safe for the call to proceed.
295
- def singleton_method_added(sym) #:nodoc:
296
- return if defined?(@ignore_new_methods) && @ignore_new_methods
356
+ def revert
357
+ @reverting = true
358
+ yield
359
+ ensure
360
+ @reverting = false
361
+ end
297
362
 
298
- begin
299
- @ignore_new_methods = true
363
+ def reverting?
364
+ @reverting
365
+ end
366
+
367
+ def up
368
+ self.class.delegate = self
369
+ return unless self.class.respond_to?(:up)
370
+ self.class.up
371
+ end
372
+
373
+ def down
374
+ self.class.delegate = self
375
+ return unless self.class.respond_to?(:down)
376
+ self.class.down
377
+ end
378
+
379
+ # Execute this migration in the named direction
380
+ def migrate(direction)
381
+ return unless respond_to?(direction)
382
+
383
+ case direction
384
+ when :up then announce "migrating"
385
+ when :down then announce "reverting"
386
+ end
300
387
 
301
- case sym
302
- when :up, :down
303
- klass = (class << self; self; end)
304
- klass.send(:alias_method_chain, sym, "benchmarks")
388
+ time = nil
389
+ 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 }
305
408
  end
306
- ensure
307
- @ignore_new_methods = false
409
+ else
410
+ time = Benchmark.measure { send(direction) }
308
411
  end
412
+ @connection = nil
309
413
  end
310
414
 
311
- def write(text="")
312
- puts(text) if verbose
415
+ case direction
416
+ when :up then announce "migrated (%.4fs)" % time.real; write
417
+ when :down then announce "reverted (%.4fs)" % time.real; write
313
418
  end
419
+ end
314
420
 
315
- def announce(message)
316
- text = "#{@version} #{name}: #{message}"
317
- length = [0, 75 - text.length].max
318
- write "== %s %s" % [text, "=" * length]
319
- end
421
+ def write(text="")
422
+ puts(text) if verbose
423
+ end
320
424
 
321
- def say(message, subitem=false)
322
- write "#{subitem ? " ->" : "--"} #{message}"
323
- end
425
+ def announce(message)
426
+ text = "#{version} #{name}: #{message}"
427
+ length = [0, 75 - text.length].max
428
+ write "== %s %s" % [text, "=" * length]
429
+ end
324
430
 
325
- def say_with_time(message)
326
- say(message)
327
- result = nil
328
- time = Benchmark.measure { result = yield }
329
- say "%.4fs" % time.real, :subitem
330
- say("#{result} rows", :subitem) if result.is_a?(Integer)
331
- result
332
- end
431
+ def say(message, subitem=false)
432
+ write "#{subitem ? " ->" : "--"} #{message}"
433
+ end
333
434
 
334
- def suppress_messages
335
- save, self.verbose = verbose, false
336
- yield
337
- ensure
338
- self.verbose = save
339
- end
435
+ def say_with_time(message)
436
+ say(message)
437
+ result = nil
438
+ time = Benchmark.measure { result = yield }
439
+ say "%.4fs" % time.real, :subitem
440
+ say("#{result} rows", :subitem) if result.is_a?(Integer)
441
+ result
442
+ end
340
443
 
341
- def connection
342
- ActiveRecord::Base.connection
343
- end
444
+ def suppress_messages
445
+ save, self.verbose = verbose, false
446
+ yield
447
+ ensure
448
+ self.verbose = save
449
+ end
450
+
451
+ def connection
452
+ @connection || ActiveRecord::Base.connection
453
+ end
344
454
 
345
- def method_missing(method, *arguments, &block)
346
- arg_list = arguments.map(&:inspect) * ', '
455
+ def method_missing(method, *arguments, &block)
456
+ arg_list = arguments.map{ |a| a.inspect } * ', '
347
457
 
348
- say_with_time "#{method}(#{arg_list})" do
458
+ say_with_time "#{method}(#{arg_list})" do
459
+ unless reverting?
349
460
  unless arguments.empty? || method == :execute
350
- arguments[0] = Migrator.proper_table_name(arguments.first)
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
351
463
  end
352
- connection.send(method, *arguments, &block)
353
464
  end
465
+ return super unless connection.respond_to?(method)
466
+ connection.send(method, *arguments, &block)
467
+ end
468
+ end
469
+
470
+ def copy(destination, sources, options = {})
471
+ copied = []
472
+
473
+ FileUtils.mkdir_p(destination) unless File.exists?(destination)
474
+
475
+ destination_migrations = ActiveRecord::Migrator.migrations(destination)
476
+ last = destination_migrations.last
477
+ sources.each do |scope, path|
478
+ source_migrations = ActiveRecord::Migrator.migrations(path)
479
+
480
+ source_migrations.each do |migration|
481
+ source = File.read(migration.filename)
482
+ source = "# This migration comes from #{scope} (originally #{migration.version})\n#{source}"
483
+
484
+ if duplicate = destination_migrations.detect { |m| m.name == migration.name }
485
+ if options[:on_skip] && duplicate.scope != scope.to_s
486
+ options[:on_skip].call(scope, migration)
487
+ end
488
+ next
489
+ end
490
+
491
+ migration.version = next_migration_number(last ? last.version + 1 : 0).to_i
492
+ new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.#{scope}.rb")
493
+ old_path, migration.filename = migration.filename, new_path
494
+ last = migration
495
+
496
+ File.open(migration.filename, "w") { |f| f.write source }
497
+ copied << migration
498
+ options[:on_copy].call(scope, migration, old_path) if options[:on_copy]
499
+ destination_migrations << migration
500
+ end
501
+ end
502
+
503
+ copied
504
+ end
505
+
506
+ def next_migration_number(number)
507
+ if ActiveRecord::Base.timestamped_migrations
508
+ [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % number].max
509
+ else
510
+ "%.3d" % number
354
511
  end
355
512
  end
356
513
  end
357
514
 
358
515
  # MigrationProxy is used to defer loading of the actual migration classes
359
516
  # until they are needed
360
- class MigrationProxy
517
+ class MigrationProxy < Struct.new(:name, :version, :filename, :scope)
361
518
 
362
- attr_accessor :name, :version, :filename
519
+ def initialize(name, version, filename, scope)
520
+ super
521
+ @migration = nil
522
+ end
523
+
524
+ def basename
525
+ File.basename(filename)
526
+ end
363
527
 
364
- delegate :migrate, :announce, :write, :to=>:migration
528
+ delegate :migrate, :announce, :write, :to => :migration
365
529
 
366
530
  private
367
531
 
@@ -370,47 +534,48 @@ module ActiveRecord
370
534
  end
371
535
 
372
536
  def load_migration
373
- load(filename)
374
- name.constantize
537
+ require(File.expand_path(filename))
538
+ name.constantize.new
375
539
  end
376
540
 
377
541
  end
378
542
 
379
543
  class Migrator#:nodoc:
380
544
  class << self
381
- def migrate(migrations_path, target_version = nil)
545
+ attr_writer :migrations_paths
546
+ alias :migrations_path= :migrations_paths=
547
+
548
+ def migrate(migrations_paths, target_version = nil, &block)
382
549
  case
383
- when target_version.nil? then up(migrations_path, target_version)
384
- when current_version == 0 && target_version == 0 then # noop
385
- when current_version > target_version then down(migrations_path, target_version)
386
- else up(migrations_path, target_version)
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)
387
558
  end
388
559
  end
389
560
 
390
- def rollback(migrations_path, steps=1)
391
- migrator = self.new(:down, migrations_path)
392
- start_index = migrator.migrations.index(migrator.current_migration)
393
-
394
- return unless start_index
395
-
396
- finish = migrator.migrations[start_index + steps]
397
- down(migrations_path, finish ? finish.version : 0)
561
+ def rollback(migrations_paths, steps=1)
562
+ move(:down, migrations_paths, steps)
398
563
  end
399
564
 
400
- def up(migrations_path, target_version = nil)
401
- self.new(:up, migrations_path, target_version).migrate
565
+ def forward(migrations_paths, steps=1)
566
+ move(:up, migrations_paths, steps)
402
567
  end
403
568
 
404
- def down(migrations_path, target_version = nil)
405
- self.new(:down, migrations_path, target_version).migrate
569
+ def up(migrations_paths, target_version = nil, &block)
570
+ self.new(:up, migrations_paths, target_version).migrate(&block)
406
571
  end
407
-
408
- def run(direction, migrations_path, target_version)
409
- self.new(direction, migrations_path, target_version).run
572
+
573
+ def down(migrations_paths, target_version = nil, &block)
574
+ self.new(:down, migrations_paths, target_version).migrate(&block)
410
575
  end
411
576
 
412
- def migrations_path
413
- 'db/migrate'
577
+ def run(direction, migrations_paths, target_version)
578
+ self.new(direction, migrations_paths, target_version).run
414
579
  end
415
580
 
416
581
  def schema_migrations_table_name
@@ -418,7 +583,8 @@ module ActiveRecord
418
583
  end
419
584
 
420
585
  def get_all_versions
421
- Base.connection.select_values("SELECT version FROM #{schema_migrations_table_name}").map(&:to_i).sort
586
+ table = Arel::Table.new(schema_migrations_table_name)
587
+ Base.connection.select_values(table.project(table['version'])).map{ |v| v.to_i }.sort
422
588
  end
423
589
 
424
590
  def current_version
@@ -434,22 +600,78 @@ module ActiveRecord
434
600
  # Use the Active Record objects own table_name, or pre/suffix from ActiveRecord::Base if name is a symbol/string
435
601
  name.table_name rescue "#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}"
436
602
  end
603
+
604
+ def migrations_paths
605
+ @migrations_paths ||= ['db/migrate']
606
+ # just to not break things if someone uses: migration_path = some_string
607
+ Array.wrap(@migrations_paths)
608
+ end
609
+
610
+ def migrations_path
611
+ migrations_paths.first
612
+ end
613
+
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
628
+
629
+ migrations = files.map do |file|
630
+ version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
631
+
632
+ raise IllegalMigrationNameError.new(file) unless version
633
+ version = version.to_i
634
+ name = name.camelize
635
+
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
+ MigrationProxy.new(name, version, file, scope)
642
+ end
643
+
644
+ migrations.sort_by(&:version)
645
+ end
646
+
647
+ private
648
+
649
+ def move(direction, migrations_paths, steps)
650
+ migrator = self.new(direction, migrations_paths)
651
+ start_index = migrator.migrations.index(migrator.current_migration)
652
+
653
+ if start_index
654
+ finish = migrator.migrations[start_index + steps]
655
+ version = finish ? finish.version : 0
656
+ send(direction, migrations_paths, version)
657
+ end
658
+ end
437
659
  end
438
660
 
439
- def initialize(direction, migrations_path, target_version = nil)
661
+ def initialize(direction, migrations_paths, target_version = nil)
440
662
  raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?
441
663
  Base.connection.initialize_schema_migrations_table
442
- @direction, @migrations_path, @target_version = direction, migrations_path, target_version
664
+ @direction, @migrations_paths, @target_version = direction, migrations_paths, target_version
443
665
  end
444
666
 
445
667
  def current_version
446
668
  migrated.last || 0
447
669
  end
448
-
670
+
449
671
  def current_migration
450
672
  migrations.detect { |m| m.version == current_version }
451
673
  end
452
-
674
+
453
675
  def run
454
676
  target = migrations.detect { |m| m.version == @target_version }
455
677
  raise UnknownMigrationVersionError.new(@target_version) if target.nil?
@@ -459,29 +681,36 @@ module ActiveRecord
459
681
  end
460
682
  end
461
683
 
462
- def migrate
684
+ def migrate(&block)
463
685
  current = migrations.detect { |m| m.version == current_version }
464
686
  target = migrations.detect { |m| m.version == @target_version }
465
687
 
466
- if target.nil? && !@target_version.nil? && @target_version > 0
688
+ if target.nil? && @target_version && @target_version > 0
467
689
  raise UnknownMigrationVersionError.new(@target_version)
468
690
  end
469
-
691
+
470
692
  start = up? ? 0 : (migrations.index(current) || 0)
471
693
  finish = migrations.index(target) || migrations.size - 1
472
694
  runnable = migrations[start..finish]
473
-
695
+
474
696
  # skip the last migration if we're headed down, but not ALL the way down
475
- runnable.pop if down? && !target.nil?
476
-
697
+ runnable.pop if down? && target
698
+
699
+ ran = []
477
700
  runnable.each do |migration|
478
- Base.logger.info "Migrating to #{migration.name} (#{migration.version})"
701
+ if block && !block.call(migration)
702
+ next
703
+ end
704
+
705
+ Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
706
+
707
+ seen = migrated.include?(migration.version.to_i)
479
708
 
480
709
  # On our way up, we skip migrating the ones we've already migrated
481
- next if up? && migrated.include?(migration.version.to_i)
710
+ next if up? && seen
482
711
 
483
712
  # On our way down, we skip reverting the ones we've never migrated
484
- if down? && !migrated.include?(migration.version.to_i)
713
+ if down? && !seen
485
714
  migration.announce 'never migrated, skipping'; migration.write
486
715
  next
487
716
  end
@@ -491,39 +720,18 @@ module ActiveRecord
491
720
  migration.migrate(@direction)
492
721
  record_version_state_after_migrating(migration.version)
493
722
  end
723
+ ran << migration
494
724
  rescue => e
495
725
  canceled_msg = Base.connection.supports_ddl_transactions? ? "this and " : ""
496
726
  raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace
497
727
  end
498
728
  end
729
+ ran
499
730
  end
500
731
 
501
732
  def migrations
502
733
  @migrations ||= begin
503
- files = Dir["#{@migrations_path}/[0-9]*_*.rb"]
504
-
505
- migrations = files.inject([]) do |klasses, file|
506
- version, name = file.scan(/([0-9]+)_([_a-z0-9]*).rb/).first
507
-
508
- raise IllegalMigrationNameError.new(file) unless version
509
- version = version.to_i
510
-
511
- if klasses.detect { |m| m.version == version }
512
- raise DuplicateMigrationVersionError.new(version)
513
- end
514
-
515
- if klasses.detect { |m| m.name == name.camelize }
516
- raise DuplicateMigrationNameError.new(name.camelize)
517
- end
518
-
519
- klasses << (MigrationProxy.new).tap do |migration|
520
- migration.name = name.camelize
521
- migration.version = version
522
- migration.filename = file
523
- end
524
- end
525
-
526
- migrations = migrations.sort_by(&:version)
734
+ migrations = self.class.migrations(@migrations_paths)
527
735
  down? ? migrations.reverse : migrations
528
736
  end
529
737
  end
@@ -539,15 +747,17 @@ module ActiveRecord
539
747
 
540
748
  private
541
749
  def record_version_state_after_migrating(version)
542
- sm_table = self.class.schema_migrations_table_name
750
+ table = Arel::Table.new(self.class.schema_migrations_table_name)
543
751
 
544
752
  @migrated_versions ||= []
545
753
  if down?
546
- @migrated_versions.delete(version.to_i)
547
- Base.connection.update("DELETE FROM #{sm_table} WHERE version = '#{version}'")
754
+ @migrated_versions.delete(version)
755
+ stmt = table.where(table["version"].eq(version.to_s)).compile_delete
756
+ Base.connection.delete stmt
548
757
  else
549
- @migrated_versions.push(version.to_i).sort!
550
- Base.connection.insert("INSERT INTO #{sm_table} (version) VALUES ('#{version}')")
758
+ @migrated_versions.push(version).sort!
759
+ stmt = table.compile_insert table["version"] => version.to_s
760
+ Base.connection.insert stmt
551
761
  end
552
762
  end
553
763