ibm_db 3.0.5-x86-mingw32 → 4.0.0-x86-mingw32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (586) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +4 -0
  3. data/LICENSE +1 -1
  4. data/MANIFEST +14 -14
  5. data/ParameterizedQueries README +6 -6
  6. data/README +208 -225
  7. data/ext/Makefile.nt32 +181 -181
  8. data/ext/Makefile.nt32.191 +212 -212
  9. data/ext/extconf.rb +291 -291
  10. data/ext/ibm_db.c +11887 -11887
  11. data/ext/ruby_ibm_db.h +241 -241
  12. data/ext/ruby_ibm_db_cli.c +866 -866
  13. data/ext/ruby_ibm_db_cli.h +500 -500
  14. data/init.rb +41 -41
  15. data/lib/IBM_DB.rb +27 -27
  16. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +3452 -3177
  17. data/lib/active_record/connection_adapters/ibmdb_adapter.rb +5 -2
  18. data/lib/active_record/vendor/db2-i5-zOS.yaml +328 -328
  19. data/lib/mswin32/ibm_db.rb +91 -123
  20. data/lib/mswin32/rb2x/i386/ibm_db.so +0 -0
  21. data/test/active_record/connection_adapters/fake_adapter.rb +49 -46
  22. data/test/assets/example.log +1 -1
  23. data/test/assets/test.txt +1 -1
  24. data/test/cases/adapter_test.rb +351 -276
  25. data/test/cases/adapters/mysql2/active_schema_test.rb +193 -0
  26. data/test/cases/adapters/mysql2/bind_parameter_test.rb +50 -0
  27. data/test/cases/adapters/mysql2/boolean_test.rb +100 -0
  28. data/test/cases/adapters/mysql2/case_sensitivity_test.rb +63 -0
  29. data/test/cases/adapters/mysql2/charset_collation_test.rb +54 -0
  30. data/test/cases/adapters/mysql2/connection_test.rb +210 -0
  31. data/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb +45 -0
  32. data/test/cases/adapters/mysql2/enum_test.rb +26 -0
  33. data/test/cases/adapters/mysql2/explain_test.rb +21 -0
  34. data/test/cases/adapters/mysql2/json_test.rb +195 -0
  35. data/test/cases/adapters/mysql2/mysql2_adapter_test.rb +83 -0
  36. data/test/cases/adapters/mysql2/reserved_word_test.rb +152 -0
  37. data/test/cases/adapters/mysql2/schema_migrations_test.rb +59 -0
  38. data/test/cases/adapters/mysql2/schema_test.rb +126 -0
  39. data/test/cases/adapters/mysql2/sp_test.rb +36 -0
  40. data/test/cases/adapters/mysql2/sql_types_test.rb +14 -0
  41. data/test/cases/adapters/mysql2/table_options_test.rb +42 -0
  42. data/test/cases/adapters/mysql2/unsigned_type_test.rb +66 -0
  43. data/test/cases/adapters/postgresql/active_schema_test.rb +98 -0
  44. data/test/cases/adapters/postgresql/array_test.rb +339 -0
  45. data/test/cases/adapters/postgresql/bit_string_test.rb +82 -0
  46. data/test/cases/adapters/postgresql/bytea_test.rb +134 -0
  47. data/test/cases/adapters/postgresql/case_insensitive_test.rb +26 -0
  48. data/test/cases/adapters/postgresql/change_schema_test.rb +38 -0
  49. data/test/cases/adapters/postgresql/cidr_test.rb +25 -0
  50. data/test/cases/adapters/postgresql/citext_test.rb +78 -0
  51. data/test/cases/adapters/postgresql/collation_test.rb +53 -0
  52. data/test/cases/adapters/postgresql/composite_test.rb +132 -0
  53. data/test/cases/adapters/postgresql/connection_test.rb +257 -0
  54. data/test/cases/adapters/postgresql/datatype_test.rb +92 -0
  55. data/test/cases/adapters/postgresql/domain_test.rb +47 -0
  56. data/test/cases/adapters/postgresql/enum_test.rb +91 -0
  57. data/test/cases/adapters/postgresql/explain_test.rb +20 -0
  58. data/test/cases/adapters/postgresql/extension_migration_test.rb +63 -0
  59. data/test/cases/adapters/postgresql/full_text_test.rb +44 -0
  60. data/test/cases/adapters/postgresql/geometric_test.rb +378 -0
  61. data/test/cases/adapters/postgresql/hstore_test.rb +382 -0
  62. data/test/cases/adapters/postgresql/infinity_test.rb +69 -0
  63. data/test/cases/adapters/postgresql/integer_test.rb +25 -0
  64. data/test/cases/adapters/postgresql/json_test.rb +237 -0
  65. data/test/cases/adapters/postgresql/ltree_test.rb +53 -0
  66. data/test/cases/adapters/postgresql/money_test.rb +96 -0
  67. data/test/cases/adapters/postgresql/network_test.rb +94 -0
  68. data/test/cases/adapters/postgresql/numbers_test.rb +49 -0
  69. data/test/cases/adapters/postgresql/postgresql_adapter_test.rb +405 -0
  70. data/test/cases/adapters/postgresql/prepared_statements_test.rb +22 -0
  71. data/test/cases/adapters/postgresql/quoting_test.rb +44 -0
  72. data/test/cases/adapters/postgresql/range_test.rb +343 -0
  73. data/test/cases/adapters/postgresql/referential_integrity_test.rb +111 -0
  74. data/test/cases/adapters/postgresql/rename_table_test.rb +34 -0
  75. data/test/cases/adapters/postgresql/schema_authorization_test.rb +119 -0
  76. data/test/cases/adapters/postgresql/schema_test.rb +597 -0
  77. data/test/cases/adapters/postgresql/serial_test.rb +154 -0
  78. data/test/cases/adapters/postgresql/statement_pool_test.rb +41 -0
  79. data/test/cases/adapters/postgresql/timestamp_test.rb +90 -0
  80. data/test/cases/adapters/postgresql/type_lookup_test.rb +33 -0
  81. data/test/cases/adapters/postgresql/utils_test.rb +62 -0
  82. data/test/cases/adapters/postgresql/uuid_test.rb +294 -0
  83. data/test/cases/adapters/postgresql/xml_test.rb +54 -0
  84. data/test/cases/adapters/sqlite3/collation_test.rb +53 -0
  85. data/test/cases/adapters/sqlite3/copy_table_test.rb +98 -0
  86. data/test/cases/adapters/sqlite3/explain_test.rb +21 -0
  87. data/test/cases/adapters/sqlite3/quoting_test.rb +101 -0
  88. data/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +441 -0
  89. data/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb +24 -0
  90. data/test/cases/adapters/sqlite3/statement_pool_test.rb +20 -0
  91. data/test/cases/aggregations_test.rb +168 -158
  92. data/test/cases/ar_schema_test.rb +146 -161
  93. data/test/cases/associations/association_scope_test.rb +16 -21
  94. data/test/cases/associations/belongs_to_associations_test.rb +1141 -1029
  95. data/test/cases/associations/bidirectional_destroy_dependencies_test.rb +41 -0
  96. data/test/cases/associations/callbacks_test.rb +190 -192
  97. data/test/cases/associations/cascaded_eager_loading_test.rb +188 -188
  98. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -36
  99. data/test/cases/associations/eager_load_nested_include_test.rb +126 -128
  100. data/test/cases/associations/eager_singularization_test.rb +148 -148
  101. data/test/cases/associations/eager_test.rb +1514 -1429
  102. data/test/cases/associations/extension_test.rb +87 -82
  103. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +1004 -972
  104. data/test/cases/associations/has_many_associations_test.rb +2501 -2182
  105. data/test/cases/associations/has_many_through_associations_test.rb +1271 -1204
  106. data/test/cases/associations/has_one_associations_test.rb +707 -610
  107. data/test/cases/associations/has_one_through_associations_test.rb +383 -380
  108. data/test/cases/associations/inner_join_association_test.rb +139 -139
  109. data/test/cases/associations/inverse_associations_test.rb +733 -706
  110. data/test/cases/associations/join_model_test.rb +777 -754
  111. data/test/cases/associations/left_outer_join_association_test.rb +88 -0
  112. data/test/cases/associations/nested_through_associations_test.rb +579 -579
  113. data/test/cases/associations/required_test.rb +102 -82
  114. data/test/cases/associations_test.rb +385 -380
  115. data/test/cases/attribute_decorators_test.rb +125 -125
  116. data/test/cases/attribute_methods/read_test.rb +60 -60
  117. data/test/cases/attribute_methods_test.rb +1009 -952
  118. data/test/cases/attribute_set_test.rb +270 -210
  119. data/test/cases/attribute_test.rb +246 -180
  120. data/test/cases/attributes_test.rb +253 -136
  121. data/test/cases/autosave_association_test.rb +1708 -1595
  122. data/test/cases/base_test.rb +1713 -1664
  123. data/test/cases/batches_test.rb +489 -212
  124. data/test/cases/binary_test.rb +44 -52
  125. data/test/cases/bind_parameter_test.rb +110 -100
  126. data/test/cases/cache_key_test.rb +25 -0
  127. data/test/cases/calculations_test.rb +798 -646
  128. data/test/cases/callbacks_test.rb +636 -543
  129. data/test/cases/clone_test.rb +40 -40
  130. data/test/cases/coders/json_test.rb +15 -0
  131. data/test/cases/coders/yaml_column_test.rb +63 -63
  132. data/test/cases/collection_cache_key_test.rb +115 -0
  133. data/test/cases/column_alias_test.rb +17 -17
  134. data/test/cases/column_definition_test.rb +92 -123
  135. data/test/cases/comment_test.rb +143 -0
  136. data/test/cases/connection_adapters/adapter_leasing_test.rb +56 -54
  137. data/test/cases/connection_adapters/connection_handler_test.rb +160 -53
  138. data/test/cases/connection_adapters/connection_specification_test.rb +12 -12
  139. data/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb +255 -293
  140. data/test/cases/connection_adapters/mysql_type_lookup_test.rb +69 -65
  141. data/test/cases/connection_adapters/quoting_test.rb +13 -13
  142. data/test/cases/connection_adapters/schema_cache_test.rb +61 -56
  143. data/test/cases/connection_adapters/type_lookup_test.rb +118 -110
  144. data/test/cases/connection_management_test.rb +112 -122
  145. data/test/cases/connection_pool_test.rb +521 -346
  146. data/test/cases/connection_specification/resolver_test.rb +131 -116
  147. data/test/cases/core_test.rb +112 -112
  148. data/test/cases/counter_cache_test.rb +214 -209
  149. data/test/cases/custom_locking_test.rb +17 -17
  150. data/test/cases/database_statements_test.rb +34 -19
  151. data/test/cases/{invalid_date_test.rb → date_test.rb} +44 -32
  152. data/test/cases/date_time_precision_test.rb +106 -0
  153. data/test/cases/date_time_test.rb +61 -61
  154. data/test/cases/defaults_test.rb +218 -223
  155. data/test/cases/dirty_test.rb +763 -785
  156. data/test/cases/disconnected_test.rb +30 -28
  157. data/test/cases/dup_test.rb +157 -157
  158. data/test/cases/enum_test.rb +444 -290
  159. data/test/cases/errors_test.rb +16 -0
  160. data/test/cases/explain_subscriber_test.rb +64 -64
  161. data/test/cases/explain_test.rb +87 -76
  162. data/test/cases/finder_respond_to_test.rb +60 -60
  163. data/test/cases/finder_test.rb +1294 -1169
  164. data/test/cases/fixture_set/file_test.rb +156 -138
  165. data/test/cases/fixtures_test.rb +988 -908
  166. data/test/cases/forbidden_attributes_protection_test.rb +165 -99
  167. data/test/cases/habtm_destroy_order_test.rb +61 -61
  168. data/test/cases/helper.rb +204 -210
  169. data/test/cases/hot_compatibility_test.rb +142 -54
  170. data/test/cases/i18n_test.rb +45 -45
  171. data/test/cases/inheritance_test.rb +606 -375
  172. data/test/cases/integration_test.rb +155 -139
  173. data/test/cases/invalid_connection_test.rb +24 -22
  174. data/test/cases/invertible_migration_test.rb +387 -295
  175. data/test/cases/json_serialization_test.rb +311 -302
  176. data/test/cases/locking_test.rb +493 -477
  177. data/test/cases/log_subscriber_test.rb +225 -136
  178. data/test/cases/migration/change_schema_test.rb +458 -512
  179. data/test/cases/migration/change_table_test.rb +256 -224
  180. data/test/cases/migration/column_attributes_test.rb +176 -192
  181. data/test/cases/migration/column_positioning_test.rb +56 -56
  182. data/test/cases/migration/columns_test.rb +310 -304
  183. data/test/cases/migration/command_recorder_test.rb +350 -305
  184. data/test/cases/migration/compatibility_test.rb +118 -0
  185. data/test/cases/migration/create_join_table_test.rb +157 -148
  186. data/test/cases/migration/foreign_key_test.rb +360 -328
  187. data/test/cases/migration/helper.rb +39 -39
  188. data/test/cases/migration/index_test.rb +218 -216
  189. data/test/cases/migration/logger_test.rb +36 -36
  190. data/test/cases/migration/pending_migrations_test.rb +52 -53
  191. data/test/cases/migration/references_foreign_key_test.rb +216 -169
  192. data/test/cases/migration/references_index_test.rb +101 -101
  193. data/test/cases/migration/references_statements_test.rb +136 -116
  194. data/test/cases/migration/rename_table_test.rb +93 -93
  195. data/test/cases/migration_test.rb +1157 -959
  196. data/test/cases/migrator_test.rb +470 -388
  197. data/test/cases/mixin_test.rb +68 -70
  198. data/test/cases/modules_test.rb +172 -173
  199. data/test/cases/multiparameter_attributes_test.rb +372 -350
  200. data/test/cases/multiple_db_test.rb +122 -115
  201. data/test/cases/nested_attributes_test.rb +1098 -1070
  202. data/test/cases/nested_attributes_with_callbacks_test.rb +144 -144
  203. data/test/cases/persistence_test.rb +1001 -909
  204. data/test/cases/pooled_connections_test.rb +81 -81
  205. data/test/cases/primary_keys_test.rb +376 -237
  206. data/test/cases/query_cache_test.rb +446 -326
  207. data/test/cases/quoting_test.rb +202 -156
  208. data/test/cases/readonly_test.rb +119 -118
  209. data/test/cases/reaper_test.rb +85 -85
  210. data/test/cases/reflection_test.rb +509 -463
  211. data/test/cases/relation/delegation_test.rb +63 -68
  212. data/test/cases/relation/merging_test.rb +157 -161
  213. data/test/cases/relation/mutation_test.rb +183 -165
  214. data/test/cases/relation/or_test.rb +92 -0
  215. data/test/cases/relation/predicate_builder_test.rb +16 -14
  216. data/test/cases/relation/record_fetch_warning_test.rb +40 -0
  217. data/test/cases/relation/where_chain_test.rb +105 -181
  218. data/test/cases/relation/where_clause_test.rb +182 -0
  219. data/test/cases/relation/where_test.rb +322 -300
  220. data/test/cases/relation_test.rb +328 -319
  221. data/test/cases/relations_test.rb +2026 -1815
  222. data/test/cases/reload_models_test.rb +22 -22
  223. data/test/cases/result_test.rb +90 -80
  224. data/test/cases/sanitize_test.rb +176 -83
  225. data/test/cases/schema_dumper_test.rb +457 -463
  226. data/test/cases/schema_loading_test.rb +52 -0
  227. data/test/cases/scoping/default_scoping_test.rb +528 -454
  228. data/test/cases/scoping/named_scoping_test.rb +561 -524
  229. data/test/cases/scoping/relation_scoping_test.rb +400 -357
  230. data/test/cases/secure_token_test.rb +32 -0
  231. data/test/cases/serialization_test.rb +104 -104
  232. data/test/cases/serialized_attribute_test.rb +364 -277
  233. data/test/cases/statement_cache_test.rb +136 -98
  234. data/test/cases/store_test.rb +195 -194
  235. data/test/cases/suppressor_test.rb +63 -0
  236. data/test/cases/tasks/database_tasks_test.rb +462 -398
  237. data/test/cases/tasks/mysql_rake_test.rb +345 -324
  238. data/test/cases/tasks/postgresql_rake_test.rb +304 -250
  239. data/test/cases/tasks/sqlite_rake_test.rb +220 -193
  240. data/test/cases/test_case.rb +131 -123
  241. data/test/cases/test_fixtures_test.rb +36 -0
  242. data/test/cases/time_precision_test.rb +102 -0
  243. data/test/cases/timestamp_test.rb +501 -467
  244. data/test/cases/touch_later_test.rb +121 -0
  245. data/test/cases/transaction_callbacks_test.rb +518 -452
  246. data/test/cases/transaction_isolation_test.rb +106 -106
  247. data/test/cases/transactions_test.rb +834 -817
  248. data/test/cases/type/adapter_specific_registry_test.rb +133 -0
  249. data/test/cases/type/date_time_test.rb +14 -0
  250. data/test/cases/type/integer_test.rb +27 -121
  251. data/test/cases/type/string_test.rb +22 -36
  252. data/test/cases/type/type_map_test.rb +177 -177
  253. data/test/cases/type_test.rb +39 -0
  254. data/test/cases/types_test.rb +24 -141
  255. data/test/cases/unconnected_test.rb +33 -33
  256. data/test/cases/validations/absence_validation_test.rb +73 -0
  257. data/test/cases/validations/association_validation_test.rb +97 -86
  258. data/test/cases/validations/i18n_generate_message_validation_test.rb +84 -84
  259. data/test/cases/validations/i18n_validation_test.rb +86 -90
  260. data/test/cases/validations/length_validation_test.rb +79 -47
  261. data/test/cases/validations/presence_validation_test.rb +103 -68
  262. data/test/cases/validations/uniqueness_validation_test.rb +548 -457
  263. data/test/cases/validations_repair_helper.rb +19 -23
  264. data/test/cases/validations_test.rb +194 -165
  265. data/test/cases/view_test.rb +216 -119
  266. data/test/cases/yaml_serialization_test.rb +121 -126
  267. data/test/config.example.yml +97 -0
  268. data/test/config.rb +5 -5
  269. data/test/fixtures/accounts.yml +29 -29
  270. data/test/fixtures/admin/accounts.yml +2 -2
  271. data/test/fixtures/admin/users.yml +10 -10
  272. data/test/fixtures/author_addresses.original +11 -0
  273. data/test/fixtures/author_addresses.yml +17 -17
  274. data/test/fixtures/author_favorites.yml +3 -3
  275. data/test/fixtures/authors.original +17 -0
  276. data/test/fixtures/authors.yml +23 -23
  277. data/test/fixtures/bad_posts.yml +9 -0
  278. data/test/fixtures/binaries.yml +133 -133
  279. data/test/fixtures/books.yml +31 -11
  280. data/test/fixtures/bulbs.yml +5 -5
  281. data/test/fixtures/cars.yml +9 -9
  282. data/test/fixtures/categories.yml +19 -19
  283. data/test/fixtures/categories/special_categories.yml +9 -9
  284. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -4
  285. data/test/fixtures/categories_ordered.yml +7 -7
  286. data/test/fixtures/categories_posts.yml +31 -31
  287. data/test/fixtures/categorizations.yml +23 -23
  288. data/test/fixtures/clubs.yml +8 -8
  289. data/test/fixtures/collections.yml +3 -3
  290. data/test/fixtures/colleges.yml +3 -3
  291. data/test/fixtures/comments.yml +65 -65
  292. data/test/fixtures/companies.yml +67 -67
  293. data/test/fixtures/computers.yml +10 -10
  294. data/test/fixtures/content.yml +3 -0
  295. data/test/fixtures/content_positions.yml +3 -0
  296. data/test/fixtures/courses.yml +8 -8
  297. data/test/fixtures/customers.yml +25 -25
  298. data/test/fixtures/dashboards.yml +6 -6
  299. data/test/fixtures/dead_parrots.yml +5 -0
  300. data/test/fixtures/developers.yml +22 -22
  301. data/test/fixtures/developers_projects.yml +16 -16
  302. data/test/fixtures/dog_lovers.yml +7 -7
  303. data/test/fixtures/dogs.yml +4 -4
  304. data/test/fixtures/doubloons.yml +3 -3
  305. data/test/fixtures/edges.yml +5 -5
  306. data/test/fixtures/entrants.yml +14 -14
  307. data/test/fixtures/essays.yml +6 -6
  308. data/test/fixtures/faces.yml +11 -11
  309. data/test/fixtures/fk_test_has_fk.yml +3 -3
  310. data/test/fixtures/fk_test_has_pk.yml +1 -1
  311. data/test/fixtures/friendships.yml +4 -4
  312. data/test/fixtures/funny_jokes.yml +10 -10
  313. data/test/fixtures/interests.yml +33 -33
  314. data/test/fixtures/items.yml +3 -3
  315. data/test/fixtures/jobs.yml +7 -7
  316. data/test/fixtures/legacy_things.yml +3 -3
  317. data/test/fixtures/live_parrots.yml +4 -0
  318. data/test/fixtures/mateys.yml +4 -4
  319. data/test/fixtures/member_details.yml +8 -8
  320. data/test/fixtures/member_types.yml +6 -6
  321. data/test/fixtures/members.yml +11 -11
  322. data/test/fixtures/memberships.yml +34 -34
  323. data/test/fixtures/men.yml +5 -5
  324. data/test/fixtures/minimalistics.yml +2 -2
  325. data/test/fixtures/minivans.yml +5 -5
  326. data/test/fixtures/mixed_case_monkeys.yml +6 -6
  327. data/test/fixtures/mixins.yml +29 -29
  328. data/test/fixtures/movies.yml +7 -7
  329. data/test/fixtures/naked/yml/accounts.yml +1 -1
  330. data/test/fixtures/naked/yml/companies.yml +1 -1
  331. data/test/fixtures/naked/yml/courses.yml +1 -1
  332. data/test/fixtures/naked/yml/parrots.yml +2 -0
  333. data/test/fixtures/naked/yml/trees.yml +3 -0
  334. data/test/fixtures/nodes.yml +29 -0
  335. data/test/fixtures/organizations.yml +5 -5
  336. data/test/fixtures/other_comments.yml +6 -0
  337. data/test/fixtures/other_dogs.yml +2 -0
  338. data/test/fixtures/other_posts.yml +7 -0
  339. data/test/fixtures/other_topics.yml +42 -42
  340. data/test/fixtures/owners.yml +9 -9
  341. data/test/fixtures/parrots.yml +27 -27
  342. data/test/fixtures/parrots_pirates.yml +7 -7
  343. data/test/fixtures/people.yml +24 -24
  344. data/test/fixtures/peoples_treasures.yml +3 -3
  345. data/test/fixtures/pets.yml +19 -19
  346. data/test/fixtures/pirates.yml +15 -12
  347. data/test/fixtures/posts.yml +80 -80
  348. data/test/fixtures/price_estimates.yml +16 -7
  349. data/test/fixtures/products.yml +4 -4
  350. data/test/fixtures/projects.yml +7 -7
  351. data/test/fixtures/ratings.yml +14 -14
  352. data/test/fixtures/readers.yml +11 -11
  353. data/test/fixtures/references.yml +17 -17
  354. data/test/fixtures/reserved_words/distinct.yml +5 -5
  355. data/test/fixtures/reserved_words/distinct_select.yml +11 -11
  356. data/test/fixtures/reserved_words/group.yml +14 -14
  357. data/test/fixtures/reserved_words/select.yml +8 -8
  358. data/test/fixtures/reserved_words/values.yml +7 -7
  359. data/test/fixtures/ships.yml +6 -6
  360. data/test/fixtures/speedometers.yml +8 -8
  361. data/test/fixtures/sponsors.yml +12 -12
  362. data/test/fixtures/string_key_objects.yml +7 -7
  363. data/test/fixtures/subscribers.yml +10 -10
  364. data/test/fixtures/subscriptions.yml +12 -12
  365. data/test/fixtures/taggings.yml +78 -78
  366. data/test/fixtures/tags.yml +11 -11
  367. data/test/fixtures/tasks.yml +7 -7
  368. data/test/fixtures/teapots.yml +3 -3
  369. data/test/fixtures/to_be_linked/accounts.yml +2 -2
  370. data/test/fixtures/to_be_linked/users.yml +10 -10
  371. data/test/fixtures/topics.yml +49 -49
  372. data/test/fixtures/toys.yml +14 -14
  373. data/test/fixtures/traffic_lights.yml +9 -9
  374. data/test/fixtures/treasures.yml +10 -10
  375. data/test/fixtures/trees.yml +3 -0
  376. data/test/fixtures/uuid_children.yml +3 -3
  377. data/test/fixtures/uuid_parents.yml +2 -2
  378. data/test/fixtures/variants.yml +4 -4
  379. data/test/fixtures/vegetables.yml +19 -19
  380. data/test/fixtures/vertices.yml +3 -3
  381. data/test/fixtures/warehouse_things.yml +2 -2
  382. data/test/fixtures/zines.yml +5 -5
  383. data/test/migrations/10_urban/9_add_expressions.rb +11 -11
  384. data/test/migrations/decimal/1_give_me_big_numbers.rb +15 -15
  385. data/test/migrations/magic/1_currencies_have_symbols.rb +12 -12
  386. data/test/migrations/missing/1000_people_have_middle_names.rb +9 -9
  387. data/test/migrations/missing/1_people_have_last_names.rb +9 -9
  388. data/test/migrations/missing/3_we_need_reminders.rb +12 -12
  389. data/test/migrations/missing/4_innocent_jointable.rb +12 -12
  390. data/test/migrations/rename/1_we_need_things.rb +11 -11
  391. data/test/migrations/rename/2_rename_things.rb +9 -9
  392. data/test/migrations/to_copy/1_people_have_hobbies.rb +9 -9
  393. data/test/migrations/to_copy/2_people_have_descriptions.rb +9 -9
  394. data/test/migrations/to_copy2/1_create_articles.rb +7 -7
  395. data/test/migrations/to_copy2/2_create_comments.rb +7 -7
  396. data/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb +9 -9
  397. data/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb +9 -9
  398. data/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb +9 -9
  399. data/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb +7 -7
  400. data/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb +7 -7
  401. data/test/migrations/valid/1_valid_people_have_last_names.rb +9 -9
  402. data/test/migrations/valid/2_we_need_reminders.rb +12 -12
  403. data/test/migrations/valid/3_innocent_jointable.rb +12 -12
  404. data/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb +9 -9
  405. data/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb +12 -12
  406. data/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb +12 -12
  407. data/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb +9 -9
  408. data/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb +12 -12
  409. data/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb +12 -12
  410. data/test/migrations/version_check/20131219224947_migration_version_check.rb +8 -8
  411. data/test/models/admin.rb +5 -5
  412. data/test/models/admin/account.rb +3 -3
  413. data/test/models/admin/randomly_named_c1.rb +6 -2
  414. data/test/models/admin/user.rb +40 -40
  415. data/test/models/aircraft.rb +5 -4
  416. data/test/models/arunit2_model.rb +3 -3
  417. data/test/models/author.rb +209 -212
  418. data/test/models/auto_id.rb +4 -4
  419. data/test/models/autoloadable/extra_firm.rb +2 -2
  420. data/test/models/binary.rb +2 -2
  421. data/test/models/bird.rb +12 -12
  422. data/test/models/book.rb +23 -18
  423. data/test/models/boolean.rb +2 -2
  424. data/test/models/bulb.rb +52 -51
  425. data/test/models/cake_designer.rb +3 -3
  426. data/test/models/car.rb +29 -26
  427. data/test/models/carrier.rb +2 -2
  428. data/test/models/cat.rb +10 -0
  429. data/test/models/categorization.rb +19 -19
  430. data/test/models/category.rb +35 -35
  431. data/test/models/chef.rb +8 -7
  432. data/test/models/citation.rb +3 -3
  433. data/test/models/club.rb +25 -23
  434. data/test/models/college.rb +10 -10
  435. data/test/models/column.rb +3 -3
  436. data/test/models/column_name.rb +3 -3
  437. data/test/models/comment.rb +76 -64
  438. data/test/models/company.rb +230 -228
  439. data/test/models/company_in_module.rb +98 -98
  440. data/test/models/computer.rb +3 -3
  441. data/test/models/contact.rb +41 -41
  442. data/test/models/content.rb +40 -0
  443. data/test/models/contract.rb +20 -20
  444. data/test/models/country.rb +7 -7
  445. data/test/models/course.rb +6 -6
  446. data/test/models/customer.rb +83 -77
  447. data/test/models/customer_carrier.rb +14 -14
  448. data/test/models/dashboard.rb +3 -3
  449. data/test/models/default.rb +2 -2
  450. data/test/models/department.rb +4 -4
  451. data/test/models/developer.rb +274 -255
  452. data/test/models/dog.rb +5 -5
  453. data/test/models/dog_lover.rb +5 -5
  454. data/test/models/doubloon.rb +12 -12
  455. data/test/models/drink_designer.rb +3 -3
  456. data/test/models/edge.rb +5 -5
  457. data/test/models/electron.rb +5 -5
  458. data/test/models/engine.rb +4 -4
  459. data/test/models/entrant.rb +3 -3
  460. data/test/models/essay.rb +5 -5
  461. data/test/models/event.rb +3 -3
  462. data/test/models/eye.rb +37 -37
  463. data/test/models/face.rb +9 -9
  464. data/test/models/friendship.rb +6 -6
  465. data/test/models/guid.rb +2 -2
  466. data/test/models/guitar.rb +4 -0
  467. data/test/models/hotel.rb +11 -9
  468. data/test/models/image.rb +3 -3
  469. data/test/models/interest.rb +5 -5
  470. data/test/models/invoice.rb +4 -4
  471. data/test/models/item.rb +7 -7
  472. data/test/models/job.rb +7 -7
  473. data/test/models/joke.rb +7 -7
  474. data/test/models/keyboard.rb +3 -3
  475. data/test/models/legacy_thing.rb +3 -3
  476. data/test/models/lesson.rb +11 -11
  477. data/test/models/line_item.rb +3 -3
  478. data/test/models/liquid.rb +4 -4
  479. data/test/models/man.rb +11 -11
  480. data/test/models/matey.rb +4 -4
  481. data/test/models/member.rb +42 -41
  482. data/test/models/member_detail.rb +8 -7
  483. data/test/models/member_type.rb +3 -3
  484. data/test/models/membership.rb +35 -35
  485. data/test/models/mentor.rb +3 -0
  486. data/test/models/minimalistic.rb +2 -2
  487. data/test/models/minivan.rb +9 -9
  488. data/test/models/mixed_case_monkey.rb +3 -3
  489. data/test/models/mocktail_designer.rb +2 -0
  490. data/test/models/molecule.rb +6 -6
  491. data/test/models/movie.rb +5 -5
  492. data/test/models/node.rb +5 -0
  493. data/test/models/non_primary_key.rb +2 -0
  494. data/test/models/notification.rb +3 -0
  495. data/test/models/order.rb +4 -4
  496. data/test/models/organization.rb +14 -14
  497. data/test/models/other_dog.rb +5 -0
  498. data/test/models/owner.rb +37 -34
  499. data/test/models/parrot.rb +28 -29
  500. data/test/models/person.rb +142 -143
  501. data/test/models/personal_legacy_thing.rb +4 -4
  502. data/test/models/pet.rb +18 -15
  503. data/test/models/pet_treasure.rb +6 -0
  504. data/test/models/pirate.rb +92 -92
  505. data/test/models/possession.rb +3 -3
  506. data/test/models/post.rb +273 -264
  507. data/test/models/price_estimate.rb +4 -4
  508. data/test/models/professor.rb +5 -5
  509. data/test/models/project.rb +40 -31
  510. data/test/models/publisher.rb +2 -2
  511. data/test/models/publisher/article.rb +4 -4
  512. data/test/models/publisher/magazine.rb +3 -3
  513. data/test/models/randomly_named_c1.rb +1 -1
  514. data/test/models/rating.rb +4 -4
  515. data/test/models/reader.rb +23 -23
  516. data/test/models/recipe.rb +3 -0
  517. data/test/models/record.rb +2 -2
  518. data/test/models/reference.rb +22 -22
  519. data/test/models/reply.rb +61 -61
  520. data/test/models/ship.rb +39 -33
  521. data/test/models/ship_part.rb +8 -8
  522. data/test/models/shop.rb +17 -17
  523. data/test/models/shop_account.rb +6 -6
  524. data/test/models/speedometer.rb +6 -6
  525. data/test/models/sponsor.rb +7 -7
  526. data/test/models/string_key_object.rb +3 -3
  527. data/test/models/student.rb +4 -4
  528. data/test/models/subject.rb +16 -16
  529. data/test/models/subscriber.rb +8 -8
  530. data/test/models/subscription.rb +4 -4
  531. data/test/models/tag.rb +13 -7
  532. data/test/models/tagging.rb +13 -13
  533. data/test/models/task.rb +5 -5
  534. data/test/models/topic.rb +118 -124
  535. data/test/models/toy.rb +6 -6
  536. data/test/models/traffic_light.rb +4 -4
  537. data/test/models/treasure.rb +14 -14
  538. data/test/models/treaty.rb +7 -7
  539. data/test/models/tree.rb +3 -0
  540. data/test/models/tuning_peg.rb +4 -0
  541. data/test/models/tyre.rb +11 -11
  542. data/test/models/user.rb +14 -0
  543. data/test/models/uuid_child.rb +3 -3
  544. data/test/models/uuid_item.rb +6 -0
  545. data/test/models/uuid_parent.rb +3 -3
  546. data/test/models/vegetables.rb +24 -24
  547. data/test/models/vehicle.rb +6 -6
  548. data/test/models/vertex.rb +9 -9
  549. data/test/models/warehouse_thing.rb +5 -5
  550. data/test/models/wheel.rb +3 -3
  551. data/test/models/without_table.rb +3 -3
  552. data/test/models/zine.rb +3 -3
  553. data/test/schema/mysql2_specific_schema.rb +68 -58
  554. data/test/schema/oracle_specific_schema.rb +40 -43
  555. data/test/schema/postgresql_specific_schema.rb +114 -202
  556. data/test/schema/schema.rb +1057 -952
  557. data/test/schema/schema.rb.original +1057 -0
  558. data/test/schema/sqlite_specific_schema.rb +18 -22
  559. data/test/support/config.rb +43 -43
  560. data/test/support/connection.rb +23 -22
  561. data/test/support/connection_helper.rb +14 -14
  562. data/test/support/ddl_helper.rb +8 -8
  563. data/test/support/schema_dumping_helper.rb +20 -20
  564. data/test/support/yaml_compatibility_fixtures/rails_4_1.yml +22 -0
  565. data/test/support/yaml_compatibility_fixtures/rails_4_2_0.yml +182 -0
  566. metadata +129 -28
  567. data/lib/mswin32/rb19x/ibm_db.so +0 -0
  568. data/lib/mswin32/rb21x/i386/ibm_db.so +0 -0
  569. data/lib/mswin32/rb22x/i386/ibm_db.so +0 -0
  570. data/lib/mswin32/rb23x/i386/ibm_db.so +0 -0
  571. data/test/cases/associations/deprecated_counter_cache_on_has_many_through_test.rb +0 -26
  572. data/test/cases/attribute_methods/serialization_test.rb +0 -29
  573. data/test/cases/migration/change_schema_test - Copy.rb +0 -448
  574. data/test/cases/migration/foreign_key_test - Changed.rb +0 -325
  575. data/test/cases/migration/table_and_index_test.rb +0 -24
  576. data/test/cases/relation/where_test2.rb +0 -36
  577. data/test/cases/type/decimal_test.rb +0 -56
  578. data/test/cases/type/unsigned_integer_test.rb +0 -18
  579. data/test/cases/xml_serialization_test.rb +0 -457
  580. data/test/connections/native_ibm_db/connection.rb +0 -44
  581. data/test/fixtures/naked/csv/accounts.csv +0 -1
  582. data/test/schema/i5/ibm_db_specific_schema.rb +0 -137
  583. data/test/schema/ids/ibm_db_specific_schema.rb +0 -140
  584. data/test/schema/luw/ibm_db_specific_schema.rb +0 -137
  585. data/test/schema/mysql_specific_schema.rb +0 -70
  586. data/test/schema/zOS/ibm_db_specific_schema.rb +0 -208
@@ -1,785 +1,763 @@
1
- require 'cases/helper'
2
- require 'models/topic' # For booleans
3
- require 'models/pirate' # For timestamps
4
- require 'models/parrot'
5
- require 'models/person' # For optimistic locking
6
- require 'models/aircraft'
7
-
8
- class Pirate # Just reopening it, not defining it
9
- attr_accessor :detected_changes_in_after_update # Boolean for if changes are detected
10
- attr_accessor :changes_detected_in_after_update # Actual changes
11
-
12
- after_update :check_changes
13
-
14
- private
15
- # after_save/update and the model itself
16
- # can end up checking dirty status and acting on the results
17
- def check_changes
18
- if self.changed?
19
- self.detected_changes_in_after_update = true
20
- self.changes_detected_in_after_update = self.changes
21
- end
22
- end
23
- end
24
-
25
- class NumericData < ActiveRecord::Base
26
- self.table_name = 'numeric_data'
27
- end
28
-
29
- class DirtyTest < ActiveRecord::TestCase
30
- include InTimeZone
31
-
32
- # Dummy to force column loads so query counts are clean.
33
- def setup
34
- Person.create :first_name => 'foo'
35
- end
36
-
37
- def test_attribute_changes
38
- # New record - no changes.
39
- pirate = Pirate.new
40
- assert !pirate.catchphrase_changed?
41
- assert_nil pirate.catchphrase_change
42
-
43
- # Change catchphrase.
44
- pirate.catchphrase = 'arrr'
45
- assert pirate.catchphrase_changed?
46
- assert_nil pirate.catchphrase_was
47
- assert_equal [nil, 'arrr'], pirate.catchphrase_change
48
-
49
- # Saved - no changes.
50
- pirate.save!
51
- assert !pirate.catchphrase_changed?
52
- assert_nil pirate.catchphrase_change
53
-
54
- # Same value - no changes.
55
- pirate.catchphrase = 'arrr'
56
- assert !pirate.catchphrase_changed?
57
- assert_nil pirate.catchphrase_change
58
- end
59
-
60
- def test_time_attributes_changes_with_time_zone
61
- in_time_zone 'Paris' do
62
- target = Class.new(ActiveRecord::Base)
63
- target.table_name = 'pirates'
64
-
65
- # New record - no changes.
66
- pirate = target.new
67
- assert !pirate.created_on_changed?
68
- assert_nil pirate.created_on_change
69
-
70
- # Saved - no changes.
71
- pirate.catchphrase = 'arrrr, time zone!!'
72
- pirate.save!
73
- assert !pirate.created_on_changed?
74
- assert_nil pirate.created_on_change
75
-
76
- # Change created_on.
77
- old_created_on = pirate.created_on
78
- pirate.created_on = Time.now - 1.day
79
- assert pirate.created_on_changed?
80
- assert_kind_of ActiveSupport::TimeWithZone, pirate.created_on_was
81
- assert_equal old_created_on, pirate.created_on_was
82
- pirate.created_on = old_created_on
83
- assert !pirate.created_on_changed?
84
- end
85
- end
86
-
87
- def test_setting_time_attributes_with_time_zone_field_to_itself_should_not_be_marked_as_a_change
88
- in_time_zone 'Paris' do
89
- target = Class.new(ActiveRecord::Base)
90
- target.table_name = 'pirates'
91
-
92
- pirate = target.create
93
- pirate.created_on = pirate.created_on
94
- assert !pirate.created_on_changed?
95
- end
96
- end
97
-
98
- def test_time_attributes_changes_without_time_zone_by_skip
99
- in_time_zone 'Paris' do
100
- target = Class.new(ActiveRecord::Base)
101
- target.table_name = 'pirates'
102
-
103
- target.skip_time_zone_conversion_for_attributes = [:created_on]
104
-
105
- # New record - no changes.
106
- pirate = target.new
107
- assert !pirate.created_on_changed?
108
- assert_nil pirate.created_on_change
109
-
110
- # Saved - no changes.
111
- pirate.catchphrase = 'arrrr, time zone!!'
112
- pirate.save!
113
- assert !pirate.created_on_changed?
114
- assert_nil pirate.created_on_change
115
-
116
- # Change created_on.
117
- old_created_on = pirate.created_on
118
- pirate.created_on = Time.now + 1.day
119
- assert pirate.created_on_changed?
120
- # kind_of does not work because
121
- # ActiveSupport::TimeWithZone.name == 'Time'
122
- assert_instance_of Time, pirate.created_on_was
123
- assert_equal old_created_on, pirate.created_on_was
124
- end
125
- end
126
-
127
- def test_time_attributes_changes_without_time_zone
128
- with_timezone_config aware_attributes: false do
129
- target = Class.new(ActiveRecord::Base)
130
- target.table_name = 'pirates'
131
-
132
- # New record - no changes.
133
- pirate = target.new
134
- assert !pirate.created_on_changed?
135
- assert_nil pirate.created_on_change
136
-
137
- # Saved - no changes.
138
- pirate.catchphrase = 'arrrr, time zone!!'
139
- pirate.save!
140
- assert !pirate.created_on_changed?
141
- assert_nil pirate.created_on_change
142
-
143
- # Change created_on.
144
- old_created_on = pirate.created_on
145
- pirate.created_on = Time.now + 1.day
146
- assert pirate.created_on_changed?
147
- # kind_of does not work because
148
- # ActiveSupport::TimeWithZone.name == 'Time'
149
- assert_instance_of Time, pirate.created_on_was
150
- assert_equal old_created_on, pirate.created_on_was
151
- end
152
- end
153
-
154
-
155
- def test_aliased_attribute_changes
156
- # the actual attribute here is name, title is an
157
- # alias setup via alias_attribute
158
- parrot = Parrot.new
159
- assert !parrot.title_changed?
160
- assert_nil parrot.title_change
161
-
162
- parrot.name = 'Sam'
163
- assert parrot.title_changed?
164
- assert_nil parrot.title_was
165
- assert_equal parrot.name_change, parrot.title_change
166
- end
167
-
168
- def test_reset_attribute!
169
- pirate = Pirate.create!(:catchphrase => 'Yar!')
170
- pirate.catchphrase = 'Ahoy!'
171
-
172
- assert_deprecated do
173
- pirate.reset_catchphrase!
174
- end
175
- assert_equal "Yar!", pirate.catchphrase
176
- assert_equal Hash.new, pirate.changes
177
- assert !pirate.catchphrase_changed?
178
- end
179
-
180
- def test_restore_attribute!
181
- pirate = Pirate.create!(:catchphrase => 'Yar!')
182
- pirate.catchphrase = 'Ahoy!'
183
-
184
- pirate.restore_catchphrase!
185
- assert_equal "Yar!", pirate.catchphrase
186
- assert_equal Hash.new, pirate.changes
187
- assert !pirate.catchphrase_changed?
188
- end
189
-
190
- def test_nullable_number_not_marked_as_changed_if_new_value_is_blank
191
- pirate = Pirate.new
192
-
193
- ["", nil].each do |value|
194
- pirate.parrot_id = value
195
- assert !pirate.parrot_id_changed?
196
- assert_nil pirate.parrot_id_change
197
- end
198
- end
199
-
200
- def test_nullable_decimal_not_marked_as_changed_if_new_value_is_blank
201
- numeric_data = NumericData.new
202
-
203
- ["", nil].each do |value|
204
- numeric_data.bank_balance = value
205
- assert !numeric_data.bank_balance_changed?
206
- assert_nil numeric_data.bank_balance_change
207
- end
208
- end
209
-
210
- def test_nullable_float_not_marked_as_changed_if_new_value_is_blank
211
- numeric_data = NumericData.new
212
-
213
- ["", nil].each do |value|
214
- numeric_data.temperature = value
215
- assert !numeric_data.temperature_changed?
216
- assert_nil numeric_data.temperature_change
217
- end
218
- end
219
-
220
- def test_nullable_datetime_not_marked_as_changed_if_new_value_is_blank
221
- in_time_zone 'Edinburgh' do
222
- target = Class.new(ActiveRecord::Base)
223
- target.table_name = 'topics'
224
-
225
- topic = target.create
226
- assert_nil topic.written_on
227
-
228
- ["", nil].each do |value|
229
- topic.written_on = value
230
- assert_nil topic.written_on
231
- assert !topic.written_on_changed?
232
- end
233
- end
234
- end
235
-
236
- def test_integer_zero_to_string_zero_not_marked_as_changed
237
- pirate = Pirate.new
238
- pirate.parrot_id = 0
239
- pirate.catchphrase = 'arrr'
240
- assert pirate.save!
241
-
242
- assert !pirate.changed?
243
-
244
- pirate.parrot_id = '0'
245
- assert !pirate.changed?
246
- end
247
-
248
- def test_integer_zero_to_integer_zero_not_marked_as_changed
249
- pirate = Pirate.new
250
- pirate.parrot_id = 0
251
- pirate.catchphrase = 'arrr'
252
- assert pirate.save!
253
-
254
- assert !pirate.changed?
255
-
256
- pirate.parrot_id = 0
257
- assert !pirate.changed?
258
- end
259
-
260
- def test_float_zero_to_string_zero_not_marked_as_changed
261
- data = NumericData.new :temperature => 0.0
262
- data.save!
263
-
264
- assert_not data.changed?
265
-
266
- data.temperature = '0'
267
- assert_empty data.changes
268
-
269
- data.temperature = '0.0'
270
- assert_empty data.changes
271
-
272
- data.temperature = '0.00'
273
- assert_empty data.changes
274
- end
275
-
276
- def test_zero_to_blank_marked_as_changed
277
- pirate = Pirate.new
278
- pirate.catchphrase = "Yarrrr, me hearties"
279
- pirate.parrot_id = 1
280
- pirate.save
281
-
282
- # check the change from 1 to ''
283
- pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
284
- pirate.parrot_id = ''
285
- assert pirate.parrot_id_changed?
286
- assert_equal([1, nil], pirate.parrot_id_change)
287
- pirate.save
288
-
289
- # check the change from nil to 0
290
- pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
291
- pirate.parrot_id = 0
292
- assert pirate.parrot_id_changed?
293
- assert_equal([nil, 0], pirate.parrot_id_change)
294
- pirate.save
295
-
296
- # check the change from 0 to ''
297
- pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
298
- pirate.parrot_id = ''
299
- assert pirate.parrot_id_changed?
300
- assert_equal([0, nil], pirate.parrot_id_change)
301
- end
302
-
303
- def test_object_should_be_changed_if_any_attribute_is_changed
304
- pirate = Pirate.new
305
- assert !pirate.changed?
306
- assert_equal [], pirate.changed
307
- assert_equal Hash.new, pirate.changes
308
-
309
- pirate.catchphrase = 'arrr'
310
- assert pirate.changed?
311
- assert_nil pirate.catchphrase_was
312
- assert_equal %w(catchphrase), pirate.changed
313
- assert_equal({'catchphrase' => [nil, 'arrr']}, pirate.changes)
314
-
315
- pirate.save
316
- assert !pirate.changed?
317
- assert_equal [], pirate.changed
318
- assert_equal Hash.new, pirate.changes
319
- end
320
-
321
- def test_attribute_will_change!
322
- pirate = Pirate.create!(:catchphrase => 'arr')
323
-
324
- assert !pirate.catchphrase_changed?
325
- assert pirate.catchphrase_will_change!
326
- assert pirate.catchphrase_changed?
327
- assert_equal ['arr', 'arr'], pirate.catchphrase_change
328
-
329
- pirate.catchphrase << ' matey!'
330
- assert pirate.catchphrase_changed?
331
- assert_equal ['arr', 'arr matey!'], pirate.catchphrase_change
332
- end
333
-
334
- def test_association_assignment_changes_foreign_key
335
- pirate = Pirate.create!(:catchphrase => 'jarl')
336
- pirate.parrot = Parrot.create!(:name => 'Lorre')
337
- assert pirate.changed?
338
- assert_equal %w(parrot_id), pirate.changed
339
- end
340
-
341
- def test_attribute_should_be_compared_with_type_cast
342
- topic = Topic.new
343
- assert topic.approved?
344
- assert !topic.approved_changed?
345
-
346
- # Coming from web form.
347
- params = {:topic => {:approved => 1}}
348
- # In the controller.
349
- topic.attributes = params[:topic]
350
- assert topic.approved?
351
- assert !topic.approved_changed?
352
- end
353
-
354
- def test_partial_update
355
- pirate = Pirate.new(:catchphrase => 'foo')
356
- old_updated_on = 1.hour.ago.beginning_of_day
357
-
358
- with_partial_writes Pirate, false do
359
- assert_queries(2) { 2.times { pirate.save! } }
360
- Pirate.where(id: pirate.id).update_all(:updated_on => old_updated_on)
361
- end
362
-
363
- with_partial_writes Pirate, true do
364
- assert_queries(0) { 2.times { pirate.save! } }
365
- assert_equal old_updated_on, pirate.reload.updated_on
366
-
367
- assert_queries(1) { pirate.catchphrase = 'bar'; pirate.save! }
368
- assert_not_equal old_updated_on, pirate.reload.updated_on
369
- end
370
- end
371
-
372
- def test_partial_update_with_optimistic_locking
373
- person = Person.new(:first_name => 'foo')
374
- old_lock_version = 1
375
-
376
- with_partial_writes Person, false do
377
- assert_queries(2) { 2.times { person.save! } }
378
- Person.where(id: person.id).update_all(:first_name => 'baz')
379
- end
380
-
381
- with_partial_writes Person, true do
382
- assert_queries(0) { 2.times { person.save! } }
383
- assert_equal old_lock_version, person.reload.lock_version
384
-
385
- assert_queries(1) { person.first_name = 'bar'; person.save! }
386
- assert_not_equal old_lock_version, person.reload.lock_version
387
- end
388
- end
389
-
390
- def test_changed_attributes_should_be_preserved_if_save_failure
391
- pirate = Pirate.new
392
- pirate.parrot_id = 1
393
- assert !pirate.save
394
- check_pirate_after_save_failure(pirate)
395
-
396
- pirate = Pirate.new
397
- pirate.parrot_id = 1
398
- assert_raise(ActiveRecord::RecordInvalid) { pirate.save! }
399
- check_pirate_after_save_failure(pirate)
400
- end
401
-
402
- def test_reload_should_clear_changed_attributes
403
- pirate = Pirate.create!(:catchphrase => "shiver me timbers")
404
- pirate.catchphrase = "*hic*"
405
- assert pirate.changed?
406
- pirate.reload
407
- assert !pirate.changed?
408
- end
409
-
410
- def test_dup_objects_should_not_copy_dirty_flag_from_creator
411
- pirate = Pirate.create!(:catchphrase => "shiver me timbers")
412
- pirate_dup = pirate.dup
413
- pirate_dup.restore_catchphrase!
414
- pirate.catchphrase = "I love Rum"
415
- assert pirate.catchphrase_changed?
416
- assert !pirate_dup.catchphrase_changed?
417
- end
418
-
419
- def test_reverted_changes_are_not_dirty
420
- phrase = "shiver me timbers"
421
- pirate = Pirate.create!(:catchphrase => phrase)
422
- pirate.catchphrase = "*hic*"
423
- assert pirate.changed?
424
- pirate.catchphrase = phrase
425
- assert !pirate.changed?
426
- end
427
-
428
- def test_reverted_changes_are_not_dirty_after_multiple_changes
429
- phrase = "shiver me timbers"
430
- pirate = Pirate.create!(:catchphrase => phrase)
431
- 10.times do |i|
432
- pirate.catchphrase = "*hic*" * i
433
- assert pirate.changed?
434
- end
435
- assert pirate.changed?
436
- pirate.catchphrase = phrase
437
- assert !pirate.changed?
438
- end
439
-
440
-
441
- def test_reverted_changes_are_not_dirty_going_from_nil_to_value_and_back
442
- pirate = Pirate.create!(:catchphrase => "Yar!")
443
-
444
- pirate.parrot_id = 1
445
- assert pirate.changed?
446
- assert pirate.parrot_id_changed?
447
- assert !pirate.catchphrase_changed?
448
-
449
- pirate.parrot_id = nil
450
- assert !pirate.changed?
451
- assert !pirate.parrot_id_changed?
452
- assert !pirate.catchphrase_changed?
453
- end
454
-
455
- def test_save_should_store_serialized_attributes_even_with_partial_writes
456
- with_partial_writes(Topic) do
457
- topic = Topic.create!(:content => {:a => "a"})
458
-
459
- assert_not topic.changed?
460
-
461
- topic.content[:b] = "b"
462
-
463
- assert topic.changed?
464
-
465
- topic.save!
466
-
467
- assert_not topic.changed?
468
- assert_equal "b", topic.content[:b]
469
-
470
- topic.reload
471
-
472
- assert_equal "b", topic.content[:b]
473
- end
474
- end
475
-
476
- def test_save_always_should_update_timestamps_when_serialized_attributes_are_present
477
- with_partial_writes(Topic) do
478
- topic = Topic.create!(:content => {:a => "a"})
479
- topic.save!
480
-
481
- updated_at = topic.updated_at
482
- topic.content[:hello] = 'world'
483
- topic.save!
484
-
485
- assert_not_equal updated_at, topic.updated_at
486
- assert_equal 'world', topic.content[:hello]
487
- end
488
- end
489
-
490
- def test_save_should_not_save_serialized_attribute_with_partial_writes_if_not_present
491
- with_partial_writes(Topic) do
492
- Topic.create!(:author_name => 'Bill', :content => {:a => "a"})
493
- topic = Topic.select('id, author_name').first
494
- topic.update_columns author_name: 'John'
495
- topic = Topic.first
496
- assert_not_nil topic.content
497
- end
498
- end
499
-
500
- def test_previous_changes
501
- # original values should be in previous_changes
502
- pirate = Pirate.new
503
-
504
- assert_equal Hash.new, pirate.previous_changes
505
- pirate.catchphrase = "arrr"
506
- pirate.save!
507
-
508
- assert_equal 4, pirate.previous_changes.size
509
- assert_equal [nil, "arrr"], pirate.previous_changes['catchphrase']
510
- assert_equal [nil, pirate.id], pirate.previous_changes['id']
511
- assert_nil pirate.previous_changes['updated_on'][0]
512
- assert_not_nil pirate.previous_changes['updated_on'][1]
513
- assert_nil pirate.previous_changes['created_on'][0]
514
- assert_not_nil pirate.previous_changes['created_on'][1]
515
- assert !pirate.previous_changes.key?('parrot_id')
516
-
517
- # original values should be in previous_changes
518
- pirate = Pirate.new
519
-
520
- assert_equal Hash.new, pirate.previous_changes
521
- pirate.catchphrase = "arrr"
522
- pirate.save
523
-
524
- assert_equal 4, pirate.previous_changes.size
525
- assert_equal [nil, "arrr"], pirate.previous_changes['catchphrase']
526
- assert_equal [nil, pirate.id], pirate.previous_changes['id']
527
- assert pirate.previous_changes.include?('updated_on')
528
- assert pirate.previous_changes.include?('created_on')
529
- assert !pirate.previous_changes.key?('parrot_id')
530
-
531
- pirate.catchphrase = "Yar!!"
532
- pirate.reload
533
- assert_equal Hash.new, pirate.previous_changes
534
-
535
- pirate = Pirate.find_by_catchphrase("arrr")
536
- pirate.catchphrase = "Me Maties!"
537
- pirate.save!
538
-
539
- assert_equal 2, pirate.previous_changes.size
540
- assert_equal ["arrr", "Me Maties!"], pirate.previous_changes['catchphrase']
541
- assert_not_nil pirate.previous_changes['updated_on'][0]
542
- assert_not_nil pirate.previous_changes['updated_on'][1]
543
- assert !pirate.previous_changes.key?('parrot_id')
544
- assert !pirate.previous_changes.key?('created_on')
545
-
546
- pirate = Pirate.find_by_catchphrase("Me Maties!")
547
- pirate.catchphrase = "Thar She Blows!"
548
- pirate.save
549
-
550
- assert_equal 2, pirate.previous_changes.size
551
- assert_equal ["Me Maties!", "Thar She Blows!"], pirate.previous_changes['catchphrase']
552
- assert_not_nil pirate.previous_changes['updated_on'][0]
553
- assert_not_nil pirate.previous_changes['updated_on'][1]
554
- assert !pirate.previous_changes.key?('parrot_id')
555
- assert !pirate.previous_changes.key?('created_on')
556
-
557
- pirate = Pirate.find_by_catchphrase("Thar She Blows!")
558
- pirate.update(catchphrase: "Ahoy!")
559
-
560
- assert_equal 2, pirate.previous_changes.size
561
- assert_equal ["Thar She Blows!", "Ahoy!"], pirate.previous_changes['catchphrase']
562
- assert_not_nil pirate.previous_changes['updated_on'][0]
563
- assert_not_nil pirate.previous_changes['updated_on'][1]
564
- assert !pirate.previous_changes.key?('parrot_id')
565
- assert !pirate.previous_changes.key?('created_on')
566
-
567
- pirate = Pirate.find_by_catchphrase("Ahoy!")
568
- pirate.update_attribute(:catchphrase, "Ninjas suck!")
569
-
570
- assert_equal 2, pirate.previous_changes.size
571
- assert_equal ["Ahoy!", "Ninjas suck!"], pirate.previous_changes['catchphrase']
572
- assert_not_nil pirate.previous_changes['updated_on'][0]
573
- assert_not_nil pirate.previous_changes['updated_on'][1]
574
- assert !pirate.previous_changes.key?('parrot_id')
575
- assert !pirate.previous_changes.key?('created_on')
576
- end
577
-
578
- if ActiveRecord::Base.connection.supports_migrations?
579
- class Testings < ActiveRecord::Base; end
580
- def test_field_named_field
581
- ActiveRecord::Base.connection.create_table :testings do |t|
582
- t.string :field
583
- end
584
- assert_nothing_raised do
585
- Testings.new.attributes
586
- end
587
- ensure
588
- ActiveRecord::Base.connection.drop_table :testings rescue nil
589
- end
590
- end
591
-
592
- def test_datetime_attribute_can_be_updated_with_fractional_seconds
593
- in_time_zone 'Paris' do
594
- target = Class.new(ActiveRecord::Base)
595
- target.table_name = 'topics'
596
-
597
- written_on = Time.utc(2012, 12, 1, 12, 0, 0).in_time_zone('Paris')
598
-
599
- topic = target.create(:written_on => written_on)
600
- topic.written_on += 0.3
601
-
602
- assert topic.written_on_changed?, 'Fractional second update not detected'
603
- end
604
- end
605
-
606
- def test_datetime_attribute_doesnt_change_if_zone_is_modified_in_string
607
- time_in_paris = Time.utc(2014, 1, 1, 12, 0, 0).in_time_zone('Paris')
608
- pirate = Pirate.create!(:catchphrase => 'rrrr', :created_on => time_in_paris)
609
-
610
- pirate.created_on = pirate.created_on.in_time_zone('Tokyo').to_s
611
- assert !pirate.created_on_changed?
612
- end
613
-
614
- test "partial insert" do
615
- with_partial_writes Person do
616
- jon = nil
617
- assert_sql(/first_name/i) do
618
- jon = Person.create! first_name: 'Jon'
619
- end
620
-
621
- assert ActiveRecord::SQLCounter.log_all.none? { |sql| sql =~ /followers_count/ }
622
-
623
- jon.reload
624
- assert_equal 'Jon', jon.first_name
625
- assert_equal 0, jon.followers_count
626
- assert_not_nil jon.id
627
- end
628
- end
629
-
630
- test "partial insert with empty values" do
631
- with_partial_writes Aircraft do
632
- a = Aircraft.create!
633
- a.reload
634
- assert_not_nil a.id
635
- end
636
- end
637
-
638
- test "defaults with type that implements `type_cast_for_database`" do
639
- type = Class.new(ActiveRecord::Type::Value) do
640
- def type_cast(value)
641
- value.to_i
642
- end
643
-
644
- def type_cast_for_database(value)
645
- value.to_s
646
- end
647
- end
648
-
649
- model_class = Class.new(ActiveRecord::Base) do
650
- self.table_name = 'numeric_data'
651
- attribute :foo, type.new, default: 1
652
- end
653
-
654
- model = model_class.new
655
- assert_not model.foo_changed?
656
-
657
- model = model_class.new(foo: 1)
658
- assert_not model.foo_changed?
659
-
660
- model = model_class.new(foo: '1')
661
- assert_not model.foo_changed?
662
- end
663
-
664
- test "in place mutation detection" do
665
- pirate = Pirate.create!(catchphrase: "arrrr")
666
- pirate.catchphrase << " matey!"
667
-
668
- assert pirate.catchphrase_changed?
669
- expected_changes = {
670
- "catchphrase" => ["arrrr", "arrrr matey!"]
671
- }
672
- assert_equal(expected_changes, pirate.changes)
673
- assert_equal("arrrr", pirate.catchphrase_was)
674
- assert pirate.catchphrase_changed?(from: "arrrr")
675
- assert_not pirate.catchphrase_changed?(from: "anything else")
676
- assert pirate.changed_attributes.include?(:catchphrase)
677
-
678
- pirate.save!
679
- pirate.reload
680
-
681
- assert_equal "arrrr matey!", pirate.catchphrase
682
- assert_not pirate.changed?
683
- end
684
-
685
- test "in place mutation for binary" do
686
- klass = Class.new(ActiveRecord::Base) do
687
- self.table_name = :binaries
688
- serialize :data
689
- end
690
-
691
- binary = klass.create!(data: "\\\\foo")
692
-
693
- assert_not binary.changed?
694
-
695
- binary.data = binary.data.dup
696
-
697
- assert_not binary.changed?
698
-
699
- binary = klass.last
700
-
701
- assert_not binary.changed?
702
-
703
- binary.data << "bar"
704
-
705
- assert binary.changed?
706
- end
707
-
708
- test "attribute_changed? doesn't compute in-place changes for unrelated attributes" do
709
- test_type_class = Class.new(ActiveRecord::Type::Value) do
710
- define_method(:changed_in_place?) do |*|
711
- raise
712
- end
713
- end
714
- klass = Class.new(ActiveRecord::Base) do
715
- self.table_name = 'people'
716
- attribute :foo, test_type_class.new
717
- end
718
-
719
- model = klass.new(first_name: "Jim")
720
- assert model.first_name_changed?
721
- end
722
-
723
- test "attribute_will_change! doesn't try to save non-persistable attributes" do
724
- klass = Class.new(ActiveRecord::Base) do
725
- self.table_name = 'people'
726
- attribute :non_persisted_attribute, ActiveRecord::Type::String.new
727
- end
728
-
729
- record = klass.new(first_name: "Sean")
730
- record.non_persisted_attribute_will_change!
731
-
732
- assert record.non_persisted_attribute_changed?
733
- assert record.save
734
- end
735
-
736
- test "mutating and then assigning doesn't remove the change" do
737
- pirate = Pirate.create!(catchphrase: "arrrr")
738
- pirate.catchphrase << " matey!"
739
- pirate.catchphrase = "arrrr matey!"
740
-
741
- assert pirate.catchphrase_changed?(from: "arrrr", to: "arrrr matey!")
742
- end
743
-
744
- test "getters with side effects are allowed" do
745
- klass = Class.new(Pirate) do
746
- def catchphrase
747
- if super.blank?
748
- update_attribute(:catchphrase, "arr") # what could possibly go wrong?
749
- end
750
- super
751
- end
752
- end
753
-
754
- pirate = klass.create!(catchphrase: "lol")
755
- pirate.update_attribute(:catchphrase, nil)
756
-
757
- assert_equal "arr", pirate.catchphrase
758
- end
759
-
760
- test "cloning and modifying an object in-place only registers changes on the new object" do
761
- pirate = Pirate.create!(catchphrase: "arrrr")
762
- assert_equal({}, pirate.changed_attributes)
763
- pirate_clone = pirate.dup
764
- assert_equal({"catchphrase" => nil}, pirate_clone.changed_attributes)
765
- pirate_clone.catchphrase = "arrrr matey!"
766
- assert_equal({}, pirate.changed_attributes)
767
- assert_equal({"catchphrase" => nil}, pirate_clone.changed_attributes)
768
- end
769
-
770
- private
771
- def with_partial_writes(klass, on = true)
772
- old = klass.partial_writes?
773
- klass.partial_writes = on
774
- yield
775
- ensure
776
- klass.partial_writes = old
777
- end
778
-
779
- def check_pirate_after_save_failure(pirate)
780
- assert pirate.changed?
781
- assert pirate.parrot_id_changed?
782
- assert_equal %w(parrot_id), pirate.changed
783
- assert_nil pirate.parrot_id_was
784
- end
785
- end
1
+ require 'cases/helper'
2
+ require 'models/topic' # For booleans
3
+ require 'models/pirate' # For timestamps
4
+ require 'models/parrot'
5
+ require 'models/person' # For optimistic locking
6
+ require 'models/aircraft'
7
+
8
+ class Pirate # Just reopening it, not defining it
9
+ attr_accessor :detected_changes_in_after_update # Boolean for if changes are detected
10
+ attr_accessor :changes_detected_in_after_update # Actual changes
11
+
12
+ after_update :check_changes
13
+
14
+ private
15
+ # after_save/update and the model itself
16
+ # can end up checking dirty status and acting on the results
17
+ def check_changes
18
+ if self.changed?
19
+ self.detected_changes_in_after_update = true
20
+ self.changes_detected_in_after_update = self.changes
21
+ end
22
+ end
23
+ end
24
+
25
+ class NumericData < ActiveRecord::Base
26
+ self.table_name = 'numeric_data'
27
+ end
28
+
29
+ class DirtyTest < ActiveRecord::TestCase
30
+ include InTimeZone
31
+
32
+ # Dummy to force column loads so query counts are clean.
33
+ def setup
34
+ Person.create :first_name => 'foo'
35
+ end
36
+
37
+ def test_attribute_changes
38
+ # New record - no changes.
39
+ pirate = Pirate.new
40
+ assert_equal false, pirate.catchphrase_changed?
41
+ assert_equal false, pirate.non_validated_parrot_id_changed?
42
+
43
+ # Change catchphrase.
44
+ pirate.catchphrase = 'arrr'
45
+ assert pirate.catchphrase_changed?
46
+ assert_nil pirate.catchphrase_was
47
+ assert_equal [nil, 'arrr'], pirate.catchphrase_change
48
+
49
+ # Saved - no changes.
50
+ pirate.save!
51
+ assert !pirate.catchphrase_changed?
52
+ assert_nil pirate.catchphrase_change
53
+
54
+ # Same value - no changes.
55
+ pirate.catchphrase = 'arrr'
56
+ assert !pirate.catchphrase_changed?
57
+ assert_nil pirate.catchphrase_change
58
+ end
59
+
60
+ def test_time_attributes_changes_with_time_zone
61
+ in_time_zone 'Paris' do
62
+ target = Class.new(ActiveRecord::Base)
63
+ target.table_name = 'pirates'
64
+
65
+ # New record - no changes.
66
+ pirate = target.new
67
+ assert !pirate.created_on_changed?
68
+ assert_nil pirate.created_on_change
69
+
70
+ # Saved - no changes.
71
+ pirate.catchphrase = 'arrrr, time zone!!'
72
+ pirate.save!
73
+ assert !pirate.created_on_changed?
74
+ assert_nil pirate.created_on_change
75
+
76
+ # Change created_on.
77
+ old_created_on = pirate.created_on
78
+ pirate.created_on = Time.now - 1.day
79
+ assert pirate.created_on_changed?
80
+ assert_kind_of ActiveSupport::TimeWithZone, pirate.created_on_was
81
+ assert_equal old_created_on, pirate.created_on_was
82
+ pirate.created_on = old_created_on
83
+ assert !pirate.created_on_changed?
84
+ end
85
+ end
86
+
87
+ def test_setting_time_attributes_with_time_zone_field_to_itself_should_not_be_marked_as_a_change
88
+ in_time_zone 'Paris' do
89
+ target = Class.new(ActiveRecord::Base)
90
+ target.table_name = 'pirates'
91
+
92
+ pirate = target.create!
93
+ pirate.created_on = pirate.created_on
94
+ assert !pirate.created_on_changed?
95
+ end
96
+ end
97
+
98
+ def test_time_attributes_changes_without_time_zone_by_skip
99
+ in_time_zone 'Paris' do
100
+ target = Class.new(ActiveRecord::Base)
101
+ target.table_name = 'pirates'
102
+
103
+ target.skip_time_zone_conversion_for_attributes = [:created_on]
104
+
105
+ # New record - no changes.
106
+ pirate = target.new
107
+ assert !pirate.created_on_changed?
108
+ assert_nil pirate.created_on_change
109
+
110
+ # Saved - no changes.
111
+ pirate.catchphrase = 'arrrr, time zone!!'
112
+ pirate.save!
113
+ assert !pirate.created_on_changed?
114
+ assert_nil pirate.created_on_change
115
+
116
+ # Change created_on.
117
+ old_created_on = pirate.created_on
118
+ pirate.created_on = Time.now + 1.day
119
+ assert pirate.created_on_changed?
120
+ # kind_of does not work because
121
+ # ActiveSupport::TimeWithZone.name == 'Time'
122
+ assert_instance_of Time, pirate.created_on_was
123
+ assert_equal old_created_on, pirate.created_on_was
124
+ end
125
+ end
126
+
127
+ def test_time_attributes_changes_without_time_zone
128
+ with_timezone_config aware_attributes: false do
129
+ target = Class.new(ActiveRecord::Base)
130
+ target.table_name = 'pirates'
131
+
132
+ # New record - no changes.
133
+ pirate = target.new
134
+ assert !pirate.created_on_changed?
135
+ assert_nil pirate.created_on_change
136
+
137
+ # Saved - no changes.
138
+ pirate.catchphrase = 'arrrr, time zone!!'
139
+ pirate.save!
140
+ assert !pirate.created_on_changed?
141
+ assert_nil pirate.created_on_change
142
+
143
+ # Change created_on.
144
+ old_created_on = pirate.created_on
145
+ pirate.created_on = Time.now + 1.day
146
+ assert pirate.created_on_changed?
147
+ # kind_of does not work because
148
+ # ActiveSupport::TimeWithZone.name == 'Time'
149
+ assert_instance_of Time, pirate.created_on_was
150
+ assert_equal old_created_on, pirate.created_on_was
151
+ end
152
+ end
153
+
154
+
155
+ def test_aliased_attribute_changes
156
+ # the actual attribute here is name, title is an
157
+ # alias setup via alias_attribute
158
+ parrot = Parrot.new
159
+ assert !parrot.title_changed?
160
+ assert_nil parrot.title_change
161
+
162
+ parrot.name = 'Sam'
163
+ assert parrot.title_changed?
164
+ assert_nil parrot.title_was
165
+ assert_equal parrot.name_change, parrot.title_change
166
+ end
167
+
168
+ def test_restore_attribute!
169
+ pirate = Pirate.create!(:catchphrase => 'Yar!')
170
+ pirate.catchphrase = 'Ahoy!'
171
+
172
+ pirate.restore_catchphrase!
173
+ assert_equal "Yar!", pirate.catchphrase
174
+ assert_equal Hash.new, pirate.changes
175
+ assert !pirate.catchphrase_changed?
176
+ end
177
+
178
+ def test_nullable_number_not_marked_as_changed_if_new_value_is_blank
179
+ pirate = Pirate.new
180
+
181
+ ["", nil].each do |value|
182
+ pirate.parrot_id = value
183
+ assert !pirate.parrot_id_changed?
184
+ assert_nil pirate.parrot_id_change
185
+ end
186
+ end
187
+
188
+ def test_nullable_decimal_not_marked_as_changed_if_new_value_is_blank
189
+ numeric_data = NumericData.new
190
+
191
+ ["", nil].each do |value|
192
+ numeric_data.bank_balance = value
193
+ assert !numeric_data.bank_balance_changed?
194
+ assert_nil numeric_data.bank_balance_change
195
+ end
196
+ end
197
+
198
+ def test_nullable_float_not_marked_as_changed_if_new_value_is_blank
199
+ numeric_data = NumericData.new
200
+
201
+ ["", nil].each do |value|
202
+ numeric_data.temperature = value
203
+ assert !numeric_data.temperature_changed?
204
+ assert_nil numeric_data.temperature_change
205
+ end
206
+ end
207
+
208
+ def test_nullable_datetime_not_marked_as_changed_if_new_value_is_blank
209
+ in_time_zone 'Edinburgh' do
210
+ target = Class.new(ActiveRecord::Base)
211
+ target.table_name = 'topics'
212
+
213
+ topic = target.create
214
+ assert_nil topic.written_on
215
+
216
+ ["", nil].each do |value|
217
+ topic.written_on = value
218
+ assert_nil topic.written_on
219
+ assert !topic.written_on_changed?
220
+ end
221
+ end
222
+ end
223
+
224
+ def test_integer_zero_to_string_zero_not_marked_as_changed
225
+ pirate = Pirate.new
226
+ pirate.parrot_id = 0
227
+ pirate.catchphrase = 'arrr'
228
+ assert pirate.save!
229
+
230
+ assert !pirate.changed?
231
+
232
+ pirate.parrot_id = '0'
233
+ assert !pirate.changed?
234
+ end
235
+
236
+ def test_integer_zero_to_integer_zero_not_marked_as_changed
237
+ pirate = Pirate.new
238
+ pirate.parrot_id = 0
239
+ pirate.catchphrase = 'arrr'
240
+ assert pirate.save!
241
+
242
+ assert !pirate.changed?
243
+
244
+ pirate.parrot_id = 0
245
+ assert !pirate.changed?
246
+ end
247
+
248
+ def test_float_zero_to_string_zero_not_marked_as_changed
249
+ data = NumericData.new :temperature => 0.0
250
+ data.save!
251
+
252
+ assert_not data.changed?
253
+
254
+ data.temperature = '0'
255
+ assert_empty data.changes
256
+
257
+ data.temperature = '0.0'
258
+ assert_empty data.changes
259
+
260
+ data.temperature = '0.00'
261
+ assert_empty data.changes
262
+ end
263
+
264
+ def test_zero_to_blank_marked_as_changed
265
+ pirate = Pirate.new
266
+ pirate.catchphrase = "Yarrrr, me hearties"
267
+ pirate.parrot_id = 1
268
+ pirate.save
269
+
270
+ # check the change from 1 to ''
271
+ pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
272
+ pirate.parrot_id = ''
273
+ assert pirate.parrot_id_changed?
274
+ assert_equal([1, nil], pirate.parrot_id_change)
275
+ pirate.save
276
+
277
+ # check the change from nil to 0
278
+ pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
279
+ pirate.parrot_id = 0
280
+ assert pirate.parrot_id_changed?
281
+ assert_equal([nil, 0], pirate.parrot_id_change)
282
+ pirate.save
283
+
284
+ # check the change from 0 to ''
285
+ pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
286
+ pirate.parrot_id = ''
287
+ assert pirate.parrot_id_changed?
288
+ assert_equal([0, nil], pirate.parrot_id_change)
289
+ end
290
+
291
+ def test_object_should_be_changed_if_any_attribute_is_changed
292
+ pirate = Pirate.new
293
+ assert !pirate.changed?
294
+ assert_equal [], pirate.changed
295
+ assert_equal Hash.new, pirate.changes
296
+
297
+ pirate.catchphrase = 'arrr'
298
+ assert pirate.changed?
299
+ assert_nil pirate.catchphrase_was
300
+ assert_equal %w(catchphrase), pirate.changed
301
+ assert_equal({'catchphrase' => [nil, 'arrr']}, pirate.changes)
302
+
303
+ pirate.save
304
+ assert !pirate.changed?
305
+ assert_equal [], pirate.changed
306
+ assert_equal Hash.new, pirate.changes
307
+ end
308
+
309
+ def test_attribute_will_change!
310
+ pirate = Pirate.create!(:catchphrase => 'arr')
311
+
312
+ assert !pirate.catchphrase_changed?
313
+ assert pirate.catchphrase_will_change!
314
+ assert pirate.catchphrase_changed?
315
+ assert_equal ['arr', 'arr'], pirate.catchphrase_change
316
+
317
+ pirate.catchphrase << ' matey!'
318
+ assert pirate.catchphrase_changed?
319
+ assert_equal ['arr', 'arr matey!'], pirate.catchphrase_change
320
+ end
321
+
322
+ def test_association_assignment_changes_foreign_key
323
+ pirate = Pirate.create!(:catchphrase => 'jarl')
324
+ pirate.parrot = Parrot.create!(:name => 'Lorre')
325
+ assert pirate.changed?
326
+ assert_equal %w(parrot_id), pirate.changed
327
+ end
328
+
329
+ def test_attribute_should_be_compared_with_type_cast
330
+ topic = Topic.new
331
+ assert topic.approved?
332
+ assert !topic.approved_changed?
333
+
334
+ # Coming from web form.
335
+ params = {:topic => {:approved => 1}}
336
+ # In the controller.
337
+ topic.attributes = params[:topic]
338
+ assert topic.approved?
339
+ assert !topic.approved_changed?
340
+ end
341
+
342
+ def test_partial_update
343
+ pirate = Pirate.new(:catchphrase => 'foo')
344
+ old_updated_on = 1.hour.ago.beginning_of_day
345
+
346
+ with_partial_writes Pirate, false do
347
+ assert_queries(2) { 2.times { pirate.save! } }
348
+ Pirate.where(id: pirate.id).update_all(:updated_on => old_updated_on)
349
+ end
350
+
351
+ with_partial_writes Pirate, true do
352
+ assert_queries(0) { 2.times { pirate.save! } }
353
+ assert_equal old_updated_on, pirate.reload.updated_on
354
+
355
+ assert_queries(1) { pirate.catchphrase = 'bar'; pirate.save! }
356
+ assert_not_equal old_updated_on, pirate.reload.updated_on
357
+ end
358
+ end
359
+
360
+ def test_partial_update_with_optimistic_locking
361
+ person = Person.new(:first_name => 'foo')
362
+ old_lock_version = 1
363
+
364
+ with_partial_writes Person, false do
365
+ assert_queries(2) { 2.times { person.save! } }
366
+ Person.where(id: person.id).update_all(:first_name => 'baz')
367
+ end
368
+
369
+ with_partial_writes Person, true do
370
+ assert_queries(0) { 2.times { person.save! } }
371
+ assert_equal old_lock_version, person.reload.lock_version
372
+
373
+ assert_queries(1) { person.first_name = 'bar'; person.save! }
374
+ assert_not_equal old_lock_version, person.reload.lock_version
375
+ end
376
+ end
377
+
378
+ def test_changed_attributes_should_be_preserved_if_save_failure
379
+ pirate = Pirate.new
380
+ pirate.parrot_id = 1
381
+ assert !pirate.save
382
+ check_pirate_after_save_failure(pirate)
383
+
384
+ pirate = Pirate.new
385
+ pirate.parrot_id = 1
386
+ assert_raise(ActiveRecord::RecordInvalid) { pirate.save! }
387
+ check_pirate_after_save_failure(pirate)
388
+ end
389
+
390
+ def test_reload_should_clear_changed_attributes
391
+ pirate = Pirate.create!(:catchphrase => "shiver me timbers")
392
+ pirate.catchphrase = "*hic*"
393
+ assert pirate.changed?
394
+ pirate.reload
395
+ assert !pirate.changed?
396
+ end
397
+
398
+ def test_dup_objects_should_not_copy_dirty_flag_from_creator
399
+ pirate = Pirate.create!(:catchphrase => "shiver me timbers")
400
+ pirate_dup = pirate.dup
401
+ pirate_dup.restore_catchphrase!
402
+ pirate.catchphrase = "I love Rum"
403
+ assert pirate.catchphrase_changed?
404
+ assert !pirate_dup.catchphrase_changed?
405
+ end
406
+
407
+ def test_reverted_changes_are_not_dirty
408
+ phrase = "shiver me timbers"
409
+ pirate = Pirate.create!(:catchphrase => phrase)
410
+ pirate.catchphrase = "*hic*"
411
+ assert pirate.changed?
412
+ pirate.catchphrase = phrase
413
+ assert !pirate.changed?
414
+ end
415
+
416
+ def test_reverted_changes_are_not_dirty_after_multiple_changes
417
+ phrase = "shiver me timbers"
418
+ pirate = Pirate.create!(:catchphrase => phrase)
419
+ 10.times do |i|
420
+ pirate.catchphrase = "*hic*" * i
421
+ assert pirate.changed?
422
+ end
423
+ assert pirate.changed?
424
+ pirate.catchphrase = phrase
425
+ assert !pirate.changed?
426
+ end
427
+
428
+
429
+ def test_reverted_changes_are_not_dirty_going_from_nil_to_value_and_back
430
+ pirate = Pirate.create!(:catchphrase => "Yar!")
431
+
432
+ pirate.parrot_id = 1
433
+ assert pirate.changed?
434
+ assert pirate.parrot_id_changed?
435
+ assert !pirate.catchphrase_changed?
436
+
437
+ pirate.parrot_id = nil
438
+ assert !pirate.changed?
439
+ assert !pirate.parrot_id_changed?
440
+ assert !pirate.catchphrase_changed?
441
+ end
442
+
443
+ def test_save_should_store_serialized_attributes_even_with_partial_writes
444
+ with_partial_writes(Topic) do
445
+ topic = Topic.create!(:content => {:a => "a"})
446
+
447
+ assert_not topic.changed?
448
+
449
+ topic.content[:b] = "b"
450
+
451
+ assert topic.changed?
452
+
453
+ topic.save!
454
+
455
+ assert_not topic.changed?
456
+ assert_equal "b", topic.content[:b]
457
+
458
+ topic.reload
459
+
460
+ assert_equal "b", topic.content[:b]
461
+ end
462
+ end
463
+
464
+ def test_save_always_should_update_timestamps_when_serialized_attributes_are_present
465
+ with_partial_writes(Topic) do
466
+ topic = Topic.create!(:content => {:a => "a"})
467
+ topic.save!
468
+
469
+ updated_at = topic.updated_at
470
+ travel(1.second) do
471
+ topic.content[:hello] = 'world'
472
+ topic.save!
473
+ end
474
+
475
+ assert_not_equal updated_at, topic.updated_at
476
+ assert_equal 'world', topic.content[:hello]
477
+ end
478
+ end
479
+
480
+ def test_save_should_not_save_serialized_attribute_with_partial_writes_if_not_present
481
+ with_partial_writes(Topic) do
482
+ Topic.create!(:author_name => 'Bill', :content => {:a => "a"})
483
+ topic = Topic.select('id, author_name').first
484
+ topic.update_columns author_name: 'John'
485
+ topic = Topic.first
486
+ assert_not_nil topic.content
487
+ end
488
+ end
489
+
490
+ def test_previous_changes
491
+ # original values should be in previous_changes
492
+ pirate = Pirate.new
493
+
494
+ assert_equal Hash.new, pirate.previous_changes
495
+ pirate.catchphrase = "arrr"
496
+ pirate.save!
497
+
498
+ assert_equal 4, pirate.previous_changes.size
499
+ assert_equal [nil, "arrr"], pirate.previous_changes['catchphrase']
500
+ assert_equal [nil, pirate.id], pirate.previous_changes['id']
501
+ assert_nil pirate.previous_changes['updated_on'][0]
502
+ assert_not_nil pirate.previous_changes['updated_on'][1]
503
+ assert_nil pirate.previous_changes['created_on'][0]
504
+ assert_not_nil pirate.previous_changes['created_on'][1]
505
+ assert !pirate.previous_changes.key?('parrot_id')
506
+
507
+ # original values should be in previous_changes
508
+ pirate = Pirate.new
509
+
510
+ assert_equal Hash.new, pirate.previous_changes
511
+ pirate.catchphrase = "arrr"
512
+ pirate.save
513
+
514
+ assert_equal 4, pirate.previous_changes.size
515
+ assert_equal [nil, "arrr"], pirate.previous_changes['catchphrase']
516
+ assert_equal [nil, pirate.id], pirate.previous_changes['id']
517
+ assert pirate.previous_changes.include?('updated_on')
518
+ assert pirate.previous_changes.include?('created_on')
519
+ assert !pirate.previous_changes.key?('parrot_id')
520
+
521
+ pirate.catchphrase = "Yar!!"
522
+ pirate.reload
523
+ assert_equal Hash.new, pirate.previous_changes
524
+
525
+ pirate = Pirate.find_by_catchphrase("arrr")
526
+
527
+ travel(1.second)
528
+
529
+ pirate.catchphrase = "Me Maties!"
530
+ pirate.save!
531
+
532
+ assert_equal 2, pirate.previous_changes.size
533
+ assert_equal ["arrr", "Me Maties!"], pirate.previous_changes['catchphrase']
534
+ assert_not_nil pirate.previous_changes['updated_on'][0]
535
+ assert_not_nil pirate.previous_changes['updated_on'][1]
536
+ assert !pirate.previous_changes.key?('parrot_id')
537
+ assert !pirate.previous_changes.key?('created_on')
538
+
539
+ pirate = Pirate.find_by_catchphrase("Me Maties!")
540
+
541
+ travel(1.second)
542
+
543
+ pirate.catchphrase = "Thar She Blows!"
544
+ pirate.save
545
+
546
+ assert_equal 2, pirate.previous_changes.size
547
+ assert_equal ["Me Maties!", "Thar She Blows!"], pirate.previous_changes['catchphrase']
548
+ assert_not_nil pirate.previous_changes['updated_on'][0]
549
+ assert_not_nil pirate.previous_changes['updated_on'][1]
550
+ assert !pirate.previous_changes.key?('parrot_id')
551
+ assert !pirate.previous_changes.key?('created_on')
552
+
553
+ travel(1.second)
554
+
555
+ pirate = Pirate.find_by_catchphrase("Thar She Blows!")
556
+ pirate.update(catchphrase: "Ahoy!")
557
+
558
+ assert_equal 2, pirate.previous_changes.size
559
+ assert_equal ["Thar She Blows!", "Ahoy!"], pirate.previous_changes['catchphrase']
560
+ assert_not_nil pirate.previous_changes['updated_on'][0]
561
+ assert_not_nil pirate.previous_changes['updated_on'][1]
562
+ assert !pirate.previous_changes.key?('parrot_id')
563
+ assert !pirate.previous_changes.key?('created_on')
564
+
565
+ travel(1.second)
566
+
567
+ pirate = Pirate.find_by_catchphrase("Ahoy!")
568
+ pirate.update_attribute(:catchphrase, "Ninjas suck!")
569
+
570
+ assert_equal 2, pirate.previous_changes.size
571
+ assert_equal ["Ahoy!", "Ninjas suck!"], pirate.previous_changes['catchphrase']
572
+ assert_not_nil pirate.previous_changes['updated_on'][0]
573
+ assert_not_nil pirate.previous_changes['updated_on'][1]
574
+ assert !pirate.previous_changes.key?('parrot_id')
575
+ assert !pirate.previous_changes.key?('created_on')
576
+ ensure
577
+ travel_back
578
+ end
579
+
580
+ if ActiveRecord::Base.connection.supports_migrations?
581
+ class Testings < ActiveRecord::Base; end
582
+ def test_field_named_field
583
+ ActiveRecord::Base.connection.create_table :testings do |t|
584
+ t.string :field
585
+ end
586
+ assert_nothing_raised do
587
+ Testings.new.attributes
588
+ end
589
+ ensure
590
+ ActiveRecord::Base.connection.drop_table :testings rescue nil
591
+ end
592
+ end
593
+
594
+ def test_datetime_attribute_can_be_updated_with_fractional_seconds
595
+ skip "Fractional seconds are not supported" unless subsecond_precision_supported?
596
+ in_time_zone 'Paris' do
597
+ target = Class.new(ActiveRecord::Base)
598
+ target.table_name = 'topics'
599
+
600
+ written_on = Time.utc(2012, 12, 1, 12, 0, 0).in_time_zone('Paris')
601
+
602
+ topic = target.create(:written_on => written_on)
603
+ topic.written_on += 0.3
604
+
605
+ assert topic.written_on_changed?, 'Fractional second update not detected'
606
+ end
607
+ end
608
+
609
+ def test_datetime_attribute_doesnt_change_if_zone_is_modified_in_string
610
+ time_in_paris = Time.utc(2014, 1, 1, 12, 0, 0).in_time_zone('Paris')
611
+ pirate = Pirate.create!(:catchphrase => 'rrrr', :created_on => time_in_paris)
612
+
613
+ pirate.created_on = pirate.created_on.in_time_zone('Tokyo').to_s
614
+ assert !pirate.created_on_changed?
615
+ end
616
+
617
+ test "partial insert" do
618
+ with_partial_writes Person do
619
+ jon = nil
620
+ assert_sql(/first_name/i) do
621
+ jon = Person.create! first_name: 'Jon'
622
+ end
623
+
624
+ assert ActiveRecord::SQLCounter.log_all.none? { |sql| sql =~ /followers_count/ }
625
+
626
+ jon.reload
627
+ assert_equal 'Jon', jon.first_name
628
+ assert_equal 0, jon.followers_count
629
+ assert_not_nil jon.id
630
+ end
631
+ end
632
+
633
+ test "partial insert with empty values" do
634
+ with_partial_writes Aircraft do
635
+ a = Aircraft.create!
636
+ a.reload
637
+ assert_not_nil a.id
638
+ end
639
+ end
640
+
641
+ test "in place mutation detection" do
642
+ pirate = Pirate.create!(catchphrase: "arrrr")
643
+ pirate.catchphrase << " matey!"
644
+
645
+ assert pirate.catchphrase_changed?
646
+ expected_changes = {
647
+ "catchphrase" => ["arrrr", "arrrr matey!"]
648
+ }
649
+ assert_equal(expected_changes, pirate.changes)
650
+ assert_equal("arrrr", pirate.catchphrase_was)
651
+ assert pirate.catchphrase_changed?(from: "arrrr")
652
+ assert_not pirate.catchphrase_changed?(from: "anything else")
653
+ assert pirate.changed_attributes.include?(:catchphrase)
654
+
655
+ pirate.save!
656
+ pirate.reload
657
+
658
+ assert_equal "arrrr matey!", pirate.catchphrase
659
+ assert_not pirate.changed?
660
+ end
661
+
662
+ test "in place mutation for binary" do
663
+ klass = Class.new(ActiveRecord::Base) do
664
+ self.table_name = :binaries
665
+ serialize :data
666
+ end
667
+
668
+ binary = klass.create!(data: "\\\\foo")
669
+
670
+ assert_not binary.changed?
671
+
672
+ binary.data = binary.data.dup
673
+
674
+ assert_not binary.changed?
675
+
676
+ binary = klass.last
677
+
678
+ assert_not binary.changed?
679
+
680
+ binary.data << "bar"
681
+
682
+ assert binary.changed?
683
+ end
684
+
685
+ test "attribute_changed? doesn't compute in-place changes for unrelated attributes" do
686
+ test_type_class = Class.new(ActiveRecord::Type::Value) do
687
+ define_method(:changed_in_place?) do |*|
688
+ raise
689
+ end
690
+ end
691
+ klass = Class.new(ActiveRecord::Base) do
692
+ self.table_name = 'people'
693
+ attribute :foo, test_type_class.new
694
+ end
695
+
696
+ model = klass.new(first_name: "Jim")
697
+ assert model.first_name_changed?
698
+ end
699
+
700
+ test "attribute_will_change! doesn't try to save non-persistable attributes" do
701
+ klass = Class.new(ActiveRecord::Base) do
702
+ self.table_name = 'people'
703
+ attribute :non_persisted_attribute, :string
704
+ end
705
+
706
+ record = klass.new(first_name: "Sean")
707
+ record.non_persisted_attribute_will_change!
708
+
709
+ assert record.non_persisted_attribute_changed?
710
+ assert record.save
711
+ end
712
+
713
+ test "mutating and then assigning doesn't remove the change" do
714
+ pirate = Pirate.create!(catchphrase: "arrrr")
715
+ pirate.catchphrase << " matey!"
716
+ pirate.catchphrase = "arrrr matey!"
717
+
718
+ assert pirate.catchphrase_changed?(from: "arrrr", to: "arrrr matey!")
719
+ end
720
+
721
+ test "getters with side effects are allowed" do
722
+ klass = Class.new(Pirate) do
723
+ def catchphrase
724
+ if super.blank?
725
+ update_attribute(:catchphrase, "arr") # what could possibly go wrong?
726
+ end
727
+ super
728
+ end
729
+ end
730
+
731
+ pirate = klass.create!(catchphrase: "lol")
732
+ pirate.update_attribute(:catchphrase, nil)
733
+
734
+ assert_equal "arr", pirate.catchphrase
735
+ end
736
+
737
+ test "attributes assigned but not selected are dirty" do
738
+ person = Person.select(:id).first
739
+ refute person.changed?
740
+
741
+ person.first_name = "Sean"
742
+ assert person.changed?
743
+
744
+ person.first_name = nil
745
+ assert person.changed?
746
+ end
747
+
748
+ private
749
+ def with_partial_writes(klass, on = true)
750
+ old = klass.partial_writes?
751
+ klass.partial_writes = on
752
+ yield
753
+ ensure
754
+ klass.partial_writes = old
755
+ end
756
+
757
+ def check_pirate_after_save_failure(pirate)
758
+ assert pirate.changed?
759
+ assert pirate.parrot_id_changed?
760
+ assert_equal %w(parrot_id), pirate.changed
761
+ assert_nil pirate.parrot_id_was
762
+ end
763
+ end