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,47 @@
1
+ require "cases/helper"
2
+ require 'models/contact'
3
+
4
+ class SerializationTest < ActiveRecord::TestCase
5
+ FORMATS = [ :xml, :json ]
6
+
7
+ def setup
8
+ @contact_attributes = {
9
+ :name => 'aaron stack',
10
+ :age => 25,
11
+ :avatar => 'binarydata',
12
+ :created_at => Time.utc(2006, 8, 1),
13
+ :awesome => false,
14
+ :preferences => { :gem => '<strong>ruby</strong>' }
15
+ }
16
+
17
+ @contact = Contact.new(@contact_attributes)
18
+ end
19
+
20
+ def test_serialize_should_be_reversible
21
+ for format in FORMATS
22
+ @serialized = Contact.new.send("to_#{format}")
23
+ contact = Contact.new.send("from_#{format}", @serialized)
24
+
25
+ assert_equal @contact_attributes.keys.collect(&:to_s).sort, contact.attributes.keys.collect(&:to_s).sort, "For #{format}"
26
+ end
27
+ end
28
+
29
+ def test_serialize_should_allow_attribute_only_filtering
30
+ for format in FORMATS
31
+ @serialized = Contact.new(@contact_attributes).send("to_#{format}", :only => [ :age, :name ])
32
+ contact = Contact.new.send("from_#{format}", @serialized)
33
+ assert_equal @contact_attributes[:name], contact.name, "For #{format}"
34
+ assert_nil contact.avatar, "For #{format}"
35
+ end
36
+ end
37
+
38
+ def test_serialize_should_allow_attribute_except_filtering
39
+ for format in FORMATS
40
+ @serialized = Contact.new(@contact_attributes).send("to_#{format}", :except => [ :age, :name ])
41
+ contact = Contact.new.send("from_#{format}", @serialized)
42
+ assert_nil contact.name, "For #{format}"
43
+ assert_nil contact.age, "For #{format}"
44
+ assert_equal @contact_attributes[:awesome], contact.awesome, "For #{format}"
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,17 @@
1
+ require "cases/helper"
2
+ require 'models/topic'
3
+ require 'models/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 < ActiveRecord::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,75 @@
1
+ require 'cases/helper'
2
+ require 'models/developer'
3
+ require 'models/owner'
4
+ require 'models/pet'
5
+
6
+ class TimestampTest < ActiveRecord::TestCase
7
+ fixtures :developers, :owners, :pets
8
+
9
+ def setup
10
+ @developer = Developer.first
11
+ @previously_updated_at = @developer.updated_at
12
+ end
13
+
14
+ def test_saving_a_changed_record_updates_its_timestamp
15
+ @developer.name = "Jack Bauer"
16
+ @developer.save!
17
+
18
+ assert @previously_updated_at != @developer.updated_at
19
+ end
20
+
21
+ def test_saving_a_unchanged_record_doesnt_update_its_timestamp
22
+ @developer.save!
23
+
24
+ assert @previously_updated_at == @developer.updated_at
25
+ end
26
+
27
+ def test_touching_a_record_updates_its_timestamp
28
+ @developer.touch
29
+
30
+ assert @previously_updated_at != @developer.updated_at
31
+ end
32
+
33
+ def test_touching_a_different_attribute
34
+ previously_created_at = @developer.created_at
35
+ @developer.touch(:created_at)
36
+
37
+ assert previously_created_at != @developer.created_at
38
+ end
39
+
40
+ def test_saving_a_record_with_a_belongs_to_that_specifies_touching_the_parent_should_update_the_parent_updated_at
41
+ pet = Pet.first
42
+ owner = pet.owner
43
+ previously_owner_updated_at = owner.updated_at
44
+
45
+ pet.name = "Fluffy the Third"
46
+ pet.save
47
+
48
+ assert previously_owner_updated_at != pet.owner.updated_at
49
+ end
50
+
51
+ def test_destroying_a_record_with_a_belongs_to_that_specifies_touching_the_parent_should_update_the_parent_updated_at
52
+ pet = Pet.first
53
+ owner = pet.owner
54
+ previously_owner_updated_at = owner.updated_at
55
+
56
+ pet.destroy
57
+
58
+ assert previously_owner_updated_at != pet.owner.updated_at
59
+ end
60
+
61
+ def test_saving_a_record_with_a_belongs_to_that_specifies_touching_a_specific_attribute_the_parent_should_update_that_attribute
62
+ Pet.belongs_to :owner, :touch => :happy_at
63
+
64
+ pet = Pet.first
65
+ owner = pet.owner
66
+ previously_owner_happy_at = owner.happy_at
67
+
68
+ pet.name = "Fluffy the Third"
69
+ pet.save
70
+
71
+ assert previously_owner_happy_at != pet.owner.happy_at
72
+ ensure
73
+ Pet.belongs_to :owner, :touch => true
74
+ end
75
+ end
@@ -0,0 +1,522 @@
1
+ require "cases/helper"
2
+ require 'models/topic'
3
+ require 'models/reply'
4
+ require 'models/developer'
5
+ require 'models/book'
6
+
7
+ class TransactionTest < ActiveRecord::TestCase
8
+ self.use_transactional_fixtures = false
9
+ fixtures :topics, :developers
10
+
11
+ def setup
12
+ @first, @second = Topic.find(1, 2).sort_by { |t| t.id }
13
+ end
14
+
15
+ def test_successful
16
+ Topic.transaction do
17
+ @first.approved = true
18
+ @second.approved = false
19
+ @first.save
20
+ @second.save
21
+ end
22
+
23
+ assert Topic.find(1).approved?, "First should have been approved"
24
+ assert !Topic.find(2).approved?, "Second should have been unapproved"
25
+ end
26
+
27
+ def transaction_with_return
28
+ Topic.transaction do
29
+ @first.approved = true
30
+ @second.approved = false
31
+ @first.save
32
+ @second.save
33
+ return
34
+ end
35
+ end
36
+
37
+ def test_successful_with_return
38
+ class << Topic.connection
39
+ alias :real_commit_db_transaction :commit_db_transaction
40
+ def commit_db_transaction
41
+ $committed = true
42
+ real_commit_db_transaction
43
+ end
44
+ end
45
+
46
+ $committed = false
47
+ transaction_with_return
48
+ assert $committed
49
+
50
+ assert Topic.find(1).approved?, "First should have been approved"
51
+ assert !Topic.find(2).approved?, "Second should have been unapproved"
52
+ ensure
53
+ class << Topic.connection
54
+ alias :commit_db_transaction :real_commit_db_transaction rescue nil
55
+ end
56
+ end
57
+
58
+ def test_successful_with_instance_method
59
+ @first.transaction do
60
+ @first.approved = true
61
+ @second.approved = false
62
+ @first.save
63
+ @second.save
64
+ end
65
+
66
+ assert Topic.find(1).approved?, "First should have been approved"
67
+ assert !Topic.find(2).approved?, "Second should have been unapproved"
68
+ end
69
+
70
+ def test_failing_on_exception
71
+ begin
72
+ Topic.transaction do
73
+ @first.approved = true
74
+ @second.approved = false
75
+ @first.save
76
+ @second.save
77
+ raise "Bad things!"
78
+ end
79
+ rescue
80
+ # caught it
81
+ end
82
+
83
+ assert @first.approved?, "First should still be changed in the objects"
84
+ assert !@second.approved?, "Second should still be changed in the objects"
85
+
86
+ assert !Topic.find(1).approved?, "First shouldn't have been approved"
87
+ assert Topic.find(2).approved?, "Second should still be approved"
88
+ end
89
+
90
+ def test_raising_exception_in_callback_rollbacks_in_save
91
+ add_exception_raising_after_save_callback_to_topic
92
+
93
+ begin
94
+ @first.approved = true
95
+ @first.save
96
+ flunk
97
+ rescue => e
98
+ assert_equal "Make the transaction rollback", e.message
99
+ assert !Topic.find(1).approved?
100
+ ensure
101
+ remove_exception_raising_after_save_callback_to_topic
102
+ end
103
+ end
104
+
105
+ def test_cancellation_from_before_destroy_rollbacks_in_destroy
106
+ add_cancelling_before_destroy_with_db_side_effect_to_topic
107
+ begin
108
+ nbooks_before_destroy = Book.count
109
+ status = @first.destroy
110
+ assert !status
111
+ assert_nothing_raised(ActiveRecord::RecordNotFound) { @first.reload }
112
+ assert_equal nbooks_before_destroy, Book.count
113
+ ensure
114
+ remove_cancelling_before_destroy_with_db_side_effect_to_topic
115
+ end
116
+ end
117
+
118
+ def test_cancellation_from_before_filters_rollbacks_in_save
119
+ %w(validation save).each do |filter|
120
+ send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic")
121
+ begin
122
+ nbooks_before_save = Book.count
123
+ original_author_name = @first.author_name
124
+ @first.author_name += '_this_should_not_end_up_in_the_db'
125
+ status = @first.save
126
+ assert !status
127
+ assert_equal original_author_name, @first.reload.author_name
128
+ assert_equal nbooks_before_save, Book.count
129
+ ensure
130
+ send("remove_cancelling_before_#{filter}_with_db_side_effect_to_topic")
131
+ end
132
+ end
133
+ end
134
+
135
+ def test_cancellation_from_before_filters_rollbacks_in_save!
136
+ %w(validation save).each do |filter|
137
+ send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic")
138
+ begin
139
+ nbooks_before_save = Book.count
140
+ original_author_name = @first.author_name
141
+ @first.author_name += '_this_should_not_end_up_in_the_db'
142
+ @first.save!
143
+ flunk
144
+ rescue => e
145
+ assert_equal original_author_name, @first.reload.author_name
146
+ assert_equal nbooks_before_save, Book.count
147
+ ensure
148
+ send("remove_cancelling_before_#{filter}_with_db_side_effect_to_topic")
149
+ end
150
+ end
151
+ end
152
+
153
+ def test_callback_rollback_in_create
154
+ new_topic = Topic.new(
155
+ :title => "A new topic",
156
+ :author_name => "Ben",
157
+ :author_email_address => "ben@example.com",
158
+ :written_on => "2003-07-16t15:28:11.2233+01:00",
159
+ :last_read => "2004-04-15",
160
+ :bonus_time => "2005-01-30t15:28:00.00+01:00",
161
+ :content => "Have a nice day",
162
+ :approved => false)
163
+ new_record_snapshot = new_topic.new_record?
164
+ id_present = new_topic.has_attribute?(Topic.primary_key)
165
+ id_snapshot = new_topic.id
166
+
167
+ # Make sure the second save gets the after_create callback called.
168
+ 2.times do
169
+ begin
170
+ add_exception_raising_after_create_callback_to_topic
171
+ new_topic.approved = true
172
+ new_topic.save
173
+ flunk
174
+ rescue => e
175
+ assert_equal "Make the transaction rollback", e.message
176
+ assert_equal new_record_snapshot, new_topic.new_record?, "The topic should have its old new_record value"
177
+ assert_equal id_snapshot, new_topic.id, "The topic should have its old id"
178
+ assert_equal id_present, new_topic.has_attribute?(Topic.primary_key)
179
+ ensure
180
+ remove_exception_raising_after_create_callback_to_topic
181
+ end
182
+ end
183
+ end
184
+
185
+ def test_nested_explicit_transactions
186
+ Topic.transaction do
187
+ Topic.transaction do
188
+ @first.approved = true
189
+ @second.approved = false
190
+ @first.save
191
+ @second.save
192
+ end
193
+ end
194
+
195
+ assert Topic.find(1).approved?, "First should have been approved"
196
+ assert !Topic.find(2).approved?, "Second should have been unapproved"
197
+ end
198
+
199
+ def test_manually_rolling_back_a_transaction
200
+ Topic.transaction do
201
+ @first.approved = true
202
+ @second.approved = false
203
+ @first.save
204
+ @second.save
205
+
206
+ raise ActiveRecord::Rollback
207
+ end
208
+
209
+ assert @first.approved?, "First should still be changed in the objects"
210
+ assert !@second.approved?, "Second should still be changed in the objects"
211
+
212
+ assert !Topic.find(1).approved?, "First shouldn't have been approved"
213
+ assert Topic.find(2).approved?, "Second should still be approved"
214
+ end
215
+
216
+ def test_invalid_keys_for_transaction
217
+ assert_raise ArgumentError do
218
+ Topic.transaction :nested => true do
219
+ end
220
+ end
221
+ end
222
+
223
+ def test_force_savepoint_in_nested_transaction
224
+ Topic.transaction do
225
+ @first.approved = true
226
+ @second.approved = false
227
+ @first.save!
228
+ @second.save!
229
+
230
+ begin
231
+ Topic.transaction :requires_new => true do
232
+ @first.happy = false
233
+ @first.save!
234
+ raise
235
+ end
236
+ rescue
237
+ end
238
+ end
239
+
240
+ assert @first.reload.approved?
241
+ assert !@second.reload.approved?
242
+ end if Topic.connection.supports_savepoints?
243
+
244
+ def test_no_savepoint_in_nested_transaction_without_force
245
+ Topic.transaction do
246
+ @first.approved = true
247
+ @second.approved = false
248
+ @first.save!
249
+ @second.save!
250
+
251
+ begin
252
+ Topic.transaction do
253
+ @first.approved = false
254
+ @first.save!
255
+ raise
256
+ end
257
+ rescue
258
+ end
259
+ end
260
+
261
+ assert !@first.reload.approved?
262
+ assert !@second.reload.approved?
263
+ end if Topic.connection.supports_savepoints?
264
+
265
+ def test_many_savepoints
266
+ Topic.transaction do
267
+ @first.content = "One"
268
+ @first.save!
269
+
270
+ begin
271
+ Topic.transaction :requires_new => true do
272
+ @first.content = "Two"
273
+ @first.save!
274
+
275
+ begin
276
+ Topic.transaction :requires_new => true do
277
+ @first.content = "Three"
278
+ @first.save!
279
+
280
+ begin
281
+ Topic.transaction :requires_new => true do
282
+ @first.content = "Four"
283
+ @first.save!
284
+ raise
285
+ end
286
+ rescue
287
+ end
288
+
289
+ @three = @first.reload.content
290
+ raise
291
+ end
292
+ rescue
293
+ end
294
+
295
+ @two = @first.reload.content
296
+ raise
297
+ end
298
+ rescue
299
+ end
300
+
301
+ @one = @first.reload.content
302
+ end
303
+
304
+ assert_equal "One", @one
305
+ assert_equal "Two", @two
306
+ assert_equal "Three", @three
307
+ end if Topic.connection.supports_savepoints?
308
+
309
+ def test_rollback_when_commit_raises
310
+ Topic.connection.expects(:begin_db_transaction)
311
+ Topic.connection.expects(:commit_db_transaction).raises('OH NOES')
312
+ Topic.connection.expects(:outside_transaction?).returns(false)
313
+ Topic.connection.expects(:rollback_db_transaction)
314
+
315
+ assert_raise RuntimeError do
316
+ Topic.transaction do
317
+ # do nothing
318
+ end
319
+ end
320
+ end
321
+
322
+ if current_adapter?(:PostgreSQLAdapter) && defined?(PGconn::PQTRANS_IDLE)
323
+ def test_outside_transaction_works
324
+ assert Topic.connection.outside_transaction?
325
+ Topic.connection.begin_db_transaction
326
+ assert !Topic.connection.outside_transaction?
327
+ Topic.connection.rollback_db_transaction
328
+ assert Topic.connection.outside_transaction?
329
+ end
330
+
331
+ def test_rollback_wont_be_executed_if_no_transaction_active
332
+ assert_raise RuntimeError do
333
+ Topic.transaction do
334
+ Topic.connection.rollback_db_transaction
335
+ Topic.connection.expects(:rollback_db_transaction).never
336
+ raise "Rails doesn't scale!"
337
+ end
338
+ end
339
+ end
340
+
341
+ def test_open_transactions_count_is_reset_to_zero_if_no_transaction_active
342
+ Topic.transaction do
343
+ Topic.transaction do
344
+ Topic.connection.rollback_db_transaction
345
+ end
346
+ assert_equal 0, Topic.connection.open_transactions
347
+ end
348
+ assert_equal 0, Topic.connection.open_transactions
349
+ end
350
+ end
351
+
352
+ def test_sqlite_add_column_in_transaction
353
+ return true unless current_adapter?(:SQLite3Adapter, :SQLiteAdapter)
354
+
355
+ # Test first if column creation/deletion works correctly when no
356
+ # transaction is in place.
357
+ #
358
+ # We go back to the connection for the column queries because
359
+ # Topic.columns is cached and won't report changes to the DB
360
+
361
+ assert_nothing_raised do
362
+ Topic.reset_column_information
363
+ Topic.connection.add_column('topics', 'stuff', :string)
364
+ assert Topic.column_names.include?('stuff')
365
+
366
+ Topic.reset_column_information
367
+ Topic.connection.remove_column('topics', 'stuff')
368
+ assert !Topic.column_names.include?('stuff')
369
+ end
370
+
371
+ if Topic.connection.supports_ddl_transactions?
372
+ assert_nothing_raised do
373
+ Topic.transaction { Topic.connection.add_column('topics', 'stuff', :string) }
374
+ end
375
+ else
376
+ Topic.transaction do
377
+ assert_raise(ActiveRecord::StatementInvalid) { Topic.connection.add_column('topics', 'stuff', :string) }
378
+ raise ActiveRecord::Rollback
379
+ end
380
+ end
381
+ end
382
+
383
+ private
384
+ def add_exception_raising_after_save_callback_to_topic
385
+ Topic.class_eval { def after_save() raise "Make the transaction rollback" end }
386
+ end
387
+
388
+ def remove_exception_raising_after_save_callback_to_topic
389
+ Topic.class_eval { remove_method :after_save }
390
+ end
391
+
392
+ def add_exception_raising_after_create_callback_to_topic
393
+ Topic.class_eval { def after_create() raise "Make the transaction rollback" end }
394
+ end
395
+
396
+ def remove_exception_raising_after_create_callback_to_topic
397
+ Topic.class_eval { remove_method :after_create }
398
+ end
399
+
400
+ %w(validation save destroy).each do |filter|
401
+ define_method("add_cancelling_before_#{filter}_with_db_side_effect_to_topic") do
402
+ Topic.class_eval "def before_#{filter}() Book.create; false end"
403
+ end
404
+
405
+ define_method("remove_cancelling_before_#{filter}_with_db_side_effect_to_topic") do
406
+ Topic.class_eval "remove_method :before_#{filter}"
407
+ end
408
+ end
409
+ end
410
+
411
+ class TransactionsWithTransactionalFixturesTest < ActiveRecord::TestCase
412
+ self.use_transactional_fixtures = true
413
+ fixtures :topics
414
+
415
+ def test_automatic_savepoint_in_outer_transaction
416
+ @first = Topic.find(1)
417
+
418
+ begin
419
+ Topic.transaction do
420
+ @first.approved = true
421
+ @first.save!
422
+ raise
423
+ end
424
+ rescue
425
+ assert !@first.reload.approved?
426
+ end
427
+ end
428
+
429
+ def test_no_automatic_savepoint_for_inner_transaction
430
+ @first = Topic.find(1)
431
+
432
+ Topic.transaction do
433
+ @first.approved = true
434
+ @first.save!
435
+
436
+ begin
437
+ Topic.transaction do
438
+ @first.approved = false
439
+ @first.save!
440
+ raise
441
+ end
442
+ rescue
443
+ end
444
+ end
445
+
446
+ assert !@first.reload.approved?
447
+ end
448
+ end if Topic.connection.supports_savepoints?
449
+
450
+ if current_adapter?(:PostgreSQLAdapter)
451
+ class ConcurrentTransactionTest < TransactionTest
452
+ use_concurrent_connections
453
+
454
+ # This will cause transactions to overlap and fail unless they are performed on
455
+ # separate database connections.
456
+ def test_transaction_per_thread
457
+ assert_nothing_raised do
458
+ threads = (1..3).map do
459
+ Thread.new do
460
+ Topic.transaction do
461
+ topic = Topic.find(1)
462
+ topic.approved = !topic.approved?
463
+ topic.save!
464
+ topic.approved = !topic.approved?
465
+ topic.save!
466
+ end
467
+ end
468
+ end
469
+
470
+ threads.each { |t| t.join }
471
+ end
472
+ end
473
+
474
+ # Test for dirty reads among simultaneous transactions.
475
+ def test_transaction_isolation__read_committed
476
+ # Should be invariant.
477
+ original_salary = Developer.find(1).salary
478
+ temporary_salary = 200000
479
+
480
+ assert_nothing_raised do
481
+ threads = (1..3).map do
482
+ Thread.new do
483
+ Developer.transaction do
484
+ # Expect original salary.
485
+ dev = Developer.find(1)
486
+ assert_equal original_salary, dev.salary
487
+
488
+ dev.salary = temporary_salary
489
+ dev.save!
490
+
491
+ # Expect temporary salary.
492
+ dev = Developer.find(1)
493
+ assert_equal temporary_salary, dev.salary
494
+
495
+ dev.salary = original_salary
496
+ dev.save!
497
+
498
+ # Expect original salary.
499
+ dev = Developer.find(1)
500
+ assert_equal original_salary, dev.salary
501
+ end
502
+ end
503
+ end
504
+
505
+ # Keep our eyes peeled.
506
+ threads << Thread.new do
507
+ 10.times do
508
+ sleep 0.05
509
+ Developer.transaction do
510
+ # Always expect original salary.
511
+ assert_equal original_salary, Developer.find(1).salary
512
+ end
513
+ end
514
+ end
515
+
516
+ threads.each { |t| t.join }
517
+ end
518
+
519
+ assert_equal original_salary, Developer.find(1).salary
520
+ end
521
+ end
522
+ end
@@ -0,0 +1,32 @@
1
+ require "cases/helper"
2
+
3
+ class TestRecord < ActiveRecord::Base
4
+ end
5
+
6
+ class TestUnconnectedAdapter < ActiveRecord::TestCase
7
+ self.use_transactional_fixtures = false
8
+
9
+ def setup
10
+ @underlying = ActiveRecord::Base.connection
11
+ @specification = ActiveRecord::Base.remove_connection
12
+ end
13
+
14
+ def teardown
15
+ @underlying = nil
16
+ ActiveRecord::Base.establish_connection(@specification)
17
+ end
18
+
19
+ def test_connection_no_longer_established
20
+ assert_raise(ActiveRecord::ConnectionNotEstablished) do
21
+ TestRecord.find(1)
22
+ end
23
+
24
+ assert_raise(ActiveRecord::ConnectionNotEstablished) do
25
+ TestRecord.new.save
26
+ end
27
+ end
28
+
29
+ def test_underlying_adapter_no_longer_active
30
+ assert !@underlying.active?, "Removed adapter should no longer be active"
31
+ end
32
+ end