activerecord 2.3.18 → 3.0.0.beta

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 (378) hide show
  1. data/CHANGELOG +105 -34
  2. data/examples/performance.rb +3 -39
  3. data/examples/simple.rb +14 -0
  4. data/lib/active_record.rb +81 -47
  5. data/lib/active_record/aggregations.rb +1 -3
  6. data/lib/active_record/association_preload.rb +39 -54
  7. data/lib/active_record/associations.rb +262 -419
  8. data/lib/active_record/associations/association_collection.rb +85 -100
  9. data/lib/active_record/associations/association_proxy.rb +20 -18
  10. data/lib/active_record/associations/belongs_to_association.rb +8 -8
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +13 -35
  12. data/lib/active_record/associations/has_many_association.rb +11 -19
  13. data/lib/active_record/associations/has_many_through_association.rb +30 -183
  14. data/lib/active_record/associations/has_one_association.rb +10 -10
  15. data/lib/active_record/associations/has_one_through_association.rb +13 -11
  16. data/lib/active_record/associations/through_association_scope.rb +153 -0
  17. data/lib/active_record/attribute_methods.rb +17 -370
  18. data/lib/active_record/attribute_methods/before_type_cast.rb +33 -0
  19. data/lib/active_record/attribute_methods/dirty.rb +87 -0
  20. data/lib/active_record/attribute_methods/primary_key.rb +44 -0
  21. data/lib/active_record/attribute_methods/query.rb +37 -0
  22. data/lib/active_record/attribute_methods/read.rb +116 -0
  23. data/lib/active_record/attribute_methods/time_zone_conversion.rb +60 -0
  24. data/lib/active_record/attribute_methods/write.rb +37 -0
  25. data/lib/active_record/autosave_association.rb +20 -41
  26. data/lib/active_record/base.rb +357 -1180
  27. data/lib/active_record/batches.rb +10 -16
  28. data/lib/active_record/callbacks.rb +66 -126
  29. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +17 -13
  30. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +5 -25
  31. data/lib/active_record/connection_adapters/abstract/database_statements.rb +4 -43
  32. data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -2
  33. data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -4
  34. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  35. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +18 -72
  36. data/lib/active_record/connection_adapters/abstract_adapter.rb +16 -49
  37. data/lib/active_record/connection_adapters/mysql_adapter.rb +15 -27
  38. data/lib/active_record/connection_adapters/postgresql_adapter.rb +84 -46
  39. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +9 -3
  40. data/lib/active_record/connection_adapters/sqlite_adapter.rb +34 -65
  41. data/lib/active_record/fixtures.rb +21 -25
  42. data/lib/active_record/locale/en.yml +9 -27
  43. data/lib/active_record/locking/optimistic.rb +16 -48
  44. data/lib/active_record/migration.rb +59 -46
  45. data/lib/active_record/named_scope.rb +85 -92
  46. data/lib/active_record/nested_attributes.rb +18 -24
  47. data/lib/active_record/observer.rb +18 -94
  48. data/lib/active_record/railtie.rb +83 -0
  49. data/lib/active_record/railties/controller_runtime.rb +38 -0
  50. data/lib/active_record/railties/databases.rake +477 -0
  51. data/lib/active_record/railties/subscriber.rb +27 -0
  52. data/lib/active_record/reflection.rb +2 -15
  53. data/lib/active_record/relation.rb +339 -0
  54. data/lib/active_record/relation/calculations.rb +259 -0
  55. data/lib/active_record/relation/finder_methods.rb +315 -0
  56. data/lib/active_record/relation/predicate_builder.rb +46 -0
  57. data/lib/active_record/relation/query_methods.rb +218 -0
  58. data/lib/active_record/relation/spawn_methods.rb +102 -0
  59. data/lib/active_record/schema_dumper.rb +10 -6
  60. data/lib/active_record/serialization.rb +31 -74
  61. data/lib/active_record/serializers/xml_serializer.rb +33 -121
  62. data/lib/active_record/session_store.rb +1 -9
  63. data/lib/active_record/test_case.rb +1 -3
  64. data/lib/active_record/timestamp.rb +7 -5
  65. data/lib/active_record/transactions.rb +9 -9
  66. data/lib/active_record/validations.rb +51 -1100
  67. data/lib/active_record/validations/associated.rb +47 -0
  68. data/lib/active_record/validations/uniqueness.rb +181 -0
  69. data/lib/active_record/version.rb +3 -3
  70. data/lib/generators/active_record.rb +30 -0
  71. data/lib/generators/active_record/migration/migration_generator.rb +25 -0
  72. data/lib/generators/active_record/migration/templates/migration.rb +11 -0
  73. data/lib/generators/active_record/model/model_generator.rb +33 -0
  74. data/lib/generators/active_record/model/templates/migration.rb +16 -0
  75. data/lib/generators/active_record/model/templates/model.rb +5 -0
  76. data/lib/generators/active_record/observer/observer_generator.rb +15 -0
  77. data/lib/generators/active_record/observer/templates/observer.rb +2 -0
  78. data/lib/generators/active_record/session_migration/session_migration_generator.rb +24 -0
  79. data/lib/generators/active_record/session_migration/templates/migration.rb +16 -0
  80. metadata +67 -325
  81. data/RUNNING_UNIT_TESTS +0 -36
  82. data/Rakefile +0 -268
  83. data/install.rb +0 -30
  84. data/lib/active_record/calculations.rb +0 -321
  85. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -57
  86. data/lib/active_record/dirty.rb +0 -183
  87. data/lib/active_record/serializers/json_serializer.rb +0 -91
  88. data/lib/activerecord.rb +0 -2
  89. data/test/assets/example.log +0 -1
  90. data/test/assets/flowers.jpg +0 -0
  91. data/test/cases/aaa_create_tables_test.rb +0 -24
  92. data/test/cases/active_schema_test_mysql.rb +0 -122
  93. data/test/cases/active_schema_test_postgresql.rb +0 -24
  94. data/test/cases/adapter_test.rb +0 -144
  95. data/test/cases/aggregations_test.rb +0 -167
  96. data/test/cases/ar_schema_test.rb +0 -32
  97. data/test/cases/associations/belongs_to_associations_test.rb +0 -438
  98. data/test/cases/associations/callbacks_test.rb +0 -161
  99. data/test/cases/associations/cascaded_eager_loading_test.rb +0 -131
  100. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +0 -36
  101. data/test/cases/associations/eager_load_nested_include_test.rb +0 -131
  102. data/test/cases/associations/eager_load_nested_polymorphic_include.rb +0 -19
  103. data/test/cases/associations/eager_singularization_test.rb +0 -145
  104. data/test/cases/associations/eager_test.rb +0 -852
  105. data/test/cases/associations/extension_test.rb +0 -62
  106. data/test/cases/associations/habtm_join_table_test.rb +0 -56
  107. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +0 -827
  108. data/test/cases/associations/has_many_associations_test.rb +0 -1273
  109. data/test/cases/associations/has_many_through_associations_test.rb +0 -360
  110. data/test/cases/associations/has_one_associations_test.rb +0 -330
  111. data/test/cases/associations/has_one_through_associations_test.rb +0 -209
  112. data/test/cases/associations/inner_join_association_test.rb +0 -93
  113. data/test/cases/associations/inverse_associations_test.rb +0 -566
  114. data/test/cases/associations/join_model_test.rb +0 -712
  115. data/test/cases/associations_test.rb +0 -282
  116. data/test/cases/attribute_methods_test.rb +0 -305
  117. data/test/cases/autosave_association_test.rb +0 -1218
  118. data/test/cases/base_test.rb +0 -2166
  119. data/test/cases/batches_test.rb +0 -81
  120. data/test/cases/binary_test.rb +0 -30
  121. data/test/cases/calculations_test.rb +0 -360
  122. data/test/cases/callbacks_observers_test.rb +0 -38
  123. data/test/cases/callbacks_test.rb +0 -438
  124. data/test/cases/class_inheritable_attributes_test.rb +0 -32
  125. data/test/cases/column_alias_test.rb +0 -17
  126. data/test/cases/column_definition_test.rb +0 -70
  127. data/test/cases/connection_pool_test.rb +0 -25
  128. data/test/cases/connection_test_firebird.rb +0 -8
  129. data/test/cases/connection_test_mysql.rb +0 -65
  130. data/test/cases/copy_table_test_sqlite.rb +0 -80
  131. data/test/cases/counter_cache_test.rb +0 -84
  132. data/test/cases/database_statements_test.rb +0 -12
  133. data/test/cases/datatype_test_postgresql.rb +0 -204
  134. data/test/cases/date_time_test.rb +0 -37
  135. data/test/cases/default_test_firebird.rb +0 -16
  136. data/test/cases/defaults_test.rb +0 -111
  137. data/test/cases/deprecated_finder_test.rb +0 -30
  138. data/test/cases/dirty_test.rb +0 -316
  139. data/test/cases/finder_respond_to_test.rb +0 -76
  140. data/test/cases/finder_test.rb +0 -1098
  141. data/test/cases/fixtures_test.rb +0 -661
  142. data/test/cases/helper.rb +0 -68
  143. data/test/cases/i18n_test.rb +0 -46
  144. data/test/cases/inheritance_test.rb +0 -262
  145. data/test/cases/invalid_date_test.rb +0 -24
  146. data/test/cases/json_serialization_test.rb +0 -219
  147. data/test/cases/lifecycle_test.rb +0 -193
  148. data/test/cases/locking_test.rb +0 -350
  149. data/test/cases/method_scoping_test.rb +0 -704
  150. data/test/cases/migration_test.rb +0 -1649
  151. data/test/cases/migration_test_firebird.rb +0 -124
  152. data/test/cases/mixin_test.rb +0 -96
  153. data/test/cases/modules_test.rb +0 -109
  154. data/test/cases/multiple_db_test.rb +0 -85
  155. data/test/cases/named_scope_test.rb +0 -372
  156. data/test/cases/nested_attributes_test.rb +0 -840
  157. data/test/cases/pk_test.rb +0 -119
  158. data/test/cases/pooled_connections_test.rb +0 -103
  159. data/test/cases/query_cache_test.rb +0 -129
  160. data/test/cases/readonly_test.rb +0 -107
  161. data/test/cases/reflection_test.rb +0 -234
  162. data/test/cases/reload_models_test.rb +0 -22
  163. data/test/cases/repair_helper.rb +0 -50
  164. data/test/cases/reserved_word_test_mysql.rb +0 -176
  165. data/test/cases/sanitize_test.rb +0 -25
  166. data/test/cases/schema_authorization_test_postgresql.rb +0 -75
  167. data/test/cases/schema_dumper_test.rb +0 -211
  168. data/test/cases/schema_test_postgresql.rb +0 -178
  169. data/test/cases/serialization_test.rb +0 -47
  170. data/test/cases/sp_test_mysql.rb +0 -16
  171. data/test/cases/synonym_test_oracle.rb +0 -17
  172. data/test/cases/timestamp_test.rb +0 -75
  173. data/test/cases/transactions_test.rb +0 -543
  174. data/test/cases/unconnected_test.rb +0 -32
  175. data/test/cases/validations_i18n_test.rb +0 -925
  176. data/test/cases/validations_test.rb +0 -1684
  177. data/test/cases/xml_serialization_test.rb +0 -240
  178. data/test/cases/yaml_serialization_test.rb +0 -11
  179. data/test/config.rb +0 -5
  180. data/test/connections/jdbc_jdbcderby/connection.rb +0 -18
  181. data/test/connections/jdbc_jdbch2/connection.rb +0 -18
  182. data/test/connections/jdbc_jdbchsqldb/connection.rb +0 -18
  183. data/test/connections/jdbc_jdbcmysql/connection.rb +0 -26
  184. data/test/connections/jdbc_jdbcpostgresql/connection.rb +0 -26
  185. data/test/connections/jdbc_jdbcsqlite3/connection.rb +0 -25
  186. data/test/connections/native_db2/connection.rb +0 -25
  187. data/test/connections/native_firebird/connection.rb +0 -26
  188. data/test/connections/native_frontbase/connection.rb +0 -27
  189. data/test/connections/native_mysql/connection.rb +0 -25
  190. data/test/connections/native_openbase/connection.rb +0 -21
  191. data/test/connections/native_oracle/connection.rb +0 -27
  192. data/test/connections/native_postgresql/connection.rb +0 -21
  193. data/test/connections/native_sqlite/connection.rb +0 -25
  194. data/test/connections/native_sqlite3/connection.rb +0 -25
  195. data/test/connections/native_sqlite3/in_memory_connection.rb +0 -18
  196. data/test/connections/native_sybase/connection.rb +0 -23
  197. data/test/fixtures/accounts.yml +0 -29
  198. data/test/fixtures/all/developers.yml +0 -0
  199. data/test/fixtures/all/people.csv +0 -0
  200. data/test/fixtures/all/tasks.yml +0 -0
  201. data/test/fixtures/author_addresses.yml +0 -5
  202. data/test/fixtures/author_favorites.yml +0 -4
  203. data/test/fixtures/authors.yml +0 -9
  204. data/test/fixtures/binaries.yml +0 -132
  205. data/test/fixtures/books.yml +0 -7
  206. data/test/fixtures/categories.yml +0 -14
  207. data/test/fixtures/categories/special_categories.yml +0 -9
  208. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +0 -4
  209. data/test/fixtures/categories_ordered.yml +0 -7
  210. data/test/fixtures/categories_posts.yml +0 -23
  211. data/test/fixtures/categorizations.yml +0 -17
  212. data/test/fixtures/clubs.yml +0 -6
  213. data/test/fixtures/comments.yml +0 -59
  214. data/test/fixtures/companies.yml +0 -56
  215. data/test/fixtures/computers.yml +0 -4
  216. data/test/fixtures/courses.yml +0 -7
  217. data/test/fixtures/customers.yml +0 -26
  218. data/test/fixtures/developers.yml +0 -21
  219. data/test/fixtures/developers_projects.yml +0 -17
  220. data/test/fixtures/edges.yml +0 -6
  221. data/test/fixtures/entrants.yml +0 -14
  222. data/test/fixtures/faces.yml +0 -11
  223. data/test/fixtures/fk_test_has_fk.yml +0 -3
  224. data/test/fixtures/fk_test_has_pk.yml +0 -2
  225. data/test/fixtures/funny_jokes.yml +0 -10
  226. data/test/fixtures/interests.yml +0 -33
  227. data/test/fixtures/items.yml +0 -4
  228. data/test/fixtures/jobs.yml +0 -7
  229. data/test/fixtures/legacy_things.yml +0 -3
  230. data/test/fixtures/mateys.yml +0 -4
  231. data/test/fixtures/member_types.yml +0 -6
  232. data/test/fixtures/members.yml +0 -6
  233. data/test/fixtures/memberships.yml +0 -20
  234. data/test/fixtures/men.yml +0 -5
  235. data/test/fixtures/minimalistics.yml +0 -2
  236. data/test/fixtures/mixed_case_monkeys.yml +0 -6
  237. data/test/fixtures/mixins.yml +0 -29
  238. data/test/fixtures/movies.yml +0 -7
  239. data/test/fixtures/naked/csv/accounts.csv +0 -1
  240. data/test/fixtures/naked/yml/accounts.yml +0 -1
  241. data/test/fixtures/naked/yml/companies.yml +0 -1
  242. data/test/fixtures/naked/yml/courses.yml +0 -1
  243. data/test/fixtures/organizations.yml +0 -5
  244. data/test/fixtures/owners.yml +0 -7
  245. data/test/fixtures/parrots.yml +0 -27
  246. data/test/fixtures/parrots_pirates.yml +0 -7
  247. data/test/fixtures/people.yml +0 -15
  248. data/test/fixtures/pets.yml +0 -14
  249. data/test/fixtures/pirates.yml +0 -9
  250. data/test/fixtures/polymorphic_designs.yml +0 -19
  251. data/test/fixtures/polymorphic_prices.yml +0 -19
  252. data/test/fixtures/posts.yml +0 -52
  253. data/test/fixtures/price_estimates.yml +0 -7
  254. data/test/fixtures/projects.yml +0 -7
  255. data/test/fixtures/readers.yml +0 -9
  256. data/test/fixtures/references.yml +0 -17
  257. data/test/fixtures/reserved_words/distinct.yml +0 -5
  258. data/test/fixtures/reserved_words/distincts_selects.yml +0 -11
  259. data/test/fixtures/reserved_words/group.yml +0 -14
  260. data/test/fixtures/reserved_words/select.yml +0 -8
  261. data/test/fixtures/reserved_words/values.yml +0 -7
  262. data/test/fixtures/ships.yml +0 -5
  263. data/test/fixtures/sponsors.yml +0 -9
  264. data/test/fixtures/subscribers.yml +0 -7
  265. data/test/fixtures/subscriptions.yml +0 -12
  266. data/test/fixtures/taggings.yml +0 -28
  267. data/test/fixtures/tags.yml +0 -7
  268. data/test/fixtures/tasks.yml +0 -7
  269. data/test/fixtures/tees.yml +0 -4
  270. data/test/fixtures/ties.yml +0 -4
  271. data/test/fixtures/topics.yml +0 -42
  272. data/test/fixtures/toys.yml +0 -4
  273. data/test/fixtures/treasures.yml +0 -10
  274. data/test/fixtures/vertices.yml +0 -4
  275. data/test/fixtures/warehouse-things.yml +0 -3
  276. data/test/fixtures/zines.yml +0 -5
  277. data/test/migrations/broken/100_migration_that_raises_exception.rb +0 -10
  278. data/test/migrations/decimal/1_give_me_big_numbers.rb +0 -15
  279. data/test/migrations/duplicate/1_people_have_last_names.rb +0 -9
  280. data/test/migrations/duplicate/2_we_need_reminders.rb +0 -12
  281. data/test/migrations/duplicate/3_foo.rb +0 -7
  282. data/test/migrations/duplicate/3_innocent_jointable.rb +0 -12
  283. data/test/migrations/duplicate_names/20080507052938_chunky.rb +0 -7
  284. data/test/migrations/duplicate_names/20080507053028_chunky.rb +0 -7
  285. data/test/migrations/interleaved/pass_1/3_innocent_jointable.rb +0 -12
  286. data/test/migrations/interleaved/pass_2/1_people_have_last_names.rb +0 -9
  287. data/test/migrations/interleaved/pass_2/3_innocent_jointable.rb +0 -12
  288. data/test/migrations/interleaved/pass_3/1_people_have_last_names.rb +0 -9
  289. data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +0 -8
  290. data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +0 -12
  291. data/test/migrations/missing/1000_people_have_middle_names.rb +0 -9
  292. data/test/migrations/missing/1_people_have_last_names.rb +0 -9
  293. data/test/migrations/missing/3_we_need_reminders.rb +0 -12
  294. data/test/migrations/missing/4_innocent_jointable.rb +0 -12
  295. data/test/migrations/valid/1_people_have_last_names.rb +0 -9
  296. data/test/migrations/valid/2_we_need_reminders.rb +0 -12
  297. data/test/migrations/valid/3_innocent_jointable.rb +0 -12
  298. data/test/models/author.rb +0 -151
  299. data/test/models/auto_id.rb +0 -4
  300. data/test/models/binary.rb +0 -2
  301. data/test/models/bird.rb +0 -9
  302. data/test/models/book.rb +0 -4
  303. data/test/models/categorization.rb +0 -5
  304. data/test/models/category.rb +0 -34
  305. data/test/models/citation.rb +0 -6
  306. data/test/models/club.rb +0 -13
  307. data/test/models/column_name.rb +0 -3
  308. data/test/models/comment.rb +0 -29
  309. data/test/models/company.rb +0 -173
  310. data/test/models/company_in_module.rb +0 -78
  311. data/test/models/computer.rb +0 -3
  312. data/test/models/contact.rb +0 -16
  313. data/test/models/contract.rb +0 -5
  314. data/test/models/course.rb +0 -3
  315. data/test/models/customer.rb +0 -73
  316. data/test/models/default.rb +0 -2
  317. data/test/models/developer.rb +0 -101
  318. data/test/models/edge.rb +0 -5
  319. data/test/models/entrant.rb +0 -3
  320. data/test/models/essay.rb +0 -3
  321. data/test/models/event.rb +0 -3
  322. data/test/models/event_author.rb +0 -8
  323. data/test/models/face.rb +0 -7
  324. data/test/models/guid.rb +0 -2
  325. data/test/models/interest.rb +0 -5
  326. data/test/models/invoice.rb +0 -4
  327. data/test/models/item.rb +0 -7
  328. data/test/models/job.rb +0 -5
  329. data/test/models/joke.rb +0 -3
  330. data/test/models/keyboard.rb +0 -3
  331. data/test/models/legacy_thing.rb +0 -3
  332. data/test/models/line_item.rb +0 -3
  333. data/test/models/man.rb +0 -9
  334. data/test/models/matey.rb +0 -4
  335. data/test/models/member.rb +0 -12
  336. data/test/models/member_detail.rb +0 -5
  337. data/test/models/member_type.rb +0 -3
  338. data/test/models/membership.rb +0 -9
  339. data/test/models/minimalistic.rb +0 -2
  340. data/test/models/mixed_case_monkey.rb +0 -3
  341. data/test/models/movie.rb +0 -5
  342. data/test/models/order.rb +0 -4
  343. data/test/models/organization.rb +0 -6
  344. data/test/models/owner.rb +0 -5
  345. data/test/models/parrot.rb +0 -22
  346. data/test/models/person.rb +0 -16
  347. data/test/models/pet.rb +0 -5
  348. data/test/models/pirate.rb +0 -80
  349. data/test/models/polymorphic_design.rb +0 -3
  350. data/test/models/polymorphic_price.rb +0 -3
  351. data/test/models/post.rb +0 -102
  352. data/test/models/price_estimate.rb +0 -3
  353. data/test/models/project.rb +0 -30
  354. data/test/models/reader.rb +0 -4
  355. data/test/models/reference.rb +0 -4
  356. data/test/models/reply.rb +0 -46
  357. data/test/models/ship.rb +0 -19
  358. data/test/models/ship_part.rb +0 -7
  359. data/test/models/sponsor.rb +0 -4
  360. data/test/models/subject.rb +0 -4
  361. data/test/models/subscriber.rb +0 -8
  362. data/test/models/subscription.rb +0 -4
  363. data/test/models/tag.rb +0 -7
  364. data/test/models/tagging.rb +0 -10
  365. data/test/models/task.rb +0 -3
  366. data/test/models/tee.rb +0 -4
  367. data/test/models/tie.rb +0 -4
  368. data/test/models/topic.rb +0 -80
  369. data/test/models/toy.rb +0 -6
  370. data/test/models/treasure.rb +0 -8
  371. data/test/models/vertex.rb +0 -9
  372. data/test/models/warehouse_thing.rb +0 -5
  373. data/test/models/zine.rb +0 -3
  374. data/test/schema/mysql_specific_schema.rb +0 -31
  375. data/test/schema/postgresql_specific_schema.rb +0 -114
  376. data/test/schema/schema.rb +0 -550
  377. data/test/schema/schema2.rb +0 -6
  378. data/test/schema/sqlite_specific_schema.rb +0 -25
@@ -1,193 +0,0 @@
1
- require "cases/helper"
2
- require 'models/topic'
3
- require 'models/developer'
4
- require 'models/reply'
5
- require 'models/minimalistic'
6
-
7
- class Topic; def after_find() end end
8
- class Developer; def after_find() end end
9
- class SpecialDeveloper < Developer; end
10
-
11
- class TopicManualObserver
12
- include Singleton
13
-
14
- attr_reader :action, :object, :callbacks
15
-
16
- def initialize
17
- Topic.add_observer(self)
18
- @callbacks = []
19
- end
20
-
21
- def update(callback_method, object)
22
- @callbacks << { "callback_method" => callback_method, "object" => object }
23
- end
24
-
25
- def has_been_notified?
26
- !@callbacks.empty?
27
- end
28
- end
29
-
30
- class TopicaAuditor < ActiveRecord::Observer
31
- observe :topic
32
-
33
- attr_reader :topic
34
-
35
- def after_find(topic)
36
- @topic = topic
37
- end
38
- end
39
-
40
- class TopicObserver < ActiveRecord::Observer
41
- attr_reader :topic
42
-
43
- def after_find(topic)
44
- @topic = topic
45
- end
46
- end
47
-
48
- class MinimalisticObserver < ActiveRecord::Observer
49
- attr_reader :minimalistic
50
-
51
- def after_find(minimalistic)
52
- @minimalistic = minimalistic
53
- end
54
- end
55
-
56
- class MultiObserver < ActiveRecord::Observer
57
- attr_reader :record
58
-
59
- def self.observed_class() [ Topic, Developer ] end
60
-
61
- cattr_reader :last_inherited
62
- @@last_inherited = nil
63
-
64
- def observed_class_inherited_with_testing(subclass)
65
- observed_class_inherited_without_testing(subclass)
66
- @@last_inherited = subclass
67
- end
68
-
69
- alias_method_chain :observed_class_inherited, :testing
70
-
71
- def after_find(record)
72
- @record = record
73
- end
74
- end
75
-
76
- class LifecycleTest < ActiveRecord::TestCase
77
- fixtures :topics, :developers, :minimalistics
78
-
79
- def test_before_destroy
80
- original_count = Topic.count
81
- (topic_to_be_destroyed = Topic.find(1)).destroy
82
- assert_equal original_count - (1 + topic_to_be_destroyed.replies.size), Topic.count
83
- end
84
-
85
- def test_after_save
86
- ActiveRecord::Base.observers = :topic_manual_observer
87
- ActiveRecord::Base.instantiate_observers
88
-
89
- topic = Topic.find(1)
90
- topic.title = "hello"
91
- topic.save
92
-
93
- assert TopicManualObserver.instance.has_been_notified?
94
- assert_equal :after_save, TopicManualObserver.instance.callbacks.last["callback_method"]
95
- end
96
-
97
- def test_observer_update_on_save
98
- ActiveRecord::Base.observers = TopicManualObserver
99
- ActiveRecord::Base.instantiate_observers
100
-
101
- topic = Topic.find(1)
102
- assert TopicManualObserver.instance.has_been_notified?
103
- assert_equal :after_find, TopicManualObserver.instance.callbacks.first["callback_method"]
104
- end
105
-
106
- def test_auto_observer
107
- topic_observer = TopicaAuditor.instance
108
- assert_nil TopicaAuditor.observed_class
109
- assert_equal [Topic], TopicaAuditor.instance.observed_classes.to_a
110
-
111
- topic = Topic.find(1)
112
- assert_equal topic.title, topic_observer.topic.title
113
- end
114
-
115
- def test_inferred_auto_observer
116
- topic_observer = TopicObserver.instance
117
- assert_equal Topic, TopicObserver.observed_class
118
-
119
- topic = Topic.find(1)
120
- assert_equal topic.title, topic_observer.topic.title
121
- end
122
-
123
- def test_observing_two_classes
124
- multi_observer = MultiObserver.instance
125
-
126
- topic = Topic.find(1)
127
- assert_equal topic.title, multi_observer.record.title
128
-
129
- developer = Developer.find(1)
130
- assert_equal developer.name, multi_observer.record.name
131
- end
132
-
133
- def test_observing_subclasses
134
- multi_observer = MultiObserver.instance
135
-
136
- developer = SpecialDeveloper.find(1)
137
- assert_equal developer.name, multi_observer.record.name
138
-
139
- klass = Class.new(Developer)
140
- assert_equal klass, multi_observer.last_inherited
141
-
142
- developer = klass.find(1)
143
- assert_equal developer.name, multi_observer.record.name
144
- end
145
-
146
- def test_after_find_can_be_observed_when_its_not_defined_on_the_model
147
- observer = MinimalisticObserver.instance
148
- assert_equal Minimalistic, MinimalisticObserver.observed_class
149
-
150
- minimalistic = Minimalistic.find(1)
151
- assert_equal minimalistic, observer.minimalistic
152
- end
153
-
154
- def test_after_find_can_be_observed_when_its_defined_on_the_model
155
- observer = TopicObserver.instance
156
- assert_equal Topic, TopicObserver.observed_class
157
-
158
- topic = Topic.find(1)
159
- assert_equal topic, observer.topic
160
- end
161
-
162
- def test_after_find_is_not_created_if_its_not_used
163
- # use a fresh class so an observer can't have defined an
164
- # after_find on it
165
- model_class = Class.new(ActiveRecord::Base)
166
- observer_class = Class.new(ActiveRecord::Observer)
167
- observer_class.observe(model_class)
168
-
169
- observer = observer_class.instance
170
-
171
- assert !model_class.method_defined?(:after_find)
172
- end
173
-
174
- def test_after_find_is_not_clobbered_if_it_already_exists
175
- # use a fresh observer class so we can instantiate it (Observer is
176
- # a Singleton)
177
- model_class = Class.new(ActiveRecord::Base) do
178
- def after_find; end
179
- end
180
- original_method = model_class.instance_method(:after_find)
181
- observer_class = Class.new(ActiveRecord::Observer) do
182
- def after_find; end
183
- end
184
- observer_class.observe(model_class)
185
-
186
- observer = observer_class.instance
187
- assert_equal original_method, model_class.instance_method(:after_find)
188
- end
189
-
190
- def test_invalid_observer
191
- assert_raise(ArgumentError) { Topic.observers = Object.new; Topic.instantiate_observers }
192
- end
193
- end
@@ -1,350 +0,0 @@
1
- require "cases/helper"
2
- require 'models/person'
3
- require 'models/reader'
4
- require 'models/legacy_thing'
5
- require 'models/reference'
6
-
7
- class LockWithoutDefault < ActiveRecord::Base; end
8
-
9
- class LockWithCustomColumnWithoutDefault < ActiveRecord::Base
10
- set_table_name :lock_without_defaults_cust
11
- set_locking_column :custom_lock_version
12
- end
13
-
14
- class ReadonlyFirstNamePerson < Person
15
- attr_readonly :first_name
16
- end
17
-
18
- class OptimisticLockingTest < ActiveRecord::TestCase
19
- fixtures :people, :legacy_things, :references
20
-
21
- # need to disable transactional fixtures, because otherwise the sqlite3
22
- # adapter (at least) chokes when we try and change the schema in the middle
23
- # of a test (see test_increment_counter_*).
24
- self.use_transactional_fixtures = false
25
-
26
- def test_lock_existing
27
- p1 = Person.find(1)
28
- p2 = Person.find(1)
29
- assert_equal 0, p1.lock_version
30
- assert_equal 0, p2.lock_version
31
-
32
- p1.first_name = 'stu'
33
- p1.save!
34
- assert_equal 1, p1.lock_version
35
- assert_equal 0, p2.lock_version
36
-
37
- p2.first_name = 'sue'
38
- assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
39
- end
40
-
41
- # See Lighthouse ticket #1966
42
- def test_lock_destroy
43
- p1 = Person.find(1)
44
- p2 = Person.find(1)
45
- assert_equal 0, p1.lock_version
46
- assert_equal 0, p2.lock_version
47
-
48
- p1.first_name = 'stu'
49
- p1.save!
50
- assert_equal 1, p1.lock_version
51
- assert_equal 0, p2.lock_version
52
-
53
- assert_raises(ActiveRecord::StaleObjectError) { p2.destroy }
54
-
55
- assert p1.destroy
56
- assert p1.frozen?
57
- assert p1.destroyed?
58
- assert_raises(ActiveRecord::RecordNotFound) { Person.find(1) }
59
- end
60
-
61
- def test_lock_repeating
62
- p1 = Person.find(1)
63
- p2 = Person.find(1)
64
- assert_equal 0, p1.lock_version
65
- assert_equal 0, p2.lock_version
66
-
67
- p1.first_name = 'stu'
68
- p1.save!
69
- assert_equal 1, p1.lock_version
70
- assert_equal 0, p2.lock_version
71
-
72
- p2.first_name = 'sue'
73
- assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
74
- p2.first_name = 'sue2'
75
- assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
76
- end
77
-
78
- def test_lock_new
79
- p1 = Person.new(:first_name => 'anika')
80
- assert_equal 0, p1.lock_version
81
-
82
- p1.first_name = 'anika2'
83
- p1.save!
84
- p2 = Person.find(p1.id)
85
- assert_equal 0, p1.lock_version
86
- assert_equal 0, p2.lock_version
87
-
88
- p1.first_name = 'anika3'
89
- p1.save!
90
- assert_equal 1, p1.lock_version
91
- assert_equal 0, p2.lock_version
92
-
93
- p2.first_name = 'sue'
94
- assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
95
- end
96
-
97
- def test_lock_new_with_nil
98
- p1 = Person.new(:first_name => 'anika')
99
- p1.save!
100
- p1.lock_version = nil # simulate bad fixture or column with no default
101
- p1.save!
102
- assert_equal 1, p1.lock_version
103
- end
104
-
105
-
106
- def test_lock_column_name_existing
107
- t1 = LegacyThing.find(1)
108
- t2 = LegacyThing.find(1)
109
- assert_equal 0, t1.version
110
- assert_equal 0, t2.version
111
-
112
- t1.tps_report_number = 700
113
- t1.save!
114
- assert_equal 1, t1.version
115
- assert_equal 0, t2.version
116
-
117
- t2.tps_report_number = 800
118
- assert_raise(ActiveRecord::StaleObjectError) { t2.save! }
119
- end
120
-
121
- def test_lock_column_is_mass_assignable
122
- p1 = Person.create(:first_name => 'bianca')
123
- assert_equal 0, p1.lock_version
124
- assert_equal p1.lock_version, Person.new(p1.attributes).lock_version
125
-
126
- p1.first_name = 'bianca2'
127
- p1.save!
128
- assert_equal 1, p1.lock_version
129
- assert_equal p1.lock_version, Person.new(p1.attributes).lock_version
130
- end
131
-
132
- def test_lock_without_default_sets_version_to_zero
133
- t1 = LockWithoutDefault.new
134
- assert_equal 0, t1.lock_version
135
- end
136
-
137
- def test_lock_with_custom_column_without_default_sets_version_to_zero
138
- t1 = LockWithCustomColumnWithoutDefault.new
139
- assert_equal 0, t1.custom_lock_version
140
- end
141
-
142
- def test_readonly_attributes
143
- assert_equal Set.new([ 'first_name' ]), ReadonlyFirstNamePerson.readonly_attributes
144
-
145
- p = ReadonlyFirstNamePerson.create(:first_name => "unchangeable name")
146
- p.reload
147
- assert_equal "unchangeable name", p.first_name
148
-
149
- p.update_attributes(:first_name => "changed name")
150
- p.reload
151
- assert_equal "unchangeable name", p.first_name
152
- end
153
-
154
- { :lock_version => Person, :custom_lock_version => LegacyThing }.each do |name, model|
155
- define_method("test_increment_counter_updates_#{name}") do
156
- counter_test model, 1 do |id|
157
- model.increment_counter :test_count, id
158
- end
159
- end
160
-
161
- define_method("test_decrement_counter_updates_#{name}") do
162
- counter_test model, -1 do |id|
163
- model.decrement_counter :test_count, id
164
- end
165
- end
166
-
167
- define_method("test_update_counters_updates_#{name}") do
168
- counter_test model, 1 do |id|
169
- model.update_counters id, :test_count => 1
170
- end
171
- end
172
- end
173
-
174
- # See Lighthouse ticket #1966
175
- def test_destroy_dependents
176
- # Establish dependent relationship between People and LegacyThing
177
- add_counter_column_to(Person, 'legacy_things_count')
178
- LegacyThing.connection.add_column LegacyThing.table_name, 'person_id', :integer
179
- LegacyThing.reset_column_information
180
- LegacyThing.class_eval do
181
- belongs_to :person, :counter_cache => true
182
- end
183
- Person.class_eval do
184
- has_many :legacy_things, :dependent => :destroy
185
- end
186
-
187
- # Make sure that counter incrementing doesn't cause problems
188
- p1 = Person.new(:first_name => 'fjord')
189
- p1.save!
190
- t = LegacyThing.new(:person => p1)
191
- t.save!
192
- p1.reload
193
- assert_equal 1, p1.legacy_things_count
194
- assert p1.destroy
195
- assert_equal true, p1.frozen?
196
- assert_raises(ActiveRecord::RecordNotFound) { Person.find(p1.id) }
197
- assert_raises(ActiveRecord::RecordNotFound) { LegacyThing.find(t.id) }
198
- end
199
-
200
- def test_quote_table_name
201
- ref = references(:michael_magician)
202
- ref.favourite = !ref.favourite
203
- assert ref.save
204
- end
205
-
206
- # Useful for partial updates, don't only update the lock_version if there
207
- # is nothing else being updated.
208
- def test_update_without_attributes_does_not_only_update_lock_version
209
- assert_nothing_raised do
210
- p1 = Person.new(:first_name => 'anika')
211
- p1.send(:update_with_lock, [])
212
- end
213
- end
214
-
215
- private
216
-
217
- def add_counter_column_to(model, col='test_count')
218
- model.connection.add_column model.table_name, col, :integer, :null => false, :default => 0
219
- model.reset_column_information
220
- # OpenBase does not set a value to existing rows when adding a not null default column
221
- model.update_all(col => 0) if current_adapter?(:OpenBaseAdapter)
222
- end
223
-
224
- def remove_counter_column_from(model)
225
- model.connection.remove_column model.table_name, :test_count
226
- model.reset_column_information
227
- end
228
-
229
- def counter_test(model, expected_count)
230
- add_counter_column_to(model)
231
- object = model.find(:first)
232
- assert_equal 0, object.test_count
233
- assert_equal 0, object.send(model.locking_column)
234
- yield object.id
235
- object.reload
236
- assert_equal expected_count, object.test_count
237
- assert_equal 1, object.send(model.locking_column)
238
- ensure
239
- remove_counter_column_from(model)
240
- end
241
- end
242
-
243
-
244
- # TODO: test against the generated SQL since testing locking behavior itself
245
- # is so cumbersome. Will deadlock Ruby threads if the underlying db.execute
246
- # blocks, so separate script called by Kernel#system is needed.
247
- # (See exec vs. async_exec in the PostgreSQL adapter.)
248
-
249
- # TODO: The Sybase, and OpenBase adapters currently have no support for pessimistic locking
250
-
251
- unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
252
- class PessimisticLockingTest < ActiveRecord::TestCase
253
- self.use_transactional_fixtures = false
254
- fixtures :people, :readers
255
-
256
- def setup
257
- # Avoid introspection queries during tests.
258
- Person.columns; Reader.columns
259
- end
260
-
261
- # Test typical find.
262
- def test_sane_find_with_lock
263
- assert_nothing_raised do
264
- Person.transaction do
265
- Person.find 1, :lock => true
266
- end
267
- end
268
- end
269
-
270
- # Test scoped lock.
271
- def test_sane_find_with_scoped_lock
272
- assert_nothing_raised do
273
- Person.transaction do
274
- Person.with_scope(:find => { :lock => true }) do
275
- Person.find 1
276
- end
277
- end
278
- end
279
- end
280
-
281
- # PostgreSQL protests SELECT ... FOR UPDATE on an outer join.
282
- unless current_adapter?(:PostgreSQLAdapter)
283
- # Test locked eager find.
284
- def test_eager_find_with_lock
285
- assert_nothing_raised do
286
- Person.transaction do
287
- Person.find 1, :include => :readers, :lock => true
288
- end
289
- end
290
- end
291
- end
292
-
293
- # Locking a record reloads it.
294
- def test_sane_lock_method
295
- assert_nothing_raised do
296
- Person.transaction do
297
- person = Person.find 1
298
- old, person.first_name = person.first_name, 'fooman'
299
- person.lock!
300
- assert_equal old, person.first_name
301
- end
302
- end
303
- end
304
-
305
- if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
306
- use_concurrent_connections
307
-
308
- def test_no_locks_no_wait
309
- first, second = duel { Person.find 1 }
310
- assert first.end > second.end
311
- end
312
-
313
- def test_second_lock_waits
314
- assert [0.2, 1, 5].any? { |zzz|
315
- first, second = duel(zzz) { Person.find 1, :lock => true }
316
- second.end > first.end
317
- }
318
- end
319
-
320
- protected
321
- def duel(zzz = 5)
322
- t0, t1, t2, t3 = nil, nil, nil, nil
323
-
324
- a = Thread.new do
325
- t0 = Time.now
326
- Person.transaction do
327
- yield
328
- sleep zzz # block thread 2 for zzz seconds
329
- end
330
- t1 = Time.now
331
- end
332
-
333
- b = Thread.new do
334
- sleep zzz / 2.0 # ensure thread 1 tx starts first
335
- t2 = Time.now
336
- Person.transaction { yield }
337
- t3 = Time.now
338
- end
339
-
340
- a.join
341
- b.join
342
-
343
- assert t1 > t0 + zzz
344
- assert t2 > t0
345
- assert t3 > t2
346
- [t0.to_f..t1.to_f, t2.to_f..t3.to_f]
347
- end
348
- end
349
- end
350
- end