activerecord_csi 2.3.5.p6

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 (333) hide show
  1. data/CHANGELOG +5858 -0
  2. data/README +351 -0
  3. data/RUNNING_UNIT_TESTS +36 -0
  4. data/Rakefile +270 -0
  5. data/examples/associations.png +0 -0
  6. data/examples/performance.rb +162 -0
  7. data/install.rb +30 -0
  8. data/lib/active_record/aggregations.rb +261 -0
  9. data/lib/active_record/association_preload.rb +389 -0
  10. data/lib/active_record/associations/association_collection.rb +475 -0
  11. data/lib/active_record/associations/association_proxy.rb +278 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +76 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +53 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +143 -0
  15. data/lib/active_record/associations/has_many_association.rb +122 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +266 -0
  17. data/lib/active_record/associations/has_one_association.rb +133 -0
  18. data/lib/active_record/associations/has_one_through_association.rb +37 -0
  19. data/lib/active_record/associations.rb +2241 -0
  20. data/lib/active_record/attribute_methods.rb +388 -0
  21. data/lib/active_record/autosave_association.rb +364 -0
  22. data/lib/active_record/base.rb +3171 -0
  23. data/lib/active_record/batches.rb +81 -0
  24. data/lib/active_record/calculations.rb +311 -0
  25. data/lib/active_record/callbacks.rb +360 -0
  26. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +371 -0
  27. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +139 -0
  28. data/lib/active_record/connection_adapters/abstract/database_statements.rb +289 -0
  29. data/lib/active_record/connection_adapters/abstract/query_cache.rb +94 -0
  30. data/lib/active_record/connection_adapters/abstract/quoting.rb +69 -0
  31. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +722 -0
  32. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +434 -0
  33. data/lib/active_record/connection_adapters/abstract_adapter.rb +241 -0
  34. data/lib/active_record/connection_adapters/mysql_adapter.rb +630 -0
  35. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1113 -0
  36. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  37. data/lib/active_record/connection_adapters/sqlite_adapter.rb +453 -0
  38. data/lib/active_record/dirty.rb +183 -0
  39. data/lib/active_record/dynamic_finder_match.rb +41 -0
  40. data/lib/active_record/dynamic_scope_match.rb +25 -0
  41. data/lib/active_record/fixtures.rb +996 -0
  42. data/lib/active_record/i18n_interpolation_deprecation.rb +26 -0
  43. data/lib/active_record/locale/en.yml +58 -0
  44. data/lib/active_record/locking/optimistic.rb +148 -0
  45. data/lib/active_record/locking/pessimistic.rb +55 -0
  46. data/lib/active_record/migration.rb +566 -0
  47. data/lib/active_record/named_scope.rb +192 -0
  48. data/lib/active_record/nested_attributes.rb +392 -0
  49. data/lib/active_record/observer.rb +197 -0
  50. data/lib/active_record/query_cache.rb +33 -0
  51. data/lib/active_record/reflection.rb +320 -0
  52. data/lib/active_record/schema.rb +51 -0
  53. data/lib/active_record/schema_dumper.rb +182 -0
  54. data/lib/active_record/serialization.rb +101 -0
  55. data/lib/active_record/serializers/json_serializer.rb +91 -0
  56. data/lib/active_record/serializers/xml_serializer.rb +357 -0
  57. data/lib/active_record/session_store.rb +326 -0
  58. data/lib/active_record/test_case.rb +66 -0
  59. data/lib/active_record/timestamp.rb +71 -0
  60. data/lib/active_record/transactions.rb +235 -0
  61. data/lib/active_record/validations.rb +1135 -0
  62. data/lib/active_record/version.rb +9 -0
  63. data/lib/active_record.rb +84 -0
  64. data/lib/activerecord.rb +2 -0
  65. data/test/assets/example.log +1 -0
  66. data/test/assets/flowers.jpg +0 -0
  67. data/test/cases/aaa_create_tables_test.rb +24 -0
  68. data/test/cases/active_schema_test_mysql.rb +100 -0
  69. data/test/cases/active_schema_test_postgresql.rb +24 -0
  70. data/test/cases/adapter_test.rb +145 -0
  71. data/test/cases/aggregations_test.rb +167 -0
  72. data/test/cases/ar_schema_test.rb +32 -0
  73. data/test/cases/associations/belongs_to_associations_test.rb +425 -0
  74. data/test/cases/associations/callbacks_test.rb +161 -0
  75. data/test/cases/associations/cascaded_eager_loading_test.rb +131 -0
  76. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -0
  77. data/test/cases/associations/eager_load_nested_include_test.rb +130 -0
  78. data/test/cases/associations/eager_singularization_test.rb +145 -0
  79. data/test/cases/associations/eager_test.rb +834 -0
  80. data/test/cases/associations/extension_test.rb +62 -0
  81. data/test/cases/associations/habtm_join_table_test.rb +56 -0
  82. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +822 -0
  83. data/test/cases/associations/has_many_associations_test.rb +1134 -0
  84. data/test/cases/associations/has_many_through_associations_test.rb +346 -0
  85. data/test/cases/associations/has_one_associations_test.rb +330 -0
  86. data/test/cases/associations/has_one_through_associations_test.rb +209 -0
  87. data/test/cases/associations/inner_join_association_test.rb +93 -0
  88. data/test/cases/associations/join_model_test.rb +712 -0
  89. data/test/cases/associations_test.rb +262 -0
  90. data/test/cases/attribute_methods_test.rb +305 -0
  91. data/test/cases/autosave_association_test.rb +1142 -0
  92. data/test/cases/base_test.rb +2154 -0
  93. data/test/cases/batches_test.rb +61 -0
  94. data/test/cases/binary_test.rb +30 -0
  95. data/test/cases/calculations_test.rb +348 -0
  96. data/test/cases/callbacks_observers_test.rb +38 -0
  97. data/test/cases/callbacks_test.rb +438 -0
  98. data/test/cases/class_inheritable_attributes_test.rb +32 -0
  99. data/test/cases/column_alias_test.rb +17 -0
  100. data/test/cases/column_definition_test.rb +70 -0
  101. data/test/cases/connection_pool_test.rb +25 -0
  102. data/test/cases/connection_test_firebird.rb +8 -0
  103. data/test/cases/connection_test_mysql.rb +64 -0
  104. data/test/cases/copy_table_test_sqlite.rb +80 -0
  105. data/test/cases/database_statements_test.rb +12 -0
  106. data/test/cases/datatype_test_postgresql.rb +204 -0
  107. data/test/cases/date_time_test.rb +37 -0
  108. data/test/cases/default_test_firebird.rb +16 -0
  109. data/test/cases/defaults_test.rb +111 -0
  110. data/test/cases/deprecated_finder_test.rb +30 -0
  111. data/test/cases/dirty_test.rb +316 -0
  112. data/test/cases/finder_respond_to_test.rb +76 -0
  113. data/test/cases/finder_test.rb +1066 -0
  114. data/test/cases/fixtures_test.rb +656 -0
  115. data/test/cases/helper.rb +68 -0
  116. data/test/cases/i18n_test.rb +46 -0
  117. data/test/cases/inheritance_test.rb +262 -0
  118. data/test/cases/invalid_date_test.rb +24 -0
  119. data/test/cases/json_serialization_test.rb +205 -0
  120. data/test/cases/lifecycle_test.rb +193 -0
  121. data/test/cases/locking_test.rb +304 -0
  122. data/test/cases/method_scoping_test.rb +704 -0
  123. data/test/cases/migration_test.rb +1523 -0
  124. data/test/cases/migration_test_firebird.rb +124 -0
  125. data/test/cases/mixin_test.rb +96 -0
  126. data/test/cases/modules_test.rb +81 -0
  127. data/test/cases/multiple_db_test.rb +85 -0
  128. data/test/cases/named_scope_test.rb +361 -0
  129. data/test/cases/nested_attributes_test.rb +581 -0
  130. data/test/cases/pk_test.rb +119 -0
  131. data/test/cases/pooled_connections_test.rb +103 -0
  132. data/test/cases/query_cache_test.rb +123 -0
  133. data/test/cases/readonly_test.rb +107 -0
  134. data/test/cases/reflection_test.rb +194 -0
  135. data/test/cases/reload_models_test.rb +22 -0
  136. data/test/cases/repair_helper.rb +50 -0
  137. data/test/cases/reserved_word_test_mysql.rb +176 -0
  138. data/test/cases/sanitize_test.rb +25 -0
  139. data/test/cases/schema_authorization_test_postgresql.rb +75 -0
  140. data/test/cases/schema_dumper_test.rb +211 -0
  141. data/test/cases/schema_test_postgresql.rb +178 -0
  142. data/test/cases/serialization_test.rb +47 -0
  143. data/test/cases/synonym_test_oracle.rb +17 -0
  144. data/test/cases/timestamp_test.rb +75 -0
  145. data/test/cases/transactions_test.rb +522 -0
  146. data/test/cases/unconnected_test.rb +32 -0
  147. data/test/cases/validations_i18n_test.rb +955 -0
  148. data/test/cases/validations_test.rb +1640 -0
  149. data/test/cases/xml_serialization_test.rb +240 -0
  150. data/test/config.rb +5 -0
  151. data/test/connections/jdbc_jdbcderby/connection.rb +18 -0
  152. data/test/connections/jdbc_jdbch2/connection.rb +18 -0
  153. data/test/connections/jdbc_jdbchsqldb/connection.rb +18 -0
  154. data/test/connections/jdbc_jdbcmysql/connection.rb +26 -0
  155. data/test/connections/jdbc_jdbcpostgresql/connection.rb +26 -0
  156. data/test/connections/jdbc_jdbcsqlite3/connection.rb +25 -0
  157. data/test/connections/native_db2/connection.rb +25 -0
  158. data/test/connections/native_firebird/connection.rb +26 -0
  159. data/test/connections/native_frontbase/connection.rb +27 -0
  160. data/test/connections/native_mysql/connection.rb +25 -0
  161. data/test/connections/native_openbase/connection.rb +21 -0
  162. data/test/connections/native_oracle/connection.rb +27 -0
  163. data/test/connections/native_postgresql/connection.rb +25 -0
  164. data/test/connections/native_sqlite/connection.rb +25 -0
  165. data/test/connections/native_sqlite3/connection.rb +25 -0
  166. data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
  167. data/test/connections/native_sybase/connection.rb +23 -0
  168. data/test/fixtures/accounts.yml +29 -0
  169. data/test/fixtures/all/developers.yml +0 -0
  170. data/test/fixtures/all/people.csv +0 -0
  171. data/test/fixtures/all/tasks.yml +0 -0
  172. data/test/fixtures/author_addresses.yml +5 -0
  173. data/test/fixtures/author_favorites.yml +4 -0
  174. data/test/fixtures/authors.yml +9 -0
  175. data/test/fixtures/binaries.yml +132 -0
  176. data/test/fixtures/books.yml +7 -0
  177. data/test/fixtures/categories/special_categories.yml +9 -0
  178. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  179. data/test/fixtures/categories.yml +14 -0
  180. data/test/fixtures/categories_ordered.yml +7 -0
  181. data/test/fixtures/categories_posts.yml +23 -0
  182. data/test/fixtures/categorizations.yml +17 -0
  183. data/test/fixtures/clubs.yml +6 -0
  184. data/test/fixtures/comments.yml +59 -0
  185. data/test/fixtures/companies.yml +56 -0
  186. data/test/fixtures/computers.yml +4 -0
  187. data/test/fixtures/courses.yml +7 -0
  188. data/test/fixtures/customers.yml +26 -0
  189. data/test/fixtures/developers.yml +21 -0
  190. data/test/fixtures/developers_projects.yml +17 -0
  191. data/test/fixtures/edges.yml +6 -0
  192. data/test/fixtures/entrants.yml +14 -0
  193. data/test/fixtures/fixture_database.sqlite3 +0 -0
  194. data/test/fixtures/fixture_database_2.sqlite3 +0 -0
  195. data/test/fixtures/fk_test_has_fk.yml +3 -0
  196. data/test/fixtures/fk_test_has_pk.yml +2 -0
  197. data/test/fixtures/funny_jokes.yml +10 -0
  198. data/test/fixtures/items.yml +4 -0
  199. data/test/fixtures/jobs.yml +7 -0
  200. data/test/fixtures/legacy_things.yml +3 -0
  201. data/test/fixtures/mateys.yml +4 -0
  202. data/test/fixtures/member_types.yml +6 -0
  203. data/test/fixtures/members.yml +6 -0
  204. data/test/fixtures/memberships.yml +20 -0
  205. data/test/fixtures/minimalistics.yml +2 -0
  206. data/test/fixtures/mixed_case_monkeys.yml +6 -0
  207. data/test/fixtures/mixins.yml +29 -0
  208. data/test/fixtures/movies.yml +7 -0
  209. data/test/fixtures/naked/csv/accounts.csv +1 -0
  210. data/test/fixtures/naked/yml/accounts.yml +1 -0
  211. data/test/fixtures/naked/yml/companies.yml +1 -0
  212. data/test/fixtures/naked/yml/courses.yml +1 -0
  213. data/test/fixtures/organizations.yml +5 -0
  214. data/test/fixtures/owners.yml +7 -0
  215. data/test/fixtures/parrots.yml +27 -0
  216. data/test/fixtures/parrots_pirates.yml +7 -0
  217. data/test/fixtures/people.yml +15 -0
  218. data/test/fixtures/pets.yml +14 -0
  219. data/test/fixtures/pirates.yml +9 -0
  220. data/test/fixtures/posts.yml +52 -0
  221. data/test/fixtures/price_estimates.yml +7 -0
  222. data/test/fixtures/projects.yml +7 -0
  223. data/test/fixtures/readers.yml +9 -0
  224. data/test/fixtures/references.yml +17 -0
  225. data/test/fixtures/reserved_words/distinct.yml +5 -0
  226. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  227. data/test/fixtures/reserved_words/group.yml +14 -0
  228. data/test/fixtures/reserved_words/select.yml +8 -0
  229. data/test/fixtures/reserved_words/values.yml +7 -0
  230. data/test/fixtures/ships.yml +5 -0
  231. data/test/fixtures/sponsors.yml +9 -0
  232. data/test/fixtures/subscribers.yml +7 -0
  233. data/test/fixtures/subscriptions.yml +12 -0
  234. data/test/fixtures/taggings.yml +28 -0
  235. data/test/fixtures/tags.yml +7 -0
  236. data/test/fixtures/tasks.yml +7 -0
  237. data/test/fixtures/topics.yml +42 -0
  238. data/test/fixtures/toys.yml +4 -0
  239. data/test/fixtures/treasures.yml +10 -0
  240. data/test/fixtures/vertices.yml +4 -0
  241. data/test/fixtures/warehouse-things.yml +3 -0
  242. data/test/migrations/broken/100_migration_that_raises_exception.rb +10 -0
  243. data/test/migrations/decimal/1_give_me_big_numbers.rb +15 -0
  244. data/test/migrations/duplicate/1_people_have_last_names.rb +9 -0
  245. data/test/migrations/duplicate/2_we_need_reminders.rb +12 -0
  246. data/test/migrations/duplicate/3_foo.rb +7 -0
  247. data/test/migrations/duplicate/3_innocent_jointable.rb +12 -0
  248. data/test/migrations/duplicate_names/20080507052938_chunky.rb +7 -0
  249. data/test/migrations/duplicate_names/20080507053028_chunky.rb +7 -0
  250. data/test/migrations/interleaved/pass_1/3_innocent_jointable.rb +12 -0
  251. data/test/migrations/interleaved/pass_2/1_people_have_last_names.rb +9 -0
  252. data/test/migrations/interleaved/pass_2/3_innocent_jointable.rb +12 -0
  253. data/test/migrations/interleaved/pass_3/1_people_have_last_names.rb +9 -0
  254. data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +8 -0
  255. data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +12 -0
  256. data/test/migrations/missing/1000_people_have_middle_names.rb +9 -0
  257. data/test/migrations/missing/1_people_have_last_names.rb +9 -0
  258. data/test/migrations/missing/3_we_need_reminders.rb +12 -0
  259. data/test/migrations/missing/4_innocent_jointable.rb +12 -0
  260. data/test/migrations/valid/1_people_have_last_names.rb +9 -0
  261. data/test/migrations/valid/2_we_need_reminders.rb +12 -0
  262. data/test/migrations/valid/3_innocent_jointable.rb +12 -0
  263. data/test/models/author.rb +146 -0
  264. data/test/models/auto_id.rb +4 -0
  265. data/test/models/binary.rb +2 -0
  266. data/test/models/bird.rb +3 -0
  267. data/test/models/book.rb +4 -0
  268. data/test/models/categorization.rb +5 -0
  269. data/test/models/category.rb +34 -0
  270. data/test/models/citation.rb +6 -0
  271. data/test/models/club.rb +13 -0
  272. data/test/models/column_name.rb +3 -0
  273. data/test/models/comment.rb +29 -0
  274. data/test/models/company.rb +171 -0
  275. data/test/models/company_in_module.rb +61 -0
  276. data/test/models/computer.rb +3 -0
  277. data/test/models/contact.rb +16 -0
  278. data/test/models/contract.rb +5 -0
  279. data/test/models/course.rb +3 -0
  280. data/test/models/customer.rb +73 -0
  281. data/test/models/default.rb +2 -0
  282. data/test/models/developer.rb +101 -0
  283. data/test/models/edge.rb +5 -0
  284. data/test/models/entrant.rb +3 -0
  285. data/test/models/essay.rb +3 -0
  286. data/test/models/event.rb +3 -0
  287. data/test/models/guid.rb +2 -0
  288. data/test/models/item.rb +7 -0
  289. data/test/models/job.rb +5 -0
  290. data/test/models/joke.rb +3 -0
  291. data/test/models/keyboard.rb +3 -0
  292. data/test/models/legacy_thing.rb +3 -0
  293. data/test/models/matey.rb +4 -0
  294. data/test/models/member.rb +12 -0
  295. data/test/models/member_detail.rb +5 -0
  296. data/test/models/member_type.rb +3 -0
  297. data/test/models/membership.rb +9 -0
  298. data/test/models/minimalistic.rb +2 -0
  299. data/test/models/mixed_case_monkey.rb +3 -0
  300. data/test/models/movie.rb +5 -0
  301. data/test/models/order.rb +4 -0
  302. data/test/models/organization.rb +6 -0
  303. data/test/models/owner.rb +5 -0
  304. data/test/models/parrot.rb +16 -0
  305. data/test/models/person.rb +16 -0
  306. data/test/models/pet.rb +5 -0
  307. data/test/models/pirate.rb +70 -0
  308. data/test/models/post.rb +100 -0
  309. data/test/models/price_estimate.rb +3 -0
  310. data/test/models/project.rb +30 -0
  311. data/test/models/reader.rb +4 -0
  312. data/test/models/reference.rb +4 -0
  313. data/test/models/reply.rb +46 -0
  314. data/test/models/ship.rb +10 -0
  315. data/test/models/ship_part.rb +5 -0
  316. data/test/models/sponsor.rb +4 -0
  317. data/test/models/subject.rb +4 -0
  318. data/test/models/subscriber.rb +8 -0
  319. data/test/models/subscription.rb +4 -0
  320. data/test/models/tag.rb +7 -0
  321. data/test/models/tagging.rb +10 -0
  322. data/test/models/task.rb +3 -0
  323. data/test/models/topic.rb +80 -0
  324. data/test/models/toy.rb +6 -0
  325. data/test/models/treasure.rb +8 -0
  326. data/test/models/vertex.rb +9 -0
  327. data/test/models/warehouse_thing.rb +5 -0
  328. data/test/schema/mysql_specific_schema.rb +24 -0
  329. data/test/schema/postgresql_specific_schema.rb +114 -0
  330. data/test/schema/schema.rb +493 -0
  331. data/test/schema/schema2.rb +6 -0
  332. data/test/schema/sqlite_specific_schema.rb +25 -0
  333. metadata +420 -0
@@ -0,0 +1,1066 @@
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/topic'
8
+ require 'models/reply'
9
+ require 'models/entrant'
10
+ require 'models/developer'
11
+ require 'models/customer'
12
+ require 'models/job'
13
+ require 'models/categorization'
14
+
15
+ class DynamicFinderMatchTest < ActiveRecord::TestCase
16
+ def test_find_no_match
17
+ assert_nil ActiveRecord::DynamicFinderMatch.match("not_a_finder")
18
+ end
19
+
20
+ def test_find_by
21
+ match = ActiveRecord::DynamicFinderMatch.match("find_by_age_and_sex_and_location")
22
+ assert_not_nil match
23
+ assert match.finder?
24
+ assert_equal :first, match.finder
25
+ assert_equal %w(age sex location), match.attribute_names
26
+ end
27
+
28
+ def find_by_bang
29
+ match = ActiveRecord::DynamicFinderMatch.match("find_by_age_and_sex_and_location!")
30
+ assert_not_nil match
31
+ assert match.finder?
32
+ assert match.bang?
33
+ assert_equal :first, match.finder
34
+ assert_equal %w(age sex location), match.attribute_names
35
+ end
36
+
37
+ def test_find_all_by
38
+ match = ActiveRecord::DynamicFinderMatch.match("find_all_by_age_and_sex_and_location")
39
+ assert_not_nil match
40
+ assert match.finder?
41
+ assert_equal :all, match.finder
42
+ assert_equal %w(age sex location), match.attribute_names
43
+ end
44
+
45
+ def test_find_or_initialize_by
46
+ match = ActiveRecord::DynamicFinderMatch.match("find_or_initialize_by_age_and_sex_and_location")
47
+ assert_not_nil match
48
+ assert !match.finder?
49
+ assert match.instantiator?
50
+ assert_equal :first, match.finder
51
+ assert_equal :new, match.instantiator
52
+ assert_equal %w(age sex location), match.attribute_names
53
+ end
54
+
55
+ def test_find_or_create_by
56
+ match = ActiveRecord::DynamicFinderMatch.match("find_or_create_by_age_and_sex_and_location")
57
+ assert_not_nil match
58
+ assert !match.finder?
59
+ assert match.instantiator?
60
+ assert_equal :first, match.finder
61
+ assert_equal :create, match.instantiator
62
+ assert_equal %w(age sex location), match.attribute_names
63
+ end
64
+ end
65
+
66
+ class FinderTest < ActiveRecord::TestCase
67
+ fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors, :customers
68
+
69
+ def test_find_by_id_with_hash
70
+ assert_raises(ActiveRecord::StatementInvalid) do
71
+ Post.find_by_id(:limit => 1)
72
+ end
73
+ end
74
+
75
+ def test_find_by_title_and_id_with_hash
76
+ assert_raises(ActiveRecord::StatementInvalid) do
77
+ Post.find_by_title_and_id('foo', :limit => 1)
78
+ end
79
+ end
80
+
81
+ def test_find
82
+ assert_equal(topics(:first).title, Topic.find(1).title)
83
+ end
84
+
85
+ # find should handle strings that come from URLs
86
+ # (example: Category.find(params[:id]))
87
+ def test_find_with_string
88
+ assert_equal(Topic.find(1).title,Topic.find("1").title)
89
+ end
90
+
91
+ def test_exists
92
+ assert Topic.exists?(1)
93
+ assert Topic.exists?("1")
94
+ assert Topic.exists?(:author_name => "David")
95
+ assert Topic.exists?(:author_name => "Mary", :approved => true)
96
+ assert Topic.exists?(["parent_id = ?", 1])
97
+ assert !Topic.exists?(45)
98
+
99
+ begin
100
+ assert !Topic.exists?("foo")
101
+ rescue ActiveRecord::StatementInvalid
102
+ # PostgreSQL complains about string comparison with integer field
103
+ rescue Exception
104
+ flunk
105
+ end
106
+
107
+ assert_raise(NoMethodError) { Topic.exists?([1,2]) }
108
+ end
109
+
110
+ def test_exists_returns_true_with_one_record_and_no_args
111
+ assert Topic.exists?
112
+ end
113
+
114
+ def test_does_not_exist_with_empty_table_and_no_args_given
115
+ Topic.delete_all
116
+ assert !Topic.exists?
117
+ end
118
+
119
+ def test_exists_with_aggregate_having_three_mappings
120
+ existing_address = customers(:david).address
121
+ assert Customer.exists?(:address => existing_address)
122
+ end
123
+
124
+ def test_exists_with_aggregate_having_three_mappings_with_one_difference
125
+ existing_address = customers(:david).address
126
+ assert !Customer.exists?(:address =>
127
+ Address.new(existing_address.street, existing_address.city, existing_address.country + "1"))
128
+ assert !Customer.exists?(:address =>
129
+ Address.new(existing_address.street, existing_address.city + "1", existing_address.country))
130
+ assert !Customer.exists?(:address =>
131
+ Address.new(existing_address.street + "1", existing_address.city, existing_address.country))
132
+ end
133
+
134
+ def test_exists_with_scoped_include
135
+ Developer.with_scope(:find => { :include => :projects, :order => "projects.name" }) do
136
+ assert Developer.exists?
137
+ end
138
+ end
139
+
140
+ def test_find_by_array_of_one_id
141
+ assert_kind_of(Array, Topic.find([ 1 ]))
142
+ assert_equal(1, Topic.find([ 1 ]).length)
143
+ end
144
+
145
+ def test_find_by_ids
146
+ assert_equal 2, Topic.find(1, 2).size
147
+ assert_equal topics(:second).title, Topic.find([2]).first.title
148
+ end
149
+
150
+ def test_find_by_ids_with_limit_and_offset
151
+ assert_equal 2, Entrant.find([1,3,2], :limit => 2).size
152
+ assert_equal 1, Entrant.find([1,3,2], :limit => 3, :offset => 2).size
153
+
154
+ # Also test an edge case: If you have 11 results, and you set a
155
+ # limit of 3 and offset of 9, then you should find that there
156
+ # will be only 2 results, regardless of the limit.
157
+ devs = Developer.find :all
158
+ last_devs = Developer.find devs.map(&:id), :limit => 3, :offset => 9
159
+ assert_equal 2, last_devs.size
160
+ end
161
+
162
+ def test_find_an_empty_array
163
+ assert_equal [], Topic.find([])
164
+ end
165
+
166
+ def test_find_by_ids_missing_one
167
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, 2, 45) }
168
+ end
169
+
170
+ def test_find_all_with_limit
171
+ assert_equal(2, Entrant.find(:all, :limit => 2).size)
172
+ assert_equal(0, Entrant.find(:all, :limit => 0).size)
173
+ end
174
+
175
+ def test_find_all_with_prepared_limit_and_offset
176
+ entrants = Entrant.find(:all, :order => "id ASC", :limit => 2, :offset => 1)
177
+
178
+ assert_equal(2, entrants.size)
179
+ assert_equal(entrants(:second).name, entrants.first.name)
180
+
181
+ assert_equal 3, Entrant.count
182
+ entrants = Entrant.find(:all, :order => "id ASC", :limit => 2, :offset => 2)
183
+ assert_equal(1, entrants.size)
184
+ assert_equal(entrants(:third).name, entrants.first.name)
185
+ end
186
+
187
+ def test_find_all_with_limit_and_offset_and_multiple_order_clauses
188
+ first_three_posts = Post.find :all, :order => 'author_id, id', :limit => 3, :offset => 0
189
+ second_three_posts = Post.find :all, :order => ' author_id,id ', :limit => 3, :offset => 3
190
+ last_posts = Post.find :all, :order => ' author_id, id ', :limit => 3, :offset => 6
191
+
192
+ assert_equal [[0,3],[1,1],[1,2]], first_three_posts.map { |p| [p.author_id, p.id] }
193
+ assert_equal [[1,4],[1,5],[1,6]], second_three_posts.map { |p| [p.author_id, p.id] }
194
+ assert_equal [[2,7]], last_posts.map { |p| [p.author_id, p.id] }
195
+ end
196
+
197
+
198
+ def test_find_with_group
199
+ developers = Developer.find(:all, :group => "salary", :select => "salary")
200
+ assert_equal 4, developers.size
201
+ assert_equal 4, developers.map(&:salary).uniq.size
202
+ end
203
+
204
+ def test_find_with_group_and_having
205
+ developers = Developer.find(:all, :group => "salary", :having => "sum(salary) > 10000", :select => "salary")
206
+ assert_equal 3, developers.size
207
+ assert_equal 3, developers.map(&:salary).uniq.size
208
+ assert developers.all? { |developer| developer.salary > 10000 }
209
+ end
210
+
211
+ def test_find_with_group_and_sanitized_having
212
+ developers = Developer.find(:all, :group => "salary", :having => ["sum(salary) > ?", 10000], :select => "salary")
213
+ assert_equal 3, developers.size
214
+ assert_equal 3, developers.map(&:salary).uniq.size
215
+ assert developers.all? { |developer| developer.salary > 10000 }
216
+ end
217
+
218
+ def test_find_with_entire_select_statement
219
+ topics = Topic.find_by_sql "SELECT * FROM topics WHERE author_name = 'Mary'"
220
+
221
+ assert_equal(1, topics.size)
222
+ assert_equal(topics(:second).title, topics.first.title)
223
+ end
224
+
225
+ def test_find_with_prepared_select_statement
226
+ topics = Topic.find_by_sql ["SELECT * FROM topics WHERE author_name = ?", "Mary"]
227
+
228
+ assert_equal(1, topics.size)
229
+ assert_equal(topics(:second).title, topics.first.title)
230
+ end
231
+
232
+ def test_find_by_sql_with_sti_on_joined_table
233
+ accounts = Account.find_by_sql("SELECT * FROM accounts INNER JOIN companies ON companies.id = accounts.firm_id")
234
+ assert_equal [Account], accounts.collect(&:class).uniq
235
+ end
236
+
237
+ def test_find_first
238
+ first = Topic.find(:first, :conditions => "title = 'The First Topic'")
239
+ assert_equal(topics(:first).title, first.title)
240
+ end
241
+
242
+ def test_find_first_failing
243
+ first = Topic.find(:first, :conditions => "title = 'The First Topic!'")
244
+ assert_nil(first)
245
+ end
246
+
247
+ def test_first
248
+ assert_equal topics(:second).title, Topic.first(:conditions => "title = 'The Second Topic of the day'").title
249
+ end
250
+
251
+ def test_first_failing
252
+ assert_nil Topic.first(:conditions => "title = 'The Second Topic of the day!'")
253
+ end
254
+
255
+ def test_unexisting_record_exception_handling
256
+ assert_raise(ActiveRecord::RecordNotFound) {
257
+ Topic.find(1).parent
258
+ }
259
+
260
+ Topic.find(2).topic
261
+ end
262
+
263
+ def test_find_only_some_columns
264
+ topic = Topic.find(1, :select => "author_name")
265
+ assert_raise(ActiveRecord::MissingAttributeError) {topic.title}
266
+ assert_equal "David", topic.author_name
267
+ assert !topic.attribute_present?("title")
268
+ #assert !topic.respond_to?("title")
269
+ assert topic.attribute_present?("author_name")
270
+ assert topic.respond_to?("author_name")
271
+ end
272
+
273
+ def test_find_on_blank_conditions
274
+ [nil, " ", [], {}].each do |blank|
275
+ assert_nothing_raised { Topic.find(:first, :conditions => blank) }
276
+ end
277
+ end
278
+
279
+ def test_find_on_blank_bind_conditions
280
+ [ [""], ["",{}] ].each do |blank|
281
+ assert_nothing_raised { Topic.find(:first, :conditions => blank) }
282
+ end
283
+ end
284
+
285
+ def test_find_on_array_conditions
286
+ assert Topic.find(1, :conditions => ["approved = ?", false])
287
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => ["approved = ?", true]) }
288
+ end
289
+
290
+ def test_find_on_hash_conditions
291
+ assert Topic.find(1, :conditions => { :approved => false })
292
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :approved => true }) }
293
+ end
294
+
295
+ def test_find_on_hash_conditions_with_explicit_table_name
296
+ assert Topic.find(1, :conditions => { 'topics.approved' => false })
297
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { 'topics.approved' => true }) }
298
+ end
299
+
300
+ def test_find_on_hash_conditions_with_hashed_table_name
301
+ assert Topic.find(1, :conditions => {:topics => { :approved => false }})
302
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => {:topics => { :approved => true }}) }
303
+ end
304
+
305
+ def test_find_with_hash_conditions_on_joined_table
306
+ firms = Firm.all :joins => :account, :conditions => {:accounts => { :credit_limit => 50 }}
307
+ assert_equal 1, firms.size
308
+ assert_equal companies(:first_firm), firms.first
309
+ end
310
+
311
+ def test_find_with_hash_conditions_on_joined_table_and_with_range
312
+ firms = DependentFirm.all :joins => :account, :conditions => {:name => 'RailsCore', :accounts => { :credit_limit => 55..60 }}
313
+ assert_equal 1, firms.size
314
+ assert_equal companies(:rails_core), firms.first
315
+ end
316
+
317
+ def test_find_on_hash_conditions_with_explicit_table_name_and_aggregate
318
+ david = customers(:david)
319
+ assert Customer.find(david.id, :conditions => { 'customers.name' => david.name, :address => david.address })
320
+ assert_raise(ActiveRecord::RecordNotFound) {
321
+ Customer.find(david.id, :conditions => { 'customers.name' => david.name + "1", :address => david.address })
322
+ }
323
+ end
324
+
325
+ def test_find_on_association_proxy_conditions
326
+ assert_equal [1, 2, 3, 5, 6, 7, 8, 9, 10], Comment.find_all_by_post_id(authors(:david).posts).map(&:id).sort
327
+ end
328
+
329
+ def test_find_on_hash_conditions_with_range
330
+ assert_equal [1,2], Topic.find(:all, :conditions => { :id => 1..2 }).map(&:id).sort
331
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :id => 2..3 }) }
332
+ end
333
+
334
+ def test_find_on_hash_conditions_with_end_exclusive_range
335
+ assert_equal [1,2,3], Topic.find(:all, :conditions => { :id => 1..3 }).map(&:id).sort
336
+ assert_equal [1,2], Topic.find(:all, :conditions => { :id => 1...3 }).map(&:id).sort
337
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(3, :conditions => { :id => 2...3 }) }
338
+ end
339
+
340
+ def test_find_on_hash_conditions_with_multiple_ranges
341
+ assert_equal [1,2,3], Comment.find(:all, :conditions => { :id => 1..3, :post_id => 1..2 }).map(&:id).sort
342
+ assert_equal [1], Comment.find(:all, :conditions => { :id => 1..1, :post_id => 1..10 }).map(&:id).sort
343
+ end
344
+
345
+ def test_find_on_multiple_hash_conditions
346
+ assert Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => false })
347
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }) }
348
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "HHC", :replies_count => 1, :approved => false }) }
349
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }) }
350
+ end
351
+
352
+ def test_condition_interpolation
353
+ assert_kind_of Firm, Company.find(:first, :conditions => ["name = '%s'", "37signals"])
354
+ assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!"])
355
+ assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!' OR 1=1"])
356
+ assert_kind_of Time, Topic.find(:first, :conditions => ["id = %d", 1]).written_on
357
+ end
358
+
359
+ def test_condition_array_interpolation
360
+ assert_kind_of Firm, Company.find(:first, :conditions => ["name = '%s'", "37signals"])
361
+ assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!"])
362
+ assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!' OR 1=1"])
363
+ assert_kind_of Time, Topic.find(:first, :conditions => ["id = %d", 1]).written_on
364
+ end
365
+
366
+ def test_condition_hash_interpolation
367
+ assert_kind_of Firm, Company.find(:first, :conditions => { :name => "37signals"})
368
+ assert_nil Company.find(:first, :conditions => { :name => "37signals!"})
369
+ assert_kind_of Time, Topic.find(:first, :conditions => {:id => 1}).written_on
370
+ end
371
+
372
+ def test_hash_condition_find_malformed
373
+ assert_raise(ActiveRecord::StatementInvalid) {
374
+ Company.find(:first, :conditions => { :id => 2, :dhh => true })
375
+ }
376
+ end
377
+
378
+ def test_hash_condition_find_with_escaped_characters
379
+ Company.create("name" => "Ain't noth'n like' \#stuff")
380
+ assert Company.find(:first, :conditions => { :name => "Ain't noth'n like' \#stuff" })
381
+ end
382
+
383
+ def test_hash_condition_find_with_array
384
+ p1, p2 = Post.find(:all, :limit => 2, :order => 'id asc')
385
+ assert_equal [p1, p2], Post.find(:all, :conditions => { :id => [p1, p2] }, :order => 'id asc')
386
+ assert_equal [p1, p2], Post.find(:all, :conditions => { :id => [p1, p2.id] }, :order => 'id asc')
387
+ end
388
+
389
+ def test_hash_condition_find_with_nil
390
+ topic = Topic.find(:first, :conditions => { :last_read => nil } )
391
+ assert_not_nil topic
392
+ assert_nil topic.last_read
393
+ end
394
+
395
+ def test_hash_condition_find_with_aggregate_having_one_mapping
396
+ balance = customers(:david).balance
397
+ assert_kind_of Money, balance
398
+ found_customer = Customer.find(:first, :conditions => {:balance => balance})
399
+ assert_equal customers(:david), found_customer
400
+ end
401
+
402
+ def test_hash_condition_find_with_aggregate_attribute_having_same_name_as_field_and_key_value_being_aggregate
403
+ gps_location = customers(:david).gps_location
404
+ assert_kind_of GpsLocation, gps_location
405
+ found_customer = Customer.find(:first, :conditions => {:gps_location => gps_location})
406
+ assert_equal customers(:david), found_customer
407
+ end
408
+
409
+ def test_hash_condition_find_with_aggregate_having_one_mapping_and_key_value_being_attribute_value
410
+ balance = customers(:david).balance
411
+ assert_kind_of Money, balance
412
+ found_customer = Customer.find(:first, :conditions => {:balance => balance.amount})
413
+ assert_equal customers(:david), found_customer
414
+ end
415
+
416
+ def test_hash_condition_find_with_aggregate_attribute_having_same_name_as_field_and_key_value_being_attribute_value
417
+ gps_location = customers(:david).gps_location
418
+ assert_kind_of GpsLocation, gps_location
419
+ found_customer = Customer.find(:first, :conditions => {:gps_location => gps_location.gps_location})
420
+ assert_equal customers(:david), found_customer
421
+ end
422
+
423
+ def test_hash_condition_find_with_aggregate_having_three_mappings
424
+ address = customers(:david).address
425
+ assert_kind_of Address, address
426
+ found_customer = Customer.find(:first, :conditions => {:address => address})
427
+ assert_equal customers(:david), found_customer
428
+ end
429
+
430
+ def test_hash_condition_find_with_one_condition_being_aggregate_and_another_not
431
+ address = customers(:david).address
432
+ assert_kind_of Address, address
433
+ found_customer = Customer.find(:first, :conditions => {:address => address, :name => customers(:david).name})
434
+ assert_equal customers(:david), found_customer
435
+ end
436
+
437
+ def test_bind_variables
438
+ assert_kind_of Firm, Company.find(:first, :conditions => ["name = ?", "37signals"])
439
+ assert_nil Company.find(:first, :conditions => ["name = ?", "37signals!"])
440
+ assert_nil Company.find(:first, :conditions => ["name = ?", "37signals!' OR 1=1"])
441
+ assert_kind_of Time, Topic.find(:first, :conditions => ["id = ?", 1]).written_on
442
+ assert_raise(ActiveRecord::PreparedStatementInvalid) {
443
+ Company.find(:first, :conditions => ["id=? AND name = ?", 2])
444
+ }
445
+ assert_raise(ActiveRecord::PreparedStatementInvalid) {
446
+ Company.find(:first, :conditions => ["id=?", 2, 3, 4])
447
+ }
448
+ end
449
+
450
+ def test_bind_variables_with_quotes
451
+ Company.create("name" => "37signals' go'es agains")
452
+ assert Company.find(:first, :conditions => ["name = ?", "37signals' go'es agains"])
453
+ end
454
+
455
+ def test_named_bind_variables_with_quotes
456
+ Company.create("name" => "37signals' go'es agains")
457
+ assert Company.find(:first, :conditions => ["name = :name", {:name => "37signals' go'es agains"}])
458
+ end
459
+
460
+ def test_bind_arity
461
+ assert_nothing_raised { bind '' }
462
+ assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '', 1 }
463
+
464
+ assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?' }
465
+ assert_nothing_raised { bind '?', 1 }
466
+ assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?', 1, 1 }
467
+ end
468
+
469
+ def test_named_bind_variables
470
+ assert_equal '1', bind(':a', :a => 1) # ' ruby-mode
471
+ assert_equal '1 1', bind(':a :a', :a => 1) # ' ruby-mode
472
+
473
+ assert_nothing_raised { bind("'+00:00'", :foo => "bar") }
474
+
475
+ assert_kind_of Firm, Company.find(:first, :conditions => ["name = :name", { :name => "37signals" }])
476
+ assert_nil Company.find(:first, :conditions => ["name = :name", { :name => "37signals!" }])
477
+ assert_nil Company.find(:first, :conditions => ["name = :name", { :name => "37signals!' OR 1=1" }])
478
+ assert_kind_of Time, Topic.find(:first, :conditions => ["id = :id", { :id => 1 }]).written_on
479
+ end
480
+
481
+ def test_bind_enumerable
482
+ quoted_abc = %(#{ActiveRecord::Base.connection.quote('a')},#{ActiveRecord::Base.connection.quote('b')},#{ActiveRecord::Base.connection.quote('c')})
483
+
484
+ assert_equal '1,2,3', bind('?', [1, 2, 3])
485
+ assert_equal quoted_abc, bind('?', %w(a b c))
486
+
487
+ assert_equal '1,2,3', bind(':a', :a => [1, 2, 3])
488
+ assert_equal quoted_abc, bind(':a', :a => %w(a b c)) # '
489
+
490
+ require 'set'
491
+ assert_equal '1,2,3', bind('?', Set.new([1, 2, 3]))
492
+ assert_equal quoted_abc, bind('?', Set.new(%w(a b c)))
493
+
494
+ assert_equal '1,2,3', bind(':a', :a => Set.new([1, 2, 3]))
495
+ assert_equal quoted_abc, bind(':a', :a => Set.new(%w(a b c))) # '
496
+ end
497
+
498
+ def test_bind_empty_enumerable
499
+ quoted_nil = ActiveRecord::Base.connection.quote(nil)
500
+ assert_equal quoted_nil, bind('?', [])
501
+ assert_equal " in (#{quoted_nil})", bind(' in (?)', [])
502
+ assert_equal "foo in (#{quoted_nil})", bind('foo in (?)', [])
503
+ end
504
+
505
+ def test_bind_string
506
+ assert_equal ActiveRecord::Base.connection.quote(''), bind('?', '')
507
+ end
508
+
509
+ def test_bind_chars
510
+ quoted_bambi = ActiveRecord::Base.connection.quote("Bambi")
511
+ quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote("Bambi\nand\nThumper")
512
+ assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi")
513
+ assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper")
514
+ assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi".mb_chars)
515
+ assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper".mb_chars)
516
+ end
517
+
518
+ def test_bind_record
519
+ o = Struct.new(:quoted_id).new(1)
520
+ assert_equal '1', bind('?', o)
521
+
522
+ os = [o] * 3
523
+ assert_equal '1,1,1', bind('?', os)
524
+ end
525
+
526
+ def test_named_bind_with_postgresql_type_casts
527
+ l = Proc.new { bind(":a::integer '2009-01-01'::date", :a => '10') }
528
+ assert_nothing_raised(&l)
529
+ assert_equal "#{ActiveRecord::Base.quote_value('10')}::integer '2009-01-01'::date", l.call
530
+ end
531
+
532
+ def test_string_sanitation
533
+ assert_not_equal "#{ActiveRecord::Base.connection.quoted_string_prefix}'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1")
534
+ assert_equal "#{ActiveRecord::Base.connection.quoted_string_prefix}'something; select table'", ActiveRecord::Base.sanitize("something; select table")
535
+ end
536
+
537
+ def test_count
538
+ assert_equal(0, Entrant.count(:conditions => "id > 3"))
539
+ assert_equal(1, Entrant.count(:conditions => ["id > ?", 2]))
540
+ assert_equal(2, Entrant.count(:conditions => ["id > ?", 1]))
541
+ end
542
+
543
+ def test_count_by_sql
544
+ assert_equal(0, Entrant.count_by_sql("SELECT COUNT(*) FROM entrants WHERE id > 3"))
545
+ assert_equal(1, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 2]))
546
+ assert_equal(2, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 1]))
547
+ end
548
+
549
+ def test_dynamic_finders_should_go_through_the_find_class_method
550
+ Topic.expects(:find).with(:first, :conditions => { :title => 'The First Topic!' })
551
+ Topic.find_by_title("The First Topic!")
552
+
553
+ Topic.expects(:find).with(:last, :conditions => { :title => 'The Last Topic!' })
554
+ Topic.find_last_by_title("The Last Topic!")
555
+
556
+ Topic.expects(:find).with(:all, :conditions => { :title => 'A Topic.' })
557
+ Topic.find_all_by_title("A Topic.")
558
+
559
+ Topic.expects(:find).with(:first, :conditions => { :title => 'Does not exist yet for sure!' }).times(2)
560
+ Topic.find_or_initialize_by_title('Does not exist yet for sure!')
561
+ Topic.find_or_create_by_title('Does not exist yet for sure!')
562
+ end
563
+
564
+ def test_find_by_one_attribute
565
+ assert_equal topics(:first), Topic.find_by_title("The First Topic")
566
+ assert_nil Topic.find_by_title("The First Topic!")
567
+ end
568
+
569
+ def test_find_by_one_attribute_bang
570
+ assert_equal topics(:first), Topic.find_by_title!("The First Topic")
571
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find_by_title!("The First Topic!") }
572
+ end
573
+
574
+ def test_find_by_one_attribute_caches_dynamic_finder
575
+ # ensure this test can run independently of order
576
+ class << Topic; self; end.send(:remove_method, :find_by_title) if Topic.public_methods.any? { |m| m.to_s == 'find_by_title' }
577
+ assert !Topic.public_methods.any? { |m| m.to_s == 'find_by_title' }
578
+ t = Topic.find_by_title("The First Topic")
579
+ assert Topic.public_methods.any? { |m| m.to_s == 'find_by_title' }
580
+ end
581
+
582
+ def test_dynamic_finder_returns_same_results_after_caching
583
+ # ensure this test can run independently of order
584
+ class << Topic; self; end.send(:remove_method, :find_by_title) if Topic.public_method_defined?(:find_by_title)
585
+ t = Topic.find_by_title("The First Topic")
586
+ assert_equal t, Topic.find_by_title("The First Topic") # find_by_title has been cached
587
+ end
588
+
589
+ def test_find_by_one_attribute_with_order_option
590
+ assert_equal accounts(:signals37), Account.find_by_credit_limit(50, :order => 'id')
591
+ assert_equal accounts(:rails_core_account), Account.find_by_credit_limit(50, :order => 'id DESC')
592
+ end
593
+
594
+ def test_find_by_one_attribute_with_conditions
595
+ assert_equal accounts(:rails_core_account), Account.find_by_credit_limit(50, :conditions => ['firm_id = ?', 6])
596
+ end
597
+
598
+ def test_find_by_one_attribute_that_is_an_aggregate
599
+ address = customers(:david).address
600
+ assert_kind_of Address, address
601
+ found_customer = Customer.find_by_address(address)
602
+ assert_equal customers(:david), found_customer
603
+ end
604
+
605
+ def test_find_by_one_attribute_that_is_an_aggregate_with_one_attribute_difference
606
+ address = customers(:david).address
607
+ assert_kind_of Address, address
608
+ missing_address = Address.new(address.street, address.city, address.country + "1")
609
+ assert_nil Customer.find_by_address(missing_address)
610
+ missing_address = Address.new(address.street, address.city + "1", address.country)
611
+ assert_nil Customer.find_by_address(missing_address)
612
+ missing_address = Address.new(address.street + "1", address.city, address.country)
613
+ assert_nil Customer.find_by_address(missing_address)
614
+ end
615
+
616
+ def test_find_by_two_attributes_that_are_both_aggregates
617
+ balance = customers(:david).balance
618
+ address = customers(:david).address
619
+ assert_kind_of Money, balance
620
+ assert_kind_of Address, address
621
+ found_customer = Customer.find_by_balance_and_address(balance, address)
622
+ assert_equal customers(:david), found_customer
623
+ end
624
+
625
+ def test_find_by_two_attributes_with_one_being_an_aggregate
626
+ balance = customers(:david).balance
627
+ assert_kind_of Money, balance
628
+ found_customer = Customer.find_by_balance_and_name(balance, customers(:david).name)
629
+ assert_equal customers(:david), found_customer
630
+ end
631
+
632
+ def test_dynamic_finder_on_one_attribute_with_conditions_caches_method
633
+ # ensure this test can run independently of order
634
+ class << Account; self; end.send(:remove_method, :find_by_credit_limit) if Account.public_methods.any? { |m| m.to_s == 'find_by_credit_limit' }
635
+ assert !Account.public_methods.any? { |m| m.to_s == 'find_by_credit_limit' }
636
+ a = Account.find_by_credit_limit(50, :conditions => ['firm_id = ?', 6])
637
+ assert Account.public_methods.any? { |m| m.to_s == 'find_by_credit_limit' }
638
+ end
639
+
640
+ def test_dynamic_finder_on_one_attribute_with_conditions_returns_same_results_after_caching
641
+ # ensure this test can run independently of order
642
+ class << Account; self; end.send(:remove_method, :find_by_credit_limit) if Account.public_methods.any? { |m| m.to_s == 'find_by_credit_limit' }
643
+ a = Account.find_by_credit_limit(50, :conditions => ['firm_id = ?', 6])
644
+ assert_equal a, Account.find_by_credit_limit(50, :conditions => ['firm_id = ?', 6]) # find_by_credit_limit has been cached
645
+ end
646
+
647
+ def test_find_by_one_attribute_with_several_options
648
+ assert_equal accounts(:unknown), Account.find_by_credit_limit(50, :order => 'id DESC', :conditions => ['id != ?', 3])
649
+ end
650
+
651
+ def test_find_by_one_missing_attribute
652
+ assert_raise(NoMethodError) { Topic.find_by_undertitle("The First Topic!") }
653
+ end
654
+
655
+ def test_find_by_invalid_method_syntax
656
+ assert_raise(NoMethodError) { Topic.fail_to_find_by_title("The First Topic") }
657
+ assert_raise(NoMethodError) { Topic.find_by_title?("The First Topic") }
658
+ assert_raise(NoMethodError) { Topic.fail_to_find_or_create_by_title("Nonexistent Title") }
659
+ assert_raise(NoMethodError) { Topic.find_or_create_by_title?("Nonexistent Title") }
660
+ end
661
+
662
+ def test_find_by_two_attributes
663
+ assert_equal topics(:first), Topic.find_by_title_and_author_name("The First Topic", "David")
664
+ assert_nil Topic.find_by_title_and_author_name("The First Topic", "Mary")
665
+ end
666
+
667
+ def test_find_last_by_one_attribute
668
+ assert_equal Topic.last, Topic.find_last_by_title(Topic.last.title)
669
+ assert_nil Topic.find_last_by_title("A title with no matches")
670
+ end
671
+
672
+ def test_find_last_by_one_attribute_caches_dynamic_finder
673
+ # ensure this test can run independently of order
674
+ class << Topic; self; end.send(:remove_method, :find_last_by_title) if Topic.public_methods.any? { |m| m.to_s == 'find_last_by_title' }
675
+ assert !Topic.public_methods.any? { |m| m.to_s == 'find_last_by_title' }
676
+ t = Topic.find_last_by_title(Topic.last.title)
677
+ assert Topic.public_methods.any? { |m| m.to_s == 'find_last_by_title' }
678
+ end
679
+
680
+ def test_find_last_by_invalid_method_syntax
681
+ assert_raise(NoMethodError) { Topic.fail_to_find_last_by_title("The First Topic") }
682
+ assert_raise(NoMethodError) { Topic.find_last_by_title?("The First Topic") }
683
+ end
684
+
685
+ def test_find_last_by_one_attribute_with_several_options
686
+ assert_equal accounts(:signals37), Account.find_last_by_credit_limit(50, :order => 'id DESC', :conditions => ['id != ?', 3])
687
+ end
688
+
689
+ def test_find_last_by_one_missing_attribute
690
+ assert_raise(NoMethodError) { Topic.find_last_by_undertitle("The Last Topic!") }
691
+ end
692
+
693
+ def test_find_last_by_two_attributes
694
+ topic = Topic.last
695
+ assert_equal topic, Topic.find_last_by_title_and_author_name(topic.title, topic.author_name)
696
+ assert_nil Topic.find_last_by_title_and_author_name(topic.title, "Anonymous")
697
+ end
698
+
699
+ def test_find_all_by_one_attribute
700
+ topics = Topic.find_all_by_content("Have a nice day")
701
+ assert_equal 2, topics.size
702
+ assert topics.include?(topics(:first))
703
+
704
+ assert_equal [], Topic.find_all_by_title("The First Topic!!")
705
+ end
706
+
707
+ def test_find_all_by_one_attribute_that_is_an_aggregate
708
+ balance = customers(:david).balance
709
+ assert_kind_of Money, balance
710
+ found_customers = Customer.find_all_by_balance(balance)
711
+ assert_equal 1, found_customers.size
712
+ assert_equal customers(:david), found_customers.first
713
+ end
714
+
715
+ def test_find_all_by_two_attributes_that_are_both_aggregates
716
+ balance = customers(:david).balance
717
+ address = customers(:david).address
718
+ assert_kind_of Money, balance
719
+ assert_kind_of Address, address
720
+ found_customers = Customer.find_all_by_balance_and_address(balance, address)
721
+ assert_equal 1, found_customers.size
722
+ assert_equal customers(:david), found_customers.first
723
+ end
724
+
725
+ def test_find_all_by_two_attributes_with_one_being_an_aggregate
726
+ balance = customers(:david).balance
727
+ assert_kind_of Money, balance
728
+ found_customers = Customer.find_all_by_balance_and_name(balance, customers(:david).name)
729
+ assert_equal 1, found_customers.size
730
+ assert_equal customers(:david), found_customers.first
731
+ end
732
+
733
+ def test_find_all_by_one_attribute_with_options
734
+ topics = Topic.find_all_by_content("Have a nice day", :order => "id DESC")
735
+ assert topics(:first), topics.last
736
+
737
+ topics = Topic.find_all_by_content("Have a nice day", :order => "id")
738
+ assert topics(:first), topics.first
739
+ end
740
+
741
+ def test_find_all_by_array_attribute
742
+ assert_equal 2, Topic.find_all_by_title(["The First Topic", "The Second Topic of the day"]).size
743
+ end
744
+
745
+ def test_find_all_by_boolean_attribute
746
+ topics = Topic.find_all_by_approved(false)
747
+ assert_equal 1, topics.size
748
+ assert topics.include?(topics(:first))
749
+
750
+ topics = Topic.find_all_by_approved(true)
751
+ assert_equal 3, topics.size
752
+ assert topics.include?(topics(:second))
753
+ end
754
+
755
+ def test_find_by_nil_attribute
756
+ topic = Topic.find_by_last_read nil
757
+ assert_not_nil topic
758
+ assert_nil topic.last_read
759
+ end
760
+
761
+ def test_find_all_by_nil_attribute
762
+ topics = Topic.find_all_by_last_read nil
763
+ assert_equal 3, topics.size
764
+ assert topics.collect(&:last_read).all?(&:nil?)
765
+ end
766
+
767
+ def test_find_by_nil_and_not_nil_attributes
768
+ topic = Topic.find_by_last_read_and_author_name nil, "Mary"
769
+ assert_equal "Mary", topic.author_name
770
+ end
771
+
772
+ def test_find_all_by_nil_and_not_nil_attributes
773
+ topics = Topic.find_all_by_last_read_and_author_name nil, "Mary"
774
+ assert_equal 1, topics.size
775
+ assert_equal "Mary", topics[0].author_name
776
+ end
777
+
778
+ def test_find_or_create_from_one_attribute
779
+ number_of_companies = Company.count
780
+ sig38 = Company.find_or_create_by_name("38signals")
781
+ assert_equal number_of_companies + 1, Company.count
782
+ assert_equal sig38, Company.find_or_create_by_name("38signals")
783
+ assert !sig38.new_record?
784
+ end
785
+
786
+ def test_find_or_create_from_two_attributes
787
+ number_of_topics = Topic.count
788
+ another = Topic.find_or_create_by_title_and_author_name("Another topic","John")
789
+ assert_equal number_of_topics + 1, Topic.count
790
+ assert_equal another, Topic.find_or_create_by_title_and_author_name("Another topic", "John")
791
+ assert !another.new_record?
792
+ end
793
+
794
+ def test_find_or_create_from_two_attributes_with_one_being_an_aggregate
795
+ number_of_customers = Customer.count
796
+ created_customer = Customer.find_or_create_by_balance_and_name(Money.new(123), "Elizabeth")
797
+ assert_equal number_of_customers + 1, Customer.count
798
+ assert_equal created_customer, Customer.find_or_create_by_balance(Money.new(123), "Elizabeth")
799
+ assert !created_customer.new_record?
800
+ end
801
+
802
+ def test_find_or_create_from_one_attribute_and_hash
803
+ number_of_companies = Company.count
804
+ sig38 = Company.find_or_create_by_name({:name => "38signals", :firm_id => 17, :client_of => 23})
805
+ assert_equal number_of_companies + 1, Company.count
806
+ assert_equal sig38, Company.find_or_create_by_name({:name => "38signals", :firm_id => 17, :client_of => 23})
807
+ assert !sig38.new_record?
808
+ assert_equal "38signals", sig38.name
809
+ assert_equal 17, sig38.firm_id
810
+ assert_equal 23, sig38.client_of
811
+ end
812
+
813
+ def test_find_or_create_from_one_aggregate_attribute
814
+ number_of_customers = Customer.count
815
+ created_customer = Customer.find_or_create_by_balance(Money.new(123))
816
+ assert_equal number_of_customers + 1, Customer.count
817
+ assert_equal created_customer, Customer.find_or_create_by_balance(Money.new(123))
818
+ assert !created_customer.new_record?
819
+ end
820
+
821
+ def test_find_or_create_from_one_aggregate_attribute_and_hash
822
+ number_of_customers = Customer.count
823
+ balance = Money.new(123)
824
+ name = "Elizabeth"
825
+ created_customer = Customer.find_or_create_by_balance({:balance => balance, :name => name})
826
+ assert_equal number_of_customers + 1, Customer.count
827
+ assert_equal created_customer, Customer.find_or_create_by_balance({:balance => balance, :name => name})
828
+ assert !created_customer.new_record?
829
+ assert_equal balance, created_customer.balance
830
+ assert_equal name, created_customer.name
831
+ end
832
+
833
+ def test_find_or_initialize_from_one_attribute
834
+ sig38 = Company.find_or_initialize_by_name("38signals")
835
+ assert_equal "38signals", sig38.name
836
+ assert sig38.new_record?
837
+ end
838
+
839
+ def test_find_or_initialize_from_one_aggregate_attribute
840
+ new_customer = Customer.find_or_initialize_by_balance(Money.new(123))
841
+ assert_equal 123, new_customer.balance.amount
842
+ assert new_customer.new_record?
843
+ end
844
+
845
+ def test_find_or_initialize_from_one_attribute_should_not_set_attribute_even_when_protected
846
+ c = Company.find_or_initialize_by_name({:name => "Fortune 1000", :rating => 1000})
847
+ assert_equal "Fortune 1000", c.name
848
+ assert_not_equal 1000, c.rating
849
+ assert c.valid?
850
+ assert c.new_record?
851
+ end
852
+
853
+ def test_find_or_create_from_one_attribute_should_set_not_attribute_even_when_protected
854
+ c = Company.find_or_create_by_name({:name => "Fortune 1000", :rating => 1000})
855
+ assert_equal "Fortune 1000", c.name
856
+ assert_not_equal 1000, c.rating
857
+ assert c.valid?
858
+ assert !c.new_record?
859
+ end
860
+
861
+ def test_find_or_initialize_from_one_attribute_should_set_attribute_even_when_protected
862
+ c = Company.find_or_initialize_by_name_and_rating("Fortune 1000", 1000)
863
+ assert_equal "Fortune 1000", c.name
864
+ assert_equal 1000, c.rating
865
+ assert c.valid?
866
+ assert c.new_record?
867
+ end
868
+
869
+ def test_find_or_create_from_one_attribute_should_set_attribute_even_when_protected
870
+ c = Company.find_or_create_by_name_and_rating("Fortune 1000", 1000)
871
+ assert_equal "Fortune 1000", c.name
872
+ assert_equal 1000, c.rating
873
+ assert c.valid?
874
+ assert !c.new_record?
875
+ end
876
+
877
+ def test_find_or_initialize_should_set_protected_attributes_if_given_as_block
878
+ c = Company.find_or_initialize_by_name(:name => "Fortune 1000") { |f| f.rating = 1000 }
879
+ assert_equal "Fortune 1000", c.name
880
+ assert_equal 1000.to_f, c.rating.to_f
881
+ assert c.valid?
882
+ assert c.new_record?
883
+ end
884
+
885
+ def test_find_or_create_should_set_protected_attributes_if_given_as_block
886
+ c = Company.find_or_create_by_name(:name => "Fortune 1000") { |f| f.rating = 1000 }
887
+ assert_equal "Fortune 1000", c.name
888
+ assert_equal 1000.to_f, c.rating.to_f
889
+ assert c.valid?
890
+ assert !c.new_record?
891
+ end
892
+
893
+ def test_find_or_create_should_work_with_block_on_first_call
894
+ class << Company
895
+ undef_method(:find_or_create_by_name) if method_defined?(:find_or_create_by_name)
896
+ end
897
+ c = Company.find_or_create_by_name(:name => "Fortune 1000") { |f| f.rating = 1000 }
898
+ assert_equal "Fortune 1000", c.name
899
+ assert_equal 1000.to_f, c.rating.to_f
900
+ assert c.valid?
901
+ assert !c.new_record?
902
+ end
903
+
904
+ def test_dynamic_find_or_initialize_from_one_attribute_caches_method
905
+ class << Company; self; end.send(:remove_method, :find_or_initialize_by_name) if Company.public_methods.any? { |m| m.to_s == 'find_or_initialize_by_name' }
906
+ assert !Company.public_methods.any? { |m| m.to_s == 'find_or_initialize_by_name' }
907
+ sig38 = Company.find_or_initialize_by_name("38signals")
908
+ assert Company.public_methods.any? { |m| m.to_s == 'find_or_initialize_by_name' }
909
+ end
910
+
911
+ def test_find_or_initialize_from_two_attributes
912
+ another = Topic.find_or_initialize_by_title_and_author_name("Another topic","John")
913
+ assert_equal "Another topic", another.title
914
+ assert_equal "John", another.author_name
915
+ assert another.new_record?
916
+ end
917
+
918
+ def test_find_or_initialize_from_one_aggregate_attribute_and_one_not
919
+ new_customer = Customer.find_or_initialize_by_balance_and_name(Money.new(123), "Elizabeth")
920
+ assert_equal 123, new_customer.balance.amount
921
+ assert_equal "Elizabeth", new_customer.name
922
+ assert new_customer.new_record?
923
+ end
924
+
925
+ def test_find_or_initialize_from_one_attribute_and_hash
926
+ sig38 = Company.find_or_initialize_by_name({:name => "38signals", :firm_id => 17, :client_of => 23})
927
+ assert_equal "38signals", sig38.name
928
+ assert_equal 17, sig38.firm_id
929
+ assert_equal 23, sig38.client_of
930
+ assert sig38.new_record?
931
+ end
932
+
933
+ def test_find_or_initialize_from_one_aggregate_attribute_and_hash
934
+ balance = Money.new(123)
935
+ name = "Elizabeth"
936
+ new_customer = Customer.find_or_initialize_by_balance({:balance => balance, :name => name})
937
+ assert_equal balance, new_customer.balance
938
+ assert_equal name, new_customer.name
939
+ assert new_customer.new_record?
940
+ end
941
+
942
+ def test_find_with_bad_sql
943
+ assert_raise(ActiveRecord::StatementInvalid) { Topic.find_by_sql "select 1 from badtable" }
944
+ end
945
+
946
+ def test_find_with_invalid_params
947
+ assert_raise(ArgumentError) { Topic.find :first, :join => "It should be `joins'" }
948
+ assert_raise(ArgumentError) { Topic.find :first, :conditions => '1 = 1', :join => "It should be `joins'" }
949
+ end
950
+
951
+ def test_dynamic_finder_with_invalid_params
952
+ assert_raise(ArgumentError) { Topic.find_by_title 'No Title', :join => "It should be `joins'" }
953
+ end
954
+
955
+ def test_find_all_with_join
956
+ developers_on_project_one = Developer.find(
957
+ :all,
958
+ :joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id',
959
+ :conditions => 'project_id=1'
960
+ )
961
+ assert_equal 3, developers_on_project_one.length
962
+ developer_names = developers_on_project_one.map { |d| d.name }
963
+ assert developer_names.include?('David')
964
+ assert developer_names.include?('Jamis')
965
+ end
966
+
967
+ def test_joins_dont_clobber_id
968
+ first = Firm.find(
969
+ :first,
970
+ :joins => 'INNER JOIN companies AS clients ON clients.firm_id = companies.id',
971
+ :conditions => 'companies.id = 1'
972
+ )
973
+ assert_equal 1, first.id
974
+ end
975
+
976
+ def test_joins_with_string_array
977
+ person_with_reader_and_post = Post.find(
978
+ :all,
979
+ :joins => [
980
+ "INNER JOIN categorizations ON categorizations.post_id = posts.id",
981
+ "INNER JOIN categories ON categories.id = categorizations.category_id AND categories.type = 'SpecialCategory'"
982
+ ]
983
+ )
984
+ assert_equal 1, person_with_reader_and_post.size
985
+ end
986
+
987
+ def test_find_by_id_with_conditions_with_or
988
+ assert_nothing_raised do
989
+ Post.find([1,2,3],
990
+ :conditions => "posts.id <= 3 OR posts.#{QUOTED_TYPE} = 'Post'")
991
+ end
992
+ end
993
+
994
+ # http://dev.rubyonrails.org/ticket/6778
995
+ def test_find_ignores_previously_inserted_record
996
+ post = Post.create!(:title => 'test', :body => 'it out')
997
+ assert_equal [], Post.find_all_by_id(nil)
998
+ end
999
+
1000
+ def test_find_by_empty_ids
1001
+ assert_equal [], Post.find([])
1002
+ end
1003
+
1004
+ def test_find_by_empty_in_condition
1005
+ assert_equal [], Post.find(:all, :conditions => ['id in (?)', []])
1006
+ end
1007
+
1008
+ def test_find_by_records
1009
+ p1, p2 = Post.find(:all, :limit => 2, :order => 'id asc')
1010
+ assert_equal [p1, p2], Post.find(:all, :conditions => ['id in (?)', [p1, p2]], :order => 'id asc')
1011
+ assert_equal [p1, p2], Post.find(:all, :conditions => ['id in (?)', [p1, p2.id]], :order => 'id asc')
1012
+ end
1013
+
1014
+ def test_select_value
1015
+ assert_equal "37signals", Company.connection.select_value("SELECT name FROM companies WHERE id = 1")
1016
+ assert_nil Company.connection.select_value("SELECT name FROM companies WHERE id = -1")
1017
+ # make sure we didn't break count...
1018
+ assert_equal 0, Company.count_by_sql("SELECT COUNT(*) FROM companies WHERE name = 'Halliburton'")
1019
+ assert_equal 1, Company.count_by_sql("SELECT COUNT(*) FROM companies WHERE name = '37signals'")
1020
+ end
1021
+
1022
+ def test_select_values
1023
+ assert_equal ["1","2","3","4","5","6","7","8","9"], Company.connection.select_values("SELECT id FROM companies ORDER BY id").map! { |i| i.to_s }
1024
+ assert_equal ["37signals","Summit","Microsoft", "Flamboyant Software", "Ex Nihilo", "RailsCore", "Leetsoft", "Jadedpixel", "Odegy"], Company.connection.select_values("SELECT name FROM companies ORDER BY id")
1025
+ end
1026
+
1027
+ def test_select_rows
1028
+ assert_equal(
1029
+ [["1", nil, nil, "37signals"],
1030
+ ["2", "1", "2", "Summit"],
1031
+ ["3", "1", "1", "Microsoft"]],
1032
+ 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?}})
1033
+ assert_equal [["1", "37signals"], ["2", "Summit"], ["3", "Microsoft"]],
1034
+ 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?}}
1035
+ end
1036
+
1037
+ def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct
1038
+ assert_equal 2, Post.find(:all, :include => { :authors => :author_address }, :order => ' author_addresses.id DESC ', :limit => 2).size
1039
+
1040
+ assert_equal 3, Post.find(:all, :include => { :author => :author_address, :authors => :author_address},
1041
+ :order => ' author_addresses_authors.id DESC ', :limit => 3).size
1042
+ end
1043
+
1044
+ def test_with_limiting_with_custom_select
1045
+ posts = Post.find(:all, :include => :author, :select => ' posts.*, authors.id as "author_id"', :limit => 3, :order => 'posts.id')
1046
+ assert_equal 3, posts.size
1047
+ assert_equal [0, 1, 1], posts.map(&:author_id).sort
1048
+ end
1049
+
1050
+ def test_finder_with_scoped_from
1051
+ all_topics = Topic.all
1052
+
1053
+ Topic.with_scope(:find => { :from => 'fake_topics' }) do
1054
+ assert_equal all_topics, Topic.all(:from => 'topics')
1055
+ end
1056
+ end
1057
+
1058
+ protected
1059
+ def bind(statement, *vars)
1060
+ if vars.first.is_a?(Hash)
1061
+ ActiveRecord::Base.send(:replace_named_bind_variables, statement, vars.first)
1062
+ else
1063
+ ActiveRecord::Base.send(:replace_bind_variables, statement, vars)
1064
+ end
1065
+ end
1066
+ end