activerecord 2.3.18 → 3.0.0.beta

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 (378) hide show
  1. data/CHANGELOG +105 -34
  2. data/examples/performance.rb +3 -39
  3. data/examples/simple.rb +14 -0
  4. data/lib/active_record.rb +81 -47
  5. data/lib/active_record/aggregations.rb +1 -3
  6. data/lib/active_record/association_preload.rb +39 -54
  7. data/lib/active_record/associations.rb +262 -419
  8. data/lib/active_record/associations/association_collection.rb +85 -100
  9. data/lib/active_record/associations/association_proxy.rb +20 -18
  10. data/lib/active_record/associations/belongs_to_association.rb +8 -8
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +13 -35
  12. data/lib/active_record/associations/has_many_association.rb +11 -19
  13. data/lib/active_record/associations/has_many_through_association.rb +30 -183
  14. data/lib/active_record/associations/has_one_association.rb +10 -10
  15. data/lib/active_record/associations/has_one_through_association.rb +13 -11
  16. data/lib/active_record/associations/through_association_scope.rb +153 -0
  17. data/lib/active_record/attribute_methods.rb +17 -370
  18. data/lib/active_record/attribute_methods/before_type_cast.rb +33 -0
  19. data/lib/active_record/attribute_methods/dirty.rb +87 -0
  20. data/lib/active_record/attribute_methods/primary_key.rb +44 -0
  21. data/lib/active_record/attribute_methods/query.rb +37 -0
  22. data/lib/active_record/attribute_methods/read.rb +116 -0
  23. data/lib/active_record/attribute_methods/time_zone_conversion.rb +60 -0
  24. data/lib/active_record/attribute_methods/write.rb +37 -0
  25. data/lib/active_record/autosave_association.rb +20 -41
  26. data/lib/active_record/base.rb +357 -1180
  27. data/lib/active_record/batches.rb +10 -16
  28. data/lib/active_record/callbacks.rb +66 -126
  29. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +17 -13
  30. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +5 -25
  31. data/lib/active_record/connection_adapters/abstract/database_statements.rb +4 -43
  32. data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -2
  33. data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -4
  34. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  35. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +18 -72
  36. data/lib/active_record/connection_adapters/abstract_adapter.rb +16 -49
  37. data/lib/active_record/connection_adapters/mysql_adapter.rb +15 -27
  38. data/lib/active_record/connection_adapters/postgresql_adapter.rb +84 -46
  39. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +9 -3
  40. data/lib/active_record/connection_adapters/sqlite_adapter.rb +34 -65
  41. data/lib/active_record/fixtures.rb +21 -25
  42. data/lib/active_record/locale/en.yml +9 -27
  43. data/lib/active_record/locking/optimistic.rb +16 -48
  44. data/lib/active_record/migration.rb +59 -46
  45. data/lib/active_record/named_scope.rb +85 -92
  46. data/lib/active_record/nested_attributes.rb +18 -24
  47. data/lib/active_record/observer.rb +18 -94
  48. data/lib/active_record/railtie.rb +83 -0
  49. data/lib/active_record/railties/controller_runtime.rb +38 -0
  50. data/lib/active_record/railties/databases.rake +477 -0
  51. data/lib/active_record/railties/subscriber.rb +27 -0
  52. data/lib/active_record/reflection.rb +2 -15
  53. data/lib/active_record/relation.rb +339 -0
  54. data/lib/active_record/relation/calculations.rb +259 -0
  55. data/lib/active_record/relation/finder_methods.rb +315 -0
  56. data/lib/active_record/relation/predicate_builder.rb +46 -0
  57. data/lib/active_record/relation/query_methods.rb +218 -0
  58. data/lib/active_record/relation/spawn_methods.rb +102 -0
  59. data/lib/active_record/schema_dumper.rb +10 -6
  60. data/lib/active_record/serialization.rb +31 -74
  61. data/lib/active_record/serializers/xml_serializer.rb +33 -121
  62. data/lib/active_record/session_store.rb +1 -9
  63. data/lib/active_record/test_case.rb +1 -3
  64. data/lib/active_record/timestamp.rb +7 -5
  65. data/lib/active_record/transactions.rb +9 -9
  66. data/lib/active_record/validations.rb +51 -1100
  67. data/lib/active_record/validations/associated.rb +47 -0
  68. data/lib/active_record/validations/uniqueness.rb +181 -0
  69. data/lib/active_record/version.rb +3 -3
  70. data/lib/generators/active_record.rb +30 -0
  71. data/lib/generators/active_record/migration/migration_generator.rb +25 -0
  72. data/lib/generators/active_record/migration/templates/migration.rb +11 -0
  73. data/lib/generators/active_record/model/model_generator.rb +33 -0
  74. data/lib/generators/active_record/model/templates/migration.rb +16 -0
  75. data/lib/generators/active_record/model/templates/model.rb +5 -0
  76. data/lib/generators/active_record/observer/observer_generator.rb +15 -0
  77. data/lib/generators/active_record/observer/templates/observer.rb +2 -0
  78. data/lib/generators/active_record/session_migration/session_migration_generator.rb +24 -0
  79. data/lib/generators/active_record/session_migration/templates/migration.rb +16 -0
  80. metadata +67 -325
  81. data/RUNNING_UNIT_TESTS +0 -36
  82. data/Rakefile +0 -268
  83. data/install.rb +0 -30
  84. data/lib/active_record/calculations.rb +0 -321
  85. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -57
  86. data/lib/active_record/dirty.rb +0 -183
  87. data/lib/active_record/serializers/json_serializer.rb +0 -91
  88. data/lib/activerecord.rb +0 -2
  89. data/test/assets/example.log +0 -1
  90. data/test/assets/flowers.jpg +0 -0
  91. data/test/cases/aaa_create_tables_test.rb +0 -24
  92. data/test/cases/active_schema_test_mysql.rb +0 -122
  93. data/test/cases/active_schema_test_postgresql.rb +0 -24
  94. data/test/cases/adapter_test.rb +0 -144
  95. data/test/cases/aggregations_test.rb +0 -167
  96. data/test/cases/ar_schema_test.rb +0 -32
  97. data/test/cases/associations/belongs_to_associations_test.rb +0 -438
  98. data/test/cases/associations/callbacks_test.rb +0 -161
  99. data/test/cases/associations/cascaded_eager_loading_test.rb +0 -131
  100. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +0 -36
  101. data/test/cases/associations/eager_load_nested_include_test.rb +0 -131
  102. data/test/cases/associations/eager_load_nested_polymorphic_include.rb +0 -19
  103. data/test/cases/associations/eager_singularization_test.rb +0 -145
  104. data/test/cases/associations/eager_test.rb +0 -852
  105. data/test/cases/associations/extension_test.rb +0 -62
  106. data/test/cases/associations/habtm_join_table_test.rb +0 -56
  107. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +0 -827
  108. data/test/cases/associations/has_many_associations_test.rb +0 -1273
  109. data/test/cases/associations/has_many_through_associations_test.rb +0 -360
  110. data/test/cases/associations/has_one_associations_test.rb +0 -330
  111. data/test/cases/associations/has_one_through_associations_test.rb +0 -209
  112. data/test/cases/associations/inner_join_association_test.rb +0 -93
  113. data/test/cases/associations/inverse_associations_test.rb +0 -566
  114. data/test/cases/associations/join_model_test.rb +0 -712
  115. data/test/cases/associations_test.rb +0 -282
  116. data/test/cases/attribute_methods_test.rb +0 -305
  117. data/test/cases/autosave_association_test.rb +0 -1218
  118. data/test/cases/base_test.rb +0 -2166
  119. data/test/cases/batches_test.rb +0 -81
  120. data/test/cases/binary_test.rb +0 -30
  121. data/test/cases/calculations_test.rb +0 -360
  122. data/test/cases/callbacks_observers_test.rb +0 -38
  123. data/test/cases/callbacks_test.rb +0 -438
  124. data/test/cases/class_inheritable_attributes_test.rb +0 -32
  125. data/test/cases/column_alias_test.rb +0 -17
  126. data/test/cases/column_definition_test.rb +0 -70
  127. data/test/cases/connection_pool_test.rb +0 -25
  128. data/test/cases/connection_test_firebird.rb +0 -8
  129. data/test/cases/connection_test_mysql.rb +0 -65
  130. data/test/cases/copy_table_test_sqlite.rb +0 -80
  131. data/test/cases/counter_cache_test.rb +0 -84
  132. data/test/cases/database_statements_test.rb +0 -12
  133. data/test/cases/datatype_test_postgresql.rb +0 -204
  134. data/test/cases/date_time_test.rb +0 -37
  135. data/test/cases/default_test_firebird.rb +0 -16
  136. data/test/cases/defaults_test.rb +0 -111
  137. data/test/cases/deprecated_finder_test.rb +0 -30
  138. data/test/cases/dirty_test.rb +0 -316
  139. data/test/cases/finder_respond_to_test.rb +0 -76
  140. data/test/cases/finder_test.rb +0 -1098
  141. data/test/cases/fixtures_test.rb +0 -661
  142. data/test/cases/helper.rb +0 -68
  143. data/test/cases/i18n_test.rb +0 -46
  144. data/test/cases/inheritance_test.rb +0 -262
  145. data/test/cases/invalid_date_test.rb +0 -24
  146. data/test/cases/json_serialization_test.rb +0 -219
  147. data/test/cases/lifecycle_test.rb +0 -193
  148. data/test/cases/locking_test.rb +0 -350
  149. data/test/cases/method_scoping_test.rb +0 -704
  150. data/test/cases/migration_test.rb +0 -1649
  151. data/test/cases/migration_test_firebird.rb +0 -124
  152. data/test/cases/mixin_test.rb +0 -96
  153. data/test/cases/modules_test.rb +0 -109
  154. data/test/cases/multiple_db_test.rb +0 -85
  155. data/test/cases/named_scope_test.rb +0 -372
  156. data/test/cases/nested_attributes_test.rb +0 -840
  157. data/test/cases/pk_test.rb +0 -119
  158. data/test/cases/pooled_connections_test.rb +0 -103
  159. data/test/cases/query_cache_test.rb +0 -129
  160. data/test/cases/readonly_test.rb +0 -107
  161. data/test/cases/reflection_test.rb +0 -234
  162. data/test/cases/reload_models_test.rb +0 -22
  163. data/test/cases/repair_helper.rb +0 -50
  164. data/test/cases/reserved_word_test_mysql.rb +0 -176
  165. data/test/cases/sanitize_test.rb +0 -25
  166. data/test/cases/schema_authorization_test_postgresql.rb +0 -75
  167. data/test/cases/schema_dumper_test.rb +0 -211
  168. data/test/cases/schema_test_postgresql.rb +0 -178
  169. data/test/cases/serialization_test.rb +0 -47
  170. data/test/cases/sp_test_mysql.rb +0 -16
  171. data/test/cases/synonym_test_oracle.rb +0 -17
  172. data/test/cases/timestamp_test.rb +0 -75
  173. data/test/cases/transactions_test.rb +0 -543
  174. data/test/cases/unconnected_test.rb +0 -32
  175. data/test/cases/validations_i18n_test.rb +0 -925
  176. data/test/cases/validations_test.rb +0 -1684
  177. data/test/cases/xml_serialization_test.rb +0 -240
  178. data/test/cases/yaml_serialization_test.rb +0 -11
  179. data/test/config.rb +0 -5
  180. data/test/connections/jdbc_jdbcderby/connection.rb +0 -18
  181. data/test/connections/jdbc_jdbch2/connection.rb +0 -18
  182. data/test/connections/jdbc_jdbchsqldb/connection.rb +0 -18
  183. data/test/connections/jdbc_jdbcmysql/connection.rb +0 -26
  184. data/test/connections/jdbc_jdbcpostgresql/connection.rb +0 -26
  185. data/test/connections/jdbc_jdbcsqlite3/connection.rb +0 -25
  186. data/test/connections/native_db2/connection.rb +0 -25
  187. data/test/connections/native_firebird/connection.rb +0 -26
  188. data/test/connections/native_frontbase/connection.rb +0 -27
  189. data/test/connections/native_mysql/connection.rb +0 -25
  190. data/test/connections/native_openbase/connection.rb +0 -21
  191. data/test/connections/native_oracle/connection.rb +0 -27
  192. data/test/connections/native_postgresql/connection.rb +0 -21
  193. data/test/connections/native_sqlite/connection.rb +0 -25
  194. data/test/connections/native_sqlite3/connection.rb +0 -25
  195. data/test/connections/native_sqlite3/in_memory_connection.rb +0 -18
  196. data/test/connections/native_sybase/connection.rb +0 -23
  197. data/test/fixtures/accounts.yml +0 -29
  198. data/test/fixtures/all/developers.yml +0 -0
  199. data/test/fixtures/all/people.csv +0 -0
  200. data/test/fixtures/all/tasks.yml +0 -0
  201. data/test/fixtures/author_addresses.yml +0 -5
  202. data/test/fixtures/author_favorites.yml +0 -4
  203. data/test/fixtures/authors.yml +0 -9
  204. data/test/fixtures/binaries.yml +0 -132
  205. data/test/fixtures/books.yml +0 -7
  206. data/test/fixtures/categories.yml +0 -14
  207. data/test/fixtures/categories/special_categories.yml +0 -9
  208. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +0 -4
  209. data/test/fixtures/categories_ordered.yml +0 -7
  210. data/test/fixtures/categories_posts.yml +0 -23
  211. data/test/fixtures/categorizations.yml +0 -17
  212. data/test/fixtures/clubs.yml +0 -6
  213. data/test/fixtures/comments.yml +0 -59
  214. data/test/fixtures/companies.yml +0 -56
  215. data/test/fixtures/computers.yml +0 -4
  216. data/test/fixtures/courses.yml +0 -7
  217. data/test/fixtures/customers.yml +0 -26
  218. data/test/fixtures/developers.yml +0 -21
  219. data/test/fixtures/developers_projects.yml +0 -17
  220. data/test/fixtures/edges.yml +0 -6
  221. data/test/fixtures/entrants.yml +0 -14
  222. data/test/fixtures/faces.yml +0 -11
  223. data/test/fixtures/fk_test_has_fk.yml +0 -3
  224. data/test/fixtures/fk_test_has_pk.yml +0 -2
  225. data/test/fixtures/funny_jokes.yml +0 -10
  226. data/test/fixtures/interests.yml +0 -33
  227. data/test/fixtures/items.yml +0 -4
  228. data/test/fixtures/jobs.yml +0 -7
  229. data/test/fixtures/legacy_things.yml +0 -3
  230. data/test/fixtures/mateys.yml +0 -4
  231. data/test/fixtures/member_types.yml +0 -6
  232. data/test/fixtures/members.yml +0 -6
  233. data/test/fixtures/memberships.yml +0 -20
  234. data/test/fixtures/men.yml +0 -5
  235. data/test/fixtures/minimalistics.yml +0 -2
  236. data/test/fixtures/mixed_case_monkeys.yml +0 -6
  237. data/test/fixtures/mixins.yml +0 -29
  238. data/test/fixtures/movies.yml +0 -7
  239. data/test/fixtures/naked/csv/accounts.csv +0 -1
  240. data/test/fixtures/naked/yml/accounts.yml +0 -1
  241. data/test/fixtures/naked/yml/companies.yml +0 -1
  242. data/test/fixtures/naked/yml/courses.yml +0 -1
  243. data/test/fixtures/organizations.yml +0 -5
  244. data/test/fixtures/owners.yml +0 -7
  245. data/test/fixtures/parrots.yml +0 -27
  246. data/test/fixtures/parrots_pirates.yml +0 -7
  247. data/test/fixtures/people.yml +0 -15
  248. data/test/fixtures/pets.yml +0 -14
  249. data/test/fixtures/pirates.yml +0 -9
  250. data/test/fixtures/polymorphic_designs.yml +0 -19
  251. data/test/fixtures/polymorphic_prices.yml +0 -19
  252. data/test/fixtures/posts.yml +0 -52
  253. data/test/fixtures/price_estimates.yml +0 -7
  254. data/test/fixtures/projects.yml +0 -7
  255. data/test/fixtures/readers.yml +0 -9
  256. data/test/fixtures/references.yml +0 -17
  257. data/test/fixtures/reserved_words/distinct.yml +0 -5
  258. data/test/fixtures/reserved_words/distincts_selects.yml +0 -11
  259. data/test/fixtures/reserved_words/group.yml +0 -14
  260. data/test/fixtures/reserved_words/select.yml +0 -8
  261. data/test/fixtures/reserved_words/values.yml +0 -7
  262. data/test/fixtures/ships.yml +0 -5
  263. data/test/fixtures/sponsors.yml +0 -9
  264. data/test/fixtures/subscribers.yml +0 -7
  265. data/test/fixtures/subscriptions.yml +0 -12
  266. data/test/fixtures/taggings.yml +0 -28
  267. data/test/fixtures/tags.yml +0 -7
  268. data/test/fixtures/tasks.yml +0 -7
  269. data/test/fixtures/tees.yml +0 -4
  270. data/test/fixtures/ties.yml +0 -4
  271. data/test/fixtures/topics.yml +0 -42
  272. data/test/fixtures/toys.yml +0 -4
  273. data/test/fixtures/treasures.yml +0 -10
  274. data/test/fixtures/vertices.yml +0 -4
  275. data/test/fixtures/warehouse-things.yml +0 -3
  276. data/test/fixtures/zines.yml +0 -5
  277. data/test/migrations/broken/100_migration_that_raises_exception.rb +0 -10
  278. data/test/migrations/decimal/1_give_me_big_numbers.rb +0 -15
  279. data/test/migrations/duplicate/1_people_have_last_names.rb +0 -9
  280. data/test/migrations/duplicate/2_we_need_reminders.rb +0 -12
  281. data/test/migrations/duplicate/3_foo.rb +0 -7
  282. data/test/migrations/duplicate/3_innocent_jointable.rb +0 -12
  283. data/test/migrations/duplicate_names/20080507052938_chunky.rb +0 -7
  284. data/test/migrations/duplicate_names/20080507053028_chunky.rb +0 -7
  285. data/test/migrations/interleaved/pass_1/3_innocent_jointable.rb +0 -12
  286. data/test/migrations/interleaved/pass_2/1_people_have_last_names.rb +0 -9
  287. data/test/migrations/interleaved/pass_2/3_innocent_jointable.rb +0 -12
  288. data/test/migrations/interleaved/pass_3/1_people_have_last_names.rb +0 -9
  289. data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +0 -8
  290. data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +0 -12
  291. data/test/migrations/missing/1000_people_have_middle_names.rb +0 -9
  292. data/test/migrations/missing/1_people_have_last_names.rb +0 -9
  293. data/test/migrations/missing/3_we_need_reminders.rb +0 -12
  294. data/test/migrations/missing/4_innocent_jointable.rb +0 -12
  295. data/test/migrations/valid/1_people_have_last_names.rb +0 -9
  296. data/test/migrations/valid/2_we_need_reminders.rb +0 -12
  297. data/test/migrations/valid/3_innocent_jointable.rb +0 -12
  298. data/test/models/author.rb +0 -151
  299. data/test/models/auto_id.rb +0 -4
  300. data/test/models/binary.rb +0 -2
  301. data/test/models/bird.rb +0 -9
  302. data/test/models/book.rb +0 -4
  303. data/test/models/categorization.rb +0 -5
  304. data/test/models/category.rb +0 -34
  305. data/test/models/citation.rb +0 -6
  306. data/test/models/club.rb +0 -13
  307. data/test/models/column_name.rb +0 -3
  308. data/test/models/comment.rb +0 -29
  309. data/test/models/company.rb +0 -173
  310. data/test/models/company_in_module.rb +0 -78
  311. data/test/models/computer.rb +0 -3
  312. data/test/models/contact.rb +0 -16
  313. data/test/models/contract.rb +0 -5
  314. data/test/models/course.rb +0 -3
  315. data/test/models/customer.rb +0 -73
  316. data/test/models/default.rb +0 -2
  317. data/test/models/developer.rb +0 -101
  318. data/test/models/edge.rb +0 -5
  319. data/test/models/entrant.rb +0 -3
  320. data/test/models/essay.rb +0 -3
  321. data/test/models/event.rb +0 -3
  322. data/test/models/event_author.rb +0 -8
  323. data/test/models/face.rb +0 -7
  324. data/test/models/guid.rb +0 -2
  325. data/test/models/interest.rb +0 -5
  326. data/test/models/invoice.rb +0 -4
  327. data/test/models/item.rb +0 -7
  328. data/test/models/job.rb +0 -5
  329. data/test/models/joke.rb +0 -3
  330. data/test/models/keyboard.rb +0 -3
  331. data/test/models/legacy_thing.rb +0 -3
  332. data/test/models/line_item.rb +0 -3
  333. data/test/models/man.rb +0 -9
  334. data/test/models/matey.rb +0 -4
  335. data/test/models/member.rb +0 -12
  336. data/test/models/member_detail.rb +0 -5
  337. data/test/models/member_type.rb +0 -3
  338. data/test/models/membership.rb +0 -9
  339. data/test/models/minimalistic.rb +0 -2
  340. data/test/models/mixed_case_monkey.rb +0 -3
  341. data/test/models/movie.rb +0 -5
  342. data/test/models/order.rb +0 -4
  343. data/test/models/organization.rb +0 -6
  344. data/test/models/owner.rb +0 -5
  345. data/test/models/parrot.rb +0 -22
  346. data/test/models/person.rb +0 -16
  347. data/test/models/pet.rb +0 -5
  348. data/test/models/pirate.rb +0 -80
  349. data/test/models/polymorphic_design.rb +0 -3
  350. data/test/models/polymorphic_price.rb +0 -3
  351. data/test/models/post.rb +0 -102
  352. data/test/models/price_estimate.rb +0 -3
  353. data/test/models/project.rb +0 -30
  354. data/test/models/reader.rb +0 -4
  355. data/test/models/reference.rb +0 -4
  356. data/test/models/reply.rb +0 -46
  357. data/test/models/ship.rb +0 -19
  358. data/test/models/ship_part.rb +0 -7
  359. data/test/models/sponsor.rb +0 -4
  360. data/test/models/subject.rb +0 -4
  361. data/test/models/subscriber.rb +0 -8
  362. data/test/models/subscription.rb +0 -4
  363. data/test/models/tag.rb +0 -7
  364. data/test/models/tagging.rb +0 -10
  365. data/test/models/task.rb +0 -3
  366. data/test/models/tee.rb +0 -4
  367. data/test/models/tie.rb +0 -4
  368. data/test/models/topic.rb +0 -80
  369. data/test/models/toy.rb +0 -6
  370. data/test/models/treasure.rb +0 -8
  371. data/test/models/vertex.rb +0 -9
  372. data/test/models/warehouse_thing.rb +0 -5
  373. data/test/models/zine.rb +0 -3
  374. data/test/schema/mysql_specific_schema.rb +0 -31
  375. data/test/schema/postgresql_specific_schema.rb +0 -114
  376. data/test/schema/schema.rb +0 -550
  377. data/test/schema/schema2.rb +0 -6
  378. data/test/schema/sqlite_specific_schema.rb +0 -25
@@ -1,36 +0,0 @@
1
- == Creating the test database
2
-
3
- The default names for the test databases are "activerecord_unittest" and
4
- "activerecord_unittest2". If you want to use another database name then be sure
5
- to update the connection adapter setups you want to test with in
6
- test/connections/<your database>/connection.rb.
7
- When you have the database online, you can import the fixture tables with
8
- the test/schema/*.sql files.
9
-
10
- Make sure that you create database objects with the same user that you specified in
11
- connection.rb otherwise (on Postgres, at least) tests for default values will fail.
12
-
13
- == Running with Rake
14
-
15
- The easiest way to run the unit tests is through Rake. The default task runs
16
- the entire test suite for all the adapters. You can also run the suite on just
17
- one adapter by using the tasks test_mysql, test_sqlite, test_postgresql or any
18
- of the other test_ tasks. For more information, checkout the full array of rake
19
- tasks with "rake -T"
20
-
21
- Rake can be found at http://rake.rubyforge.org
22
-
23
- == Running by hand
24
-
25
- Unit tests are located in test/cases directory. If you only want to run a single test suite,
26
- you can do so with:
27
-
28
- rake test_mysql TEST=test/cases/base_test.rb
29
-
30
- That'll run the base suite using the MySQL-Ruby adapter. Some tests rely on the schema
31
- being initialized - you can initialize the schema with:
32
-
33
- rake test_mysql TEST=test/cases/aaa_create_tables_test.rb
34
-
35
-
36
-
data/Rakefile DELETED
@@ -1,268 +0,0 @@
1
- require 'rubygems'
2
- require 'rake'
3
- require 'rake/testtask'
4
- require 'rdoc/task'
5
- require 'rake/packagetask'
6
- require 'rubygems/package_task'
7
-
8
- require File.join(File.dirname(__FILE__), 'lib', 'active_record', 'version')
9
- require File.expand_path(File.dirname(__FILE__)) + "/test/config"
10
-
11
- PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
12
- PKG_NAME = 'activerecord'
13
- PKG_VERSION = ActiveRecord::VERSION::STRING + PKG_BUILD
14
- PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
15
-
16
- RELEASE_NAME = "REL #{PKG_VERSION}"
17
-
18
- RUBY_FORGE_PROJECT = "activerecord"
19
- RUBY_FORGE_USER = "webster132"
20
-
21
- MYSQL_DB_USER = 'rails'
22
-
23
- PKG_FILES = FileList[
24
- "lib/**/*", "test/**/*", "examples/**/*", "doc/**/*", "[A-Z]*", "install.rb", "Rakefile"
25
- ].exclude(/\bCVS\b|~$/)
26
-
27
- def run_without_aborting(*tasks)
28
- errors = []
29
-
30
- tasks.each do |task|
31
- begin
32
- Rake::Task[task].invoke
33
- rescue Exception
34
- errors << task
35
- end
36
- end
37
-
38
- abort "Errors running #{errors.join(', ')}" if errors.any?
39
- end
40
-
41
- desc 'Run mysql, sqlite, and postgresql tests by default'
42
- task :default => :test
43
-
44
- desc 'Run mysql, sqlite, and postgresql tests'
45
- task :test do
46
- tasks = defined?(JRUBY_VERSION) ?
47
- %w(test_jdbcmysql test_jdbcsqlite3 test_jdbcpostgresql) :
48
- %w(test_mysql test_sqlite3 test_postgresql)
49
- run_without_aborting(*tasks)
50
- end
51
-
52
- for adapter in %w( mysql postgresql sqlite sqlite3 firebird db2 oracle sybase openbase frontbase jdbcmysql jdbcpostgresql jdbcsqlite3 jdbcderby jdbch2 jdbchsqldb )
53
- Rake::TestTask.new("test_#{adapter}") { |t|
54
- if adapter =~ /jdbc/
55
- t.libs << "test" << "test/connections/jdbc_#{adapter}"
56
- else
57
- t.libs << "test" << "test/connections/native_#{adapter}"
58
- end
59
- adapter_short = adapter == 'db2' ? adapter : adapter[/^[a-z]+/]
60
- t.test_files=Dir.glob( "test/cases/**/*_test{,_#{adapter_short}}.rb" ).sort
61
- t.verbose = true
62
- }
63
-
64
- namespace adapter do
65
- task :test => "test_#{adapter}"
66
- end
67
- end
68
-
69
- namespace :mysql do
70
- desc 'Build the MySQL test databases'
71
- task :build_databases do
72
- %x( echo "create DATABASE activerecord_unittest DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_unicode_ci " | mysql --user=#{MYSQL_DB_USER})
73
- %x( echo "create DATABASE activerecord_unittest2 DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_unicode_ci " | mysql --user=#{MYSQL_DB_USER})
74
- end
75
-
76
- desc 'Drop the MySQL test databases'
77
- task :drop_databases do
78
- %x( mysqladmin --user=#{MYSQL_DB_USER} -f drop activerecord_unittest )
79
- %x( mysqladmin --user=#{MYSQL_DB_USER} -f drop activerecord_unittest2 )
80
- end
81
-
82
- desc 'Rebuild the MySQL test databases'
83
- task :rebuild_databases => [:drop_databases, :build_databases]
84
- end
85
-
86
- task :build_mysql_databases => 'mysql:build_databases'
87
- task :drop_mysql_databases => 'mysql:drop_databases'
88
- task :rebuild_mysql_databases => 'mysql:rebuild_databases'
89
-
90
-
91
- namespace :postgresql do
92
- desc 'Build the PostgreSQL test databases'
93
- task :build_databases do
94
- %x( createdb -E UTF8 activerecord_unittest )
95
- %x( createdb -E UTF8 activerecord_unittest2 )
96
- end
97
-
98
- desc 'Drop the PostgreSQL test databases'
99
- task :drop_databases do
100
- %x( dropdb activerecord_unittest )
101
- %x( dropdb activerecord_unittest2 )
102
- end
103
-
104
- desc 'Rebuild the PostgreSQL test databases'
105
- task :rebuild_databases => [:drop_databases, :build_databases]
106
- end
107
-
108
- task :build_postgresql_databases => 'postgresql:build_databases'
109
- task :drop_postgresql_databases => 'postgresql:drop_databases'
110
- task :rebuild_postgresql_databases => 'postgresql:rebuild_databases'
111
-
112
-
113
- namespace :frontbase do
114
- desc 'Build the FrontBase test databases'
115
- task :build_databases => :rebuild_frontbase_databases
116
-
117
- desc 'Rebuild the FrontBase test databases'
118
- task :rebuild_databases do
119
- build_frontbase_database = Proc.new do |db_name, sql_definition_file|
120
- %(
121
- STOP DATABASE #{db_name};
122
- DELETE DATABASE #{db_name};
123
- CREATE DATABASE #{db_name};
124
-
125
- CONNECT TO #{db_name} AS SESSION_NAME USER _SYSTEM;
126
- SET COMMIT FALSE;
127
-
128
- CREATE USER RAILS;
129
- CREATE SCHEMA RAILS AUTHORIZATION RAILS;
130
- COMMIT;
131
-
132
- SET SESSION AUTHORIZATION RAILS;
133
- SCRIPT '#{sql_definition_file}';
134
-
135
- COMMIT;
136
-
137
- DISCONNECT ALL;
138
- )
139
- end
140
- create_activerecord_unittest = build_frontbase_database['activerecord_unittest', File.join(SCHEMA_ROOT, 'frontbase.sql')]
141
- create_activerecord_unittest2 = build_frontbase_database['activerecord_unittest2', File.join(SCHEMA_ROOT, 'frontbase2.sql')]
142
- execute_frontbase_sql = Proc.new do |sql|
143
- system(<<-SHELL)
144
- /Library/FrontBase/bin/sql92 <<-SQL
145
- #{sql}
146
- SQL
147
- SHELL
148
- end
149
- execute_frontbase_sql[create_activerecord_unittest]
150
- execute_frontbase_sql[create_activerecord_unittest2]
151
- end
152
- end
153
-
154
- task :build_frontbase_databases => 'frontbase:build_databases'
155
- task :rebuild_frontbase_databases => 'frontbase:rebuild_databases'
156
-
157
-
158
- # Generate the RDoc documentation
159
-
160
- RDoc::Task.new { |rdoc|
161
- rdoc.rdoc_dir = 'doc'
162
- rdoc.title = "Active Record -- Object-relation mapping put on rails"
163
- rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
164
- rdoc.options << '--charset' << 'utf-8'
165
- rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo'
166
- rdoc.rdoc_files.include('README', 'RUNNING_UNIT_TESTS', 'CHANGELOG')
167
- rdoc.rdoc_files.include('lib/**/*.rb')
168
- rdoc.rdoc_files.exclude('lib/active_record/vendor/*')
169
- rdoc.rdoc_files.include('dev-utils/*.rb')
170
- }
171
-
172
- # Enhance rdoc task to copy referenced images also
173
- task :rdoc do
174
- FileUtils.mkdir_p "doc/files/examples/"
175
- FileUtils.copy "examples/associations.png", "doc/files/examples/associations.png"
176
- end
177
-
178
-
179
- # Create compressed packages
180
-
181
- dist_dirs = [ "lib", "test", "examples" ]
182
-
183
- spec = Gem::Specification.new do |s|
184
- s.platform = Gem::Platform::RUBY
185
- s.name = PKG_NAME
186
- s.version = PKG_VERSION
187
- s.summary = "Implements the ActiveRecord pattern for ORM."
188
- s.description = %q{Implements the ActiveRecord pattern (Fowler, PoEAA) for ORM. It ties database tables and classes together for business objects, like Customer or Subscription, that can find, save, and destroy themselves without resorting to manual SQL.}
189
-
190
- s.files = [ "Rakefile", "install.rb", "README", "RUNNING_UNIT_TESTS", "CHANGELOG" ]
191
- dist_dirs.each do |dir|
192
- s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
193
- end
194
-
195
- s.add_dependency('activesupport', '= 2.3.18' + PKG_BUILD)
196
-
197
- s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite"
198
- s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite"
199
- s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite3"
200
- s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite3"
201
- s.require_path = 'lib'
202
-
203
- s.extra_rdoc_files = %w( README )
204
- s.rdoc_options.concat ['--main', 'README']
205
-
206
- s.author = "David Heinemeier Hansson"
207
- s.email = "david@loudthinking.com"
208
- s.homepage = "http://www.rubyonrails.org"
209
- s.rubyforge_project = "activerecord"
210
- end
211
-
212
- Gem::PackageTask.new(spec) do |p|
213
- p.gem_spec = spec
214
- p.need_tar = true
215
- p.need_zip = true
216
- end
217
-
218
- task :lines do
219
- lines, codelines, total_lines, total_codelines = 0, 0, 0, 0
220
-
221
- for file_name in FileList["lib/active_record/**/*.rb"]
222
- next if file_name =~ /vendor/
223
- f = File.open(file_name)
224
-
225
- while line = f.gets
226
- lines += 1
227
- next if line =~ /^\s*$/
228
- next if line =~ /^\s*#/
229
- codelines += 1
230
- end
231
- puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}"
232
-
233
- total_lines += lines
234
- total_codelines += codelines
235
-
236
- lines, codelines = 0, 0
237
- end
238
-
239
- puts "Total: Lines #{total_lines}, LOC #{total_codelines}"
240
- end
241
-
242
-
243
- # Publishing ------------------------------------------------------
244
-
245
- desc "Publish the beta gem"
246
- task :pgem => [:package] do
247
- require 'rake/contrib/sshpublisher'
248
- Rake::SshFilePublisher.new("gems.rubyonrails.org", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
249
- `ssh gems.rubyonrails.org '/u/sites/gems/gemupdate.sh'`
250
- end
251
-
252
- desc "Publish the API documentation"
253
- task :pdoc => [:rdoc] do
254
- require 'rake/contrib/sshpublisher'
255
- Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/ar", "doc").upload
256
- end
257
-
258
- desc "Publish the release files to RubyForge."
259
- task :release => [ :package ] do
260
- require 'rubyforge'
261
- require 'rake/contrib/rubyforgepublisher'
262
-
263
- packages = %w( gem tgz zip ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" }
264
-
265
- rubyforge = RubyForge.new
266
- rubyforge.login
267
- rubyforge.add_release(PKG_NAME, PKG_NAME, "REL #{PKG_VERSION}", *packages)
268
- end
data/install.rb DELETED
@@ -1,30 +0,0 @@
1
- require 'rbconfig'
2
- require 'find'
3
- require 'ftools'
4
-
5
- include Config
6
-
7
- # this was adapted from rdoc's install.rb by ways of Log4r
8
-
9
- $sitedir = CONFIG["sitelibdir"]
10
- unless $sitedir
11
- version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"]
12
- $libdir = File.join(CONFIG["libdir"], "ruby", version)
13
- $sitedir = $:.find {|x| x =~ /site_ruby/ }
14
- if !$sitedir
15
- $sitedir = File.join($libdir, "site_ruby")
16
- elsif $sitedir !~ Regexp.quote(version)
17
- $sitedir = File.join($sitedir, version)
18
- end
19
- end
20
-
21
- # the actual gruntwork
22
- Dir.chdir("lib")
23
-
24
- Find.find("active_record", "active_record.rb") { |f|
25
- if f[-3..-1] == ".rb"
26
- File::install(f, File.join($sitedir, *f.split(/\//)), 0644, true)
27
- else
28
- File::makedirs(File.join($sitedir, *f.split(/\//)))
29
- end
30
- }
@@ -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