activerecord 2.3.18 → 3.2.22

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (454) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1014 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +222 -0
  5. data/examples/performance.rb +100 -126
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record/aggregations.rb +93 -99
  8. data/lib/active_record/associations/alias_tracker.rb +76 -0
  9. data/lib/active_record/associations/association.rb +247 -0
  10. data/lib/active_record/associations/association_scope.rb +134 -0
  11. data/lib/active_record/associations/belongs_to_association.rb +54 -61
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +17 -59
  13. data/lib/active_record/associations/builder/association.rb +55 -0
  14. data/lib/active_record/associations/builder/belongs_to.rb +88 -0
  15. data/lib/active_record/associations/builder/collection_association.rb +75 -0
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +57 -0
  17. data/lib/active_record/associations/builder/has_many.rb +71 -0
  18. data/lib/active_record/associations/builder/has_one.rb +62 -0
  19. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  20. data/lib/active_record/associations/collection_association.rb +580 -0
  21. data/lib/active_record/associations/collection_proxy.rb +133 -0
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +39 -119
  23. data/lib/active_record/associations/has_many_association.rb +60 -79
  24. data/lib/active_record/associations/has_many_through_association.rb +127 -206
  25. data/lib/active_record/associations/has_one_association.rb +55 -114
  26. data/lib/active_record/associations/has_one_through_association.rb +25 -26
  27. data/lib/active_record/associations/join_dependency/join_association.rb +159 -0
  28. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  29. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  30. data/lib/active_record/associations/join_dependency.rb +214 -0
  31. data/lib/active_record/associations/join_helper.rb +55 -0
  32. data/lib/active_record/associations/preloader/association.rb +125 -0
  33. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  34. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  35. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  36. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  37. data/lib/active_record/associations/preloader/has_many_through.rb +15 -0
  38. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  39. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  40. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  41. data/lib/active_record/associations/preloader/through_association.rb +67 -0
  42. data/lib/active_record/associations/preloader.rb +181 -0
  43. data/lib/active_record/associations/singular_association.rb +64 -0
  44. data/lib/active_record/associations/through_association.rb +87 -0
  45. data/lib/active_record/associations.rb +693 -1337
  46. data/lib/active_record/attribute_assignment.rb +221 -0
  47. data/lib/active_record/attribute_methods/before_type_cast.rb +31 -0
  48. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
  49. data/lib/active_record/attribute_methods/dirty.rb +111 -0
  50. data/lib/active_record/attribute_methods/primary_key.rb +114 -0
  51. data/lib/active_record/attribute_methods/query.rb +39 -0
  52. data/lib/active_record/attribute_methods/read.rb +136 -0
  53. data/lib/active_record/attribute_methods/serialization.rb +120 -0
  54. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -0
  55. data/lib/active_record/attribute_methods/write.rb +70 -0
  56. data/lib/active_record/attribute_methods.rb +211 -339
  57. data/lib/active_record/autosave_association.rb +179 -149
  58. data/lib/active_record/base.rb +401 -2907
  59. data/lib/active_record/callbacks.rb +91 -176
  60. data/lib/active_record/coders/yaml_column.rb +41 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +236 -119
  62. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +110 -58
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +12 -11
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +175 -74
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -35
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +71 -21
  67. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +81 -311
  68. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +194 -78
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +130 -83
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +676 -0
  71. data/lib/active_record/connection_adapters/column.rb +296 -0
  72. data/lib/active_record/connection_adapters/mysql2_adapter.rb +280 -0
  73. data/lib/active_record/connection_adapters/mysql_adapter.rb +272 -493
  74. data/lib/active_record/connection_adapters/postgresql_adapter.rb +650 -405
  75. data/lib/active_record/connection_adapters/schema_cache.rb +69 -0
  76. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +30 -9
  77. data/lib/active_record/connection_adapters/sqlite_adapter.rb +276 -147
  78. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  79. data/lib/active_record/counter_cache.rb +123 -0
  80. data/lib/active_record/dynamic_finder_match.rb +41 -14
  81. data/lib/active_record/dynamic_matchers.rb +84 -0
  82. data/lib/active_record/dynamic_scope_match.rb +13 -15
  83. data/lib/active_record/errors.rb +195 -0
  84. data/lib/active_record/explain.rb +86 -0
  85. data/lib/active_record/explain_subscriber.rb +25 -0
  86. data/lib/active_record/fixtures/file.rb +65 -0
  87. data/lib/active_record/fixtures.rb +695 -770
  88. data/lib/active_record/identity_map.rb +162 -0
  89. data/lib/active_record/inheritance.rb +174 -0
  90. data/lib/active_record/integration.rb +60 -0
  91. data/lib/active_record/locale/en.yml +9 -27
  92. data/lib/active_record/locking/optimistic.rb +76 -73
  93. data/lib/active_record/locking/pessimistic.rb +32 -10
  94. data/lib/active_record/log_subscriber.rb +72 -0
  95. data/lib/active_record/migration/command_recorder.rb +105 -0
  96. data/lib/active_record/migration.rb +415 -205
  97. data/lib/active_record/model_schema.rb +368 -0
  98. data/lib/active_record/nested_attributes.rb +153 -63
  99. data/lib/active_record/observer.rb +27 -103
  100. data/lib/active_record/persistence.rb +376 -0
  101. data/lib/active_record/query_cache.rb +49 -8
  102. data/lib/active_record/querying.rb +58 -0
  103. data/lib/active_record/railtie.rb +131 -0
  104. data/lib/active_record/railties/console_sandbox.rb +6 -0
  105. data/lib/active_record/railties/controller_runtime.rb +49 -0
  106. data/lib/active_record/railties/databases.rake +659 -0
  107. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  108. data/lib/active_record/readonly_attributes.rb +26 -0
  109. data/lib/active_record/reflection.rb +269 -120
  110. data/lib/active_record/relation/batches.rb +90 -0
  111. data/lib/active_record/relation/calculations.rb +372 -0
  112. data/lib/active_record/relation/delegation.rb +49 -0
  113. data/lib/active_record/relation/finder_methods.rb +402 -0
  114. data/lib/active_record/relation/predicate_builder.rb +63 -0
  115. data/lib/active_record/relation/query_methods.rb +417 -0
  116. data/lib/active_record/relation/spawn_methods.rb +180 -0
  117. data/lib/active_record/relation.rb +537 -0
  118. data/lib/active_record/result.rb +40 -0
  119. data/lib/active_record/sanitization.rb +194 -0
  120. data/lib/active_record/schema.rb +9 -6
  121. data/lib/active_record/schema_dumper.rb +55 -32
  122. data/lib/active_record/scoping/default.rb +142 -0
  123. data/lib/active_record/scoping/named.rb +200 -0
  124. data/lib/active_record/scoping.rb +152 -0
  125. data/lib/active_record/serialization.rb +8 -91
  126. data/lib/active_record/serializers/xml_serializer.rb +43 -197
  127. data/lib/active_record/session_store.rb +129 -103
  128. data/lib/active_record/store.rb +52 -0
  129. data/lib/active_record/test_case.rb +30 -23
  130. data/lib/active_record/timestamp.rb +95 -52
  131. data/lib/active_record/transactions.rb +212 -66
  132. data/lib/active_record/translation.rb +22 -0
  133. data/lib/active_record/validations/associated.rb +43 -0
  134. data/lib/active_record/validations/uniqueness.rb +180 -0
  135. data/lib/active_record/validations.rb +43 -1106
  136. data/lib/active_record/version.rb +5 -4
  137. data/lib/active_record.rb +121 -48
  138. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  139. data/lib/rails/generators/active_record/migration/templates/migration.rb +34 -0
  140. data/lib/rails/generators/active_record/migration.rb +15 -0
  141. data/lib/rails/generators/active_record/model/model_generator.rb +47 -0
  142. data/lib/rails/generators/active_record/model/templates/migration.rb +15 -0
  143. data/lib/rails/generators/active_record/model/templates/model.rb +12 -0
  144. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  145. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  146. data/lib/rails/generators/active_record/observer/templates/observer.rb +4 -0
  147. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +25 -0
  148. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +12 -0
  149. data/lib/rails/generators/active_record.rb +25 -0
  150. metadata +187 -363
  151. data/CHANGELOG +0 -5904
  152. data/README +0 -351
  153. data/RUNNING_UNIT_TESTS +0 -36
  154. data/Rakefile +0 -268
  155. data/install.rb +0 -30
  156. data/lib/active_record/association_preload.rb +0 -406
  157. data/lib/active_record/associations/association_collection.rb +0 -533
  158. data/lib/active_record/associations/association_proxy.rb +0 -288
  159. data/lib/active_record/batches.rb +0 -85
  160. data/lib/active_record/calculations.rb +0 -321
  161. data/lib/active_record/dirty.rb +0 -183
  162. data/lib/active_record/named_scope.rb +0 -197
  163. data/lib/active_record/serializers/json_serializer.rb +0 -91
  164. data/lib/activerecord.rb +0 -2
  165. data/test/assets/example.log +0 -1
  166. data/test/assets/flowers.jpg +0 -0
  167. data/test/cases/aaa_create_tables_test.rb +0 -24
  168. data/test/cases/active_schema_test_mysql.rb +0 -122
  169. data/test/cases/active_schema_test_postgresql.rb +0 -24
  170. data/test/cases/adapter_test.rb +0 -144
  171. data/test/cases/aggregations_test.rb +0 -167
  172. data/test/cases/ar_schema_test.rb +0 -32
  173. data/test/cases/associations/belongs_to_associations_test.rb +0 -438
  174. data/test/cases/associations/callbacks_test.rb +0 -161
  175. data/test/cases/associations/cascaded_eager_loading_test.rb +0 -131
  176. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +0 -36
  177. data/test/cases/associations/eager_load_nested_include_test.rb +0 -131
  178. data/test/cases/associations/eager_load_nested_polymorphic_include.rb +0 -19
  179. data/test/cases/associations/eager_singularization_test.rb +0 -145
  180. data/test/cases/associations/eager_test.rb +0 -852
  181. data/test/cases/associations/extension_test.rb +0 -62
  182. data/test/cases/associations/habtm_join_table_test.rb +0 -56
  183. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +0 -827
  184. data/test/cases/associations/has_many_associations_test.rb +0 -1273
  185. data/test/cases/associations/has_many_through_associations_test.rb +0 -360
  186. data/test/cases/associations/has_one_associations_test.rb +0 -330
  187. data/test/cases/associations/has_one_through_associations_test.rb +0 -209
  188. data/test/cases/associations/inner_join_association_test.rb +0 -93
  189. data/test/cases/associations/inverse_associations_test.rb +0 -566
  190. data/test/cases/associations/join_model_test.rb +0 -712
  191. data/test/cases/associations_test.rb +0 -282
  192. data/test/cases/attribute_methods_test.rb +0 -305
  193. data/test/cases/autosave_association_test.rb +0 -1218
  194. data/test/cases/base_test.rb +0 -2166
  195. data/test/cases/batches_test.rb +0 -81
  196. data/test/cases/binary_test.rb +0 -30
  197. data/test/cases/calculations_test.rb +0 -360
  198. data/test/cases/callbacks_observers_test.rb +0 -38
  199. data/test/cases/callbacks_test.rb +0 -438
  200. data/test/cases/class_inheritable_attributes_test.rb +0 -32
  201. data/test/cases/column_alias_test.rb +0 -17
  202. data/test/cases/column_definition_test.rb +0 -70
  203. data/test/cases/connection_pool_test.rb +0 -25
  204. data/test/cases/connection_test_firebird.rb +0 -8
  205. data/test/cases/connection_test_mysql.rb +0 -65
  206. data/test/cases/copy_table_test_sqlite.rb +0 -80
  207. data/test/cases/counter_cache_test.rb +0 -84
  208. data/test/cases/database_statements_test.rb +0 -12
  209. data/test/cases/datatype_test_postgresql.rb +0 -204
  210. data/test/cases/date_time_test.rb +0 -37
  211. data/test/cases/default_test_firebird.rb +0 -16
  212. data/test/cases/defaults_test.rb +0 -111
  213. data/test/cases/deprecated_finder_test.rb +0 -30
  214. data/test/cases/dirty_test.rb +0 -316
  215. data/test/cases/finder_respond_to_test.rb +0 -76
  216. data/test/cases/finder_test.rb +0 -1098
  217. data/test/cases/fixtures_test.rb +0 -661
  218. data/test/cases/helper.rb +0 -68
  219. data/test/cases/i18n_test.rb +0 -46
  220. data/test/cases/inheritance_test.rb +0 -262
  221. data/test/cases/invalid_date_test.rb +0 -24
  222. data/test/cases/json_serialization_test.rb +0 -219
  223. data/test/cases/lifecycle_test.rb +0 -193
  224. data/test/cases/locking_test.rb +0 -350
  225. data/test/cases/method_scoping_test.rb +0 -704
  226. data/test/cases/migration_test.rb +0 -1649
  227. data/test/cases/migration_test_firebird.rb +0 -124
  228. data/test/cases/mixin_test.rb +0 -96
  229. data/test/cases/modules_test.rb +0 -109
  230. data/test/cases/multiple_db_test.rb +0 -85
  231. data/test/cases/named_scope_test.rb +0 -372
  232. data/test/cases/nested_attributes_test.rb +0 -840
  233. data/test/cases/pk_test.rb +0 -119
  234. data/test/cases/pooled_connections_test.rb +0 -103
  235. data/test/cases/query_cache_test.rb +0 -129
  236. data/test/cases/readonly_test.rb +0 -107
  237. data/test/cases/reflection_test.rb +0 -234
  238. data/test/cases/reload_models_test.rb +0 -22
  239. data/test/cases/repair_helper.rb +0 -50
  240. data/test/cases/reserved_word_test_mysql.rb +0 -176
  241. data/test/cases/sanitize_test.rb +0 -25
  242. data/test/cases/schema_authorization_test_postgresql.rb +0 -75
  243. data/test/cases/schema_dumper_test.rb +0 -211
  244. data/test/cases/schema_test_postgresql.rb +0 -178
  245. data/test/cases/serialization_test.rb +0 -47
  246. data/test/cases/sp_test_mysql.rb +0 -16
  247. data/test/cases/synonym_test_oracle.rb +0 -17
  248. data/test/cases/timestamp_test.rb +0 -75
  249. data/test/cases/transactions_test.rb +0 -543
  250. data/test/cases/unconnected_test.rb +0 -32
  251. data/test/cases/validations_i18n_test.rb +0 -925
  252. data/test/cases/validations_test.rb +0 -1684
  253. data/test/cases/xml_serialization_test.rb +0 -240
  254. data/test/cases/yaml_serialization_test.rb +0 -11
  255. data/test/config.rb +0 -5
  256. data/test/connections/jdbc_jdbcderby/connection.rb +0 -18
  257. data/test/connections/jdbc_jdbch2/connection.rb +0 -18
  258. data/test/connections/jdbc_jdbchsqldb/connection.rb +0 -18
  259. data/test/connections/jdbc_jdbcmysql/connection.rb +0 -26
  260. data/test/connections/jdbc_jdbcpostgresql/connection.rb +0 -26
  261. data/test/connections/jdbc_jdbcsqlite3/connection.rb +0 -25
  262. data/test/connections/native_db2/connection.rb +0 -25
  263. data/test/connections/native_firebird/connection.rb +0 -26
  264. data/test/connections/native_frontbase/connection.rb +0 -27
  265. data/test/connections/native_mysql/connection.rb +0 -25
  266. data/test/connections/native_openbase/connection.rb +0 -21
  267. data/test/connections/native_oracle/connection.rb +0 -27
  268. data/test/connections/native_postgresql/connection.rb +0 -21
  269. data/test/connections/native_sqlite/connection.rb +0 -25
  270. data/test/connections/native_sqlite3/connection.rb +0 -25
  271. data/test/connections/native_sqlite3/in_memory_connection.rb +0 -18
  272. data/test/connections/native_sybase/connection.rb +0 -23
  273. data/test/fixtures/accounts.yml +0 -29
  274. data/test/fixtures/all/developers.yml +0 -0
  275. data/test/fixtures/all/people.csv +0 -0
  276. data/test/fixtures/all/tasks.yml +0 -0
  277. data/test/fixtures/author_addresses.yml +0 -5
  278. data/test/fixtures/author_favorites.yml +0 -4
  279. data/test/fixtures/authors.yml +0 -9
  280. data/test/fixtures/binaries.yml +0 -132
  281. data/test/fixtures/books.yml +0 -7
  282. data/test/fixtures/categories/special_categories.yml +0 -9
  283. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +0 -4
  284. data/test/fixtures/categories.yml +0 -14
  285. data/test/fixtures/categories_ordered.yml +0 -7
  286. data/test/fixtures/categories_posts.yml +0 -23
  287. data/test/fixtures/categorizations.yml +0 -17
  288. data/test/fixtures/clubs.yml +0 -6
  289. data/test/fixtures/comments.yml +0 -59
  290. data/test/fixtures/companies.yml +0 -56
  291. data/test/fixtures/computers.yml +0 -4
  292. data/test/fixtures/courses.yml +0 -7
  293. data/test/fixtures/customers.yml +0 -26
  294. data/test/fixtures/developers.yml +0 -21
  295. data/test/fixtures/developers_projects.yml +0 -17
  296. data/test/fixtures/edges.yml +0 -6
  297. data/test/fixtures/entrants.yml +0 -14
  298. data/test/fixtures/faces.yml +0 -11
  299. data/test/fixtures/fk_test_has_fk.yml +0 -3
  300. data/test/fixtures/fk_test_has_pk.yml +0 -2
  301. data/test/fixtures/funny_jokes.yml +0 -10
  302. data/test/fixtures/interests.yml +0 -33
  303. data/test/fixtures/items.yml +0 -4
  304. data/test/fixtures/jobs.yml +0 -7
  305. data/test/fixtures/legacy_things.yml +0 -3
  306. data/test/fixtures/mateys.yml +0 -4
  307. data/test/fixtures/member_types.yml +0 -6
  308. data/test/fixtures/members.yml +0 -6
  309. data/test/fixtures/memberships.yml +0 -20
  310. data/test/fixtures/men.yml +0 -5
  311. data/test/fixtures/minimalistics.yml +0 -2
  312. data/test/fixtures/mixed_case_monkeys.yml +0 -6
  313. data/test/fixtures/mixins.yml +0 -29
  314. data/test/fixtures/movies.yml +0 -7
  315. data/test/fixtures/naked/csv/accounts.csv +0 -1
  316. data/test/fixtures/naked/yml/accounts.yml +0 -1
  317. data/test/fixtures/naked/yml/companies.yml +0 -1
  318. data/test/fixtures/naked/yml/courses.yml +0 -1
  319. data/test/fixtures/organizations.yml +0 -5
  320. data/test/fixtures/owners.yml +0 -7
  321. data/test/fixtures/parrots.yml +0 -27
  322. data/test/fixtures/parrots_pirates.yml +0 -7
  323. data/test/fixtures/people.yml +0 -15
  324. data/test/fixtures/pets.yml +0 -14
  325. data/test/fixtures/pirates.yml +0 -9
  326. data/test/fixtures/polymorphic_designs.yml +0 -19
  327. data/test/fixtures/polymorphic_prices.yml +0 -19
  328. data/test/fixtures/posts.yml +0 -52
  329. data/test/fixtures/price_estimates.yml +0 -7
  330. data/test/fixtures/projects.yml +0 -7
  331. data/test/fixtures/readers.yml +0 -9
  332. data/test/fixtures/references.yml +0 -17
  333. data/test/fixtures/reserved_words/distinct.yml +0 -5
  334. data/test/fixtures/reserved_words/distincts_selects.yml +0 -11
  335. data/test/fixtures/reserved_words/group.yml +0 -14
  336. data/test/fixtures/reserved_words/select.yml +0 -8
  337. data/test/fixtures/reserved_words/values.yml +0 -7
  338. data/test/fixtures/ships.yml +0 -5
  339. data/test/fixtures/sponsors.yml +0 -9
  340. data/test/fixtures/subscribers.yml +0 -7
  341. data/test/fixtures/subscriptions.yml +0 -12
  342. data/test/fixtures/taggings.yml +0 -28
  343. data/test/fixtures/tags.yml +0 -7
  344. data/test/fixtures/tasks.yml +0 -7
  345. data/test/fixtures/tees.yml +0 -4
  346. data/test/fixtures/ties.yml +0 -4
  347. data/test/fixtures/topics.yml +0 -42
  348. data/test/fixtures/toys.yml +0 -4
  349. data/test/fixtures/treasures.yml +0 -10
  350. data/test/fixtures/vertices.yml +0 -4
  351. data/test/fixtures/warehouse-things.yml +0 -3
  352. data/test/fixtures/zines.yml +0 -5
  353. data/test/migrations/broken/100_migration_that_raises_exception.rb +0 -10
  354. data/test/migrations/decimal/1_give_me_big_numbers.rb +0 -15
  355. data/test/migrations/duplicate/1_people_have_last_names.rb +0 -9
  356. data/test/migrations/duplicate/2_we_need_reminders.rb +0 -12
  357. data/test/migrations/duplicate/3_foo.rb +0 -7
  358. data/test/migrations/duplicate/3_innocent_jointable.rb +0 -12
  359. data/test/migrations/duplicate_names/20080507052938_chunky.rb +0 -7
  360. data/test/migrations/duplicate_names/20080507053028_chunky.rb +0 -7
  361. data/test/migrations/interleaved/pass_1/3_innocent_jointable.rb +0 -12
  362. data/test/migrations/interleaved/pass_2/1_people_have_last_names.rb +0 -9
  363. data/test/migrations/interleaved/pass_2/3_innocent_jointable.rb +0 -12
  364. data/test/migrations/interleaved/pass_3/1_people_have_last_names.rb +0 -9
  365. data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +0 -8
  366. data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +0 -12
  367. data/test/migrations/missing/1000_people_have_middle_names.rb +0 -9
  368. data/test/migrations/missing/1_people_have_last_names.rb +0 -9
  369. data/test/migrations/missing/3_we_need_reminders.rb +0 -12
  370. data/test/migrations/missing/4_innocent_jointable.rb +0 -12
  371. data/test/migrations/valid/1_people_have_last_names.rb +0 -9
  372. data/test/migrations/valid/2_we_need_reminders.rb +0 -12
  373. data/test/migrations/valid/3_innocent_jointable.rb +0 -12
  374. data/test/models/author.rb +0 -151
  375. data/test/models/auto_id.rb +0 -4
  376. data/test/models/binary.rb +0 -2
  377. data/test/models/bird.rb +0 -9
  378. data/test/models/book.rb +0 -4
  379. data/test/models/categorization.rb +0 -5
  380. data/test/models/category.rb +0 -34
  381. data/test/models/citation.rb +0 -6
  382. data/test/models/club.rb +0 -13
  383. data/test/models/column_name.rb +0 -3
  384. data/test/models/comment.rb +0 -29
  385. data/test/models/company.rb +0 -173
  386. data/test/models/company_in_module.rb +0 -78
  387. data/test/models/computer.rb +0 -3
  388. data/test/models/contact.rb +0 -16
  389. data/test/models/contract.rb +0 -5
  390. data/test/models/course.rb +0 -3
  391. data/test/models/customer.rb +0 -73
  392. data/test/models/default.rb +0 -2
  393. data/test/models/developer.rb +0 -101
  394. data/test/models/edge.rb +0 -5
  395. data/test/models/entrant.rb +0 -3
  396. data/test/models/essay.rb +0 -3
  397. data/test/models/event.rb +0 -3
  398. data/test/models/event_author.rb +0 -8
  399. data/test/models/face.rb +0 -7
  400. data/test/models/guid.rb +0 -2
  401. data/test/models/interest.rb +0 -5
  402. data/test/models/invoice.rb +0 -4
  403. data/test/models/item.rb +0 -7
  404. data/test/models/job.rb +0 -5
  405. data/test/models/joke.rb +0 -3
  406. data/test/models/keyboard.rb +0 -3
  407. data/test/models/legacy_thing.rb +0 -3
  408. data/test/models/line_item.rb +0 -3
  409. data/test/models/man.rb +0 -9
  410. data/test/models/matey.rb +0 -4
  411. data/test/models/member.rb +0 -12
  412. data/test/models/member_detail.rb +0 -5
  413. data/test/models/member_type.rb +0 -3
  414. data/test/models/membership.rb +0 -9
  415. data/test/models/minimalistic.rb +0 -2
  416. data/test/models/mixed_case_monkey.rb +0 -3
  417. data/test/models/movie.rb +0 -5
  418. data/test/models/order.rb +0 -4
  419. data/test/models/organization.rb +0 -6
  420. data/test/models/owner.rb +0 -5
  421. data/test/models/parrot.rb +0 -22
  422. data/test/models/person.rb +0 -16
  423. data/test/models/pet.rb +0 -5
  424. data/test/models/pirate.rb +0 -80
  425. data/test/models/polymorphic_design.rb +0 -3
  426. data/test/models/polymorphic_price.rb +0 -3
  427. data/test/models/post.rb +0 -102
  428. data/test/models/price_estimate.rb +0 -3
  429. data/test/models/project.rb +0 -30
  430. data/test/models/reader.rb +0 -4
  431. data/test/models/reference.rb +0 -4
  432. data/test/models/reply.rb +0 -46
  433. data/test/models/ship.rb +0 -19
  434. data/test/models/ship_part.rb +0 -7
  435. data/test/models/sponsor.rb +0 -4
  436. data/test/models/subject.rb +0 -4
  437. data/test/models/subscriber.rb +0 -8
  438. data/test/models/subscription.rb +0 -4
  439. data/test/models/tag.rb +0 -7
  440. data/test/models/tagging.rb +0 -10
  441. data/test/models/task.rb +0 -3
  442. data/test/models/tee.rb +0 -4
  443. data/test/models/tie.rb +0 -4
  444. data/test/models/topic.rb +0 -80
  445. data/test/models/toy.rb +0 -6
  446. data/test/models/treasure.rb +0 -8
  447. data/test/models/vertex.rb +0 -9
  448. data/test/models/warehouse_thing.rb +0 -5
  449. data/test/models/zine.rb +0 -3
  450. data/test/schema/mysql_specific_schema.rb +0 -31
  451. data/test/schema/postgresql_specific_schema.rb +0 -114
  452. data/test/schema/schema.rb +0 -550
  453. data/test/schema/schema2.rb +0 -6
  454. data/test/schema/sqlite_specific_schema.rb +0 -25
@@ -1,288 +0,0 @@
1
- module ActiveRecord
2
- module Associations
3
- # This is the root class of all association proxies:
4
- #
5
- # AssociationProxy
6
- # BelongsToAssociation
7
- # HasOneAssociation
8
- # BelongsToPolymorphicAssociation
9
- # AssociationCollection
10
- # HasAndBelongsToManyAssociation
11
- # HasManyAssociation
12
- # HasManyThroughAssociation
13
- # HasOneThroughAssociation
14
- #
15
- # Association proxies in Active Record are middlemen between the object that
16
- # holds the association, known as the <tt>@owner</tt>, and the actual associated
17
- # object, known as the <tt>@target</tt>. The kind of association any proxy is
18
- # about is available in <tt>@reflection</tt>. That's an instance of the class
19
- # ActiveRecord::Reflection::AssociationReflection.
20
- #
21
- # For example, given
22
- #
23
- # class Blog < ActiveRecord::Base
24
- # has_many :posts
25
- # end
26
- #
27
- # blog = Blog.find(:first)
28
- #
29
- # the association proxy in <tt>blog.posts</tt> has the object in +blog+ as
30
- # <tt>@owner</tt>, the collection of its posts as <tt>@target</tt>, and
31
- # the <tt>@reflection</tt> object represents a <tt>:has_many</tt> macro.
32
- #
33
- # This class has most of the basic instance methods removed, and delegates
34
- # unknown methods to <tt>@target</tt> via <tt>method_missing</tt>. As a
35
- # corner case, it even removes the +class+ method and that's why you get
36
- #
37
- # blog.posts.class # => Array
38
- #
39
- # though the object behind <tt>blog.posts</tt> is not an Array, but an
40
- # ActiveRecord::Associations::HasManyAssociation.
41
- #
42
- # The <tt>@target</tt> object is not \loaded until needed. For example,
43
- #
44
- # blog.posts.count
45
- #
46
- # is computed directly through SQL and does not trigger by itself the
47
- # instantiation of the actual post records.
48
- class AssociationProxy #:nodoc:
49
- alias_method :proxy_respond_to?, :respond_to?
50
- alias_method :proxy_extend, :extend
51
- delegate :to_param, :to => :proxy_target
52
- instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|proxy_|^object_id$)/ }
53
-
54
- def initialize(owner, reflection)
55
- @owner, @reflection = owner, reflection
56
- reflection.check_validity!
57
- Array(reflection.options[:extend]).each { |ext| proxy_extend(ext) }
58
- reset
59
- end
60
-
61
- # Returns the owner of the proxy.
62
- def proxy_owner
63
- @owner
64
- end
65
-
66
- # Returns the reflection object that represents the association handled
67
- # by the proxy.
68
- def proxy_reflection
69
- @reflection
70
- end
71
-
72
- # Returns the \target of the proxy, same as +target+.
73
- def proxy_target
74
- @target
75
- end
76
-
77
- # Does the proxy or its \target respond to +symbol+?
78
- def respond_to?(*args)
79
- proxy_respond_to?(*args) || (load_target && @target.respond_to?(*args))
80
- end
81
-
82
- # Forwards <tt>===</tt> explicitly to the \target because the instance method
83
- # removal above doesn't catch it. Loads the \target if needed.
84
- def ===(other)
85
- load_target
86
- other === @target
87
- end
88
-
89
- # Returns the name of the table of the related class:
90
- #
91
- # post.comments.aliased_table_name # => "comments"
92
- #
93
- def aliased_table_name
94
- @reflection.klass.table_name
95
- end
96
-
97
- # Returns the SQL string that corresponds to the <tt>:conditions</tt>
98
- # option of the macro, if given, or +nil+ otherwise.
99
- def conditions
100
- @conditions ||= interpolate_sql(@reflection.sanitized_conditions) if @reflection.sanitized_conditions
101
- end
102
- alias :sql_conditions :conditions
103
-
104
- # Resets the \loaded flag to +false+ and sets the \target to +nil+.
105
- def reset
106
- @loaded = false
107
- @target = nil
108
- end
109
-
110
- # Reloads the \target and returns +self+ on success.
111
- def reload
112
- reset
113
- load_target
114
- self unless @target.nil?
115
- end
116
-
117
- # Has the \target been already \loaded?
118
- def loaded?
119
- @loaded
120
- end
121
-
122
- # Asserts the \target has been loaded setting the \loaded flag to +true+.
123
- def loaded
124
- @loaded = true
125
- end
126
-
127
- # Returns the target of this proxy, same as +proxy_target+.
128
- def target
129
- @target
130
- end
131
-
132
- # Sets the target of this proxy to <tt>\target</tt>, and the \loaded flag to +true+.
133
- def target=(target)
134
- @target = target
135
- loaded
136
- end
137
-
138
- # Forwards the call to the target. Loads the \target if needed.
139
- def inspect
140
- load_target
141
- @target.inspect
142
- end
143
-
144
- def send(method, *args)
145
- if proxy_respond_to?(method)
146
- super
147
- else
148
- load_target
149
- @target.send(method, *args)
150
- end
151
- end
152
-
153
- protected
154
- # Does the association have a <tt>:dependent</tt> option?
155
- def dependent?
156
- @reflection.options[:dependent]
157
- end
158
-
159
- # Returns a string with the IDs of +records+ joined with a comma, quoted
160
- # if needed. The result is ready to be inserted into a SQL IN clause.
161
- #
162
- # quoted_record_ids(records) # => "23,56,58,67"
163
- #
164
- def quoted_record_ids(records)
165
- records.map { |record| record.quoted_id }.join(',')
166
- end
167
-
168
- def interpolate_sql(sql, record = nil)
169
- @owner.send(:interpolate_sql, sql, record)
170
- end
171
-
172
- # Forwards the call to the reflection class.
173
- def sanitize_sql(sql, table_name = @reflection.klass.quoted_table_name)
174
- @reflection.klass.send(:sanitize_sql, sql, table_name)
175
- end
176
-
177
- # Assigns the ID of the owner to the corresponding foreign key in +record+.
178
- # If the association is polymorphic the type of the owner is also set.
179
- def set_belongs_to_association_for(record)
180
- if @reflection.options[:as]
181
- record["#{@reflection.options[:as]}_id"] = @owner.id unless @owner.new_record?
182
- record["#{@reflection.options[:as]}_type"] = @owner.class.base_class.name.to_s
183
- else
184
- unless @owner.new_record?
185
- primary_key = @reflection.options[:primary_key] || :id
186
- record[@reflection.primary_key_name] = @owner.send(primary_key)
187
- end
188
- end
189
- end
190
-
191
- # Merges into +options+ the ones coming from the reflection.
192
- def merge_options_from_reflection!(options)
193
- options.reverse_merge!(
194
- :group => @reflection.options[:group],
195
- :having => @reflection.options[:having],
196
- :limit => @reflection.options[:limit],
197
- :offset => @reflection.options[:offset],
198
- :joins => @reflection.options[:joins],
199
- :include => @reflection.options[:include],
200
- :select => @reflection.options[:select],
201
- :readonly => @reflection.options[:readonly]
202
- )
203
- end
204
-
205
- # Forwards +with_scope+ to the reflection.
206
- def with_scope(*args, &block)
207
- @reflection.klass.send :with_scope, *args, &block
208
- end
209
-
210
- private
211
- # Forwards any missing method call to the \target.
212
- def method_missing(method, *args, &block)
213
- if load_target
214
- if @target.respond_to?(method)
215
- @target.send(method, *args, &block)
216
- else
217
- super
218
- end
219
- end
220
- end
221
-
222
- # Loads the \target if needed and returns it.
223
- #
224
- # This method is abstract in the sense that it relies on +find_target+,
225
- # which is expected to be provided by descendants.
226
- #
227
- # If the \target is already \loaded it is just returned. Thus, you can call
228
- # +load_target+ unconditionally to get the \target.
229
- #
230
- # ActiveRecord::RecordNotFound is rescued within the method, and it is
231
- # not reraised. The proxy is \reset and +nil+ is the return value.
232
- def load_target
233
- return nil unless defined?(@loaded)
234
-
235
- if !loaded? and (!@owner.new_record? || foreign_key_present)
236
- @target = find_target
237
- end
238
-
239
- @loaded = true
240
- @target
241
- rescue ActiveRecord::RecordNotFound
242
- reset
243
- end
244
-
245
- # Can be overwritten by associations that might have the foreign key
246
- # available for an association without having the object itself (and
247
- # still being a new record). Currently, only +belongs_to+ presents
248
- # this scenario (both vanilla and polymorphic).
249
- def foreign_key_present
250
- false
251
- end
252
-
253
- # Raises ActiveRecord::AssociationTypeMismatch unless +record+ is of
254
- # the kind of the class of the associated objects. Meant to be used as
255
- # a sanity check when you are about to assign an associated record.
256
- def raise_on_type_mismatch(record)
257
- unless record.is_a?(@reflection.klass) || record.is_a?(@reflection.class_name.constantize)
258
- message = "#{@reflection.class_name}(##{@reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
259
- raise ActiveRecord::AssociationTypeMismatch, message
260
- end
261
- end
262
-
263
- # Array#flatten has problems with recursive arrays. Going one level
264
- # deeper solves the majority of the problems.
265
- def flatten_deeper(array)
266
- array.collect { |element| (element.respond_to?(:flatten) && !element.is_a?(Hash)) ? element.flatten : element }.flatten
267
- end
268
-
269
- # Returns the ID of the owner, quoted if needed.
270
- def owner_quoted_id
271
- @owner.quoted_id
272
- end
273
-
274
- def set_inverse_instance(record, instance)
275
- return if record.nil? || !we_can_set_the_inverse_on_this?(record)
276
- inverse_relationship = @reflection.inverse_of
277
- unless inverse_relationship.nil?
278
- record.send(:"set_#{inverse_relationship.name}_target", instance)
279
- end
280
- end
281
-
282
- # Override in subclasses
283
- def we_can_set_the_inverse_on_this?(record)
284
- false
285
- end
286
- end
287
- end
288
- end
@@ -1,85 +0,0 @@
1
- module ActiveRecord
2
- module Batches # :nodoc:
3
- def self.included(base)
4
- base.extend(ClassMethods)
5
- end
6
-
7
- # When processing large numbers of records, it's often a good idea to do
8
- # so in batches to prevent memory ballooning.
9
- module ClassMethods
10
- # Yields each record that was found by the find +options+. The find is
11
- # performed by find_in_batches with a batch size of 1000 (or as
12
- # specified by the <tt>:batch_size</tt> option).
13
- #
14
- # Example:
15
- #
16
- # Person.find_each(:conditions => "age > 21") do |person|
17
- # person.party_all_night!
18
- # end
19
- #
20
- # Note: This method is only intended to use for batch processing of
21
- # large amounts of records that wouldn't fit in memory all at once. If
22
- # you just need to loop over less than 1000 records, it's probably
23
- # better just to use the regular find methods.
24
- def find_each(options = {})
25
- find_in_batches(options) do |records|
26
- records.each { |record| yield record }
27
- end
28
-
29
- self
30
- end
31
-
32
- # Yields each batch of records that was found by the find +options+ as
33
- # an array. The size of each batch is set by the <tt>:batch_size</tt>
34
- # option; the default is 1000.
35
- #
36
- # You can control the starting point for the batch processing by
37
- # supplying the <tt>:start</tt> option. This is especially useful if you
38
- # want multiple workers dealing with the same processing queue. You can
39
- # make worker 1 handle all the records between id 0 and 10,000 and
40
- # worker 2 handle from 10,000 and beyond (by setting the <tt>:start</tt>
41
- # option on that worker).
42
- #
43
- # It's not possible to set the order. That is automatically set to
44
- # ascending on the primary key ("id ASC") to make the batch ordering
45
- # work. This also mean that this method only works with integer-based
46
- # primary keys. You can't set the limit either, that's used to control
47
- # the the batch sizes.
48
- #
49
- # Example:
50
- #
51
- # Person.find_in_batches(:conditions => "age > 21") do |group|
52
- # sleep(50) # Make sure it doesn't get too crowded in there!
53
- # group.each { |person| person.party_all_night! }
54
- # end
55
- def find_in_batches(options = {})
56
- raise "You can't specify an order, it's forced to be #{batch_order}" if options[:order]
57
- raise "You can't specify a limit, it's forced to be the batch_size" if options[:limit]
58
-
59
- start = options.delete(:start).to_i
60
- batch_size = options.delete(:batch_size) || 1000
61
-
62
- proxy = scoped(options.merge(:order => batch_order, :limit => batch_size))
63
- records = proxy.find(:all, :conditions => [ "#{table_name}.#{primary_key} >= ?", start ])
64
-
65
- while records.any?
66
- yield records
67
-
68
- break if records.size < batch_size
69
-
70
- last_value = records.last.id
71
-
72
- raise "You must include the primary key if you define a select" unless last_value.present?
73
-
74
- records = proxy.find(:all, :conditions => [ "#{table_name}.#{primary_key} > ?", last_value ])
75
- end
76
- end
77
-
78
-
79
- private
80
- def batch_order
81
- "#{table_name}.#{primary_key} ASC"
82
- end
83
- end
84
- end
85
- end
@@ -1,321 +0,0 @@
1
- module ActiveRecord
2
- module Calculations #:nodoc:
3
- CALCULATIONS_OPTIONS = [:conditions, :joins, :order, :select, :group, :having, :distinct, :limit, :offset, :include, :from]
4
- def self.included(base)
5
- base.extend(ClassMethods)
6
- end
7
-
8
- module ClassMethods
9
- # Count operates using three different approaches.
10
- #
11
- # * Count all: By not passing any parameters to count, it will return a count of all the rows for the model.
12
- # * Count using column: By passing a column name to count, it will return a count of all the rows for the model with supplied column present
13
- # * Count using options will find the row count matched by the options used.
14
- #
15
- # The third approach, count using options, accepts an option hash as the only parameter. The options are:
16
- #
17
- # * <tt>:conditions</tt>: An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro to ActiveRecord::Base.
18
- # * <tt>:joins</tt>: Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed)
19
- # or named associations in the same form used for the <tt>:include</tt> option, which will perform an INNER JOIN on the associated table(s).
20
- # If the value is a string, then the records will be returned read-only since they will have attributes that do not correspond to the table's columns.
21
- # Pass <tt>:readonly => false</tt> to override.
22
- # * <tt>:include</tt>: Named associations that should be loaded alongside using LEFT OUTER JOINs. The symbols named refer
23
- # to already defined associations. When using named associations, count returns the number of DISTINCT items for the model you're counting.
24
- # See eager loading under Associations.
25
- # * <tt>:order</tt>: An SQL fragment like "created_at DESC, name" (really only used with GROUP BY calculations).
26
- # * <tt>:group</tt>: An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
27
- # * <tt>:select</tt>: By default, this is * as in SELECT * FROM, but can be changed if you, for example, want to do a join but not
28
- # include the joined columns.
29
- # * <tt>:distinct</tt>: Set this to true to make this a distinct calculation, such as SELECT COUNT(DISTINCT posts.id) ...
30
- # * <tt>:from</tt> - By default, this is the table name of the class, but can be changed to an alternate table name (or even the name
31
- # of a database view).
32
- #
33
- # Examples for counting all:
34
- # Person.count # returns the total count of all people
35
- #
36
- # Examples for counting by column:
37
- # Person.count(:age) # returns the total count of all people whose age is present in database
38
- #
39
- # Examples for count with options:
40
- # Person.count(:conditions => "age > 26")
41
- # Person.count(:conditions => "age > 26 AND job.salary > 60000", :include => :job) # because of the named association, it finds the DISTINCT count using LEFT OUTER JOIN.
42
- # Person.count(:conditions => "age > 26 AND job.salary > 60000", :joins => "LEFT JOIN jobs on jobs.person_id = person.id") # finds the number of rows matching the conditions and joins.
43
- # Person.count('id', :conditions => "age > 26") # Performs a COUNT(id)
44
- # Person.count(:all, :conditions => "age > 26") # Performs a COUNT(*) (:all is an alias for '*')
45
- #
46
- # Note: <tt>Person.count(:all)</tt> will not work because it will use <tt>:all</tt> as the condition. Use Person.count instead.
47
- def count(*args)
48
- calculate(:count, *construct_count_options_from_args(*args))
49
- end
50
-
51
- # Calculates the average value on a given column. The value is returned as
52
- # a float, or +nil+ if there's no row. See +calculate+ for examples with
53
- # options.
54
- #
55
- # Person.average('age') # => 35.8
56
- def average(column_name, options = {})
57
- calculate(:avg, column_name, options)
58
- end
59
-
60
- # Calculates the minimum value on a given column. The value is returned
61
- # with the same data type of the column, or +nil+ if there's no row. See
62
- # +calculate+ for examples with options.
63
- #
64
- # Person.minimum('age') # => 7
65
- def minimum(column_name, options = {})
66
- calculate(:min, column_name, options)
67
- end
68
-
69
- # Calculates the maximum value on a given column. The value is returned
70
- # with the same data type of the column, or +nil+ if there's no row. See
71
- # +calculate+ for examples with options.
72
- #
73
- # Person.maximum('age') # => 93
74
- def maximum(column_name, options = {})
75
- calculate(:max, column_name, options)
76
- end
77
-
78
- # Calculates the sum of values on a given column. The value is returned
79
- # with the same data type of the column, 0 if there's no row. See
80
- # +calculate+ for examples with options.
81
- #
82
- # Person.sum('age') # => 4562
83
- def sum(column_name, options = {})
84
- calculate(:sum, column_name, options)
85
- end
86
-
87
- # This calculates aggregate values in the given column. Methods for count, sum, average, minimum, and maximum have been added as shortcuts.
88
- # Options such as <tt>:conditions</tt>, <tt>:order</tt>, <tt>:group</tt>, <tt>:having</tt>, and <tt>:joins</tt> can be passed to customize the query.
89
- #
90
- # There are two basic forms of output:
91
- # * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float for AVG, and the given column's type for everything else.
92
- # * Grouped values: This returns an ordered hash of the values and groups them by the <tt>:group</tt> option. It takes either a column name, or the name
93
- # of a belongs_to association.
94
- #
95
- # values = Person.maximum(:age, :group => 'last_name')
96
- # puts values["Drake"]
97
- # => 43
98
- #
99
- # drake = Family.find_by_last_name('Drake')
100
- # values = Person.maximum(:age, :group => :family) # Person belongs_to :family
101
- # puts values[drake]
102
- # => 43
103
- #
104
- # values.each do |family, max_age|
105
- # ...
106
- # end
107
- #
108
- # Options:
109
- # * <tt>:conditions</tt> - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro to ActiveRecord::Base.
110
- # * <tt>:include</tt>: Eager loading, see Associations for details. Since calculations don't load anything, the purpose of this is to access fields on joined tables in your conditions, order, or group clauses.
111
- # * <tt>:joins</tt> - An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id". (Rarely needed).
112
- # The records will be returned read-only since they will have attributes that do not correspond to the table's columns.
113
- # * <tt>:order</tt> - An SQL fragment like "created_at DESC, name" (really only used with GROUP BY calculations).
114
- # * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
115
- # * <tt>:select</tt> - By default, this is * as in SELECT * FROM, but can be changed if you for example want to do a join, but not
116
- # include the joined columns.
117
- # * <tt>:distinct</tt> - Set this to true to make this a distinct calculation, such as SELECT COUNT(DISTINCT posts.id) ...
118
- #
119
- # Examples:
120
- # Person.calculate(:count, :all) # The same as Person.count
121
- # Person.average(:age) # SELECT AVG(age) FROM people...
122
- # Person.minimum(:age, :conditions => ['last_name != ?', 'Drake']) # Selects the minimum age for everyone with a last name other than 'Drake'
123
- # Person.minimum(:age, :having => 'min(age) > 17', :group => :last_name) # Selects the minimum age for any family without any minors
124
- # Person.sum("2 * age")
125
- def calculate(operation, column_name, options = {})
126
- validate_calculation_options(operation, options)
127
- column_name = options[:select] if options[:select]
128
- column_name = '*' if column_name == :all
129
- column = column_for column_name
130
- catch :invalid_query do
131
- if options[:group]
132
- return execute_grouped_calculation(operation, column_name, column, options)
133
- else
134
- return execute_simple_calculation(operation, column_name, column, options)
135
- end
136
- end
137
- 0
138
- end
139
-
140
- protected
141
- def construct_count_options_from_args(*args)
142
- options = {}
143
- column_name = :all
144
-
145
- # We need to handle
146
- # count()
147
- # count(:column_name=:all)
148
- # count(options={})
149
- # count(column_name=:all, options={})
150
- case args.size
151
- when 1
152
- args[0].is_a?(Hash) ? options = args[0] : column_name = args[0]
153
- when 2
154
- column_name, options = args
155
- else
156
- raise ArgumentError, "Unexpected parameters passed to count(): #{args.inspect}"
157
- end if args.size > 0
158
-
159
- [column_name, options]
160
- end
161
-
162
- def construct_calculation_sql(operation, column_name, options) #:nodoc:
163
- operation = operation.to_s.downcase
164
- options = options.symbolize_keys
165
-
166
- scope = scope(:find)
167
- merged_includes = merge_includes(scope ? scope[:include] : [], options[:include])
168
- aggregate_alias = column_alias_for(operation, column_name)
169
- column_name = "#{connection.quote_table_name(table_name)}.#{column_name}" if column_names.include?(column_name.to_s)
170
-
171
- if operation == 'count'
172
- if merged_includes.any?
173
- options[:distinct] = true
174
- column_name = options[:select] || [connection.quote_table_name(table_name), primary_key] * '.'
175
- end
176
-
177
- if options[:distinct]
178
- use_workaround = !connection.supports_count_distinct?
179
- end
180
- end
181
-
182
- if options[:distinct] && column_name.to_s !~ /\s*DISTINCT\s+/i
183
- distinct = 'DISTINCT '
184
- end
185
- sql = "SELECT #{operation}(#{distinct}#{column_name}) AS #{aggregate_alias}"
186
-
187
- # A (slower) workaround if we're using a backend, like sqlite, that doesn't support COUNT DISTINCT.
188
- sql = "SELECT COUNT(*) AS #{aggregate_alias}" if use_workaround
189
-
190
- options[:group_fields].each_index{|i| sql << ", #{options[:group_fields][i]} AS #{options[:group_aliases][i]}" } if options[:group]
191
- if options[:from]
192
- sql << " FROM #{options[:from]} "
193
- elsif scope && scope[:from] && !use_workaround
194
- sql << " FROM #{scope[:from]} "
195
- else
196
- sql << " FROM (SELECT #{distinct}#{column_name}" if use_workaround
197
- sql << " FROM #{connection.quote_table_name(table_name)} "
198
- end
199
-
200
- joins = ""
201
- add_joins!(joins, options[:joins], scope)
202
-
203
- if merged_includes.any?
204
- join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_includes, joins)
205
- sql << join_dependency.join_associations.collect{|join| join.association_join }.join
206
- end
207
-
208
- sql << joins unless joins.blank?
209
-
210
- add_conditions!(sql, options[:conditions], scope)
211
- add_limited_ids_condition!(sql, options, join_dependency) if join_dependency && !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit])
212
-
213
- if options[:group]
214
- group_key = connection.adapter_name == 'FrontBase' ? :group_aliases : :group_fields
215
- sql << " GROUP BY #{options[group_key].join(',')} "
216
- end
217
-
218
- if options[:group] && options[:having]
219
- having = sanitize_sql_for_conditions(options[:having])
220
-
221
- # FrontBase requires identifiers in the HAVING clause and chokes on function calls
222
- if connection.adapter_name == 'FrontBase'
223
- having.downcase!
224
- having.gsub!(/#{operation}\s*\(\s*#{column_name}\s*\)/, aggregate_alias)
225
- end
226
-
227
- sql << " HAVING #{having} "
228
- end
229
-
230
- sql << " ORDER BY #{options[:order]} " if options[:order]
231
- add_limit!(sql, options, scope)
232
- sql << ") #{aggregate_alias}_subquery" if use_workaround
233
- sql
234
- end
235
-
236
- def execute_simple_calculation(operation, column_name, column, options) #:nodoc:
237
- value = connection.select_value(construct_calculation_sql(operation, column_name, options))
238
- type_cast_calculated_value(value, column, operation)
239
- end
240
-
241
- def execute_grouped_calculation(operation, column_name, column, options) #:nodoc:
242
- group_attr = options[:group]
243
- association = reflect_on_association(group_attr.to_s.to_sym)
244
- associated = association && association.macro == :belongs_to # only count belongs_to associations
245
- group_fields = Array(associated ? association.primary_key_name : group_attr)
246
- group_aliases = []
247
- group_columns = {}
248
-
249
- group_fields.each do |field|
250
- group_aliases << column_alias_for(field)
251
- group_columns[column_alias_for(field)] = column_for(field)
252
- end
253
-
254
- sql = construct_calculation_sql(operation, column_name, options.merge(:group_fields => group_fields, :group_aliases => group_aliases))
255
- calculated_data = connection.select_all(sql)
256
- aggregate_alias = column_alias_for(operation, column_name)
257
-
258
- if association
259
- key_ids = calculated_data.collect { |row| row[group_aliases.first] }
260
- key_records = association.klass.base_class.find(key_ids)
261
- key_records = key_records.inject({}) { |hsh, r| hsh.merge(r.id => r) }
262
- end
263
-
264
- calculated_data.inject(ActiveSupport::OrderedHash.new) do |all, row|
265
- key = group_aliases.map{|group_alias| type_cast_calculated_value(row[group_alias], group_columns[group_alias])}
266
- key = key.first if key.size == 1
267
- key = key_records[key] if associated
268
- value = row[aggregate_alias]
269
- all[key] = type_cast_calculated_value(value, column, operation)
270
- all
271
- end
272
- end
273
-
274
- private
275
- def validate_calculation_options(operation, options = {})
276
- options.assert_valid_keys(CALCULATIONS_OPTIONS)
277
- end
278
-
279
- # Converts the given keys to the value that the database adapter returns as
280
- # a usable column name:
281
- #
282
- # column_alias_for("users.id") # => "users_id"
283
- # column_alias_for("sum(id)") # => "sum_id"
284
- # column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
285
- # column_alias_for("count(*)") # => "count_all"
286
- # column_alias_for("count", "id") # => "count_id"
287
- def column_alias_for(*keys)
288
- table_name = keys.join(' ')
289
- table_name.downcase!
290
- table_name.gsub!(/\*/, 'all')
291
- table_name.gsub!(/\W+/, ' ')
292
- table_name.strip!
293
- table_name.gsub!(/ +/, '_')
294
-
295
- connection.table_alias_for(table_name)
296
- end
297
-
298
- def column_for(field)
299
- field_name = field.to_s.split('.').last
300
- columns.detect { |c| c.name.to_s == field_name }
301
- end
302
-
303
- def type_cast_calculated_value(value, column, operation = nil)
304
- if value.is_a?(String) || value.nil?
305
- case operation.to_s.downcase
306
- when 'count' then value.to_i
307
- when 'sum' then type_cast_using_column(value || '0', column)
308
- when 'avg' then value.try(:to_d)
309
- else type_cast_using_column(value, column)
310
- end
311
- else
312
- value
313
- end
314
- end
315
-
316
- def type_cast_using_column(value, column)
317
- column ? column.type_cast(value) : value
318
- end
319
- end
320
- end
321
- end