activerecord_csi 2.3.5.p6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. data/CHANGELOG +5858 -0
  2. data/README +351 -0
  3. data/RUNNING_UNIT_TESTS +36 -0
  4. data/Rakefile +270 -0
  5. data/examples/associations.png +0 -0
  6. data/examples/performance.rb +162 -0
  7. data/install.rb +30 -0
  8. data/lib/active_record/aggregations.rb +261 -0
  9. data/lib/active_record/association_preload.rb +389 -0
  10. data/lib/active_record/associations/association_collection.rb +475 -0
  11. data/lib/active_record/associations/association_proxy.rb +278 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +76 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +53 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +143 -0
  15. data/lib/active_record/associations/has_many_association.rb +122 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +266 -0
  17. data/lib/active_record/associations/has_one_association.rb +133 -0
  18. data/lib/active_record/associations/has_one_through_association.rb +37 -0
  19. data/lib/active_record/associations.rb +2241 -0
  20. data/lib/active_record/attribute_methods.rb +388 -0
  21. data/lib/active_record/autosave_association.rb +364 -0
  22. data/lib/active_record/base.rb +3171 -0
  23. data/lib/active_record/batches.rb +81 -0
  24. data/lib/active_record/calculations.rb +311 -0
  25. data/lib/active_record/callbacks.rb +360 -0
  26. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +371 -0
  27. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +139 -0
  28. data/lib/active_record/connection_adapters/abstract/database_statements.rb +289 -0
  29. data/lib/active_record/connection_adapters/abstract/query_cache.rb +94 -0
  30. data/lib/active_record/connection_adapters/abstract/quoting.rb +69 -0
  31. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +722 -0
  32. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +434 -0
  33. data/lib/active_record/connection_adapters/abstract_adapter.rb +241 -0
  34. data/lib/active_record/connection_adapters/mysql_adapter.rb +630 -0
  35. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1113 -0
  36. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  37. data/lib/active_record/connection_adapters/sqlite_adapter.rb +453 -0
  38. data/lib/active_record/dirty.rb +183 -0
  39. data/lib/active_record/dynamic_finder_match.rb +41 -0
  40. data/lib/active_record/dynamic_scope_match.rb +25 -0
  41. data/lib/active_record/fixtures.rb +996 -0
  42. data/lib/active_record/i18n_interpolation_deprecation.rb +26 -0
  43. data/lib/active_record/locale/en.yml +58 -0
  44. data/lib/active_record/locking/optimistic.rb +148 -0
  45. data/lib/active_record/locking/pessimistic.rb +55 -0
  46. data/lib/active_record/migration.rb +566 -0
  47. data/lib/active_record/named_scope.rb +192 -0
  48. data/lib/active_record/nested_attributes.rb +392 -0
  49. data/lib/active_record/observer.rb +197 -0
  50. data/lib/active_record/query_cache.rb +33 -0
  51. data/lib/active_record/reflection.rb +320 -0
  52. data/lib/active_record/schema.rb +51 -0
  53. data/lib/active_record/schema_dumper.rb +182 -0
  54. data/lib/active_record/serialization.rb +101 -0
  55. data/lib/active_record/serializers/json_serializer.rb +91 -0
  56. data/lib/active_record/serializers/xml_serializer.rb +357 -0
  57. data/lib/active_record/session_store.rb +326 -0
  58. data/lib/active_record/test_case.rb +66 -0
  59. data/lib/active_record/timestamp.rb +71 -0
  60. data/lib/active_record/transactions.rb +235 -0
  61. data/lib/active_record/validations.rb +1135 -0
  62. data/lib/active_record/version.rb +9 -0
  63. data/lib/active_record.rb +84 -0
  64. data/lib/activerecord.rb +2 -0
  65. data/test/assets/example.log +1 -0
  66. data/test/assets/flowers.jpg +0 -0
  67. data/test/cases/aaa_create_tables_test.rb +24 -0
  68. data/test/cases/active_schema_test_mysql.rb +100 -0
  69. data/test/cases/active_schema_test_postgresql.rb +24 -0
  70. data/test/cases/adapter_test.rb +145 -0
  71. data/test/cases/aggregations_test.rb +167 -0
  72. data/test/cases/ar_schema_test.rb +32 -0
  73. data/test/cases/associations/belongs_to_associations_test.rb +425 -0
  74. data/test/cases/associations/callbacks_test.rb +161 -0
  75. data/test/cases/associations/cascaded_eager_loading_test.rb +131 -0
  76. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -0
  77. data/test/cases/associations/eager_load_nested_include_test.rb +130 -0
  78. data/test/cases/associations/eager_singularization_test.rb +145 -0
  79. data/test/cases/associations/eager_test.rb +834 -0
  80. data/test/cases/associations/extension_test.rb +62 -0
  81. data/test/cases/associations/habtm_join_table_test.rb +56 -0
  82. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +822 -0
  83. data/test/cases/associations/has_many_associations_test.rb +1134 -0
  84. data/test/cases/associations/has_many_through_associations_test.rb +346 -0
  85. data/test/cases/associations/has_one_associations_test.rb +330 -0
  86. data/test/cases/associations/has_one_through_associations_test.rb +209 -0
  87. data/test/cases/associations/inner_join_association_test.rb +93 -0
  88. data/test/cases/associations/join_model_test.rb +712 -0
  89. data/test/cases/associations_test.rb +262 -0
  90. data/test/cases/attribute_methods_test.rb +305 -0
  91. data/test/cases/autosave_association_test.rb +1142 -0
  92. data/test/cases/base_test.rb +2154 -0
  93. data/test/cases/batches_test.rb +61 -0
  94. data/test/cases/binary_test.rb +30 -0
  95. data/test/cases/calculations_test.rb +348 -0
  96. data/test/cases/callbacks_observers_test.rb +38 -0
  97. data/test/cases/callbacks_test.rb +438 -0
  98. data/test/cases/class_inheritable_attributes_test.rb +32 -0
  99. data/test/cases/column_alias_test.rb +17 -0
  100. data/test/cases/column_definition_test.rb +70 -0
  101. data/test/cases/connection_pool_test.rb +25 -0
  102. data/test/cases/connection_test_firebird.rb +8 -0
  103. data/test/cases/connection_test_mysql.rb +64 -0
  104. data/test/cases/copy_table_test_sqlite.rb +80 -0
  105. data/test/cases/database_statements_test.rb +12 -0
  106. data/test/cases/datatype_test_postgresql.rb +204 -0
  107. data/test/cases/date_time_test.rb +37 -0
  108. data/test/cases/default_test_firebird.rb +16 -0
  109. data/test/cases/defaults_test.rb +111 -0
  110. data/test/cases/deprecated_finder_test.rb +30 -0
  111. data/test/cases/dirty_test.rb +316 -0
  112. data/test/cases/finder_respond_to_test.rb +76 -0
  113. data/test/cases/finder_test.rb +1066 -0
  114. data/test/cases/fixtures_test.rb +656 -0
  115. data/test/cases/helper.rb +68 -0
  116. data/test/cases/i18n_test.rb +46 -0
  117. data/test/cases/inheritance_test.rb +262 -0
  118. data/test/cases/invalid_date_test.rb +24 -0
  119. data/test/cases/json_serialization_test.rb +205 -0
  120. data/test/cases/lifecycle_test.rb +193 -0
  121. data/test/cases/locking_test.rb +304 -0
  122. data/test/cases/method_scoping_test.rb +704 -0
  123. data/test/cases/migration_test.rb +1523 -0
  124. data/test/cases/migration_test_firebird.rb +124 -0
  125. data/test/cases/mixin_test.rb +96 -0
  126. data/test/cases/modules_test.rb +81 -0
  127. data/test/cases/multiple_db_test.rb +85 -0
  128. data/test/cases/named_scope_test.rb +361 -0
  129. data/test/cases/nested_attributes_test.rb +581 -0
  130. data/test/cases/pk_test.rb +119 -0
  131. data/test/cases/pooled_connections_test.rb +103 -0
  132. data/test/cases/query_cache_test.rb +123 -0
  133. data/test/cases/readonly_test.rb +107 -0
  134. data/test/cases/reflection_test.rb +194 -0
  135. data/test/cases/reload_models_test.rb +22 -0
  136. data/test/cases/repair_helper.rb +50 -0
  137. data/test/cases/reserved_word_test_mysql.rb +176 -0
  138. data/test/cases/sanitize_test.rb +25 -0
  139. data/test/cases/schema_authorization_test_postgresql.rb +75 -0
  140. data/test/cases/schema_dumper_test.rb +211 -0
  141. data/test/cases/schema_test_postgresql.rb +178 -0
  142. data/test/cases/serialization_test.rb +47 -0
  143. data/test/cases/synonym_test_oracle.rb +17 -0
  144. data/test/cases/timestamp_test.rb +75 -0
  145. data/test/cases/transactions_test.rb +522 -0
  146. data/test/cases/unconnected_test.rb +32 -0
  147. data/test/cases/validations_i18n_test.rb +955 -0
  148. data/test/cases/validations_test.rb +1640 -0
  149. data/test/cases/xml_serialization_test.rb +240 -0
  150. data/test/config.rb +5 -0
  151. data/test/connections/jdbc_jdbcderby/connection.rb +18 -0
  152. data/test/connections/jdbc_jdbch2/connection.rb +18 -0
  153. data/test/connections/jdbc_jdbchsqldb/connection.rb +18 -0
  154. data/test/connections/jdbc_jdbcmysql/connection.rb +26 -0
  155. data/test/connections/jdbc_jdbcpostgresql/connection.rb +26 -0
  156. data/test/connections/jdbc_jdbcsqlite3/connection.rb +25 -0
  157. data/test/connections/native_db2/connection.rb +25 -0
  158. data/test/connections/native_firebird/connection.rb +26 -0
  159. data/test/connections/native_frontbase/connection.rb +27 -0
  160. data/test/connections/native_mysql/connection.rb +25 -0
  161. data/test/connections/native_openbase/connection.rb +21 -0
  162. data/test/connections/native_oracle/connection.rb +27 -0
  163. data/test/connections/native_postgresql/connection.rb +25 -0
  164. data/test/connections/native_sqlite/connection.rb +25 -0
  165. data/test/connections/native_sqlite3/connection.rb +25 -0
  166. data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
  167. data/test/connections/native_sybase/connection.rb +23 -0
  168. data/test/fixtures/accounts.yml +29 -0
  169. data/test/fixtures/all/developers.yml +0 -0
  170. data/test/fixtures/all/people.csv +0 -0
  171. data/test/fixtures/all/tasks.yml +0 -0
  172. data/test/fixtures/author_addresses.yml +5 -0
  173. data/test/fixtures/author_favorites.yml +4 -0
  174. data/test/fixtures/authors.yml +9 -0
  175. data/test/fixtures/binaries.yml +132 -0
  176. data/test/fixtures/books.yml +7 -0
  177. data/test/fixtures/categories/special_categories.yml +9 -0
  178. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  179. data/test/fixtures/categories.yml +14 -0
  180. data/test/fixtures/categories_ordered.yml +7 -0
  181. data/test/fixtures/categories_posts.yml +23 -0
  182. data/test/fixtures/categorizations.yml +17 -0
  183. data/test/fixtures/clubs.yml +6 -0
  184. data/test/fixtures/comments.yml +59 -0
  185. data/test/fixtures/companies.yml +56 -0
  186. data/test/fixtures/computers.yml +4 -0
  187. data/test/fixtures/courses.yml +7 -0
  188. data/test/fixtures/customers.yml +26 -0
  189. data/test/fixtures/developers.yml +21 -0
  190. data/test/fixtures/developers_projects.yml +17 -0
  191. data/test/fixtures/edges.yml +6 -0
  192. data/test/fixtures/entrants.yml +14 -0
  193. data/test/fixtures/fixture_database.sqlite3 +0 -0
  194. data/test/fixtures/fixture_database_2.sqlite3 +0 -0
  195. data/test/fixtures/fk_test_has_fk.yml +3 -0
  196. data/test/fixtures/fk_test_has_pk.yml +2 -0
  197. data/test/fixtures/funny_jokes.yml +10 -0
  198. data/test/fixtures/items.yml +4 -0
  199. data/test/fixtures/jobs.yml +7 -0
  200. data/test/fixtures/legacy_things.yml +3 -0
  201. data/test/fixtures/mateys.yml +4 -0
  202. data/test/fixtures/member_types.yml +6 -0
  203. data/test/fixtures/members.yml +6 -0
  204. data/test/fixtures/memberships.yml +20 -0
  205. data/test/fixtures/minimalistics.yml +2 -0
  206. data/test/fixtures/mixed_case_monkeys.yml +6 -0
  207. data/test/fixtures/mixins.yml +29 -0
  208. data/test/fixtures/movies.yml +7 -0
  209. data/test/fixtures/naked/csv/accounts.csv +1 -0
  210. data/test/fixtures/naked/yml/accounts.yml +1 -0
  211. data/test/fixtures/naked/yml/companies.yml +1 -0
  212. data/test/fixtures/naked/yml/courses.yml +1 -0
  213. data/test/fixtures/organizations.yml +5 -0
  214. data/test/fixtures/owners.yml +7 -0
  215. data/test/fixtures/parrots.yml +27 -0
  216. data/test/fixtures/parrots_pirates.yml +7 -0
  217. data/test/fixtures/people.yml +15 -0
  218. data/test/fixtures/pets.yml +14 -0
  219. data/test/fixtures/pirates.yml +9 -0
  220. data/test/fixtures/posts.yml +52 -0
  221. data/test/fixtures/price_estimates.yml +7 -0
  222. data/test/fixtures/projects.yml +7 -0
  223. data/test/fixtures/readers.yml +9 -0
  224. data/test/fixtures/references.yml +17 -0
  225. data/test/fixtures/reserved_words/distinct.yml +5 -0
  226. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  227. data/test/fixtures/reserved_words/group.yml +14 -0
  228. data/test/fixtures/reserved_words/select.yml +8 -0
  229. data/test/fixtures/reserved_words/values.yml +7 -0
  230. data/test/fixtures/ships.yml +5 -0
  231. data/test/fixtures/sponsors.yml +9 -0
  232. data/test/fixtures/subscribers.yml +7 -0
  233. data/test/fixtures/subscriptions.yml +12 -0
  234. data/test/fixtures/taggings.yml +28 -0
  235. data/test/fixtures/tags.yml +7 -0
  236. data/test/fixtures/tasks.yml +7 -0
  237. data/test/fixtures/topics.yml +42 -0
  238. data/test/fixtures/toys.yml +4 -0
  239. data/test/fixtures/treasures.yml +10 -0
  240. data/test/fixtures/vertices.yml +4 -0
  241. data/test/fixtures/warehouse-things.yml +3 -0
  242. data/test/migrations/broken/100_migration_that_raises_exception.rb +10 -0
  243. data/test/migrations/decimal/1_give_me_big_numbers.rb +15 -0
  244. data/test/migrations/duplicate/1_people_have_last_names.rb +9 -0
  245. data/test/migrations/duplicate/2_we_need_reminders.rb +12 -0
  246. data/test/migrations/duplicate/3_foo.rb +7 -0
  247. data/test/migrations/duplicate/3_innocent_jointable.rb +12 -0
  248. data/test/migrations/duplicate_names/20080507052938_chunky.rb +7 -0
  249. data/test/migrations/duplicate_names/20080507053028_chunky.rb +7 -0
  250. data/test/migrations/interleaved/pass_1/3_innocent_jointable.rb +12 -0
  251. data/test/migrations/interleaved/pass_2/1_people_have_last_names.rb +9 -0
  252. data/test/migrations/interleaved/pass_2/3_innocent_jointable.rb +12 -0
  253. data/test/migrations/interleaved/pass_3/1_people_have_last_names.rb +9 -0
  254. data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +8 -0
  255. data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +12 -0
  256. data/test/migrations/missing/1000_people_have_middle_names.rb +9 -0
  257. data/test/migrations/missing/1_people_have_last_names.rb +9 -0
  258. data/test/migrations/missing/3_we_need_reminders.rb +12 -0
  259. data/test/migrations/missing/4_innocent_jointable.rb +12 -0
  260. data/test/migrations/valid/1_people_have_last_names.rb +9 -0
  261. data/test/migrations/valid/2_we_need_reminders.rb +12 -0
  262. data/test/migrations/valid/3_innocent_jointable.rb +12 -0
  263. data/test/models/author.rb +146 -0
  264. data/test/models/auto_id.rb +4 -0
  265. data/test/models/binary.rb +2 -0
  266. data/test/models/bird.rb +3 -0
  267. data/test/models/book.rb +4 -0
  268. data/test/models/categorization.rb +5 -0
  269. data/test/models/category.rb +34 -0
  270. data/test/models/citation.rb +6 -0
  271. data/test/models/club.rb +13 -0
  272. data/test/models/column_name.rb +3 -0
  273. data/test/models/comment.rb +29 -0
  274. data/test/models/company.rb +171 -0
  275. data/test/models/company_in_module.rb +61 -0
  276. data/test/models/computer.rb +3 -0
  277. data/test/models/contact.rb +16 -0
  278. data/test/models/contract.rb +5 -0
  279. data/test/models/course.rb +3 -0
  280. data/test/models/customer.rb +73 -0
  281. data/test/models/default.rb +2 -0
  282. data/test/models/developer.rb +101 -0
  283. data/test/models/edge.rb +5 -0
  284. data/test/models/entrant.rb +3 -0
  285. data/test/models/essay.rb +3 -0
  286. data/test/models/event.rb +3 -0
  287. data/test/models/guid.rb +2 -0
  288. data/test/models/item.rb +7 -0
  289. data/test/models/job.rb +5 -0
  290. data/test/models/joke.rb +3 -0
  291. data/test/models/keyboard.rb +3 -0
  292. data/test/models/legacy_thing.rb +3 -0
  293. data/test/models/matey.rb +4 -0
  294. data/test/models/member.rb +12 -0
  295. data/test/models/member_detail.rb +5 -0
  296. data/test/models/member_type.rb +3 -0
  297. data/test/models/membership.rb +9 -0
  298. data/test/models/minimalistic.rb +2 -0
  299. data/test/models/mixed_case_monkey.rb +3 -0
  300. data/test/models/movie.rb +5 -0
  301. data/test/models/order.rb +4 -0
  302. data/test/models/organization.rb +6 -0
  303. data/test/models/owner.rb +5 -0
  304. data/test/models/parrot.rb +16 -0
  305. data/test/models/person.rb +16 -0
  306. data/test/models/pet.rb +5 -0
  307. data/test/models/pirate.rb +70 -0
  308. data/test/models/post.rb +100 -0
  309. data/test/models/price_estimate.rb +3 -0
  310. data/test/models/project.rb +30 -0
  311. data/test/models/reader.rb +4 -0
  312. data/test/models/reference.rb +4 -0
  313. data/test/models/reply.rb +46 -0
  314. data/test/models/ship.rb +10 -0
  315. data/test/models/ship_part.rb +5 -0
  316. data/test/models/sponsor.rb +4 -0
  317. data/test/models/subject.rb +4 -0
  318. data/test/models/subscriber.rb +8 -0
  319. data/test/models/subscription.rb +4 -0
  320. data/test/models/tag.rb +7 -0
  321. data/test/models/tagging.rb +10 -0
  322. data/test/models/task.rb +3 -0
  323. data/test/models/topic.rb +80 -0
  324. data/test/models/toy.rb +6 -0
  325. data/test/models/treasure.rb +8 -0
  326. data/test/models/vertex.rb +9 -0
  327. data/test/models/warehouse_thing.rb +5 -0
  328. data/test/schema/mysql_specific_schema.rb +24 -0
  329. data/test/schema/postgresql_specific_schema.rb +114 -0
  330. data/test/schema/schema.rb +493 -0
  331. data/test/schema/schema2.rb +6 -0
  332. data/test/schema/sqlite_specific_schema.rb +25 -0
  333. metadata +420 -0
@@ -0,0 +1,566 @@
1
+ module ActiveRecord
2
+ class IrreversibleMigration < ActiveRecordError#:nodoc:
3
+ end
4
+
5
+ class DuplicateMigrationVersionError < ActiveRecordError#:nodoc:
6
+ def initialize(version)
7
+ super("Multiple migrations have the version number #{version}")
8
+ end
9
+ end
10
+
11
+ class DuplicateMigrationNameError < ActiveRecordError#:nodoc:
12
+ def initialize(name)
13
+ super("Multiple migrations have the name #{name}")
14
+ end
15
+ end
16
+
17
+ class UnknownMigrationVersionError < ActiveRecordError #:nodoc:
18
+ def initialize(version)
19
+ super("No migration with version number #{version}")
20
+ end
21
+ end
22
+
23
+ class IllegalMigrationNameError < ActiveRecordError#:nodoc:
24
+ def initialize(name)
25
+ super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed)")
26
+ end
27
+ end
28
+
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.
34
+ #
35
+ # Example of a simple migration:
36
+ #
37
+ # class AddSsl < ActiveRecord::Migration
38
+ # def self.up
39
+ # add_column :accounts, :ssl_enabled, :boolean, :default => 1
40
+ # end
41
+ #
42
+ # def self.down
43
+ # remove_column :accounts, :ssl_enabled
44
+ # end
45
+ # end
46
+ #
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.
51
+ #
52
+ # Example of a more complex migration that also needs to initialize data:
53
+ #
54
+ # class AddSystemSettings < ActiveRecord::Migration
55
+ # def self.up
56
+ # create_table :system_settings do |t|
57
+ # t.string :name
58
+ # t.string :label
59
+ # t.text :value
60
+ # t.string :type
61
+ # t.integer :position
62
+ # end
63
+ #
64
+ # SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1
65
+ # end
66
+ #
67
+ # def self.down
68
+ # drop_table :system_settings
69
+ # end
70
+ # end
71
+ #
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.
75
+ #
76
+ # == Available transformations
77
+ #
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.
81
+ # * <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+
84
+ # 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+.
96
+ #
97
+ # == Irreversible transformations
98
+ #
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.
101
+ #
102
+ # == Running migrations from within Rails
103
+ #
104
+ # The Rails package has several tools to help create and apply migrations.
105
+ #
106
+ # To generate a new migration, you can use
107
+ # script/generate migration MyNewMigration
108
+ #
109
+ # 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.
112
+ #
113
+ # You may then edit the <tt>self.up</tt> and <tt>self.down</tt> methods of
114
+ # MyNewMigration.
115
+ #
116
+ # 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
+ #
119
+ # This will generate the file <tt>nnn_add_fieldname_to_tablename</tt>, which will look like this:
120
+ # class AddFieldnameToTablename < ActiveRecord::Migration
121
+ # def self.up
122
+ # add_column :tablenames, :fieldname, :string
123
+ # end
124
+ #
125
+ # def self.down
126
+ # remove_column :tablenames, :fieldname
127
+ # end
128
+ # end
129
+ #
130
+ # To run migrations against the currently configured database, use
131
+ # <tt>rake db:migrate</tt>. This will update the database by running all of the
132
+ # pending migrations, creating the <tt>schema_migrations</tt> table
133
+ # (see "About the schema_migrations table" section below) if missing. It will also
134
+ # invoke the db:schema:dump task, which will update your db/schema.rb file
135
+ # to match the structure of your database.
136
+ #
137
+ # To roll the database back to a previous migration version, use
138
+ # <tt>rake db:migrate VERSION=X</tt> where <tt>X</tt> is the version to which
139
+ # you wish to downgrade. If any of the migrations throw an
140
+ # <tt>ActiveRecord::IrreversibleMigration</tt> exception, that step will fail and you'll
141
+ # have some manual work to do.
142
+ #
143
+ # == Database support
144
+ #
145
+ # Migrations are currently supported in MySQL, PostgreSQL, SQLite,
146
+ # SQL Server, Sybase, and Oracle (all supported databases except DB2).
147
+ #
148
+ # == More examples
149
+ #
150
+ # Not all migrations change the schema. Some just fix the data:
151
+ #
152
+ # class RemoveEmptyTags < ActiveRecord::Migration
153
+ # def self.up
154
+ # Tag.find(:all).each { |tag| tag.destroy if tag.pages.empty? }
155
+ # end
156
+ #
157
+ # def self.down
158
+ # # not much we can do to restore deleted data
159
+ # raise ActiveRecord::IrreversibleMigration, "Can't recover the deleted tags"
160
+ # end
161
+ # end
162
+ #
163
+ # Others remove columns when they migrate up instead of down:
164
+ #
165
+ # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration
166
+ # def self.up
167
+ # remove_column :items, :incomplete_items_count
168
+ # remove_column :items, :completed_items_count
169
+ # end
170
+ #
171
+ # def self.down
172
+ # add_column :items, :incomplete_items_count
173
+ # add_column :items, :completed_items_count
174
+ # end
175
+ # end
176
+ #
177
+ # And sometimes you need to do something in SQL not abstracted directly by migrations:
178
+ #
179
+ # class MakeJoinUnique < ActiveRecord::Migration
180
+ # def self.up
181
+ # execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
182
+ # end
183
+ #
184
+ # def self.down
185
+ # execute "ALTER TABLE `pages_linked_pages` DROP INDEX `page_id_linked_page_id`"
186
+ # end
187
+ # end
188
+ #
189
+ # == Using a model after changing its table
190
+ #
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:
194
+ #
195
+ # class AddPeopleSalary < ActiveRecord::Migration
196
+ # def self.up
197
+ # add_column :people, :salary, :integer
198
+ # Person.reset_column_information
199
+ # Person.find(:all).each do |p|
200
+ # p.update_attribute :salary, SalaryCalculator.compute(p)
201
+ # end
202
+ # end
203
+ # end
204
+ #
205
+ # == Controlling verbosity
206
+ #
207
+ # By default, migrations will describe the actions they are taking, writing
208
+ # them to the console as they happen, along with benchmarks describing how
209
+ # long each step took.
210
+ #
211
+ # You can quiet them down by setting ActiveRecord::Migration.verbose = false.
212
+ #
213
+ # You can also insert your own messages and benchmarks by using the +say_with_time+
214
+ # method:
215
+ #
216
+ # def self.up
217
+ # ...
218
+ # say_with_time "Updating salaries..." do
219
+ # Person.find(:all).each do |p|
220
+ # p.update_attribute :salary, SalaryCalculator.compute(p)
221
+ # end
222
+ # end
223
+ # ...
224
+ # end
225
+ #
226
+ # The phrase "Updating salaries..." would then be printed, along with the
227
+ # benchmark for the block when the block completes.
228
+ #
229
+ # == About the schema_migrations table
230
+ #
231
+ # Rails versions 2.0 and prior used to create a table called
232
+ # <tt>schema_info</tt> when using migrations. This table contained the
233
+ # version of the schema as of the last applied migration.
234
+ #
235
+ # Starting with Rails 2.1, the <tt>schema_info</tt> table is
236
+ # (automatically) replaced by the <tt>schema_migrations</tt> table, which
237
+ # contains the version numbers of all the migrations applied.
238
+ #
239
+ # As a result, it is now possible to add migration files that are numbered
240
+ # lower than the current schema version: when migrating up, those
241
+ # never-applied "interleaved" migrations will be automatically applied, and
242
+ # when migrating down, never-applied "interleaved" migrations will be skipped.
243
+ #
244
+ # == Timestamped Migrations
245
+ #
246
+ # By default, Rails generates migrations that look like:
247
+ #
248
+ # 20080717013526_your_migration_name.rb
249
+ #
250
+ # The prefix is a generation timestamp (in UTC).
251
+ #
252
+ # If you'd prefer to use numeric prefixes, you can turn timestamped migrations
253
+ # off by setting:
254
+ #
255
+ # config.active_record.timestamped_migrations = false
256
+ #
257
+ # In environment.rb.
258
+ #
259
+ class Migration
260
+ @@verbose = true
261
+ cattr_accessor :verbose
262
+
263
+ class << self
264
+ def up_with_benchmarks #:nodoc:
265
+ migrate(:up)
266
+ end
267
+
268
+ def down_with_benchmarks #:nodoc:
269
+ migrate(:down)
270
+ end
271
+
272
+ # Execute this migration in the named direction
273
+ def migrate(direction)
274
+ return unless respond_to?(direction)
275
+
276
+ case direction
277
+ when :up then announce "migrating"
278
+ when :down then announce "reverting"
279
+ end
280
+
281
+ result = nil
282
+ time = Benchmark.measure { result = send("#{direction}_without_benchmarks") }
283
+
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
288
+
289
+ result
290
+ end
291
+
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
297
+
298
+ begin
299
+ @ignore_new_methods = true
300
+
301
+ case sym
302
+ when :up, :down
303
+ klass = (class << self; self; end)
304
+ klass.send(:alias_method_chain, sym, "benchmarks")
305
+ end
306
+ ensure
307
+ @ignore_new_methods = false
308
+ end
309
+ end
310
+
311
+ def write(text="")
312
+ puts(text) if verbose
313
+ end
314
+
315
+ def announce(message)
316
+ text = "#{@version} #{name}: #{message}"
317
+ length = [0, 75 - text.length].max
318
+ write "== %s %s" % [text, "=" * length]
319
+ end
320
+
321
+ def say(message, subitem=false)
322
+ write "#{subitem ? " ->" : "--"} #{message}"
323
+ end
324
+
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
333
+
334
+ def suppress_messages
335
+ save, self.verbose = verbose, false
336
+ yield
337
+ ensure
338
+ self.verbose = save
339
+ end
340
+
341
+ def connection
342
+ ActiveRecord::Base.connection
343
+ end
344
+
345
+ def method_missing(method, *arguments, &block)
346
+ arg_list = arguments.map(&:inspect) * ', '
347
+
348
+ say_with_time "#{method}(#{arg_list})" do
349
+ unless arguments.empty? || method == :execute
350
+ arguments[0] = Migrator.proper_table_name(arguments.first)
351
+ end
352
+ connection.send(method, *arguments, &block)
353
+ end
354
+ end
355
+ end
356
+ end
357
+
358
+ # MigrationProxy is used to defer loading of the actual migration classes
359
+ # until they are needed
360
+ class MigrationProxy
361
+
362
+ attr_accessor :name, :version, :filename
363
+
364
+ delegate :migrate, :announce, :write, :to=>:migration
365
+
366
+ private
367
+
368
+ def migration
369
+ @migration ||= load_migration
370
+ end
371
+
372
+ def load_migration
373
+ load(filename)
374
+ name.constantize
375
+ end
376
+
377
+ end
378
+
379
+ class Migrator#:nodoc:
380
+ class << self
381
+ def migrate(migrations_path, target_version = nil)
382
+ case
383
+ when target_version.nil? then up(migrations_path, target_version)
384
+ when current_version > target_version then down(migrations_path, target_version)
385
+ else up(migrations_path, target_version)
386
+ end
387
+ end
388
+
389
+ def rollback(migrations_path, steps=1)
390
+ migrator = self.new(:down, migrations_path)
391
+ start_index = migrator.migrations.index(migrator.current_migration)
392
+
393
+ return unless start_index
394
+
395
+ finish = migrator.migrations[start_index + steps]
396
+ down(migrations_path, finish ? finish.version : 0)
397
+ end
398
+
399
+ def up(migrations_path, target_version = nil)
400
+ self.new(:up, migrations_path, target_version).migrate
401
+ end
402
+
403
+ def down(migrations_path, target_version = nil)
404
+ self.new(:down, migrations_path, target_version).migrate
405
+ end
406
+
407
+ def run(direction, migrations_path, target_version)
408
+ self.new(direction, migrations_path, target_version).run
409
+ end
410
+
411
+ def schema_migrations_table_name
412
+ Base.table_name_prefix + 'schema_migrations' + Base.table_name_suffix
413
+ end
414
+
415
+ def get_all_versions
416
+ Base.connection.select_values("SELECT version FROM #{schema_migrations_table_name}").map(&:to_i).sort
417
+ end
418
+
419
+ def current_version
420
+ sm_table = schema_migrations_table_name
421
+ if Base.connection.table_exists?(sm_table)
422
+ get_all_versions.max || 0
423
+ else
424
+ 0
425
+ end
426
+ end
427
+
428
+ def proper_table_name(name)
429
+ # Use the Active Record objects own table_name, or pre/suffix from ActiveRecord::Base if name is a symbol/string
430
+ name.table_name rescue "#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}"
431
+ end
432
+ end
433
+
434
+ def initialize(direction, migrations_path, target_version = nil)
435
+ raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?
436
+ Base.connection.initialize_schema_migrations_table
437
+ @direction, @migrations_path, @target_version = direction, migrations_path, target_version
438
+ end
439
+
440
+ def current_version
441
+ migrated.last || 0
442
+ end
443
+
444
+ def current_migration
445
+ migrations.detect { |m| m.version == current_version }
446
+ end
447
+
448
+ def run
449
+ target = migrations.detect { |m| m.version == @target_version }
450
+ raise UnknownMigrationVersionError.new(@target_version) if target.nil?
451
+ unless (up? && migrated.include?(target.version.to_i)) || (down? && !migrated.include?(target.version.to_i))
452
+ target.migrate(@direction)
453
+ record_version_state_after_migrating(target.version)
454
+ end
455
+ end
456
+
457
+ def migrate
458
+ current = migrations.detect { |m| m.version == current_version }
459
+ target = migrations.detect { |m| m.version == @target_version }
460
+
461
+ if target.nil? && !@target_version.nil? && @target_version > 0
462
+ raise UnknownMigrationVersionError.new(@target_version)
463
+ end
464
+
465
+ start = up? ? 0 : (migrations.index(current) || 0)
466
+ finish = migrations.index(target) || migrations.size - 1
467
+ runnable = migrations[start..finish]
468
+
469
+ # skip the last migration if we're headed down, but not ALL the way down
470
+ runnable.pop if down? && !target.nil?
471
+
472
+ runnable.each do |migration|
473
+ Base.logger.info "Migrating to #{migration.name} (#{migration.version})"
474
+
475
+ # On our way up, we skip migrating the ones we've already migrated
476
+ next if up? && migrated.include?(migration.version.to_i)
477
+
478
+ # On our way down, we skip reverting the ones we've never migrated
479
+ if down? && !migrated.include?(migration.version.to_i)
480
+ migration.announce 'never migrated, skipping'; migration.write
481
+ next
482
+ end
483
+
484
+ begin
485
+ ddl_transaction do
486
+ migration.migrate(@direction)
487
+ record_version_state_after_migrating(migration.version)
488
+ end
489
+ rescue => e
490
+ canceled_msg = Base.connection.supports_ddl_transactions? ? "this and " : ""
491
+ raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace
492
+ end
493
+ end
494
+ end
495
+
496
+ def migrations
497
+ @migrations ||= begin
498
+ files = Dir["#{@migrations_path}/[0-9]*_*.rb"]
499
+
500
+ migrations = files.inject([]) do |klasses, file|
501
+ version, name = file.scan(/([0-9]+)_([_a-z0-9]*).rb/).first
502
+
503
+ raise IllegalMigrationNameError.new(file) unless version
504
+ version = version.to_i
505
+
506
+ if klasses.detect { |m| m.version == version }
507
+ raise DuplicateMigrationVersionError.new(version)
508
+ end
509
+
510
+ if klasses.detect { |m| m.name == name.camelize }
511
+ raise DuplicateMigrationNameError.new(name.camelize)
512
+ end
513
+
514
+ klasses << returning(MigrationProxy.new) do |migration|
515
+ migration.name = name.camelize
516
+ migration.version = version
517
+ migration.filename = file
518
+ end
519
+ end
520
+
521
+ migrations = migrations.sort_by(&:version)
522
+ down? ? migrations.reverse : migrations
523
+ end
524
+ end
525
+
526
+ def pending_migrations
527
+ already_migrated = migrated
528
+ migrations.reject { |m| already_migrated.include?(m.version.to_i) }
529
+ end
530
+
531
+ def migrated
532
+ @migrated_versions ||= self.class.get_all_versions
533
+ end
534
+
535
+ private
536
+ def record_version_state_after_migrating(version)
537
+ sm_table = self.class.schema_migrations_table_name
538
+
539
+ @migrated_versions ||= []
540
+ if down?
541
+ @migrated_versions.delete(version.to_i)
542
+ Base.connection.update("DELETE FROM #{sm_table} WHERE version = '#{version}'")
543
+ else
544
+ @migrated_versions.push(version.to_i).sort!
545
+ Base.connection.insert("INSERT INTO #{sm_table} (version) VALUES ('#{version}')")
546
+ end
547
+ end
548
+
549
+ def up?
550
+ @direction == :up
551
+ end
552
+
553
+ def down?
554
+ @direction == :down
555
+ end
556
+
557
+ # Wrap the migration in a transaction only if supported by the adapter.
558
+ def ddl_transaction(&block)
559
+ if Base.connection.supports_ddl_transactions?
560
+ Base.transaction { block.call }
561
+ else
562
+ block.call
563
+ end
564
+ end
565
+ end
566
+ end