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,17 @@
1
+ require 'abstract_unit'
2
+ require 'fixtures/topic'
3
+ require 'fixtures/subject'
4
+
5
+ # confirm that synonyms work just like tables; in this case
6
+ # the "subjects" table in Oracle (defined in oci.sql) is just
7
+ # a synonym to the "topics" table
8
+
9
+ class TestOracleSynonym < Test::Unit::TestCase
10
+
11
+ def test_oracle_synonym
12
+ topic = Topic.new
13
+ subject = Subject.new
14
+ assert_equal(topic.attributes, subject.attributes)
15
+ end
16
+
17
+ end
@@ -0,0 +1,23 @@
1
+ require 'abstract_unit'
2
+ require "#{File.dirname(__FILE__)}/../lib/active_record/schema"
3
+
4
+ if ActiveRecord::Base.connection.supports_migrations?
5
+ class Order < ActiveRecord::Base
6
+ self.table_name = '[order]'
7
+ end
8
+
9
+ class TableNameTest < Test::Unit::TestCase
10
+ self.use_transactional_fixtures = false
11
+
12
+ # Ensures Model.columns works when using SQLServer escape characters.
13
+ # Enables legacy schemas using SQL reserved words as table names.
14
+ # Should work with table names with spaces as well ('table name').
15
+ def test_escaped_table_name
16
+ assert_nothing_raised do
17
+ ActiveRecord::Base.connection.select_all 'SELECT * FROM [order]'
18
+ end
19
+ assert_equal '[order]', Order.table_name
20
+ assert_equal 5, Order.columns.length
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,48 @@
1
+ require 'abstract_unit'
2
+ require 'fixtures/topic'
3
+ require 'fixtures/reply'
4
+
5
+ unless %w(FrontBase).include? ActiveRecord::Base.connection.adapter_name
6
+ class ThreadedConnectionsTest < Test::Unit::TestCase
7
+ self.use_transactional_fixtures = false
8
+
9
+ fixtures :topics
10
+
11
+ def setup
12
+ @connection = ActiveRecord::Base.remove_connection
13
+ @connections = []
14
+ @allow_concurrency = ActiveRecord::Base.allow_concurrency
15
+ end
16
+
17
+ def teardown
18
+ # clear the connection cache
19
+ ActiveRecord::Base.send(:clear_all_cached_connections!)
20
+ # set allow_concurrency to saved value
21
+ ActiveRecord::Base.allow_concurrency = @allow_concurrency
22
+ # reestablish old connection
23
+ ActiveRecord::Base.establish_connection(@connection)
24
+ end
25
+
26
+ def gather_connections(use_threaded_connections)
27
+ ActiveRecord::Base.allow_concurrency = use_threaded_connections
28
+ ActiveRecord::Base.establish_connection(@connection)
29
+
30
+ 5.times do
31
+ Thread.new do
32
+ Topic.find :first
33
+ @connections << ActiveRecord::Base.active_connections.values.first
34
+ end.join
35
+ end
36
+ end
37
+
38
+ def test_threaded_connections
39
+ gather_connections(true)
40
+ assert_equal @connections.uniq.length, 5
41
+ end
42
+
43
+ def test_unthreaded_connections
44
+ gather_connections(false)
45
+ assert_equal @connections.uniq.length, 1
46
+ end
47
+ end
48
+ end
@@ -1,25 +1,71 @@
1
1
  require 'abstract_unit'
2
2
  require 'fixtures/topic'
3
-
3
+ require 'fixtures/reply'
4
+ require 'fixtures/developer'
4
5
 
5
6
  class TransactionTest < Test::Unit::TestCase
7
+ self.use_transactional_fixtures = false
8
+ fixtures :topics, :developers
9
+
6
10
  def setup
7
- @topics = create_fixtures "topics"
8
- @first, @second = Topic.find(1, 2)
11
+ @first, @second = Topic.find(1, 2).sort_by { |t| t.id }
9
12
  end
10
13
 
11
- def test_succesful
14
+ def test_successful
12
15
  Topic.transaction do
13
- @first.approved = 1
14
- @second.approved = 0
16
+ @first.approved = true
17
+ @second.approved = false
18
+ @first.save
19
+ @second.save
20
+ end
21
+
22
+ assert Topic.find(1).approved?, "First should have been approved"
23
+ assert !Topic.find(2).approved?, "Second should have been unapproved"
24
+ end
25
+
26
+ def transaction_with_return
27
+ Topic.transaction do
28
+ @first.approved = true
29
+ @second.approved = false
30
+ @first.save
31
+ @second.save
32
+ return
33
+ end
34
+ end
35
+
36
+ def test_successful_with_return
37
+ class << Topic.connection
38
+ alias :real_commit_db_transaction :commit_db_transaction
39
+ def commit_db_transaction
40
+ $committed = true
41
+ real_commit_db_transaction
42
+ end
43
+ end
44
+
45
+ $committed = false
46
+ transaction_with_return
47
+ assert $committed
48
+
49
+ assert Topic.find(1).approved?, "First should have been approved"
50
+ assert !Topic.find(2).approved?, "Second should have been unapproved"
51
+ ensure
52
+ class << Topic.connection
53
+ alias :commit_db_transaction :real_commit_db_transaction rescue nil
54
+ end
55
+ end
56
+
57
+ def test_successful_with_instance_method
58
+ @first.transaction do
59
+ @first.approved = true
60
+ @second.approved = false
15
61
  @first.save
16
62
  @second.save
17
63
  end
18
-
64
+
19
65
  assert Topic.find(1).approved?, "First should have been approved"
20
66
  assert !Topic.find(2).approved?, "Second should have been unapproved"
21
67
  end
22
-
68
+
23
69
  def test_failing_on_exception
24
70
  begin
25
71
  Topic.transaction do
@@ -35,28 +81,12 @@ class TransactionTest < Test::Unit::TestCase
35
81
 
36
82
  assert @first.approved?, "First should still be changed in the objects"
37
83
  assert !@second.approved?, "Second should still be changed in the objects"
38
-
84
+
39
85
  assert !Topic.find(1).approved?, "First shouldn't have been approved"
40
86
  assert Topic.find(2).approved?, "Second should still be approved"
41
87
  end
42
-
43
- def test_failing_with_object_rollback
44
- begin
45
- Topic.transaction(@first, @second) do
46
- @first.approved = true
47
- @second.approved = false
48
- @first.save
49
- @second.save
50
- raise "Bad things!"
51
- end
52
- rescue
53
- # caught it
54
- end
55
-
56
- assert !@first.approved?, "First shouldn't have been approved"
57
- assert @second.approved?, "Second should still be approved"
58
- end
59
-
88
+
89
+
60
90
  def test_callback_rollback_in_save
61
91
  add_exception_raising_after_save_callback_to_topic
62
92
 
@@ -72,12 +102,180 @@ class TransactionTest < Test::Unit::TestCase
72
102
  end
73
103
  end
74
104
 
105
+ def test_callback_rollback_in_create
106
+ new_topic = Topic.new(
107
+ :title => "A new topic",
108
+ :author_name => "Ben",
109
+ :author_email_address => "ben@example.com",
110
+ :written_on => "2003-07-16t15:28:11.2233+01:00",
111
+ :last_read => "2004-04-15",
112
+ :bonus_time => "2005-01-30t15:28:00.00+01:00",
113
+ :content => "Have a nice day",
114
+ :approved => false)
115
+ new_record_snapshot = new_topic.new_record?
116
+ id_present = new_topic.has_attribute?(Topic.primary_key)
117
+ id_snapshot = new_topic.id
118
+
119
+ # Make sure the second save gets the after_create callback called.
120
+ 2.times do
121
+ begin
122
+ add_exception_raising_after_create_callback_to_topic
123
+ new_topic.approved = true
124
+ new_topic.save
125
+ flunk
126
+ rescue => e
127
+ assert_equal "Make the transaction rollback", e.message
128
+ assert_equal new_record_snapshot, new_topic.new_record?, "The topic should have its old new_record value"
129
+ assert_equal id_snapshot, new_topic.id, "The topic should have its old id"
130
+ assert_equal id_present, new_topic.has_attribute?(Topic.primary_key)
131
+ ensure
132
+ remove_exception_raising_after_create_callback_to_topic
133
+ end
134
+ end
135
+ end
136
+
137
+ def test_nested_explicit_transactions
138
+ Topic.transaction do
139
+ Topic.transaction do
140
+ @first.approved = true
141
+ @second.approved = false
142
+ @first.save
143
+ @second.save
144
+ end
145
+ end
146
+
147
+ assert Topic.find(1).approved?, "First should have been approved"
148
+ assert !Topic.find(2).approved?, "Second should have been unapproved"
149
+ end
150
+
151
+ def test_manually_rolling_back_a_transaction
152
+ Topic.transaction do
153
+ @first.approved = true
154
+ @second.approved = false
155
+ @first.save
156
+ @second.save
157
+
158
+ raise ActiveRecord::Rollback
159
+ end
160
+
161
+ assert @first.approved?, "First should still be changed in the objects"
162
+ assert !@second.approved?, "Second should still be changed in the objects"
163
+
164
+ assert !Topic.find(1).approved?, "First shouldn't have been approved"
165
+ assert Topic.find(2).approved?, "Second should still be approved"
166
+ end
167
+
168
+ uses_mocha 'mocking connection.commit_db_transaction' do
169
+ def test_rollback_when_commit_raises
170
+ Topic.connection.expects(:begin_db_transaction)
171
+ Topic.connection.expects(:commit_db_transaction).raises('OH NOES')
172
+ Topic.connection.expects(:rollback_db_transaction)
173
+
174
+ assert_raise RuntimeError do
175
+ Topic.transaction do
176
+ # do nothing
177
+ end
178
+ end
179
+ end
180
+ end
181
+
75
182
  private
76
183
  def add_exception_raising_after_save_callback_to_topic
77
184
  Topic.class_eval { def after_save() raise "Make the transaction rollback" end }
78
185
  end
79
-
186
+
80
187
  def remove_exception_raising_after_save_callback_to_topic
81
188
  Topic.class_eval { remove_method :after_save }
82
189
  end
83
- end
190
+
191
+ def add_exception_raising_after_create_callback_to_topic
192
+ Topic.class_eval { def after_create() raise "Make the transaction rollback" end }
193
+ end
194
+
195
+ def remove_exception_raising_after_create_callback_to_topic
196
+ Topic.class_eval { remove_method :after_create }
197
+ end
198
+ end
199
+
200
+ if current_adapter?(:PostgreSQLAdapter)
201
+ class ConcurrentTransactionTest < TransactionTest
202
+ def setup
203
+ @allow_concurrency = ActiveRecord::Base.allow_concurrency
204
+ ActiveRecord::Base.allow_concurrency = true
205
+ super
206
+ end
207
+
208
+ def teardown
209
+ super
210
+ ActiveRecord::Base.allow_concurrency = @allow_concurrency
211
+ end
212
+
213
+ # This will cause transactions to overlap and fail unless they are performed on
214
+ # separate database connections.
215
+ def test_transaction_per_thread
216
+ assert_nothing_raised do
217
+ threads = (1..3).map do
218
+ Thread.new do
219
+ Topic.transaction do
220
+ topic = Topic.find(1)
221
+ topic.approved = !topic.approved?
222
+ topic.save!
223
+ topic.approved = !topic.approved?
224
+ topic.save!
225
+ end
226
+ end
227
+ end
228
+
229
+ threads.each { |t| t.join }
230
+ end
231
+ end
232
+
233
+ # Test for dirty reads among simultaneous transactions.
234
+ def test_transaction_isolation__read_committed
235
+ # Should be invariant.
236
+ original_salary = Developer.find(1).salary
237
+ temporary_salary = 200000
238
+
239
+ assert_nothing_raised do
240
+ threads = (1..3).map do
241
+ Thread.new do
242
+ Developer.transaction do
243
+ # Expect original salary.
244
+ dev = Developer.find(1)
245
+ assert_equal original_salary, dev.salary
246
+
247
+ dev.salary = temporary_salary
248
+ dev.save!
249
+
250
+ # Expect temporary salary.
251
+ dev = Developer.find(1)
252
+ assert_equal temporary_salary, dev.salary
253
+
254
+ dev.salary = original_salary
255
+ dev.save!
256
+
257
+ # Expect original salary.
258
+ dev = Developer.find(1)
259
+ assert_equal original_salary, dev.salary
260
+ end
261
+ end
262
+ end
263
+
264
+ # Keep our eyes peeled.
265
+ threads << Thread.new do
266
+ 10.times do
267
+ sleep 0.05
268
+ Developer.transaction do
269
+ # Always expect original salary.
270
+ assert_equal original_salary, Developer.find(1).salary
271
+ end
272
+ end
273
+ end
274
+
275
+ threads.each { |t| t.join }
276
+ end
277
+
278
+ assert_equal original_salary, Developer.find(1).salary
279
+ end
280
+ end
281
+ end
@@ -3,22 +3,30 @@ require 'abstract_unit'
3
3
  class TestRecord < ActiveRecord::Base
4
4
  end
5
5
 
6
- class TestUnconnectedAdaptor < Test::Unit::TestCase
6
+ class TestUnconnectedAdapter < Test::Unit::TestCase
7
+ self.use_transactional_fixtures = false
7
8
 
8
9
  def setup
9
- @connection = ActiveRecord::Base.remove_connection
10
+ @underlying = ActiveRecord::Base.connection
11
+ @specification = ActiveRecord::Base.remove_connection
10
12
  end
11
13
 
12
14
  def teardown
13
- ActiveRecord::Base.establish_connection(@connection)
15
+ @underlying = nil
16
+ ActiveRecord::Base.establish_connection(@specification)
14
17
  end
15
18
 
16
- def test_unconnected
19
+ def test_connection_no_longer_established
17
20
  assert_raise(ActiveRecord::ConnectionNotEstablished) do
18
- TestRecord.find(1)
21
+ TestRecord.find(1)
19
22
  end
23
+
20
24
  assert_raise(ActiveRecord::ConnectionNotEstablished) do
21
- TestRecord.new.save
25
+ TestRecord.new.save
22
26
  end
23
27
  end
28
+
29
+ def test_underlying_adapter_no_longer_active
30
+ assert !@underlying.active?, "Removed adapter should no longer be active"
31
+ end
24
32
  end