ibm_db 3.0.0-x86-mingw32 → 3.0.1-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 (465) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +4 -0
  3. data/MANIFEST +14 -14
  4. data/README +225 -225
  5. data/ext/Makefile.nt32 +181 -181
  6. data/ext/Makefile.nt32.191 +212 -212
  7. data/ext/OLD/extconf.rb +264 -0
  8. data/ext/{extconf_MacOS.rb → OLD/extconf_MacOS.rb} +269 -269
  9. data/ext/extconf.rb +291 -264
  10. data/ext/ibm_db.c +2 -2
  11. data/ext/ruby_ibm_db.h +241 -241
  12. data/ext/ruby_ibm_db_cli.h +500 -500
  13. data/init.rb +41 -41
  14. data/lib/IBM_DB.rb +27 -27
  15. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +4 -4
  16. data/lib/active_record/connection_adapters/ibmdb_adapter.rb +1 -1
  17. data/lib/active_record/vendor/db2-i5-zOS.yaml +328 -328
  18. data/lib/mswin32/ibm_db.rb +115 -115
  19. data/test/active_record/connection_adapters/fake_adapter.rb +46 -0
  20. data/test/assets/example.log +1 -0
  21. data/test/assets/flowers.jpg +0 -0
  22. data/test/assets/test.txt +1 -0
  23. data/test/cases/adapter_test.rb +261 -207
  24. data/test/cases/aggregations_test.rb +158 -0
  25. data/test/cases/ar_schema_test.rb +161 -0
  26. data/test/cases/associations/association_scope_test.rb +21 -0
  27. data/test/cases/associations/belongs_to_associations_test.rb +1029 -711
  28. data/test/cases/associations/callbacks_test.rb +192 -0
  29. data/test/cases/associations/cascaded_eager_loading_test.rb +188 -181
  30. data/test/cases/associations/deprecated_counter_cache_on_has_many_through_test.rb +26 -0
  31. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -0
  32. data/test/cases/associations/eager_load_nested_include_test.rb +128 -0
  33. data/test/cases/associations/eager_singularization_test.rb +148 -0
  34. data/test/cases/associations/eager_test.rb +1411 -0
  35. data/test/cases/associations/extension_test.rb +82 -0
  36. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +932 -851
  37. data/test/cases/associations/has_many_associations_test.rb +2162 -0
  38. data/test/cases/associations/has_many_through_associations_test.rb +1204 -0
  39. data/test/cases/associations/has_one_associations_test.rb +610 -0
  40. data/test/cases/associations/has_one_through_associations_test.rb +380 -0
  41. data/test/cases/associations/inner_join_association_test.rb +139 -0
  42. data/test/cases/associations/inverse_associations_test.rb +693 -0
  43. data/test/cases/associations/join_model_test.rb +754 -743
  44. data/test/cases/associations/nested_through_associations_test.rb +579 -0
  45. data/test/cases/associations/required_test.rb +82 -0
  46. data/test/cases/associations_test.rb +380 -0
  47. data/test/cases/attribute_decorators_test.rb +125 -0
  48. data/test/cases/attribute_methods/read_test.rb +60 -0
  49. data/test/cases/attribute_methods/serialization_test.rb +29 -0
  50. data/test/cases/attribute_methods_test.rb +952 -822
  51. data/test/cases/attribute_set_test.rb +200 -0
  52. data/test/cases/attribute_test.rb +180 -0
  53. data/test/cases/attributes_test.rb +136 -0
  54. data/test/cases/autosave_association_test.rb +1595 -0
  55. data/test/cases/base_test.rb +1638 -2133
  56. data/test/cases/batches_test.rb +212 -0
  57. data/test/cases/binary_test.rb +52 -0
  58. data/test/cases/bind_parameter_test.rb +100 -0
  59. data/test/cases/calculations_test.rb +646 -482
  60. data/test/cases/callbacks_test.rb +543 -0
  61. data/test/cases/clone_test.rb +40 -0
  62. data/test/cases/coders/yaml_column_test.rb +63 -0
  63. data/test/cases/column_alias_test.rb +17 -0
  64. data/test/cases/column_definition_test.rb +123 -0
  65. data/test/cases/connection_adapters/adapter_leasing_test.rb +54 -0
  66. data/test/cases/connection_adapters/connection_handler_test.rb +53 -0
  67. data/test/cases/connection_adapters/connection_specification_test.rb +12 -0
  68. data/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb +293 -0
  69. data/test/cases/connection_adapters/mysql_type_lookup_test.rb +65 -0
  70. data/test/cases/connection_adapters/quoting_test.rb +13 -0
  71. data/test/cases/connection_adapters/schema_cache_test.rb +56 -0
  72. data/test/cases/connection_adapters/type_lookup_test.rb +110 -0
  73. data/test/cases/connection_management_test.rb +122 -0
  74. data/test/cases/connection_pool_test.rb +346 -0
  75. data/test/cases/connection_specification/resolver_test.rb +116 -0
  76. data/test/cases/core_test.rb +112 -0
  77. data/test/cases/counter_cache_test.rb +209 -0
  78. data/test/cases/custom_locking_test.rb +17 -0
  79. data/test/cases/database_statements_test.rb +19 -0
  80. data/test/cases/date_time_test.rb +61 -0
  81. data/test/cases/defaults_test.rb +223 -0
  82. data/test/cases/dirty_test.rb +775 -0
  83. data/test/cases/disconnected_test.rb +28 -0
  84. data/test/cases/dup_test.rb +157 -0
  85. data/test/cases/enum_test.rb +290 -0
  86. data/test/cases/explain_subscriber_test.rb +64 -0
  87. data/test/cases/explain_test.rb +76 -0
  88. data/test/cases/finder_respond_to_test.rb +60 -0
  89. data/test/cases/finder_test.rb +1166 -0
  90. data/test/cases/fixture_set/file_test.rb +138 -0
  91. data/test/cases/fixtures_test.rb +897 -0
  92. data/test/cases/forbidden_attributes_protection_test.rb +99 -0
  93. data/test/cases/habtm_destroy_order_test.rb +61 -0
  94. data/test/cases/helper.rb +210 -0
  95. data/test/cases/hot_compatibility_test.rb +54 -0
  96. data/test/cases/i18n_test.rb +45 -0
  97. data/test/cases/inheritance_test.rb +375 -0
  98. data/test/cases/integration_test.rb +139 -0
  99. data/test/cases/invalid_connection_test.rb +22 -0
  100. data/test/cases/invalid_date_test.rb +32 -0
  101. data/test/cases/invertible_migration_test.rb +295 -0
  102. data/test/cases/json_serialization_test.rb +302 -0
  103. data/test/cases/locking_test.rb +477 -0
  104. data/test/cases/log_subscriber_test.rb +136 -0
  105. data/test/cases/migration/change_schema_test - Copy.rb +448 -0
  106. data/test/cases/migration/change_schema_test.rb +472 -0
  107. data/test/cases/migration/change_table_test.rb +224 -0
  108. data/test/cases/migration/column_attributes_test.rb +192 -0
  109. data/test/cases/migration/column_positioning_test.rb +56 -0
  110. data/test/cases/migration/columns_test.rb +304 -0
  111. data/test/cases/migration/command_recorder_test.rb +305 -0
  112. data/test/cases/migration/create_join_table_test.rb +148 -0
  113. data/test/cases/migration/foreign_key_test - Changed.rb +325 -0
  114. data/test/cases/migration/foreign_key_test.rb +360 -0
  115. data/test/cases/migration/helper.rb +39 -0
  116. data/test/cases/migration/index_test.rb +216 -0
  117. data/test/cases/migration/logger_test.rb +36 -0
  118. data/test/cases/migration/pending_migrations_test.rb +53 -0
  119. data/test/cases/migration/references_foreign_key_test.rb +214 -0
  120. data/test/cases/migration/references_index_test.rb +101 -0
  121. data/test/cases/migration/references_statements_test.rb +116 -0
  122. data/test/cases/migration/rename_table_test.rb +93 -0
  123. data/test/cases/migration/table_and_index_test.rb +24 -0
  124. data/test/cases/migration_test.rb +959 -2408
  125. data/test/cases/migrator_test.rb +388 -0
  126. data/test/cases/mixin_test.rb +70 -0
  127. data/test/cases/modules_test.rb +173 -0
  128. data/test/cases/multiparameter_attributes_test.rb +350 -0
  129. data/test/cases/multiple_db_test.rb +115 -0
  130. data/test/cases/nested_attributes_test.rb +1057 -0
  131. data/test/cases/nested_attributes_with_callbacks_test.rb +144 -0
  132. data/test/cases/persistence_test.rb +909 -642
  133. data/test/cases/pooled_connections_test.rb +81 -0
  134. data/test/cases/primary_keys_test.rb +237 -0
  135. data/test/cases/query_cache_test.rb +326 -257
  136. data/test/cases/quoting_test.rb +156 -0
  137. data/test/cases/readonly_test.rb +118 -0
  138. data/test/cases/reaper_test.rb +85 -0
  139. data/test/cases/reflection_test.rb +454 -0
  140. data/test/cases/relation/delegation_test.rb +68 -0
  141. data/test/cases/relation/merging_test.rb +161 -0
  142. data/test/cases/relation/mutation_test.rb +165 -0
  143. data/test/cases/relation/predicate_builder_test.rb +14 -0
  144. data/test/cases/relation/where_chain_test.rb +181 -0
  145. data/test/cases/relation/where_test.rb +300 -0
  146. data/test/cases/relation/where_test2.rb +36 -0
  147. data/test/cases/relation_test.rb +297 -0
  148. data/test/cases/relations_test.rb +1815 -1182
  149. data/test/cases/reload_models_test.rb +22 -0
  150. data/test/cases/result_test.rb +80 -0
  151. data/test/cases/sanitize_test.rb +83 -0
  152. data/test/cases/schema_dumper_test.rb +463 -256
  153. data/test/cases/scoping/default_scoping_test.rb +454 -0
  154. data/test/cases/scoping/named_scoping_test.rb +524 -0
  155. data/test/cases/scoping/relation_scoping_test.rb +357 -0
  156. data/test/cases/serialization_test.rb +104 -0
  157. data/test/cases/serialized_attribute_test.rb +277 -0
  158. data/test/cases/statement_cache_test.rb +98 -0
  159. data/test/cases/store_test.rb +194 -0
  160. data/test/cases/tasks/database_tasks_test.rb +396 -0
  161. data/test/cases/tasks/mysql_rake_test.rb +311 -0
  162. data/test/cases/tasks/postgresql_rake_test.rb +245 -0
  163. data/test/cases/tasks/sqlite_rake_test.rb +193 -0
  164. data/test/cases/test_case.rb +123 -0
  165. data/test/cases/timestamp_test.rb +468 -0
  166. data/test/cases/transaction_callbacks_test.rb +452 -300
  167. data/test/cases/transaction_isolation_test.rb +106 -0
  168. data/test/cases/transactions_test.rb +817 -0
  169. data/test/cases/type/decimal_test.rb +51 -0
  170. data/test/cases/type/integer_test.rb +121 -0
  171. data/test/cases/type/string_test.rb +36 -0
  172. data/test/cases/type/type_map_test.rb +177 -0
  173. data/test/cases/type/unsigned_integer_test.rb +18 -0
  174. data/test/cases/types_test.rb +141 -0
  175. data/test/cases/unconnected_test.rb +33 -0
  176. data/test/cases/validations/association_validation_test.rb +86 -0
  177. data/test/cases/validations/i18n_generate_message_validation_test.rb +84 -0
  178. data/test/cases/validations/i18n_validation_test.rb +90 -0
  179. data/test/cases/validations/length_validation_test.rb +47 -0
  180. data/test/cases/validations/presence_validation_test.rb +68 -0
  181. data/test/cases/validations/uniqueness_validation_test.rb +434 -299
  182. data/test/cases/validations_repair_helper.rb +23 -0
  183. data/test/cases/validations_test.rb +165 -0
  184. data/test/cases/view_test.rb +113 -0
  185. data/test/cases/xml_serialization_test.rb +457 -408
  186. data/test/cases/yaml_serialization_test.rb +86 -0
  187. data/test/config.rb +5 -0
  188. data/test/config.yml +154 -154
  189. data/test/connections/native_ibm_db/connection.rb +43 -43
  190. data/test/fixtures/accounts.yml +29 -0
  191. data/test/fixtures/admin/accounts.yml +2 -0
  192. data/test/fixtures/admin/randomly_named_a9.yml +7 -0
  193. data/test/fixtures/admin/randomly_named_b0.yml +7 -0
  194. data/test/fixtures/admin/users.yml +10 -0
  195. data/test/fixtures/all/admin +1 -0
  196. data/test/fixtures/all/developers.yml +0 -0
  197. data/test/fixtures/all/people.yml +0 -0
  198. data/test/fixtures/all/tasks.yml +0 -0
  199. data/test/fixtures/author_addresses.yml +18 -0
  200. data/test/fixtures/author_favorites.yml +4 -0
  201. data/test/fixtures/authors.yml +23 -0
  202. data/test/fixtures/binaries.yml +133 -0
  203. data/test/fixtures/books.yml +11 -0
  204. data/test/fixtures/bulbs.yml +5 -0
  205. data/test/fixtures/cars.yml +9 -0
  206. data/test/fixtures/categories.yml +19 -0
  207. data/test/fixtures/categories/special_categories.yml +9 -0
  208. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  209. data/test/fixtures/categories_ordered.yml +7 -0
  210. data/test/fixtures/categories_posts.yml +31 -0
  211. data/test/fixtures/categorizations.yml +23 -0
  212. data/test/fixtures/clubs.yml +8 -0
  213. data/test/fixtures/collections.yml +3 -0
  214. data/test/fixtures/colleges.yml +3 -0
  215. data/test/fixtures/comments.yml +65 -0
  216. data/test/fixtures/companies.yml +67 -0
  217. data/test/fixtures/computers.yml +10 -0
  218. data/test/fixtures/courses.yml +8 -0
  219. data/test/fixtures/customers.yml +26 -0
  220. data/test/fixtures/dashboards.yml +6 -0
  221. data/test/fixtures/developers.yml +22 -0
  222. data/test/fixtures/developers_projects.yml +17 -0
  223. data/test/fixtures/dog_lovers.yml +7 -0
  224. data/test/fixtures/dogs.yml +4 -0
  225. data/test/fixtures/doubloons.yml +3 -0
  226. data/test/fixtures/edges.yml +5 -0
  227. data/test/fixtures/entrants.yml +14 -0
  228. data/test/fixtures/essays.yml +6 -0
  229. data/test/fixtures/faces.yml +11 -0
  230. data/test/fixtures/fk_test_has_fk.yml +3 -0
  231. data/test/fixtures/fk_test_has_pk.yml +2 -0
  232. data/test/fixtures/friendships.yml +4 -0
  233. data/test/fixtures/funny_jokes.yml +10 -0
  234. data/test/fixtures/interests.yml +33 -0
  235. data/test/fixtures/items.yml +3 -0
  236. data/test/fixtures/jobs.yml +7 -0
  237. data/test/fixtures/legacy_things.yml +3 -0
  238. data/test/fixtures/mateys.yml +4 -0
  239. data/test/fixtures/member_details.yml +8 -0
  240. data/test/fixtures/member_types.yml +6 -0
  241. data/test/fixtures/members.yml +11 -0
  242. data/test/fixtures/memberships.yml +34 -0
  243. data/test/fixtures/men.yml +5 -0
  244. data/test/fixtures/minimalistics.yml +2 -0
  245. data/test/fixtures/minivans.yml +5 -0
  246. data/test/fixtures/mixed_case_monkeys.yml +6 -0
  247. data/test/fixtures/mixins.yml +29 -0
  248. data/test/fixtures/movies.yml +7 -0
  249. data/test/fixtures/naked/csv/accounts.csv +1 -0
  250. data/test/fixtures/naked/yml/accounts.yml +1 -0
  251. data/test/fixtures/naked/yml/companies.yml +1 -0
  252. data/test/fixtures/naked/yml/courses.yml +1 -0
  253. data/test/fixtures/organizations.yml +5 -0
  254. data/test/fixtures/other_topics.yml +42 -0
  255. data/test/fixtures/owners.yml +9 -0
  256. data/test/fixtures/parrots.yml +27 -0
  257. data/test/fixtures/parrots_pirates.yml +7 -0
  258. data/test/fixtures/people.yml +24 -0
  259. data/test/fixtures/peoples_treasures.yml +3 -0
  260. data/test/fixtures/pets.yml +19 -0
  261. data/test/fixtures/pirates.yml +12 -0
  262. data/test/fixtures/posts.yml +80 -0
  263. data/test/fixtures/price_estimates.yml +7 -0
  264. data/test/fixtures/products.yml +4 -0
  265. data/test/fixtures/projects.yml +7 -0
  266. data/test/fixtures/randomly_named_a9.yml +7 -0
  267. data/test/fixtures/ratings.yml +14 -0
  268. data/test/fixtures/readers.yml +11 -0
  269. data/test/fixtures/references.yml +17 -0
  270. data/test/fixtures/reserved_words/distinct.yml +5 -0
  271. data/test/fixtures/reserved_words/distinct_select.yml +11 -0
  272. data/test/fixtures/reserved_words/group.yml +14 -0
  273. data/test/fixtures/reserved_words/select.yml +8 -0
  274. data/test/fixtures/reserved_words/values.yml +7 -0
  275. data/test/fixtures/ships.yml +6 -0
  276. data/test/fixtures/speedometers.yml +8 -0
  277. data/test/fixtures/sponsors.yml +12 -0
  278. data/test/fixtures/string_key_objects.yml +7 -0
  279. data/test/fixtures/subscribers.yml +11 -0
  280. data/test/fixtures/subscriptions.yml +12 -0
  281. data/test/fixtures/taggings.yml +78 -0
  282. data/test/fixtures/tags.yml +11 -0
  283. data/test/fixtures/tasks.yml +7 -0
  284. data/test/fixtures/teapots.yml +3 -0
  285. data/test/fixtures/to_be_linked/accounts.yml +2 -0
  286. data/test/fixtures/to_be_linked/users.yml +10 -0
  287. data/test/fixtures/topics.yml +49 -0
  288. data/test/fixtures/toys.yml +14 -0
  289. data/test/fixtures/traffic_lights.yml +10 -0
  290. data/test/fixtures/treasures.yml +10 -0
  291. data/test/fixtures/uuid_children.yml +3 -0
  292. data/test/fixtures/uuid_parents.yml +2 -0
  293. data/test/fixtures/variants.yml +4 -0
  294. data/test/fixtures/vegetables.yml +20 -0
  295. data/test/fixtures/vertices.yml +4 -0
  296. data/test/fixtures/warehouse_things.yml +3 -0
  297. data/test/fixtures/zines.yml +5 -0
  298. data/test/ibm_db_test.rb +24 -24
  299. data/test/migrations/10_urban/9_add_expressions.rb +11 -0
  300. data/test/migrations/decimal/1_give_me_big_numbers.rb +15 -0
  301. data/test/migrations/magic/1_currencies_have_symbols.rb +12 -0
  302. data/test/migrations/missing/1000_people_have_middle_names.rb +9 -0
  303. data/test/migrations/missing/1_people_have_last_names.rb +9 -0
  304. data/test/migrations/missing/3_we_need_reminders.rb +12 -0
  305. data/test/migrations/missing/4_innocent_jointable.rb +12 -0
  306. data/test/migrations/rename/1_we_need_things.rb +11 -0
  307. data/test/migrations/rename/2_rename_things.rb +9 -0
  308. data/test/migrations/to_copy/1_people_have_hobbies.rb +9 -0
  309. data/test/migrations/to_copy/2_people_have_descriptions.rb +9 -0
  310. data/test/migrations/to_copy2/1_create_articles.rb +7 -0
  311. data/test/migrations/to_copy2/2_create_comments.rb +7 -0
  312. data/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb +9 -0
  313. data/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb +9 -0
  314. data/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb +9 -0
  315. data/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb +7 -0
  316. data/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb +7 -0
  317. data/test/migrations/valid/1_valid_people_have_last_names.rb +9 -0
  318. data/test/migrations/valid/2_we_need_reminders.rb +12 -0
  319. data/test/migrations/valid/3_innocent_jointable.rb +12 -0
  320. data/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb +9 -0
  321. data/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb +12 -0
  322. data/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb +12 -0
  323. data/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb +9 -0
  324. data/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb +12 -0
  325. data/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb +12 -0
  326. data/test/migrations/version_check/20131219224947_migration_version_check.rb +8 -0
  327. data/test/models/admin.rb +5 -0
  328. data/test/models/admin/account.rb +3 -0
  329. data/test/models/admin/randomly_named_c1.rb +3 -0
  330. data/test/models/admin/user.rb +40 -0
  331. data/test/models/aircraft.rb +4 -0
  332. data/test/models/arunit2_model.rb +3 -0
  333. data/test/models/author.rb +212 -0
  334. data/test/models/auto_id.rb +4 -0
  335. data/test/models/autoloadable/extra_firm.rb +2 -0
  336. data/test/models/binary.rb +2 -0
  337. data/test/models/bird.rb +12 -0
  338. data/test/models/book.rb +18 -0
  339. data/test/models/boolean.rb +2 -0
  340. data/test/models/bulb.rb +51 -0
  341. data/test/models/cake_designer.rb +3 -0
  342. data/test/models/car.rb +26 -0
  343. data/test/models/carrier.rb +2 -0
  344. data/test/models/categorization.rb +19 -0
  345. data/test/models/category.rb +35 -0
  346. data/test/models/chef.rb +3 -0
  347. data/test/models/citation.rb +3 -0
  348. data/test/models/club.rb +23 -0
  349. data/test/models/college.rb +10 -0
  350. data/test/models/column.rb +3 -0
  351. data/test/models/column_name.rb +3 -0
  352. data/test/models/comment.rb +64 -0
  353. data/test/models/company.rb +225 -0
  354. data/test/models/company_in_module.rb +98 -0
  355. data/test/models/computer.rb +3 -0
  356. data/test/models/contact.rb +41 -0
  357. data/test/models/contract.rb +20 -0
  358. data/test/models/country.rb +7 -0
  359. data/test/models/course.rb +6 -0
  360. data/test/models/customer.rb +77 -0
  361. data/test/models/customer_carrier.rb +14 -0
  362. data/test/models/dashboard.rb +3 -0
  363. data/test/models/default.rb +2 -0
  364. data/test/models/department.rb +4 -0
  365. data/test/models/developer.rb +252 -0
  366. data/test/models/dog.rb +5 -0
  367. data/test/models/dog_lover.rb +5 -0
  368. data/test/models/doubloon.rb +12 -0
  369. data/test/models/drink_designer.rb +3 -0
  370. data/test/models/edge.rb +5 -0
  371. data/test/models/electron.rb +5 -0
  372. data/test/models/engine.rb +4 -0
  373. data/test/models/entrant.rb +3 -0
  374. data/test/models/essay.rb +5 -0
  375. data/test/models/event.rb +3 -0
  376. data/test/models/eye.rb +37 -0
  377. data/test/models/face.rb +9 -0
  378. data/test/models/friendship.rb +6 -0
  379. data/test/models/guid.rb +2 -0
  380. data/test/models/hotel.rb +6 -0
  381. data/test/models/image.rb +3 -0
  382. data/test/models/interest.rb +5 -0
  383. data/test/models/invoice.rb +4 -0
  384. data/test/models/item.rb +7 -0
  385. data/test/models/job.rb +7 -0
  386. data/test/models/joke.rb +7 -0
  387. data/test/models/keyboard.rb +3 -0
  388. data/test/models/legacy_thing.rb +3 -0
  389. data/test/models/lesson.rb +11 -0
  390. data/test/models/line_item.rb +3 -0
  391. data/test/models/liquid.rb +4 -0
  392. data/test/models/man.rb +11 -0
  393. data/test/models/matey.rb +4 -0
  394. data/test/models/member.rb +41 -0
  395. data/test/models/member_detail.rb +7 -0
  396. data/test/models/member_type.rb +3 -0
  397. data/test/models/membership.rb +35 -0
  398. data/test/models/minimalistic.rb +2 -0
  399. data/test/models/minivan.rb +9 -0
  400. data/test/models/mixed_case_monkey.rb +3 -0
  401. data/test/models/molecule.rb +6 -0
  402. data/test/models/movie.rb +5 -0
  403. data/test/models/order.rb +4 -0
  404. data/test/models/organization.rb +14 -0
  405. data/test/models/owner.rb +34 -0
  406. data/test/models/parrot.rb +29 -0
  407. data/test/models/person.rb +143 -0
  408. data/test/models/personal_legacy_thing.rb +4 -0
  409. data/test/models/pet.rb +15 -0
  410. data/test/models/pirate.rb +92 -0
  411. data/test/models/possession.rb +3 -0
  412. data/test/models/post.rb +264 -0
  413. data/test/models/price_estimate.rb +4 -0
  414. data/test/models/professor.rb +5 -0
  415. data/test/models/project.rb +29 -0
  416. data/test/models/publisher.rb +2 -0
  417. data/test/models/publisher/article.rb +4 -0
  418. data/test/models/publisher/magazine.rb +3 -0
  419. data/test/models/randomly_named_c1.rb +3 -0
  420. data/test/models/rating.rb +4 -0
  421. data/test/models/reader.rb +23 -0
  422. data/test/models/record.rb +2 -0
  423. data/test/models/reference.rb +22 -0
  424. data/test/models/reply.rb +61 -0
  425. data/test/models/ship.rb +33 -0
  426. data/test/models/ship_part.rb +8 -0
  427. data/test/models/shop.rb +17 -0
  428. data/test/models/shop_account.rb +6 -0
  429. data/test/models/speedometer.rb +6 -0
  430. data/test/models/sponsor.rb +7 -0
  431. data/test/models/string_key_object.rb +3 -0
  432. data/test/models/student.rb +4 -0
  433. data/test/models/subject.rb +16 -0
  434. data/test/models/subscriber.rb +8 -0
  435. data/test/models/subscription.rb +4 -0
  436. data/test/models/tag.rb +7 -0
  437. data/test/models/tagging.rb +13 -0
  438. data/test/models/task.rb +5 -0
  439. data/test/models/topic.rb +124 -0
  440. data/test/models/toy.rb +6 -0
  441. data/test/models/traffic_light.rb +4 -0
  442. data/test/models/treasure.rb +14 -0
  443. data/test/models/treaty.rb +7 -0
  444. data/test/models/tyre.rb +11 -0
  445. data/test/models/uuid_child.rb +3 -0
  446. data/test/models/uuid_parent.rb +3 -0
  447. data/test/models/vegetables.rb +24 -0
  448. data/test/models/vehicle.rb +7 -0
  449. data/test/models/vertex.rb +9 -0
  450. data/test/models/warehouse_thing.rb +5 -5
  451. data/test/models/wheel.rb +3 -0
  452. data/test/models/without_table.rb +3 -0
  453. data/test/models/zine.rb +3 -0
  454. data/test/schema/mysql2_specific_schema.rb +58 -0
  455. data/test/schema/mysql_specific_schema.rb +70 -0
  456. data/test/schema/oracle_specific_schema.rb +43 -0
  457. data/test/schema/postgresql_specific_schema.rb +202 -0
  458. data/test/schema/schema.rb +938 -751
  459. data/test/schema/sqlite_specific_schema.rb +22 -0
  460. data/test/support/config.rb +43 -0
  461. data/test/support/connection.rb +22 -0
  462. data/test/support/connection_helper.rb +14 -0
  463. data/test/support/ddl_helper.rb +8 -0
  464. data/test/support/schema_dumping_helper.rb +20 -0
  465. metadata +444 -18
@@ -0,0 +1,64 @@
1
+ require 'cases/helper'
2
+ require 'active_record/explain_subscriber'
3
+ require 'active_record/explain_registry'
4
+
5
+ if ActiveRecord::Base.connection.supports_explain?
6
+ class ExplainSubscriberTest < ActiveRecord::TestCase
7
+ SUBSCRIBER = ActiveRecord::ExplainSubscriber.new
8
+
9
+ def setup
10
+ ActiveRecord::ExplainRegistry.reset
11
+ ActiveRecord::ExplainRegistry.collect = true
12
+ end
13
+
14
+ def test_collects_nothing_if_the_payload_has_an_exception
15
+ SUBSCRIBER.finish(nil, nil, exception: Exception.new)
16
+ assert queries.empty?
17
+ end
18
+
19
+ def test_collects_nothing_for_ignored_payloads
20
+ ActiveRecord::ExplainSubscriber::IGNORED_PAYLOADS.each do |ip|
21
+ SUBSCRIBER.finish(nil, nil, name: ip)
22
+ end
23
+ assert queries.empty?
24
+ end
25
+
26
+ def test_collects_nothing_if_collect_is_false
27
+ ActiveRecord::ExplainRegistry.collect = false
28
+ SUBSCRIBER.finish(nil, nil, name: 'SQL', sql: 'select 1 from users', binds: [1, 2])
29
+ assert queries.empty?
30
+ end
31
+
32
+ def test_collects_pairs_of_queries_and_binds
33
+ sql = 'select 1 from users'
34
+ binds = [1, 2]
35
+ SUBSCRIBER.finish(nil, nil, name: 'SQL', sql: sql, binds: binds)
36
+ assert_equal 1, queries.size
37
+ assert_equal sql, queries[0][0]
38
+ assert_equal binds, queries[0][1]
39
+ end
40
+
41
+ def test_collects_nothing_if_the_statement_is_not_whitelisted
42
+ SUBSCRIBER.finish(nil, nil, name: 'SQL', sql: 'SHOW max_identifier_length')
43
+ assert queries.empty?
44
+ end
45
+
46
+ def test_collects_nothing_if_the_statement_is_only_partially_matched
47
+ SUBSCRIBER.finish(nil, nil, name: 'SQL', sql: 'select_db yo_mama')
48
+ assert queries.empty?
49
+ end
50
+
51
+ def test_collects_cte_queries
52
+ SUBSCRIBER.finish(nil, nil, name: 'SQL', sql: 'with s as (values(3)) select 1 from s')
53
+ assert_equal 1, queries.size
54
+ end
55
+
56
+ teardown do
57
+ ActiveRecord::ExplainRegistry.reset
58
+ end
59
+
60
+ def queries
61
+ ActiveRecord::ExplainRegistry.queries
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,76 @@
1
+ require 'cases/helper'
2
+ require 'models/car'
3
+ require 'active_support/core_ext/string/strip'
4
+
5
+ if ActiveRecord::Base.connection.supports_explain?
6
+ class ExplainTest < ActiveRecord::TestCase
7
+ fixtures :cars
8
+
9
+ def base
10
+ ActiveRecord::Base
11
+ end
12
+
13
+ def connection
14
+ base.connection
15
+ end
16
+
17
+ def test_relation_explain
18
+ message = Car.where(:name => 'honda').explain
19
+ assert_match(/^EXPLAIN for:/, message)
20
+ end
21
+
22
+ def test_collecting_queries_for_explain
23
+ queries = ActiveRecord::Base.collecting_queries_for_explain do
24
+ Car.where(:name => 'honda').to_a
25
+ end
26
+
27
+ sql, binds = queries[0]
28
+ assert_match "SELECT", sql
29
+ if binds.any?
30
+ assert_equal 1, binds.length
31
+ assert_equal "honda", binds.flatten.last
32
+ else
33
+ assert_match 'honda', sql
34
+ end
35
+ end
36
+
37
+ def test_exec_explain_with_no_binds
38
+ sqls = %w(foo bar)
39
+ binds = [[], []]
40
+ queries = sqls.zip(binds)
41
+
42
+ connection.stubs(:explain).returns('query plan foo', 'query plan bar')
43
+ expected = sqls.map {|sql| "EXPLAIN for: #{sql}\nquery plan #{sql}"}.join("\n")
44
+ assert_equal expected, base.exec_explain(queries)
45
+ end
46
+
47
+ def test_exec_explain_with_binds
48
+ cols = [Object.new, Object.new]
49
+ cols[0].expects(:name).returns('wadus')
50
+ cols[1].expects(:name).returns('chaflan')
51
+
52
+ sqls = %w(foo bar)
53
+ binds = [[[cols[0], 1]], [[cols[1], 2]]]
54
+ queries = sqls.zip(binds)
55
+
56
+ connection.stubs(:explain).returns("query plan foo\n", "query plan bar\n")
57
+ expected = <<-SQL.strip_heredoc
58
+ EXPLAIN for: #{sqls[0]} [["wadus", 1]]
59
+ query plan foo
60
+
61
+ EXPLAIN for: #{sqls[1]} [["chaflan", 2]]
62
+ query plan bar
63
+ SQL
64
+ assert_equal expected, base.exec_explain(queries)
65
+ end
66
+
67
+ def test_unsupported_connection_adapter
68
+ connection.stubs(:supports_explain?).returns(false)
69
+
70
+ base.logger.expects(:warn).never
71
+
72
+ Car.where(:name => 'honda').to_a
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,60 @@
1
+ require "cases/helper"
2
+ require 'models/topic'
3
+
4
+ class FinderRespondToTest < ActiveRecord::TestCase
5
+
6
+ fixtures :topics
7
+
8
+ def test_should_preserve_normal_respond_to_behaviour_on_base
9
+ assert_respond_to ActiveRecord::Base, :new
10
+ assert !ActiveRecord::Base.respond_to?(:find_by_something)
11
+ end
12
+
13
+ def test_should_preserve_normal_respond_to_behaviour_and_respond_to_newly_added_method
14
+ class << Topic; self; end.send(:define_method, :method_added_for_finder_respond_to_test) { }
15
+ assert_respond_to Topic, :method_added_for_finder_respond_to_test
16
+ ensure
17
+ class << Topic; self; end.send(:remove_method, :method_added_for_finder_respond_to_test)
18
+ end
19
+
20
+ def test_should_preserve_normal_respond_to_behaviour_and_respond_to_standard_object_method
21
+ assert_respond_to Topic, :to_s
22
+ end
23
+
24
+ def test_should_respond_to_find_by_one_attribute_before_caching
25
+ ensure_topic_method_is_not_cached(:find_by_title)
26
+ assert_respond_to Topic, :find_by_title
27
+ end
28
+
29
+ def test_should_respond_to_find_by_with_bang
30
+ ensure_topic_method_is_not_cached(:find_by_title!)
31
+ assert_respond_to Topic, :find_by_title!
32
+ end
33
+
34
+ def test_should_respond_to_find_by_two_attributes
35
+ ensure_topic_method_is_not_cached(:find_by_title_and_author_name)
36
+ assert_respond_to Topic, :find_by_title_and_author_name
37
+ end
38
+
39
+ def test_should_respond_to_find_all_by_an_aliased_attribute
40
+ ensure_topic_method_is_not_cached(:find_by_heading)
41
+ assert_respond_to Topic, :find_by_heading
42
+ end
43
+
44
+ def test_should_not_respond_to_find_by_one_missing_attribute
45
+ assert !Topic.respond_to?(:find_by_undertitle)
46
+ end
47
+
48
+ def test_should_not_respond_to_find_by_invalid_method_syntax
49
+ assert !Topic.respond_to?(:fail_to_find_by_title)
50
+ assert !Topic.respond_to?(:find_by_title?)
51
+ assert !Topic.respond_to?(:fail_to_find_or_create_by_title)
52
+ assert !Topic.respond_to?(:find_or_create_by_title?)
53
+ end
54
+
55
+ private
56
+
57
+ def ensure_topic_method_is_not_cached(method_id)
58
+ class << Topic; self; end.send(:remove_method, method_id) if Topic.public_methods.include? method_id
59
+ end
60
+ end
@@ -0,0 +1,1166 @@
1
+ require "cases/helper"
2
+ require 'models/post'
3
+ require 'models/author'
4
+ require 'models/categorization'
5
+ require 'models/comment'
6
+ require 'models/company'
7
+ require 'models/tagging'
8
+ require 'models/topic'
9
+ require 'models/reply'
10
+ require 'models/entrant'
11
+ require 'models/project'
12
+ require 'models/developer'
13
+ require 'models/computer'
14
+ require 'models/customer'
15
+ require 'models/toy'
16
+ require 'models/matey'
17
+ require 'models/dog'
18
+ require 'models/car'
19
+ require 'models/tyre'
20
+
21
+ class FinderTest < ActiveRecord::TestCase
22
+ fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors, :customers, :categories, :categorizations, :cars, :author_addresses
23
+
24
+ def test_find_by_id_with_hash
25
+ assert_raises(ActiveRecord::StatementInvalid) do
26
+ Post.find_by_id(:limit => 1)
27
+ end
28
+ end
29
+
30
+ def test_find_by_title_and_id_with_hash
31
+ assert_raises(ActiveRecord::StatementInvalid) do
32
+ Post.find_by_title_and_id('foo', :limit => 1)
33
+ end
34
+ end
35
+
36
+ def test_find
37
+ assert_equal(topics(:first).title, Topic.find(1).title)
38
+ end
39
+
40
+ def test_find_with_proc_parameter_and_block
41
+ exception = assert_raises(RuntimeError) do
42
+ Topic.all.find(-> { raise "should happen" }) { |e| e.title == "non-existing-title" }
43
+ end
44
+ assert_equal "should happen", exception.message
45
+
46
+ assert_nothing_raised(RuntimeError) do
47
+ Topic.all.find(-> { raise "should not happen" }) { |e| e.title == topics(:first).title }
48
+ end
49
+ end
50
+
51
+ def test_find_passing_active_record_object_is_deprecated
52
+ assert_deprecated do
53
+ Topic.find(Topic.last)
54
+ end
55
+ end
56
+
57
+ def test_symbols_table_ref
58
+ gc_disabled = GC.disable
59
+ Post.where("author_id" => nil) # warm up
60
+ x = Symbol.all_symbols.count
61
+ Post.where("title" => {"xxxqqqq" => "bar"})
62
+ assert_equal x, Symbol.all_symbols.count
63
+ ensure
64
+ GC.enable if gc_disabled == false
65
+ end
66
+
67
+ # find should handle strings that come from URLs
68
+ # (example: Category.find(params[:id]))
69
+ def test_find_with_string
70
+ assert_equal(Topic.find(1).title,Topic.find("1").title)
71
+ end
72
+
73
+ def test_exists
74
+ assert_equal true, Topic.exists?(1)
75
+ assert_equal true, Topic.exists?("1")
76
+ assert_equal true, Topic.exists?(title: "The First Topic")
77
+ assert_equal true, Topic.exists?(heading: "The First Topic")
78
+ assert_equal true, Topic.exists?(:author_name => "Mary", :approved => true)
79
+ assert_equal true, Topic.exists?(["parent_id = ?", 1])
80
+ assert_equal true, Topic.exists?(id: [1, 9999])
81
+
82
+ assert_equal false, Topic.exists?(45)
83
+ assert_equal false, Topic.exists?(Topic.new.id)
84
+
85
+ assert_raise(NoMethodError) { Topic.exists?([1,2]) }
86
+ end
87
+
88
+ def test_exists_with_polymorphic_relation
89
+ post = Post.create!(title: 'Post', body: 'default', taggings: [Tagging.new(comment: 'tagging comment')])
90
+ relation = Post.tagged_with_comment('tagging comment')
91
+
92
+ assert_equal true, relation.exists?(title: ['Post'])
93
+ assert_equal true, relation.exists?(['title LIKE ?', 'Post%'])
94
+ assert_equal true, relation.exists?
95
+ assert_equal true, relation.exists?(post.id)
96
+ assert_equal true, relation.exists?(post.id.to_s)
97
+
98
+ assert_equal false, relation.exists?(false)
99
+ end
100
+
101
+ def test_exists_passing_active_record_object_is_deprecated
102
+ assert_deprecated do
103
+ Topic.exists?(Topic.new)
104
+ end
105
+ end
106
+
107
+ def test_exists_fails_when_parameter_has_invalid_type
108
+ assert_raises(RangeError) do
109
+ assert_equal false, Topic.exists?(("9"*53).to_i) # number that's bigger than int
110
+ end
111
+ assert_equal false, Topic.exists?("foo")
112
+ end
113
+
114
+ def test_exists_does_not_select_columns_without_alias
115
+ assert_sql(/SELECT\W+1 AS one FROM ["`]topics["`]/i) do
116
+ Topic.exists?
117
+ end
118
+ end
119
+
120
+ def test_exists_returns_true_with_one_record_and_no_args
121
+ assert_equal true, Topic.exists?
122
+ end
123
+
124
+ def test_exists_returns_false_with_false_arg
125
+ assert_equal false, Topic.exists?(false)
126
+ end
127
+
128
+ # exists? should handle nil for id's that come from URLs and always return false
129
+ # (example: Topic.exists?(params[:id])) where params[:id] is nil
130
+ def test_exists_with_nil_arg
131
+ assert_equal false, Topic.exists?(nil)
132
+ assert_equal true, Topic.exists?
133
+
134
+ assert_equal false, Topic.first.replies.exists?(nil)
135
+ assert_equal true, Topic.first.replies.exists?
136
+ end
137
+
138
+ # ensures +exists?+ runs valid SQL by excluding order value
139
+ def test_exists_with_order
140
+ assert_equal true, Topic.order(:id).distinct.exists?
141
+ end
142
+
143
+ def test_exists_with_includes_limit_and_empty_result
144
+ assert_equal false, Topic.includes(:replies).limit(0).exists?
145
+ assert_equal false, Topic.includes(:replies).limit(1).where('0 = 1').exists?
146
+ end
147
+
148
+ def test_exists_with_distinct_association_includes_and_limit
149
+ author = Author.first
150
+ assert_equal false, author.unique_categorized_posts.includes(:special_comments).limit(0).exists?
151
+ assert_equal true, author.unique_categorized_posts.includes(:special_comments).limit(1).exists?
152
+ end
153
+
154
+ def test_exists_with_distinct_association_includes_limit_and_order
155
+ author = Author.first
156
+ assert_equal false, author.unique_categorized_posts.includes(:special_comments).order('comments.tags_count DESC').limit(0).exists?
157
+ assert_equal true, author.unique_categorized_posts.includes(:special_comments).order('comments.tags_count DESC').limit(1).exists?
158
+ end
159
+
160
+ def test_exists_with_empty_table_and_no_args_given
161
+ Topic.delete_all
162
+ assert_equal false, Topic.exists?
163
+ end
164
+
165
+ def test_exists_with_aggregate_having_three_mappings
166
+ existing_address = customers(:david).address
167
+ assert_equal true, Customer.exists?(:address => existing_address)
168
+ end
169
+
170
+ def test_exists_with_aggregate_having_three_mappings_with_one_difference
171
+ existing_address = customers(:david).address
172
+ assert_equal false, Customer.exists?(:address =>
173
+ Address.new(existing_address.street, existing_address.city, existing_address.country + "1"))
174
+ assert_equal false, Customer.exists?(:address =>
175
+ Address.new(existing_address.street, existing_address.city + "1", existing_address.country))
176
+ assert_equal false, Customer.exists?(:address =>
177
+ Address.new(existing_address.street + "1", existing_address.city, existing_address.country))
178
+ end
179
+
180
+ def test_exists_does_not_instantiate_records
181
+ Developer.expects(:instantiate).never
182
+ Developer.exists?
183
+ end
184
+
185
+ def test_find_by_array_of_one_id
186
+ assert_kind_of(Array, Topic.find([ 1 ]))
187
+ assert_equal(1, Topic.find([ 1 ]).length)
188
+ end
189
+
190
+ def test_find_by_ids
191
+ assert_equal 2, Topic.find(1, 2).size
192
+ assert_equal topics(:second).title, Topic.find([2]).first.title
193
+ end
194
+
195
+ def test_find_by_ids_with_limit_and_offset
196
+ assert_equal 2, Entrant.limit(2).find([1,3,2]).size
197
+ assert_equal 1, Entrant.limit(3).offset(2).find([1,3,2]).size
198
+
199
+ # Also test an edge case: If you have 11 results, and you set a
200
+ # limit of 3 and offset of 9, then you should find that there
201
+ # will be only 2 results, regardless of the limit.
202
+ devs = Developer.all
203
+ last_devs = Developer.limit(3).offset(9).find devs.map(&:id)
204
+ assert_equal 2, last_devs.size
205
+ end
206
+
207
+ def test_find_with_large_number
208
+ assert_raises(ActiveRecord::RecordNotFound) { Topic.find('9999999999999999999999999999999') }
209
+ end
210
+
211
+ def test_find_by_with_large_number
212
+ assert_nil Topic.find_by(id: '9999999999999999999999999999999')
213
+ end
214
+
215
+ def test_find_by_id_with_large_number
216
+ assert_nil Topic.find_by_id('9999999999999999999999999999999')
217
+ end
218
+
219
+ def test_find_on_relation_with_large_number
220
+ assert_nil Topic.where('1=1').find_by(id: 9999999999999999999999999999999)
221
+ end
222
+
223
+ def test_find_by_bang_on_relation_with_large_number
224
+ assert_raises(ActiveRecord::RecordNotFound) do
225
+ Topic.where('1=1').find_by!(id: 9999999999999999999999999999999)
226
+ end
227
+ end
228
+
229
+ def test_find_an_empty_array
230
+ assert_equal [], Topic.find([])
231
+ end
232
+
233
+ def test_find_doesnt_have_implicit_ordering
234
+ assert_sql(/^((?!ORDER).)*$/) { Topic.find(1) }
235
+ end
236
+
237
+ def test_find_by_ids_missing_one
238
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, 2, 45) }
239
+ end
240
+
241
+ def test_find_with_group_and_sanitized_having_method
242
+ developers = Developer.group(:salary).having("sum(salary) > ?", 10000).select('salary').to_a
243
+ assert_equal 3, developers.size
244
+ assert_equal 3, developers.map(&:salary).uniq.size
245
+ assert developers.all? { |developer| developer.salary > 10000 }
246
+ end
247
+
248
+ def test_find_with_entire_select_statement
249
+ topics = Topic.find_by_sql "SELECT * FROM topics WHERE author_name = 'Mary'"
250
+
251
+ assert_equal(1, topics.size)
252
+ assert_equal(topics(:second).title, topics.first.title)
253
+ end
254
+
255
+ def test_find_with_prepared_select_statement
256
+ topics = Topic.find_by_sql ["SELECT * FROM topics WHERE author_name = ?", "Mary"]
257
+
258
+ assert_equal(1, topics.size)
259
+ assert_equal(topics(:second).title, topics.first.title)
260
+ end
261
+
262
+ def test_find_by_sql_with_sti_on_joined_table
263
+ accounts = Account.find_by_sql("SELECT * FROM accounts INNER JOIN companies ON companies.id = accounts.firm_id")
264
+ assert_equal [Account], accounts.collect(&:class).uniq
265
+ end
266
+
267
+ def test_take
268
+ assert_equal topics(:first), Topic.take
269
+ end
270
+
271
+ def test_take_failing
272
+ assert_nil Topic.where("title = 'This title does not exist'").take
273
+ end
274
+
275
+ def test_take_bang_present
276
+ assert_nothing_raised do
277
+ assert_equal topics(:second), Topic.where("title = 'The Second Topic of the day'").take!
278
+ end
279
+ end
280
+
281
+ def test_take_bang_missing
282
+ assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
283
+ Topic.where("title = 'This title does not exist'").take!
284
+ end
285
+ end
286
+
287
+ def test_first
288
+ assert_equal topics(:second).title, Topic.where("title = 'The Second Topic of the day'").first.title
289
+ end
290
+
291
+ def test_first_failing
292
+ assert_nil Topic.where("title = 'The Second Topic of the day!'").first
293
+ end
294
+
295
+ def test_first_bang_present
296
+ assert_nothing_raised do
297
+ assert_equal topics(:second), Topic.where("title = 'The Second Topic of the day'").first!
298
+ end
299
+ end
300
+
301
+ def test_first_bang_missing
302
+ assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
303
+ Topic.where("title = 'This title does not exist'").first!
304
+ end
305
+ end
306
+
307
+ def test_first_have_primary_key_order_by_default
308
+ expected = topics(:first)
309
+ expected.touch # PostgreSQL changes the default order if no order clause is used
310
+ assert_equal expected, Topic.first
311
+ end
312
+
313
+ def test_model_class_responds_to_first_bang
314
+ assert Topic.first!
315
+ Topic.delete_all
316
+ assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
317
+ Topic.first!
318
+ end
319
+ end
320
+
321
+ def test_second
322
+ assert_equal topics(:second).title, Topic.second.title
323
+ end
324
+
325
+ def test_second_with_offset
326
+ assert_equal topics(:fifth), Topic.offset(3).second
327
+ end
328
+
329
+ def test_second_have_primary_key_order_by_default
330
+ expected = topics(:second)
331
+ expected.touch # PostgreSQL changes the default order if no order clause is used
332
+ assert_equal expected, Topic.second
333
+ end
334
+
335
+ def test_model_class_responds_to_second_bang
336
+ assert Topic.second!
337
+ Topic.delete_all
338
+ assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
339
+ Topic.second!
340
+ end
341
+ end
342
+
343
+ def test_third
344
+ assert_equal topics(:third).title, Topic.third.title
345
+ end
346
+
347
+ def test_third_with_offset
348
+ assert_equal topics(:fifth), Topic.offset(2).third
349
+ end
350
+
351
+ def test_third_have_primary_key_order_by_default
352
+ expected = topics(:third)
353
+ expected.touch # PostgreSQL changes the default order if no order clause is used
354
+ assert_equal expected, Topic.third
355
+ end
356
+
357
+ def test_model_class_responds_to_third_bang
358
+ assert Topic.third!
359
+ Topic.delete_all
360
+ assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
361
+ Topic.third!
362
+ end
363
+ end
364
+
365
+ def test_fourth
366
+ assert_equal topics(:fourth).title, Topic.fourth.title
367
+ end
368
+
369
+ def test_fourth_with_offset
370
+ assert_equal topics(:fifth), Topic.offset(1).fourth
371
+ end
372
+
373
+ def test_fourth_have_primary_key_order_by_default
374
+ expected = topics(:fourth)
375
+ expected.touch # PostgreSQL changes the default order if no order clause is used
376
+ assert_equal expected, Topic.fourth
377
+ end
378
+
379
+ def test_model_class_responds_to_fourth_bang
380
+ assert Topic.fourth!
381
+ Topic.delete_all
382
+ assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
383
+ Topic.fourth!
384
+ end
385
+ end
386
+
387
+ def test_fifth
388
+ assert_equal topics(:fifth).title, Topic.fifth.title
389
+ end
390
+
391
+ def test_fifth_with_offset
392
+ assert_equal topics(:fifth), Topic.offset(0).fifth
393
+ end
394
+
395
+ def test_fifth_have_primary_key_order_by_default
396
+ expected = topics(:fifth)
397
+ expected.touch # PostgreSQL changes the default order if no order clause is used
398
+ assert_equal expected, Topic.fifth
399
+ end
400
+
401
+ def test_model_class_responds_to_fifth_bang
402
+ assert Topic.fifth!
403
+ Topic.delete_all
404
+ assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
405
+ Topic.fifth!
406
+ end
407
+ end
408
+
409
+ def test_last_bang_present
410
+ assert_nothing_raised do
411
+ assert_equal topics(:second), Topic.where("title = 'The Second Topic of the day'").last!
412
+ end
413
+ end
414
+
415
+ def test_last_bang_missing
416
+ assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
417
+ Topic.where("title = 'This title does not exist'").last!
418
+ end
419
+ end
420
+
421
+ def test_model_class_responds_to_last_bang
422
+ assert_equal topics(:fifth), Topic.last!
423
+ assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
424
+ Topic.delete_all
425
+ Topic.last!
426
+ end
427
+ end
428
+
429
+ def test_take_and_first_and_last_with_integer_should_use_sql_limit
430
+ assert_sql(/LIMIT 3|ROWNUM <= 3/) { Topic.take(3).entries }
431
+ assert_sql(/LIMIT 2|ROWNUM <= 2/) { Topic.first(2).entries }
432
+ assert_sql(/LIMIT 5|ROWNUM <= 5/) { Topic.last(5).entries }
433
+ end
434
+
435
+ def test_last_with_integer_and_order_should_keep_the_order
436
+ assert_equal Topic.order("title").to_a.last(2), Topic.order("title").last(2)
437
+ end
438
+
439
+ def test_last_with_integer_and_order_should_not_use_sql_limit
440
+ query = assert_sql { Topic.order("title").last(5).entries }
441
+ assert_equal 1, query.length
442
+ assert_no_match(/LIMIT/, query.first)
443
+ end
444
+
445
+ def test_last_with_integer_and_reorder_should_not_use_sql_limit
446
+ query = assert_sql { Topic.reorder("title").last(5).entries }
447
+ assert_equal 1, query.length
448
+ assert_no_match(/LIMIT/, query.first)
449
+ end
450
+
451
+ def test_take_and_first_and_last_with_integer_should_return_an_array
452
+ assert_kind_of Array, Topic.take(5)
453
+ assert_kind_of Array, Topic.first(5)
454
+ assert_kind_of Array, Topic.last(5)
455
+ end
456
+
457
+ def test_unexisting_record_exception_handling
458
+ assert_raise(ActiveRecord::RecordNotFound) {
459
+ Topic.find(1).parent
460
+ }
461
+
462
+ Topic.find(2).topic
463
+ end
464
+
465
+ def test_find_only_some_columns
466
+ topic = Topic.select("author_name").find(1)
467
+ assert_raise(ActiveModel::MissingAttributeError) {topic.title}
468
+ assert_raise(ActiveModel::MissingAttributeError) {topic.title?}
469
+ assert_nil topic.read_attribute("title")
470
+ assert_equal "David", topic.author_name
471
+ assert !topic.attribute_present?("title")
472
+ assert !topic.attribute_present?(:title)
473
+ assert topic.attribute_present?("author_name")
474
+ assert_respond_to topic, "author_name"
475
+ end
476
+
477
+ def test_find_on_array_conditions
478
+ assert Topic.where(["approved = ?", false]).find(1)
479
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.where(["approved = ?", true]).find(1) }
480
+ end
481
+
482
+ def test_find_on_hash_conditions
483
+ assert Topic.where(approved: false).find(1)
484
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.where(approved: true).find(1) }
485
+ end
486
+
487
+ def test_find_on_hash_conditions_with_explicit_table_name
488
+ assert Topic.where('topics.approved' => false).find(1)
489
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.where('topics.approved' => true).find(1) }
490
+ end
491
+
492
+ def test_find_on_hash_conditions_with_hashed_table_name
493
+ assert Topic.where(topics: { approved: false }).find(1)
494
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.where(topics: { approved: true }).find(1) }
495
+ end
496
+
497
+ def test_find_with_hash_conditions_on_joined_table
498
+ firms = Firm.joins(:account).where(:accounts => { :credit_limit => 50 })
499
+ assert_equal 1, firms.size
500
+ assert_equal companies(:first_firm), firms.first
501
+ end
502
+
503
+ def test_find_with_hash_conditions_on_joined_table_and_with_range
504
+ firms = DependentFirm.joins(:account).where(name: 'RailsCore', accounts: { credit_limit: 55..60 })
505
+ assert_equal 1, firms.size
506
+ assert_equal companies(:rails_core), firms.first
507
+ end
508
+
509
+ def test_find_on_hash_conditions_with_explicit_table_name_and_aggregate
510
+ david = customers(:david)
511
+ assert Customer.where('customers.name' => david.name, :address => david.address).find(david.id)
512
+ assert_raise(ActiveRecord::RecordNotFound) {
513
+ Customer.where('customers.name' => david.name + "1", :address => david.address).find(david.id)
514
+ }
515
+ end
516
+
517
+ def test_find_on_association_proxy_conditions
518
+ assert_equal [1, 2, 3, 5, 6, 7, 8, 9, 10, 12], Comment.where(post_id: authors(:david).posts).map(&:id).sort
519
+ end
520
+
521
+ def test_find_on_hash_conditions_with_range
522
+ assert_equal [1,2], Topic.where(id: 1..2).to_a.map(&:id).sort
523
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.where(id: 2..3).find(1) }
524
+ end
525
+
526
+ def test_find_on_hash_conditions_with_end_exclusive_range
527
+ assert_equal [1,2,3], Topic.where(id: 1..3).to_a.map(&:id).sort
528
+ assert_equal [1,2], Topic.where(id: 1...3).to_a.map(&:id).sort
529
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.where(id: 2...3).find(3) }
530
+ end
531
+
532
+ def test_find_on_hash_conditions_with_multiple_ranges
533
+ assert_equal [1,2,3], Comment.where(id: 1..3, post_id: 1..2).to_a.map(&:id).sort
534
+ assert_equal [1], Comment.where(id: 1..1, post_id: 1..10).to_a.map(&:id).sort
535
+ end
536
+
537
+ def test_find_on_hash_conditions_with_array_of_integers_and_ranges
538
+ assert_equal [1,2,3,5,6,7,8,9], Comment.where(id: [1..2, 3, 5, 6..8, 9]).to_a.map(&:id).sort
539
+ end
540
+
541
+ def test_find_on_hash_conditions_with_array_of_ranges
542
+ assert_equal [1,2,6,7,8], Comment.where(id: [1..2, 6..8]).to_a.map(&:id).sort
543
+ end
544
+
545
+ def test_find_on_hash_conditions_with_nested_array_of_integers_and_ranges
546
+ assert_deprecated do
547
+ assert_equal [1,2,3,5,6,7,8,9], Comment.where(id: [[1..2], 3, [5], 6..8, 9]).to_a.map(&:id).sort
548
+ end
549
+ end
550
+
551
+ def test_find_on_hash_conditions_with_array_of_integers_and_arrays
552
+ assert_deprecated do
553
+ assert_equal [1,2,3,5,6,7,8,9], Comment.where(id: [[1, 2], 3, 5, [6, [7], 8], 9]).to_a.map(&:id).sort
554
+ end
555
+ end
556
+
557
+ def test_find_on_hash_conditions_with_nested_array_of_integers_and_ranges_and_nils
558
+ assert_deprecated do
559
+ assert_equal [1,3,4,5], Topic.where(parent_id: [[2..6], nil]).to_a.map(&:id).sort
560
+ end
561
+ end
562
+
563
+ def test_find_on_hash_conditions_with_nested_array_of_integers_and_ranges_and_more_nils
564
+ assert_deprecated do
565
+ assert_equal [], Topic.where(parent_id: [[7..10, nil, [nil]], [nil]]).to_a.map(&:id).sort
566
+ end
567
+ end
568
+
569
+ def test_find_on_multiple_hash_conditions
570
+ assert Topic.where(author_name: "David", title: "The First Topic", replies_count: 1, approved: false).find(1)
571
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.where(author_name: "David", title: "The First Topic", replies_count: 1, approved: true).find(1) }
572
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.where(author_name: "David", title: "HHC", replies_count: 1, approved: false).find(1) }
573
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.where(author_name: "David", title: "The First Topic", replies_count: 1, approved: true).find(1) }
574
+ end
575
+
576
+ def test_condition_interpolation
577
+ assert_kind_of Firm, Company.where("name = '%s'", "37signals").first
578
+ assert_nil Company.where(["name = '%s'", "37signals!"]).first
579
+ assert_nil Company.where(["name = '%s'", "37signals!' OR 1=1"]).first
580
+ assert_kind_of Time, Topic.where(["id = %d", 1]).first.written_on
581
+ end
582
+
583
+ def test_condition_array_interpolation
584
+ assert_kind_of Firm, Company.where(["name = '%s'", "37signals"]).first
585
+ assert_nil Company.where(["name = '%s'", "37signals!"]).first
586
+ assert_nil Company.where(["name = '%s'", "37signals!' OR 1=1"]).first
587
+ assert_kind_of Time, Topic.where(["id = %d", 1]).first.written_on
588
+ end
589
+
590
+ def test_condition_hash_interpolation
591
+ assert_kind_of Firm, Company.where(name: "37signals").first
592
+ assert_nil Company.where(name: "37signals!").first
593
+ assert_kind_of Time, Topic.where(id: 1).first.written_on
594
+ end
595
+
596
+ def test_hash_condition_find_malformed
597
+ assert_raise(ActiveRecord::StatementInvalid) {
598
+ Company.where(id: 2, dhh: true).first
599
+ }
600
+ end
601
+
602
+ def test_hash_condition_find_with_escaped_characters
603
+ Company.create("name" => "Ain't noth'n like' \#stuff")
604
+ assert Company.where(name: "Ain't noth'n like' \#stuff").first
605
+ end
606
+
607
+ def test_hash_condition_find_with_array
608
+ p1, p2 = Post.limit(2).order('id asc').to_a
609
+ assert_equal [p1, p2], Post.where(id: [p1, p2]).order('id asc').to_a
610
+ assert_equal [p1, p2], Post.where(id: [p1, p2.id]).order('id asc').to_a
611
+ end
612
+
613
+ def test_hash_condition_find_with_nil
614
+ topic = Topic.where(last_read: nil).first
615
+ assert_not_nil topic
616
+ assert_nil topic.last_read
617
+ end
618
+
619
+ def test_hash_condition_find_with_aggregate_having_one_mapping
620
+ balance = customers(:david).balance
621
+ assert_kind_of Money, balance
622
+ found_customer = Customer.where(:balance => balance).first
623
+ assert_equal customers(:david), found_customer
624
+ end
625
+
626
+ def test_hash_condition_find_with_aggregate_attribute_having_same_name_as_field_and_key_value_being_aggregate
627
+ gps_location = customers(:david).gps_location
628
+ assert_kind_of GpsLocation, gps_location
629
+ found_customer = Customer.where(:gps_location => gps_location).first
630
+ assert_equal customers(:david), found_customer
631
+ end
632
+
633
+ def test_hash_condition_find_with_aggregate_having_one_mapping_and_key_value_being_attribute_value
634
+ balance = customers(:david).balance
635
+ assert_kind_of Money, balance
636
+ found_customer = Customer.where(:balance => balance.amount).first
637
+ assert_equal customers(:david), found_customer
638
+ end
639
+
640
+ def test_hash_condition_find_with_aggregate_attribute_having_same_name_as_field_and_key_value_being_attribute_value
641
+ gps_location = customers(:david).gps_location
642
+ assert_kind_of GpsLocation, gps_location
643
+ found_customer = Customer.where(:gps_location => gps_location.gps_location).first
644
+ assert_equal customers(:david), found_customer
645
+ end
646
+
647
+ def test_hash_condition_find_with_aggregate_having_three_mappings
648
+ address = customers(:david).address
649
+ assert_kind_of Address, address
650
+ found_customer = Customer.where(:address => address).first
651
+ assert_equal customers(:david), found_customer
652
+ end
653
+
654
+ def test_hash_condition_find_with_one_condition_being_aggregate_and_another_not
655
+ address = customers(:david).address
656
+ assert_kind_of Address, address
657
+ found_customer = Customer.where(:address => address, :name => customers(:david).name).first
658
+ assert_equal customers(:david), found_customer
659
+ end
660
+
661
+ def test_condition_utc_time_interpolation_with_default_timezone_local
662
+ with_env_tz 'America/New_York' do
663
+ with_timezone_config default: :local do
664
+ topic = Topic.first
665
+ assert_equal topic, Topic.where(['written_on = ?', topic.written_on.getutc]).first
666
+ end
667
+ end
668
+ end
669
+
670
+ def test_hash_condition_utc_time_interpolation_with_default_timezone_local
671
+ with_env_tz 'America/New_York' do
672
+ with_timezone_config default: :local do
673
+ topic = Topic.first
674
+ assert_equal topic, Topic.where(written_on: topic.written_on.getutc).first
675
+ end
676
+ end
677
+ end
678
+
679
+ def test_condition_local_time_interpolation_with_default_timezone_utc
680
+ with_env_tz 'America/New_York' do
681
+ with_timezone_config default: :utc do
682
+ topic = Topic.first
683
+ assert_equal topic, Topic.where(['written_on = ?', topic.written_on.getlocal]).first
684
+ end
685
+ end
686
+ end
687
+
688
+ def test_hash_condition_local_time_interpolation_with_default_timezone_utc
689
+ with_env_tz 'America/New_York' do
690
+ with_timezone_config default: :utc do
691
+ topic = Topic.first
692
+ assert_equal topic, Topic.where(written_on: topic.written_on.getlocal).first
693
+ end
694
+ end
695
+ end
696
+
697
+ def test_bind_variables
698
+ assert_kind_of Firm, Company.where(["name = ?", "37signals"]).first
699
+ assert_nil Company.where(["name = ?", "37signals!"]).first
700
+ assert_nil Company.where(["name = ?", "37signals!' OR 1=1"]).first
701
+ assert_kind_of Time, Topic.where(["id = ?", 1]).first.written_on
702
+ assert_raise(ActiveRecord::PreparedStatementInvalid) {
703
+ Company.where(["id=? AND name = ?", 2]).first
704
+ }
705
+ assert_raise(ActiveRecord::PreparedStatementInvalid) {
706
+ Company.where(["id=?", 2, 3, 4]).first
707
+ }
708
+ end
709
+
710
+ def test_bind_variables_with_quotes
711
+ Company.create("name" => "37signals' go'es agains")
712
+ assert Company.where(["name = ?", "37signals' go'es agains"]).first
713
+ end
714
+
715
+ def test_named_bind_variables_with_quotes
716
+ Company.create("name" => "37signals' go'es agains")
717
+ assert Company.where(["name = :name", {name: "37signals' go'es agains"}]).first
718
+ end
719
+
720
+ def test_bind_arity
721
+ assert_nothing_raised { bind '' }
722
+ assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '', 1 }
723
+
724
+ assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?' }
725
+ assert_nothing_raised { bind '?', 1 }
726
+ assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?', 1, 1 }
727
+ end
728
+
729
+ def test_named_bind_variables
730
+ assert_equal '1', bind(':a', :a => 1) # ' ruby-mode
731
+ assert_equal '1 1', bind(':a :a', :a => 1) # ' ruby-mode
732
+
733
+ assert_nothing_raised { bind("'+00:00'", :foo => "bar") }
734
+
735
+ assert_kind_of Firm, Company.where(["name = :name", { name: "37signals" }]).first
736
+ assert_nil Company.where(["name = :name", { name: "37signals!" }]).first
737
+ assert_nil Company.where(["name = :name", { name: "37signals!' OR 1=1" }]).first
738
+ assert_kind_of Time, Topic.where(["id = :id", { id: 1 }]).first.written_on
739
+ end
740
+
741
+ class SimpleEnumerable
742
+ include Enumerable
743
+
744
+ def initialize(ary)
745
+ @ary = ary
746
+ end
747
+
748
+ def each(&b)
749
+ @ary.each(&b)
750
+ end
751
+ end
752
+
753
+ def test_bind_enumerable
754
+ quoted_abc = %(#{ActiveRecord::Base.connection.quote('a')},#{ActiveRecord::Base.connection.quote('b')},#{ActiveRecord::Base.connection.quote('c')})
755
+
756
+ assert_equal '1,2,3', bind('?', [1, 2, 3])
757
+ assert_equal quoted_abc, bind('?', %w(a b c))
758
+
759
+ assert_equal '1,2,3', bind(':a', :a => [1, 2, 3])
760
+ assert_equal quoted_abc, bind(':a', :a => %w(a b c)) # '
761
+
762
+ assert_equal '1,2,3', bind('?', SimpleEnumerable.new([1, 2, 3]))
763
+ assert_equal quoted_abc, bind('?', SimpleEnumerable.new(%w(a b c)))
764
+
765
+ assert_equal '1,2,3', bind(':a', :a => SimpleEnumerable.new([1, 2, 3]))
766
+ assert_equal quoted_abc, bind(':a', :a => SimpleEnumerable.new(%w(a b c))) # '
767
+ end
768
+
769
+ def test_bind_empty_enumerable
770
+ quoted_nil = ActiveRecord::Base.connection.quote(nil)
771
+ assert_equal quoted_nil, bind('?', [])
772
+ assert_equal " in (#{quoted_nil})", bind(' in (?)', [])
773
+ assert_equal "foo in (#{quoted_nil})", bind('foo in (?)', [])
774
+ end
775
+
776
+ def test_bind_empty_string
777
+ quoted_empty = ActiveRecord::Base.connection.quote('')
778
+ assert_equal quoted_empty, bind('?', '')
779
+ end
780
+
781
+ def test_bind_chars
782
+ quoted_bambi = ActiveRecord::Base.connection.quote("Bambi")
783
+ quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote("Bambi\nand\nThumper")
784
+ assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi")
785
+ assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper")
786
+ assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi".mb_chars)
787
+ assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper".mb_chars)
788
+ end
789
+
790
+ def test_bind_record
791
+ o = Struct.new(:quoted_id).new(1)
792
+ assert_equal '1', bind('?', o)
793
+
794
+ os = [o] * 3
795
+ assert_equal '1,1,1', bind('?', os)
796
+ end
797
+
798
+ def test_named_bind_with_postgresql_type_casts
799
+ l = Proc.new { bind(":a::integer '2009-01-01'::date", :a => '10') }
800
+ assert_nothing_raised(&l)
801
+ assert_equal "#{ActiveRecord::Base.connection.quote('10')}::integer '2009-01-01'::date", l.call
802
+ end
803
+
804
+ def test_string_sanitation
805
+ assert_not_equal "'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1")
806
+ assert_equal "'something; select table'", ActiveRecord::Base.sanitize("something; select table")
807
+ end
808
+
809
+ def test_count_by_sql
810
+ assert_equal(0, Entrant.count_by_sql("SELECT COUNT(*) FROM entrants WHERE id > 3"))
811
+ assert_equal(1, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 2]))
812
+ assert_equal(2, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 1]))
813
+ end
814
+
815
+ def test_find_by_one_attribute
816
+ assert_equal topics(:first), Topic.find_by_title("The First Topic")
817
+ assert_nil Topic.find_by_title("The First Topic!")
818
+ end
819
+
820
+ def test_find_by_one_attribute_bang
821
+ assert_equal topics(:first), Topic.find_by_title!("The First Topic")
822
+ assert_raises_with_message(ActiveRecord::RecordNotFound, "Couldn't find Topic") do
823
+ Topic.find_by_title!("The First Topic!")
824
+ end
825
+ end
826
+
827
+ def test_find_by_on_attribute_that_is_a_reserved_word
828
+ dog_alias = 'Dog'
829
+ dog = Dog.create(alias: dog_alias)
830
+
831
+ assert_equal dog, Dog.find_by_alias(dog_alias)
832
+ end
833
+
834
+ def test_find_by_one_attribute_that_is_an_alias
835
+ assert_equal topics(:first), Topic.find_by_heading("The First Topic")
836
+ assert_nil Topic.find_by_heading("The First Topic!")
837
+ end
838
+
839
+ def test_find_by_one_attribute_bang_with_blank_defined
840
+ blank_topic = BlankTopic.create(title: "The Blank One")
841
+ assert_equal blank_topic, BlankTopic.find_by_title!("The Blank One")
842
+ end
843
+
844
+ def test_find_by_one_attribute_with_conditions
845
+ assert_equal accounts(:rails_core_account), Account.where('firm_id = ?', 6).find_by_credit_limit(50)
846
+ end
847
+
848
+ def test_find_by_one_attribute_that_is_an_aggregate
849
+ address = customers(:david).address
850
+ assert_kind_of Address, address
851
+ found_customer = Customer.find_by_address(address)
852
+ assert_equal customers(:david), found_customer
853
+ end
854
+
855
+ def test_find_by_one_attribute_that_is_an_aggregate_with_one_attribute_difference
856
+ address = customers(:david).address
857
+ assert_kind_of Address, address
858
+ missing_address = Address.new(address.street, address.city, address.country + "1")
859
+ assert_nil Customer.find_by_address(missing_address)
860
+ missing_address = Address.new(address.street, address.city + "1", address.country)
861
+ assert_nil Customer.find_by_address(missing_address)
862
+ missing_address = Address.new(address.street + "1", address.city, address.country)
863
+ assert_nil Customer.find_by_address(missing_address)
864
+ end
865
+
866
+ def test_find_by_two_attributes_that_are_both_aggregates
867
+ balance = customers(:david).balance
868
+ address = customers(:david).address
869
+ assert_kind_of Money, balance
870
+ assert_kind_of Address, address
871
+ found_customer = Customer.find_by_balance_and_address(balance, address)
872
+ assert_equal customers(:david), found_customer
873
+ end
874
+
875
+ def test_find_by_two_attributes_with_one_being_an_aggregate
876
+ balance = customers(:david).balance
877
+ assert_kind_of Money, balance
878
+ found_customer = Customer.find_by_balance_and_name(balance, customers(:david).name)
879
+ assert_equal customers(:david), found_customer
880
+ end
881
+
882
+ def test_dynamic_finder_on_one_attribute_with_conditions_returns_same_results_after_caching
883
+ # ensure this test can run independently of order
884
+ class << Account; self; end.send(:remove_method, :find_by_credit_limit) if Account.public_methods.include?(:find_by_credit_limit)
885
+ a = Account.where('firm_id = ?', 6).find_by_credit_limit(50)
886
+ assert_equal a, Account.where('firm_id = ?', 6).find_by_credit_limit(50) # find_by_credit_limit has been cached
887
+ end
888
+
889
+ def test_find_by_one_attribute_with_several_options
890
+ assert_equal accounts(:unknown), Account.order('id DESC').where('id != ?', 3).find_by_credit_limit(50)
891
+ end
892
+
893
+ def test_find_by_one_missing_attribute
894
+ assert_raise(NoMethodError) { Topic.find_by_undertitle("The First Topic!") }
895
+ end
896
+
897
+ def test_find_by_invalid_method_syntax
898
+ assert_raise(NoMethodError) { Topic.fail_to_find_by_title("The First Topic") }
899
+ assert_raise(NoMethodError) { Topic.find_by_title?("The First Topic") }
900
+ assert_raise(NoMethodError) { Topic.fail_to_find_or_create_by_title("Nonexistent Title") }
901
+ assert_raise(NoMethodError) { Topic.find_or_create_by_title?("Nonexistent Title") }
902
+ end
903
+
904
+ def test_find_by_two_attributes
905
+ assert_equal topics(:first), Topic.find_by_title_and_author_name("The First Topic", "David")
906
+ assert_nil Topic.find_by_title_and_author_name("The First Topic", "Mary")
907
+ end
908
+
909
+ def test_find_by_two_attributes_but_passing_only_one
910
+ assert_raise(ArgumentError) { Topic.find_by_title_and_author_name("The First Topic") }
911
+ end
912
+
913
+ def test_find_last_with_offset
914
+ devs = Developer.order('id')
915
+
916
+ assert_equal devs[2], Developer.offset(2).first
917
+ assert_equal devs[-3], Developer.offset(2).last
918
+ assert_equal devs[-3], Developer.offset(2).last
919
+ assert_equal devs[-3], Developer.offset(2).order('id DESC').first
920
+ end
921
+
922
+ def test_find_by_nil_attribute
923
+ topic = Topic.find_by_last_read nil
924
+ assert_not_nil topic
925
+ assert_nil topic.last_read
926
+ end
927
+
928
+ def test_find_by_nil_and_not_nil_attributes
929
+ topic = Topic.find_by_last_read_and_author_name nil, "Mary"
930
+ assert_equal "Mary", topic.author_name
931
+ end
932
+
933
+ def test_find_with_bad_sql
934
+ assert_raise(ActiveRecord::StatementInvalid) { Topic.find_by_sql "select 1 from badtable" }
935
+ end
936
+
937
+ def test_find_all_with_join
938
+ developers_on_project_one = Developer.
939
+ joins('LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id').
940
+ where('project_id=1').to_a
941
+ assert_equal 3, developers_on_project_one.length
942
+ developer_names = developers_on_project_one.map { |d| d.name }
943
+ assert developer_names.include?('David')
944
+ assert developer_names.include?('Jamis')
945
+ end
946
+
947
+ def test_joins_dont_clobber_id
948
+ first = Firm.
949
+ joins('INNER JOIN companies clients ON clients.firm_id = companies.id').
950
+ where('companies.id = 1').first
951
+ assert_equal 1, first.id
952
+ end
953
+
954
+ def test_joins_with_string_array
955
+ person_with_reader_and_post = Post.
956
+ joins(["INNER JOIN categorizations ON categorizations.post_id = posts.id",
957
+ "INNER JOIN categories ON categories.id = categorizations.category_id AND categories.type = 'SpecialCategory'"
958
+ ])
959
+ assert_equal 1, person_with_reader_and_post.size
960
+ end
961
+
962
+ def test_find_by_id_with_conditions_with_or
963
+ assert_nothing_raised do
964
+ Post.where("posts.id <= 3 OR posts.#{QUOTED_TYPE} = 'Post'").find([1,2,3])
965
+ end
966
+ end
967
+
968
+ # http://dev.rubyonrails.org/ticket/6778
969
+ def test_find_ignores_previously_inserted_record
970
+ Post.create!(:title => 'test', :body => 'it out')
971
+ assert_equal [], Post.where(id: nil)
972
+ end
973
+
974
+ def test_find_by_empty_ids
975
+ assert_equal [], Post.find([])
976
+ end
977
+
978
+ def test_find_by_empty_in_condition
979
+ assert_equal [], Post.where('id in (?)', [])
980
+ end
981
+
982
+ def test_find_by_records
983
+ p1, p2 = Post.limit(2).order('id asc').to_a
984
+ assert_equal [p1, p2], Post.where(['id in (?)', [p1, p2]]).order('id asc')
985
+ assert_equal [p1, p2], Post.where(['id in (?)', [p1, p2.id]]).order('id asc')
986
+ end
987
+
988
+ def test_select_value
989
+ assert_equal "37signals", Company.connection.select_value("SELECT name FROM companies WHERE id = 1")
990
+ assert_nil Company.connection.select_value("SELECT name FROM companies WHERE id = -1")
991
+ # make sure we didn't break count...
992
+ assert_equal 0, Company.count_by_sql("SELECT COUNT(*) FROM companies WHERE name = 'Halliburton'")
993
+ assert_equal 1, Company.count_by_sql("SELECT COUNT(*) FROM companies WHERE name = '37signals'")
994
+ end
995
+
996
+ def test_select_values
997
+ assert_equal ["1","2","3","4","5","6","7","8","9", "10", "11"], Company.connection.select_values("SELECT id FROM companies ORDER BY id").map! { |i| i.to_s }
998
+ assert_equal ["37signals","Summit","Microsoft", "Flamboyant Software", "Ex Nihilo", "RailsCore", "Leetsoft", "Jadedpixel", "Odegy", "Ex Nihilo Part Deux", "Apex"], Company.connection.select_values("SELECT name FROM companies ORDER BY id")
999
+ end
1000
+
1001
+ def test_select_rows
1002
+ assert_equal(
1003
+ [["1", "1", nil, "37signals"],
1004
+ ["2", "1", "2", "Summit"],
1005
+ ["3", "1", "1", "Microsoft"]],
1006
+ Company.connection.select_rows("SELECT id, firm_id, client_of, name FROM companies WHERE id IN (1,2,3) ORDER BY id").map! {|i| i.map! {|j| j.to_s unless j.nil?}})
1007
+ assert_equal [["1", "37signals"], ["2", "Summit"], ["3", "Microsoft"]],
1008
+ Company.connection.select_rows("SELECT id, name FROM companies WHERE id IN (1,2,3) ORDER BY id").map! {|i| i.map! {|j| j.to_s unless j.nil?}}
1009
+ end
1010
+
1011
+ def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct
1012
+ assert_equal 2, Post.includes(authors: :author_address).order('author_addresses.id DESC ').limit(2).to_a.size
1013
+
1014
+ assert_equal 3, Post.includes(author: :author_address, authors: :author_address).
1015
+ order('author_addresses_authors.id DESC ').limit(3).to_a.size
1016
+ end
1017
+
1018
+ def test_find_with_nil_inside_set_passed_for_one_attribute
1019
+ client_of = Company.
1020
+ where(client_of: [2, 1, nil],
1021
+ name: ['37signals', 'Summit', 'Microsoft']).
1022
+ order('client_of DESC').
1023
+ map { |x| x.client_of }
1024
+
1025
+ assert client_of.include?(nil)
1026
+ assert_equal [2, 1].sort, client_of.compact.sort
1027
+ end
1028
+
1029
+ def test_find_with_nil_inside_set_passed_for_attribute
1030
+ client_of = Company.
1031
+ where(client_of: [nil]).
1032
+ order('client_of DESC').
1033
+ map { |x| x.client_of }
1034
+
1035
+ assert_equal [], client_of.compact
1036
+ end
1037
+
1038
+ def test_with_limiting_with_custom_select
1039
+ posts = Post.references(:authors).merge(
1040
+ :includes => :author, :select => 'posts.*, authors.id as "author_id"',
1041
+ :limit => 3, :order => 'posts.id'
1042
+ ).to_a
1043
+ assert_equal 3, posts.size
1044
+ assert_equal [0, 1, 1], posts.map(&:author_id).sort
1045
+ end
1046
+
1047
+ def test_find_one_message_with_custom_primary_key
1048
+ table_with_custom_primary_key do |model|
1049
+ model.primary_key = :name
1050
+ e = assert_raises(ActiveRecord::RecordNotFound) do
1051
+ model.find 'Hello World!'
1052
+ end
1053
+ assert_equal %Q{Couldn't find MercedesCar with 'name'=Hello World!}, e.message
1054
+ end
1055
+ end
1056
+
1057
+ def test_find_some_message_with_custom_primary_key
1058
+ table_with_custom_primary_key do |model|
1059
+ model.primary_key = :name
1060
+ e = assert_raises(ActiveRecord::RecordNotFound) do
1061
+ model.find 'Hello', 'World!'
1062
+ end
1063
+ assert_equal %Q{Couldn't find all MercedesCars with 'name': (Hello, World!) (found 0 results, but was looking for 2)}, e.message
1064
+ end
1065
+ end
1066
+
1067
+ def test_find_without_primary_key
1068
+ assert_raises(ActiveRecord::UnknownPrimaryKey) do
1069
+ Matey.find(1)
1070
+ end
1071
+ end
1072
+
1073
+ def test_finder_with_offset_string
1074
+ assert_nothing_raised(ActiveRecord::StatementInvalid) { Topic.offset("3").to_a }
1075
+ end
1076
+
1077
+ test "find_by with hash conditions returns the first matching record" do
1078
+ assert_equal posts(:eager_other), Post.find_by(id: posts(:eager_other).id)
1079
+ end
1080
+
1081
+ test "find_by with non-hash conditions returns the first matching record" do
1082
+ assert_equal posts(:eager_other), Post.find_by("id = #{posts(:eager_other).id}")
1083
+ end
1084
+
1085
+ test "find_by with multi-arg conditions returns the first matching record" do
1086
+ assert_equal posts(:eager_other), Post.find_by('id = ?', posts(:eager_other).id)
1087
+ end
1088
+
1089
+ test "find_by returns nil if the record is missing" do
1090
+ assert_equal nil, Post.find_by("1 = 0")
1091
+ end
1092
+
1093
+ test "find_by with associations" do
1094
+ assert_equal authors(:david), Post.find_by(author: authors(:david)).author
1095
+ assert_equal authors(:mary) , Post.find_by(author: authors(:mary) ).author
1096
+ end
1097
+
1098
+ test "find_by doesn't have implicit ordering" do
1099
+ assert_sql(/^((?!ORDER).)*$/) { Post.find_by(id: posts(:eager_other).id) }
1100
+ end
1101
+
1102
+ test "find_by! with hash conditions returns the first matching record" do
1103
+ assert_equal posts(:eager_other), Post.find_by!(id: posts(:eager_other).id)
1104
+ end
1105
+
1106
+ test "find_by! with non-hash conditions returns the first matching record" do
1107
+ assert_equal posts(:eager_other), Post.find_by!("id = #{posts(:eager_other).id}")
1108
+ end
1109
+
1110
+ test "find_by! with multi-arg conditions returns the first matching record" do
1111
+ assert_equal posts(:eager_other), Post.find_by!('id = ?', posts(:eager_other).id)
1112
+ end
1113
+
1114
+ test "find_by! doesn't have implicit ordering" do
1115
+ assert_sql(/^((?!ORDER).)*$/) { Post.find_by!(id: posts(:eager_other).id) }
1116
+ end
1117
+
1118
+ test "find_by! raises RecordNotFound if the record is missing" do
1119
+ assert_raises(ActiveRecord::RecordNotFound) do
1120
+ Post.find_by!("1 = 0")
1121
+ end
1122
+ end
1123
+
1124
+ test "find on a scope does not perform statement caching" do
1125
+ honda = cars(:honda)
1126
+ zyke = cars(:zyke)
1127
+ tyre = honda.tyres.create!
1128
+ tyre2 = zyke.tyres.create!
1129
+
1130
+ assert_equal tyre, honda.tyres.custom_find(tyre.id)
1131
+ assert_equal tyre2, zyke.tyres.custom_find(tyre2.id)
1132
+ end
1133
+
1134
+ test "find_by on a scope does not perform statement caching" do
1135
+ honda = cars(:honda)
1136
+ zyke = cars(:zyke)
1137
+ tyre = honda.tyres.create!
1138
+ tyre2 = zyke.tyres.create!
1139
+
1140
+ assert_equal tyre, honda.tyres.custom_find_by(id: tyre.id)
1141
+ assert_equal tyre2, zyke.tyres.custom_find_by(id: tyre2.id)
1142
+ end
1143
+
1144
+ protected
1145
+ def bind(statement, *vars)
1146
+ if vars.first.is_a?(Hash)
1147
+ ActiveRecord::Base.send(:replace_named_bind_variables, statement, vars.first)
1148
+ else
1149
+ ActiveRecord::Base.send(:replace_bind_variables, statement, vars)
1150
+ end
1151
+ end
1152
+
1153
+ def table_with_custom_primary_key
1154
+ yield(Class.new(Toy) do
1155
+ def self.name
1156
+ 'MercedesCar'
1157
+ end
1158
+ end)
1159
+ end
1160
+
1161
+ def assert_raises_with_message(exception_class, message, &block)
1162
+ err = assert_raises(exception_class) { block.call }
1163
+ assert_match message, err.message
1164
+ end
1165
+
1166
+ end