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,1218 +0,0 @@
1
- require 'cases/helper'
2
- require 'models/bird'
3
- require 'models/company'
4
- require 'models/customer'
5
- require 'models/developer'
6
- require 'models/invoice'
7
- require 'models/line_item'
8
- require 'models/order'
9
- require 'models/parrot'
10
- require 'models/person'
11
- require 'models/pirate'
12
- require 'models/post'
13
- require 'models/reader'
14
- require 'models/ship'
15
- require 'models/ship_part'
16
- require 'models/treasure'
17
-
18
- class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
19
- def test_autosave_should_be_a_valid_option_for_has_one
20
- assert base.valid_keys_for_has_one_association.include?(:autosave)
21
- end
22
-
23
- def test_autosave_should_be_a_valid_option_for_belongs_to
24
- assert base.valid_keys_for_belongs_to_association.include?(:autosave)
25
- end
26
-
27
- def test_autosave_should_be_a_valid_option_for_has_many
28
- assert base.valid_keys_for_has_many_association.include?(:autosave)
29
- end
30
-
31
- def test_autosave_should_be_a_valid_option_for_has_and_belongs_to_many
32
- assert base.valid_keys_for_has_and_belongs_to_many_association.include?(:autosave)
33
- end
34
-
35
- def test_should_not_add_the_same_callbacks_multiple_times_for_has_one
36
- assert_no_difference_when_adding_callbacks_twice_for Pirate, :ship
37
- end
38
-
39
- def test_should_not_add_the_same_callbacks_multiple_times_for_belongs_to
40
- assert_no_difference_when_adding_callbacks_twice_for Ship, :pirate
41
- end
42
-
43
- def test_should_not_add_the_same_callbacks_multiple_times_for_has_many
44
- assert_no_difference_when_adding_callbacks_twice_for Pirate, :birds
45
- end
46
-
47
- def test_should_not_add_the_same_callbacks_multiple_times_for_has_and_belongs_to_many
48
- assert_no_difference_when_adding_callbacks_twice_for Pirate, :parrots
49
- end
50
-
51
- private
52
-
53
- def base
54
- ActiveRecord::Base
55
- end
56
-
57
- def assert_no_difference_when_adding_callbacks_twice_for(model, association_name)
58
- reflection = model.reflect_on_association(association_name)
59
- assert_no_difference "callbacks_for_model(#{model.name}).length" do
60
- model.send(:add_autosave_association_callbacks, reflection)
61
- end
62
- end
63
-
64
- def callbacks_for_model(model)
65
- model.instance_variables.grep(/_callbacks$/).map do |ivar|
66
- model.instance_variable_get(ivar)
67
- end.flatten
68
- end
69
- end
70
-
71
- class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
72
- def test_should_save_parent_but_not_invalid_child
73
- firm = Firm.new(:name => 'GlobalMegaCorp')
74
- assert firm.valid?
75
-
76
- firm.build_account_using_primary_key
77
- assert !firm.build_account_using_primary_key.valid?
78
-
79
- assert firm.save
80
- assert firm.account_using_primary_key.new_record?
81
- end
82
-
83
- def test_save_fails_for_invalid_has_one
84
- firm = Firm.find(:first)
85
- assert firm.valid?
86
-
87
- firm.account = Account.new
88
-
89
- assert !firm.account.valid?
90
- assert !firm.valid?
91
- assert !firm.save
92
- assert_equal "is invalid", firm.errors.on("account")
93
- end
94
-
95
- def test_save_succeeds_for_invalid_has_one_with_validate_false
96
- firm = Firm.find(:first)
97
- assert firm.valid?
98
-
99
- firm.unvalidated_account = Account.new
100
-
101
- assert !firm.unvalidated_account.valid?
102
- assert firm.valid?
103
- assert firm.save
104
- end
105
-
106
- def test_build_before_child_saved
107
- firm = Firm.find(1)
108
-
109
- account = firm.account.build("credit_limit" => 1000)
110
- assert_equal account, firm.account
111
- assert account.new_record?
112
- assert firm.save
113
- assert_equal account, firm.account
114
- assert !account.new_record?
115
- end
116
-
117
- def test_build_before_either_saved
118
- firm = Firm.new("name" => "GlobalMegaCorp")
119
-
120
- firm.account = account = Account.new("credit_limit" => 1000)
121
- assert_equal account, firm.account
122
- assert account.new_record?
123
- assert firm.save
124
- assert_equal account, firm.account
125
- assert !account.new_record?
126
- end
127
-
128
- def test_assignment_before_parent_saved
129
- firm = Firm.new("name" => "GlobalMegaCorp")
130
- firm.account = a = Account.find(1)
131
- assert firm.new_record?
132
- assert_equal a, firm.account
133
- assert firm.save
134
- assert_equal a, firm.account
135
- assert_equal a, firm.account(true)
136
- end
137
-
138
- def test_assignment_before_either_saved
139
- firm = Firm.new("name" => "GlobalMegaCorp")
140
- firm.account = a = Account.new("credit_limit" => 1000)
141
- assert firm.new_record?
142
- assert a.new_record?
143
- assert_equal a, firm.account
144
- assert firm.save
145
- assert !firm.new_record?
146
- assert !a.new_record?
147
- assert_equal a, firm.account
148
- assert_equal a, firm.account(true)
149
- end
150
-
151
- def test_not_resaved_when_unchanged
152
- firm = Firm.find(:first, :include => :account)
153
- firm.name += '-changed'
154
- assert_queries(1) { firm.save! }
155
-
156
- firm = Firm.find(:first)
157
- firm.account = Account.find(:first)
158
- assert_queries(Firm.partial_updates? ? 0 : 1) { firm.save! }
159
-
160
- firm = Firm.find(:first).clone
161
- firm.account = Account.find(:first)
162
- assert_queries(2) { firm.save! }
163
-
164
- firm = Firm.find(:first).clone
165
- firm.account = Account.find(:first).clone
166
- assert_queries(2) { firm.save! }
167
- end
168
- end
169
-
170
- class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
171
- def test_should_save_parent_but_not_invalid_child
172
- client = Client.new(:name => 'Joe (the Plumber)')
173
- assert client.valid?
174
-
175
- client.build_firm
176
- assert !client.firm.valid?
177
-
178
- assert client.save
179
- assert client.firm.new_record?
180
- end
181
-
182
- def test_save_fails_for_invalid_belongs_to
183
- assert log = AuditLog.create(:developer_id => 0, :message => "")
184
-
185
- log.developer = Developer.new
186
- assert !log.developer.valid?
187
- assert !log.valid?
188
- assert !log.save
189
- assert_equal "is invalid", log.errors.on("developer")
190
- end
191
-
192
- def test_save_succeeds_for_invalid_belongs_to_with_validate_false
193
- assert log = AuditLog.create(:developer_id => 0, :message=> "")
194
-
195
- log.unvalidated_developer = Developer.new
196
- assert !log.unvalidated_developer.valid?
197
- assert log.valid?
198
- assert log.save
199
- end
200
-
201
- def test_assignment_before_parent_saved
202
- client = Client.find(:first)
203
- apple = Firm.new("name" => "Apple")
204
- client.firm = apple
205
- assert_equal apple, client.firm
206
- assert apple.new_record?
207
- assert client.save
208
- assert apple.save
209
- assert !apple.new_record?
210
- assert_equal apple, client.firm
211
- assert_equal apple, client.firm(true)
212
- end
213
-
214
- def test_assignment_before_either_saved
215
- final_cut = Client.new("name" => "Final Cut")
216
- apple = Firm.new("name" => "Apple")
217
- final_cut.firm = apple
218
- assert final_cut.new_record?
219
- assert apple.new_record?
220
- assert final_cut.save
221
- assert !final_cut.new_record?
222
- assert !apple.new_record?
223
- assert_equal apple, final_cut.firm
224
- assert_equal apple, final_cut.firm(true)
225
- end
226
-
227
- def test_store_two_association_with_one_save
228
- num_orders = Order.count
229
- num_customers = Customer.count
230
- order = Order.new
231
-
232
- customer1 = order.billing = Customer.new
233
- customer2 = order.shipping = Customer.new
234
- assert order.save
235
- assert_equal customer1, order.billing
236
- assert_equal customer2, order.shipping
237
-
238
- order.reload
239
-
240
- assert_equal customer1, order.billing
241
- assert_equal customer2, order.shipping
242
-
243
- assert_equal num_orders +1, Order.count
244
- assert_equal num_customers +2, Customer.count
245
- end
246
-
247
- def test_store_association_in_two_relations_with_one_save
248
- num_orders = Order.count
249
- num_customers = Customer.count
250
- order = Order.new
251
-
252
- customer = order.billing = order.shipping = Customer.new
253
- assert order.save
254
- assert_equal customer, order.billing
255
- assert_equal customer, order.shipping
256
-
257
- order.reload
258
-
259
- assert_equal customer, order.billing
260
- assert_equal customer, order.shipping
261
-
262
- assert_equal num_orders +1, Order.count
263
- assert_equal num_customers +1, Customer.count
264
- end
265
-
266
- def test_store_association_in_two_relations_with_one_save_in_existing_object
267
- num_orders = Order.count
268
- num_customers = Customer.count
269
- order = Order.create
270
-
271
- customer = order.billing = order.shipping = Customer.new
272
- assert order.save
273
- assert_equal customer, order.billing
274
- assert_equal customer, order.shipping
275
-
276
- order.reload
277
-
278
- assert_equal customer, order.billing
279
- assert_equal customer, order.shipping
280
-
281
- assert_equal num_orders +1, Order.count
282
- assert_equal num_customers +1, Customer.count
283
- end
284
-
285
- def test_store_association_in_two_relations_with_one_save_in_existing_object_with_values
286
- num_orders = Order.count
287
- num_customers = Customer.count
288
- order = Order.create
289
-
290
- customer = order.billing = order.shipping = Customer.new
291
- assert order.save
292
- assert_equal customer, order.billing
293
- assert_equal customer, order.shipping
294
-
295
- order.reload
296
-
297
- customer = order.billing = order.shipping = Customer.new
298
-
299
- assert order.save
300
- order.reload
301
-
302
- assert_equal customer, order.billing
303
- assert_equal customer, order.shipping
304
-
305
- assert_equal num_orders +1, Order.count
306
- assert_equal num_customers +2, Customer.count
307
- end
308
- end
309
-
310
- class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCase
311
- fixtures :companies, :people
312
-
313
- def test_invalid_adding
314
- firm = Firm.find(1)
315
- assert !(firm.clients_of_firm << c = Client.new)
316
- assert c.new_record?
317
- assert !firm.valid?
318
- assert !firm.save
319
- assert c.new_record?
320
- end
321
-
322
- def test_invalid_adding_before_save
323
- no_of_firms = Firm.count
324
- no_of_clients = Client.count
325
- new_firm = Firm.new("name" => "A New Firm, Inc")
326
- new_firm.clients_of_firm.concat([c = Client.new, Client.new("name" => "Apple")])
327
- assert c.new_record?
328
- assert !c.valid?
329
- assert !new_firm.valid?
330
- assert !new_firm.save
331
- assert c.new_record?
332
- assert new_firm.new_record?
333
- end
334
-
335
- def test_invalid_adding_with_validate_false
336
- firm = Firm.find(:first)
337
- client = Client.new
338
- firm.unvalidated_clients_of_firm << client
339
-
340
- assert firm.valid?
341
- assert !client.valid?
342
- assert firm.save
343
- assert client.new_record?
344
- end
345
-
346
- def test_valid_adding_with_validate_false
347
- no_of_clients = Client.count
348
-
349
- firm = Firm.find(:first)
350
- client = Client.new("name" => "Apple")
351
-
352
- assert firm.valid?
353
- assert client.valid?
354
- assert client.new_record?
355
-
356
- firm.unvalidated_clients_of_firm << client
357
-
358
- assert firm.save
359
- assert !client.new_record?
360
- assert_equal no_of_clients+1, Client.count
361
- end
362
-
363
- def test_invalid_build
364
- new_client = companies(:first_firm).clients_of_firm.build
365
- assert new_client.new_record?
366
- assert !new_client.valid?
367
- assert_equal new_client, companies(:first_firm).clients_of_firm.last
368
- assert !companies(:first_firm).save
369
- assert new_client.new_record?
370
- assert_equal 1, companies(:first_firm).clients_of_firm(true).size
371
- end
372
-
373
- def test_adding_before_save
374
- no_of_firms = Firm.count
375
- no_of_clients = Client.count
376
-
377
- new_firm = Firm.new("name" => "A New Firm, Inc")
378
- c = Client.new("name" => "Apple")
379
-
380
- new_firm.clients_of_firm.push Client.new("name" => "Natural Company")
381
- assert_equal 1, new_firm.clients_of_firm.size
382
- new_firm.clients_of_firm << c
383
- assert_equal 2, new_firm.clients_of_firm.size
384
-
385
- assert_equal no_of_firms, Firm.count # Firm was not saved to database.
386
- assert_equal no_of_clients, Client.count # Clients were not saved to database.
387
- assert new_firm.save
388
- assert !new_firm.new_record?
389
- assert !c.new_record?
390
- assert_equal new_firm, c.firm
391
- assert_equal no_of_firms+1, Firm.count # Firm was saved to database.
392
- assert_equal no_of_clients+2, Client.count # Clients were saved to database.
393
-
394
- assert_equal 2, new_firm.clients_of_firm.size
395
- assert_equal 2, new_firm.clients_of_firm(true).size
396
- end
397
-
398
- def test_assign_ids
399
- firm = Firm.new("name" => "Apple")
400
- firm.client_ids = [companies(:first_client).id, companies(:second_client).id]
401
- firm.save
402
- firm.reload
403
- assert_equal 2, firm.clients.length
404
- assert firm.clients.include?(companies(:second_client))
405
- end
406
-
407
- def test_assign_ids_for_through_a_belongs_to
408
- post = Post.new(:title => "Assigning IDs works!", :body => "You heared it here first, folks!")
409
- post.person_ids = [people(:david).id, people(:michael).id]
410
- post.save
411
- post.reload
412
- assert_equal 2, post.people.length
413
- assert post.people.include?(people(:david))
414
- end
415
-
416
- def test_build_before_save
417
- company = companies(:first_firm)
418
- new_client = assert_no_queries { company.clients_of_firm.build("name" => "Another Client") }
419
- assert !company.clients_of_firm.loaded?
420
-
421
- company.name += '-changed'
422
- assert_queries(2) { assert company.save }
423
- assert !new_client.new_record?
424
- assert_equal 2, company.clients_of_firm(true).size
425
- end
426
-
427
- def test_build_many_before_save
428
- company = companies(:first_firm)
429
- new_clients = assert_no_queries { company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) }
430
-
431
- company.name += '-changed'
432
- assert_queries(3) { assert company.save }
433
- assert_equal 3, company.clients_of_firm(true).size
434
- end
435
-
436
- def test_build_via_block_before_save
437
- company = companies(:first_firm)
438
- new_client = assert_no_queries { company.clients_of_firm.build {|client| client.name = "Another Client" } }
439
- assert !company.clients_of_firm.loaded?
440
-
441
- company.name += '-changed'
442
- assert_queries(2) { assert company.save }
443
- assert !new_client.new_record?
444
- assert_equal 2, company.clients_of_firm(true).size
445
- end
446
-
447
- def test_build_many_via_block_before_save
448
- company = companies(:first_firm)
449
- new_clients = assert_no_queries do
450
- company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) do |client|
451
- client.name = "changed"
452
- end
453
- end
454
-
455
- company.name += '-changed'
456
- assert_queries(3) { assert company.save }
457
- assert_equal 3, company.clients_of_firm(true).size
458
- end
459
-
460
- def test_replace_on_new_object
461
- firm = Firm.new("name" => "New Firm")
462
- firm.clients = [companies(:second_client), Client.new("name" => "New Client")]
463
- assert firm.save
464
- firm.reload
465
- assert_equal 2, firm.clients.length
466
- assert firm.clients.include?(Client.find_by_name("New Client"))
467
- end
468
- end
469
-
470
- class TestDefaultAutosaveAssociationOnNewRecord < ActiveRecord::TestCase
471
- def test_autosave_new_record_on_belongs_to_can_be_disabled_per_relationship
472
- new_account = Account.new("credit_limit" => 1000)
473
- new_firm = Firm.new("name" => "some firm")
474
-
475
- assert new_firm.new_record?
476
- new_account.firm = new_firm
477
- new_account.save!
478
-
479
- assert !new_firm.new_record?
480
-
481
- new_account = Account.new("credit_limit" => 1000)
482
- new_autosaved_firm = Firm.new("name" => "some firm")
483
-
484
- assert new_autosaved_firm.new_record?
485
- new_account.unautosaved_firm = new_autosaved_firm
486
- new_account.save!
487
-
488
- assert new_autosaved_firm.new_record?
489
- end
490
-
491
- def test_autosave_new_record_on_has_one_can_be_disabled_per_relationship
492
- firm = Firm.new("name" => "some firm")
493
- account = Account.new("credit_limit" => 1000)
494
-
495
- assert account.new_record?
496
- firm.account = account
497
- firm.save!
498
-
499
- assert !account.new_record?
500
-
501
- firm = Firm.new("name" => "some firm")
502
- account = Account.new("credit_limit" => 1000)
503
-
504
- firm.unautosaved_account = account
505
-
506
- assert account.new_record?
507
- firm.unautosaved_account = account
508
- firm.save!
509
-
510
- assert account.new_record?
511
- end
512
-
513
- def test_autosave_new_record_on_has_many_can_be_disabled_per_relationship
514
- firm = Firm.new("name" => "some firm")
515
- account = Account.new("credit_limit" => 1000)
516
-
517
- assert account.new_record?
518
- firm.accounts << account
519
-
520
- firm.save!
521
- assert !account.new_record?
522
-
523
- firm = Firm.new("name" => "some firm")
524
- account = Account.new("credit_limit" => 1000)
525
-
526
- assert account.new_record?
527
- firm.unautosaved_accounts << account
528
-
529
- firm.save!
530
- assert account.new_record?
531
- end
532
- end
533
-
534
- class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
535
- self.use_transactional_fixtures = false
536
-
537
- def setup
538
- @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
539
- @ship = @pirate.create_ship(:name => 'Nights Dirty Lightning')
540
- end
541
-
542
- # reload
543
- def test_a_marked_for_destruction_record_should_not_be_be_marked_after_reload
544
- @pirate.mark_for_destruction
545
- @pirate.ship.mark_for_destruction
546
-
547
- assert !@pirate.reload.marked_for_destruction?
548
- assert !@pirate.ship.marked_for_destruction?
549
- end
550
-
551
- # has_one
552
- def test_should_destroy_a_child_association_as_part_of_the_save_transaction_if_it_was_marked_for_destroyal
553
- assert !@pirate.ship.marked_for_destruction?
554
-
555
- @pirate.ship.mark_for_destruction
556
- id = @pirate.ship.id
557
-
558
- assert @pirate.ship.marked_for_destruction?
559
- assert Ship.find_by_id(id)
560
-
561
- @pirate.save
562
- assert_nil @pirate.reload.ship
563
- assert_nil Ship.find_by_id(id)
564
- end
565
-
566
- def test_should_skip_validation_on_a_child_association_if_marked_for_destruction
567
- @pirate.ship.name = ''
568
- assert !@pirate.valid?
569
-
570
- @pirate.ship.mark_for_destruction
571
- @pirate.ship.expects(:valid?).never
572
- assert_difference('Ship.count', -1) { @pirate.save! }
573
- end
574
-
575
- def test_a_child_marked_for_destruction_should_not_be_destroyed_twice
576
- @pirate.ship.mark_for_destruction
577
- assert @pirate.save
578
- @pirate.ship.expects(:destroy).never
579
- assert @pirate.save
580
- end
581
-
582
- def test_should_rollback_destructions_if_an_exception_occurred_while_saving_a_child
583
- # Stub the save method of the @pirate.ship instance to destroy and then raise an exception
584
- class << @pirate.ship
585
- def save(*args)
586
- super
587
- destroy
588
- raise 'Oh noes!'
589
- end
590
- end
591
-
592
- assert_raise(RuntimeError) { assert !@pirate.save }
593
- assert_not_nil @pirate.reload.ship
594
- end
595
-
596
- # belongs_to
597
- def test_should_destroy_a_parent_association_as_part_of_the_save_transaction_if_it_was_marked_for_destroyal
598
- assert !@ship.pirate.marked_for_destruction?
599
-
600
- @ship.pirate.mark_for_destruction
601
- id = @ship.pirate.id
602
-
603
- assert @ship.pirate.marked_for_destruction?
604
- assert Pirate.find_by_id(id)
605
-
606
- @ship.save
607
- assert_nil @ship.reload.pirate
608
- assert_nil Pirate.find_by_id(id)
609
- end
610
-
611
- def test_should_skip_validation_on_a_parent_association_if_marked_for_destruction
612
- @ship.pirate.catchphrase = ''
613
- assert !@ship.valid?
614
-
615
- @ship.pirate.mark_for_destruction
616
- @ship.pirate.expects(:valid?).never
617
- assert_difference('Pirate.count', -1) { @ship.save! }
618
- end
619
-
620
- def test_a_parent_marked_for_destruction_should_not_be_destroyed_twice
621
- @ship.pirate.mark_for_destruction
622
- assert @ship.save
623
- @ship.pirate.expects(:destroy).never
624
- assert @ship.save
625
- end
626
-
627
- def test_should_rollback_destructions_if_an_exception_occurred_while_saving_a_parent
628
- # Stub the save method of the @ship.pirate instance to destroy and then raise an exception
629
- class << @ship.pirate
630
- def save(*args)
631
- super
632
- destroy
633
- raise 'Oh noes!'
634
- end
635
- end
636
-
637
- assert_raise(RuntimeError) { assert !@ship.save }
638
- assert_not_nil @ship.reload.pirate
639
- end
640
-
641
- # has_many & has_and_belongs_to
642
- %w{ parrots birds }.each do |association_name|
643
- define_method("test_should_destroy_#{association_name}_as_part_of_the_save_transaction_if_they_were_marked_for_destroyal") do
644
- 2.times { |i| @pirate.send(association_name).create!(:name => "#{association_name}_#{i}") }
645
-
646
- assert !@pirate.send(association_name).any? { |child| child.marked_for_destruction? }
647
-
648
- @pirate.send(association_name).each { |child| child.mark_for_destruction }
649
- klass = @pirate.send(association_name).first.class
650
- ids = @pirate.send(association_name).map(&:id)
651
-
652
- assert @pirate.send(association_name).all? { |child| child.marked_for_destruction? }
653
- ids.each { |id| assert klass.find_by_id(id) }
654
-
655
- @pirate.save
656
- assert @pirate.reload.send(association_name).empty?
657
- ids.each { |id| assert_nil klass.find_by_id(id) }
658
- end
659
-
660
- define_method("test_should_skip_validation_on_the_#{association_name}_association_if_marked_for_destruction") do
661
- 2.times { |i| @pirate.send(association_name).create!(:name => "#{association_name}_#{i}") }
662
- children = @pirate.send(association_name)
663
-
664
- children.each { |child| child.name = '' }
665
- assert !@pirate.valid?
666
-
667
- children.each do |child|
668
- child.mark_for_destruction
669
- child.expects(:valid?).never
670
- end
671
- assert_difference("#{association_name.classify}.count", -2) { @pirate.save! }
672
- end
673
-
674
- define_method("test_should_skip_validation_on_the_#{association_name}_association_if_destroyed") do
675
- @pirate.send(association_name).create!(:name => "#{association_name}_1")
676
- children = @pirate.send(association_name)
677
-
678
- children.each { |child| child.name = '' }
679
- assert !@pirate.valid?
680
-
681
- children.each { |child| child.destroy }
682
- assert @pirate.valid?
683
- end
684
-
685
- define_method("test_a_child_marked_for_destruction_should_not_be_destroyed_twice_while_saving_#{association_name}") do
686
- @pirate.send(association_name).create!(:name => "#{association_name}_1")
687
- children = @pirate.send(association_name)
688
-
689
- children.each { |child| child.mark_for_destruction }
690
- assert @pirate.save
691
- children.each { |child| child.expects(:destroy).never }
692
- assert @pirate.save
693
- end
694
-
695
- define_method("test_should_rollback_destructions_if_an_exception_occurred_while_saving_#{association_name}") do
696
- 2.times { |i| @pirate.send(association_name).create!(:name => "#{association_name}_#{i}") }
697
- before = @pirate.send(association_name).map { |c| c.mark_for_destruction ; c }
698
-
699
- # Stub the destroy method of the the second child to raise an exception
700
- class << before.last
701
- def destroy(*args)
702
- super
703
- raise 'Oh noes!'
704
- end
705
- end
706
-
707
- assert_raise(RuntimeError) { assert !@pirate.save }
708
- assert before.first.frozen? # the first child was indeed destroyed
709
- assert_equal before, @pirate.reload.send(association_name)
710
- end
711
-
712
- # Add and remove callbacks tests for association collections.
713
- %w{ method proc }.each do |callback_type|
714
- define_method("test_should_run_add_callback_#{callback_type}s_for_#{association_name}") do
715
- association_name_with_callbacks = "#{association_name}_with_#{callback_type}_callbacks"
716
-
717
- pirate = Pirate.new(:catchphrase => "Arr")
718
- pirate.send(association_name_with_callbacks).build(:name => "Crowe the One-Eyed")
719
-
720
- expected = [
721
- "before_adding_#{callback_type}_#{association_name.singularize}_<new>",
722
- "after_adding_#{callback_type}_#{association_name.singularize}_<new>"
723
- ]
724
-
725
- assert_equal expected, pirate.ship_log
726
- end
727
-
728
- define_method("test_should_run_remove_callback_#{callback_type}s_for_#{association_name}") do
729
- association_name_with_callbacks = "#{association_name}_with_#{callback_type}_callbacks"
730
-
731
- @pirate.send(association_name_with_callbacks).create!(:name => "Crowe the One-Eyed")
732
- @pirate.send(association_name_with_callbacks).each { |c| c.mark_for_destruction }
733
- child_id = @pirate.send(association_name_with_callbacks).first.id
734
-
735
- @pirate.ship_log.clear
736
- @pirate.save
737
-
738
- expected = [
739
- "before_removing_#{callback_type}_#{association_name.singularize}_#{child_id}",
740
- "after_removing_#{callback_type}_#{association_name.singularize}_#{child_id}"
741
- ]
742
-
743
- assert_equal expected, @pirate.ship_log
744
- end
745
- end
746
- end
747
- end
748
-
749
- class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
750
- self.use_transactional_fixtures = false
751
-
752
- def setup
753
- @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
754
- @ship = @pirate.create_ship(:name => 'Nights Dirty Lightning')
755
- end
756
-
757
- def test_should_still_work_without_an_associated_model
758
- @ship.destroy
759
- @pirate.reload.catchphrase = "Arr"
760
- @pirate.save
761
- assert 'Arr', @pirate.reload.catchphrase
762
- end
763
-
764
- def test_should_automatically_save_the_associated_model
765
- @pirate.ship.name = 'The Vile Insanity'
766
- @pirate.save
767
- assert_equal 'The Vile Insanity', @pirate.reload.ship.name
768
- end
769
-
770
- def test_should_automatically_save_bang_the_associated_model
771
- @pirate.ship.name = 'The Vile Insanity'
772
- @pirate.save!
773
- assert_equal 'The Vile Insanity', @pirate.reload.ship.name
774
- end
775
-
776
- def test_should_automatically_validate_the_associated_model
777
- @pirate.ship.name = ''
778
- assert !@pirate.valid?
779
- assert_equal "can't be blank", @pirate.errors.on(:"ship.name")
780
- end
781
-
782
- def test_should_merge_errors_on_the_associated_models_onto_the_parent_even_if_it_is_not_valid
783
- @pirate.ship.name = nil
784
- @pirate.catchphrase = nil
785
- assert !@pirate.valid?
786
- assert @pirate.errors.full_messages.include?("Name can't be blank")
787
- assert @pirate.errors.full_messages.include?("Catchphrase can't be blank")
788
- end
789
-
790
- def test_should_still_allow_to_bypass_validations_on_the_associated_model
791
- @pirate.catchphrase = ''
792
- @pirate.ship.name = ''
793
- @pirate.save(false)
794
- assert_equal ['', ''], [@pirate.reload.catchphrase, @pirate.ship.name]
795
- end
796
-
797
- def test_should_allow_to_bypass_validations_on_associated_models_at_any_depth
798
- 2.times { |i| @pirate.ship.parts.create!(:name => "part #{i}") }
799
-
800
- @pirate.catchphrase = ''
801
- @pirate.ship.name = ''
802
- @pirate.ship.parts.each { |part| part.name = '' }
803
- @pirate.save(false)
804
-
805
- values = [@pirate.reload.catchphrase, @pirate.ship.name, *@pirate.ship.parts.map(&:name)]
806
- assert_equal ['', '', '', ''], values
807
- end
808
-
809
- def test_should_still_raise_an_ActiveRecordRecord_Invalid_exception_if_we_want_that
810
- @pirate.ship.name = ''
811
- assert_raise(ActiveRecord::RecordInvalid) do
812
- @pirate.save!
813
- end
814
- end
815
-
816
- def test_should_not_save_and_return_false_if_a_callback_cancelled_saving
817
- pirate = Pirate.new(:catchphrase => 'Arr')
818
- ship = pirate.build_ship(:name => 'The Vile Insanity')
819
- ship.cancel_save_from_callback = true
820
-
821
- assert_no_difference 'Pirate.count' do
822
- assert_no_difference 'Ship.count' do
823
- assert !pirate.save
824
- end
825
- end
826
- end
827
-
828
- def test_should_rollback_any_changes_if_an_exception_occurred_while_saving
829
- before = [@pirate.catchphrase, @pirate.ship.name]
830
-
831
- @pirate.catchphrase = 'Arr'
832
- @pirate.ship.name = 'The Vile Insanity'
833
-
834
- # Stub the save method of the @pirate.ship instance to raise an exception
835
- class << @pirate.ship
836
- def save(*args)
837
- super
838
- raise 'Oh noes!'
839
- end
840
- end
841
-
842
- assert_raise(RuntimeError) { assert !@pirate.save }
843
- assert_equal before, [@pirate.reload.catchphrase, @pirate.ship.name]
844
- end
845
-
846
- def test_should_not_load_the_associated_model
847
- assert_queries(1) { @pirate.catchphrase = 'Arr'; @pirate.save! }
848
- end
849
- end
850
-
851
- class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
852
- self.use_transactional_fixtures = false
853
-
854
- def setup
855
- @ship = Ship.create(:name => 'Nights Dirty Lightning')
856
- @pirate = @ship.create_pirate(:catchphrase => "Don' botharrr talkin' like one, savvy?")
857
- end
858
-
859
- def test_should_still_work_without_an_associated_model
860
- @pirate.destroy
861
- @ship.reload.name = "The Vile Insanity"
862
- @ship.save
863
- assert 'The Vile Insanity', @ship.reload.name
864
- end
865
-
866
- def test_should_automatically_save_the_associated_model
867
- @ship.pirate.catchphrase = 'Arr'
868
- @ship.save
869
- assert_equal 'Arr', @ship.reload.pirate.catchphrase
870
- end
871
-
872
- def test_should_automatically_save_bang_the_associated_model
873
- @ship.pirate.catchphrase = 'Arr'
874
- @ship.save!
875
- assert_equal 'Arr', @ship.reload.pirate.catchphrase
876
- end
877
-
878
- def test_should_automatically_validate_the_associated_model
879
- @ship.pirate.catchphrase = ''
880
- assert !@ship.valid?
881
- assert_equal "can't be blank", @ship.errors.on(:"pirate.catchphrase")
882
- end
883
-
884
- def test_should_merge_errors_on_the_associated_model_onto_the_parent_even_if_it_is_not_valid
885
- @ship.name = nil
886
- @ship.pirate.catchphrase = nil
887
- assert !@ship.valid?
888
- assert @ship.errors.full_messages.include?("Name can't be blank")
889
- assert @ship.errors.full_messages.include?("Catchphrase can't be blank")
890
- end
891
-
892
- def test_should_still_allow_to_bypass_validations_on_the_associated_model
893
- @ship.pirate.catchphrase = ''
894
- @ship.name = ''
895
- @ship.save(false)
896
- assert_equal ['', ''], [@ship.reload.name, @ship.pirate.catchphrase]
897
- end
898
-
899
- def test_should_still_raise_an_ActiveRecordRecord_Invalid_exception_if_we_want_that
900
- @ship.pirate.catchphrase = ''
901
- assert_raise(ActiveRecord::RecordInvalid) do
902
- @ship.save!
903
- end
904
- end
905
-
906
- def test_should_not_save_and_return_false_if_a_callback_cancelled_saving
907
- ship = Ship.new(:name => 'The Vile Insanity')
908
- pirate = ship.build_pirate(:catchphrase => 'Arr')
909
- pirate.cancel_save_from_callback = true
910
-
911
- assert_no_difference 'Ship.count' do
912
- assert_no_difference 'Pirate.count' do
913
- assert !ship.save
914
- end
915
- end
916
- end
917
-
918
- def test_should_rollback_any_changes_if_an_exception_occurred_while_saving
919
- before = [@ship.pirate.catchphrase, @ship.name]
920
-
921
- @ship.pirate.catchphrase = 'Arr'
922
- @ship.name = 'The Vile Insanity'
923
-
924
- # Stub the save method of the @ship.pirate instance to raise an exception
925
- class << @ship.pirate
926
- def save(*args)
927
- super
928
- raise 'Oh noes!'
929
- end
930
- end
931
-
932
- assert_raise(RuntimeError) { assert !@ship.save }
933
- assert_equal before, [@ship.pirate.reload.catchphrase, @ship.reload.name]
934
- end
935
-
936
- def test_should_not_load_the_associated_model
937
- assert_queries(1) { @ship.name = 'The Vile Insanity'; @ship.save! }
938
- end
939
- end
940
-
941
- module AutosaveAssociationOnACollectionAssociationTests
942
- def test_should_automatically_save_the_associated_models
943
- new_names = ['Grace OMalley', 'Privateers Greed']
944
- @pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
945
-
946
- @pirate.save
947
- assert_equal new_names, @pirate.reload.send(@association_name).map(&:name)
948
- end
949
-
950
- def test_should_automatically_save_bang_the_associated_models
951
- new_names = ['Grace OMalley', 'Privateers Greed']
952
- @pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
953
-
954
- @pirate.save!
955
- assert_equal new_names, @pirate.reload.send(@association_name).map(&:name)
956
- end
957
-
958
- def test_should_automatically_validate_the_associated_models
959
- @pirate.send(@association_name).each { |child| child.name = '' }
960
-
961
- assert !@pirate.valid?
962
- assert @pirate.errors.full_messages.include?("Name can't be blank")
963
- assert @pirate.errors.on(@association_name).blank?
964
- end
965
-
966
- def test_should_not_use_default_invalid_error_on_associated_models
967
- @pirate.send(@association_name).build(:name => '')
968
-
969
- assert !@pirate.valid?
970
- assert_equal "can't be blank", @pirate.errors.on("#{@association_name}.name")
971
- assert @pirate.errors.on(@association_name).blank?
972
- end
973
-
974
- def test_should_merge_errors_on_the_associated_models_onto_the_parent_even_if_it_is_not_valid
975
- @pirate.send(@association_name).each { |child| child.name = '' }
976
- @pirate.catchphrase = nil
977
-
978
- assert !@pirate.valid?
979
- assert_equal "can't be blank", @pirate.errors.on("#{@association_name}.name")
980
- assert !@pirate.errors.on(:catchphrase).blank?
981
- end
982
-
983
- def test_should_allow_to_bypass_validations_on_the_associated_models_on_update
984
- @pirate.catchphrase = ''
985
- @pirate.send(@association_name).each { |child| child.name = '' }
986
-
987
- assert @pirate.save(false)
988
- assert_equal ['', '', ''], [
989
- @pirate.reload.catchphrase,
990
- @pirate.send(@association_name).first.name,
991
- @pirate.send(@association_name).last.name
992
- ]
993
- end
994
-
995
- def test_should_validation_the_associated_models_on_create
996
- assert_no_difference("#{ @association_name == :birds ? 'Bird' : 'Parrot' }.count") do
997
- 2.times { @pirate.send(@association_name).build }
998
- @pirate.save(true)
999
- end
1000
- end
1001
-
1002
- def test_should_allow_to_bypass_validations_on_the_associated_models_on_create
1003
- assert_difference("#{ @association_name == :birds ? 'Bird' : 'Parrot' }.count", +2) do
1004
- 2.times { @pirate.send(@association_name).build }
1005
- @pirate.save(false)
1006
- end
1007
- end
1008
-
1009
- def test_should_not_save_and_return_false_if_a_callback_cancelled_saving_in_either_create_or_update
1010
- @pirate.catchphrase = 'Changed'
1011
- @child_1.name = 'Changed'
1012
- @child_1.cancel_save_from_callback = true
1013
-
1014
- assert !@pirate.save
1015
- assert_equal "Don' botharrr talkin' like one, savvy?", @pirate.reload.catchphrase
1016
- assert_equal "Posideons Killer", @child_1.reload.name
1017
-
1018
- new_pirate = Pirate.new(:catchphrase => 'Arr')
1019
- new_child = new_pirate.send(@association_name).build(:name => 'Grace OMalley')
1020
- new_child.cancel_save_from_callback = true
1021
-
1022
- assert_no_difference 'Pirate.count' do
1023
- assert_no_difference "#{new_child.class.name}.count" do
1024
- assert !new_pirate.save
1025
- end
1026
- end
1027
- end
1028
-
1029
- def test_should_rollback_any_changes_if_an_exception_occurred_while_saving
1030
- before = [@pirate.catchphrase, *@pirate.send(@association_name).map(&:name)]
1031
- new_names = ['Grace OMalley', 'Privateers Greed']
1032
-
1033
- @pirate.catchphrase = 'Arr'
1034
- @pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
1035
-
1036
- # Stub the save method of the first child instance to raise an exception
1037
- class << @pirate.send(@association_name).first
1038
- def save(*args)
1039
- super
1040
- raise 'Oh noes!'
1041
- end
1042
- end
1043
-
1044
- assert_raise(RuntimeError) { assert !@pirate.save }
1045
- assert_equal before, [@pirate.reload.catchphrase, *@pirate.send(@association_name).map(&:name)]
1046
- end
1047
-
1048
- def test_should_still_raise_an_ActiveRecordRecord_Invalid_exception_if_we_want_that
1049
- @pirate.send(@association_name).each { |child| child.name = '' }
1050
- assert_raise(ActiveRecord::RecordInvalid) do
1051
- @pirate.save!
1052
- end
1053
- end
1054
-
1055
- def test_should_not_load_the_associated_models_if_they_were_not_loaded_yet
1056
- assert_queries(1) { @pirate.catchphrase = 'Arr'; @pirate.save! }
1057
-
1058
- @pirate.send(@association_name).class # hack to load the target
1059
-
1060
- assert_queries(3) do
1061
- @pirate.catchphrase = 'Yarr'
1062
- new_names = ['Grace OMalley', 'Privateers Greed']
1063
- @pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
1064
- @pirate.save!
1065
- end
1066
- end
1067
- end
1068
-
1069
- class TestAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCase
1070
- self.use_transactional_fixtures = false
1071
-
1072
- def setup
1073
- @association_name = :birds
1074
-
1075
- @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
1076
- @child_1 = @pirate.birds.create(:name => 'Posideons Killer')
1077
- @child_2 = @pirate.birds.create(:name => 'Killer bandita Dionne')
1078
- end
1079
-
1080
- include AutosaveAssociationOnACollectionAssociationTests
1081
- end
1082
-
1083
- class TestAutosaveAssociationOnAHasAndBelongsToManyAssociation < ActiveRecord::TestCase
1084
- self.use_transactional_fixtures = false
1085
-
1086
- def setup
1087
- @association_name = :parrots
1088
- @habtm = true
1089
-
1090
- @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
1091
- @child_1 = @pirate.parrots.create(:name => 'Posideons Killer')
1092
- @child_2 = @pirate.parrots.create(:name => 'Killer bandita Dionne')
1093
- end
1094
-
1095
- include AutosaveAssociationOnACollectionAssociationTests
1096
- end
1097
-
1098
- class TestAutosaveAssociationValidationsOnAHasManyAssocication < ActiveRecord::TestCase
1099
- self.use_transactional_fixtures = false
1100
-
1101
- def setup
1102
- @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
1103
- @pirate.birds.create(:name => 'cookoo')
1104
- end
1105
-
1106
- test "should automatically validate associations" do
1107
- assert @pirate.valid?
1108
- @pirate.birds.each { |bird| bird.name = '' }
1109
-
1110
- assert !@pirate.valid?
1111
- end
1112
- end
1113
-
1114
- class TestAutosaveAssociationValidationsOnAHasOneAssocication < ActiveRecord::TestCase
1115
- self.use_transactional_fixtures = false
1116
-
1117
- def setup
1118
- @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
1119
- @pirate.create_ship(:name => 'titanic')
1120
- end
1121
-
1122
- test "should automatically validate associations with :validate => true" do
1123
- assert @pirate.valid?
1124
- @pirate.ship.name = ''
1125
- assert !@pirate.valid?
1126
- end
1127
-
1128
- test "should not automatically validate associations without :validate => true" do
1129
- assert @pirate.valid?
1130
- @pirate.non_validated_ship.name = ''
1131
- assert @pirate.valid?
1132
- end
1133
- end
1134
-
1135
- class TestAutosaveAssociationValidationsOnABelongsToAssocication < ActiveRecord::TestCase
1136
- self.use_transactional_fixtures = false
1137
-
1138
- def setup
1139
- @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
1140
- end
1141
-
1142
- test "should automatically validate associations with :validate => true" do
1143
- assert @pirate.valid?
1144
- @pirate.parrot = Parrot.new(:name => '')
1145
- assert !@pirate.valid?
1146
- end
1147
-
1148
- test "should not automatically validate associations without :validate => true" do
1149
- assert @pirate.valid?
1150
- @pirate.non_validated_parrot = Parrot.new(:name => '')
1151
- assert @pirate.valid?
1152
- end
1153
- end
1154
-
1155
- class TestAutosaveAssociationValidationsOnAHABTMAssocication < ActiveRecord::TestCase
1156
- self.use_transactional_fixtures = false
1157
-
1158
- def setup
1159
- @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
1160
- end
1161
-
1162
- test "should automatically validate associations with :validate => true" do
1163
- assert @pirate.valid?
1164
- @pirate.parrots = [ Parrot.new(:name => 'popuga') ]
1165
- @pirate.parrots.each { |parrot| parrot.name = '' }
1166
- assert !@pirate.valid?
1167
- end
1168
-
1169
- test "should not automatically validate associations without :validate => true" do
1170
- assert @pirate.valid?
1171
- @pirate.non_validated_parrots = [ Parrot.new(:name => 'popuga') ]
1172
- @pirate.non_validated_parrots.each { |parrot| parrot.name = '' }
1173
- assert @pirate.valid?
1174
- end
1175
- end
1176
-
1177
- class TestAutosaveAssociationValidationMethodsGeneration < ActiveRecord::TestCase
1178
- self.use_transactional_fixtures = false
1179
-
1180
- def setup
1181
- @pirate = Pirate.new
1182
- end
1183
-
1184
- test "should generate validation methods for has_many associations" do
1185
- assert @pirate.respond_to?(:validate_associated_records_for_birds)
1186
- end
1187
-
1188
- test "should generate validation methods for has_one associations with :validate => true" do
1189
- assert @pirate.respond_to?(:validate_associated_records_for_ship)
1190
- end
1191
-
1192
- test "should not generate validation methods for has_one associations without :validate => true" do
1193
- assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_ship)
1194
- end
1195
-
1196
- test "should generate validation methods for belongs_to associations with :validate => true" do
1197
- assert @pirate.respond_to?(:validate_associated_records_for_parrot)
1198
- end
1199
-
1200
- test "should not generate validation methods for belongs_to associations without :validate => true" do
1201
- assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_parrot)
1202
- end
1203
-
1204
- test "should generate validation methods for HABTM associations with :validate => true" do
1205
- assert @pirate.respond_to?(:validate_associated_records_for_parrots)
1206
- end
1207
-
1208
- test "should not generate validation methods for HABTM associations without :validate => true" do
1209
- assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_parrots)
1210
- end
1211
- end
1212
-
1213
- class TestAutosaveAssociationWithTouch < ActiveRecord::TestCase
1214
- def test_autosave_with_touch_should_not_raise_system_stack_error
1215
- invoice = Invoice.create
1216
- assert_nothing_raised { invoice.line_items.create(:amount => 10) }
1217
- end
1218
- end