ibm_db 4.0.0-x86-mingw32 → 5.1.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 (576) hide show
  1. checksums.yaml +5 -5
  2. data/MANIFEST +14 -14
  3. data/README +208 -208
  4. data/ext/Makefile +269 -0
  5. data/ext/Makefile.nt32 +181 -181
  6. data/ext/Makefile.nt32.191 +212 -212
  7. data/ext/extconf.rb +322 -291
  8. data/ext/gil_release_version.h +3 -0
  9. data/ext/ibm_db-i386-mingw32.def +2 -0
  10. data/ext/ibm_db.c +11879 -11887
  11. data/ext/ibm_db.o +0 -0
  12. data/ext/ibm_db.so +0 -0
  13. data/ext/mkmf.log +110 -0
  14. data/ext/ruby_ibm_db.h +241 -241
  15. data/ext/ruby_ibm_db_cli.c +866 -866
  16. data/ext/ruby_ibm_db_cli.h +500 -500
  17. data/ext/ruby_ibm_db_cli.o +0 -0
  18. data/ext/unicode_support_version.h +3 -0
  19. data/init.rb +41 -41
  20. data/lib/IBM_DB.rb +27 -27
  21. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +3593 -3452
  22. data/lib/active_record/connection_adapters/ibmdb_adapter.rb +5 -5
  23. data/lib/active_record/vendor/db2-i5-zOS.yaml +328 -328
  24. data/lib/mswin32/ibm_db.rb +94 -90
  25. data/lib/mswin32/rb2x/i386/ruby26/ibm_db.so +0 -0
  26. data/lib/mswin32/rb2x/i386/ruby27/ibm_db.so +0 -0
  27. data/test/active_record/connection_adapters/fake_adapter.rb +49 -49
  28. data/test/assets/example.log +1 -1
  29. data/test/assets/test.txt +1 -1
  30. data/test/cases/adapter_test.rb +351 -351
  31. data/test/cases/adapters/mysql2/active_schema_test.rb +193 -193
  32. data/test/cases/adapters/mysql2/bind_parameter_test.rb +50 -50
  33. data/test/cases/adapters/mysql2/boolean_test.rb +100 -100
  34. data/test/cases/adapters/mysql2/case_sensitivity_test.rb +63 -63
  35. data/test/cases/adapters/mysql2/charset_collation_test.rb +54 -54
  36. data/test/cases/adapters/mysql2/connection_test.rb +210 -210
  37. data/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb +45 -45
  38. data/test/cases/adapters/mysql2/enum_test.rb +26 -26
  39. data/test/cases/adapters/mysql2/explain_test.rb +21 -21
  40. data/test/cases/adapters/mysql2/json_test.rb +195 -195
  41. data/test/cases/adapters/mysql2/mysql2_adapter_test.rb +83 -83
  42. data/test/cases/adapters/mysql2/reserved_word_test.rb +152 -152
  43. data/test/cases/adapters/mysql2/schema_migrations_test.rb +59 -59
  44. data/test/cases/adapters/mysql2/schema_test.rb +126 -126
  45. data/test/cases/adapters/mysql2/sp_test.rb +36 -36
  46. data/test/cases/adapters/mysql2/sql_types_test.rb +14 -14
  47. data/test/cases/adapters/mysql2/table_options_test.rb +42 -42
  48. data/test/cases/adapters/mysql2/unsigned_type_test.rb +66 -66
  49. data/test/cases/adapters/postgresql/active_schema_test.rb +98 -98
  50. data/test/cases/adapters/postgresql/array_test.rb +339 -339
  51. data/test/cases/adapters/postgresql/bit_string_test.rb +82 -82
  52. data/test/cases/adapters/postgresql/bytea_test.rb +134 -134
  53. data/test/cases/adapters/postgresql/case_insensitive_test.rb +26 -26
  54. data/test/cases/adapters/postgresql/change_schema_test.rb +38 -38
  55. data/test/cases/adapters/postgresql/cidr_test.rb +25 -25
  56. data/test/cases/adapters/postgresql/citext_test.rb +78 -78
  57. data/test/cases/adapters/postgresql/collation_test.rb +53 -53
  58. data/test/cases/adapters/postgresql/composite_test.rb +132 -132
  59. data/test/cases/adapters/postgresql/connection_test.rb +257 -257
  60. data/test/cases/adapters/postgresql/datatype_test.rb +92 -92
  61. data/test/cases/adapters/postgresql/domain_test.rb +47 -47
  62. data/test/cases/adapters/postgresql/enum_test.rb +91 -91
  63. data/test/cases/adapters/postgresql/explain_test.rb +20 -20
  64. data/test/cases/adapters/postgresql/extension_migration_test.rb +63 -63
  65. data/test/cases/adapters/postgresql/full_text_test.rb +44 -44
  66. data/test/cases/adapters/postgresql/geometric_test.rb +378 -378
  67. data/test/cases/adapters/postgresql/hstore_test.rb +382 -382
  68. data/test/cases/adapters/postgresql/infinity_test.rb +69 -69
  69. data/test/cases/adapters/postgresql/integer_test.rb +25 -25
  70. data/test/cases/adapters/postgresql/json_test.rb +237 -237
  71. data/test/cases/adapters/postgresql/ltree_test.rb +53 -53
  72. data/test/cases/adapters/postgresql/money_test.rb +96 -96
  73. data/test/cases/adapters/postgresql/network_test.rb +94 -94
  74. data/test/cases/adapters/postgresql/numbers_test.rb +49 -49
  75. data/test/cases/adapters/postgresql/postgresql_adapter_test.rb +405 -405
  76. data/test/cases/adapters/postgresql/prepared_statements_test.rb +22 -22
  77. data/test/cases/adapters/postgresql/quoting_test.rb +44 -44
  78. data/test/cases/adapters/postgresql/range_test.rb +343 -343
  79. data/test/cases/adapters/postgresql/referential_integrity_test.rb +111 -111
  80. data/test/cases/adapters/postgresql/rename_table_test.rb +34 -34
  81. data/test/cases/adapters/postgresql/schema_authorization_test.rb +119 -119
  82. data/test/cases/adapters/postgresql/schema_test.rb +597 -597
  83. data/test/cases/adapters/postgresql/serial_test.rb +154 -154
  84. data/test/cases/adapters/postgresql/statement_pool_test.rb +41 -41
  85. data/test/cases/adapters/postgresql/timestamp_test.rb +90 -90
  86. data/test/cases/adapters/postgresql/type_lookup_test.rb +33 -33
  87. data/test/cases/adapters/postgresql/utils_test.rb +62 -62
  88. data/test/cases/adapters/postgresql/uuid_test.rb +294 -294
  89. data/test/cases/adapters/postgresql/xml_test.rb +54 -54
  90. data/test/cases/adapters/sqlite3/collation_test.rb +53 -53
  91. data/test/cases/adapters/sqlite3/copy_table_test.rb +98 -98
  92. data/test/cases/adapters/sqlite3/explain_test.rb +21 -21
  93. data/test/cases/adapters/sqlite3/quoting_test.rb +101 -101
  94. data/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +441 -441
  95. data/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb +24 -24
  96. data/test/cases/adapters/sqlite3/statement_pool_test.rb +20 -20
  97. data/test/cases/aggregations_test.rb +168 -168
  98. data/test/cases/ar_schema_test.rb +146 -146
  99. data/test/cases/associations/association_scope_test.rb +16 -16
  100. data/test/cases/associations/belongs_to_associations_test.rb +1141 -1141
  101. data/test/cases/associations/bidirectional_destroy_dependencies_test.rb +41 -41
  102. data/test/cases/associations/callbacks_test.rb +190 -190
  103. data/test/cases/associations/cascaded_eager_loading_test.rb +188 -188
  104. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -36
  105. data/test/cases/associations/eager_load_nested_include_test.rb +126 -126
  106. data/test/cases/associations/eager_singularization_test.rb +148 -148
  107. data/test/cases/associations/eager_test.rb +1514 -1514
  108. data/test/cases/associations/extension_test.rb +87 -87
  109. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +1004 -1004
  110. data/test/cases/associations/has_many_associations_test.rb +2501 -2501
  111. data/test/cases/associations/has_many_through_associations_test.rb +1271 -1271
  112. data/test/cases/associations/has_one_associations_test.rb +707 -707
  113. data/test/cases/associations/has_one_through_associations_test.rb +383 -383
  114. data/test/cases/associations/inner_join_association_test.rb +139 -139
  115. data/test/cases/associations/inverse_associations_test.rb +733 -733
  116. data/test/cases/associations/join_model_test.rb +777 -777
  117. data/test/cases/associations/left_outer_join_association_test.rb +88 -88
  118. data/test/cases/associations/nested_through_associations_test.rb +579 -579
  119. data/test/cases/associations/required_test.rb +102 -102
  120. data/test/cases/associations_test.rb +385 -385
  121. data/test/cases/attribute_decorators_test.rb +126 -125
  122. data/test/cases/attribute_methods/read_test.rb +60 -60
  123. data/test/cases/attribute_methods_test.rb +1009 -1009
  124. data/test/cases/attribute_set_test.rb +270 -270
  125. data/test/cases/attribute_test.rb +246 -246
  126. data/test/cases/attributes_test.rb +253 -253
  127. data/test/cases/autosave_association_test.rb +1708 -1708
  128. data/test/cases/base_test.rb +1713 -1713
  129. data/test/cases/batches_test.rb +489 -489
  130. data/test/cases/binary_test.rb +44 -44
  131. data/test/cases/bind_parameter_test.rb +110 -110
  132. data/test/cases/cache_key_test.rb +26 -25
  133. data/test/cases/calculations_test.rb +798 -798
  134. data/test/cases/callbacks_test.rb +636 -636
  135. data/test/cases/clone_test.rb +40 -40
  136. data/test/cases/coders/json_test.rb +15 -15
  137. data/test/cases/coders/yaml_column_test.rb +63 -63
  138. data/test/cases/collection_cache_key_test.rb +115 -115
  139. data/test/cases/column_alias_test.rb +17 -17
  140. data/test/cases/column_definition_test.rb +92 -92
  141. data/test/cases/comment_test.rb +145 -143
  142. data/test/cases/connection_adapters/adapter_leasing_test.rb +56 -56
  143. data/test/cases/connection_adapters/connection_handler_test.rb +160 -160
  144. data/test/cases/connection_adapters/connection_specification_test.rb +12 -12
  145. data/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb +255 -255
  146. data/test/cases/connection_adapters/mysql_type_lookup_test.rb +69 -69
  147. data/test/cases/connection_adapters/quoting_test.rb +13 -13
  148. data/test/cases/connection_adapters/schema_cache_test.rb +61 -61
  149. data/test/cases/connection_adapters/type_lookup_test.rb +118 -118
  150. data/test/cases/connection_management_test.rb +112 -112
  151. data/test/cases/connection_pool_test.rb +521 -521
  152. data/test/cases/connection_specification/resolver_test.rb +131 -131
  153. data/test/cases/core_test.rb +112 -112
  154. data/test/cases/counter_cache_test.rb +214 -214
  155. data/test/cases/custom_locking_test.rb +17 -17
  156. data/test/cases/database_statements_test.rb +34 -34
  157. data/test/cases/date_test.rb +44 -44
  158. data/test/cases/date_time_precision_test.rb +107 -106
  159. data/test/cases/date_time_test.rb +61 -61
  160. data/test/cases/defaults_test.rb +219 -218
  161. data/test/cases/dirty_test.rb +763 -763
  162. data/test/cases/disconnected_test.rb +30 -30
  163. data/test/cases/dup_test.rb +157 -157
  164. data/test/cases/enum_test.rb +444 -444
  165. data/test/cases/errors_test.rb +16 -16
  166. data/test/cases/explain_subscriber_test.rb +64 -64
  167. data/test/cases/explain_test.rb +87 -87
  168. data/test/cases/finder_respond_to_test.rb +60 -60
  169. data/test/cases/finder_test.rb +1294 -1294
  170. data/test/cases/fixture_set/file_test.rb +156 -156
  171. data/test/cases/fixtures_test.rb +988 -988
  172. data/test/cases/forbidden_attributes_protection_test.rb +165 -165
  173. data/test/cases/habtm_destroy_order_test.rb +61 -61
  174. data/test/cases/helper.rb +204 -204
  175. data/test/cases/hot_compatibility_test.rb +142 -142
  176. data/test/cases/i18n_test.rb +45 -45
  177. data/test/cases/inheritance_test.rb +606 -606
  178. data/test/cases/integration_test.rb +155 -155
  179. data/test/cases/invalid_connection_test.rb +24 -24
  180. data/test/cases/invertible_migration_test.rb +387 -387
  181. data/test/cases/json_serialization_test.rb +311 -311
  182. data/test/cases/locking_test.rb +493 -493
  183. data/test/cases/log_subscriber_test.rb +225 -225
  184. data/test/cases/migration/change_schema_test.rb +458 -458
  185. data/test/cases/migration/change_table_test.rb +256 -256
  186. data/test/cases/migration/column_attributes_test.rb +176 -176
  187. data/test/cases/migration/column_positioning_test.rb +56 -56
  188. data/test/cases/migration/columns_test.rb +310 -310
  189. data/test/cases/migration/command_recorder_test.rb +350 -350
  190. data/test/cases/migration/compatibility_test.rb +118 -118
  191. data/test/cases/migration/create_join_table_test.rb +157 -157
  192. data/test/cases/migration/foreign_key_test.rb +362 -360
  193. data/test/cases/migration/helper.rb +39 -39
  194. data/test/cases/migration/index_test.rb +218 -218
  195. data/test/cases/migration/logger_test.rb +36 -36
  196. data/test/cases/migration/pending_migrations_test.rb +52 -52
  197. data/test/cases/migration/references_foreign_key_test.rb +221 -216
  198. data/test/cases/migration/references_index_test.rb +101 -101
  199. data/test/cases/migration/references_statements_test.rb +136 -136
  200. data/test/cases/migration/rename_table_test.rb +93 -93
  201. data/test/cases/migration_test.rb +1157 -1157
  202. data/test/cases/migrator_test.rb +471 -470
  203. data/test/cases/mixin_test.rb +68 -68
  204. data/test/cases/modules_test.rb +172 -172
  205. data/test/cases/multiparameter_attributes_test.rb +372 -372
  206. data/test/cases/multiple_db_test.rb +122 -122
  207. data/test/cases/nested_attributes_test.rb +1098 -1098
  208. data/test/cases/nested_attributes_with_callbacks_test.rb +144 -144
  209. data/test/cases/persistence_test.rb +1001 -1001
  210. data/test/cases/pooled_connections_test.rb +81 -81
  211. data/test/cases/primary_keys_test.rb +376 -376
  212. data/test/cases/query_cache_test.rb +446 -446
  213. data/test/cases/quoting_test.rb +202 -202
  214. data/test/cases/readonly_test.rb +119 -119
  215. data/test/cases/reaper_test.rb +85 -85
  216. data/test/cases/reflection_test.rb +509 -509
  217. data/test/cases/relation/delegation_test.rb +63 -63
  218. data/test/cases/relation/merging_test.rb +157 -157
  219. data/test/cases/relation/mutation_test.rb +183 -183
  220. data/test/cases/relation/or_test.rb +92 -92
  221. data/test/cases/relation/predicate_builder_test.rb +16 -16
  222. data/test/cases/relation/record_fetch_warning_test.rb +40 -40
  223. data/test/cases/relation/where_chain_test.rb +105 -105
  224. data/test/cases/relation/where_clause_test.rb +182 -182
  225. data/test/cases/relation/where_test.rb +322 -322
  226. data/test/cases/relation_test.rb +328 -328
  227. data/test/cases/relations_test.rb +2026 -2026
  228. data/test/cases/reload_models_test.rb +22 -22
  229. data/test/cases/result_test.rb +90 -90
  230. data/test/cases/sanitize_test.rb +176 -176
  231. data/test/cases/schema_dumper_test.rb +457 -457
  232. data/test/cases/schema_loading_test.rb +52 -52
  233. data/test/cases/scoping/default_scoping_test.rb +528 -528
  234. data/test/cases/scoping/named_scoping_test.rb +561 -561
  235. data/test/cases/scoping/relation_scoping_test.rb +400 -400
  236. data/test/cases/secure_token_test.rb +32 -32
  237. data/test/cases/serialization_test.rb +104 -104
  238. data/test/cases/serialized_attribute_test.rb +364 -364
  239. data/test/cases/statement_cache_test.rb +136 -136
  240. data/test/cases/store_test.rb +195 -195
  241. data/test/cases/suppressor_test.rb +63 -63
  242. data/test/cases/tasks/database_tasks_test.rb +462 -462
  243. data/test/cases/tasks/mysql_rake_test.rb +345 -345
  244. data/test/cases/tasks/postgresql_rake_test.rb +304 -304
  245. data/test/cases/tasks/sqlite_rake_test.rb +220 -220
  246. data/test/cases/test_case.rb +131 -131
  247. data/test/cases/test_fixtures_test.rb +36 -36
  248. data/test/cases/time_precision_test.rb +103 -102
  249. data/test/cases/timestamp_test.rb +501 -501
  250. data/test/cases/touch_later_test.rb +121 -121
  251. data/test/cases/transaction_callbacks_test.rb +518 -518
  252. data/test/cases/transaction_isolation_test.rb +106 -106
  253. data/test/cases/transactions_test.rb +835 -834
  254. data/test/cases/type/adapter_specific_registry_test.rb +133 -133
  255. data/test/cases/type/date_time_test.rb +14 -14
  256. data/test/cases/type/integer_test.rb +27 -27
  257. data/test/cases/type/string_test.rb +22 -22
  258. data/test/cases/type/type_map_test.rb +177 -177
  259. data/test/cases/type_test.rb +39 -39
  260. data/test/cases/types_test.rb +24 -24
  261. data/test/cases/unconnected_test.rb +33 -33
  262. data/test/cases/validations/absence_validation_test.rb +73 -73
  263. data/test/cases/validations/association_validation_test.rb +97 -97
  264. data/test/cases/validations/i18n_generate_message_validation_test.rb +84 -84
  265. data/test/cases/validations/i18n_validation_test.rb +86 -86
  266. data/test/cases/validations/length_validation_test.rb +79 -79
  267. data/test/cases/validations/presence_validation_test.rb +103 -103
  268. data/test/cases/validations/uniqueness_validation_test.rb +548 -548
  269. data/test/cases/validations_repair_helper.rb +19 -19
  270. data/test/cases/validations_test.rb +194 -194
  271. data/test/cases/view_test.rb +216 -216
  272. data/test/cases/yaml_serialization_test.rb +121 -121
  273. data/test/config.example.yml +97 -97
  274. data/test/config.rb +5 -5
  275. data/test/connections/native_ibm_db/connection.rb +44 -0
  276. data/test/fixtures/accounts.yml +29 -29
  277. data/test/fixtures/admin/accounts.yml +2 -2
  278. data/test/fixtures/admin/users.yml +10 -10
  279. data/test/fixtures/author_addresses.yml +17 -17
  280. data/test/fixtures/author_favorites.yml +3 -3
  281. data/test/fixtures/authors.yml +23 -23
  282. data/test/fixtures/bad_posts.yml +9 -9
  283. data/test/fixtures/binaries.yml +133 -133
  284. data/test/fixtures/books.yml +31 -31
  285. data/test/fixtures/bulbs.yml +5 -5
  286. data/test/fixtures/cars.yml +9 -9
  287. data/test/fixtures/categories.yml +19 -19
  288. data/test/fixtures/categories/special_categories.yml +9 -9
  289. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -4
  290. data/test/fixtures/categories_ordered.yml +7 -7
  291. data/test/fixtures/categories_posts.yml +31 -31
  292. data/test/fixtures/categorizations.yml +23 -23
  293. data/test/fixtures/clubs.yml +8 -8
  294. data/test/fixtures/collections.yml +3 -3
  295. data/test/fixtures/colleges.yml +3 -3
  296. data/test/fixtures/comments.yml +65 -65
  297. data/test/fixtures/companies.yml +67 -67
  298. data/test/fixtures/computers.yml +10 -10
  299. data/test/fixtures/content.yml +3 -3
  300. data/test/fixtures/content_positions.yml +3 -3
  301. data/test/fixtures/courses.yml +8 -8
  302. data/test/fixtures/customers.yml +25 -25
  303. data/test/fixtures/dashboards.yml +6 -6
  304. data/test/fixtures/dead_parrots.yml +5 -5
  305. data/test/fixtures/developers.yml +22 -22
  306. data/test/fixtures/developers_projects.yml +16 -16
  307. data/test/fixtures/dog_lovers.yml +7 -7
  308. data/test/fixtures/dogs.yml +4 -4
  309. data/test/fixtures/doubloons.yml +3 -3
  310. data/test/fixtures/edges.yml +5 -5
  311. data/test/fixtures/entrants.yml +14 -14
  312. data/test/fixtures/essays.yml +6 -6
  313. data/test/fixtures/faces.yml +11 -11
  314. data/test/fixtures/fk_test_has_fk.yml +3 -3
  315. data/test/fixtures/fk_test_has_pk.yml +1 -1
  316. data/test/fixtures/friendships.yml +4 -4
  317. data/test/fixtures/funny_jokes.yml +10 -10
  318. data/test/fixtures/interests.yml +33 -33
  319. data/test/fixtures/items.yml +3 -3
  320. data/test/fixtures/jobs.yml +7 -7
  321. data/test/fixtures/legacy_things.yml +3 -3
  322. data/test/fixtures/live_parrots.yml +4 -4
  323. data/test/fixtures/mateys.yml +4 -4
  324. data/test/fixtures/member_details.yml +8 -8
  325. data/test/fixtures/member_types.yml +6 -6
  326. data/test/fixtures/members.yml +11 -11
  327. data/test/fixtures/memberships.yml +34 -34
  328. data/test/fixtures/men.yml +5 -5
  329. data/test/fixtures/minimalistics.yml +2 -2
  330. data/test/fixtures/minivans.yml +5 -5
  331. data/test/fixtures/mixed_case_monkeys.yml +6 -6
  332. data/test/fixtures/mixins.yml +29 -29
  333. data/test/fixtures/movies.yml +7 -7
  334. data/test/fixtures/naked/yml/accounts.yml +1 -1
  335. data/test/fixtures/naked/yml/companies.yml +1 -1
  336. data/test/fixtures/naked/yml/courses.yml +1 -1
  337. data/test/fixtures/naked/yml/parrots.yml +2 -2
  338. data/test/fixtures/naked/yml/trees.yml +3 -3
  339. data/test/fixtures/nodes.yml +29 -29
  340. data/test/fixtures/organizations.yml +5 -5
  341. data/test/fixtures/other_comments.yml +6 -6
  342. data/test/fixtures/other_dogs.yml +2 -2
  343. data/test/fixtures/other_posts.yml +7 -7
  344. data/test/fixtures/other_topics.yml +42 -42
  345. data/test/fixtures/owners.yml +9 -9
  346. data/test/fixtures/parrots.yml +27 -27
  347. data/test/fixtures/parrots_pirates.yml +7 -7
  348. data/test/fixtures/people.yml +24 -24
  349. data/test/fixtures/peoples_treasures.yml +3 -3
  350. data/test/fixtures/pets.yml +19 -19
  351. data/test/fixtures/pirates.yml +12 -15
  352. data/test/fixtures/posts.yml +80 -80
  353. data/test/fixtures/price_estimates.yml +16 -16
  354. data/test/fixtures/products.yml +4 -4
  355. data/test/fixtures/projects.yml +7 -7
  356. data/test/fixtures/ratings.yml +14 -14
  357. data/test/fixtures/readers.yml +11 -11
  358. data/test/fixtures/references.yml +17 -17
  359. data/test/fixtures/reserved_words/distinct.yml +5 -5
  360. data/test/fixtures/reserved_words/distinct_select.yml +11 -11
  361. data/test/fixtures/reserved_words/group.yml +14 -14
  362. data/test/fixtures/reserved_words/select.yml +8 -8
  363. data/test/fixtures/reserved_words/values.yml +7 -7
  364. data/test/fixtures/ships.yml +6 -6
  365. data/test/fixtures/speedometers.yml +8 -8
  366. data/test/fixtures/sponsors.yml +12 -12
  367. data/test/fixtures/string_key_objects.yml +7 -7
  368. data/test/fixtures/subscribers.yml +10 -10
  369. data/test/fixtures/subscriptions.yml +12 -12
  370. data/test/fixtures/taggings.yml +78 -78
  371. data/test/fixtures/tags.yml +11 -11
  372. data/test/fixtures/tasks.yml +7 -7
  373. data/test/fixtures/teapots.yml +3 -3
  374. data/test/fixtures/to_be_linked/accounts.yml +2 -2
  375. data/test/fixtures/to_be_linked/users.yml +10 -10
  376. data/test/fixtures/topics.yml +49 -49
  377. data/test/fixtures/toys.yml +14 -14
  378. data/test/fixtures/traffic_lights.yml +9 -9
  379. data/test/fixtures/treasures.yml +10 -10
  380. data/test/fixtures/trees.yml +3 -3
  381. data/test/fixtures/uuid_children.yml +3 -3
  382. data/test/fixtures/uuid_parents.yml +2 -2
  383. data/test/fixtures/variants.yml +4 -4
  384. data/test/fixtures/vegetables.yml +19 -19
  385. data/test/fixtures/vertices.yml +3 -3
  386. data/test/fixtures/warehouse_things.yml +2 -2
  387. data/test/fixtures/zines.yml +5 -5
  388. data/test/migrations/10_urban/9_add_expressions.rb +11 -11
  389. data/test/migrations/decimal/1_give_me_big_numbers.rb +15 -15
  390. data/test/migrations/magic/1_currencies_have_symbols.rb +12 -12
  391. data/test/migrations/missing/1000_people_have_middle_names.rb +9 -9
  392. data/test/migrations/missing/1_people_have_last_names.rb +9 -9
  393. data/test/migrations/missing/3_we_need_reminders.rb +12 -12
  394. data/test/migrations/missing/4_innocent_jointable.rb +12 -12
  395. data/test/migrations/rename/1_we_need_things.rb +11 -11
  396. data/test/migrations/rename/2_rename_things.rb +9 -9
  397. data/test/migrations/to_copy/1_people_have_hobbies.rb +9 -9
  398. data/test/migrations/to_copy/2_people_have_descriptions.rb +9 -9
  399. data/test/migrations/to_copy2/1_create_articles.rb +7 -7
  400. data/test/migrations/to_copy2/2_create_comments.rb +7 -7
  401. data/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb +9 -9
  402. data/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb +9 -9
  403. data/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb +9 -9
  404. data/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb +7 -7
  405. data/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb +7 -7
  406. data/test/migrations/valid/1_valid_people_have_last_names.rb +9 -9
  407. data/test/migrations/valid/2_we_need_reminders.rb +12 -12
  408. data/test/migrations/valid/3_innocent_jointable.rb +12 -12
  409. data/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb +9 -9
  410. data/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb +12 -12
  411. data/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb +12 -12
  412. data/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb +9 -9
  413. data/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb +12 -12
  414. data/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb +12 -12
  415. data/test/migrations/version_check/20131219224947_migration_version_check.rb +8 -8
  416. data/test/models/admin.rb +5 -5
  417. data/test/models/admin/account.rb +3 -3
  418. data/test/models/admin/user.rb +40 -40
  419. data/test/models/aircraft.rb +5 -5
  420. data/test/models/arunit2_model.rb +3 -3
  421. data/test/models/author.rb +209 -209
  422. data/test/models/auto_id.rb +4 -4
  423. data/test/models/autoloadable/extra_firm.rb +2 -2
  424. data/test/models/binary.rb +2 -2
  425. data/test/models/bird.rb +12 -12
  426. data/test/models/book.rb +23 -23
  427. data/test/models/boolean.rb +2 -2
  428. data/test/models/bulb.rb +52 -52
  429. data/test/models/cake_designer.rb +3 -3
  430. data/test/models/car.rb +29 -29
  431. data/test/models/carrier.rb +2 -2
  432. data/test/models/cat.rb +10 -10
  433. data/test/models/categorization.rb +19 -19
  434. data/test/models/category.rb +35 -35
  435. data/test/models/chef.rb +8 -8
  436. data/test/models/citation.rb +3 -3
  437. data/test/models/club.rb +25 -25
  438. data/test/models/college.rb +10 -10
  439. data/test/models/column.rb +3 -3
  440. data/test/models/column_name.rb +3 -3
  441. data/test/models/comment.rb +76 -76
  442. data/test/models/company.rb +230 -230
  443. data/test/models/company_in_module.rb +98 -98
  444. data/test/models/computer.rb +3 -3
  445. data/test/models/contact.rb +41 -41
  446. data/test/models/content.rb +40 -40
  447. data/test/models/contract.rb +20 -20
  448. data/test/models/country.rb +7 -7
  449. data/test/models/course.rb +6 -6
  450. data/test/models/customer.rb +83 -83
  451. data/test/models/customer_carrier.rb +14 -14
  452. data/test/models/dashboard.rb +3 -3
  453. data/test/models/default.rb +2 -2
  454. data/test/models/department.rb +4 -4
  455. data/test/models/developer.rb +274 -274
  456. data/test/models/dog.rb +5 -5
  457. data/test/models/dog_lover.rb +5 -5
  458. data/test/models/doubloon.rb +12 -12
  459. data/test/models/drink_designer.rb +3 -3
  460. data/test/models/edge.rb +5 -5
  461. data/test/models/electron.rb +5 -5
  462. data/test/models/engine.rb +4 -4
  463. data/test/models/entrant.rb +3 -3
  464. data/test/models/essay.rb +5 -5
  465. data/test/models/event.rb +3 -3
  466. data/test/models/eye.rb +37 -37
  467. data/test/models/face.rb +9 -9
  468. data/test/models/friendship.rb +6 -6
  469. data/test/models/guid.rb +2 -2
  470. data/test/models/guitar.rb +4 -4
  471. data/test/models/hotel.rb +11 -11
  472. data/test/models/image.rb +3 -3
  473. data/test/models/interest.rb +5 -5
  474. data/test/models/invoice.rb +4 -4
  475. data/test/models/item.rb +7 -7
  476. data/test/models/job.rb +7 -7
  477. data/test/models/joke.rb +7 -7
  478. data/test/models/keyboard.rb +3 -3
  479. data/test/models/legacy_thing.rb +3 -3
  480. data/test/models/lesson.rb +11 -11
  481. data/test/models/line_item.rb +3 -3
  482. data/test/models/liquid.rb +4 -4
  483. data/test/models/man.rb +11 -11
  484. data/test/models/matey.rb +4 -4
  485. data/test/models/member.rb +42 -42
  486. data/test/models/member_detail.rb +8 -8
  487. data/test/models/member_type.rb +3 -3
  488. data/test/models/membership.rb +35 -35
  489. data/test/models/mentor.rb +2 -2
  490. data/test/models/minimalistic.rb +2 -2
  491. data/test/models/minivan.rb +9 -9
  492. data/test/models/mixed_case_monkey.rb +3 -3
  493. data/test/models/mocktail_designer.rb +2 -2
  494. data/test/models/molecule.rb +6 -6
  495. data/test/models/movie.rb +5 -5
  496. data/test/models/node.rb +5 -5
  497. data/test/models/non_primary_key.rb +2 -2
  498. data/test/models/notification.rb +3 -3
  499. data/test/models/order.rb +4 -4
  500. data/test/models/organization.rb +14 -14
  501. data/test/models/other_dog.rb +5 -5
  502. data/test/models/owner.rb +37 -37
  503. data/test/models/parrot.rb +28 -28
  504. data/test/models/person.rb +142 -142
  505. data/test/models/personal_legacy_thing.rb +4 -4
  506. data/test/models/pet.rb +18 -18
  507. data/test/models/pet_treasure.rb +6 -6
  508. data/test/models/pirate.rb +92 -92
  509. data/test/models/possession.rb +3 -3
  510. data/test/models/post.rb +273 -273
  511. data/test/models/price_estimate.rb +4 -4
  512. data/test/models/professor.rb +5 -5
  513. data/test/models/project.rb +40 -40
  514. data/test/models/publisher.rb +2 -2
  515. data/test/models/publisher/article.rb +4 -4
  516. data/test/models/publisher/magazine.rb +3 -3
  517. data/test/models/rating.rb +4 -4
  518. data/test/models/reader.rb +23 -23
  519. data/test/models/recipe.rb +3 -3
  520. data/test/models/record.rb +2 -2
  521. data/test/models/reference.rb +22 -22
  522. data/test/models/reply.rb +61 -61
  523. data/test/models/ship.rb +39 -39
  524. data/test/models/ship_part.rb +8 -8
  525. data/test/models/shop.rb +17 -17
  526. data/test/models/shop_account.rb +6 -6
  527. data/test/models/speedometer.rb +6 -6
  528. data/test/models/sponsor.rb +7 -7
  529. data/test/models/string_key_object.rb +3 -3
  530. data/test/models/student.rb +4 -4
  531. data/test/models/subject.rb +16 -16
  532. data/test/models/subscriber.rb +8 -8
  533. data/test/models/subscription.rb +4 -4
  534. data/test/models/tag.rb +13 -13
  535. data/test/models/tagging.rb +13 -13
  536. data/test/models/task.rb +5 -5
  537. data/test/models/topic.rb +118 -118
  538. data/test/models/toy.rb +6 -6
  539. data/test/models/traffic_light.rb +4 -4
  540. data/test/models/treasure.rb +14 -14
  541. data/test/models/treaty.rb +7 -7
  542. data/test/models/tree.rb +3 -3
  543. data/test/models/tuning_peg.rb +4 -4
  544. data/test/models/tyre.rb +11 -11
  545. data/test/models/user.rb +14 -14
  546. data/test/models/uuid_child.rb +3 -3
  547. data/test/models/uuid_item.rb +6 -6
  548. data/test/models/uuid_parent.rb +3 -3
  549. data/test/models/vegetables.rb +24 -24
  550. data/test/models/vehicle.rb +6 -6
  551. data/test/models/vertex.rb +9 -9
  552. data/test/models/warehouse_thing.rb +5 -5
  553. data/test/models/wheel.rb +3 -3
  554. data/test/models/without_table.rb +3 -3
  555. data/test/models/zine.rb +3 -3
  556. data/test/schema/i5/ibm_db_specific_schema.rb +137 -0
  557. data/test/schema/ids/ibm_db_specific_schema.rb +140 -0
  558. data/test/schema/luw/ibm_db_specific_schema.rb +137 -0
  559. data/test/schema/mysql2_specific_schema.rb +68 -68
  560. data/test/schema/oracle_specific_schema.rb +40 -40
  561. data/test/schema/postgresql_specific_schema.rb +114 -114
  562. data/test/schema/schema.rb +1057 -1057
  563. data/test/schema/schema.rb.original +1057 -1057
  564. data/test/schema/sqlite_specific_schema.rb +18 -18
  565. data/test/schema/zOS/ibm_db_specific_schema.rb +208 -0
  566. data/test/support/config.rb +43 -43
  567. data/test/support/connection.rb +23 -23
  568. data/test/support/connection_helper.rb +14 -14
  569. data/test/support/ddl_helper.rb +8 -8
  570. data/test/support/schema_dumping_helper.rb +20 -20
  571. data/test/support/yaml_compatibility_fixtures/rails_4_1.yml +22 -22
  572. data/test/support/yaml_compatibility_fixtures/rails_4_2_0.yml +182 -182
  573. metadata +30 -14
  574. data/lib/mswin32/rb2x/i386/ibm_db.so +0 -0
  575. data/test/fixtures/author_addresses.original +0 -11
  576. data/test/fixtures/authors.original +0 -17
@@ -1,218 +1,219 @@
1
- require "cases/helper"
2
- require 'support/schema_dumping_helper'
3
- require 'models/default'
4
- require 'models/entrant'
5
-
6
- class DefaultTest < ActiveRecord::TestCase
7
- def test_nil_defaults_for_not_null_columns
8
- %w(id name course_id).each do |name|
9
- column = Entrant.columns_hash[name]
10
- assert !column.null, "#{name} column should be NOT NULL"
11
- assert_not column.default, "#{name} column should be DEFAULT 'nil'"
12
- end
13
- end
14
-
15
- if current_adapter?(:PostgreSQLAdapter)
16
- def test_multiline_default_text
17
- record = Default.new
18
- # older postgres versions represent the default with escapes ("\\012" for a newline)
19
- assert("--- []\n\n" == record.multiline_default || "--- []\\012\\012" == record.multiline_default)
20
- end
21
- end
22
- end
23
-
24
- class DefaultNumbersTest < ActiveRecord::TestCase
25
- class DefaultNumber < ActiveRecord::Base; end
26
-
27
- setup do
28
- @connection = ActiveRecord::Base.connection
29
- @connection.create_table :default_numbers do |t|
30
- t.integer :positive_integer, default: 7
31
- t.integer :negative_integer, default: -5
32
- t.decimal :decimal_number, default: "2.78", precision: 5, scale: 2
33
- end
34
- end
35
-
36
- teardown do
37
- @connection.drop_table :default_numbers, if_exists: true
38
- end
39
-
40
- def test_default_positive_integer
41
- record = DefaultNumber.new
42
- assert_equal 7, record.positive_integer
43
- assert_equal "7", record.positive_integer_before_type_cast
44
- end
45
-
46
- def test_default_negative_integer
47
- record = DefaultNumber.new
48
- assert_equal (-5), record.negative_integer
49
- assert_equal "-5", record.negative_integer_before_type_cast
50
- end
51
-
52
- def test_default_decimal_number
53
- record = DefaultNumber.new
54
- assert_equal BigDecimal.new("2.78"), record.decimal_number
55
- assert_equal "2.78", record.decimal_number_before_type_cast
56
- end
57
- end
58
-
59
- class DefaultStringsTest < ActiveRecord::TestCase
60
- class DefaultString < ActiveRecord::Base; end
61
-
62
- setup do
63
- @connection = ActiveRecord::Base.connection
64
- @connection.create_table :default_strings do |t|
65
- t.string :string_col, default: "Smith"
66
- t.string :string_col_with_quotes, default: "O'Connor"
67
- end
68
- DefaultString.reset_column_information
69
- end
70
-
71
- def test_default_strings
72
- assert_equal "Smith", DefaultString.new.string_col
73
- end
74
-
75
- def test_default_strings_containing_single_quotes
76
- assert_equal "O'Connor", DefaultString.new.string_col_with_quotes
77
- end
78
-
79
- teardown do
80
- @connection.drop_table :default_strings
81
- end
82
- end
83
-
84
- if current_adapter?(:PostgreSQLAdapter)
85
- class PostgresqlDefaultExpressionTest < ActiveRecord::TestCase
86
- include SchemaDumpingHelper
87
-
88
- test "schema dump includes default expression" do
89
- output = dump_table_schema("defaults")
90
- assert_match %r/t\.date\s+"modified_date",\s+default: -> { "\('now'::text\)::date" }/, output
91
- assert_match %r/t\.date\s+"modified_date_function",\s+default: -> { "now\(\)" }/, output
92
- assert_match %r/t\.datetime\s+"modified_time",\s+default: -> { "now\(\)" }/, output
93
- assert_match %r/t\.datetime\s+"modified_time_function",\s+default: -> { "now\(\)" }/, output
94
- end
95
- end
96
- end
97
-
98
- if current_adapter?(:Mysql2Adapter)
99
- class MysqlDefaultExpressionTest < ActiveRecord::TestCase
100
- include SchemaDumpingHelper
101
-
102
- if ActiveRecord::Base.connection.version >= '5.6.0'
103
- test "schema dump includes default expression" do
104
- output = dump_table_schema("datetime_defaults")
105
- assert_match %r/t\.datetime\s+"modified_datetime",\s+default: -> { "CURRENT_TIMESTAMP" }/, output
106
- end
107
- end
108
- end
109
-
110
- class DefaultsTestWithoutTransactionalFixtures < ActiveRecord::TestCase
111
- # ActiveRecord::Base#create! (and #save and other related methods) will
112
- # open a new transaction. When in transactional tests mode, this will
113
- # cause Active Record to create a new savepoint. However, since MySQL doesn't
114
- # support DDL transactions, creating a table will result in any created
115
- # savepoints to be automatically released. This in turn causes the savepoint
116
- # release code in AbstractAdapter#transaction to fail.
117
- #
118
- # We don't want that to happen, so we disable transactional tests here.
119
- self.use_transactional_tests = false
120
-
121
- def using_strict(strict)
122
- connection = ActiveRecord::Base.remove_connection
123
- ActiveRecord::Base.establish_connection connection.merge(strict: strict)
124
- yield
125
- ensure
126
- ActiveRecord::Base.remove_connection
127
- ActiveRecord::Base.establish_connection connection
128
- end
129
-
130
- # MySQL cannot have defaults on text/blob columns. It reports the
131
- # default value as null.
132
- #
133
- # Despite this, in non-strict mode, MySQL will use an empty string
134
- # as the default value of the field, if no other value is
135
- # specified.
136
- #
137
- # Therefore, in non-strict mode, we want column.default to report
138
- # an empty string as its default, to be consistent with that.
139
- #
140
- # In strict mode, column.default should be nil.
141
- def test_mysql_text_not_null_defaults_non_strict
142
- using_strict(false) do
143
- with_text_blob_not_null_table do |klass|
144
- record = klass.new
145
- assert_equal '', record.non_null_blob
146
- assert_equal '', record.non_null_text
147
-
148
- assert_nil record.null_blob
149
- assert_nil record.null_text
150
-
151
- record.save!
152
- record.reload
153
-
154
- assert_equal '', record.non_null_text
155
- assert_equal '', record.non_null_blob
156
-
157
- assert_nil record.null_text
158
- assert_nil record.null_blob
159
- end
160
- end
161
- end
162
-
163
- def test_mysql_text_not_null_defaults_strict
164
- using_strict(true) do
165
- with_text_blob_not_null_table do |klass|
166
- record = klass.new
167
- assert_nil record.non_null_blob
168
- assert_nil record.non_null_text
169
- assert_nil record.null_blob
170
- assert_nil record.null_text
171
-
172
- assert_raises(ActiveRecord::StatementInvalid) { klass.create }
173
- end
174
- end
175
- end
176
-
177
- def with_text_blob_not_null_table
178
- klass = Class.new(ActiveRecord::Base)
179
- klass.table_name = 'test_mysql_text_not_null_defaults'
180
- klass.connection.create_table klass.table_name do |t|
181
- t.column :non_null_text, :text, :null => false
182
- t.column :non_null_blob, :blob, :null => false
183
- t.column :null_text, :text, :null => true
184
- t.column :null_blob, :blob, :null => true
185
- end
186
-
187
- yield klass
188
- ensure
189
- klass.connection.drop_table(klass.table_name) rescue nil
190
- end
191
-
192
- # MySQL uses an implicit default 0 rather than NULL unless in strict mode.
193
- # We use an implicit NULL so schema.rb is compatible with other databases.
194
- def test_mysql_integer_not_null_defaults
195
- klass = Class.new(ActiveRecord::Base)
196
- klass.table_name = 'test_integer_not_null_default_zero'
197
- klass.connection.create_table klass.table_name do |t|
198
- t.column :zero, :integer, :null => false, :default => 0
199
- t.column :omit, :integer, :null => false
200
- end
201
-
202
- assert_equal '0', klass.columns_hash['zero'].default
203
- assert !klass.columns_hash['zero'].null
204
- assert_equal nil, klass.columns_hash['omit'].default
205
- assert !klass.columns_hash['omit'].null
206
-
207
- assert_raise(ActiveRecord::StatementInvalid) { klass.create! }
208
-
209
- assert_nothing_raised do
210
- instance = klass.create!(:omit => 1)
211
- assert_equal 0, instance.zero
212
- assert_equal 1, instance.omit
213
- end
214
- ensure
215
- klass.connection.drop_table(klass.table_name) rescue nil
216
- end
217
- end
218
- end
1
+ require "cases/helper"
2
+ require 'support/schema_dumping_helper'
3
+ require 'models/default'
4
+ require 'models/entrant'
5
+
6
+ class DefaultTest < ActiveRecord::TestCase
7
+ def test_nil_defaults_for_not_null_columns
8
+ %w(id name course_id).each do |name|
9
+ column = Entrant.columns_hash[name]
10
+ assert !column.null, "#{name} column should be NOT NULL"
11
+ assert_not column.default, "#{name} column should be DEFAULT 'nil'"
12
+ end
13
+ end
14
+
15
+ if current_adapter?(:PostgreSQLAdapter)
16
+ def test_multiline_default_text
17
+ record = Default.new
18
+ # older postgres versions represent the default with escapes ("\\012" for a newline)
19
+ assert("--- []\n\n" == record.multiline_default || "--- []\\012\\012" == record.multiline_default)
20
+ end
21
+ end
22
+ end
23
+
24
+ class DefaultNumbersTest < ActiveRecord::TestCase
25
+ class DefaultNumber < ActiveRecord::Base; end
26
+
27
+ setup do
28
+ @connection = ActiveRecord::Base.connection
29
+ @connection.create_table :default_numbers do |t|
30
+ t.integer :positive_integer, default: 7
31
+ t.integer :negative_integer, default: -5
32
+ t.decimal :decimal_number, default: "2.78", precision: 5, scale: 2
33
+ end
34
+ end
35
+
36
+ teardown do
37
+ @connection.drop_table :default_numbers
38
+ #, if_exists: true
39
+ end
40
+
41
+ def test_default_positive_integer
42
+ record = DefaultNumber.new
43
+ assert_equal 7, record.positive_integer
44
+ assert_equal "7", record.positive_integer_before_type_cast
45
+ end
46
+
47
+ def test_default_negative_integer
48
+ record = DefaultNumber.new
49
+ assert_equal (-5), record.negative_integer
50
+ assert_equal "-5", record.negative_integer_before_type_cast
51
+ end
52
+
53
+ def test_default_decimal_number
54
+ record = DefaultNumber.new
55
+ assert_equal BigDecimal.new("2.78"), record.decimal_number
56
+ assert_equal "2.78", record.decimal_number_before_type_cast
57
+ end
58
+ end
59
+
60
+ class DefaultStringsTest < ActiveRecord::TestCase
61
+ class DefaultString < ActiveRecord::Base; end
62
+
63
+ setup do
64
+ @connection = ActiveRecord::Base.connection
65
+ @connection.create_table :default_strings do |t|
66
+ t.string :string_col, default: "Smith"
67
+ t.string :string_col_with_quotes, default: "O'Connor"
68
+ end
69
+ DefaultString.reset_column_information
70
+ end
71
+
72
+ def test_default_strings
73
+ assert_equal "Smith", DefaultString.new.string_col
74
+ end
75
+
76
+ def test_default_strings_containing_single_quotes
77
+ assert_equal "O'Connor", DefaultString.new.string_col_with_quotes
78
+ end
79
+
80
+ teardown do
81
+ @connection.drop_table :default_strings
82
+ end
83
+ end
84
+
85
+ if current_adapter?(:PostgreSQLAdapter)
86
+ class PostgresqlDefaultExpressionTest < ActiveRecord::TestCase
87
+ include SchemaDumpingHelper
88
+
89
+ test "schema dump includes default expression" do
90
+ output = dump_table_schema("defaults")
91
+ assert_match %r/t\.date\s+"modified_date",\s+default: -> { "\('now'::text\)::date" }/, output
92
+ assert_match %r/t\.date\s+"modified_date_function",\s+default: -> { "now\(\)" }/, output
93
+ assert_match %r/t\.datetime\s+"modified_time",\s+default: -> { "now\(\)" }/, output
94
+ assert_match %r/t\.datetime\s+"modified_time_function",\s+default: -> { "now\(\)" }/, output
95
+ end
96
+ end
97
+ end
98
+
99
+ if current_adapter?(:Mysql2Adapter)
100
+ class MysqlDefaultExpressionTest < ActiveRecord::TestCase
101
+ include SchemaDumpingHelper
102
+
103
+ if ActiveRecord::Base.connection.version >= '5.6.0'
104
+ test "schema dump includes default expression" do
105
+ output = dump_table_schema("datetime_defaults")
106
+ assert_match %r/t\.datetime\s+"modified_datetime",\s+default: -> { "CURRENT_TIMESTAMP" }/, output
107
+ end
108
+ end
109
+ end
110
+
111
+ class DefaultsTestWithoutTransactionalFixtures < ActiveRecord::TestCase
112
+ # ActiveRecord::Base#create! (and #save and other related methods) will
113
+ # open a new transaction. When in transactional tests mode, this will
114
+ # cause Active Record to create a new savepoint. However, since MySQL doesn't
115
+ # support DDL transactions, creating a table will result in any created
116
+ # savepoints to be automatically released. This in turn causes the savepoint
117
+ # release code in AbstractAdapter#transaction to fail.
118
+ #
119
+ # We don't want that to happen, so we disable transactional tests here.
120
+ self.use_transactional_tests = false
121
+
122
+ def using_strict(strict)
123
+ connection = ActiveRecord::Base.remove_connection
124
+ ActiveRecord::Base.establish_connection connection.merge(strict: strict)
125
+ yield
126
+ ensure
127
+ ActiveRecord::Base.remove_connection
128
+ ActiveRecord::Base.establish_connection connection
129
+ end
130
+
131
+ # MySQL cannot have defaults on text/blob columns. It reports the
132
+ # default value as null.
133
+ #
134
+ # Despite this, in non-strict mode, MySQL will use an empty string
135
+ # as the default value of the field, if no other value is
136
+ # specified.
137
+ #
138
+ # Therefore, in non-strict mode, we want column.default to report
139
+ # an empty string as its default, to be consistent with that.
140
+ #
141
+ # In strict mode, column.default should be nil.
142
+ def test_mysql_text_not_null_defaults_non_strict
143
+ using_strict(false) do
144
+ with_text_blob_not_null_table do |klass|
145
+ record = klass.new
146
+ assert_equal '', record.non_null_blob
147
+ assert_equal '', record.non_null_text
148
+
149
+ assert_nil record.null_blob
150
+ assert_nil record.null_text
151
+
152
+ record.save!
153
+ record.reload
154
+
155
+ assert_equal '', record.non_null_text
156
+ assert_equal '', record.non_null_blob
157
+
158
+ assert_nil record.null_text
159
+ assert_nil record.null_blob
160
+ end
161
+ end
162
+ end
163
+
164
+ def test_mysql_text_not_null_defaults_strict
165
+ using_strict(true) do
166
+ with_text_blob_not_null_table do |klass|
167
+ record = klass.new
168
+ assert_nil record.non_null_blob
169
+ assert_nil record.non_null_text
170
+ assert_nil record.null_blob
171
+ assert_nil record.null_text
172
+
173
+ assert_raises(ActiveRecord::StatementInvalid) { klass.create }
174
+ end
175
+ end
176
+ end
177
+
178
+ def with_text_blob_not_null_table
179
+ klass = Class.new(ActiveRecord::Base)
180
+ klass.table_name = 'test_mysql_text_not_null_defaults'
181
+ klass.connection.create_table klass.table_name do |t|
182
+ t.column :non_null_text, :text, :null => false
183
+ t.column :non_null_blob, :blob, :null => false
184
+ t.column :null_text, :text, :null => true
185
+ t.column :null_blob, :blob, :null => true
186
+ end
187
+
188
+ yield klass
189
+ ensure
190
+ klass.connection.drop_table(klass.table_name) rescue nil
191
+ end
192
+
193
+ # MySQL uses an implicit default 0 rather than NULL unless in strict mode.
194
+ # We use an implicit NULL so schema.rb is compatible with other databases.
195
+ def test_mysql_integer_not_null_defaults
196
+ klass = Class.new(ActiveRecord::Base)
197
+ klass.table_name = 'test_integer_not_null_default_zero'
198
+ klass.connection.create_table klass.table_name do |t|
199
+ t.column :zero, :integer, :null => false, :default => 0
200
+ t.column :omit, :integer, :null => false
201
+ end
202
+
203
+ assert_equal '0', klass.columns_hash['zero'].default
204
+ assert !klass.columns_hash['zero'].null
205
+ assert_equal nil, klass.columns_hash['omit'].default
206
+ assert !klass.columns_hash['omit'].null
207
+
208
+ assert_raise(ActiveRecord::StatementInvalid) { klass.create! }
209
+
210
+ assert_nothing_raised do
211
+ instance = klass.create!(:omit => 1)
212
+ assert_equal 0, instance.zero
213
+ assert_equal 1, instance.omit
214
+ end
215
+ ensure
216
+ klass.connection.drop_table(klass.table_name) rescue nil
217
+ end
218
+ end
219
+ end
@@ -1,763 +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_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
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