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,1523 @@
1
+ require "cases/helper"
2
+ require 'bigdecimal/util'
3
+
4
+ require 'models/person'
5
+ require 'models/topic'
6
+ require 'models/developer'
7
+
8
+ require MIGRATIONS_ROOT + "/valid/1_people_have_last_names"
9
+ require MIGRATIONS_ROOT + "/valid/2_we_need_reminders"
10
+ require MIGRATIONS_ROOT + "/decimal/1_give_me_big_numbers"
11
+ require MIGRATIONS_ROOT + "/interleaved/pass_3/2_i_raise_on_down"
12
+
13
+ if ActiveRecord::Base.connection.supports_migrations?
14
+ class BigNumber < ActiveRecord::Base; end
15
+
16
+ class Reminder < ActiveRecord::Base; end
17
+
18
+ class ActiveRecord::Migration
19
+ class <<self
20
+ attr_accessor :message_count
21
+ def puts(text="")
22
+ self.message_count ||= 0
23
+ self.message_count += 1
24
+ end
25
+ end
26
+ end
27
+
28
+ class MigrationTableAndIndexTest < ActiveRecord::TestCase
29
+ def test_add_schema_info_respects_prefix_and_suffix
30
+ conn = ActiveRecord::Base.connection
31
+
32
+ conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
33
+ ActiveRecord::Base.table_name_prefix = 'foo_'
34
+ ActiveRecord::Base.table_name_suffix = '_bar'
35
+ conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
36
+
37
+ conn.initialize_schema_migrations_table
38
+
39
+ assert_equal "foo_unique_schema_migrations_bar", conn.indexes(ActiveRecord::Migrator.schema_migrations_table_name)[0][:name]
40
+ ensure
41
+ ActiveRecord::Base.table_name_prefix = ""
42
+ ActiveRecord::Base.table_name_suffix = ""
43
+ end
44
+ end
45
+
46
+ class MigrationTest < ActiveRecord::TestCase
47
+ self.use_transactional_fixtures = false
48
+
49
+ fixtures :people
50
+
51
+ def setup
52
+ ActiveRecord::Migration.verbose = true
53
+ PeopleHaveLastNames.message_count = 0
54
+ end
55
+
56
+ def teardown
57
+ ActiveRecord::Base.connection.initialize_schema_migrations_table
58
+ ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::Migrator.schema_migrations_table_name}"
59
+
60
+ %w(reminders people_reminders prefix_reminders_suffix).each do |table|
61
+ Reminder.connection.drop_table(table) rescue nil
62
+ end
63
+ Reminder.reset_column_information
64
+
65
+ %w(last_name key bio age height wealth birthday favorite_day
66
+ moment_of_truth male administrator funny).each do |column|
67
+ Person.connection.remove_column('people', column) rescue nil
68
+ end
69
+ Person.connection.remove_column("people", "first_name") rescue nil
70
+ Person.connection.remove_column("people", "middle_name") rescue nil
71
+ Person.connection.add_column("people", "first_name", :string, :limit => 40)
72
+ Person.reset_column_information
73
+ end
74
+
75
+ def test_add_index
76
+ # Limit size of last_name and key columns to support Firebird index limitations
77
+ Person.connection.add_column "people", "last_name", :string, :limit => 100
78
+ Person.connection.add_column "people", "key", :string, :limit => 100
79
+ Person.connection.add_column "people", "administrator", :boolean
80
+
81
+ assert_nothing_raised { Person.connection.add_index("people", "last_name") }
82
+ assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
83
+
84
+ # Orcl nds shrt indx nms. Sybs 2.
85
+ # OpenBase does not have named indexes. You must specify a single column name
86
+ unless current_adapter?(:OracleAdapter, :SybaseAdapter, :OpenBaseAdapter)
87
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
88
+ assert_nothing_raised { Person.connection.remove_index("people", :column => ["last_name", "first_name"]) }
89
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
90
+ assert_nothing_raised { Person.connection.remove_index("people", :name => "index_people_on_last_name_and_first_name") }
91
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
92
+ assert_nothing_raised { Person.connection.remove_index("people", "last_name_and_first_name") }
93
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
94
+ assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
95
+ end
96
+
97
+ # quoting
98
+ # Note: changed index name from "key" to "key_idx" since "key" is a Firebird reserved word
99
+ # OpenBase does not have named indexes. You must specify a single column name
100
+ unless current_adapter?(:OpenBaseAdapter)
101
+ Person.update_all "#{Person.connection.quote_column_name 'key'}=#{Person.connection.quote_column_name 'id'}" #some databases (including sqlite2 won't add a unique index if existing data non unique)
102
+ assert_nothing_raised { Person.connection.add_index("people", ["key"], :name => "key_idx", :unique => true) }
103
+ assert_nothing_raised { Person.connection.remove_index("people", :name => "key_idx", :unique => true) }
104
+ end
105
+
106
+ # Sybase adapter does not support indexes on :boolean columns
107
+ # OpenBase does not have named indexes. You must specify a single column
108
+ unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
109
+ assert_nothing_raised { Person.connection.add_index("people", %w(last_name first_name administrator), :name => "named_admin") }
110
+ assert_nothing_raised { Person.connection.remove_index("people", :name => "named_admin") }
111
+ end
112
+ end
113
+
114
+ def testing_table_with_only_foo_attribute
115
+ Person.connection.create_table :testings, :id => false do |t|
116
+ t.column :foo, :string
117
+ end
118
+
119
+ yield Person.connection
120
+ ensure
121
+ Person.connection.drop_table :testings rescue nil
122
+ end
123
+ protected :testing_table_with_only_foo_attribute
124
+
125
+ def test_create_table_without_id
126
+ testing_table_with_only_foo_attribute do |connection|
127
+ assert_equal connection.columns(:testings).size, 1
128
+ end
129
+ end
130
+
131
+ def test_add_column_with_primary_key_attribute
132
+ testing_table_with_only_foo_attribute do |connection|
133
+ assert_nothing_raised { connection.add_column :testings, :id, :primary_key }
134
+ assert_equal connection.columns(:testings).size, 2
135
+ end
136
+ end
137
+
138
+ def test_create_table_adds_id
139
+ Person.connection.create_table :testings do |t|
140
+ t.column :foo, :string
141
+ end
142
+
143
+ assert_equal %w(foo id),
144
+ Person.connection.columns(:testings).map { |c| c.name }.sort
145
+ ensure
146
+ Person.connection.drop_table :testings rescue nil
147
+ end
148
+
149
+ def test_create_table_with_not_null_column
150
+ assert_nothing_raised do
151
+ Person.connection.create_table :testings do |t|
152
+ t.column :foo, :string, :null => false
153
+ end
154
+ end
155
+
156
+ assert_raise(ActiveRecord::StatementInvalid) do
157
+ Person.connection.execute "insert into testings (foo) values (NULL)"
158
+ end
159
+ ensure
160
+ Person.connection.drop_table :testings rescue nil
161
+ end
162
+
163
+ def test_create_table_with_defaults
164
+ # MySQL doesn't allow defaults on TEXT or BLOB columns.
165
+ mysql = current_adapter?(:MysqlAdapter)
166
+
167
+ Person.connection.create_table :testings do |t|
168
+ t.column :one, :string, :default => "hello"
169
+ t.column :two, :boolean, :default => true
170
+ t.column :three, :boolean, :default => false
171
+ t.column :four, :integer, :default => 1
172
+ t.column :five, :text, :default => "hello" unless mysql
173
+ end
174
+
175
+ columns = Person.connection.columns(:testings)
176
+ one = columns.detect { |c| c.name == "one" }
177
+ two = columns.detect { |c| c.name == "two" }
178
+ three = columns.detect { |c| c.name == "three" }
179
+ four = columns.detect { |c| c.name == "four" }
180
+ five = columns.detect { |c| c.name == "five" } unless mysql
181
+
182
+ assert_equal "hello", one.default
183
+ assert_equal true, two.default
184
+ assert_equal false, three.default
185
+ assert_equal 1, four.default
186
+ assert_equal "hello", five.default unless mysql
187
+
188
+ ensure
189
+ Person.connection.drop_table :testings rescue nil
190
+ end
191
+
192
+ def test_create_table_with_limits
193
+ assert_nothing_raised do
194
+ Person.connection.create_table :testings do |t|
195
+ t.column :foo, :string, :limit => 255
196
+
197
+ t.column :default_int, :integer
198
+
199
+ t.column :one_int, :integer, :limit => 1
200
+ t.column :four_int, :integer, :limit => 4
201
+ t.column :eight_int, :integer, :limit => 8
202
+ t.column :eleven_int, :integer, :limit => 11
203
+ end
204
+ end
205
+
206
+ columns = Person.connection.columns(:testings)
207
+ foo = columns.detect { |c| c.name == "foo" }
208
+ assert_equal 255, foo.limit
209
+
210
+ default = columns.detect { |c| c.name == "default_int" }
211
+ one = columns.detect { |c| c.name == "one_int" }
212
+ four = columns.detect { |c| c.name == "four_int" }
213
+ eight = columns.detect { |c| c.name == "eight_int" }
214
+ eleven = columns.detect { |c| c.name == "eleven_int" }
215
+
216
+ if current_adapter?(:PostgreSQLAdapter)
217
+ assert_equal 'integer', default.sql_type
218
+ assert_equal 'smallint', one.sql_type
219
+ assert_equal 'integer', four.sql_type
220
+ assert_equal 'bigint', eight.sql_type
221
+ assert_equal 'integer', eleven.sql_type
222
+ elsif current_adapter?(:MysqlAdapter)
223
+ assert_match 'int(11)', default.sql_type
224
+ assert_match 'tinyint', one.sql_type
225
+ assert_match 'int', four.sql_type
226
+ assert_match 'bigint', eight.sql_type
227
+ assert_match 'int(11)', eleven.sql_type
228
+ elsif current_adapter?(:OracleAdapter)
229
+ assert_equal 'NUMBER(38)', default.sql_type
230
+ assert_equal 'NUMBER(1)', one.sql_type
231
+ assert_equal 'NUMBER(4)', four.sql_type
232
+ assert_equal 'NUMBER(8)', eight.sql_type
233
+ end
234
+ ensure
235
+ Person.connection.drop_table :testings rescue nil
236
+ end
237
+
238
+ def test_create_table_with_primary_key_prefix_as_table_name_with_underscore
239
+ ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore
240
+
241
+ Person.connection.create_table :testings do |t|
242
+ t.column :foo, :string
243
+ end
244
+
245
+ assert_equal %w(foo testing_id), Person.connection.columns(:testings).map { |c| c.name }.sort
246
+ ensure
247
+ Person.connection.drop_table :testings rescue nil
248
+ ActiveRecord::Base.primary_key_prefix_type = nil
249
+ end
250
+
251
+ def test_create_table_with_primary_key_prefix_as_table_name
252
+ ActiveRecord::Base.primary_key_prefix_type = :table_name
253
+
254
+ Person.connection.create_table :testings do |t|
255
+ t.column :foo, :string
256
+ end
257
+
258
+ assert_equal %w(foo testingid), Person.connection.columns(:testings).map { |c| c.name }.sort
259
+ ensure
260
+ Person.connection.drop_table :testings rescue nil
261
+ ActiveRecord::Base.primary_key_prefix_type = nil
262
+ end
263
+
264
+ def test_create_table_with_force_true_does_not_drop_nonexisting_table
265
+ if Person.connection.table_exists?(:testings2)
266
+ Person.connection.drop_table :testings2
267
+ end
268
+
269
+ # using a copy as we need the drop_table method to
270
+ # continue to work for the ensure block of the test
271
+ temp_conn = Person.connection.dup
272
+ temp_conn.expects(:drop_table).never
273
+ temp_conn.create_table :testings2, :force => true do |t|
274
+ t.column :foo, :string
275
+ end
276
+ ensure
277
+ Person.connection.drop_table :testings2 rescue nil
278
+ end
279
+
280
+ def test_create_table_with_timestamps_should_create_datetime_columns
281
+ table_name = :testings
282
+
283
+ Person.connection.create_table table_name do |t|
284
+ t.timestamps
285
+ end
286
+ created_columns = Person.connection.columns(table_name)
287
+
288
+ created_at_column = created_columns.detect {|c| c.name == 'created_at' }
289
+ updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
290
+
291
+ assert created_at_column.null
292
+ assert updated_at_column.null
293
+ ensure
294
+ Person.connection.drop_table table_name rescue nil
295
+ end
296
+
297
+ def test_create_table_with_timestamps_should_create_datetime_columns_with_options
298
+ table_name = :testings
299
+
300
+ Person.connection.create_table table_name do |t|
301
+ t.timestamps :null => false
302
+ end
303
+ created_columns = Person.connection.columns(table_name)
304
+
305
+ created_at_column = created_columns.detect {|c| c.name == 'created_at' }
306
+ updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
307
+
308
+ assert !created_at_column.null
309
+ assert !updated_at_column.null
310
+ ensure
311
+ Person.connection.drop_table table_name rescue nil
312
+ end
313
+
314
+ # Sybase, and SQLite3 will not allow you to add a NOT NULL
315
+ # column to a table without a default value.
316
+ unless current_adapter?(:SybaseAdapter, :SQLiteAdapter)
317
+ def test_add_column_not_null_without_default
318
+ Person.connection.create_table :testings do |t|
319
+ t.column :foo, :string
320
+ end
321
+ Person.connection.add_column :testings, :bar, :string, :null => false
322
+
323
+ assert_raise(ActiveRecord::StatementInvalid) do
324
+ Person.connection.execute "insert into testings (foo, bar) values ('hello', NULL)"
325
+ end
326
+ ensure
327
+ Person.connection.drop_table :testings rescue nil
328
+ end
329
+ end
330
+
331
+ def test_add_column_not_null_with_default
332
+ Person.connection.create_table :testings do |t|
333
+ t.column :foo, :string
334
+ end
335
+
336
+ con = Person.connection
337
+ Person.connection.enable_identity_insert("testings", true) if current_adapter?(:SybaseAdapter)
338
+ Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}) values (1, 'hello')"
339
+ Person.connection.enable_identity_insert("testings", false) if current_adapter?(:SybaseAdapter)
340
+ assert_nothing_raised {Person.connection.add_column :testings, :bar, :string, :null => false, :default => "default" }
341
+
342
+ assert_raise(ActiveRecord::StatementInvalid) do
343
+ unless current_adapter?(:OpenBaseAdapter)
344
+ Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) values (2, 'hello', NULL)"
345
+ else
346
+ Person.connection.insert("INSERT INTO testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) VALUES (2, 'hello', NULL)",
347
+ "Testing Insert","id",2)
348
+ end
349
+ end
350
+ ensure
351
+ Person.connection.drop_table :testings rescue nil
352
+ end
353
+
354
+ # We specifically do a manual INSERT here, and then test only the SELECT
355
+ # functionality. This allows us to more easily catch INSERT being broken,
356
+ # but SELECT actually working fine.
357
+ def test_native_decimal_insert_manual_vs_automatic
358
+ correct_value = '0012345678901234567890.0123456789'.to_d
359
+
360
+ Person.delete_all
361
+ Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
362
+ Person.reset_column_information
363
+
364
+ # Do a manual insertion
365
+ if current_adapter?(:OracleAdapter)
366
+ Person.connection.execute "insert into people (id, wealth) values (people_seq.nextval, 12345678901234567890.0123456789)"
367
+ elsif current_adapter?(:OpenBaseAdapter) || (current_adapter?(:MysqlAdapter) && Mysql.client_version < 50003) #before mysql 5.0.3 decimals stored as strings
368
+ Person.connection.execute "insert into people (wealth) values ('12345678901234567890.0123456789')"
369
+ else
370
+ Person.connection.execute "insert into people (wealth) values (12345678901234567890.0123456789)"
371
+ end
372
+
373
+ # SELECT
374
+ row = Person.find(:first)
375
+ assert_kind_of BigDecimal, row.wealth
376
+
377
+ # If this assert fails, that means the SELECT is broken!
378
+ unless current_adapter?(:SQLite3Adapter)
379
+ assert_equal correct_value, row.wealth
380
+ end
381
+
382
+ # Reset to old state
383
+ Person.delete_all
384
+
385
+ # Now use the Rails insertion
386
+ assert_nothing_raised { Person.create :wealth => BigDecimal.new("12345678901234567890.0123456789") }
387
+
388
+ # SELECT
389
+ row = Person.find(:first)
390
+ assert_kind_of BigDecimal, row.wealth
391
+
392
+ # If these asserts fail, that means the INSERT (create function, or cast to SQL) is broken!
393
+ unless current_adapter?(:SQLite3Adapter)
394
+ assert_equal correct_value, row.wealth
395
+ end
396
+
397
+ # Reset to old state
398
+ Person.connection.del_column "people", "wealth" rescue nil
399
+ Person.reset_column_information
400
+ end
401
+
402
+ def test_add_column_with_precision_and_scale
403
+ Person.connection.add_column 'people', 'wealth', :decimal, :precision => 9, :scale => 7
404
+ Person.reset_column_information
405
+
406
+ wealth_column = Person.columns_hash['wealth']
407
+ assert_equal 9, wealth_column.precision
408
+ assert_equal 7, wealth_column.scale
409
+ end
410
+
411
+ def test_native_types
412
+ Person.delete_all
413
+ Person.connection.add_column "people", "last_name", :string
414
+ Person.connection.add_column "people", "bio", :text
415
+ Person.connection.add_column "people", "age", :integer
416
+ Person.connection.add_column "people", "height", :float
417
+ Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
418
+ Person.connection.add_column "people", "birthday", :datetime
419
+ Person.connection.add_column "people", "favorite_day", :date
420
+ Person.connection.add_column "people", "moment_of_truth", :datetime
421
+ Person.connection.add_column "people", "male", :boolean
422
+ Person.reset_column_information
423
+
424
+ assert_nothing_raised do
425
+ Person.create :first_name => 'bob', :last_name => 'bobsen',
426
+ :bio => "I was born ....", :age => 18, :height => 1.78,
427
+ :wealth => BigDecimal.new("12345678901234567890.0123456789"),
428
+ :birthday => 18.years.ago, :favorite_day => 10.days.ago,
429
+ :moment_of_truth => "1782-10-10 21:40:18", :male => true
430
+ end
431
+
432
+ bob = Person.find(:first)
433
+ assert_equal 'bob', bob.first_name
434
+ assert_equal 'bobsen', bob.last_name
435
+ assert_equal "I was born ....", bob.bio
436
+ assert_equal 18, bob.age
437
+
438
+ # Test for 30 significent digits (beyond the 16 of float), 10 of them
439
+ # after the decimal place.
440
+
441
+ unless current_adapter?(:SQLite3Adapter)
442
+ assert_equal BigDecimal.new("0012345678901234567890.0123456789"), bob.wealth
443
+ end
444
+
445
+ assert_equal true, bob.male?
446
+
447
+ assert_equal String, bob.first_name.class
448
+ assert_equal String, bob.last_name.class
449
+ assert_equal String, bob.bio.class
450
+ assert_equal Fixnum, bob.age.class
451
+ assert_equal Time, bob.birthday.class
452
+
453
+ if current_adapter?(:OracleAdapter, :SybaseAdapter)
454
+ # Sybase, and Oracle don't differentiate between date/time
455
+ assert_equal Time, bob.favorite_day.class
456
+ else
457
+ assert_equal Date, bob.favorite_day.class
458
+ end
459
+
460
+ # Test DateTime column and defaults, including timezone.
461
+ # FIXME: moment of truth may be Time on 64-bit platforms.
462
+ if bob.moment_of_truth.is_a?(DateTime)
463
+
464
+ with_env_tz 'US/Eastern' do
465
+ assert_equal DateTime.local_offset, bob.moment_of_truth.offset
466
+ assert_not_equal 0, bob.moment_of_truth.offset
467
+ assert_not_equal "Z", bob.moment_of_truth.zone
468
+ # US/Eastern is -5 hours from GMT
469
+ assert_equal Rational(-5, 24), bob.moment_of_truth.offset
470
+ assert_match /\A-05:?00\Z/, bob.moment_of_truth.zone #ruby 1.8.6 uses HH:MM, prior versions use HHMM
471
+ assert_equal DateTime::ITALY, bob.moment_of_truth.start
472
+ end
473
+ end
474
+
475
+ assert_equal TrueClass, bob.male?.class
476
+ assert_kind_of BigDecimal, bob.wealth
477
+ end
478
+
479
+ if current_adapter?(:MysqlAdapter)
480
+ def test_unabstracted_database_dependent_types
481
+ Person.delete_all
482
+
483
+ ActiveRecord::Migration.add_column :people, :intelligence_quotient, :tinyint
484
+ Person.reset_column_information
485
+ assert_match /tinyint/, Person.columns_hash['intelligence_quotient'].sql_type
486
+ ensure
487
+ ActiveRecord::Migration.remove_column :people, :intelligence_quotient rescue nil
488
+ end
489
+ end
490
+
491
+ def test_add_remove_single_field_using_string_arguments
492
+ assert !Person.column_methods_hash.include?(:last_name)
493
+
494
+ ActiveRecord::Migration.add_column 'people', 'last_name', :string
495
+
496
+ Person.reset_column_information
497
+ assert Person.column_methods_hash.include?(:last_name)
498
+
499
+ ActiveRecord::Migration.remove_column 'people', 'last_name'
500
+
501
+ Person.reset_column_information
502
+ assert !Person.column_methods_hash.include?(:last_name)
503
+ end
504
+
505
+ def test_add_remove_single_field_using_symbol_arguments
506
+ assert !Person.column_methods_hash.include?(:last_name)
507
+
508
+ ActiveRecord::Migration.add_column :people, :last_name, :string
509
+
510
+ Person.reset_column_information
511
+ assert Person.column_methods_hash.include?(:last_name)
512
+
513
+ ActiveRecord::Migration.remove_column :people, :last_name
514
+
515
+ Person.reset_column_information
516
+ assert !Person.column_methods_hash.include?(:last_name)
517
+ end
518
+
519
+ def test_add_rename
520
+ Person.delete_all
521
+
522
+ begin
523
+ Person.connection.add_column "people", "girlfriend", :string
524
+ Person.reset_column_information
525
+ Person.create :girlfriend => 'bobette'
526
+
527
+ Person.connection.rename_column "people", "girlfriend", "exgirlfriend"
528
+
529
+ Person.reset_column_information
530
+ bob = Person.find(:first)
531
+
532
+ assert_equal "bobette", bob.exgirlfriend
533
+ ensure
534
+ Person.connection.remove_column("people", "girlfriend") rescue nil
535
+ Person.connection.remove_column("people", "exgirlfriend") rescue nil
536
+ end
537
+
538
+ end
539
+
540
+ def test_rename_column_using_symbol_arguments
541
+ begin
542
+ names_before = Person.find(:all).map(&:first_name)
543
+ Person.connection.rename_column :people, :first_name, :nick_name
544
+ Person.reset_column_information
545
+ assert Person.column_names.include?("nick_name")
546
+ assert_equal names_before, Person.find(:all).map(&:nick_name)
547
+ ensure
548
+ Person.connection.remove_column("people","nick_name")
549
+ Person.connection.add_column("people","first_name", :string)
550
+ end
551
+ end
552
+
553
+ def test_rename_column
554
+ begin
555
+ names_before = Person.find(:all).map(&:first_name)
556
+ Person.connection.rename_column "people", "first_name", "nick_name"
557
+ Person.reset_column_information
558
+ assert Person.column_names.include?("nick_name")
559
+ assert_equal names_before, Person.find(:all).map(&:nick_name)
560
+ ensure
561
+ Person.connection.remove_column("people","nick_name")
562
+ Person.connection.add_column("people","first_name", :string)
563
+ end
564
+ end
565
+
566
+ def test_rename_column_preserves_default_value_not_null
567
+ begin
568
+ default_before = Developer.connection.columns("developers").find { |c| c.name == "salary" }.default
569
+ assert_equal 70000, default_before
570
+ Developer.connection.rename_column "developers", "salary", "anual_salary"
571
+ Developer.reset_column_information
572
+ assert Developer.column_names.include?("anual_salary")
573
+ default_after = Developer.connection.columns("developers").find { |c| c.name == "anual_salary" }.default
574
+ assert_equal 70000, default_after
575
+ ensure
576
+ Developer.connection.rename_column "developers", "anual_salary", "salary"
577
+ Developer.reset_column_information
578
+ end
579
+ end
580
+
581
+ def test_rename_nonexistent_column
582
+ ActiveRecord::Base.connection.create_table(:hats) do |table|
583
+ table.column :hat_name, :string, :default => nil
584
+ end
585
+ exception = if current_adapter?(:PostgreSQLAdapter)
586
+ ActiveRecord::StatementInvalid
587
+ else
588
+ ActiveRecord::ActiveRecordError
589
+ end
590
+ assert_raise(exception) do
591
+ Person.connection.rename_column "hats", "nonexistent", "should_fail"
592
+ end
593
+ ensure
594
+ ActiveRecord::Base.connection.drop_table(:hats)
595
+ end
596
+
597
+ def test_rename_column_with_sql_reserved_word
598
+ begin
599
+ assert_nothing_raised { Person.connection.rename_column "people", "first_name", "group" }
600
+ Person.reset_column_information
601
+ assert Person.column_names.include?("group")
602
+ ensure
603
+ Person.connection.remove_column("people", "group") rescue nil
604
+ Person.connection.add_column("people", "first_name", :string) rescue nil
605
+ end
606
+ end
607
+
608
+ def test_rename_column_with_an_index
609
+ ActiveRecord::Base.connection.create_table(:hats) do |table|
610
+ table.column :hat_name, :string, :limit => 100
611
+ table.column :hat_size, :integer
612
+ end
613
+ Person.connection.add_index :hats, :hat_name
614
+ assert_nothing_raised do
615
+ Person.connection.rename_column "hats", "hat_name", "name"
616
+ end
617
+ ensure
618
+ ActiveRecord::Base.connection.drop_table(:hats)
619
+ end
620
+
621
+ def test_remove_column_with_index
622
+ ActiveRecord::Base.connection.create_table(:hats) do |table|
623
+ table.column :hat_name, :string, :limit => 100
624
+ table.column :hat_size, :integer
625
+ end
626
+ ActiveRecord::Base.connection.add_index "hats", "hat_size"
627
+
628
+ assert_nothing_raised { Person.connection.remove_column("hats", "hat_size") }
629
+ ensure
630
+ ActiveRecord::Base.connection.drop_table(:hats)
631
+ end
632
+
633
+ def test_remove_column_with_multi_column_index
634
+ ActiveRecord::Base.connection.create_table(:hats) do |table|
635
+ table.column :hat_name, :string, :limit => 100
636
+ table.column :hat_size, :integer
637
+ table.column :hat_style, :string, :limit => 100
638
+ end
639
+ ActiveRecord::Base.connection.add_index "hats", ["hat_style", "hat_size"], :unique => true
640
+
641
+ assert_nothing_raised { Person.connection.remove_column("hats", "hat_size") }
642
+ ensure
643
+ ActiveRecord::Base.connection.drop_table(:hats)
644
+ end
645
+
646
+ def test_change_type_of_not_null_column
647
+ assert_nothing_raised do
648
+ Topic.connection.change_column "topics", "written_on", :datetime, :null => false
649
+ Topic.reset_column_information
650
+
651
+ Topic.connection.change_column "topics", "written_on", :datetime, :null => false
652
+ Topic.reset_column_information
653
+ end
654
+ end
655
+
656
+ def test_rename_table
657
+ begin
658
+ ActiveRecord::Base.connection.create_table :octopuses do |t|
659
+ t.column :url, :string
660
+ end
661
+ ActiveRecord::Base.connection.rename_table :octopuses, :octopi
662
+
663
+ # Using explicit id in insert for compatibility across all databases
664
+ con = ActiveRecord::Base.connection
665
+ con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
666
+ assert_nothing_raised { con.execute "INSERT INTO octopi (#{con.quote_column_name('id')}, #{con.quote_column_name('url')}) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')" }
667
+ con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
668
+
669
+ assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
670
+
671
+ ensure
672
+ ActiveRecord::Base.connection.drop_table :octopuses rescue nil
673
+ ActiveRecord::Base.connection.drop_table :octopi rescue nil
674
+ end
675
+ end
676
+
677
+ def test_change_column_nullability
678
+ Person.delete_all
679
+ Person.connection.add_column "people", "funny", :boolean
680
+ Person.reset_column_information
681
+ assert Person.columns_hash["funny"].null, "Column 'funny' must initially allow nulls"
682
+ Person.connection.change_column "people", "funny", :boolean, :null => false, :default => true
683
+ Person.reset_column_information
684
+ assert !Person.columns_hash["funny"].null, "Column 'funny' must *not* allow nulls at this point"
685
+ Person.connection.change_column "people", "funny", :boolean, :null => true
686
+ Person.reset_column_information
687
+ assert Person.columns_hash["funny"].null, "Column 'funny' must allow nulls again at this point"
688
+ end
689
+
690
+ def test_rename_table_with_an_index
691
+ begin
692
+ ActiveRecord::Base.connection.create_table :octopuses do |t|
693
+ t.column :url, :string
694
+ end
695
+ ActiveRecord::Base.connection.add_index :octopuses, :url
696
+
697
+ ActiveRecord::Base.connection.rename_table :octopuses, :octopi
698
+
699
+ # Using explicit id in insert for compatibility across all databases
700
+ con = ActiveRecord::Base.connection
701
+ con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
702
+ assert_nothing_raised { con.execute "INSERT INTO octopi (#{con.quote_column_name('id')}, #{con.quote_column_name('url')}) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')" }
703
+ con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
704
+
705
+ assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
706
+ assert ActiveRecord::Base.connection.indexes(:octopi).first.columns.include?("url")
707
+ ensure
708
+ ActiveRecord::Base.connection.drop_table :octopuses rescue nil
709
+ ActiveRecord::Base.connection.drop_table :octopi rescue nil
710
+ end
711
+ end
712
+
713
+ def test_change_column
714
+ Person.connection.add_column 'people', 'age', :integer
715
+ old_columns = Person.connection.columns(Person.table_name, "#{name} Columns")
716
+ assert old_columns.find { |c| c.name == 'age' and c.type == :integer }
717
+
718
+ assert_nothing_raised { Person.connection.change_column "people", "age", :string }
719
+
720
+ new_columns = Person.connection.columns(Person.table_name, "#{name} Columns")
721
+ assert_nil new_columns.find { |c| c.name == 'age' and c.type == :integer }
722
+ assert new_columns.find { |c| c.name == 'age' and c.type == :string }
723
+
724
+ old_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns")
725
+ assert old_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
726
+ assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => false }
727
+ new_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns")
728
+ assert_nil new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
729
+ assert new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == false }
730
+ assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => true }
731
+ end
732
+
733
+ def test_change_column_with_nil_default
734
+ Person.connection.add_column "people", "contributor", :boolean, :default => true
735
+ Person.reset_column_information
736
+ assert Person.new.contributor?
737
+
738
+ assert_nothing_raised { Person.connection.change_column "people", "contributor", :boolean, :default => nil }
739
+ Person.reset_column_information
740
+ assert !Person.new.contributor?
741
+ assert_nil Person.new.contributor
742
+ ensure
743
+ Person.connection.remove_column("people", "contributor") rescue nil
744
+ end
745
+
746
+ def test_change_column_with_new_default
747
+ Person.connection.add_column "people", "administrator", :boolean, :default => true
748
+ Person.reset_column_information
749
+ assert Person.new.administrator?
750
+
751
+ assert_nothing_raised { Person.connection.change_column "people", "administrator", :boolean, :default => false }
752
+ Person.reset_column_information
753
+ assert !Person.new.administrator?
754
+ ensure
755
+ Person.connection.remove_column("people", "administrator") rescue nil
756
+ end
757
+
758
+ def test_change_column_default
759
+ Person.connection.change_column_default "people", "first_name", "Tester"
760
+ Person.reset_column_information
761
+ assert_equal "Tester", Person.new.first_name
762
+ end
763
+
764
+ def test_change_column_quotes_column_names
765
+ Person.connection.create_table :testings do |t|
766
+ t.column :select, :string
767
+ end
768
+
769
+ assert_nothing_raised { Person.connection.change_column :testings, :select, :string, :limit => 10 }
770
+
771
+ assert_nothing_raised { Person.connection.execute "insert into testings (#{Person.connection.quote_column_name('select')}) values ('7 chars')" }
772
+ ensure
773
+ Person.connection.drop_table :testings rescue nil
774
+ end
775
+
776
+ def test_keeping_default_and_notnull_constaint_on_change
777
+ Person.connection.create_table :testings do |t|
778
+ t.column :title, :string
779
+ end
780
+ person_klass = Class.new(Person)
781
+ person_klass.set_table_name 'testings'
782
+
783
+ person_klass.connection.add_column "testings", "wealth", :integer, :null => false, :default => 99
784
+ person_klass.reset_column_information
785
+ assert_equal 99, person_klass.columns_hash["wealth"].default
786
+ assert_equal false, person_klass.columns_hash["wealth"].null
787
+ assert_nothing_raised {person_klass.connection.execute("insert into testings (title) values ('tester')")}
788
+
789
+ # change column default to see that column doesn't lose its not null definition
790
+ person_klass.connection.change_column_default "testings", "wealth", 100
791
+ person_klass.reset_column_information
792
+ assert_equal 100, person_klass.columns_hash["wealth"].default
793
+ assert_equal false, person_klass.columns_hash["wealth"].null
794
+
795
+ # rename column to see that column doesn't lose its not null and/or default definition
796
+ person_klass.connection.rename_column "testings", "wealth", "money"
797
+ person_klass.reset_column_information
798
+ assert_nil person_klass.columns_hash["wealth"]
799
+ assert_equal 100, person_klass.columns_hash["money"].default
800
+ assert_equal false, person_klass.columns_hash["money"].null
801
+
802
+ # change column
803
+ person_klass.connection.change_column "testings", "money", :integer, :null => false, :default => 1000
804
+ person_klass.reset_column_information
805
+ assert_equal 1000, person_klass.columns_hash["money"].default
806
+ assert_equal false, person_klass.columns_hash["money"].null
807
+
808
+ # change column, make it nullable and clear default
809
+ person_klass.connection.change_column "testings", "money", :integer, :null => true, :default => nil
810
+ person_klass.reset_column_information
811
+ assert_nil person_klass.columns_hash["money"].default
812
+ assert_equal true, person_klass.columns_hash["money"].null
813
+
814
+ # change_column_null, make it not nullable and set null values to a default value
815
+ person_klass.connection.execute('UPDATE testings SET money = NULL')
816
+ person_klass.connection.change_column_null "testings", "money", false, 2000
817
+ person_klass.reset_column_information
818
+ assert_nil person_klass.columns_hash["money"].default
819
+ assert_equal false, person_klass.columns_hash["money"].null
820
+ assert_equal [2000], Person.connection.select_values("SELECT money FROM testings").map { |s| s.to_i }.sort
821
+ ensure
822
+ Person.connection.drop_table :testings rescue nil
823
+ end
824
+
825
+ def test_change_column_default_to_null
826
+ Person.connection.change_column_default "people", "first_name", nil
827
+ Person.reset_column_information
828
+ assert_nil Person.new.first_name
829
+ end
830
+
831
+ def test_add_table
832
+ assert !Reminder.table_exists?
833
+
834
+ WeNeedReminders.up
835
+
836
+ assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
837
+ assert_equal "hello world", Reminder.find(:first).content
838
+
839
+ WeNeedReminders.down
840
+ assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
841
+ end
842
+
843
+ def test_add_table_with_decimals
844
+ Person.connection.drop_table :big_numbers rescue nil
845
+
846
+ assert !BigNumber.table_exists?
847
+ GiveMeBigNumbers.up
848
+
849
+ assert BigNumber.create(
850
+ :bank_balance => 1586.43,
851
+ :big_bank_balance => BigDecimal("1000234000567.95"),
852
+ :world_population => 6000000000,
853
+ :my_house_population => 3,
854
+ :value_of_e => BigDecimal("2.7182818284590452353602875")
855
+ )
856
+
857
+ b = BigNumber.find(:first)
858
+ assert_not_nil b
859
+
860
+ assert_not_nil b.bank_balance
861
+ assert_not_nil b.big_bank_balance
862
+ assert_not_nil b.world_population
863
+ assert_not_nil b.my_house_population
864
+ assert_not_nil b.value_of_e
865
+
866
+ # TODO: set world_population >= 2**62 to cover 64-bit platforms and test
867
+ # is_a?(Bignum)
868
+ assert_kind_of Integer, b.world_population
869
+ assert_equal 6000000000, b.world_population
870
+ assert_kind_of Fixnum, b.my_house_population
871
+ assert_equal 3, b.my_house_population
872
+ assert_kind_of BigDecimal, b.bank_balance
873
+ assert_equal BigDecimal("1586.43"), b.bank_balance
874
+ assert_kind_of BigDecimal, b.big_bank_balance
875
+ assert_equal BigDecimal("1000234000567.95"), b.big_bank_balance
876
+
877
+ # This one is fun. The 'value_of_e' field is defined as 'DECIMAL' with
878
+ # precision/scale explicitly left out. By the SQL standard, numbers
879
+ # assigned to this field should be truncated but that's seldom respected.
880
+ if current_adapter?(:PostgreSQLAdapter, :SQLite2Adapter)
881
+ # - PostgreSQL changes the SQL spec on columns declared simply as
882
+ # "decimal" to something more useful: instead of being given a scale
883
+ # of 0, they take on the compile-time limit for precision and scale,
884
+ # so the following should succeed unless you have used really wacky
885
+ # compilation options
886
+ # - SQLite2 has the default behavior of preserving all data sent in,
887
+ # so this happens there too
888
+ assert_kind_of BigDecimal, b.value_of_e
889
+ assert_equal BigDecimal("2.7182818284590452353602875"), b.value_of_e
890
+ elsif current_adapter?(:SQLiteAdapter)
891
+ # - SQLite3 stores a float, in violation of SQL
892
+ assert_kind_of BigDecimal, b.value_of_e
893
+ assert_equal BigDecimal("2.71828182845905"), b.value_of_e
894
+ else
895
+ # - SQL standard is an integer
896
+ assert_kind_of Fixnum, b.value_of_e
897
+ assert_equal 2, b.value_of_e
898
+ end
899
+
900
+ GiveMeBigNumbers.down
901
+ assert_raise(ActiveRecord::StatementInvalid) { BigNumber.find(:first) }
902
+ end
903
+
904
+ def test_migrator
905
+ assert !Person.column_methods_hash.include?(:last_name)
906
+ assert !Reminder.table_exists?
907
+
908
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid")
909
+
910
+ assert_equal 3, ActiveRecord::Migrator.current_version
911
+ Person.reset_column_information
912
+ assert Person.column_methods_hash.include?(:last_name)
913
+ assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
914
+ assert_equal "hello world", Reminder.find(:first).content
915
+
916
+ ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid")
917
+
918
+ assert_equal 0, ActiveRecord::Migrator.current_version
919
+ Person.reset_column_information
920
+ assert !Person.column_methods_hash.include?(:last_name)
921
+ assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
922
+ end
923
+
924
+ def test_migrator_one_up
925
+ assert !Person.column_methods_hash.include?(:last_name)
926
+ assert !Reminder.table_exists?
927
+
928
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
929
+
930
+ Person.reset_column_information
931
+ assert Person.column_methods_hash.include?(:last_name)
932
+ assert !Reminder.table_exists?
933
+
934
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 2)
935
+
936
+ assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
937
+ assert_equal "hello world", Reminder.find(:first).content
938
+ end
939
+
940
+ def test_migrator_one_down
941
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid")
942
+
943
+ ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 1)
944
+
945
+ Person.reset_column_information
946
+ assert Person.column_methods_hash.include?(:last_name)
947
+ assert !Reminder.table_exists?
948
+ end
949
+
950
+ def test_migrator_one_up_one_down
951
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
952
+ ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
953
+
954
+ assert !Person.column_methods_hash.include?(:last_name)
955
+ assert !Reminder.table_exists?
956
+ end
957
+
958
+ def test_migrator_double_up
959
+ assert_equal(0, ActiveRecord::Migrator.current_version)
960
+ ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1)
961
+ assert_nothing_raised { ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1) }
962
+ assert_equal(1, ActiveRecord::Migrator.current_version)
963
+ end
964
+
965
+ def test_migrator_double_down
966
+ assert_equal(0, ActiveRecord::Migrator.current_version)
967
+ ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1)
968
+ ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 1)
969
+ assert_nothing_raised { ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 1) }
970
+ assert_equal(0, ActiveRecord::Migrator.current_version)
971
+ end
972
+
973
+ if ActiveRecord::Base.connection.supports_ddl_transactions?
974
+ def test_migrator_one_up_with_exception_and_rollback
975
+ assert !Person.column_methods_hash.include?(:last_name)
976
+
977
+ e = assert_raise(StandardError) do
978
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/broken", 100)
979
+ end
980
+
981
+ assert_equal "An error has occurred, this and all later migrations canceled:\n\nSomething broke", e.message
982
+
983
+ Person.reset_column_information
984
+ assert !Person.column_methods_hash.include?(:last_name)
985
+ end
986
+ end
987
+
988
+ def test_finds_migrations
989
+ migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/valid").migrations
990
+
991
+ [[1, 'PeopleHaveLastNames'], [2, 'WeNeedReminders'], [3, 'InnocentJointable']].each_with_index do |pair, i|
992
+ assert_equal migrations[i].version, pair.first
993
+ assert_equal migrations[i].name, pair.last
994
+ end
995
+ end
996
+
997
+ def test_finds_pending_migrations
998
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_2", 1)
999
+ migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/interleaved/pass_2").pending_migrations
1000
+
1001
+ assert_equal 1, migrations.size
1002
+ assert_equal migrations[0].version, 3
1003
+ assert_equal migrations[0].name, 'InnocentJointable'
1004
+ end
1005
+
1006
+ def test_only_loads_pending_migrations
1007
+ # migrate up to 1
1008
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
1009
+
1010
+ # now unload the migrations that have been defined
1011
+ PeopleHaveLastNames.unloadable
1012
+ ActiveSupport::Dependencies.remove_unloadable_constants!
1013
+
1014
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", nil)
1015
+
1016
+ assert !defined? PeopleHaveLastNames
1017
+
1018
+ %w(WeNeedReminders, InnocentJointable).each do |migration|
1019
+ assert defined? migration
1020
+ end
1021
+
1022
+ ensure
1023
+ load(MIGRATIONS_ROOT + "/valid/1_people_have_last_names.rb")
1024
+ end
1025
+
1026
+ def test_migrator_interleaved_migrations
1027
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_1")
1028
+
1029
+ assert_nothing_raised do
1030
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_2")
1031
+ end
1032
+
1033
+ Person.reset_column_information
1034
+ assert Person.column_methods_hash.include?(:last_name)
1035
+
1036
+ assert_nothing_raised do
1037
+ ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/interleaved/pass_3")
1038
+ end
1039
+ end
1040
+
1041
+ def test_migrator_db_has_no_schema_migrations_table
1042
+ ActiveRecord::Base.connection.execute("DROP TABLE schema_migrations;")
1043
+ assert_nothing_raised do
1044
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1)
1045
+ end
1046
+ end
1047
+
1048
+ def test_migrator_verbosity
1049
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
1050
+ assert PeopleHaveLastNames.message_count > 0
1051
+ PeopleHaveLastNames.message_count = 0
1052
+
1053
+ ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
1054
+ assert PeopleHaveLastNames.message_count > 0
1055
+ PeopleHaveLastNames.message_count = 0
1056
+ end
1057
+
1058
+ def test_migrator_verbosity_off
1059
+ PeopleHaveLastNames.verbose = false
1060
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
1061
+ assert PeopleHaveLastNames.message_count.zero?
1062
+ ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
1063
+ assert PeopleHaveLastNames.message_count.zero?
1064
+ end
1065
+
1066
+ def test_migrator_going_down_due_to_version_target
1067
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
1068
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0)
1069
+
1070
+ assert !Person.column_methods_hash.include?(:last_name)
1071
+ assert !Reminder.table_exists?
1072
+
1073
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
1074
+
1075
+ Person.reset_column_information
1076
+ assert Person.column_methods_hash.include?(:last_name)
1077
+ assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
1078
+ assert_equal "hello world", Reminder.find(:first).content
1079
+ end
1080
+
1081
+ def test_migrator_rollback
1082
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
1083
+ assert_equal(3, ActiveRecord::Migrator.current_version)
1084
+
1085
+ ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
1086
+ assert_equal(2, ActiveRecord::Migrator.current_version)
1087
+
1088
+ ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
1089
+ assert_equal(1, ActiveRecord::Migrator.current_version)
1090
+
1091
+ ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
1092
+ assert_equal(0, ActiveRecord::Migrator.current_version)
1093
+
1094
+ ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
1095
+ assert_equal(0, ActiveRecord::Migrator.current_version)
1096
+ end
1097
+
1098
+ def test_schema_migrations_table_name
1099
+ ActiveRecord::Base.table_name_prefix = "prefix_"
1100
+ ActiveRecord::Base.table_name_suffix = "_suffix"
1101
+ Reminder.reset_table_name
1102
+ assert_equal "prefix_schema_migrations_suffix", ActiveRecord::Migrator.schema_migrations_table_name
1103
+ ActiveRecord::Base.table_name_prefix = ""
1104
+ ActiveRecord::Base.table_name_suffix = ""
1105
+ Reminder.reset_table_name
1106
+ assert_equal "schema_migrations", ActiveRecord::Migrator.schema_migrations_table_name
1107
+ ensure
1108
+ ActiveRecord::Base.table_name_prefix = ""
1109
+ ActiveRecord::Base.table_name_suffix = ""
1110
+ end
1111
+
1112
+ def test_proper_table_name
1113
+ assert_equal "table", ActiveRecord::Migrator.proper_table_name('table')
1114
+ assert_equal "table", ActiveRecord::Migrator.proper_table_name(:table)
1115
+ assert_equal "reminders", ActiveRecord::Migrator.proper_table_name(Reminder)
1116
+ Reminder.reset_table_name
1117
+ assert_equal Reminder.table_name, ActiveRecord::Migrator.proper_table_name(Reminder)
1118
+
1119
+ # Use the model's own prefix/suffix if a model is given
1120
+ ActiveRecord::Base.table_name_prefix = "ARprefix_"
1121
+ ActiveRecord::Base.table_name_suffix = "_ARsuffix"
1122
+ Reminder.table_name_prefix = 'prefix_'
1123
+ Reminder.table_name_suffix = '_suffix'
1124
+ Reminder.reset_table_name
1125
+ assert_equal "prefix_reminders_suffix", ActiveRecord::Migrator.proper_table_name(Reminder)
1126
+ Reminder.table_name_prefix = ''
1127
+ Reminder.table_name_suffix = ''
1128
+ Reminder.reset_table_name
1129
+
1130
+ # Use AR::Base's prefix/suffix if string or symbol is given
1131
+ ActiveRecord::Base.table_name_prefix = "prefix_"
1132
+ ActiveRecord::Base.table_name_suffix = "_suffix"
1133
+ Reminder.reset_table_name
1134
+ assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name('table')
1135
+ assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name(:table)
1136
+ ActiveRecord::Base.table_name_prefix = ""
1137
+ ActiveRecord::Base.table_name_suffix = ""
1138
+ Reminder.reset_table_name
1139
+ end
1140
+
1141
+ def test_add_drop_table_with_prefix_and_suffix
1142
+ assert !Reminder.table_exists?
1143
+ ActiveRecord::Base.table_name_prefix = 'prefix_'
1144
+ ActiveRecord::Base.table_name_suffix = '_suffix'
1145
+ Reminder.reset_table_name
1146
+ Reminder.reset_sequence_name
1147
+ WeNeedReminders.up
1148
+ assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
1149
+ assert_equal "hello world", Reminder.find(:first).content
1150
+
1151
+ WeNeedReminders.down
1152
+ assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
1153
+ ensure
1154
+ ActiveRecord::Base.table_name_prefix = ''
1155
+ ActiveRecord::Base.table_name_suffix = ''
1156
+ Reminder.reset_table_name
1157
+ Reminder.reset_sequence_name
1158
+ end
1159
+
1160
+ def test_create_table_with_binary_column
1161
+ Person.connection.drop_table :binary_testings rescue nil
1162
+
1163
+ assert_nothing_raised {
1164
+ Person.connection.create_table :binary_testings do |t|
1165
+ t.column "data", :binary, :null => false
1166
+ end
1167
+ }
1168
+
1169
+ columns = Person.connection.columns(:binary_testings)
1170
+ data_column = columns.detect { |c| c.name == "data" }
1171
+
1172
+ if current_adapter?(:MysqlAdapter)
1173
+ assert_equal '', data_column.default
1174
+ else
1175
+ assert_nil data_column.default
1176
+ end
1177
+
1178
+ Person.connection.drop_table :binary_testings rescue nil
1179
+ end
1180
+
1181
+ def test_migrator_with_duplicates
1182
+ assert_raise(ActiveRecord::DuplicateMigrationVersionError) do
1183
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/duplicate", nil)
1184
+ end
1185
+ end
1186
+
1187
+ def test_migrator_with_duplicate_names
1188
+ assert_raise(ActiveRecord::DuplicateMigrationNameError, "Multiple migrations have the name Chunky") do
1189
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/duplicate_names", nil)
1190
+ end
1191
+ end
1192
+
1193
+ def test_migrator_with_missing_version_numbers
1194
+ assert_raise(ActiveRecord::UnknownMigrationVersionError) do
1195
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/missing", 500)
1196
+ end
1197
+ end
1198
+
1199
+ def test_create_table_with_custom_sequence_name
1200
+ return unless current_adapter? :OracleAdapter
1201
+
1202
+ # table name is 29 chars, the standard sequence name will
1203
+ # be 33 chars and fail
1204
+ assert_raise(ActiveRecord::StatementInvalid) do
1205
+ begin
1206
+ Person.connection.create_table :table_with_name_thats_just_ok do |t|
1207
+ t.column :foo, :string, :null => false
1208
+ end
1209
+ ensure
1210
+ Person.connection.drop_table :table_with_name_thats_just_ok rescue nil
1211
+ end
1212
+ end
1213
+
1214
+ # should be all good w/ a custom sequence name
1215
+ assert_nothing_raised do
1216
+ begin
1217
+ Person.connection.create_table :table_with_name_thats_just_ok,
1218
+ :sequence_name => 'suitably_short_seq' do |t|
1219
+ t.column :foo, :string, :null => false
1220
+ end
1221
+
1222
+ Person.connection.execute("select suitably_short_seq.nextval from dual")
1223
+
1224
+ ensure
1225
+ Person.connection.drop_table :table_with_name_thats_just_ok,
1226
+ :sequence_name => 'suitably_short_seq' rescue nil
1227
+ end
1228
+ end
1229
+
1230
+ # confirm the custom sequence got dropped
1231
+ assert_raise(ActiveRecord::StatementInvalid) do
1232
+ Person.connection.execute("select suitably_short_seq.nextval from dual")
1233
+ end
1234
+ end
1235
+
1236
+ protected
1237
+ def with_env_tz(new_tz = 'US/Eastern')
1238
+ old_tz, ENV['TZ'] = ENV['TZ'], new_tz
1239
+ yield
1240
+ ensure
1241
+ old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
1242
+ end
1243
+
1244
+ end
1245
+
1246
+ class SexyMigrationsTest < ActiveRecord::TestCase
1247
+ def test_references_column_type_adds_id
1248
+ with_new_table do |t|
1249
+ t.expects(:column).with('customer_id', :integer, {})
1250
+ t.references :customer
1251
+ end
1252
+ end
1253
+
1254
+ def test_references_column_type_with_polymorphic_adds_type
1255
+ with_new_table do |t|
1256
+ t.expects(:column).with('taggable_type', :string, {})
1257
+ t.expects(:column).with('taggable_id', :integer, {})
1258
+ t.references :taggable, :polymorphic => true
1259
+ end
1260
+ end
1261
+
1262
+ def test_references_column_type_with_polymorphic_and_options_null_is_false_adds_table_flag
1263
+ with_new_table do |t|
1264
+ t.expects(:column).with('taggable_type', :string, {:null => false})
1265
+ t.expects(:column).with('taggable_id', :integer, {:null => false})
1266
+ t.references :taggable, :polymorphic => true, :null => false
1267
+ end
1268
+ end
1269
+
1270
+ def test_belongs_to_works_like_references
1271
+ with_new_table do |t|
1272
+ t.expects(:column).with('customer_id', :integer, {})
1273
+ t.belongs_to :customer
1274
+ end
1275
+ end
1276
+
1277
+ def test_timestamps_creates_updated_at_and_created_at
1278
+ with_new_table do |t|
1279
+ t.expects(:column).with(:created_at, :datetime, kind_of(Hash))
1280
+ t.expects(:column).with(:updated_at, :datetime, kind_of(Hash))
1281
+ t.timestamps
1282
+ end
1283
+ end
1284
+
1285
+ def test_integer_creates_integer_column
1286
+ with_new_table do |t|
1287
+ t.expects(:column).with(:foo, 'integer', {})
1288
+ t.expects(:column).with(:bar, 'integer', {})
1289
+ t.integer :foo, :bar
1290
+ end
1291
+ end
1292
+
1293
+ def test_string_creates_string_column
1294
+ with_new_table do |t|
1295
+ t.expects(:column).with(:foo, 'string', {})
1296
+ t.expects(:column).with(:bar, 'string', {})
1297
+ t.string :foo, :bar
1298
+ end
1299
+ end
1300
+
1301
+ if current_adapter?(:PostgreSQLAdapter)
1302
+ def test_xml_creates_xml_column
1303
+ with_new_table do |t|
1304
+ t.expects(:column).with(:data, 'xml', {})
1305
+ t.xml :data
1306
+ end
1307
+ end
1308
+ end
1309
+
1310
+ protected
1311
+ def with_new_table
1312
+ Person.connection.create_table :delete_me, :force => true do |t|
1313
+ yield t
1314
+ end
1315
+ ensure
1316
+ Person.connection.drop_table :delete_me rescue nil
1317
+ end
1318
+
1319
+ end # SexyMigrationsTest
1320
+
1321
+ class ChangeTableMigrationsTest < ActiveRecord::TestCase
1322
+ def setup
1323
+ @connection = Person.connection
1324
+ @connection.create_table :delete_me, :force => true do |t|
1325
+ end
1326
+ end
1327
+
1328
+ def teardown
1329
+ Person.connection.drop_table :delete_me rescue nil
1330
+ end
1331
+
1332
+ def test_references_column_type_adds_id
1333
+ with_change_table do |t|
1334
+ @connection.expects(:add_column).with(:delete_me, 'customer_id', :integer, {})
1335
+ t.references :customer
1336
+ end
1337
+ end
1338
+
1339
+ def test_remove_references_column_type_removes_id
1340
+ with_change_table do |t|
1341
+ @connection.expects(:remove_column).with(:delete_me, 'customer_id')
1342
+ t.remove_references :customer
1343
+ end
1344
+ end
1345
+
1346
+ def test_add_belongs_to_works_like_add_references
1347
+ with_change_table do |t|
1348
+ @connection.expects(:add_column).with(:delete_me, 'customer_id', :integer, {})
1349
+ t.belongs_to :customer
1350
+ end
1351
+ end
1352
+
1353
+ def test_remove_belongs_to_works_like_remove_references
1354
+ with_change_table do |t|
1355
+ @connection.expects(:remove_column).with(:delete_me, 'customer_id')
1356
+ t.remove_belongs_to :customer
1357
+ end
1358
+ end
1359
+
1360
+ def test_references_column_type_with_polymorphic_adds_type
1361
+ with_change_table do |t|
1362
+ @connection.expects(:add_column).with(:delete_me, 'taggable_type', :string, {})
1363
+ @connection.expects(:add_column).with(:delete_me, 'taggable_id', :integer, {})
1364
+ t.references :taggable, :polymorphic => true
1365
+ end
1366
+ end
1367
+
1368
+ def test_remove_references_column_type_with_polymorphic_removes_type
1369
+ with_change_table do |t|
1370
+ @connection.expects(:remove_column).with(:delete_me, 'taggable_type')
1371
+ @connection.expects(:remove_column).with(:delete_me, 'taggable_id')
1372
+ t.remove_references :taggable, :polymorphic => true
1373
+ end
1374
+ end
1375
+
1376
+ def test_references_column_type_with_polymorphic_and_options_null_is_false_adds_table_flag
1377
+ with_change_table do |t|
1378
+ @connection.expects(:add_column).with(:delete_me, 'taggable_type', :string, {:null => false})
1379
+ @connection.expects(:add_column).with(:delete_me, 'taggable_id', :integer, {:null => false})
1380
+ t.references :taggable, :polymorphic => true, :null => false
1381
+ end
1382
+ end
1383
+
1384
+ def test_remove_references_column_type_with_polymorphic_and_options_null_is_false_removes_table_flag
1385
+ with_change_table do |t|
1386
+ @connection.expects(:remove_column).with(:delete_me, 'taggable_type')
1387
+ @connection.expects(:remove_column).with(:delete_me, 'taggable_id')
1388
+ t.remove_references :taggable, :polymorphic => true, :null => false
1389
+ end
1390
+ end
1391
+
1392
+ def test_timestamps_creates_updated_at_and_created_at
1393
+ with_change_table do |t|
1394
+ @connection.expects(:add_timestamps).with(:delete_me)
1395
+ t.timestamps
1396
+ end
1397
+ end
1398
+
1399
+ def test_remove_timestamps_creates_updated_at_and_created_at
1400
+ with_change_table do |t|
1401
+ @connection.expects(:remove_timestamps).with(:delete_me)
1402
+ t.remove_timestamps
1403
+ end
1404
+ end
1405
+
1406
+ def string_column
1407
+ if current_adapter?(:PostgreSQLAdapter)
1408
+ "character varying(255)"
1409
+ else
1410
+ 'varchar(255)'
1411
+ end
1412
+ end
1413
+
1414
+ def integer_column
1415
+ if current_adapter?(:MysqlAdapter)
1416
+ 'int(11)'
1417
+ else
1418
+ 'integer'
1419
+ end
1420
+ end
1421
+
1422
+ def test_integer_creates_integer_column
1423
+ with_change_table do |t|
1424
+ @connection.expects(:add_column).with(:delete_me, :foo, integer_column, {})
1425
+ @connection.expects(:add_column).with(:delete_me, :bar, integer_column, {})
1426
+ t.integer :foo, :bar
1427
+ end
1428
+ end
1429
+
1430
+ def test_string_creates_string_column
1431
+ with_change_table do |t|
1432
+ @connection.expects(:add_column).with(:delete_me, :foo, string_column, {})
1433
+ @connection.expects(:add_column).with(:delete_me, :bar, string_column, {})
1434
+ t.string :foo, :bar
1435
+ end
1436
+ end
1437
+
1438
+ def test_column_creates_column
1439
+ with_change_table do |t|
1440
+ @connection.expects(:add_column).with(:delete_me, :bar, :integer, {})
1441
+ t.column :bar, :integer
1442
+ end
1443
+ end
1444
+
1445
+ def test_column_creates_column_with_options
1446
+ with_change_table do |t|
1447
+ @connection.expects(:add_column).with(:delete_me, :bar, :integer, {:null => false})
1448
+ t.column :bar, :integer, :null => false
1449
+ end
1450
+ end
1451
+
1452
+ def test_index_creates_index
1453
+ with_change_table do |t|
1454
+ @connection.expects(:add_index).with(:delete_me, :bar, {})
1455
+ t.index :bar
1456
+ end
1457
+ end
1458
+
1459
+ def test_index_creates_index_with_options
1460
+ with_change_table do |t|
1461
+ @connection.expects(:add_index).with(:delete_me, :bar, {:unique => true})
1462
+ t.index :bar, :unique => true
1463
+ end
1464
+ end
1465
+
1466
+ def test_change_changes_column
1467
+ with_change_table do |t|
1468
+ @connection.expects(:change_column).with(:delete_me, :bar, :string, {})
1469
+ t.change :bar, :string
1470
+ end
1471
+ end
1472
+
1473
+ def test_change_changes_column_with_options
1474
+ with_change_table do |t|
1475
+ @connection.expects(:change_column).with(:delete_me, :bar, :string, {:null => true})
1476
+ t.change :bar, :string, :null => true
1477
+ end
1478
+ end
1479
+
1480
+ def test_change_default_changes_column
1481
+ with_change_table do |t|
1482
+ @connection.expects(:change_column_default).with(:delete_me, :bar, :string)
1483
+ t.change_default :bar, :string
1484
+ end
1485
+ end
1486
+
1487
+ def test_remove_drops_single_column
1488
+ with_change_table do |t|
1489
+ @connection.expects(:remove_column).with(:delete_me, [:bar])
1490
+ t.remove :bar
1491
+ end
1492
+ end
1493
+
1494
+ def test_remove_drops_multiple_columns
1495
+ with_change_table do |t|
1496
+ @connection.expects(:remove_column).with(:delete_me, [:bar, :baz])
1497
+ t.remove :bar, :baz
1498
+ end
1499
+ end
1500
+
1501
+ def test_remove_index_removes_index_with_options
1502
+ with_change_table do |t|
1503
+ @connection.expects(:remove_index).with(:delete_me, {:unique => true})
1504
+ t.remove_index :unique => true
1505
+ end
1506
+ end
1507
+
1508
+ def test_rename_renames_column
1509
+ with_change_table do |t|
1510
+ @connection.expects(:rename_column).with(:delete_me, :bar, :baz)
1511
+ t.rename :bar, :baz
1512
+ end
1513
+ end
1514
+
1515
+ protected
1516
+ def with_change_table
1517
+ Person.connection.change_table :delete_me do |t|
1518
+ yield t
1519
+ end
1520
+ end
1521
+ end
1522
+ end
1523
+