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,3 +1,11 @@
1
+ require 'active_support/core_ext/array/wrap'
2
+ require 'active_support/core_ext/enumerable'
3
+ require 'active_support/core_ext/module/delegation'
4
+ require 'active_support/core_ext/object/blank'
5
+ require 'active_support/core_ext/string/conversions'
6
+ require 'active_support/core_ext/module/remove_method'
7
+ require 'active_support/core_ext/class/attribute'
8
+
1
9
  module ActiveRecord
2
10
  class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
3
11
  def initialize(reflection, associated_class = nil)
@@ -11,15 +19,27 @@ module ActiveRecord
11
19
  end
12
20
  end
13
21
 
14
- class HasManyThroughAssociationPolymorphicError < ActiveRecordError #:nodoc:
22
+ class HasManyThroughAssociationPolymorphicSourceError < ActiveRecordError #:nodoc:
15
23
  def initialize(owner_class_name, reflection, source_reflection)
16
24
  super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' on the polymorphic object '#{source_reflection.class_name}##{source_reflection.name}'.")
17
25
  end
18
26
  end
19
27
 
28
+ class HasManyThroughAssociationPolymorphicThroughError < ActiveRecordError #:nodoc:
29
+ def initialize(owner_class_name, reflection)
30
+ super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' which goes through the polymorphic association '#{owner_class_name}##{reflection.through_reflection.name}'.")
31
+ end
32
+ end
33
+
20
34
  class HasManyThroughAssociationPointlessSourceTypeError < ActiveRecordError #:nodoc:
21
35
  def initialize(owner_class_name, reflection, source_reflection)
22
- super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' with a :source_type option if the '#{reflection.through_reflection.class_name}##{source_reflection.name}' is not polymorphic. Try removing :source_type on your association.")
36
+ super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' with a :source_type option if the '#{reflection.through_reflection.class_name}##{source_reflection.name}' is not polymorphic. Try removing :source_type on your association.")
37
+ end
38
+ end
39
+
40
+ class HasOneThroughCantAssociateThroughCollection < ActiveRecordError #:nodoc:
41
+ def initialize(owner_class_name, reflection, through_reflection)
42
+ super("Cannot have a has_one :through association '#{owner_class_name}##{reflection.name}' where the :through association '#{owner_class_name}##{through_reflection.name}' is a collection. Specify a has_one or belongs_to association in the :through option instead.")
23
43
  end
24
44
  end
25
45
 
@@ -28,15 +48,7 @@ module ActiveRecord
28
48
  through_reflection = reflection.through_reflection
29
49
  source_reflection_names = reflection.source_reflection_names
30
50
  source_associations = reflection.through_reflection.klass.reflect_on_all_associations.collect { |a| a.name.inspect }
31
- super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)}?")
32
- end
33
- end
34
-
35
- class HasManyThroughSourceAssociationMacroError < ActiveRecordError #:nodoc:
36
- def initialize(reflection)
37
- through_reflection = reflection.through_reflection
38
- source_reflection = reflection.source_reflection
39
- super("Invalid source reflection macro :#{source_reflection.macro}#{" :through" if source_reflection.options[:through]} for has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}. Use :source to specify the source reflection.")
51
+ super("Could not find the source association(s) #{source_reflection_names.collect{ |a| a.inspect }.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)}?")
40
52
  end
41
53
  end
42
54
 
@@ -45,7 +57,6 @@ module ActiveRecord
45
57
  super("Cannot modify association '#{owner.class.name}##{reflection.name}' because the source reflection class '#{reflection.source_reflection.class_name}' is associated to '#{reflection.through_reflection.class_name}' via :#{reflection.source_reflection.macro}.")
46
58
  end
47
59
  end
48
- HasManyThroughCantAssociateThroughHasManyReflection = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('ActiveRecord::HasManyThroughCantAssociateThroughHasManyReflection', 'ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection')
49
60
 
50
61
  class HasManyThroughCantAssociateNewRecords < ActiveRecordError #:nodoc:
51
62
  def initialize(owner, reflection)
@@ -59,6 +70,12 @@ module ActiveRecord
59
70
  end
60
71
  end
61
72
 
73
+ class HasManyThroughNestedAssociationsAreReadonly < ActiveRecordError #:nodoc:
74
+ def initialize(owner, reflection)
75
+ super("Cannot modify association '#{owner.class.name}##{reflection.name}' because it goes through more than one other association.")
76
+ end
77
+ end
78
+
62
79
  class HasAndBelongsToManyAssociationForeignKeyNeeded < ActiveRecordError #:nodoc:
63
80
  def initialize(reflection)
64
81
  super("Cannot create self referential has_and_belongs_to_many association on '#{reflection.class_name rescue nil}##{reflection.name rescue nil}'. :association_foreign_key cannot be the same as the :foreign_key.")
@@ -73,51 +90,97 @@ module ActiveRecord
73
90
 
74
91
  class ReadOnlyAssociation < ActiveRecordError #:nodoc:
75
92
  def initialize(reflection)
76
- super("Can not add to a has_many :through association. Try adding to #{reflection.through_reflection.name.inspect}.")
93
+ super("Can not add to a has_many :through association. Try adding to #{reflection.through_reflection.name.inspect}.")
94
+ end
95
+ end
96
+
97
+ # This error is raised when trying to destroy a parent instance in N:1 or 1:1 associations
98
+ # (has_many, has_one) when there is at least 1 child associated instance.
99
+ # ex: if @project.tasks.size > 0, DeleteRestrictionError will be raised when trying to destroy @project
100
+ class DeleteRestrictionError < ActiveRecordError #:nodoc:
101
+ def initialize(name)
102
+ super("Cannot delete record because of dependent #{name}")
77
103
  end
78
104
  end
79
105
 
80
106
  # See ActiveRecord::Associations::ClassMethods for documentation.
81
107
  module Associations # :nodoc:
108
+ extend ActiveSupport::Autoload
109
+ extend ActiveSupport::Concern
110
+
82
111
  # These classes will be loaded when associations are created.
83
112
  # So there is no need to eager load them.
84
- autoload :AssociationCollection, 'active_record/associations/association_collection'
85
- autoload :AssociationProxy, 'active_record/associations/association_proxy'
86
- autoload :BelongsToAssociation, 'active_record/associations/belongs_to_association'
113
+ autoload :Association, 'active_record/associations/association'
114
+ autoload :SingularAssociation, 'active_record/associations/singular_association'
115
+ autoload :CollectionAssociation, 'active_record/associations/collection_association'
116
+ autoload :CollectionProxy, 'active_record/associations/collection_proxy'
117
+
118
+ autoload :BelongsToAssociation, 'active_record/associations/belongs_to_association'
87
119
  autoload :BelongsToPolymorphicAssociation, 'active_record/associations/belongs_to_polymorphic_association'
88
- autoload :HasAndBelongsToManyAssociation, 'active_record/associations/has_and_belongs_to_many_association'
89
- autoload :HasManyAssociation, 'active_record/associations/has_many_association'
90
- autoload :HasManyThroughAssociation, 'active_record/associations/has_many_through_association'
91
- autoload :HasOneAssociation, 'active_record/associations/has_one_association'
92
- autoload :HasOneThroughAssociation, 'active_record/associations/has_one_through_association'
120
+ autoload :HasAndBelongsToManyAssociation, 'active_record/associations/has_and_belongs_to_many_association'
121
+ autoload :HasManyAssociation, 'active_record/associations/has_many_association'
122
+ autoload :HasManyThroughAssociation, 'active_record/associations/has_many_through_association'
123
+ autoload :HasOneAssociation, 'active_record/associations/has_one_association'
124
+ autoload :HasOneThroughAssociation, 'active_record/associations/has_one_through_association'
125
+ autoload :ThroughAssociation, 'active_record/associations/through_association'
126
+
127
+ module Builder #:nodoc:
128
+ autoload :Association, 'active_record/associations/builder/association'
129
+ autoload :SingularAssociation, 'active_record/associations/builder/singular_association'
130
+ autoload :CollectionAssociation, 'active_record/associations/builder/collection_association'
131
+
132
+ autoload :BelongsTo, 'active_record/associations/builder/belongs_to'
133
+ autoload :HasOne, 'active_record/associations/builder/has_one'
134
+ autoload :HasMany, 'active_record/associations/builder/has_many'
135
+ autoload :HasAndBelongsToMany, 'active_record/associations/builder/has_and_belongs_to_many'
136
+ end
93
137
 
94
- def self.included(base)
95
- base.extend(ClassMethods)
138
+ eager_autoload do
139
+ autoload :Preloader, 'active_record/associations/preloader'
140
+ autoload :JoinDependency, 'active_record/associations/join_dependency'
141
+ autoload :AssociationScope, 'active_record/associations/association_scope'
142
+ autoload :AliasTracker, 'active_record/associations/alias_tracker'
143
+ autoload :JoinHelper, 'active_record/associations/join_helper'
96
144
  end
97
145
 
98
- # Clears out the association cache
146
+ # Clears out the association cache.
99
147
  def clear_association_cache #:nodoc:
100
- self.class.reflect_on_all_associations.to_a.each do |assoc|
101
- instance_variable_set "@#{assoc.name}", nil
102
- end unless self.new_record?
148
+ @association_cache.clear if persisted?
149
+ end
150
+
151
+ # :nodoc:
152
+ attr_reader :association_cache
153
+
154
+ # Returns the association instance for the given name, instantiating it if it doesn't already exist
155
+ def association(name) #:nodoc:
156
+ association = association_instance_get(name)
157
+
158
+ if association.nil?
159
+ reflection = self.class.reflect_on_association(name)
160
+ association = reflection.association_class.new(self, reflection)
161
+ association_instance_set(name, association)
162
+ end
163
+
164
+ association
103
165
  end
104
166
 
105
167
  private
106
- # Gets the specified association instance if it responds to :loaded?, nil otherwise.
168
+ # Returns the specified association instance if it responds to :loaded?, nil otherwise.
107
169
  def association_instance_get(name)
108
- association = instance_variable_get("@#{name}")
109
- association if association.respond_to?(:loaded?)
170
+ @association_cache[name.to_sym]
110
171
  end
111
172
 
112
173
  # Set the specified association instance.
113
174
  def association_instance_set(name, association)
114
- instance_variable_set("@#{name}", association)
175
+ @association_cache[name] = association
115
176
  end
116
177
 
117
- # Associations are a set of macro-like class methods for tying objects together through foreign keys. They express relationships like
118
- # "Project has one Project Manager" or "Project belongs to a Portfolio". Each macro adds a number of methods to the class which are
119
- # specialized according to the collection or association symbol and the options hash. It works much the same way as Ruby's own <tt>attr*</tt>
120
- # methods. Example:
178
+ # Associations are a set of macro-like class methods for tying objects together through
179
+ # foreign keys. They express relationships like "Project has one Project Manager"
180
+ # or "Project belongs to a Portfolio". Each macro adds a number of methods to the
181
+ # class which are specialized according to the collection or association symbol and the
182
+ # options hash. It works much the same way as Ruby's own <tt>attr*</tt>
183
+ # methods.
121
184
  #
122
185
  # class Project < ActiveRecord::Base
123
186
  # belongs_to :portfolio
@@ -126,19 +189,41 @@ module ActiveRecord
126
189
  # has_and_belongs_to_many :categories
127
190
  # end
128
191
  #
129
- # The project class now has the following methods (and more) to ease the traversal and manipulation of its relationships:
192
+ # The project class now has the following methods (and more) to ease the traversal and
193
+ # manipulation of its relationships:
130
194
  # * <tt>Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil?</tt>
131
195
  # * <tt>Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?,</tt>
132
196
  # * <tt>Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone),</tt>
133
- # <tt>Project#milestones.delete(milestone), Project#milestones.find(milestone_id), Project#milestones.find(:all, options),</tt>
197
+ # <tt>Project#milestones.delete(milestone), Project#milestones.find(milestone_id), Project#milestones.all(options),</tt>
134
198
  # <tt>Project#milestones.build, Project#milestones.create</tt>
135
199
  # * <tt>Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),</tt>
136
200
  # <tt>Project#categories.delete(category1)</tt>
137
201
  #
202
+ # === Overriding generated methods
203
+ #
204
+ # Association methods are generated in a module that is included into the model class,
205
+ # which allows you to easily override with your own methods and call the original
206
+ # generated method with +super+. For example:
207
+ #
208
+ # class Car < ActiveRecord::Base
209
+ # belongs_to :owner
210
+ # belongs_to :old_owner
211
+ # def owner=(new_owner)
212
+ # self.old_owner = self.owner
213
+ # super
214
+ # end
215
+ # end
216
+ #
217
+ # If your model class is <tt>Project</tt>, the module is
218
+ # named <tt>Project::GeneratedFeatureMethods</tt>. The GeneratedFeatureMethods module is
219
+ # included in the model class immediately after the (anonymous) generated attributes methods
220
+ # module, meaning an association will override the methods for an attribute with the same name.
221
+ #
138
222
  # === A word of warning
139
223
  #
140
- # Don't create associations that have the same name as instance methods of ActiveRecord::Base. Since the association
141
- # adds a method with that name to its model, it will override the inherited method and break things.
224
+ # Don't create associations that have the same name as instance methods of
225
+ # <tt>ActiveRecord::Base</tt>. Since the association adds a method with that name to
226
+ # its model, it will override the inherited method and break things.
142
227
  # For instance, +attributes+ and +connection+ would be bad choices for association names.
143
228
  #
144
229
  # == Auto-generated methods
@@ -151,7 +236,7 @@ module ActiveRecord
151
236
  # other=(other) | X | X | X
152
237
  # build_other(attributes={}) | X | | X
153
238
  # create_other(attributes={}) | X | | X
154
- # other.create!(attributes={}) | | | X
239
+ # create_other!(attributes={}) | X | | X
155
240
  #
156
241
  # ===Collection associations (one-to-many / many-to-many)
157
242
  # | | | has_many
@@ -174,10 +259,9 @@ module ActiveRecord
174
259
  # others.empty? | X | X | X
175
260
  # others.clear | X | X | X
176
261
  # others.delete(other,other,...) | X | X | X
177
- # others.delete_all | X | X |
262
+ # others.delete_all | X | X | X
178
263
  # others.destroy_all | X | X | X
179
264
  # others.find(*args) | X | X | X
180
- # others.find_first | X | |
181
265
  # others.exists? | X | X | X
182
266
  # others.uniq | X | X | X
183
267
  # others.reset | X | X | X
@@ -248,8 +332,8 @@ module ActiveRecord
248
332
  #
249
333
  # == Is it a +belongs_to+ or +has_one+ association?
250
334
  #
251
- # Both express a 1-1 relationship. The difference is mostly where to place the foreign key, which goes on the table for the class
252
- # declaring the +belongs_to+ relationship. Example:
335
+ # Both express a 1-1 relationship. The difference is mostly where to place the foreign
336
+ # key, which goes on the table for the class declaring the +belongs_to+ relationship.
253
337
  #
254
338
  # class User < ActiveRecord::Base
255
339
  # # I reference an account.
@@ -278,36 +362,50 @@ module ActiveRecord
278
362
  #
279
363
  # == Unsaved objects and associations
280
364
  #
281
- # You can manipulate objects and associations before they are saved to the database, but there is some special behavior you should be
282
- # aware of, mostly involving the saving of associated objects.
365
+ # You can manipulate objects and associations before they are saved to the database, but
366
+ # there is some special behavior you should be aware of, mostly involving the saving of
367
+ # associated objects.
283
368
  #
284
- # Unless you set the :autosave option on a <tt>has_one</tt>, <tt>belongs_to</tt>,
369
+ # You can set the :autosave option on a <tt>has_one</tt>, <tt>belongs_to</tt>,
285
370
  # <tt>has_many</tt>, or <tt>has_and_belongs_to_many</tt> association. Setting it
286
371
  # to +true+ will _always_ save the members, whereas setting it to +false+ will
287
- # _never_ save the members.
372
+ # _never_ save the members. More details about :autosave option is available at
373
+ # autosave_association.rb .
288
374
  #
289
375
  # === One-to-one associations
290
376
  #
291
- # * Assigning an object to a +has_one+ association automatically saves that object and the object being replaced (if there is one), in
292
- # order to update their primary keys - except if the parent object is unsaved (<tt>new_record? == true</tt>).
293
- # * If either of these saves fail (due to one of the objects being invalid) the assignment statement returns +false+ and the assignment
294
- # is cancelled.
295
- # * If you wish to assign an object to a +has_one+ association without saving it, use the <tt>association.build</tt> method (documented below).
296
- # * Assigning an object to a +belongs_to+ association does not save the object, since the foreign key field belongs on the parent. It
297
- # does not save the parent either.
377
+ # * Assigning an object to a +has_one+ association automatically saves that object and
378
+ # the object being replaced (if there is one), in order to update their foreign
379
+ # keys - except if the parent object is unsaved (<tt>new_record? == true</tt>).
380
+ # * If either of these saves fail (due to one of the objects being invalid), an
381
+ # <tt>ActiveRecord::RecordNotSaved</tt> exception is raised and the assignment is
382
+ # cancelled.
383
+ # * If you wish to assign an object to a +has_one+ association without saving it,
384
+ # use the <tt>build_association</tt> method (documented below). The object being
385
+ # replaced will still be saved to update its foreign key.
386
+ # * Assigning an object to a +belongs_to+ association does not save the object, since
387
+ # the foreign key field belongs on the parent. It does not save the parent either.
298
388
  #
299
389
  # === Collections
300
390
  #
301
- # * Adding an object to a collection (+has_many+ or +has_and_belongs_to_many+) automatically saves that object, except if the parent object
302
- # (the owner of the collection) is not yet stored in the database.
303
- # * If saving any of the objects being added to a collection (via <tt>push</tt> or similar) fails, then <tt>push</tt> returns +false+.
304
- # * You can add an object to a collection without automatically saving it by using the <tt>collection.build</tt> method (documented below).
305
- # * All unsaved (<tt>new_record? == true</tt>) members of the collection are automatically saved when the parent is saved.
391
+ # * Adding an object to a collection (+has_many+ or +has_and_belongs_to_many+) automatically
392
+ # saves that object, except if the parent object (the owner of the collection) is not yet
393
+ # stored in the database.
394
+ # * If saving any of the objects being added to a collection (via <tt>push</tt> or similar)
395
+ # fails, then <tt>push</tt> returns +false+.
396
+ # * If saving fails while replacing the collection (via <tt>association=</tt>), an
397
+ # <tt>ActiveRecord::RecordNotSaved</tt> exception is raised and the assignment is
398
+ # cancelled.
399
+ # * You can add an object to a collection without automatically saving it by using the
400
+ # <tt>collection.build</tt> method (documented below).
401
+ # * All unsaved (<tt>new_record? == true</tt>) members of the collection are automatically
402
+ # saved when the parent is saved.
306
403
  #
307
404
  # === Association callbacks
308
405
  #
309
- # Similar to the normal callbacks that hook into the lifecycle of an Active Record object, you can also define callbacks that get
310
- # triggered when you add an object to or remove an object from an association collection. Example:
406
+ # Similar to the normal callbacks that hook into the life cycle of an Active Record object,
407
+ # you can also define callbacks that get triggered when you add an object to or remove an
408
+ # object from an association collection.
311
409
  #
312
410
  # class Project
313
411
  # has_and_belongs_to_many :developers, :after_add => :evaluate_velocity
@@ -320,19 +418,21 @@ module ActiveRecord
320
418
  # It's possible to stack callbacks by passing them as an array. Example:
321
419
  #
322
420
  # class Project
323
- # has_and_belongs_to_many :developers, :after_add => [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}]
421
+ # has_and_belongs_to_many :developers,
422
+ # :after_add => [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}]
324
423
  # end
325
424
  #
326
425
  # Possible callbacks are: +before_add+, +after_add+, +before_remove+ and +after_remove+.
327
426
  #
328
- # Should any of the +before_add+ callbacks throw an exception, the object does not get added to the collection. Same with
329
- # the +before_remove+ callbacks; if an exception is thrown the object doesn't get removed.
427
+ # Should any of the +before_add+ callbacks throw an exception, the object does not get
428
+ # added to the collection. Same with the +before_remove+ callbacks; if an exception is
429
+ # thrown the object doesn't get removed.
330
430
  #
331
431
  # === Association extensions
332
432
  #
333
- # The proxy objects that control the access to associations can be extended through anonymous modules. This is especially
334
- # beneficial for adding new finders, creators, and other factory-type methods that are only used as part of this association.
335
- # Example:
433
+ # The proxy objects that control the access to associations can be extended through anonymous
434
+ # modules. This is especially beneficial for adding new finders, creators, and other
435
+ # factory-type methods that are only used as part of this association.
336
436
  #
337
437
  # class Account < ActiveRecord::Base
338
438
  # has_many :people do
@@ -343,11 +443,12 @@ module ActiveRecord
343
443
  # end
344
444
  # end
345
445
  #
346
- # person = Account.find(:first).people.find_or_create_by_name("David Heinemeier Hansson")
446
+ # person = Account.first.people.find_or_create_by_name("David Heinemeier Hansson")
347
447
  # person.first_name # => "David"
348
448
  # person.last_name # => "Heinemeier Hansson"
349
449
  #
350
- # If you need to share the same extensions between many associations, you can use a named extension module. Example:
450
+ # If you need to share the same extensions between many associations, you can use a named
451
+ # extension module.
351
452
  #
352
453
  # module FindOrCreateByNameExtension
353
454
  # def find_or_create_by_name(name)
@@ -364,26 +465,36 @@ module ActiveRecord
364
465
  # has_many :people, :extend => FindOrCreateByNameExtension
365
466
  # end
366
467
  #
367
- # If you need to use multiple named extension modules, you can specify an array of modules with the <tt>:extend</tt> option.
368
- # In the case of name conflicts between methods in the modules, methods in modules later in the array supercede
369
- # those earlier in the array. Example:
468
+ # If you need to use multiple named extension modules, you can specify an array of modules
469
+ # with the <tt>:extend</tt> option.
470
+ # In the case of name conflicts between methods in the modules, methods in modules later
471
+ # in the array supercede those earlier in the array.
370
472
  #
371
473
  # class Account < ActiveRecord::Base
372
474
  # has_many :people, :extend => [FindOrCreateByNameExtension, FindRecentExtension]
373
475
  # end
374
476
  #
375
- # Some extensions can only be made to work with knowledge of the association proxy's internals.
376
- # Extensions can access relevant state using accessors on the association proxy:
477
+ # Some extensions can only be made to work with knowledge of the association's internals.
478
+ # Extensions can access relevant state using the following methods (where +items+ is the
479
+ # name of the association):
480
+ #
481
+ # * <tt>record.association(:items).owner</tt> - Returns the object the association is part of.
482
+ # * <tt>record.association(:items).reflection</tt> - Returns the reflection object that describes the association.
483
+ # * <tt>record.association(:items).target</tt> - Returns the associated object for +belongs_to+ and +has_one+, or
484
+ # the collection of associated objects for +has_many+ and +has_and_belongs_to_many+.
377
485
  #
378
- # * +proxy_owner+ - Returns the object the association is part of.
379
- # * +proxy_reflection+ - Returns the reflection object that describes the association.
380
- # * +proxy_target+ - Returns the associated object for +belongs_to+ and +has_one+, or the collection of associated objects for +has_many+ and +has_and_belongs_to_many+.
486
+ # However, inside the actual extension code, you will not have access to the <tt>record</tt> as
487
+ # above. In this case, you can access <tt>proxy_association</tt>. For example,
488
+ # <tt>record.association(:items)</tt> and <tt>record.items.proxy_association</tt> will return
489
+ # the same object, allowing you to make calls like <tt>proxy_association.owner</tt> inside
490
+ # association extensions.
381
491
  #
382
492
  # === Association Join Models
383
493
  #
384
- # Has Many associations can be configured with the <tt>:through</tt> option to use an explicit join model to retrieve the data. This
385
- # operates similarly to a +has_and_belongs_to_many+ association. The advantage is that you're able to add validations,
386
- # callbacks, and extra attributes on the join model. Consider the following schema:
494
+ # Has Many associations can be configured with the <tt>:through</tt> option to use an
495
+ # explicit join model to retrieve the data. This operates similarly to a
496
+ # +has_and_belongs_to_many+ association. The advantage is that you're able to add validations,
497
+ # callbacks, and extra attributes on the join model. Consider the following schema:
387
498
  #
388
499
  # class Author < ActiveRecord::Base
389
500
  # has_many :authorships
@@ -395,8 +506,8 @@ module ActiveRecord
395
506
  # belongs_to :book
396
507
  # end
397
508
  #
398
- # @author = Author.find :first
399
- # @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to.
509
+ # @author = Author.first
510
+ # @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to
400
511
  # @author.books # selects all books by using the Authorship join model
401
512
  #
402
513
  # You can also go through a +has_many+ association on the join model:
@@ -415,9 +526,9 @@ module ActiveRecord
415
526
  # belongs_to :client
416
527
  # end
417
528
  #
418
- # @firm = Firm.find :first
529
+ # @firm = Firm.first
419
530
  # @firm.clients.collect { |c| c.invoices }.flatten # select all invoices for all clients of the firm
420
- # @firm.invoices # selects all invoices by going through the Client join model.
531
+ # @firm.invoices # selects all invoices by going through the Client join model
421
532
  #
422
533
  # Similarly you can go through a +has_one+ association on the join model:
423
534
  #
@@ -439,16 +550,77 @@ module ActiveRecord
439
550
  # @group.users.collect { |u| u.avatar }.flatten # select all avatars for all users in the group
440
551
  # @group.avatars # selects all avatars by going through the User join model.
441
552
  #
442
- # An important caveat with going through +has_one+ or +has_many+ associations on the join model is that these associations are
443
- # *read-only*. For example, the following would not work following the previous example:
553
+ # An important caveat with going through +has_one+ or +has_many+ associations on the
554
+ # join model is that these associations are *read-only*. For example, the following
555
+ # would not work following the previous example:
444
556
  #
445
- # @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around.
557
+ # @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around
446
558
  # @group.avatars.delete(@group.avatars.last) # so would this
447
559
  #
560
+ # If you are using a +belongs_to+ on the join model, it is a good idea to set the
561
+ # <tt>:inverse_of</tt> option on the +belongs_to+, which will mean that the following example
562
+ # works correctly (where <tt>tags</tt> is a +has_many+ <tt>:through</tt> association):
563
+ #
564
+ # @post = Post.first
565
+ # @tag = @post.tags.build :name => "ruby"
566
+ # @tag.save
567
+ #
568
+ # The last line ought to save the through record (a <tt>Taggable</tt>). This will only work if the
569
+ # <tt>:inverse_of</tt> is set:
570
+ #
571
+ # class Taggable < ActiveRecord::Base
572
+ # belongs_to :post
573
+ # belongs_to :tag, :inverse_of => :taggings
574
+ # end
575
+ #
576
+ # === Nested Associations
577
+ #
578
+ # You can actually specify *any* association with the <tt>:through</tt> option, including an
579
+ # association which has a <tt>:through</tt> option itself. For example:
580
+ #
581
+ # class Author < ActiveRecord::Base
582
+ # has_many :posts
583
+ # has_many :comments, :through => :posts
584
+ # has_many :commenters, :through => :comments
585
+ # end
586
+ #
587
+ # class Post < ActiveRecord::Base
588
+ # has_many :comments
589
+ # end
590
+ #
591
+ # class Comment < ActiveRecord::Base
592
+ # belongs_to :commenter
593
+ # end
594
+ #
595
+ # @author = Author.first
596
+ # @author.commenters # => People who commented on posts written by the author
597
+ #
598
+ # An equivalent way of setting up this association this would be:
599
+ #
600
+ # class Author < ActiveRecord::Base
601
+ # has_many :posts
602
+ # has_many :commenters, :through => :posts
603
+ # end
604
+ #
605
+ # class Post < ActiveRecord::Base
606
+ # has_many :comments
607
+ # has_many :commenters, :through => :comments
608
+ # end
609
+ #
610
+ # class Comment < ActiveRecord::Base
611
+ # belongs_to :commenter
612
+ # end
613
+ #
614
+ # When using nested association, you will not be able to modify the association because there
615
+ # is not enough information to know what modification to make. For example, if you tried to
616
+ # add a <tt>Commenter</tt> in the example above, there would be no way to tell how to set up the
617
+ # intermediate <tt>Post</tt> and <tt>Comment</tt> objects.
618
+ #
448
619
  # === Polymorphic Associations
449
620
  #
450
- # Polymorphic associations on models are not restricted on what types of models they can be associated with. Rather, they
451
- # specify an interface that a +has_many+ association must adhere to.
621
+ # Polymorphic associations on models are not restricted on what types of models they
622
+ # can be associated with. Rather, they specify an interface that a +has_many+ association
623
+ # must adhere to.
452
624
  #
453
625
  # class Asset < ActiveRecord::Base
454
626
  # belongs_to :attachable, :polymorphic => true
@@ -460,13 +632,16 @@ module ActiveRecord
460
632
  #
461
633
  # @asset.attachable = @post
462
634
  #
463
- # This works by using a type column in addition to a foreign key to specify the associated record. In the Asset example, you'd need
464
- # an +attachable_id+ integer column and an +attachable_type+ string column.
635
+ # This works by using a type column in addition to a foreign key to specify the associated
636
+ # record. In the Asset example, you'd need an +attachable_id+ integer column and an
637
+ # +attachable_type+ string column.
465
638
  #
466
- # Using polymorphic associations in combination with single table inheritance (STI) is a little tricky. In order
467
- # for the associations to work as expected, ensure that you store the base model for the STI models in the
468
- # type column of the polymorphic association. To continue with the asset example above, suppose there are guest posts
469
- # and member posts that use the posts table for STI. In this case, there must be a +type+ column in the posts table.
639
+ # Using polymorphic associations in combination with single table inheritance (STI) is
640
+ # a little tricky. In order for the associations to work as expected, ensure that you
641
+ # store the base model for the STI models in the type column of the polymorphic
642
+ # association. To continue with the asset example above, suppose there are guest posts
643
+ # and member posts that use the posts table for STI. In this case, there must be a +type+
644
+ # column in the posts table.
470
645
  #
471
646
  # class Asset < ActiveRecord::Base
472
647
  # belongs_to :attachable, :polymorphic => true
@@ -489,9 +664,10 @@ module ActiveRecord
489
664
  #
490
665
  # == Caching
491
666
  #
492
- # All of the methods are built on a simple caching principle that will keep the result of the last query around unless specifically
493
- # instructed not to. The cache is even shared across methods to make it even cheaper to use the macro-added methods without
494
- # worrying too much about performance at the first go. Example:
667
+ # All of the methods are built on a simple caching principle that will keep the result
668
+ # of the last query around unless specifically instructed not to. The cache is even
669
+ # shared across methods to make it even cheaper to use the macro-added methods without
670
+ # worrying too much about performance at the first go.
495
671
  #
496
672
  # project.milestones # fetches milestones from the database
497
673
  # project.milestones.size # uses the milestone cache
@@ -501,9 +677,10 @@ module ActiveRecord
501
677
  #
502
678
  # == Eager loading of associations
503
679
  #
504
- # Eager loading is a way to find objects of a certain class and a number of named associations. This is
505
- # one of the easiest ways of to prevent the dreaded 1+N problem in which fetching 100 posts that each need to display their author
506
- # triggers 101 database queries. Through the use of eager loading, the 101 queries can be reduced to 2. Example:
680
+ # Eager loading is a way to find objects of a certain class and a number of named associations.
681
+ # This is one of the easiest ways of to prevent the dreaded 1+N problem in which fetching 100
682
+ # posts that each need to display their author triggers 101 database queries. Through the
683
+ # use of eager loading, the 101 queries can be reduced to 2.
507
684
  #
508
685
  # class Post < ActiveRecord::Base
509
686
  # belongs_to :author
@@ -512,69 +689,82 @@ module ActiveRecord
512
689
  #
513
690
  # Consider the following loop using the class above:
514
691
  #
515
- # for post in Post.all
692
+ # Post.all.each do |post|
516
693
  # puts "Post: " + post.title
517
694
  # puts "Written by: " + post.author.name
518
695
  # puts "Last comment on: " + post.comments.first.created_on
519
696
  # end
520
697
  #
521
- # To iterate over these one hundred posts, we'll generate 201 database queries. Let's first just optimize it for retrieving the author:
698
+ # To iterate over these one hundred posts, we'll generate 201 database queries. Let's
699
+ # first just optimize it for retrieving the author:
522
700
  #
523
- # for post in Post.find(:all, :include => :author)
701
+ # Post.includes(:author).each do |post|
524
702
  #
525
- # This references the name of the +belongs_to+ association that also used the <tt>:author</tt> symbol. After loading the posts, find
526
- # will collect the +author_id+ from each one and load all the referenced authors with one query. Doing so will cut down the number of queries from 201 to 102.
703
+ # This references the name of the +belongs_to+ association that also used the <tt>:author</tt>
704
+ # symbol. After loading the posts, find will collect the +author_id+ from each one and load
705
+ # all the referenced authors with one query. Doing so will cut down the number of queries
706
+ # from 201 to 102.
527
707
  #
528
708
  # We can improve upon the situation further by referencing both associations in the finder with:
529
709
  #
530
- # for post in Post.find(:all, :include => [ :author, :comments ])
710
+ # Post.includes(:author, :comments).each do |post|
531
711
  #
532
- # This will load all comments with a single query. This reduces the total number of queries to 3. More generally the number of queries
533
- # will be 1 plus the number of associations named (except if some of the associations are polymorphic +belongs_to+ - see below).
712
+ # This will load all comments with a single query. This reduces the total number of queries
713
+ # to 3. More generally the number of queries will be 1 plus the number of associations
714
+ # named (except if some of the associations are polymorphic +belongs_to+ - see below).
534
715
  #
535
716
  # To include a deep hierarchy of associations, use a hash:
536
717
  #
537
- # for post in Post.find(:all, :include => [ :author, { :comments => { :author => :gravatar } } ])
718
+ # Post.includes(:author, {:comments => {:author => :gravatar}}).each do |post|
719
+ #
720
+ # That'll grab not only all the comments but all their authors and gravatar pictures.
721
+ # You can mix and match symbols, arrays and hashes in any combination to describe the
722
+ # associations you want to load.
538
723
  #
539
- # That'll grab not only all the comments but all their authors and gravatar pictures. You can mix and match
540
- # symbols, arrays and hashes in any combination to describe the associations you want to load.
724
+ # All of this power shouldn't fool you into thinking that you can pull out huge amounts
725
+ # of data with no performance penalty just because you've reduced the number of queries.
726
+ # The database still needs to send all the data to Active Record and it still needs to
727
+ # be processed. So it's no catch-all for performance problems, but it's a great way to
728
+ # cut down on the number of queries in a situation as the one described above.
541
729
  #
542
- # All of this power shouldn't fool you into thinking that you can pull out huge amounts of data with no performance penalty just because you've reduced
543
- # the number of queries. The database still needs to send all the data to Active Record and it still needs to be processed. So it's no
544
- # catch-all for performance problems, but it's a great way to cut down on the number of queries in a situation as the one described above.
730
+ # Since only one table is loaded at a time, conditions or orders cannot reference tables
731
+ # other than the main one. If this is the case Active Record falls back to the previously
732
+ # used LEFT OUTER JOIN based strategy. For example
545
733
  #
546
- # Since only one table is loaded at a time, conditions or orders cannot reference tables other than the main one. If this is the case
547
- # Active Record falls back to the previously used LEFT OUTER JOIN based strategy. For example
548
- #
549
- # Post.find(:all, :include => [ :author, :comments ], :conditions => ['comments.approved = ?', true])
734
+ # Post.includes([:author, :comments]).where(['comments.approved = ?', true]).all
550
735
  #
551
- # will result in a single SQL query with joins along the lines of: <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and
552
- # <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Note that using conditions like this can have unintended consequences.
553
- # In the above example posts with no approved comments are not returned at all, because the conditions apply to the SQL statement as a whole
554
- # and not just to the association. You must disambiguate column references for this fallback to happen, for example
555
- # <tt>:order => "author.name DESC"</tt> will work but <tt>:order => "name DESC"</tt> will not.
736
+ # This will result in a single SQL query with joins along the lines of:
737
+ # <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and
738
+ # <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Note that using conditions
739
+ # like this can have unintended consequences.
740
+ # In the above example posts with no approved comments are not returned at all, because
741
+ # the conditions apply to the SQL statement as a whole and not just to the association.
742
+ # You must disambiguate column references for this fallback to happen, for example
743
+ # <tt>:order => "author.name DESC"</tt> will work but <tt>:order => "name DESC"</tt> will not.
556
744
  #
557
- # If you do want eagerload only some members of an association it is usually more natural to <tt>:include</tt> an association
558
- # which has conditions defined on it:
745
+ # If you do want eager load only some members of an association it is usually more natural
746
+ # to include an association which has conditions defined on it:
559
747
  #
560
748
  # class Post < ActiveRecord::Base
561
749
  # has_many :approved_comments, :class_name => 'Comment', :conditions => ['approved = ?', true]
562
750
  # end
563
751
  #
564
- # Post.find(:all, :include => :approved_comments)
752
+ # Post.includes(:approved_comments)
565
753
  #
566
- # will load posts and eager load the +approved_comments+ association, which contains only those comments that have been approved.
754
+ # This will load posts and eager load the +approved_comments+ association, which contains
755
+ # only those comments that have been approved.
567
756
  #
568
- # If you eager load an association with a specified <tt>:limit</tt> option, it will be ignored, returning all the associated objects:
757
+ # If you eager load an association with a specified <tt>:limit</tt> option, it will be ignored,
758
+ # returning all the associated objects:
569
759
  #
570
760
  # class Picture < ActiveRecord::Base
571
761
  # has_many :most_recent_comments, :class_name => 'Comment', :order => 'id DESC', :limit => 10
572
762
  # end
573
763
  #
574
- # Picture.find(:first, :include => :most_recent_comments).most_recent_comments # => returns all associated comments.
764
+ # Picture.includes(:most_recent_comments).first.most_recent_comments # => returns all associated comments.
575
765
  #
576
- # When eager loaded, conditions are interpolated in the context of the model class, not the model instance. Conditions are lazily interpolated
577
- # before the actual model exists.
766
+ # When eager loaded, conditions are interpolated in the context of the model class, not
767
+ # the model instance. Conditions are lazily interpolated before the actual model exists.
578
768
  #
579
769
  # Eager loading is supported with polymorphic associations.
580
770
  #
@@ -584,61 +774,67 @@ module ActiveRecord
584
774
  #
585
775
  # A call that tries to eager load the addressable model
586
776
  #
587
- # Address.find(:all, :include => :addressable)
777
+ # Address.includes(:addressable)
588
778
  #
589
- # will execute one query to load the addresses and load the addressables with one query per addressable type.
590
- # For example if all the addressables are either of class Person or Company then a total of 3 queries will be executed. The list of
591
- # addressable types to load is determined on the back of the addresses loaded. This is not supported if Active Record has to fallback
592
- # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError. The reason is that the parent
593
- # model's type is a column value so its corresponding table name cannot be put in the +FROM+/+JOIN+ clauses of that query.
779
+ # This will execute one query to load the addresses and load the addressables with one
780
+ # query per addressable type.
781
+ # For example if all the addressables are either of class Person or Company then a total
782
+ # of 3 queries will be executed. The list of addressable types to load is determined on
783
+ # the back of the addresses loaded. This is not supported if Active Record has to fallback
784
+ # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError.
785
+ # The reason is that the parent model's type is a column value so its corresponding table
786
+ # name cannot be put in the +FROM+/+JOIN+ clauses of that query.
594
787
  #
595
788
  # == Table Aliasing
596
789
  #
597
- # Active Record uses table aliasing in the case that a table is referenced multiple times in a join. If a table is referenced only once,
598
- # the standard table name is used. The second time, the table is aliased as <tt>#{reflection_name}_#{parent_table_name}</tt>. Indexes are appended
599
- # for any more successive uses of the table name.
790
+ # Active Record uses table aliasing in the case that a table is referenced multiple times
791
+ # in a join. If a table is referenced only once, the standard table name is used. The
792
+ # second time, the table is aliased as <tt>#{reflection_name}_#{parent_table_name}</tt>.
793
+ # Indexes are appended for any more successive uses of the table name.
600
794
  #
601
- # Post.find :all, :joins => :comments
795
+ # Post.joins(:comments)
602
796
  # # => SELECT ... FROM posts INNER JOIN comments ON ...
603
- # Post.find :all, :joins => :special_comments # STI
797
+ # Post.joins(:special_comments) # STI
604
798
  # # => SELECT ... FROM posts INNER JOIN comments ON ... AND comments.type = 'SpecialComment'
605
- # Post.find :all, :joins => [:comments, :special_comments] # special_comments is the reflection name, posts is the parent table name
799
+ # Post.joins(:comments, :special_comments) # special_comments is the reflection name, posts is the parent table name
606
800
  # # => SELECT ... FROM posts INNER JOIN comments ON ... INNER JOIN comments special_comments_posts
607
801
  #
608
802
  # Acts as tree example:
609
803
  #
610
- # TreeMixin.find :all, :joins => :children
804
+ # TreeMixin.joins(:children)
611
805
  # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
612
- # TreeMixin.find :all, :joins => {:children => :parent}
806
+ # TreeMixin.joins(:children => :parent)
613
807
  # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
614
808
  # INNER JOIN parents_mixins ...
615
- # TreeMixin.find :all, :joins => {:children => {:parent => :children}}
809
+ # TreeMixin.joins(:children => {:parent => :children})
616
810
  # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
617
811
  # INNER JOIN parents_mixins ...
618
812
  # INNER JOIN mixins childrens_mixins_2
619
813
  #
620
814
  # Has and Belongs to Many join tables use the same idea, but add a <tt>_join</tt> suffix:
621
815
  #
622
- # Post.find :all, :joins => :categories
816
+ # Post.joins(:categories)
623
817
  # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
624
- # Post.find :all, :joins => {:categories => :posts}
818
+ # Post.joins(:categories => :posts)
625
819
  # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
626
820
  # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
627
- # Post.find :all, :joins => {:categories => {:posts => :categories}}
821
+ # Post.joins(:categories => {:posts => :categories})
628
822
  # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
629
823
  # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
630
824
  # INNER JOIN categories_posts categories_posts_join INNER JOIN categories categories_posts_2
631
825
  #
632
- # If you wish to specify your own custom joins using a <tt>:joins</tt> option, those table names will take precedence over the eager associations:
826
+ # If you wish to specify your own custom joins using <tt>joins</tt> method, those table
827
+ # names will take precedence over the eager associations:
633
828
  #
634
- # Post.find :all, :joins => :comments, :joins => "inner join comments ..."
829
+ # Post.joins(:comments).joins("inner join comments ...")
635
830
  # # => SELECT ... FROM posts INNER JOIN comments_posts ON ... INNER JOIN comments ...
636
- # Post.find :all, :joins => [:comments, :special_comments], :joins => "inner join comments ..."
831
+ # Post.joins(:comments, :special_comments).joins("inner join comments ...")
637
832
  # # => SELECT ... FROM posts INNER JOIN comments comments_posts ON ...
638
833
  # INNER JOIN comments special_comments_posts ...
639
834
  # INNER JOIN comments ...
640
835
  #
641
- # Table aliases are automatically truncated according to the maximum length of table identifiers according to the specific database.
836
+ # Table aliases are automatically truncated according to the maximum length of table identifiers
837
+ # according to the specific database.
642
838
  #
643
839
  # == Modules
644
840
  #
@@ -654,9 +850,10 @@ module ActiveRecord
654
850
  # end
655
851
  # end
656
852
  #
657
- # When <tt>Firm#clients</tt> is called, it will in turn call <tt>MyApplication::Business::Client.find_all_by_firm_id(firm.id)</tt>.
658
- # If you want to associate with a class in another module scope, this can be done by specifying the complete class name.
659
- # Example:
853
+ # When <tt>Firm#clients</tt> is called, it will in turn call
854
+ # <tt>MyApplication::Business::Client.find_all_by_firm_id(firm.id)</tt>.
855
+ # If you want to associate with a class in another module scope, this can be done by
856
+ # specifying the complete class name.
660
857
  #
661
858
  # module MyApplication
662
859
  # module Business
@@ -670,15 +867,140 @@ module ActiveRecord
670
867
  # end
671
868
  # end
672
869
  #
870
+ # == Bi-directional associations
871
+ #
872
+ # When you specify an association there is usually an association on the associated model
873
+ # that specifies the same relationship in reverse. For example, with the following models:
874
+ #
875
+ # class Dungeon < ActiveRecord::Base
876
+ # has_many :traps
877
+ # has_one :evil_wizard
878
+ # end
879
+ #
880
+ # class Trap < ActiveRecord::Base
881
+ # belongs_to :dungeon
882
+ # end
883
+ #
884
+ # class EvilWizard < ActiveRecord::Base
885
+ # belongs_to :dungeon
886
+ # end
887
+ #
888
+ # The +traps+ association on +Dungeon+ and the +dungeon+ association on +Trap+ are
889
+ # the inverse of each other and the inverse of the +dungeon+ association on +EvilWizard+
890
+ # is the +evil_wizard+ association on +Dungeon+ (and vice-versa). By default,
891
+ # Active Record doesn't know anything about these inverse relationships and so no object
892
+ # loading optimization is possible. For example:
893
+ #
894
+ # d = Dungeon.first
895
+ # t = d.traps.first
896
+ # d.level == t.dungeon.level # => true
897
+ # d.level = 10
898
+ # d.level == t.dungeon.level # => false
899
+ #
900
+ # The +Dungeon+ instances +d+ and <tt>t.dungeon</tt> in the above example refer to
901
+ # the same object data from the database, but are actually different in-memory copies
902
+ # of that data. Specifying the <tt>:inverse_of</tt> option on associations lets you tell
903
+ # Active Record about inverse relationships and it will optimise object loading. For
904
+ # example, if we changed our model definitions to:
905
+ #
906
+ # class Dungeon < ActiveRecord::Base
907
+ # has_many :traps, :inverse_of => :dungeon
908
+ # has_one :evil_wizard, :inverse_of => :dungeon
909
+ # end
910
+ #
911
+ # class Trap < ActiveRecord::Base
912
+ # belongs_to :dungeon, :inverse_of => :traps
913
+ # end
914
+ #
915
+ # class EvilWizard < ActiveRecord::Base
916
+ # belongs_to :dungeon, :inverse_of => :evil_wizard
917
+ # end
918
+ #
919
+ # Then, from our code snippet above, +d+ and <tt>t.dungeon</tt> are actually the same
920
+ # in-memory instance and our final <tt>d.level == t.dungeon.level</tt> will return +true+.
921
+ #
922
+ # There are limitations to <tt>:inverse_of</tt> support:
923
+ #
924
+ # * does not work with <tt>:through</tt> associations.
925
+ # * does not work with <tt>:polymorphic</tt> associations.
926
+ # * for +belongs_to+ associations +has_many+ inverse associations are ignored.
927
+ #
928
+ # == Deleting from associations
929
+ #
930
+ # === Dependent associations
931
+ #
932
+ # +has_many+, +has_one+ and +belongs_to+ associations support the <tt>:dependent</tt> option.
933
+ # This allows you to specify that associated records should be deleted when the owner is
934
+ # deleted.
935
+ #
936
+ # For example:
937
+ #
938
+ # class Author
939
+ # has_many :posts, :dependent => :destroy
940
+ # end
941
+ # Author.find(1).destroy # => Will destroy all of the author's posts, too
942
+ #
943
+ # The <tt>:dependent</tt> option can have different values which specify how the deletion
944
+ # is done. For more information, see the documentation for this option on the different
945
+ # specific association types.
946
+ #
947
+ # === Delete or destroy?
948
+ #
949
+ # +has_many+ and +has_and_belongs_to_many+ associations have the methods <tt>destroy</tt>,
950
+ # <tt>delete</tt>, <tt>destroy_all</tt> and <tt>delete_all</tt>.
951
+ #
952
+ # For +has_and_belongs_to_many+, <tt>delete</tt> and <tt>destroy</tt> are the same: they
953
+ # cause the records in the join table to be removed.
954
+ #
955
+ # For +has_many+, <tt>destroy</tt> will always call the <tt>destroy</tt> method of the
956
+ # record(s) being removed so that callbacks are run. However <tt>delete</tt> will either
957
+ # do the deletion according to the strategy specified by the <tt>:dependent</tt> option, or
958
+ # if no <tt>:dependent</tt> option is given, then it will follow the default strategy.
959
+ # The default strategy is <tt>:nullify</tt> (set the foreign keys to <tt>nil</tt>), except for
960
+ # +has_many+ <tt>:through</tt>, where the default strategy is <tt>delete_all</tt> (delete
961
+ # the join records, without running their callbacks).
962
+ #
963
+ # There is also a <tt>clear</tt> method which is the same as <tt>delete_all</tt>, except that
964
+ # it returns the association rather than the records which have been deleted.
965
+ #
966
+ # === What gets deleted?
967
+ #
968
+ # There is a potential pitfall here: +has_and_belongs_to_many+ and +has_many+ <tt>:through</tt>
969
+ # associations have records in join tables, as well as the associated records. So when we
970
+ # call one of these deletion methods, what exactly should be deleted?
971
+ #
972
+ # The answer is that it is assumed that deletion on an association is about removing the
973
+ # <i>link</i> between the owner and the associated object(s), rather than necessarily the
974
+ # associated objects themselves. So with +has_and_belongs_to_many+ and +has_many+
975
+ # <tt>:through</tt>, the join records will be deleted, but the associated records won't.
976
+ #
977
+ # This makes sense if you think about it: if you were to call <tt>post.tags.delete(Tag.find_by_name('food'))</tt>
978
+ # you would want the 'food' tag to be unlinked from the post, rather than for the tag itself
979
+ # to be removed from the database.
980
+ #
981
+ # However, there are examples where this strategy doesn't make sense. For example, suppose
982
+ # a person has many projects, and each project has many tasks. If we deleted one of a person's
983
+ # tasks, we would probably not want the project to be deleted. In this scenario, the delete method
984
+ # won't actually work: it can only be used if the association on the join model is a
985
+ # +belongs_to+. In other situations you are expected to perform operations directly on
986
+ # either the associated records or the <tt>:through</tt> association.
987
+ #
988
+ # With a regular +has_many+ there is no distinction between the "associated records"
989
+ # and the "link", so there is only one choice for what gets deleted.
990
+ #
991
+ # With +has_and_belongs_to_many+ and +has_many+ <tt>:through</tt>, if you want to delete the
992
+ # associated records themselves, you can always do something along the lines of
993
+ # <tt>person.tasks.each(&:destroy)</tt>.
994
+ #
673
995
  # == Type safety with <tt>ActiveRecord::AssociationTypeMismatch</tt>
674
996
  #
675
- # If you attempt to assign an object to an association that doesn't match the inferred or specified <tt>:class_name</tt>, you'll
676
- # get an <tt>ActiveRecord::AssociationTypeMismatch</tt>.
997
+ # If you attempt to assign an object to an association that doesn't match the inferred
998
+ # or specified <tt>:class_name</tt>, you'll get an <tt>ActiveRecord::AssociationTypeMismatch</tt>.
677
999
  #
678
1000
  # == Options
679
1001
  #
680
- # All of the association macros can be specialized through options. This makes cases more complex than the simple and guessable ones
681
- # possible.
1002
+ # All of the association macros can be specialized through options. This makes cases
1003
+ # more complex than the simple and guessable ones possible.
682
1004
  module ClassMethods
683
1005
  # Specifies a one-to-many association. The following methods for retrieval and query of
684
1006
  # collections of associated objects will be added:
@@ -688,20 +1010,31 @@ module ActiveRecord
688
1010
  # An empty array is returned if none are found.
689
1011
  # [collection<<(object, ...)]
690
1012
  # Adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
1013
+ # Note that this operation instantly fires update sql without waiting for the save or update call on the
1014
+ # parent object.
691
1015
  # [collection.delete(object, ...)]
692
1016
  # Removes one or more objects from the collection by setting their foreign keys to +NULL+.
693
1017
  # Objects will be in addition destroyed if they're associated with <tt>:dependent => :destroy</tt>,
694
1018
  # and deleted if they're associated with <tt>:dependent => :delete_all</tt>.
1019
+ #
1020
+ # If the <tt>:through</tt> option is used, then the join records are deleted (rather than
1021
+ # nullified) by default, but you can specify <tt>:dependent => :destroy</tt> or
1022
+ # <tt>:dependent => :nullify</tt> to override this.
695
1023
  # [collection=objects]
696
- # Replaces the collections content by deleting and adding objects as appropriate.
1024
+ # Replaces the collections content by deleting and adding objects as appropriate. If the <tt>:through</tt>
1025
+ # option is true callbacks in the join models are triggered except destroy callbacks, since deletion is
1026
+ # direct.
697
1027
  # [collection_singular_ids]
698
1028
  # Returns an array of the associated objects' ids
699
1029
  # [collection_singular_ids=ids]
700
- # Replace the collection with the objects identified by the primary keys in +ids+
1030
+ # Replace the collection with the objects identified by the primary keys in +ids+. This
1031
+ # method loads the models and calls <tt>collection=</tt>. See above.
701
1032
  # [collection.clear]
702
1033
  # Removes every object from the collection. This destroys the associated objects if they
703
1034
  # are associated with <tt>:dependent => :destroy</tt>, deletes them directly from the
704
1035
  # database if <tt>:dependent => :delete_all</tt>, otherwise sets their foreign keys to +NULL+.
1036
+ # If the <tt>:through</tt> option is true no destroy callbacks are invoked on the join models.
1037
+ # Join models are directly deleted.
705
1038
  # [collection.empty?]
706
1039
  # Returns +true+ if there are no associated objects.
707
1040
  # [collection.size]
@@ -714,13 +1047,12 @@ module ActiveRecord
714
1047
  # [collection.build(attributes = {}, ...)]
715
1048
  # Returns one or more new objects of the collection type that have been instantiated
716
1049
  # with +attributes+ and linked to this object through a foreign key, but have not yet
717
- # been saved. <b>Note:</b> This only works if an associated object already exists, not if
718
- # it's +nil+!
1050
+ # been saved.
719
1051
  # [collection.create(attributes = {})]
720
1052
  # Returns a new object of the collection type that has been instantiated
721
1053
  # with +attributes+, linked to this object through a foreign key, and that has already
722
- # been saved (if it passed the validation). <b>Note:</b> This only works if an associated
723
- # object already exists, not if it's +nil+!
1054
+ # been saved (if it passed the validation). *Note*: This only works if the base model
1055
+ # already exists in the DB, not if it is a new (unsaved) record!
724
1056
  #
725
1057
  # (*Note*: +collection+ is replaced with the symbol passed as the first argument, so
726
1058
  # <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.)
@@ -728,7 +1060,7 @@ module ActiveRecord
728
1060
  # === Example
729
1061
  #
730
1062
  # Example: A Firm class declares <tt>has_many :clients</tt>, which will add:
731
- # * <tt>Firm#clients</tt> (similar to <tt>Clients.find :all, :conditions => ["firm_id = ?", id]</tt>)
1063
+ # * <tt>Firm#clients</tt> (similar to <tt>Clients.all :conditions => ["firm_id = ?", id]</tt>)
732
1064
  # * <tt>Firm#clients<<</tt>
733
1065
  # * <tt>Firm#clients.delete</tt>
734
1066
  # * <tt>Firm#clients=</tt>
@@ -743,37 +1075,48 @@ module ActiveRecord
743
1075
  # * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)
744
1076
  # The declaration can also include an options hash to specialize the behavior of the association.
745
1077
  #
746
- # === Supported options
1078
+ # === Options
747
1079
  # [:class_name]
748
1080
  # Specify the class name of the association. Use it only if that name can't be inferred
749
- # from the association name. So <tt>has_many :products</tt> will by default be linked to the Product class, but
750
- # if the real class name is SpecialProduct, you'll have to specify it with this option.
1081
+ # from the association name. So <tt>has_many :products</tt> will by default be linked
1082
+ # to the Product class, but if the real class name is SpecialProduct, you'll have to
1083
+ # specify it with this option.
751
1084
  # [:conditions]
752
1085
  # Specify the conditions that the associated objects must meet in order to be included as a +WHERE+
753
- # SQL fragment, such as <tt>price > 5 AND name LIKE 'B%'</tt>. Record creations from the association are scoped if a hash
754
- # is used. <tt>has_many :posts, :conditions => {:published => true}</tt> will create published posts with <tt>@blog.posts.create</tt>
755
- # or <tt>@blog.posts.build</tt>.
1086
+ # SQL fragment, such as <tt>price > 5 AND name LIKE 'B%'</tt>. Record creations from
1087
+ # the association are scoped if a hash is used.
1088
+ # <tt>has_many :posts, :conditions => {:published => true}</tt> will create published
1089
+ # posts with <tt>@blog.posts.create</tt> or <tt>@blog.posts.build</tt>.
756
1090
  # [:order]
757
1091
  # Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
758
1092
  # such as <tt>last_name, first_name DESC</tt>.
759
1093
  # [:foreign_key]
760
1094
  # Specify the foreign key used for the association. By default this is guessed to be the name
761
- # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_many+ association will use "person_id"
762
- # as the default <tt>:foreign_key</tt>.
1095
+ # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_many+
1096
+ # association will use "person_id" as the default <tt>:foreign_key</tt>.
763
1097
  # [:primary_key]
764
1098
  # Specify the method that returns the primary key used for the association. By default this is +id+.
765
1099
  # [:dependent]
766
1100
  # If set to <tt>:destroy</tt> all the associated objects are destroyed
767
- # alongside this object by calling their +destroy+ method. If set to <tt>:delete_all</tt> all associated
768
- # objects are deleted *without* calling their +destroy+ method. If set to <tt>:nullify</tt> all associated
769
- # objects' foreign keys are set to +NULL+ *without* calling their +save+ callbacks. *Warning:* This option is ignored when also using
770
- # the <tt>:through</tt> option.
1101
+ # alongside this object by calling their +destroy+ method. If set to <tt>:delete_all</tt> all associated
1102
+ # objects are deleted *without* calling their +destroy+ method. If set to <tt>:nullify</tt> all associated
1103
+ # objects' foreign keys are set to +NULL+ *without* calling their +save+ callbacks. If set to
1104
+ # <tt>:restrict</tt> this object raises an <tt>ActiveRecord::DeleteRestrictionError</tt> exception and
1105
+ # cannot be deleted if it has any associated objects.
1106
+ #
1107
+ # If using with the <tt>:through</tt> option, the association on the join model must be
1108
+ # a +belongs_to+, and the records which get deleted are the join records, rather than
1109
+ # the associated records.
1110
+ #
771
1111
  # [:finder_sql]
772
1112
  # Specify a complete SQL statement to fetch the association. This is a good way to go for complex
773
- # associations that depend on multiple tables. Note: When this option is used, +find_in_collection+ is _not_ added.
1113
+ # associations that depend on multiple tables. May be supplied as a string or a proc where interpolation is
1114
+ # required. Note: When this option is used, +find_in_collection+
1115
+ # is _not_ added.
774
1116
  # [:counter_sql]
775
1117
  # Specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
776
- # specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
1118
+ # specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by
1119
+ # replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
777
1120
  # [:extend]
778
1121
  # Specify a named module for extending the proxy. See "Association extensions".
779
1122
  # [:include]
@@ -781,23 +1124,39 @@ module ActiveRecord
781
1124
  # [:group]
782
1125
  # An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
783
1126
  # [:having]
784
- # Combined with +:group+ this can be used to filter the records that a <tt>GROUP BY</tt> returns. Uses the <tt>HAVING</tt> SQL-clause.
1127
+ # Combined with +:group+ this can be used to filter the records that a <tt>GROUP BY</tt>
1128
+ # returns. Uses the <tt>HAVING</tt> SQL-clause.
785
1129
  # [:limit]
786
1130
  # An integer determining the limit on the number of rows that should be returned.
787
1131
  # [:offset]
788
- # An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
1132
+ # An integer determining the offset from where the rows should be fetched. So at 5,
1133
+ # it would skip the first 4 rows.
789
1134
  # [:select]
790
- # By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if you, for example, want to do a join
791
- # but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
1135
+ # By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if
1136
+ # you, for example, want to do a join but not include the joined columns. Do not forget
1137
+ # to include the primary and foreign keys, otherwise it will raise an error.
792
1138
  # [:as]
793
1139
  # Specifies a polymorphic interface (See <tt>belongs_to</tt>).
794
1140
  # [:through]
795
- # Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
796
- # are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a <tt>belongs_to</tt>
797
- # <tt>has_one</tt> or <tt>has_many</tt> association on the join model.
1141
+ # Specifies an association through which to perform the query. This can be any other type
1142
+ # of association, including other <tt>:through</tt> associations. Options for <tt>:class_name</tt>,
1143
+ # <tt>:primary_key</tt> and <tt>:foreign_key</tt> are ignored, as the association uses the
1144
+ # source reflection.
1145
+ #
1146
+ # If the association on the join model is a +belongs_to+, the collection can be modified
1147
+ # and the records on the <tt>:through</tt> model will be automatically created and removed
1148
+ # as appropriate. Otherwise, the collection is read-only, so you should manipulate the
1149
+ # <tt>:through</tt> association directly.
1150
+ #
1151
+ # If you are going to modify the association (rather than just read from it), then it is
1152
+ # a good idea to set the <tt>:inverse_of</tt> option on the source association on the
1153
+ # join model. This allows associated records to be built which will automatically create
1154
+ # the appropriate join model records when they are saved. (See the 'Association Join Models'
1155
+ # section above.)
798
1156
  # [:source]
799
- # Specifies the source association name used by <tt>has_many :through</tt> queries. Only use it if the name cannot be
800
- # inferred from the association. <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either <tt>:subscribers</tt> or
1157
+ # Specifies the source association name used by <tt>has_many :through</tt> queries.
1158
+ # Only use it if the name cannot be inferred from the association.
1159
+ # <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either <tt>:subscribers</tt> or
801
1160
  # <tt>:subscriber</tt> on Subscription, unless a <tt>:source</tt> is given.
802
1161
  # [:source_type]
803
1162
  # Specifies type of the source association used by <tt>has_many :through</tt> queries where the source
@@ -807,9 +1166,16 @@ module ActiveRecord
807
1166
  # [:readonly]
808
1167
  # If true, all the associated objects are readonly through the association.
809
1168
  # [:validate]
810
- # If false, don't validate the associated objects when saving the parent object. true by default.
1169
+ # If +false+, don't validate the associated objects when saving the parent object. true by default.
811
1170
  # [:autosave]
812
- # If true, always save any loaded members and destroy members marked for destruction, when saving the parent object. Off by default.
1171
+ # If true, always save the associated objects or destroy them if marked for destruction,
1172
+ # when saving the parent object. If false, never save or destroy the associated objects.
1173
+ # By default, only save associated objects that are new records.
1174
+ # [:inverse_of]
1175
+ # Specifies the name of the <tt>belongs_to</tt> association on the associated object
1176
+ # that is the inverse of this <tt>has_many</tt> association. Does not work in combination
1177
+ # with <tt>:through</tt> or <tt>:as</tt> options.
1178
+ # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
813
1179
  #
814
1180
  # Option examples:
815
1181
  # has_many :comments, :order => "posted_on"
@@ -820,21 +1186,16 @@ module ActiveRecord
820
1186
  # has_many :tags, :as => :taggable
821
1187
  # has_many :reports, :readonly => true
822
1188
  # has_many :subscribers, :through => :subscriptions, :source => :user
823
- # has_many :subscribers, :class_name => "Person", :finder_sql =>
824
- # 'SELECT DISTINCT people.* ' +
825
- # 'FROM people p, post_subscriptions ps ' +
826
- # 'WHERE ps.post_id = #{id} AND ps.person_id = p.id ' +
827
- # 'ORDER BY p.first_name'
828
- def has_many(association_id, options = {}, &extension)
829
- reflection = create_has_many_reflection(association_id, options, &extension)
830
- configure_dependency_for_has_many(reflection)
831
- add_association_callbacks(reflection.name, reflection.options)
832
-
833
- if options[:through]
834
- collection_accessor_methods(reflection, HasManyThroughAssociation)
835
- else
836
- collection_accessor_methods(reflection, HasManyAssociation)
837
- end
1189
+ # has_many :subscribers, :class_name => "Person", :finder_sql => Proc.new {
1190
+ # %Q{
1191
+ # SELECT DISTINCT *
1192
+ # FROM people p, post_subscriptions ps
1193
+ # WHERE ps.post_id = #{id} AND ps.person_id = p.id
1194
+ # ORDER BY p.first_name
1195
+ # }
1196
+ # }
1197
+ def has_many(name, options = {}, &extension)
1198
+ Builder::HasMany.build(self, name, options, &extension)
838
1199
  end
839
1200
 
840
1201
  # Specifies a one-to-one association with another class. This method should only be used
@@ -852,12 +1213,14 @@ module ActiveRecord
852
1213
  # [build_association(attributes = {})]
853
1214
  # Returns a new object of the associated type that has been instantiated
854
1215
  # with +attributes+ and linked to this object through a foreign key, but has not
855
- # yet been saved. <b>Note:</b> This ONLY works if an association already exists.
856
- # It will NOT work if the association is +nil+.
1216
+ # yet been saved.
857
1217
  # [create_association(attributes = {})]
858
1218
  # Returns a new object of the associated type that has been instantiated
859
1219
  # with +attributes+, linked to this object through a foreign key, and that
860
1220
  # has already been saved (if it passed the validation).
1221
+ # [create_association!(attributes = {})]
1222
+ # Does the same as <tt>create_association</tt>, but raises <tt>ActiveRecord::RecordInvalid</tt>
1223
+ # if the record is invalid.
861
1224
  #
862
1225
  # (+association+ is replaced with the symbol passed as the first argument, so
863
1226
  # <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>.)
@@ -865,10 +1228,11 @@ module ActiveRecord
865
1228
  # === Example
866
1229
  #
867
1230
  # An Account class declares <tt>has_one :beneficiary</tt>, which will add:
868
- # * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.find(:first, :conditions => "account_id = #{id}")</tt>)
1231
+ # * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.first(:conditions => "account_id = #{id}")</tt>)
869
1232
  # * <tt>Account#beneficiary=(beneficiary)</tt> (similar to <tt>beneficiary.account_id = account.id; beneficiary.save</tt>)
870
1233
  # * <tt>Account#build_beneficiary</tt> (similar to <tt>Beneficiary.new("account_id" => id)</tt>)
871
1234
  # * <tt>Account#create_beneficiary</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save; b</tt>)
1235
+ # * <tt>Account#create_beneficiary!</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save!; b</tt>)
872
1236
  #
873
1237
  # === Options
874
1238
  #
@@ -882,19 +1246,21 @@ module ActiveRecord
882
1246
  # [:conditions]
883
1247
  # Specify the conditions that the associated object must meet in order to be included as a +WHERE+
884
1248
  # SQL fragment, such as <tt>rank = 5</tt>. Record creation from the association is scoped if a hash
885
- # is used. <tt>has_one :account, :conditions => {:enabled => true}</tt> will create an enabled account with <tt>@company.create_account</tt>
886
- # or <tt>@company.build_account</tt>.
1249
+ # is used. <tt>has_one :account, :conditions => {:enabled => true}</tt> will create
1250
+ # an enabled account with <tt>@company.create_account</tt> or <tt>@company.build_account</tt>.
887
1251
  # [:order]
888
1252
  # Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
889
1253
  # such as <tt>last_name, first_name DESC</tt>.
890
1254
  # [:dependent]
891
1255
  # If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
892
- # <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method. If set to <tt>:nullify</tt>, the associated
893
- # object's foreign key is set to +NULL+. Also, association is assigned.
1256
+ # <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method.
1257
+ # If set to <tt>:nullify</tt>, the associated object's foreign key is set to +NULL+.
1258
+ # Also, association is assigned. If set to <tt>:restrict</tt> this object raises an
1259
+ # <tt>ActiveRecord::DeleteRestrictionError</tt> exception and cannot be deleted if it has any associated object.
894
1260
  # [:foreign_key]
895
1261
  # Specify the foreign key used for the association. By default this is guessed to be the name
896
- # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_one+ association will use "person_id"
897
- # as the default <tt>:foreign_key</tt>.
1262
+ # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_one+ association
1263
+ # will use "person_id" as the default <tt>:foreign_key</tt>.
898
1264
  # [:primary_key]
899
1265
  # Specify the method that returns the primary key used for the association. By default this is +id+.
900
1266
  # [:include]
@@ -902,15 +1268,18 @@ module ActiveRecord
902
1268
  # [:as]
903
1269
  # Specifies a polymorphic interface (See <tt>belongs_to</tt>).
904
1270
  # [:select]
905
- # By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example, you want to do a join
906
- # but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
1271
+ # By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example,
1272
+ # you want to do a join but not include the joined columns. Do not forget to include the
1273
+ # primary and foreign keys, otherwise it will raise an error.
907
1274
  # [:through]
908
- # Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
909
- # are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a
910
- # <tt>has_one</tt> or <tt>belongs_to</tt> association on the join model.
1275
+ # Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt>,
1276
+ # <tt>:primary_key</tt>, and <tt>:foreign_key</tt> are ignored, as the association uses the
1277
+ # source reflection. You can only use a <tt>:through</tt> query through a <tt>has_one</tt>
1278
+ # or <tt>belongs_to</tt> association on the join model.
911
1279
  # [:source]
912
- # Specifies the source association name used by <tt>has_one :through</tt> queries. Only use it if the name cannot be
913
- # inferred from the association. <tt>has_one :favorite, :through => :favorites</tt> will look for a
1280
+ # Specifies the source association name used by <tt>has_one :through</tt> queries.
1281
+ # Only use it if the name cannot be inferred from the association.
1282
+ # <tt>has_one :favorite, :through => :favorites</tt> will look for a
914
1283
  # <tt>:favorite</tt> on Favorite, unless a <tt>:source</tt> is given.
915
1284
  # [:source_type]
916
1285
  # Specifies type of the source association used by <tt>has_one :through</tt> queries where the source
@@ -918,30 +1287,29 @@ module ActiveRecord
918
1287
  # [:readonly]
919
1288
  # If true, the associated object is readonly through the association.
920
1289
  # [:validate]
921
- # If false, don't validate the associated object when saving the parent object. +false+ by default.
1290
+ # If +false+, don't validate the associated object when saving the parent object. +false+ by default.
922
1291
  # [:autosave]
923
- # If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. Off by default.
1292
+ # If true, always save the associated object or destroy it if marked for destruction,
1293
+ # when saving the parent object. If false, never save or destroy the associated object.
1294
+ # By default, only save the associated object if it's a new record.
1295
+ # [:inverse_of]
1296
+ # Specifies the name of the <tt>belongs_to</tt> association on the associated object
1297
+ # that is the inverse of this <tt>has_one</tt> association. Does not work in combination
1298
+ # with <tt>:through</tt> or <tt>:as</tt> options.
1299
+ # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
924
1300
  #
925
1301
  # Option examples:
926
1302
  # has_one :credit_card, :dependent => :destroy # destroys the associated credit card
927
- # has_one :credit_card, :dependent => :nullify # updates the associated records foreign key value to NULL rather than destroying it
1303
+ # has_one :credit_card, :dependent => :nullify # updates the associated records foreign
1304
+ # # key value to NULL rather than destroying it
928
1305
  # has_one :last_comment, :class_name => "Comment", :order => "posted_on"
929
1306
  # has_one :project_manager, :class_name => "Person", :conditions => "role = 'project_manager'"
930
1307
  # has_one :attachment, :as => :attachable
931
1308
  # has_one :boss, :readonly => :true
932
1309
  # has_one :club, :through => :membership
933
1310
  # has_one :primary_address, :through => :addressables, :conditions => ["addressable.primary = ?", true], :source => :addressable
934
- def has_one(association_id, options = {})
935
- if options[:through]
936
- reflection = create_has_one_through_reflection(association_id, options)
937
- association_accessor_methods(reflection, ActiveRecord::Associations::HasOneThroughAssociation)
938
- else
939
- reflection = create_has_one_reflection(association_id, options)
940
- association_accessor_methods(reflection, HasOneAssociation)
941
- association_constructor_method(:build, reflection, HasOneAssociation)
942
- association_constructor_method(:create, reflection, HasOneAssociation)
943
- configure_dependency_for_has_one(reflection)
944
- end
1311
+ def has_one(name, options = {})
1312
+ Builder::HasOne.build(self, name, options)
945
1313
  end
946
1314
 
947
1315
  # Specifies a one-to-one association with another class. This method should only be used
@@ -963,6 +1331,9 @@ module ActiveRecord
963
1331
  # Returns a new object of the associated type that has been instantiated
964
1332
  # with +attributes+, linked to this object through a foreign key, and that
965
1333
  # has already been saved (if it passed the validation).
1334
+ # [create_association!(attributes = {})]
1335
+ # Does the same as <tt>create_association</tt>, but raises <tt>ActiveRecord::RecordInvalid</tt>
1336
+ # if the record is invalid.
966
1337
  #
967
1338
  # (+association+ is replaced with the symbol passed as the first argument, so
968
1339
  # <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.)
@@ -972,42 +1343,54 @@ module ActiveRecord
972
1343
  # A Post class declares <tt>belongs_to :author</tt>, which will add:
973
1344
  # * <tt>Post#author</tt> (similar to <tt>Author.find(author_id)</tt>)
974
1345
  # * <tt>Post#author=(author)</tt> (similar to <tt>post.author_id = author.id</tt>)
975
- # * <tt>Post#author?</tt> (similar to <tt>post.author == some_author</tt>)
976
1346
  # * <tt>Post#build_author</tt> (similar to <tt>post.author = Author.new</tt>)
977
1347
  # * <tt>Post#create_author</tt> (similar to <tt>post.author = Author.new; post.author.save; post.author</tt>)
1348
+ # * <tt>Post#create_author!</tt> (similar to <tt>post.author = Author.new; post.author.save!; post.author</tt>)
978
1349
  # The declaration can also include an options hash to specialize the behavior of the association.
979
1350
  #
980
1351
  # === Options
981
1352
  #
982
1353
  # [:class_name]
983
1354
  # Specify the class name of the association. Use it only if that name can't be inferred
984
- # from the association name. So <tt>has_one :author</tt> will by default be linked to the Author class, but
1355
+ # from the association name. So <tt>belongs_to :author</tt> will by default be linked to the Author class, but
985
1356
  # if the real class name is Person, you'll have to specify it with this option.
986
1357
  # [:conditions]
987
1358
  # Specify the conditions that the associated object must meet in order to be included as a +WHERE+
988
1359
  # SQL fragment, such as <tt>authorized = 1</tt>.
989
1360
  # [:select]
990
- # By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example, you want to do a join
991
- # but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
1361
+ # By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed
1362
+ # if, for example, you want to do a join but not include the joined columns. Do not
1363
+ # forget to include the primary and foreign keys, otherwise it will raise an error.
992
1364
  # [:foreign_key]
993
1365
  # Specify the foreign key used for the association. By default this is guessed to be the name
994
- # of the association with an "_id" suffix. So a class that defines a <tt>belongs_to :person</tt> association will use
995
- # "person_id" as the default <tt>:foreign_key</tt>. Similarly, <tt>belongs_to :favorite_person, :class_name => "Person"</tt>
996
- # will use a foreign key of "favorite_person_id".
1366
+ # of the association with an "_id" suffix. So a class that defines a <tt>belongs_to :person</tt>
1367
+ # association will use "person_id" as the default <tt>:foreign_key</tt>. Similarly,
1368
+ # <tt>belongs_to :favorite_person, :class_name => "Person"</tt> will use a foreign key
1369
+ # of "favorite_person_id".
1370
+ # [:foreign_type]
1371
+ # Specify the column used to store the associated object's type, if this is a polymorphic
1372
+ # association. By default this is guessed to be the name of the association with a "_type"
1373
+ # suffix. So a class that defines a <tt>belongs_to :taggable, :polymorphic => true</tt>
1374
+ # association will use "taggable_type" as the default <tt>:foreign_type</tt>.
997
1375
  # [:primary_key]
998
- # Specify the method that returns the primary key of associated object used for the association. By default this is id.
1376
+ # Specify the method that returns the primary key of associated object used for the association.
1377
+ # By default this is id.
999
1378
  # [:dependent]
1000
1379
  # If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
1001
- # <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method. This option should not be specified when
1002
- # <tt>belongs_to</tt> is used in conjunction with a <tt>has_many</tt> relationship on another class because of the potential to leave
1380
+ # <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method.
1381
+ # This option should not be specified when <tt>belongs_to</tt> is used in conjunction with
1382
+ # a <tt>has_many</tt> relationship on another class because of the potential to leave
1003
1383
  # orphaned records behind.
1004
1384
  # [:counter_cache]
1005
1385
  # Caches the number of belonging objects on the associate class through the use of +increment_counter+
1006
- # and +decrement_counter+. The counter cache is incremented when an object of this class is created and decremented when it's
1007
- # destroyed. This requires that a column named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging Comment class)
1008
- # is used on the associate class (such as a Post class). You can also specify a custom counter cache column by providing
1009
- # a column name instead of a +true+/+false+ value to this option (e.g., <tt>:counter_cache => :my_custom_counter</tt>.)
1010
- # Note: Specifying a counter cache will add it to that model's list of readonly attributes using +attr_readonly+.
1386
+ # and +decrement_counter+. The counter cache is incremented when an object of this
1387
+ # class is created and decremented when it's destroyed. This requires that a column
1388
+ # named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging Comment class)
1389
+ # is used on the associate class (such as a Post class). You can also specify a custom counter
1390
+ # cache column by providing a column name instead of a +true+/+false+ value to this
1391
+ # option (e.g., <tt>:counter_cache => :my_custom_counter</tt>.)
1392
+ # Note: Specifying a counter cache will add it to that model's list of readonly attributes
1393
+ # using +attr_readonly+.
1011
1394
  # [:include]
1012
1395
  # Specify second-order associations that should be eager loaded when this object is loaded.
1013
1396
  # [:polymorphic]
@@ -1017,12 +1400,21 @@ module ActiveRecord
1017
1400
  # [:readonly]
1018
1401
  # If true, the associated object is readonly through the association.
1019
1402
  # [:validate]
1020
- # If false, don't validate the associated objects when saving the parent object. +false+ by default.
1403
+ # If +false+, don't validate the associated objects when saving the parent object. +false+ by default.
1021
1404
  # [:autosave]
1022
- # If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. Off by default.
1405
+ # If true, always save the associated object or destroy it if marked for destruction, when
1406
+ # saving the parent object.
1407
+ # If false, never save or destroy the associated object.
1408
+ # By default, only save the associated object if it's a new record.
1023
1409
  # [:touch]
1024
- # If true, the associated object will be touched (the updated_at/on attributes set to now) when this record is either saved or
1025
- # destroyed. If you specify a symbol, that attribute will be updated with the current time instead of the updated_at/on attribute.
1410
+ # If true, the associated object will be touched (the updated_at/on attributes set to now)
1411
+ # when this record is either saved or destroyed. If you specify a symbol, that attribute
1412
+ # will be updated with the current time in addition to the updated_at/on attribute.
1413
+ # [:inverse_of]
1414
+ # Specifies the name of the <tt>has_one</tt> or <tt>has_many</tt> association on the associated
1415
+ # object that is the inverse of this <tt>belongs_to</tt> association. Does not work in
1416
+ # combination with the <tt>:polymorphic</tt> options.
1417
+ # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
1026
1418
  #
1027
1419
  # Option examples:
1028
1420
  # belongs_to :firm, :foreign_key => "client_of"
@@ -1035,54 +1427,37 @@ module ActiveRecord
1035
1427
  # belongs_to :post, :counter_cache => true
1036
1428
  # belongs_to :company, :touch => true
1037
1429
  # belongs_to :company, :touch => :employees_last_updated_at
1038
- def belongs_to(association_id, options = {})
1039
- reflection = create_belongs_to_reflection(association_id, options)
1040
-
1041
- if reflection.options[:polymorphic]
1042
- association_accessor_methods(reflection, BelongsToPolymorphicAssociation)
1043
- else
1044
- association_accessor_methods(reflection, BelongsToAssociation)
1045
- association_constructor_method(:build, reflection, BelongsToAssociation)
1046
- association_constructor_method(:create, reflection, BelongsToAssociation)
1047
- end
1048
-
1049
- add_counter_cache_callbacks(reflection) if options[:counter_cache]
1050
- add_touch_callbacks(reflection, options[:touch]) if options[:touch]
1051
-
1052
- configure_dependency_for_belongs_to(reflection)
1430
+ def belongs_to(name, options = {})
1431
+ Builder::BelongsTo.build(self, name, options)
1053
1432
  end
1054
1433
 
1055
1434
  # Specifies a many-to-many relationship with another class. This associates two classes via an
1056
- # intermediate join table. Unless the join table is explicitly specified as an option, it is
1435
+ # intermediate join table. Unless the join table is explicitly specified as an option, it is
1057
1436
  # guessed using the lexical order of the class names. So a join between Developer and Project
1058
- # will give the default join table name of "developers_projects" because "D" outranks "P". Note that this precedence
1059
- # is calculated using the <tt><</tt> operator for String. This means that if the strings are of different lengths,
1060
- # and the strings are equal when compared up to the shortest length, then the longer string is considered of higher
1061
- # lexical precedence than the shorter one. For example, one would expect the tables "paper_boxes" and "papers"
1437
+ # will give the default join table name of "developers_projects" because "D" outranks "P".
1438
+ # Note that this precedence is calculated using the <tt><</tt> operator for String. This
1439
+ # means that if the strings are of different lengths, and the strings are equal when compared
1440
+ # up to the shortest length, then the longer string is considered of higher
1441
+ # lexical precedence than the shorter one. For example, one would expect the tables "paper_boxes" and "papers"
1062
1442
  # to generate a join table name of "papers_paper_boxes" because of the length of the name "paper_boxes",
1063
- # but it in fact generates a join table name of "paper_boxes_papers". Be aware of this caveat, and use the
1443
+ # but it in fact generates a join table name of "paper_boxes_papers". Be aware of this caveat, and use the
1064
1444
  # custom <tt>:join_table</tt> option if you need to.
1065
1445
  #
1066
1446
  # The join table should not have a primary key or a model associated with it. You must manually generate the
1067
1447
  # join table with a migration such as this:
1068
1448
  #
1069
1449
  # class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration
1070
- # def self.up
1450
+ # def change
1071
1451
  # create_table :developers_projects, :id => false do |t|
1072
1452
  # t.integer :developer_id
1073
1453
  # t.integer :project_id
1074
1454
  # end
1075
1455
  # end
1076
- #
1077
- # def self.down
1078
- # drop_table :developers_projects
1079
- # end
1080
1456
  # end
1081
1457
  #
1082
- # Deprecated: Any additional fields added to the join table will be placed as attributes when pulling records out through
1083
- # +has_and_belongs_to_many+ associations. Records returned from join tables with additional attributes will be marked as
1084
- # readonly (because we can't save changes to the additional attributes). It's strongly recommended that you upgrade any
1085
- # associations with attributes to a real join model (see introduction).
1458
+ # It's also a good idea to add indexes to each of those columns to speed up the joins process.
1459
+ # However, in MySQL it is advised to add a compound index for both of the columns as MySQL only
1460
+ # uses one index per table during the lookup.
1086
1461
  #
1087
1462
  # Adds the following methods for retrieval and query:
1088
1463
  #
@@ -1092,6 +1467,8 @@ module ActiveRecord
1092
1467
  # [collection<<(object, ...)]
1093
1468
  # Adds one or more objects to the collection by creating associations in the join table
1094
1469
  # (<tt>collection.push</tt> and <tt>collection.concat</tt> are aliases to this method).
1470
+ # Note that this operation instantly fires update sql without waiting for the save or update call on the
1471
+ # parent object.
1095
1472
  # [collection.delete(object, ...)]
1096
1473
  # Removes one or more objects from the collection by removing their associations from the join table.
1097
1474
  # This does not destroy the objects.
@@ -1119,7 +1496,8 @@ module ActiveRecord
1119
1496
  # with +attributes+ and linked to this object through the join table, but has not yet been saved.
1120
1497
  # [collection.create(attributes = {})]
1121
1498
  # Returns a new object of the collection type that has been instantiated
1122
- # with +attributes+, linked to this object through the join table, and that has already been saved (if it passed the validation).
1499
+ # with +attributes+, linked to this object through the join table, and that has already been
1500
+ # saved (if it passed the validation).
1123
1501
  #
1124
1502
  # (+collection+ is replaced with the symbol passed as the first argument, so
1125
1503
  # <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>.)
@@ -1137,9 +1515,9 @@ module ActiveRecord
1137
1515
  # * <tt>Developer#projects.empty?</tt>
1138
1516
  # * <tt>Developer#projects.size</tt>
1139
1517
  # * <tt>Developer#projects.find(id)</tt>
1140
- # * <tt>Developer#clients.exists?(...)</tt>
1141
- # * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("project_id" => id)</tt>)
1142
- # * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("project_id" => id); c.save; c</tt>)
1518
+ # * <tt>Developer#projects.exists?(...)</tt>
1519
+ # * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("developer_id" => id)</tt>)
1520
+ # * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("developer_id" => id); c.save; c</tt>)
1143
1521
  # The declaration may include an options hash to specialize the behavior of the association.
1144
1522
  #
1145
1523
  # === Options
@@ -1154,8 +1532,9 @@ module ActiveRecord
1154
1532
  # MUST be declared underneath any +has_and_belongs_to_many+ declaration in order to work.
1155
1533
  # [:foreign_key]
1156
1534
  # Specify the foreign key used for the association. By default this is guessed to be the name
1157
- # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_and_belongs_to_many+ association
1158
- # to Project will use "person_id" as the default <tt>:foreign_key</tt>.
1535
+ # of this class in lower-case and "_id" suffixed. So a Person class that makes
1536
+ # a +has_and_belongs_to_many+ association to Project will use "person_id" as the
1537
+ # default <tt>:foreign_key</tt>.
1159
1538
  # [:association_foreign_key]
1160
1539
  # Specify the foreign key used for the association on the receiving side of the association.
1161
1540
  # By default this is guessed to be the name of the associated class in lower-case and "_id" suffixed.
@@ -1163,8 +1542,9 @@ module ActiveRecord
1163
1542
  # the association will use "project_id" as the default <tt>:association_foreign_key</tt>.
1164
1543
  # [:conditions]
1165
1544
  # Specify the conditions that the associated object must meet in order to be included as a +WHERE+
1166
- # SQL fragment, such as <tt>authorized = 1</tt>. Record creations from the association are scoped if a hash is used.
1167
- # <tt>has_many :posts, :conditions => {:published => true}</tt> will create published posts with <tt>@blog.posts.create</tt>
1545
+ # SQL fragment, such as <tt>authorized = 1</tt>. Record creations from the association are
1546
+ # scoped if a hash is used.
1547
+ # <tt>has_many :posts, :conditions => {:published => true}</tt> will create published posts with <tt>@blog.posts.create</tt>
1168
1548
  # or <tt>@blog.posts.build</tt>.
1169
1549
  # [:order]
1170
1550
  # Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
@@ -1175,7 +1555,8 @@ module ActiveRecord
1175
1555
  # Overwrite the default generated SQL statement used to fetch the association with a manual statement
1176
1556
  # [:counter_sql]
1177
1557
  # Specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
1178
- # specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
1558
+ # specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by
1559
+ # replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
1179
1560
  # [:delete_sql]
1180
1561
  # Overwrite the default generated SQL statement used to remove links between the associated
1181
1562
  # classes with a manual statement.
@@ -1189,20 +1570,26 @@ module ActiveRecord
1189
1570
  # [:group]
1190
1571
  # An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
1191
1572
  # [:having]
1192
- # Combined with +:group+ this can be used to filter the records that a <tt>GROUP BY</tt> returns. Uses the <tt>HAVING</tt> SQL-clause.
1573
+ # Combined with +:group+ this can be used to filter the records that a <tt>GROUP BY</tt> returns.
1574
+ # Uses the <tt>HAVING</tt> SQL-clause.
1193
1575
  # [:limit]
1194
1576
  # An integer determining the limit on the number of rows that should be returned.
1195
1577
  # [:offset]
1196
- # An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
1578
+ # An integer determining the offset from where the rows should be fetched. So at 5,
1579
+ # it would skip the first 4 rows.
1197
1580
  # [:select]
1198
- # By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example, you want to do a join
1199
- # but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
1581
+ # By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example,
1582
+ # you want to do a join but not include the joined columns. Do not forget to include the primary
1583
+ # and foreign keys, otherwise it will raise an error.
1200
1584
  # [:readonly]
1201
1585
  # If true, all the associated objects are readonly through the association.
1202
1586
  # [:validate]
1203
- # If false, don't validate the associated objects when saving the parent object. +true+ by default.
1587
+ # If +false+, don't validate the associated objects when saving the parent object. +true+ by default.
1204
1588
  # [:autosave]
1205
- # If true, always save any loaded members and destroy members marked for destruction, when saving the parent object. Off by default.
1589
+ # If true, always save the associated objects or destroy them if marked for destruction, when
1590
+ # saving the parent object.
1591
+ # If false, never save or destroy the associated objects.
1592
+ # By default, only save associated objects that are new records.
1206
1593
  #
1207
1594
  # Option examples:
1208
1595
  # has_and_belongs_to_many :projects
@@ -1211,1041 +1598,10 @@ module ActiveRecord
1211
1598
  # has_and_belongs_to_many :categories, :join_table => "prods_cats"
1212
1599
  # has_and_belongs_to_many :categories, :readonly => true
1213
1600
  # has_and_belongs_to_many :active_projects, :join_table => 'developers_projects', :delete_sql =>
1214
- # 'DELETE FROM developers_projects WHERE active=1 AND developer_id = #{id} AND project_id = #{record.id}'
1215
- def has_and_belongs_to_many(association_id, options = {}, &extension)
1216
- reflection = create_has_and_belongs_to_many_reflection(association_id, options, &extension)
1217
- collection_accessor_methods(reflection, HasAndBelongsToManyAssociation)
1218
-
1219
- # Don't use a before_destroy callback since users' before_destroy
1220
- # callbacks will be executed after the association is wiped out.
1221
- old_method = "destroy_without_habtm_shim_for_#{reflection.name}"
1222
- class_eval <<-end_eval unless method_defined?(old_method)
1223
- alias_method :#{old_method}, :destroy_without_callbacks # alias_method :destroy_without_habtm_shim_for_posts, :destroy_without_callbacks
1224
- def destroy_without_callbacks # def destroy_without_callbacks
1225
- #{reflection.name}.clear # posts.clear
1226
- #{old_method} # destroy_without_habtm_shim_for_posts
1227
- end # end
1228
- end_eval
1229
-
1230
- add_association_callbacks(reflection.name, options)
1601
+ # "DELETE FROM developers_projects WHERE active=1 AND developer_id = #{id} AND project_id = #{record.id}"
1602
+ def has_and_belongs_to_many(name, options = {}, &extension)
1603
+ Builder::HasAndBelongsToMany.build(self, name, options, &extension)
1231
1604
  end
1232
-
1233
- private
1234
- # Generates a join table name from two provided table names.
1235
- # The names in the join table namesme end up in lexicographic order.
1236
- #
1237
- # join_table_name("members", "clubs") # => "clubs_members"
1238
- # join_table_name("members", "special_clubs") # => "members_special_clubs"
1239
- def join_table_name(first_table_name, second_table_name)
1240
- if first_table_name < second_table_name
1241
- join_table = "#{first_table_name}_#{second_table_name}"
1242
- else
1243
- join_table = "#{second_table_name}_#{first_table_name}"
1244
- end
1245
-
1246
- table_name_prefix + join_table + table_name_suffix
1247
- end
1248
-
1249
- def association_accessor_methods(reflection, association_proxy_class)
1250
- define_method(reflection.name) do |*params|
1251
- force_reload = params.first unless params.empty?
1252
- association = association_instance_get(reflection.name)
1253
-
1254
- if association.nil? || force_reload
1255
- association = association_proxy_class.new(self, reflection)
1256
- retval = force_reload ? reflection.klass.uncached { association.reload } : association.reload
1257
- if retval.nil? and association_proxy_class == BelongsToAssociation
1258
- association_instance_set(reflection.name, nil)
1259
- return nil
1260
- end
1261
- association_instance_set(reflection.name, association)
1262
- end
1263
-
1264
- association.target.nil? ? nil : association
1265
- end
1266
-
1267
- define_method("loaded_#{reflection.name}?") do
1268
- association = association_instance_get(reflection.name)
1269
- association && association.loaded?
1270
- end
1271
-
1272
- define_method("#{reflection.name}=") do |new_value|
1273
- association = association_instance_get(reflection.name)
1274
-
1275
- if association.nil? || association.target != new_value
1276
- association = association_proxy_class.new(self, reflection)
1277
- end
1278
-
1279
- if association_proxy_class == HasOneThroughAssociation
1280
- association.create_through_record(new_value)
1281
- if new_record?
1282
- association_instance_set(reflection.name, new_value.nil? ? nil : association)
1283
- else
1284
- self.send(reflection.name, new_value)
1285
- end
1286
- else
1287
- association.replace(new_value)
1288
- association_instance_set(reflection.name, new_value.nil? ? nil : association)
1289
- end
1290
- end
1291
-
1292
- define_method("set_#{reflection.name}_target") do |target|
1293
- return if target.nil? and association_proxy_class == BelongsToAssociation
1294
- association = association_proxy_class.new(self, reflection)
1295
- association.target = target
1296
- association_instance_set(reflection.name, association)
1297
- end
1298
- end
1299
-
1300
- def collection_reader_method(reflection, association_proxy_class)
1301
- define_method(reflection.name) do |*params|
1302
- force_reload = params.first unless params.empty?
1303
- association = association_instance_get(reflection.name)
1304
-
1305
- unless association
1306
- association = association_proxy_class.new(self, reflection)
1307
- association_instance_set(reflection.name, association)
1308
- end
1309
-
1310
- reflection.klass.uncached { association.reload } if force_reload
1311
-
1312
- association
1313
- end
1314
-
1315
- define_method("#{reflection.name.to_s.singularize}_ids") do
1316
- if send(reflection.name).loaded? || reflection.options[:finder_sql]
1317
- send(reflection.name).map(&:id)
1318
- else
1319
- send(reflection.name).all(:select => "#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").map(&:id)
1320
- end
1321
- end
1322
- end
1323
-
1324
- def collection_accessor_methods(reflection, association_proxy_class, writer = true)
1325
- collection_reader_method(reflection, association_proxy_class)
1326
-
1327
- if writer
1328
- define_method("#{reflection.name}=") do |new_value|
1329
- # Loads proxy class instance (defined in collection_reader_method) if not already loaded
1330
- association = send(reflection.name)
1331
- association.replace(new_value)
1332
- association
1333
- end
1334
-
1335
- define_method("#{reflection.name.to_s.singularize}_ids=") do |new_value|
1336
- ids = (new_value || []).reject { |nid| nid.blank? }.map(&:to_i)
1337
- send("#{reflection.name}=", reflection.klass.find(ids).index_by(&:id).values_at(*ids))
1338
- end
1339
- end
1340
- end
1341
-
1342
- def association_constructor_method(constructor, reflection, association_proxy_class)
1343
- define_method("#{constructor}_#{reflection.name}") do |*params|
1344
- attributees = params.first unless params.empty?
1345
- replace_existing = params[1].nil? ? true : params[1]
1346
- association = association_instance_get(reflection.name)
1347
-
1348
- unless association
1349
- association = association_proxy_class.new(self, reflection)
1350
- association_instance_set(reflection.name, association)
1351
- end
1352
-
1353
- if association_proxy_class == HasOneAssociation
1354
- association.send(constructor, attributees, replace_existing)
1355
- else
1356
- association.send(constructor, attributees)
1357
- end
1358
- end
1359
- end
1360
-
1361
- def add_counter_cache_callbacks(reflection)
1362
- cache_column = reflection.counter_cache_column
1363
-
1364
- method_name = "belongs_to_counter_cache_after_create_for_#{reflection.name}".to_sym
1365
- define_method(method_name) do
1366
- association = send(reflection.name)
1367
- association.class.increment_counter(cache_column, association.id) unless association.nil?
1368
- end
1369
- after_create(method_name)
1370
-
1371
- method_name = "belongs_to_counter_cache_before_destroy_for_#{reflection.name}".to_sym
1372
- define_method(method_name) do
1373
- association = send(reflection.name)
1374
- association.class.decrement_counter(cache_column, association.id) unless association.nil?
1375
- end
1376
- before_destroy(method_name)
1377
-
1378
- module_eval(
1379
- "#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)"
1380
- )
1381
- end
1382
-
1383
- def add_touch_callbacks(reflection, touch_attribute)
1384
- method_name = "belongs_to_touch_after_save_or_destroy_for_#{reflection.name}".to_sym
1385
- define_method(method_name) do
1386
- association = send(reflection.name)
1387
-
1388
- if touch_attribute == true
1389
- association.touch unless association.nil?
1390
- else
1391
- association.touch(touch_attribute) unless association.nil?
1392
- end
1393
- end
1394
- after_save(method_name)
1395
- after_destroy(method_name)
1396
- end
1397
-
1398
- def find_with_associations(options = {})
1399
- catch :invalid_query do
1400
- join_dependency = JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins])
1401
- rows = select_all_rows(options, join_dependency)
1402
- return join_dependency.instantiate(rows)
1403
- end
1404
- []
1405
- end
1406
-
1407
- # Creates before_destroy callback methods that nullify, delete or destroy
1408
- # has_many associated objects, according to the defined :dependent rule.
1409
- #
1410
- # See HasManyAssociation#delete_records. Dependent associations
1411
- # delete children, otherwise foreign key is set to NULL.
1412
- #
1413
- # The +extra_conditions+ parameter, which is not used within the main
1414
- # Active Record codebase, is meant to allow plugins to define extra
1415
- # finder conditions.
1416
- def configure_dependency_for_has_many(reflection, extra_conditions = nil)
1417
- if reflection.options.include?(:dependent)
1418
- case reflection.options[:dependent]
1419
- when :destroy
1420
- method_name = "has_many_dependent_destroy_for_#{reflection.name}".to_sym
1421
- define_method(method_name) do
1422
- send(reflection.name).each do |o|
1423
- # No point in executing the counter update since we're going to destroy the parent anyway
1424
- counter_method = ('belongs_to_counter_cache_before_destroy_for_' + self.class.name.downcase).to_sym
1425
- if(o.respond_to? counter_method) then
1426
- class << o
1427
- self
1428
- end.send(:define_method, counter_method, Proc.new {})
1429
- end
1430
- o.destroy
1431
- end
1432
- end
1433
- before_destroy method_name
1434
- when :delete_all
1435
- before_destroy do |record|
1436
- record.class.send(:delete_all_has_many_dependencies,
1437
- record,
1438
- reflection.name,
1439
- reflection.klass,
1440
- reflection.dependent_conditions(record, record.class, extra_conditions))
1441
- end
1442
- when :nullify
1443
- before_destroy do |record|
1444
- record.class.send(:nullify_has_many_dependencies,
1445
- record,
1446
- reflection.name,
1447
- reflection.klass,
1448
- reflection.primary_key_name,
1449
- reflection.dependent_conditions(record, record.class, extra_conditions))
1450
- end
1451
- else
1452
- raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, or :nullify (#{reflection.options[:dependent].inspect})"
1453
- end
1454
- end
1455
- end
1456
-
1457
- # Creates before_destroy callback methods that nullify, delete or destroy
1458
- # has_one associated objects, according to the defined :dependent rule.
1459
- def configure_dependency_for_has_one(reflection)
1460
- if reflection.options.include?(:dependent)
1461
- case reflection.options[:dependent]
1462
- when :destroy
1463
- method_name = "has_one_dependent_destroy_for_#{reflection.name}".to_sym
1464
- define_method(method_name) do
1465
- association = send(reflection.name)
1466
- association.destroy unless association.nil?
1467
- end
1468
- before_destroy method_name
1469
- when :delete
1470
- method_name = "has_one_dependent_delete_for_#{reflection.name}".to_sym
1471
- define_method(method_name) do
1472
- # Retrieve the associated object and delete it. The retrieval
1473
- # is necessary because there may be multiple associated objects
1474
- # with foreign keys pointing to this object, and we only want
1475
- # to delete the correct one, not all of them.
1476
- association = send(reflection.name)
1477
- association.delete unless association.nil?
1478
- end
1479
- before_destroy method_name
1480
- when :nullify
1481
- method_name = "has_one_dependent_nullify_for_#{reflection.name}".to_sym
1482
- define_method(method_name) do
1483
- association = send(reflection.name)
1484
- association.update_attribute(reflection.primary_key_name, nil) unless association.nil?
1485
- end
1486
- before_destroy method_name
1487
- else
1488
- raise ArgumentError, "The :dependent option expects either :destroy, :delete or :nullify (#{reflection.options[:dependent].inspect})"
1489
- end
1490
- end
1491
- end
1492
-
1493
- def configure_dependency_for_belongs_to(reflection)
1494
- if reflection.options.include?(:dependent)
1495
- case reflection.options[:dependent]
1496
- when :destroy
1497
- method_name = "belongs_to_dependent_destroy_for_#{reflection.name}".to_sym
1498
- define_method(method_name) do
1499
- association = send(reflection.name)
1500
- association.destroy unless association.nil?
1501
- end
1502
- after_destroy method_name
1503
- when :delete
1504
- method_name = "belongs_to_dependent_delete_for_#{reflection.name}".to_sym
1505
- define_method(method_name) do
1506
- association = send(reflection.name)
1507
- association.delete unless association.nil?
1508
- end
1509
- after_destroy method_name
1510
- else
1511
- raise ArgumentError, "The :dependent option expects either :destroy or :delete (#{reflection.options[:dependent].inspect})"
1512
- end
1513
- end
1514
- end
1515
-
1516
- def delete_all_has_many_dependencies(record, reflection_name, association_class, dependent_conditions)
1517
- association_class.delete_all(dependent_conditions)
1518
- end
1519
-
1520
- def nullify_has_many_dependencies(record, reflection_name, association_class, primary_key_name, dependent_conditions)
1521
- association_class.update_all("#{primary_key_name} = NULL", dependent_conditions)
1522
- end
1523
-
1524
- mattr_accessor :valid_keys_for_has_many_association
1525
- @@valid_keys_for_has_many_association = [
1526
- :class_name, :table_name, :foreign_key, :primary_key,
1527
- :dependent,
1528
- :select, :conditions, :include, :order, :group, :having, :limit, :offset,
1529
- :as, :through, :source, :source_type,
1530
- :uniq,
1531
- :finder_sql, :counter_sql,
1532
- :before_add, :after_add, :before_remove, :after_remove,
1533
- :extend, :readonly,
1534
- :validate, :inverse_of
1535
- ]
1536
-
1537
- def create_has_many_reflection(association_id, options, &extension)
1538
- options.assert_valid_keys(valid_keys_for_has_many_association)
1539
- options[:extend] = create_extension_modules(association_id, extension, options[:extend])
1540
-
1541
- create_reflection(:has_many, association_id, options, self)
1542
- end
1543
-
1544
- mattr_accessor :valid_keys_for_has_one_association
1545
- @@valid_keys_for_has_one_association = [
1546
- :class_name, :foreign_key, :remote, :select, :conditions, :order,
1547
- :include, :dependent, :counter_cache, :extend, :as, :readonly,
1548
- :validate, :primary_key, :inverse_of
1549
- ]
1550
-
1551
- def create_has_one_reflection(association_id, options)
1552
- options.assert_valid_keys(valid_keys_for_has_one_association)
1553
- create_reflection(:has_one, association_id, options, self)
1554
- end
1555
-
1556
- def create_has_one_through_reflection(association_id, options)
1557
- options.assert_valid_keys(
1558
- :class_name, :foreign_key, :remote, :select, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :through, :source, :source_type, :validate
1559
- )
1560
- create_reflection(:has_one, association_id, options, self)
1561
- end
1562
-
1563
- mattr_accessor :valid_keys_for_belongs_to_association
1564
- @@valid_keys_for_belongs_to_association = [
1565
- :class_name, :primary_key, :foreign_key, :foreign_type, :remote, :select, :conditions,
1566
- :include, :dependent, :counter_cache, :extend, :polymorphic, :readonly,
1567
- :validate, :touch, :inverse_of
1568
- ]
1569
-
1570
- def create_belongs_to_reflection(association_id, options)
1571
- options.assert_valid_keys(valid_keys_for_belongs_to_association)
1572
- reflection = create_reflection(:belongs_to, association_id, options, self)
1573
-
1574
- if options[:polymorphic]
1575
- reflection.options[:foreign_type] ||= reflection.class_name.underscore + "_type"
1576
- end
1577
-
1578
- reflection
1579
- end
1580
-
1581
- mattr_accessor :valid_keys_for_has_and_belongs_to_many_association
1582
- @@valid_keys_for_has_and_belongs_to_many_association = [
1583
- :class_name, :table_name, :join_table, :foreign_key, :association_foreign_key,
1584
- :select, :conditions, :include, :order, :group, :having, :limit, :offset,
1585
- :uniq,
1586
- :finder_sql, :counter_sql, :delete_sql, :insert_sql,
1587
- :before_add, :after_add, :before_remove, :after_remove,
1588
- :extend, :readonly,
1589
- :validate
1590
- ]
1591
-
1592
- def create_has_and_belongs_to_many_reflection(association_id, options, &extension)
1593
- options.assert_valid_keys(valid_keys_for_has_and_belongs_to_many_association)
1594
-
1595
- options[:extend] = create_extension_modules(association_id, extension, options[:extend])
1596
-
1597
- reflection = create_reflection(:has_and_belongs_to_many, association_id, options, self)
1598
-
1599
- if reflection.association_foreign_key == reflection.primary_key_name
1600
- raise HasAndBelongsToManyAssociationForeignKeyNeeded.new(reflection)
1601
- end
1602
-
1603
- reflection.options[:join_table] ||= join_table_name(undecorated_table_name(self.to_s), undecorated_table_name(reflection.class_name))
1604
-
1605
- reflection
1606
- end
1607
-
1608
- def reflect_on_included_associations(associations)
1609
- [ associations ].flatten.collect { |association| reflect_on_association(association.to_s.intern) }
1610
- end
1611
-
1612
- def guard_against_unlimitable_reflections(reflections, options)
1613
- if (options[:offset] || options[:limit]) && !using_limitable_reflections?(reflections)
1614
- raise(
1615
- ConfigurationError,
1616
- "You can not use offset and limit together with has_many or has_and_belongs_to_many associations"
1617
- )
1618
- end
1619
- end
1620
-
1621
- def select_all_rows(options, join_dependency)
1622
- connection.select_all(
1623
- construct_finder_sql_with_included_associations(options, join_dependency),
1624
- "#{name} Load Including Associations"
1625
- )
1626
- end
1627
-
1628
- def construct_finder_sql_with_included_associations(options, join_dependency)
1629
- scope = scope(:find)
1630
- sql = "SELECT #{column_aliases(join_dependency)} FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} "
1631
- sql << join_dependency.join_associations.collect{|join| join.association_join }.join
1632
-
1633
- add_joins!(sql, options[:joins], scope)
1634
- add_conditions!(sql, options[:conditions], scope)
1635
- add_limited_ids_condition!(sql, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit])
1636
-
1637
- add_group!(sql, options[:group], options[:having], scope)
1638
- add_order!(sql, options[:order], scope)
1639
- add_limit!(sql, options, scope) if using_limitable_reflections?(join_dependency.reflections)
1640
- add_lock!(sql, options, scope)
1641
-
1642
- return sanitize_sql(sql)
1643
- end
1644
-
1645
- def add_limited_ids_condition!(sql, options, join_dependency)
1646
- unless (id_list = select_limited_ids_list(options, join_dependency)).empty?
1647
- sql << "#{condition_word(sql)} #{connection.quote_table_name table_name}.#{primary_key} IN (#{id_list}) "
1648
- else
1649
- throw :invalid_query
1650
- end
1651
- end
1652
-
1653
- def select_limited_ids_list(options, join_dependency)
1654
- pk = columns_hash[primary_key]
1655
-
1656
- connection.select_all(
1657
- construct_finder_sql_for_association_limiting(options, join_dependency),
1658
- "#{name} Load IDs For Limited Eager Loading"
1659
- ).collect { |row| connection.quote(row[primary_key], pk) }.join(", ")
1660
- end
1661
-
1662
- def construct_finder_sql_for_association_limiting(options, join_dependency)
1663
- scope = scope(:find)
1664
-
1665
- # Only join tables referenced in order or conditions since this is particularly slow on the pre-query.
1666
- tables_from_conditions = conditions_tables(options)
1667
- tables_from_order = order_tables(options)
1668
- all_tables = tables_from_conditions + tables_from_order
1669
- distinct_join_associations = all_tables.uniq.map{|table|
1670
- join_dependency.joins_for_table_name(table)
1671
- }.flatten.compact.uniq
1672
-
1673
- order = options[:order]
1674
- if scoped_order = (scope && scope[:order])
1675
- order = order ? "#{order}, #{scoped_order}" : scoped_order
1676
- end
1677
-
1678
- is_distinct = !options[:joins].blank? || include_eager_conditions?(options, tables_from_conditions) || include_eager_order?(options, tables_from_order)
1679
- sql = "SELECT "
1680
- if is_distinct
1681
- sql << connection.distinct("#{connection.quote_table_name table_name}.#{primary_key}", order)
1682
- else
1683
- sql << primary_key
1684
- end
1685
- sql << " FROM #{connection.quote_table_name table_name} "
1686
-
1687
- if is_distinct
1688
- sql << distinct_join_associations.collect { |assoc| assoc.association_join }.join
1689
- add_joins!(sql, options[:joins], scope)
1690
- end
1691
-
1692
- add_conditions!(sql, options[:conditions], scope)
1693
- add_group!(sql, options[:group], options[:having], scope)
1694
-
1695
- if order && is_distinct
1696
- connection.add_order_by_for_association_limiting!(sql, :order => order)
1697
- else
1698
- add_order!(sql, options[:order], scope)
1699
- end
1700
-
1701
- add_limit!(sql, options, scope)
1702
-
1703
- return sanitize_sql(sql)
1704
- end
1705
-
1706
- def tables_in_string(string)
1707
- return [] if string.blank?
1708
- string.scan(/([\.a-zA-Z_]+).?\./).flatten
1709
- end
1710
-
1711
- def tables_in_hash(hash)
1712
- return [] if hash.blank?
1713
- tables = hash.map do |key, value|
1714
- if value.is_a?(Hash)
1715
- key.to_s
1716
- else
1717
- tables_in_string(key) if key.is_a?(String)
1718
- end
1719
- end
1720
- tables.flatten.compact
1721
- end
1722
-
1723
- def conditions_tables(options)
1724
- # look in both sets of conditions
1725
- conditions = [scope(:find, :conditions), options[:conditions]].inject([]) do |all, cond|
1726
- case cond
1727
- when nil then all
1728
- when Array then all << tables_in_string(cond.first)
1729
- when Hash then all << tables_in_hash(cond)
1730
- else all << tables_in_string(cond)
1731
- end
1732
- end
1733
- conditions.flatten
1734
- end
1735
-
1736
- def order_tables(options)
1737
- order = [options[:order], scope(:find, :order) ].join(", ")
1738
- return [] unless order && order.is_a?(String)
1739
- tables_in_string(order)
1740
- end
1741
-
1742
- def selects_tables(options)
1743
- select = options[:select]
1744
- return [] unless select && select.is_a?(String)
1745
- tables_in_string(select)
1746
- end
1747
-
1748
- def joined_tables(options)
1749
- scope = scope(:find)
1750
- joins = options[:joins]
1751
- merged_joins = scope && scope[:joins] && joins ? merge_joins(scope[:joins], joins) : (joins || scope && scope[:joins])
1752
- [table_name] + case merged_joins
1753
- when Symbol, Hash, Array
1754
- if array_of_strings?(merged_joins)
1755
- tables_in_string(merged_joins.join(' '))
1756
- else
1757
- join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, merged_joins, nil)
1758
- join_dependency.join_associations.collect {|join_association| [join_association.aliased_join_table_name, join_association.aliased_table_name]}.flatten.compact
1759
- end
1760
- else
1761
- tables_in_string(merged_joins)
1762
- end
1763
- end
1764
-
1765
- # Checks if the conditions reference a table other than the current model table
1766
- def include_eager_conditions?(options, tables = nil, joined_tables = nil)
1767
- ((tables || conditions_tables(options)) - (joined_tables || joined_tables(options))).any?
1768
- end
1769
-
1770
- # Checks if the query order references a table other than the current model's table.
1771
- def include_eager_order?(options, tables = nil, joined_tables = nil)
1772
- ((tables || order_tables(options)) - (joined_tables || joined_tables(options))).any?
1773
- end
1774
-
1775
- def include_eager_select?(options, joined_tables = nil)
1776
- (selects_tables(options) - (joined_tables || joined_tables(options))).any?
1777
- end
1778
-
1779
- def references_eager_loaded_tables?(options)
1780
- joined_tables = joined_tables(options)
1781
- include_eager_order?(options, nil, joined_tables) || include_eager_conditions?(options, nil, joined_tables) || include_eager_select?(options, joined_tables)
1782
- end
1783
-
1784
- def using_limitable_reflections?(reflections)
1785
- reflections.none?(&:collection?)
1786
- end
1787
-
1788
- def column_aliases(join_dependency)
1789
- join_dependency.joins.collect{|join| join.column_names_with_alias.collect{|column_name, aliased_name|
1790
- "#{connection.quote_table_name join.aliased_table_name}.#{connection.quote_column_name column_name} AS #{aliased_name}"}}.flatten.join(", ")
1791
- end
1792
-
1793
- def add_association_callbacks(association_name, options)
1794
- callbacks = %w(before_add after_add before_remove after_remove)
1795
- callbacks.each do |callback_name|
1796
- full_callback_name = "#{callback_name}_for_#{association_name}"
1797
- defined_callbacks = options[callback_name.to_sym]
1798
- if options.has_key?(callback_name.to_sym)
1799
- class_inheritable_reader full_callback_name.to_sym
1800
- write_inheritable_attribute(full_callback_name.to_sym, [defined_callbacks].flatten)
1801
- else
1802
- write_inheritable_attribute(full_callback_name.to_sym, [])
1803
- end
1804
- end
1805
- end
1806
-
1807
- def condition_word(sql)
1808
- sql =~ /where/i ? " AND " : "WHERE "
1809
- end
1810
-
1811
- def create_extension_modules(association_id, block_extension, extensions)
1812
- if block_extension
1813
- extension_module_name = "#{self.to_s.demodulize}#{association_id.to_s.camelize}AssociationExtension"
1814
-
1815
- silence_warnings do
1816
- self.parent.const_set(extension_module_name, Module.new(&block_extension))
1817
- end
1818
- Array(extensions).push("#{self.parent}::#{extension_module_name}".constantize)
1819
- else
1820
- Array(extensions)
1821
- end
1822
- end
1823
-
1824
- class JoinDependency # :nodoc:
1825
- attr_reader :joins, :reflections, :table_aliases
1826
-
1827
- def initialize(base, associations, joins)
1828
- @joins = [JoinBase.new(base, joins)]
1829
- @associations = associations
1830
- @reflections = []
1831
- @base_records_hash = {}
1832
- @base_records_in_order = []
1833
- @table_aliases = Hash.new { |aliases, table| aliases[table] = 0 }
1834
- @table_aliases[base.table_name] = 1
1835
- build(associations)
1836
- end
1837
-
1838
- def join_associations
1839
- @joins[1..-1].to_a
1840
- end
1841
-
1842
- def join_base
1843
- @joins[0]
1844
- end
1845
-
1846
- def instantiate(rows)
1847
- rows.each_with_index do |row, i|
1848
- primary_id = join_base.record_id(row)
1849
- unless @base_records_hash[primary_id]
1850
- @base_records_in_order << (@base_records_hash[primary_id] = join_base.instantiate(row))
1851
- end
1852
- construct(@base_records_hash[primary_id], @associations, join_associations.dup, row)
1853
- end
1854
- remove_duplicate_results!(join_base.active_record, @base_records_in_order, @associations)
1855
- return @base_records_in_order
1856
- end
1857
-
1858
- def remove_duplicate_results!(base, records, associations)
1859
- case associations
1860
- when Symbol, String
1861
- reflection = base.reflections[associations]
1862
- if reflection && reflection.collection?
1863
- records.each { |record| record.send(reflection.name).target.uniq! }
1864
- end
1865
- when Array
1866
- associations.each do |association|
1867
- remove_duplicate_results!(base, records, association)
1868
- end
1869
- when Hash
1870
- associations.keys.each do |name|
1871
- reflection = base.reflections[name]
1872
-
1873
- parent_records = records.map do |record|
1874
- descendant = record.send(reflection.name)
1875
- next unless descendant
1876
- descendant.target.uniq! if reflection.collection?
1877
- descendant
1878
- end.flatten.compact
1879
-
1880
- remove_duplicate_results!(reflection.klass, parent_records, associations[name]) unless parent_records.empty?
1881
- end
1882
- end
1883
- end
1884
-
1885
- def join_for_table_name(table_name)
1886
- join = (@joins.select{|j|j.aliased_table_name == table_name.gsub(/^\"(.*)\"$/){$1} }.first) rescue nil
1887
- return join unless join.nil?
1888
- @joins.select{|j|j.is_a?(JoinAssociation) && j.aliased_join_table_name == table_name.gsub(/^\"(.*)\"$/){$1} }.first rescue nil
1889
- end
1890
-
1891
- def joins_for_table_name(table_name)
1892
- join = join_for_table_name(table_name)
1893
- result = nil
1894
- if join && join.is_a?(JoinAssociation)
1895
- result = [join]
1896
- if join.parent && join.parent.is_a?(JoinAssociation)
1897
- result = joins_for_table_name(join.parent.aliased_table_name) +
1898
- result
1899
- end
1900
- end
1901
- result
1902
- end
1903
-
1904
- protected
1905
- def build(associations, parent = nil)
1906
- parent ||= @joins.last
1907
- case associations
1908
- when Symbol, String
1909
- reflection = parent.reflections[associations.to_s.intern] or
1910
- raise ConfigurationError, "Association named '#{ associations }' was not found; perhaps you misspelled it?"
1911
- @reflections << reflection
1912
- @joins << build_join_association(reflection, parent)
1913
- when Array
1914
- associations.each do |association|
1915
- build(association, parent)
1916
- end
1917
- when Hash
1918
- associations.keys.sort{|a,b|a.to_s<=>b.to_s}.each do |name|
1919
- build(name, parent)
1920
- build(associations[name])
1921
- end
1922
- else
1923
- raise ConfigurationError, associations.inspect
1924
- end
1925
- end
1926
-
1927
- # overridden in InnerJoinDependency subclass
1928
- def build_join_association(reflection, parent)
1929
- JoinAssociation.new(reflection, self, parent)
1930
- end
1931
-
1932
- def construct(parent, associations, joins, row)
1933
- case associations
1934
- when Symbol, String
1935
- join = joins.detect{|j| j.reflection.name.to_s == associations.to_s && j.parent_table_name == parent.class.table_name }
1936
- raise(ConfigurationError, "No such association") if join.nil?
1937
-
1938
- joins.delete(join)
1939
- construct_association(parent, join, row)
1940
- when Array
1941
- associations.each do |association|
1942
- construct(parent, association, joins, row)
1943
- end
1944
- when Hash
1945
- associations.keys.sort{|a,b|a.to_s<=>b.to_s}.each do |name|
1946
- join = joins.detect{|j| j.reflection.name.to_s == name.to_s && j.parent_table_name == parent.class.table_name }
1947
- raise(ConfigurationError, "No such association") if join.nil?
1948
-
1949
- association = construct_association(parent, join, row)
1950
- joins.delete(join)
1951
- construct(association, associations[name], joins, row) if association
1952
- end
1953
- else
1954
- raise ConfigurationError, associations.inspect
1955
- end
1956
- end
1957
-
1958
- def construct_association(record, join, row)
1959
- case join.reflection.macro
1960
- when :has_many, :has_and_belongs_to_many
1961
- collection = record.send(join.reflection.name)
1962
- collection.loaded
1963
-
1964
- return nil if record.id.to_s != join.parent.record_id(row).to_s or row[join.aliased_primary_key].nil?
1965
- association = join.instantiate(row)
1966
- collection.target.push(association)
1967
- collection.__send__(:set_inverse_instance, association, record)
1968
- when :has_one
1969
- return if record.id.to_s != join.parent.record_id(row).to_s
1970
- return if record.instance_variable_defined?("@#{join.reflection.name}")
1971
- association = join.instantiate(row) unless row[join.aliased_primary_key].nil?
1972
- set_target_and_inverse(join, association, record)
1973
- when :belongs_to
1974
- return if record.id.to_s != join.parent.record_id(row).to_s or row[join.aliased_primary_key].nil?
1975
- association = join.instantiate(row)
1976
- set_target_and_inverse(join, association, record)
1977
- else
1978
- raise ConfigurationError, "unknown macro: #{join.reflection.macro}"
1979
- end
1980
- return association
1981
- end
1982
-
1983
- def set_target_and_inverse(join, association, record)
1984
- association_proxy = record.send("set_#{join.reflection.name}_target", association)
1985
- association_proxy.__send__(:set_inverse_instance, association, record)
1986
- end
1987
-
1988
- class JoinBase # :nodoc:
1989
- attr_reader :active_record, :table_joins
1990
- delegate :table_name, :column_names, :primary_key, :reflections, :sanitize_sql, :to => :active_record
1991
-
1992
- def initialize(active_record, joins = nil)
1993
- @active_record = active_record
1994
- @cached_record = {}
1995
- @table_joins = joins
1996
- end
1997
-
1998
- def aliased_prefix
1999
- "t0"
2000
- end
2001
-
2002
- def aliased_primary_key
2003
- "#{aliased_prefix}_r0"
2004
- end
2005
-
2006
- def aliased_table_name
2007
- active_record.table_name
2008
- end
2009
-
2010
- def column_names_with_alias
2011
- unless defined?(@column_names_with_alias)
2012
- @column_names_with_alias = []
2013
-
2014
- ([primary_key] + (column_names - [primary_key])).each_with_index do |column_name, i|
2015
- @column_names_with_alias << [column_name, "#{aliased_prefix}_r#{i}"]
2016
- end
2017
- end
2018
-
2019
- @column_names_with_alias
2020
- end
2021
-
2022
- def extract_record(row)
2023
- column_names_with_alias.inject({}){|record, (cn, an)| record[cn] = row[an]; record}
2024
- end
2025
-
2026
- def record_id(row)
2027
- row[aliased_primary_key]
2028
- end
2029
-
2030
- def instantiate(row)
2031
- @cached_record[record_id(row)] ||= active_record.send(:instantiate, extract_record(row))
2032
- end
2033
- end
2034
-
2035
- class JoinAssociation < JoinBase # :nodoc:
2036
- attr_reader :reflection, :parent, :aliased_table_name, :aliased_prefix, :aliased_join_table_name, :parent_table_name
2037
- delegate :options, :klass, :through_reflection, :source_reflection, :to => :reflection
2038
-
2039
- def initialize(reflection, join_dependency, parent = nil)
2040
- reflection.check_validity!
2041
- if reflection.options[:polymorphic]
2042
- raise EagerLoadPolymorphicError.new(reflection)
2043
- end
2044
-
2045
- super(reflection.klass)
2046
- @join_dependency = join_dependency
2047
- @parent = parent
2048
- @reflection = reflection
2049
- @aliased_prefix = "t#{ join_dependency.joins.size }"
2050
- @parent_table_name = parent.active_record.table_name
2051
- @aliased_table_name = aliased_table_name_for(table_name)
2052
-
2053
- if reflection.macro == :has_and_belongs_to_many
2054
- @aliased_join_table_name = aliased_table_name_for(reflection.options[:join_table], "_join")
2055
- end
2056
-
2057
- if [:has_many, :has_one].include?(reflection.macro) && reflection.options[:through]
2058
- @aliased_join_table_name = aliased_table_name_for(reflection.through_reflection.klass.table_name, "_join")
2059
- end
2060
- end
2061
-
2062
- def association_join
2063
- connection = reflection.active_record.connection
2064
- join = case reflection.macro
2065
- when :has_and_belongs_to_many
2066
- " #{join_type} %s ON %s.%s = %s.%s " % [
2067
- table_alias_for(options[:join_table], aliased_join_table_name),
2068
- connection.quote_table_name(aliased_join_table_name),
2069
- options[:foreign_key] || reflection.active_record.to_s.foreign_key,
2070
- connection.quote_table_name(parent.aliased_table_name),
2071
- reflection.active_record.primary_key] +
2072
- " #{join_type} %s ON %s.%s = %s.%s " % [
2073
- table_name_and_alias,
2074
- connection.quote_table_name(aliased_table_name),
2075
- klass.primary_key,
2076
- connection.quote_table_name(aliased_join_table_name),
2077
- options[:association_foreign_key] || klass.to_s.foreign_key
2078
- ]
2079
- when :has_many, :has_one
2080
- case
2081
- when reflection.options[:through]
2082
- through_conditions = through_reflection.options[:conditions] ? "AND #{interpolate_sql(sanitize_sql(through_reflection.options[:conditions]))}" : ''
2083
-
2084
- jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil
2085
- first_key = second_key = as_extra = nil
2086
-
2087
- if through_reflection.options[:as] # has_many :through against a polymorphic join
2088
- jt_foreign_key = through_reflection.options[:as].to_s + '_id'
2089
- jt_as_extra = " AND %s.%s = %s" % [
2090
- connection.quote_table_name(aliased_join_table_name),
2091
- connection.quote_column_name(through_reflection.options[:as].to_s + '_type'),
2092
- klass.quote_value(parent.active_record.base_class.name)
2093
- ]
2094
- else
2095
- jt_foreign_key = through_reflection.primary_key_name
2096
- end
2097
-
2098
- case source_reflection.macro
2099
- when :has_many
2100
- if source_reflection.options[:as]
2101
- first_key = "#{source_reflection.options[:as]}_id"
2102
- second_key = options[:foreign_key] || primary_key
2103
- as_extra = " AND %s.%s = %s" % [
2104
- connection.quote_table_name(aliased_table_name),
2105
- connection.quote_column_name("#{source_reflection.options[:as]}_type"),
2106
- klass.quote_value(source_reflection.active_record.base_class.name)
2107
- ]
2108
- else
2109
- first_key = through_reflection.klass.base_class.to_s.foreign_key
2110
- second_key = options[:foreign_key] || primary_key
2111
- end
2112
-
2113
- unless through_reflection.klass.descends_from_active_record?
2114
- jt_sti_extra = " AND %s.%s = %s" % [
2115
- connection.quote_table_name(aliased_join_table_name),
2116
- connection.quote_column_name(through_reflection.active_record.inheritance_column),
2117
- through_reflection.klass.quote_value(through_reflection.klass.sti_name)]
2118
- end
2119
- when :belongs_to
2120
- first_key = primary_key
2121
- if reflection.options[:source_type]
2122
- second_key = source_reflection.association_foreign_key
2123
- jt_source_extra = " AND %s.%s = %s" % [
2124
- connection.quote_table_name(aliased_join_table_name),
2125
- connection.quote_column_name(reflection.source_reflection.options[:foreign_type]),
2126
- klass.quote_value(reflection.options[:source_type])
2127
- ]
2128
- else
2129
- second_key = source_reflection.primary_key_name
2130
- end
2131
- end
2132
-
2133
- " #{join_type} %s ON (%s.%s = %s.%s%s%s%s) " % [
2134
- table_alias_for(through_reflection.klass.table_name, aliased_join_table_name),
2135
- connection.quote_table_name(parent.aliased_table_name),
2136
- connection.quote_column_name(parent.primary_key),
2137
- connection.quote_table_name(aliased_join_table_name),
2138
- connection.quote_column_name(jt_foreign_key),
2139
- jt_as_extra, jt_source_extra, jt_sti_extra
2140
- ] +
2141
- " #{join_type} %s ON (%s.%s = %s.%s%s) " % [
2142
- table_name_and_alias,
2143
- connection.quote_table_name(aliased_table_name),
2144
- connection.quote_column_name(first_key),
2145
- connection.quote_table_name(aliased_join_table_name),
2146
- connection.quote_column_name(second_key),
2147
- as_extra
2148
- ]
2149
-
2150
- when reflection.options[:as] && [:has_many, :has_one].include?(reflection.macro)
2151
- " #{join_type} %s ON %s.%s = %s.%s AND %s.%s = %s" % [
2152
- table_name_and_alias,
2153
- connection.quote_table_name(aliased_table_name),
2154
- "#{reflection.options[:as]}_id",
2155
- connection.quote_table_name(parent.aliased_table_name),
2156
- parent.primary_key,
2157
- connection.quote_table_name(aliased_table_name),
2158
- "#{reflection.options[:as]}_type",
2159
- klass.quote_value(parent.active_record.base_class.name)
2160
- ]
2161
- else
2162
- foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key
2163
- " #{join_type} %s ON %s.%s = %s.%s " % [
2164
- table_name_and_alias,
2165
- aliased_table_name,
2166
- foreign_key,
2167
- parent.aliased_table_name,
2168
- reflection.options[:primary_key] || parent.primary_key
2169
- ]
2170
- end
2171
- when :belongs_to
2172
- " #{join_type} %s ON %s.%s = %s.%s " % [
2173
- table_name_and_alias,
2174
- connection.quote_table_name(aliased_table_name),
2175
- reflection.options[:primary_key] || reflection.klass.primary_key,
2176
- connection.quote_table_name(parent.aliased_table_name),
2177
- options[:foreign_key] || reflection.primary_key_name
2178
- ]
2179
- else
2180
- ""
2181
- end || ''
2182
- join << %(AND %s) % [
2183
- klass.send(:type_condition, aliased_table_name)] unless klass.descends_from_active_record?
2184
-
2185
- [through_reflection, reflection].each do |ref|
2186
- join << "AND #{interpolate_sql(sanitize_sql(ref.options[:conditions], aliased_table_name))} " if ref && ref.options[:conditions]
2187
- end
2188
-
2189
- join
2190
- end
2191
-
2192
- protected
2193
-
2194
- def aliased_table_name_for(name, suffix = nil)
2195
- if !parent.table_joins.blank? && parent.table_joins.to_s.downcase =~ %r{join(\s+\w+)?\s+#{active_record.connection.quote_table_name name.downcase}\son}
2196
- @join_dependency.table_aliases[name] += 1
2197
- end
2198
-
2199
- unless @join_dependency.table_aliases[name].zero?
2200
- # if the table name has been used, then use an alias
2201
- name = active_record.connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}#{suffix}"
2202
- table_index = @join_dependency.table_aliases[name]
2203
- @join_dependency.table_aliases[name] += 1
2204
- name = name[0..active_record.connection.table_alias_length-3] + "_#{table_index+1}" if table_index > 0
2205
- else
2206
- @join_dependency.table_aliases[name] += 1
2207
- end
2208
-
2209
- name
2210
- end
2211
-
2212
- def pluralize(table_name)
2213
- ActiveRecord::Base.pluralize_table_names ? table_name.to_s.pluralize : table_name
2214
- end
2215
-
2216
- def table_alias_for(table_name, table_alias)
2217
- "#{reflection.active_record.connection.quote_table_name(table_name)} #{table_alias if table_name != table_alias}".strip
2218
- end
2219
-
2220
- def table_name_and_alias
2221
- table_alias_for table_name, @aliased_table_name
2222
- end
2223
-
2224
- def interpolate_sql(sql)
2225
- instance_eval("%@#{sql.gsub('@', '\@')}@")
2226
- end
2227
-
2228
- private
2229
- def join_type
2230
- "LEFT OUTER JOIN"
2231
- end
2232
- end
2233
- end
2234
-
2235
- class InnerJoinDependency < JoinDependency # :nodoc:
2236
- protected
2237
- def build_join_association(reflection, parent)
2238
- InnerJoinAssociation.new(reflection, self, parent)
2239
- end
2240
-
2241
- class InnerJoinAssociation < JoinAssociation
2242
- private
2243
- def join_type
2244
- "INNER JOIN"
2245
- end
2246
- end
2247
- end
2248
-
2249
1605
  end
2250
1606
  end
2251
1607
  end