activerecord 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (311) hide show
  1. data/CHANGELOG +4928 -3
  2. data/README +45 -46
  3. data/RUNNING_UNIT_TESTS +8 -11
  4. data/Rakefile +247 -0
  5. data/install.rb +8 -38
  6. data/lib/active_record/aggregations.rb +64 -49
  7. data/lib/active_record/associations/association_collection.rb +217 -47
  8. data/lib/active_record/associations/association_proxy.rb +159 -0
  9. data/lib/active_record/associations/belongs_to_association.rb +56 -0
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +155 -37
  12. data/lib/active_record/associations/has_many_association.rb +145 -75
  13. data/lib/active_record/associations/has_many_through_association.rb +283 -0
  14. data/lib/active_record/associations/has_one_association.rb +96 -0
  15. data/lib/active_record/associations.rb +1537 -304
  16. data/lib/active_record/attribute_methods.rb +328 -0
  17. data/lib/active_record/base.rb +2001 -588
  18. data/lib/active_record/calculations.rb +269 -0
  19. data/lib/active_record/callbacks.rb +169 -165
  20. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +308 -0
  21. data/lib/active_record/connection_adapters/abstract/database_statements.rb +171 -0
  22. data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
  23. data/lib/active_record/connection_adapters/abstract/quoting.rb +69 -0
  24. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +472 -0
  25. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +306 -0
  26. data/lib/active_record/connection_adapters/abstract_adapter.rb +125 -279
  27. data/lib/active_record/connection_adapters/mysql_adapter.rb +442 -77
  28. data/lib/active_record/connection_adapters/postgresql_adapter.rb +805 -135
  29. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  30. data/lib/active_record/connection_adapters/sqlite_adapter.rb +353 -69
  31. data/lib/active_record/fixtures.rb +946 -100
  32. data/lib/active_record/locking/optimistic.rb +144 -0
  33. data/lib/active_record/locking/pessimistic.rb +77 -0
  34. data/lib/active_record/migration.rb +417 -0
  35. data/lib/active_record/observer.rb +142 -32
  36. data/lib/active_record/query_cache.rb +23 -0
  37. data/lib/active_record/reflection.rb +163 -70
  38. data/lib/active_record/schema.rb +58 -0
  39. data/lib/active_record/schema_dumper.rb +171 -0
  40. data/lib/active_record/serialization.rb +98 -0
  41. data/lib/active_record/serializers/json_serializer.rb +71 -0
  42. data/lib/active_record/serializers/xml_serializer.rb +315 -0
  43. data/lib/active_record/timestamp.rb +41 -0
  44. data/lib/active_record/transactions.rb +87 -57
  45. data/lib/active_record/validations.rb +909 -122
  46. data/lib/active_record/vendor/db2.rb +362 -0
  47. data/lib/active_record/vendor/mysql.rb +126 -29
  48. data/lib/active_record/version.rb +9 -0
  49. data/lib/active_record.rb +35 -7
  50. data/lib/activerecord.rb +1 -0
  51. data/test/aaa_create_tables_test.rb +72 -0
  52. data/test/abstract_unit.rb +73 -5
  53. data/test/active_schema_test_mysql.rb +43 -0
  54. data/test/adapter_test.rb +105 -0
  55. data/test/adapter_test_sqlserver.rb +95 -0
  56. data/test/aggregations_test.rb +110 -16
  57. data/test/all.sh +2 -2
  58. data/test/ar_schema_test.rb +33 -0
  59. data/test/association_inheritance_reload.rb +14 -0
  60. data/test/associations/ar_joins_test.rb +0 -0
  61. data/test/associations/callbacks_test.rb +147 -0
  62. data/test/associations/cascaded_eager_loading_test.rb +110 -0
  63. data/test/associations/eager_singularization_test.rb +145 -0
  64. data/test/associations/eager_test.rb +442 -0
  65. data/test/associations/extension_test.rb +47 -0
  66. data/test/associations/inner_join_association_test.rb +88 -0
  67. data/test/associations/join_model_test.rb +553 -0
  68. data/test/associations_test.rb +1930 -267
  69. data/test/attribute_methods_test.rb +146 -0
  70. data/test/base_test.rb +1316 -84
  71. data/test/binary_test.rb +32 -0
  72. data/test/calculations_test.rb +251 -0
  73. data/test/callbacks_test.rb +400 -0
  74. data/test/class_inheritable_attributes_test.rb +3 -4
  75. data/test/column_alias_test.rb +17 -0
  76. data/test/connection_test_firebird.rb +8 -0
  77. data/test/connection_test_mysql.rb +30 -0
  78. data/test/connections/native_db2/connection.rb +25 -0
  79. data/test/connections/native_firebird/connection.rb +26 -0
  80. data/test/connections/native_frontbase/connection.rb +27 -0
  81. data/test/connections/native_mysql/connection.rb +21 -18
  82. data/test/connections/native_openbase/connection.rb +21 -0
  83. data/test/connections/native_oracle/connection.rb +27 -0
  84. data/test/connections/native_postgresql/connection.rb +17 -18
  85. data/test/connections/native_sqlite/connection.rb +17 -16
  86. data/test/connections/native_sqlite3/connection.rb +25 -0
  87. data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
  88. data/test/connections/native_sybase/connection.rb +23 -0
  89. data/test/copy_table_test_sqlite.rb +69 -0
  90. data/test/datatype_test_postgresql.rb +203 -0
  91. data/test/date_time_test.rb +37 -0
  92. data/test/default_test_firebird.rb +16 -0
  93. data/test/defaults_test.rb +67 -0
  94. data/test/deprecated_finder_test.rb +30 -0
  95. data/test/finder_test.rb +607 -32
  96. data/test/fixtures/accounts.yml +28 -0
  97. data/test/fixtures/all/developers.yml +0 -0
  98. data/test/fixtures/all/people.csv +0 -0
  99. data/test/fixtures/all/tasks.yml +0 -0
  100. data/test/fixtures/author.rb +107 -0
  101. data/test/fixtures/author_favorites.yml +4 -0
  102. data/test/fixtures/authors.yml +7 -0
  103. data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +1 -0
  104. data/test/fixtures/bad_fixtures/attr_with_spaces +1 -0
  105. data/test/fixtures/bad_fixtures/blank_line +3 -0
  106. data/test/fixtures/bad_fixtures/duplicate_attributes +3 -0
  107. data/test/fixtures/bad_fixtures/missing_value +1 -0
  108. data/test/fixtures/binaries.yml +132 -0
  109. data/test/fixtures/binary.rb +2 -0
  110. data/test/fixtures/book.rb +4 -0
  111. data/test/fixtures/books.yml +7 -0
  112. data/test/fixtures/categories/special_categories.yml +9 -0
  113. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  114. data/test/fixtures/categories.yml +14 -0
  115. data/test/fixtures/categories_ordered.yml +7 -0
  116. data/test/fixtures/categories_posts.yml +23 -0
  117. data/test/fixtures/categorization.rb +5 -0
  118. data/test/fixtures/categorizations.yml +17 -0
  119. data/test/fixtures/category.rb +26 -0
  120. data/test/fixtures/citation.rb +6 -0
  121. data/test/fixtures/comment.rb +23 -0
  122. data/test/fixtures/comments.yml +59 -0
  123. data/test/fixtures/companies.yml +55 -0
  124. data/test/fixtures/company.rb +81 -4
  125. data/test/fixtures/company_in_module.rb +32 -6
  126. data/test/fixtures/computer.rb +4 -0
  127. data/test/fixtures/computers.yml +4 -0
  128. data/test/fixtures/contact.rb +16 -0
  129. data/test/fixtures/courses.yml +7 -0
  130. data/test/fixtures/customer.rb +28 -3
  131. data/test/fixtures/customers.yml +17 -0
  132. data/test/fixtures/db_definitions/db2.drop.sql +33 -0
  133. data/test/fixtures/db_definitions/db2.sql +235 -0
  134. data/test/fixtures/db_definitions/db22.drop.sql +2 -0
  135. data/test/fixtures/db_definitions/db22.sql +5 -0
  136. data/test/fixtures/db_definitions/firebird.drop.sql +65 -0
  137. data/test/fixtures/db_definitions/firebird.sql +310 -0
  138. data/test/fixtures/db_definitions/firebird2.drop.sql +2 -0
  139. data/test/fixtures/db_definitions/firebird2.sql +6 -0
  140. data/test/fixtures/db_definitions/frontbase.drop.sql +33 -0
  141. data/test/fixtures/db_definitions/frontbase.sql +273 -0
  142. data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
  143. data/test/fixtures/db_definitions/frontbase2.sql +4 -0
  144. data/test/fixtures/db_definitions/openbase.drop.sql +2 -0
  145. data/test/fixtures/db_definitions/openbase.sql +318 -0
  146. data/test/fixtures/db_definitions/openbase2.drop.sql +2 -0
  147. data/test/fixtures/db_definitions/openbase2.sql +7 -0
  148. data/test/fixtures/db_definitions/oracle.drop.sql +67 -0
  149. data/test/fixtures/db_definitions/oracle.sql +330 -0
  150. data/test/fixtures/db_definitions/oracle2.drop.sql +2 -0
  151. data/test/fixtures/db_definitions/oracle2.sql +6 -0
  152. data/test/fixtures/db_definitions/postgresql.drop.sql +44 -0
  153. data/test/fixtures/db_definitions/postgresql.sql +217 -38
  154. data/test/fixtures/db_definitions/postgresql2.drop.sql +2 -0
  155. data/test/fixtures/db_definitions/postgresql2.sql +2 -2
  156. data/test/fixtures/db_definitions/schema.rb +354 -0
  157. data/test/fixtures/db_definitions/schema2.rb +11 -0
  158. data/test/fixtures/db_definitions/sqlite.drop.sql +33 -0
  159. data/test/fixtures/db_definitions/sqlite.sql +139 -5
  160. data/test/fixtures/db_definitions/sqlite2.drop.sql +2 -0
  161. data/test/fixtures/db_definitions/sqlite2.sql +1 -0
  162. data/test/fixtures/db_definitions/sybase.drop.sql +35 -0
  163. data/test/fixtures/db_definitions/sybase.sql +222 -0
  164. data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
  165. data/test/fixtures/db_definitions/sybase2.sql +5 -0
  166. data/test/fixtures/developer.rb +70 -6
  167. data/test/fixtures/developers.yml +21 -0
  168. data/test/fixtures/developers_projects/david_action_controller +2 -1
  169. data/test/fixtures/developers_projects/david_active_record +2 -1
  170. data/test/fixtures/developers_projects.yml +17 -0
  171. data/test/fixtures/edge.rb +5 -0
  172. data/test/fixtures/edges.yml +6 -0
  173. data/test/fixtures/entrants.yml +14 -0
  174. data/test/fixtures/example.log +1 -0
  175. data/test/fixtures/fk_test_has_fk.yml +3 -0
  176. data/test/fixtures/fk_test_has_pk.yml +2 -0
  177. data/test/fixtures/flowers.jpg +0 -0
  178. data/test/fixtures/funny_jokes.yml +10 -0
  179. data/test/fixtures/item.rb +7 -0
  180. data/test/fixtures/items.yml +4 -0
  181. data/test/fixtures/joke.rb +3 -0
  182. data/test/fixtures/keyboard.rb +3 -0
  183. data/test/fixtures/legacy_thing.rb +3 -0
  184. data/test/fixtures/legacy_things.yml +3 -0
  185. data/test/fixtures/matey.rb +4 -0
  186. data/test/fixtures/mateys.yml +4 -0
  187. data/test/fixtures/migrations/1_people_have_last_names.rb +9 -0
  188. data/test/fixtures/migrations/2_we_need_reminders.rb +12 -0
  189. data/test/fixtures/migrations/3_innocent_jointable.rb +12 -0
  190. data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
  191. data/test/fixtures/migrations_with_duplicate/1_people_have_last_names.rb +9 -0
  192. data/test/fixtures/migrations_with_duplicate/2_we_need_reminders.rb +12 -0
  193. data/test/fixtures/migrations_with_duplicate/3_foo.rb +7 -0
  194. data/test/fixtures/migrations_with_duplicate/3_innocent_jointable.rb +12 -0
  195. data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
  196. data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
  197. data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
  198. data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
  199. data/test/fixtures/minimalistic.rb +2 -0
  200. data/test/fixtures/minimalistics.yml +2 -0
  201. data/test/fixtures/mixed_case_monkey.rb +3 -0
  202. data/test/fixtures/mixed_case_monkeys.yml +6 -0
  203. data/test/fixtures/mixins.yml +29 -0
  204. data/test/fixtures/movies.yml +7 -0
  205. data/test/fixtures/naked/csv/accounts.csv +1 -0
  206. data/test/fixtures/naked/yml/accounts.yml +1 -0
  207. data/test/fixtures/naked/yml/companies.yml +1 -0
  208. data/test/fixtures/naked/yml/courses.yml +1 -0
  209. data/test/fixtures/order.rb +4 -0
  210. data/test/fixtures/parrot.rb +13 -0
  211. data/test/fixtures/parrots.yml +27 -0
  212. data/test/fixtures/parrots_pirates.yml +7 -0
  213. data/test/fixtures/people.yml +3 -0
  214. data/test/fixtures/person.rb +4 -0
  215. data/test/fixtures/pirate.rb +5 -0
  216. data/test/fixtures/pirates.yml +9 -0
  217. data/test/fixtures/post.rb +59 -0
  218. data/test/fixtures/posts.yml +48 -0
  219. data/test/fixtures/project.rb +27 -2
  220. data/test/fixtures/projects.yml +7 -0
  221. data/test/fixtures/reader.rb +4 -0
  222. data/test/fixtures/readers.yml +4 -0
  223. data/test/fixtures/reply.rb +18 -2
  224. data/test/fixtures/reserved_words/distinct.yml +5 -0
  225. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  226. data/test/fixtures/reserved_words/group.yml +14 -0
  227. data/test/fixtures/reserved_words/select.yml +8 -0
  228. data/test/fixtures/reserved_words/values.yml +7 -0
  229. data/test/fixtures/ship.rb +3 -0
  230. data/test/fixtures/ships.yml +5 -0
  231. data/test/fixtures/subject.rb +4 -0
  232. data/test/fixtures/subscriber.rb +4 -3
  233. data/test/fixtures/tag.rb +7 -0
  234. data/test/fixtures/tagging.rb +10 -0
  235. data/test/fixtures/taggings.yml +25 -0
  236. data/test/fixtures/tags.yml +7 -0
  237. data/test/fixtures/task.rb +3 -0
  238. data/test/fixtures/tasks.yml +7 -0
  239. data/test/fixtures/topic.rb +20 -3
  240. data/test/fixtures/topics.yml +22 -0
  241. data/test/fixtures/treasure.rb +4 -0
  242. data/test/fixtures/treasures.yml +10 -0
  243. data/test/fixtures/vertex.rb +9 -0
  244. data/test/fixtures/vertices.yml +4 -0
  245. data/test/fixtures_test.rb +574 -8
  246. data/test/inheritance_test.rb +113 -27
  247. data/test/json_serialization_test.rb +180 -0
  248. data/test/lifecycle_test.rb +56 -29
  249. data/test/locking_test.rb +273 -0
  250. data/test/method_scoping_test.rb +416 -0
  251. data/test/migration_test.rb +933 -0
  252. data/test/migration_test_firebird.rb +124 -0
  253. data/test/mixin_test.rb +95 -0
  254. data/test/modules_test.rb +23 -10
  255. data/test/multiple_db_test.rb +17 -3
  256. data/test/pk_test.rb +59 -15
  257. data/test/query_cache_test.rb +104 -0
  258. data/test/readonly_test.rb +107 -0
  259. data/test/reflection_test.rb +124 -27
  260. data/test/reserved_word_test_mysql.rb +177 -0
  261. data/test/schema_authorization_test_postgresql.rb +75 -0
  262. data/test/schema_dumper_test.rb +131 -0
  263. data/test/schema_test_postgresql.rb +64 -0
  264. data/test/serialization_test.rb +47 -0
  265. data/test/synonym_test_oracle.rb +17 -0
  266. data/test/table_name_test_sqlserver.rb +23 -0
  267. data/test/threaded_connections_test.rb +48 -0
  268. data/test/transactions_test.rb +227 -29
  269. data/test/unconnected_test.rb +14 -6
  270. data/test/validations_test.rb +1293 -32
  271. data/test/xml_serialization_test.rb +202 -0
  272. metadata +347 -143
  273. data/dev-utils/eval_debugger.rb +0 -9
  274. data/examples/associations.rb +0 -87
  275. data/examples/shared_setup.rb +0 -15
  276. data/examples/validation.rb +0 -88
  277. data/lib/active_record/deprecated_associations.rb +0 -70
  278. data/lib/active_record/support/class_attribute_accessors.rb +0 -43
  279. data/lib/active_record/support/class_inheritable_attributes.rb +0 -37
  280. data/lib/active_record/support/clean_logger.rb +0 -10
  281. data/lib/active_record/support/inflector.rb +0 -70
  282. data/lib/active_record/vendor/simple.rb +0 -702
  283. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  284. data/lib/active_record/wrappings.rb +0 -59
  285. data/rakefile +0 -122
  286. data/test/deprecated_associations_test.rb +0 -336
  287. data/test/fixtures/accounts/signals37 +0 -3
  288. data/test/fixtures/accounts/unknown +0 -2
  289. data/test/fixtures/companies/first_client +0 -6
  290. data/test/fixtures/companies/first_firm +0 -4
  291. data/test/fixtures/companies/second_client +0 -6
  292. data/test/fixtures/courses/java +0 -2
  293. data/test/fixtures/courses/ruby +0 -2
  294. data/test/fixtures/customers/david +0 -6
  295. data/test/fixtures/db_definitions/mysql.sql +0 -96
  296. data/test/fixtures/db_definitions/mysql2.sql +0 -4
  297. data/test/fixtures/developers/david +0 -2
  298. data/test/fixtures/developers/jamis +0 -2
  299. data/test/fixtures/entrants/first +0 -3
  300. data/test/fixtures/entrants/second +0 -3
  301. data/test/fixtures/entrants/third +0 -3
  302. data/test/fixtures/fixture_database.sqlite +0 -0
  303. data/test/fixtures/fixture_database_2.sqlite +0 -0
  304. data/test/fixtures/movies/first +0 -2
  305. data/test/fixtures/movies/second +0 -2
  306. data/test/fixtures/projects/action_controller +0 -2
  307. data/test/fixtures/projects/active_record +0 -2
  308. data/test/fixtures/topics/first +0 -9
  309. data/test/fixtures/topics/second +0 -8
  310. data/test/inflector_test.rb +0 -104
  311. data/test/thread_safety_test.rb +0 -33
@@ -0,0 +1,933 @@
1
+ require 'abstract_unit'
2
+ require 'bigdecimal/util'
3
+
4
+ require 'fixtures/person'
5
+ require 'fixtures/topic'
6
+ require File.dirname(__FILE__) + '/fixtures/migrations/1_people_have_last_names'
7
+ require File.dirname(__FILE__) + '/fixtures/migrations/2_we_need_reminders'
8
+ require File.dirname(__FILE__) + '/fixtures/migrations_with_decimal/1_give_me_big_numbers'
9
+
10
+ if ActiveRecord::Base.connection.supports_migrations?
11
+ class BigNumber < ActiveRecord::Base; end
12
+
13
+ class Reminder < ActiveRecord::Base; end
14
+
15
+ class ActiveRecord::Migration
16
+ class <<self
17
+ attr_accessor :message_count
18
+ def puts(text="")
19
+ self.message_count ||= 0
20
+ self.message_count += 1
21
+ end
22
+ end
23
+ end
24
+
25
+ class MigrationTest < Test::Unit::TestCase
26
+ self.use_transactional_fixtures = false
27
+
28
+ fixtures :people
29
+
30
+ def setup
31
+ ActiveRecord::Migration.verbose = true
32
+ PeopleHaveLastNames.message_count = 0
33
+ end
34
+
35
+ def teardown
36
+ ActiveRecord::Base.connection.initialize_schema_information
37
+ ActiveRecord::Base.connection.update "UPDATE #{ActiveRecord::Migrator.schema_info_table_name} SET version = 0"
38
+
39
+ %w(reminders people_reminders prefix_reminders_suffix).each do |table|
40
+ Reminder.connection.drop_table(table) rescue nil
41
+ end
42
+ Reminder.reset_column_information
43
+
44
+ %w(last_name key bio age height wealth birthday favorite_day
45
+ moment_of_truth male administrator funny).each do |column|
46
+ Person.connection.remove_column('people', column) rescue nil
47
+ end
48
+ Person.connection.remove_column("people", "first_name") rescue nil
49
+ Person.connection.remove_column("people", "middle_name") rescue nil
50
+ Person.connection.add_column("people", "first_name", :string, :limit => 40)
51
+ Person.reset_column_information
52
+ end
53
+
54
+ def test_add_index
55
+ # Limit size of last_name and key columns to support Firebird index limitations
56
+ Person.connection.add_column "people", "last_name", :string, :limit => 100
57
+ Person.connection.add_column "people", "key", :string, :limit => 100
58
+ Person.connection.add_column "people", "administrator", :boolean
59
+
60
+ assert_nothing_raised { Person.connection.add_index("people", "last_name") }
61
+ assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
62
+
63
+ # Orcl nds shrt indx nms. Sybs 2.
64
+ # OpenBase does not have named indexes. You must specify a single column name
65
+ unless current_adapter?(:OracleAdapter, :SybaseAdapter, :OpenBaseAdapter)
66
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
67
+ assert_nothing_raised { Person.connection.remove_index("people", :column => ["last_name", "first_name"]) }
68
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
69
+ assert_nothing_raised { Person.connection.remove_index("people", :name => "index_people_on_last_name_and_first_name") }
70
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
71
+ assert_nothing_raised { Person.connection.remove_index("people", "last_name_and_first_name") }
72
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
73
+ assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
74
+ end
75
+
76
+ # quoting
77
+ # Note: changed index name from "key" to "key_idx" since "key" is a Firebird reserved word
78
+ # OpenBase does not have named indexes. You must specify a single column name
79
+ unless current_adapter?(:OpenBaseAdapter)
80
+ assert_nothing_raised { Person.connection.add_index("people", ["key"], :name => "key_idx", :unique => true) }
81
+ assert_nothing_raised { Person.connection.remove_index("people", :name => "key_idx", :unique => true) }
82
+ end
83
+
84
+ # Sybase adapter does not support indexes on :boolean columns
85
+ # OpenBase does not have named indexes. You must specify a single column
86
+ unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
87
+ assert_nothing_raised { Person.connection.add_index("people", %w(last_name first_name administrator), :name => "named_admin") }
88
+ assert_nothing_raised { Person.connection.remove_index("people", :name => "named_admin") }
89
+ end
90
+ end
91
+
92
+ def test_create_table_adds_id
93
+ Person.connection.create_table :testings do |t|
94
+ t.column :foo, :string
95
+ end
96
+
97
+ assert_equal %w(foo id),
98
+ Person.connection.columns(:testings).map { |c| c.name }.sort
99
+ ensure
100
+ Person.connection.drop_table :testings rescue nil
101
+ end
102
+
103
+ def test_create_table_with_not_null_column
104
+ assert_nothing_raised do
105
+ Person.connection.create_table :testings do |t|
106
+ t.column :foo, :string, :null => false
107
+ end
108
+ end
109
+
110
+ assert_raises(ActiveRecord::StatementInvalid) do
111
+ Person.connection.execute "insert into testings (foo) values (NULL)"
112
+ end
113
+ ensure
114
+ Person.connection.drop_table :testings rescue nil
115
+ end
116
+
117
+ def test_create_table_with_defaults
118
+ # MySQL doesn't allow defaults on TEXT or BLOB columns.
119
+ mysql = current_adapter?(:MysqlAdapter)
120
+
121
+ Person.connection.create_table :testings do |t|
122
+ t.column :one, :string, :default => "hello"
123
+ t.column :two, :boolean, :default => true
124
+ t.column :three, :boolean, :default => false
125
+ t.column :four, :integer, :default => 1
126
+ t.column :five, :text, :default => "hello" unless mysql
127
+ end
128
+
129
+ columns = Person.connection.columns(:testings)
130
+ one = columns.detect { |c| c.name == "one" }
131
+ two = columns.detect { |c| c.name == "two" }
132
+ three = columns.detect { |c| c.name == "three" }
133
+ four = columns.detect { |c| c.name == "four" }
134
+ five = columns.detect { |c| c.name == "five" } unless mysql
135
+
136
+ assert_equal "hello", one.default
137
+ assert_equal true, two.default
138
+ assert_equal false, three.default
139
+ assert_equal 1, four.default
140
+ assert_equal "hello", five.default unless mysql
141
+
142
+ ensure
143
+ Person.connection.drop_table :testings rescue nil
144
+ end
145
+
146
+ def test_create_table_with_limits
147
+ assert_nothing_raised do
148
+ Person.connection.create_table :testings do |t|
149
+ t.column :foo, :string, :limit => 255
150
+
151
+ t.column :default_int, :integer
152
+
153
+ t.column :one_int, :integer, :limit => 1
154
+ t.column :four_int, :integer, :limit => 4
155
+ t.column :eight_int, :integer, :limit => 8
156
+ end
157
+ end
158
+
159
+ columns = Person.connection.columns(:testings)
160
+ foo = columns.detect { |c| c.name == "foo" }
161
+ assert_equal 255, foo.limit
162
+
163
+ default = columns.detect { |c| c.name == "default_int" }
164
+ one = columns.detect { |c| c.name == "one_int" }
165
+ four = columns.detect { |c| c.name == "four_int" }
166
+ eight = columns.detect { |c| c.name == "eight_int" }
167
+
168
+ if current_adapter?(:PostgreSQLAdapter)
169
+ assert_equal 'integer', default.sql_type
170
+ assert_equal 'smallint', one.sql_type
171
+ assert_equal 'integer', four.sql_type
172
+ assert_equal 'bigint', eight.sql_type
173
+ elsif current_adapter?(:OracleAdapter)
174
+ assert_equal 'NUMBER(38)', default.sql_type
175
+ assert_equal 'NUMBER(1)', one.sql_type
176
+ assert_equal 'NUMBER(4)', four.sql_type
177
+ assert_equal 'NUMBER(8)', eight.sql_type
178
+ end
179
+ ensure
180
+ Person.connection.drop_table :testings rescue nil
181
+ end
182
+
183
+ # SQL Server, Sybase, and SQLite3 will not allow you to add a NOT NULL
184
+ # column to a table without a default value.
185
+ unless current_adapter?(:SQLServerAdapter, :SybaseAdapter, :SQLiteAdapter)
186
+ def test_add_column_not_null_without_default
187
+ Person.connection.create_table :testings do |t|
188
+ t.column :foo, :string
189
+ end
190
+ Person.connection.add_column :testings, :bar, :string, :null => false
191
+
192
+ assert_raises(ActiveRecord::StatementInvalid) do
193
+ Person.connection.execute "insert into testings (foo, bar) values ('hello', NULL)"
194
+ end
195
+ ensure
196
+ Person.connection.drop_table :testings rescue nil
197
+ end
198
+ end
199
+
200
+ def test_add_column_not_null_with_default
201
+ Person.connection.create_table :testings do |t|
202
+ t.column :foo, :string
203
+ end
204
+
205
+ con = Person.connection
206
+ Person.connection.enable_identity_insert("testings", true) if current_adapter?(:SybaseAdapter)
207
+ Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}) values (1, 'hello')"
208
+ Person.connection.enable_identity_insert("testings", false) if current_adapter?(:SybaseAdapter)
209
+ assert_nothing_raised {Person.connection.add_column :testings, :bar, :string, :null => false, :default => "default" }
210
+
211
+ assert_raises(ActiveRecord::StatementInvalid) do
212
+ unless current_adapter?(:OpenBaseAdapter)
213
+ 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)"
214
+ else
215
+ 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)",
216
+ "Testing Insert","id",2)
217
+ end
218
+ end
219
+ ensure
220
+ Person.connection.drop_table :testings rescue nil
221
+ end
222
+
223
+ # We specifically do a manual INSERT here, and then test only the SELECT
224
+ # functionality. This allows us to more easily catch INSERT being broken,
225
+ # but SELECT actually working fine.
226
+ def test_native_decimal_insert_manual_vs_automatic
227
+ correct_value = '0012345678901234567890.0123456789'.to_d
228
+
229
+ Person.delete_all
230
+ Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
231
+ Person.reset_column_information
232
+
233
+ # Do a manual insertion
234
+ if current_adapter?(:OracleAdapter)
235
+ Person.connection.execute "insert into people (id, wealth) values (people_seq.nextval, 12345678901234567890.0123456789)"
236
+ elsif current_adapter?(:OpenBaseAdapter)
237
+ Person.connection.execute "insert into people (wealth) values ('12345678901234567890.0123456789')"
238
+ else
239
+ Person.connection.execute "insert into people (wealth) values (12345678901234567890.0123456789)"
240
+ end
241
+
242
+ # SELECT
243
+ row = Person.find(:first)
244
+ assert_kind_of BigDecimal, row.wealth
245
+
246
+ # If this assert fails, that means the SELECT is broken!
247
+ unless current_adapter?(:SQLite3Adapter)
248
+ assert_equal correct_value, row.wealth
249
+ end
250
+
251
+ # Reset to old state
252
+ Person.delete_all
253
+
254
+ # Now use the Rails insertion
255
+ assert_nothing_raised { Person.create :wealth => BigDecimal.new("12345678901234567890.0123456789") }
256
+
257
+ # SELECT
258
+ row = Person.find(:first)
259
+ assert_kind_of BigDecimal, row.wealth
260
+
261
+ # If these asserts fail, that means the INSERT (create function, or cast to SQL) is broken!
262
+ unless current_adapter?(:SQLite3Adapter)
263
+ assert_equal correct_value, row.wealth
264
+ end
265
+
266
+ # Reset to old state
267
+ Person.connection.del_column "people", "wealth" rescue nil
268
+ Person.reset_column_information
269
+ end
270
+
271
+ def test_native_types
272
+ Person.delete_all
273
+ Person.connection.add_column "people", "last_name", :string
274
+ Person.connection.add_column "people", "bio", :text
275
+ Person.connection.add_column "people", "age", :integer
276
+ Person.connection.add_column "people", "height", :float
277
+ Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
278
+ Person.connection.add_column "people", "birthday", :datetime
279
+ Person.connection.add_column "people", "favorite_day", :date
280
+ Person.connection.add_column "people", "moment_of_truth", :datetime
281
+ Person.connection.add_column "people", "male", :boolean
282
+ Person.reset_column_information
283
+
284
+ assert_nothing_raised do
285
+ Person.create :first_name => 'bob', :last_name => 'bobsen',
286
+ :bio => "I was born ....", :age => 18, :height => 1.78,
287
+ :wealth => BigDecimal.new("12345678901234567890.0123456789"),
288
+ :birthday => 18.years.ago, :favorite_day => 10.days.ago,
289
+ :moment_of_truth => "1782-10-10 21:40:18", :male => true
290
+ end
291
+
292
+ bob = Person.find(:first)
293
+ assert_equal 'bob', bob.first_name
294
+ assert_equal 'bobsen', bob.last_name
295
+ assert_equal "I was born ....", bob.bio
296
+ assert_equal 18, bob.age
297
+
298
+ # Test for 30 significent digits (beyond the 16 of float), 10 of them
299
+ # after the decimal place.
300
+
301
+ unless current_adapter?(:SQLite3Adapter)
302
+ assert_equal BigDecimal.new("0012345678901234567890.0123456789"), bob.wealth
303
+ end
304
+
305
+ assert_equal true, bob.male?
306
+
307
+ assert_equal String, bob.first_name.class
308
+ assert_equal String, bob.last_name.class
309
+ assert_equal String, bob.bio.class
310
+ assert_equal Fixnum, bob.age.class
311
+ assert_equal Time, bob.birthday.class
312
+
313
+ if current_adapter?(:SQLServerAdapter, :OracleAdapter, :SybaseAdapter)
314
+ # Sybase, and Oracle don't differentiate between date/time
315
+ assert_equal Time, bob.favorite_day.class
316
+ else
317
+ assert_equal Date, bob.favorite_day.class
318
+ end
319
+
320
+ # Test DateTime column and defaults, including timezone.
321
+ # FIXME: moment of truth may be Time on 64-bit platforms.
322
+ if bob.moment_of_truth.is_a?(DateTime)
323
+ assert_equal DateTime.now.offset, bob.moment_of_truth.offset
324
+ assert_not_equal 0, bob.moment_of_truth.offset
325
+ assert_not_equal "Z", bob.moment_of_truth.zone
326
+ assert_equal DateTime::ITALY, bob.moment_of_truth.start
327
+ end
328
+
329
+ assert_equal TrueClass, bob.male?.class
330
+ assert_kind_of BigDecimal, bob.wealth
331
+ end
332
+
333
+ if current_adapter?(:MysqlAdapter)
334
+ def test_unabstracted_database_dependent_types
335
+ Person.delete_all
336
+
337
+ ActiveRecord::Migration.add_column :people, :intelligence_quotient, :tinyint
338
+ Person.reset_column_information
339
+ Person.create :intelligence_quotient => 300
340
+ jonnyg = Person.find(:first)
341
+ assert_equal 127, jonnyg.intelligence_quotient
342
+ jonnyg.destroy
343
+ ensure
344
+ ActiveRecord::Migration.remove_column :people, :intelligence_quotient rescue nil
345
+ end
346
+ end
347
+
348
+ def test_add_remove_single_field_using_string_arguments
349
+ assert !Person.column_methods_hash.include?(:last_name)
350
+
351
+ ActiveRecord::Migration.add_column 'people', 'last_name', :string
352
+
353
+ Person.reset_column_information
354
+ assert Person.column_methods_hash.include?(:last_name)
355
+
356
+ ActiveRecord::Migration.remove_column 'people', 'last_name'
357
+
358
+ Person.reset_column_information
359
+ assert !Person.column_methods_hash.include?(:last_name)
360
+ end
361
+
362
+ def test_add_remove_single_field_using_symbol_arguments
363
+ assert !Person.column_methods_hash.include?(:last_name)
364
+
365
+ ActiveRecord::Migration.add_column :people, :last_name, :string
366
+
367
+ Person.reset_column_information
368
+ assert Person.column_methods_hash.include?(:last_name)
369
+
370
+ ActiveRecord::Migration.remove_column :people, :last_name
371
+
372
+ Person.reset_column_information
373
+ assert !Person.column_methods_hash.include?(:last_name)
374
+ end
375
+
376
+ def test_add_rename
377
+ Person.delete_all
378
+
379
+ begin
380
+ Person.connection.add_column "people", "girlfriend", :string
381
+ Person.reset_column_information
382
+ Person.create :girlfriend => 'bobette'
383
+
384
+ Person.connection.rename_column "people", "girlfriend", "exgirlfriend"
385
+
386
+ Person.reset_column_information
387
+ bob = Person.find(:first)
388
+
389
+ assert_equal "bobette", bob.exgirlfriend
390
+ ensure
391
+ Person.connection.remove_column("people", "girlfriend") rescue nil
392
+ Person.connection.remove_column("people", "exgirlfriend") rescue nil
393
+ end
394
+
395
+ end
396
+
397
+ def test_rename_column_using_symbol_arguments
398
+ begin
399
+ names_before = Person.find(:all).map(&:first_name)
400
+ Person.connection.rename_column :people, :first_name, :nick_name
401
+ Person.reset_column_information
402
+ assert Person.column_names.include?("nick_name")
403
+ assert_equal names_before, Person.find(:all).map(&:nick_name)
404
+ ensure
405
+ Person.connection.remove_column("people","nick_name")
406
+ Person.connection.add_column("people","first_name", :string)
407
+ end
408
+ end
409
+
410
+ def test_rename_column
411
+ begin
412
+ names_before = Person.find(:all).map(&:first_name)
413
+ Person.connection.rename_column "people", "first_name", "nick_name"
414
+ Person.reset_column_information
415
+ assert Person.column_names.include?("nick_name")
416
+ assert_equal names_before, Person.find(:all).map(&:nick_name)
417
+ ensure
418
+ Person.connection.remove_column("people","nick_name")
419
+ Person.connection.add_column("people","first_name", :string)
420
+ end
421
+ end
422
+
423
+ def test_rename_column_with_sql_reserved_word
424
+ begin
425
+ assert_nothing_raised { Person.connection.rename_column "people", "first_name", "group" }
426
+ Person.reset_column_information
427
+ assert Person.column_names.include?("group")
428
+ ensure
429
+ Person.connection.remove_column("people", "group") rescue nil
430
+ Person.connection.add_column("people", "first_name", :string) rescue nil
431
+ end
432
+ end
433
+
434
+ def test_change_type_of_not_null_column
435
+ assert_nothing_raised do
436
+ Topic.connection.change_column "topics", "written_on", :datetime, :null => false
437
+ Topic.reset_column_information
438
+
439
+ Topic.connection.change_column "topics", "written_on", :datetime, :null => false
440
+ Topic.reset_column_information
441
+ end
442
+ end
443
+
444
+ def test_rename_table
445
+ begin
446
+ ActiveRecord::Base.connection.create_table :octopuses do |t|
447
+ t.column :url, :string
448
+ end
449
+ ActiveRecord::Base.connection.rename_table :octopuses, :octopi
450
+
451
+ # Using explicit id in insert for compatibility across all databases
452
+ con = ActiveRecord::Base.connection
453
+ con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
454
+ 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')" }
455
+ con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
456
+
457
+ assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
458
+
459
+ ensure
460
+ ActiveRecord::Base.connection.drop_table :octopuses rescue nil
461
+ ActiveRecord::Base.connection.drop_table :octopi rescue nil
462
+ end
463
+ end
464
+
465
+ def test_change_column_nullability
466
+ Person.delete_all
467
+ Person.connection.add_column "people", "funny", :boolean
468
+ Person.reset_column_information
469
+ assert Person.columns_hash["funny"].null, "Column 'funny' must initially allow nulls"
470
+ Person.connection.change_column "people", "funny", :boolean, :null => false, :default => true
471
+ Person.reset_column_information
472
+ assert !Person.columns_hash["funny"].null, "Column 'funny' must *not* allow nulls at this point"
473
+ Person.connection.change_column "people", "funny", :boolean, :null => true
474
+ Person.reset_column_information
475
+ assert Person.columns_hash["funny"].null, "Column 'funny' must allow nulls again at this point"
476
+ end
477
+
478
+ def test_rename_table_with_an_index
479
+ begin
480
+ ActiveRecord::Base.connection.create_table :octopuses do |t|
481
+ t.column :url, :string
482
+ end
483
+ ActiveRecord::Base.connection.add_index :octopuses, :url
484
+
485
+ ActiveRecord::Base.connection.rename_table :octopuses, :octopi
486
+
487
+ # Using explicit id in insert for compatibility across all databases
488
+ con = ActiveRecord::Base.connection
489
+ con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
490
+ 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')" }
491
+ con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
492
+
493
+ assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
494
+ assert ActiveRecord::Base.connection.indexes(:octopi).first.columns.include?("url")
495
+ ensure
496
+ ActiveRecord::Base.connection.drop_table :octopuses rescue nil
497
+ ActiveRecord::Base.connection.drop_table :octopi rescue nil
498
+ end
499
+ end
500
+
501
+ def test_change_column
502
+ Person.connection.add_column 'people', 'age', :integer
503
+ old_columns = Person.connection.columns(Person.table_name, "#{name} Columns")
504
+ assert old_columns.find { |c| c.name == 'age' and c.type == :integer }
505
+
506
+ assert_nothing_raised { Person.connection.change_column "people", "age", :string }
507
+
508
+ new_columns = Person.connection.columns(Person.table_name, "#{name} Columns")
509
+ assert_nil new_columns.find { |c| c.name == 'age' and c.type == :integer }
510
+ assert new_columns.find { |c| c.name == 'age' and c.type == :string }
511
+
512
+ old_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns")
513
+ assert old_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
514
+ assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => false }
515
+ new_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns")
516
+ assert_nil new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
517
+ assert new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == false }
518
+ assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => true }
519
+ end
520
+
521
+ def test_change_column_with_nil_default
522
+ Person.connection.add_column "people", "contributor", :boolean, :default => true
523
+ Person.reset_column_information
524
+ assert Person.new.contributor?
525
+
526
+ assert_nothing_raised { Person.connection.change_column "people", "contributor", :boolean, :default => nil }
527
+ Person.reset_column_information
528
+ assert !Person.new.contributor?
529
+ assert_nil Person.new.contributor
530
+ ensure
531
+ Person.connection.remove_column("people", "contributor") rescue nil
532
+ end
533
+
534
+ def test_change_column_with_new_default
535
+ Person.connection.add_column "people", "administrator", :boolean, :default => true
536
+ Person.reset_column_information
537
+ assert Person.new.administrator?
538
+
539
+ assert_nothing_raised { Person.connection.change_column "people", "administrator", :boolean, :default => false }
540
+ Person.reset_column_information
541
+ assert !Person.new.administrator?
542
+ ensure
543
+ Person.connection.remove_column("people", "administrator") rescue nil
544
+ end
545
+
546
+ def test_change_column_default
547
+ Person.connection.change_column_default "people", "first_name", "Tester"
548
+ Person.reset_column_information
549
+ assert_equal "Tester", Person.new.first_name
550
+ end
551
+
552
+ def test_change_column_quotes_column_names
553
+ Person.connection.create_table :testings do |t|
554
+ t.column :select, :string
555
+ end
556
+
557
+ assert_nothing_raised { Person.connection.change_column :testings, :select, :string, :limit => 10 }
558
+
559
+ assert_nothing_raised { Person.connection.execute "insert into testings (#{Person.connection.quote_column_name('select')}) values ('7 chars')" }
560
+ ensure
561
+ Person.connection.drop_table :testings rescue nil
562
+ end
563
+
564
+ def test_change_column_default_to_null
565
+ Person.connection.change_column_default "people", "first_name", nil
566
+ Person.reset_column_information
567
+ assert_nil Person.new.first_name
568
+ end
569
+
570
+ def test_add_table
571
+ assert !Reminder.table_exists?
572
+
573
+ WeNeedReminders.up
574
+
575
+ assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
576
+ assert_equal "hello world", Reminder.find(:first).content
577
+
578
+ WeNeedReminders.down
579
+ assert_raises(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
580
+ end
581
+
582
+ def test_add_table_with_decimals
583
+ Person.connection.drop_table :big_numbers rescue nil
584
+
585
+ assert !BigNumber.table_exists?
586
+ GiveMeBigNumbers.up
587
+
588
+ assert BigNumber.create(
589
+ :bank_balance => 1586.43,
590
+ :big_bank_balance => BigDecimal("1000234000567.95"),
591
+ :world_population => 6000000000,
592
+ :my_house_population => 3,
593
+ :value_of_e => BigDecimal("2.7182818284590452353602875")
594
+ )
595
+
596
+ b = BigNumber.find(:first)
597
+ assert_not_nil b
598
+
599
+ assert_not_nil b.bank_balance
600
+ assert_not_nil b.big_bank_balance
601
+ assert_not_nil b.world_population
602
+ assert_not_nil b.my_house_population
603
+ assert_not_nil b.value_of_e
604
+
605
+ # TODO: set world_population >= 2**62 to cover 64-bit platforms and test
606
+ # is_a?(Bignum)
607
+ assert_kind_of Integer, b.world_population
608
+ assert_equal 6000000000, b.world_population
609
+ assert_kind_of Fixnum, b.my_house_population
610
+ assert_equal 3, b.my_house_population
611
+ assert_kind_of BigDecimal, b.bank_balance
612
+ assert_equal BigDecimal("1586.43"), b.bank_balance
613
+ assert_kind_of BigDecimal, b.big_bank_balance
614
+ assert_equal BigDecimal("1000234000567.95"), b.big_bank_balance
615
+
616
+ # This one is fun. The 'value_of_e' field is defined as 'DECIMAL' with
617
+ # precision/scale explicitly left out. By the SQL standard, numbers
618
+ # assigned to this field should be truncated but that's seldom respected.
619
+ if current_adapter?(:PostgreSQLAdapter, :SQLite2Adapter)
620
+ # - PostgreSQL changes the SQL spec on columns declared simply as
621
+ # "decimal" to something more useful: instead of being given a scale
622
+ # of 0, they take on the compile-time limit for precision and scale,
623
+ # so the following should succeed unless you have used really wacky
624
+ # compilation options
625
+ # - SQLite2 has the default behavior of preserving all data sent in,
626
+ # so this happens there too
627
+ assert_kind_of BigDecimal, b.value_of_e
628
+ assert_equal BigDecimal("2.7182818284590452353602875"), b.value_of_e
629
+ elsif current_adapter?(:SQLiteAdapter)
630
+ # - SQLite3 stores a float, in violation of SQL
631
+ assert_kind_of BigDecimal, b.value_of_e
632
+ assert_equal BigDecimal("2.71828182845905"), b.value_of_e
633
+ elsif current_adapter?(:SQLServer)
634
+ # - SQL Server rounds instead of truncating
635
+ assert_kind_of Fixnum, b.value_of_e
636
+ assert_equal 3, b.value_of_e
637
+ else
638
+ # - SQL standard is an integer
639
+ assert_kind_of Fixnum, b.value_of_e
640
+ assert_equal 2, b.value_of_e
641
+ end
642
+
643
+ GiveMeBigNumbers.down
644
+ assert_raises(ActiveRecord::StatementInvalid) { BigNumber.find(:first) }
645
+ end
646
+
647
+ def test_migrator
648
+ assert !Person.column_methods_hash.include?(:last_name)
649
+ assert !Reminder.table_exists?
650
+
651
+ ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/')
652
+
653
+ assert_equal 3, ActiveRecord::Migrator.current_version
654
+ Person.reset_column_information
655
+ assert Person.column_methods_hash.include?(:last_name)
656
+ assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
657
+ assert_equal "hello world", Reminder.find(:first).content
658
+
659
+ ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/')
660
+
661
+ assert_equal 0, ActiveRecord::Migrator.current_version
662
+ Person.reset_column_information
663
+ assert !Person.column_methods_hash.include?(:last_name)
664
+ assert_raises(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
665
+ end
666
+
667
+ def test_migrator_one_up
668
+ assert !Person.column_methods_hash.include?(:last_name)
669
+ assert !Reminder.table_exists?
670
+
671
+ ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
672
+
673
+ Person.reset_column_information
674
+ assert Person.column_methods_hash.include?(:last_name)
675
+ assert !Reminder.table_exists?
676
+
677
+ ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 2)
678
+
679
+ assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
680
+ assert_equal "hello world", Reminder.find(:first).content
681
+ end
682
+
683
+ def test_migrator_one_down
684
+ ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/')
685
+
686
+ ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
687
+
688
+ Person.reset_column_information
689
+ assert Person.column_methods_hash.include?(:last_name)
690
+ assert !Reminder.table_exists?
691
+ end
692
+
693
+ def test_migrator_one_up_one_down
694
+ ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
695
+ ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/', 0)
696
+
697
+ assert !Person.column_methods_hash.include?(:last_name)
698
+ assert !Reminder.table_exists?
699
+ end
700
+
701
+ def test_migrator_verbosity
702
+ ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
703
+ assert PeopleHaveLastNames.message_count > 0
704
+ PeopleHaveLastNames.message_count = 0
705
+
706
+ ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/', 0)
707
+ assert PeopleHaveLastNames.message_count > 0
708
+ PeopleHaveLastNames.message_count = 0
709
+ end
710
+
711
+ def test_migrator_verbosity_off
712
+ PeopleHaveLastNames.verbose = false
713
+ ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
714
+ assert PeopleHaveLastNames.message_count.zero?
715
+ ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/', 0)
716
+ assert PeopleHaveLastNames.message_count.zero?
717
+ end
718
+
719
+ def test_migrator_going_down_due_to_version_target
720
+ ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
721
+ ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations/', 0)
722
+
723
+ assert !Person.column_methods_hash.include?(:last_name)
724
+ assert !Reminder.table_exists?
725
+
726
+ ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations/')
727
+
728
+ Person.reset_column_information
729
+ assert Person.column_methods_hash.include?(:last_name)
730
+ assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
731
+ assert_equal "hello world", Reminder.find(:first).content
732
+ end
733
+
734
+ def test_schema_info_table_name
735
+ ActiveRecord::Base.table_name_prefix = "prefix_"
736
+ ActiveRecord::Base.table_name_suffix = "_suffix"
737
+ Reminder.reset_table_name
738
+ assert_equal "prefix_schema_info_suffix", ActiveRecord::Migrator.schema_info_table_name
739
+ ActiveRecord::Base.table_name_prefix = ""
740
+ ActiveRecord::Base.table_name_suffix = ""
741
+ Reminder.reset_table_name
742
+ assert_equal "schema_info", ActiveRecord::Migrator.schema_info_table_name
743
+ ensure
744
+ ActiveRecord::Base.table_name_prefix = ""
745
+ ActiveRecord::Base.table_name_suffix = ""
746
+ end
747
+
748
+ def test_proper_table_name
749
+ assert_equal "table", ActiveRecord::Migrator.proper_table_name('table')
750
+ assert_equal "table", ActiveRecord::Migrator.proper_table_name(:table)
751
+ assert_equal "reminders", ActiveRecord::Migrator.proper_table_name(Reminder)
752
+ Reminder.reset_table_name
753
+ assert_equal Reminder.table_name, ActiveRecord::Migrator.proper_table_name(Reminder)
754
+
755
+ # Use the model's own prefix/suffix if a model is given
756
+ ActiveRecord::Base.table_name_prefix = "ARprefix_"
757
+ ActiveRecord::Base.table_name_suffix = "_ARsuffix"
758
+ Reminder.table_name_prefix = 'prefix_'
759
+ Reminder.table_name_suffix = '_suffix'
760
+ Reminder.reset_table_name
761
+ assert_equal "prefix_reminders_suffix", ActiveRecord::Migrator.proper_table_name(Reminder)
762
+ Reminder.table_name_prefix = ''
763
+ Reminder.table_name_suffix = ''
764
+ Reminder.reset_table_name
765
+
766
+ # Use AR::Base's prefix/suffix if string or symbol is given
767
+ ActiveRecord::Base.table_name_prefix = "prefix_"
768
+ ActiveRecord::Base.table_name_suffix = "_suffix"
769
+ Reminder.reset_table_name
770
+ assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name('table')
771
+ assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name(:table)
772
+ ActiveRecord::Base.table_name_prefix = ""
773
+ ActiveRecord::Base.table_name_suffix = ""
774
+ Reminder.reset_table_name
775
+ end
776
+
777
+ def test_add_drop_table_with_prefix_and_suffix
778
+ assert !Reminder.table_exists?
779
+ ActiveRecord::Base.table_name_prefix = 'prefix_'
780
+ ActiveRecord::Base.table_name_suffix = '_suffix'
781
+ Reminder.reset_table_name
782
+ Reminder.reset_sequence_name
783
+ WeNeedReminders.up
784
+ assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
785
+ assert_equal "hello world", Reminder.find(:first).content
786
+
787
+ WeNeedReminders.down
788
+ assert_raises(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
789
+ ensure
790
+ ActiveRecord::Base.table_name_prefix = ''
791
+ ActiveRecord::Base.table_name_suffix = ''
792
+ Reminder.reset_table_name
793
+ Reminder.reset_sequence_name
794
+ end
795
+
796
+ def test_create_table_with_binary_column
797
+ Person.connection.drop_table :binary_testings rescue nil
798
+
799
+ assert_nothing_raised {
800
+ Person.connection.create_table :binary_testings do |t|
801
+ t.column "data", :binary, :null => false
802
+ end
803
+ }
804
+
805
+ columns = Person.connection.columns(:binary_testings)
806
+ data_column = columns.detect { |c| c.name == "data" }
807
+
808
+ if current_adapter?(:MysqlAdapter)
809
+ assert_equal '', data_column.default
810
+ else
811
+ assert_nil data_column.default
812
+ end
813
+
814
+ Person.connection.drop_table :binary_testings rescue nil
815
+ end
816
+
817
+ def test_migrator_with_duplicates
818
+ assert_raises(ActiveRecord::DuplicateMigrationVersionError) do
819
+ ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations_with_duplicate/', nil)
820
+ end
821
+ end
822
+
823
+ def test_migrator_with_missing_version_numbers
824
+ ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations_with_missing_versions/', 500)
825
+ assert !Person.column_methods_hash.include?(:middle_name)
826
+ assert_equal 4, ActiveRecord::Migrator.current_version
827
+
828
+ ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations_with_missing_versions/', 2)
829
+ Person.reset_column_information
830
+ assert !Reminder.table_exists?
831
+ assert Person.column_methods_hash.include?(:last_name)
832
+ assert_equal 2, ActiveRecord::Migrator.current_version
833
+ end
834
+
835
+ def test_create_table_with_custom_sequence_name
836
+ return unless current_adapter? :OracleAdapter
837
+
838
+ # table name is 29 chars, the standard sequence name will
839
+ # be 33 chars and fail
840
+ assert_raises(ActiveRecord::StatementInvalid) do
841
+ begin
842
+ Person.connection.create_table :table_with_name_thats_just_ok do |t|
843
+ t.column :foo, :string, :null => false
844
+ end
845
+ ensure
846
+ Person.connection.drop_table :table_with_name_thats_just_ok rescue nil
847
+ end
848
+ end
849
+
850
+ # should be all good w/ a custom sequence name
851
+ assert_nothing_raised do
852
+ begin
853
+ Person.connection.create_table :table_with_name_thats_just_ok,
854
+ :sequence_name => 'suitably_short_seq' do |t|
855
+ t.column :foo, :string, :null => false
856
+ end
857
+
858
+ Person.connection.execute("select suitably_short_seq.nextval from dual")
859
+
860
+ ensure
861
+ Person.connection.drop_table :table_with_name_thats_just_ok,
862
+ :sequence_name => 'suitably_short_seq' rescue nil
863
+ end
864
+ end
865
+
866
+ # confirm the custom sequence got dropped
867
+ assert_raises(ActiveRecord::StatementInvalid) do
868
+ Person.connection.execute("select suitably_short_seq.nextval from dual")
869
+ end
870
+ end
871
+ end
872
+
873
+ uses_mocha 'Sexy migration tests' do
874
+ class SexyMigrationsTest < Test::Unit::TestCase
875
+ def test_references_column_type_adds_id
876
+ with_new_table do |t|
877
+ t.expects(:column).with('customer_id', :integer, {})
878
+ t.references :customer
879
+ end
880
+ end
881
+
882
+ def test_references_column_type_with_polymorphic_adds_type
883
+ with_new_table do |t|
884
+ t.expects(:column).with('taggable_type', :string, {})
885
+ t.expects(:column).with('taggable_id', :integer, {})
886
+ t.references :taggable, :polymorphic => true
887
+ end
888
+ end
889
+
890
+ def test_belongs_to_works_like_references
891
+ with_new_table do |t|
892
+ t.expects(:column).with('customer_id', :integer, {})
893
+ t.belongs_to :customer
894
+ end
895
+ end
896
+
897
+ def test_timestamps_creates_updated_at_and_created_at
898
+ with_new_table do |t|
899
+ t.expects(:column).with(:created_at, :datetime)
900
+ t.expects(:column).with(:updated_at, :datetime)
901
+ t.timestamps
902
+ end
903
+ end
904
+
905
+ def test_integer_creates_integer_column
906
+ with_new_table do |t|
907
+ t.expects(:column).with(:foo, 'integer', {})
908
+ t.expects(:column).with(:bar, 'integer', {})
909
+ t.integer :foo, :bar
910
+ end
911
+ end
912
+
913
+ def test_string_creates_string_column
914
+ with_new_table do |t|
915
+ t.expects(:column).with(:foo, 'string', {})
916
+ t.expects(:column).with(:bar, 'string', {})
917
+ t.string :foo, :bar
918
+ end
919
+ end
920
+
921
+ protected
922
+ def with_new_table
923
+ Person.connection.create_table :delete_me do |t|
924
+ yield t
925
+ end
926
+ ensure
927
+ Person.connection.drop_table :delete_me rescue nil
928
+ end
929
+
930
+ end # SexyMigrationsTest
931
+ end # uses_mocha
932
+ end
933
+