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
@@ -0,0 +1,26 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/core_ext/class/attribute'
3
+
4
+ module ActiveRecord
5
+ module ReadonlyAttributes
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :_attr_readonly, :instance_writer => false
10
+ self._attr_readonly = []
11
+ end
12
+
13
+ module ClassMethods
14
+ # Attributes listed as readonly will be used to create a new record but update operations will
15
+ # ignore these fields.
16
+ def attr_readonly(*attributes)
17
+ self._attr_readonly = Set.new(attributes.map { |a| a.to_s }) + (self._attr_readonly || [])
18
+ end
19
+
20
+ # Returns an array of all the attributes that have been specified as readonly.
21
+ def readonly_attributes
22
+ self._attr_readonly
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,14 +1,24 @@
1
+ require 'active_support/core_ext/class/attribute'
2
+ require 'active_support/core_ext/object/inclusion'
3
+
1
4
  module ActiveRecord
5
+ # = Active Record Reflection
2
6
  module Reflection # :nodoc:
3
- def self.included(base)
4
- base.extend(ClassMethods)
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ class_attribute :reflections
11
+ self.reflections = {}
5
12
  end
6
13
 
7
- # Reflection allows you to interrogate Active Record classes and objects about their associations and aggregations.
8
- # This information can, for example, be used in a form builder that took an Active Record object and created input
9
- # fields for all of the attributes depending on their type and displayed the associations to other objects.
14
+ # Reflection enables to interrogate Active Record classes and objects
15
+ # about their associations and aggregations. This information can,
16
+ # for example, be used in a form builder that takes an Active Record object
17
+ # and creates input fields for all of the attributes depending on their type
18
+ # and displays the associations to other objects.
10
19
  #
11
- # You can find the interface for the AggregateReflection and AssociationReflection classes in the abstract MacroReflection class.
20
+ # MacroReflection class has info for AggregateReflection and AssociationReflection
21
+ # classes.
12
22
  module ClassMethods
13
23
  def create_reflection(macro, name, options, active_record)
14
24
  case macro
@@ -18,48 +28,42 @@ module ActiveRecord
18
28
  when :composed_of
19
29
  reflection = AggregateReflection.new(macro, name, options, active_record)
20
30
  end
21
- write_inheritable_hiwa :reflections, name => reflection
22
- reflection
23
- end
24
31
 
25
- # Returns a hash containing all AssociationReflection objects for the current class
26
- # Example:
27
- #
28
- # Invoice.reflections
29
- # Account.reflections
30
- #
31
- def reflections
32
- read_inheritable_attribute(:reflections) || write_inheritable_attribute(:reflections, {})
32
+ self.reflections = self.reflections.merge(name => reflection)
33
+ reflection
33
34
  end
34
35
 
35
36
  # Returns an array of AggregateReflection objects for all the aggregations in the class.
36
37
  def reflect_on_all_aggregations
37
- reflections.values.select { |reflection| reflection.is_a?(AggregateReflection) }
38
+ reflections.values.grep(AggregateReflection)
38
39
  end
39
40
 
40
- # Returns the AggregateReflection object for the named +aggregation+ (use the symbol). Example:
41
+ # Returns the AggregateReflection object for the named +aggregation+ (use the symbol).
41
42
  #
42
- # Account.reflect_on_aggregation(:balance) # returns the balance AggregateReflection
43
+ # Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
43
44
  #
44
45
  def reflect_on_aggregation(aggregation)
45
46
  reflections[aggregation].is_a?(AggregateReflection) ? reflections[aggregation] : nil
46
47
  end
47
48
 
48
- # Returns an array of AssociationReflection objects for all the associations in the class. If you only want to reflect on a
49
- # certain association type, pass in the symbol (<tt>:has_many</tt>, <tt>:has_one</tt>, <tt>:belongs_to</tt>) for that as the first parameter.
49
+ # Returns an array of AssociationReflection objects for all the
50
+ # associations in the class. If you only want to reflect on a certain
51
+ # association type, pass in the symbol (<tt>:has_many</tt>, <tt>:has_one</tt>,
52
+ # <tt>:belongs_to</tt>) as the first parameter.
53
+ #
50
54
  # Example:
51
55
  #
52
56
  # Account.reflect_on_all_associations # returns an array of all associations
53
57
  # Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
54
58
  #
55
59
  def reflect_on_all_associations(macro = nil)
56
- association_reflections = reflections.values.select { |reflection| reflection.is_a?(AssociationReflection) }
60
+ association_reflections = reflections.values.grep(AssociationReflection)
57
61
  macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
58
62
  end
59
63
 
60
- # Returns the AssociationReflection object for the named +association+ (use the symbol). Example:
64
+ # Returns the AssociationReflection object for the +association+ (use the symbol).
61
65
  #
62
- # Account.reflect_on_association(:owner) # returns the owner AssociationReflection
66
+ # Account.reflect_on_association(:owner) # returns the owner AssociationReflection
63
67
  # Invoice.reflect_on_association(:line_items).macro # returns :has_many
64
68
  #
65
69
  def reflect_on_association(association)
@@ -73,60 +77,70 @@ module ActiveRecord
73
77
  end
74
78
 
75
79
 
76
- # Abstract base class for AggregateReflection and AssociationReflection that describes the interface available for both of
77
- # those classes. Objects of AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
80
+ # Abstract base class for AggregateReflection and AssociationReflection. Objects of
81
+ # AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
78
82
  class MacroReflection
79
- attr_reader :active_record
83
+ # Returns the name of the macro.
84
+ #
85
+ # <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>:balance</tt>
86
+ # <tt>has_many :clients</tt> returns <tt>:clients</tt>
87
+ attr_reader :name
80
88
 
81
- def initialize(macro, name, options, active_record)
82
- @macro, @name, @options, @active_record = macro, name, options, active_record
83
- end
89
+ # Returns the macro type.
90
+ #
91
+ # <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>:composed_of</tt>
92
+ # <tt>has_many :clients</tt> returns <tt>:has_many</tt>
93
+ attr_reader :macro
84
94
 
85
- # Returns the name of the macro. For example, <tt>composed_of :balance, :class_name => 'Money'</tt> will return
86
- # <tt>:balance</tt> or for <tt>has_many :clients</tt> it will return <tt>:clients</tt>.
87
- def name
88
- @name
89
- end
95
+ # Returns the hash of options used for the macro.
96
+ #
97
+ # <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>{ :class_name => "Money" }</tt>
98
+ # <tt>has_many :clients</tt> returns +{}+
99
+ attr_reader :options
90
100
 
91
- # Returns the macro type. For example, <tt>composed_of :balance, :class_name => 'Money'</tt> will return <tt>:composed_of</tt>
92
- # or for <tt>has_many :clients</tt> will return <tt>:has_many</tt>.
93
- def macro
94
- @macro
95
- end
101
+ attr_reader :active_record
96
102
 
97
- # Returns the hash of options used for the macro. For example, it would return <tt>{ :class_name => "Money" }</tt> for
98
- # <tt>composed_of :balance, :class_name => 'Money'</tt> or +{}+ for <tt>has_many :clients</tt>.
99
- def options
100
- @options
103
+ attr_reader :plural_name # :nodoc:
104
+
105
+ def initialize(macro, name, options, active_record)
106
+ @macro = macro
107
+ @name = name
108
+ @options = options
109
+ @active_record = active_record
110
+ @plural_name = active_record.pluralize_table_names ?
111
+ name.to_s.pluralize : name.to_s
101
112
  end
102
113
 
103
- # Returns the class for the macro. For example, <tt>composed_of :balance, :class_name => 'Money'</tt> returns the Money
104
- # class and <tt>has_many :clients</tt> returns the Client class.
114
+ # Returns the class for the macro.
115
+ #
116
+ # <tt>composed_of :balance, :class_name => 'Money'</tt> returns the Money class
117
+ # <tt>has_many :clients</tt> returns the Client class
105
118
  def klass
106
119
  @klass ||= class_name.constantize
107
120
  end
108
121
 
109
- # Returns the class name for the macro. For example, <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>'Money'</tt>
110
- # and <tt>has_many :clients</tt> returns <tt>'Client'</tt>.
122
+ # Returns the class name for the macro.
123
+ #
124
+ # <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>'Money'</tt>
125
+ # <tt>has_many :clients</tt> returns <tt>'Client'</tt>
111
126
  def class_name
112
- @class_name ||= options[:class_name] || derive_class_name
127
+ @class_name ||= (options[:class_name] || derive_class_name).to_s
113
128
  end
114
129
 
115
130
  # Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
116
131
  # and +other_aggregation+ has an options hash assigned to it.
117
132
  def ==(other_aggregation)
118
- other_aggregation.kind_of?(self.class) && name == other_aggregation.name && other_aggregation.options && active_record == other_aggregation.active_record
133
+ super ||
134
+ other_aggregation.kind_of?(self.class) &&
135
+ name == other_aggregation.name &&
136
+ other_aggregation.options &&
137
+ active_record == other_aggregation.active_record
119
138
  end
120
139
 
121
140
  def sanitized_conditions #:nodoc:
122
141
  @sanitized_conditions ||= klass.send(:sanitize_sql, options[:conditions]) if options[:conditions]
123
142
  end
124
143
 
125
- # Returns +true+ if +self+ is a +belongs_to+ reflection.
126
- def belongs_to?
127
- macro == :belongs_to
128
- end
129
-
130
144
  private
131
145
  def derive_class_name
132
146
  name.to_s.camelize
@@ -134,13 +148,15 @@ module ActiveRecord
134
148
  end
135
149
 
136
150
 
137
- # Holds all the meta-data about an aggregation as it was specified in the Active Record class.
151
+ # Holds all the meta-data about an aggregation as it was specified in the
152
+ # Active Record class.
138
153
  class AggregateReflection < MacroReflection #:nodoc:
139
154
  end
140
155
 
141
- # Holds all the meta-data about an association as it was specified in the Active Record class.
156
+ # Holds all the meta-data about an association as it was specified in the
157
+ # Active Record class.
142
158
  class AssociationReflection < MacroReflection #:nodoc:
143
- # Returns the target association's class:
159
+ # Returns the target association's class.
144
160
  #
145
161
  # class Author < ActiveRecord::Base
146
162
  # has_many :books
@@ -149,34 +165,22 @@ module ActiveRecord
149
165
  # Author.reflect_on_association(:books).klass
150
166
  # # => Book
151
167
  #
152
- # <b>Note:</b> do not call +klass.new+ or +klass.create+ to instantiate
168
+ # <b>Note:</b> Do not call +klass.new+ or +klass.create+ to instantiate
153
169
  # a new association object. Use +build_association+ or +create_association+
154
170
  # instead. This allows plugins to hook into association object creation.
155
171
  def klass
156
172
  @klass ||= active_record.send(:compute_type, class_name)
157
173
  end
158
174
 
159
- # Returns a new, unsaved instance of the associated class. +options+ will
160
- # be passed to the class's constructor.
161
- def build_association(*options)
162
- klass.new(*options)
163
- end
164
-
165
- # Creates a new instance of the associated class, and immediates saves it
166
- # with ActiveRecord::Base#save. +options+ will be passed to the class's
167
- # creation method. Returns the newly created object.
168
- def create_association(*options)
169
- klass.create(*options)
175
+ def initialize(macro, name, options, active_record)
176
+ super
177
+ @collection = macro.in?([:has_many, :has_and_belongs_to_many])
170
178
  end
171
179
 
172
- # Creates a new instance of the associated class, and immediates saves it
173
- # with ActiveRecord::Base#save!. +options+ will be passed to the class's
174
- # creation method. If the created record doesn't pass validations, then an
175
- # exception will be raised.
176
- #
177
- # Returns the newly created object.
178
- def create_association!(*options)
179
- klass.create!(*options)
180
+ # Returns a new, unsaved instance of the associated class. +options+ will
181
+ # be passed to the class's constructor.
182
+ def build_association(*options, &block)
183
+ klass.new(*options, &block)
180
184
  end
181
185
 
182
186
  def table_name
@@ -187,19 +191,40 @@ module ActiveRecord
187
191
  @quoted_table_name ||= klass.quoted_table_name
188
192
  end
189
193
 
190
- def primary_key_name
191
- @primary_key_name ||= options[:foreign_key] || derive_primary_key_name
194
+ def foreign_key
195
+ @foreign_key ||= options[:foreign_key] || derive_foreign_key
196
+ end
197
+
198
+ def foreign_type
199
+ @foreign_type ||= options[:foreign_type] || "#{name}_type"
200
+ end
201
+
202
+ def type
203
+ @type ||= options[:as] && "#{options[:as]}_type"
204
+ end
205
+
206
+ def primary_key_column
207
+ @primary_key_column ||= klass.columns.find { |c| c.name == klass.primary_key }
192
208
  end
193
209
 
194
210
  def association_foreign_key
195
- @association_foreign_key ||= @options[:association_foreign_key] || class_name.foreign_key
211
+ @association_foreign_key ||= options[:association_foreign_key] || class_name.foreign_key
212
+ end
213
+
214
+ # klass option is necessary to support loading polymorphic associations
215
+ def association_primary_key(klass = nil)
216
+ options[:primary_key] || primary_key(klass || self.klass)
217
+ end
218
+
219
+ def active_record_primary_key
220
+ @active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
196
221
  end
197
222
 
198
223
  def counter_cache_column
199
224
  if options[:counter_cache] == true
200
225
  "#{active_record.name.demodulize.underscore.pluralize}_count"
201
226
  elsif options[:counter_cache]
202
- options[:counter_cache]
227
+ options[:counter_cache].to_s
203
228
  end
204
229
  end
205
230
 
@@ -224,18 +249,34 @@ module ActiveRecord
224
249
  end
225
250
 
226
251
  def through_reflection
227
- false
228
- end
229
-
230
- def through_reflection_primary_key_name
252
+ nil
231
253
  end
232
254
 
233
255
  def source_reflection
234
256
  nil
235
257
  end
236
258
 
259
+ # A chain of reflections from this one back to the owner. For more see the explanation in
260
+ # ThroughReflection.
261
+ def chain
262
+ [self]
263
+ end
264
+
265
+ def nested?
266
+ false
267
+ end
268
+
269
+ # An array of arrays of conditions. Each item in the outside array corresponds to a reflection
270
+ # in the #chain. The inside arrays are simply conditions (and each condition may itself be
271
+ # a hash, array, arel predicate, etc...)
272
+ def conditions
273
+ [[options[:conditions]].compact]
274
+ end
275
+
276
+ alias :source_macro :macro
277
+
237
278
  def has_inverse?
238
- !@options[:inverse_of].nil?
279
+ @options[:inverse_of]
239
280
  end
240
281
 
241
282
  def inverse_of
@@ -255,37 +296,53 @@ module ActiveRecord
255
296
  end
256
297
 
257
298
  # Returns whether or not this association reflection is for a collection
258
- # association. Returns +true+ if the +macro+ is one of +has_many+ or
299
+ # association. Returns +true+ if the +macro+ is either +has_many+ or
259
300
  # +has_and_belongs_to_many+, +false+ otherwise.
260
301
  def collection?
261
- if @collection.nil?
262
- @collection = [:has_many, :has_and_belongs_to_many].include?(macro)
263
- end
264
302
  @collection
265
303
  end
266
304
 
267
305
  # Returns whether or not the association should be validated as part of
268
306
  # the parent's validation.
269
307
  #
270
- # Unless you explicitely disable validation with
271
- # <tt>:validate => false</tt>, it will take place when:
308
+ # Unless you explicitly disable validation with
309
+ # <tt>:validate => false</tt>, validation will take place when:
272
310
  #
273
- # * you explicitely enable validation; <tt>:validate => true</tt>
311
+ # * you explicitly enable validation; <tt>:validate => true</tt>
274
312
  # * you use autosave; <tt>:autosave => true</tt>
275
313
  # * the association is a +has_many+ association
276
314
  def validate?
277
315
  !options[:validate].nil? ? options[:validate] : (options[:autosave] == true || macro == :has_many)
278
316
  end
279
317
 
280
- def dependent_conditions(record, base_class, extra_conditions)
281
- dependent_conditions = []
282
- dependent_conditions << "#{primary_key_name} = #{record.send(name).send(:owner_quoted_id)}"
283
- dependent_conditions << "#{options[:as]}_type = '#{base_class.name}'" if options[:as]
284
- dependent_conditions << klass.send(:sanitize_sql, options[:conditions]) if options[:conditions]
285
- dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ")
286
- dependent_conditions << extra_conditions if extra_conditions
287
- dependent_conditions = dependent_conditions.gsub('@', '\@')
288
- dependent_conditions
318
+ # Returns +true+ if +self+ is a +belongs_to+ reflection.
319
+ def belongs_to?
320
+ macro == :belongs_to
321
+ end
322
+
323
+ def association_class
324
+ case macro
325
+ when :belongs_to
326
+ if options[:polymorphic]
327
+ Associations::BelongsToPolymorphicAssociation
328
+ else
329
+ Associations::BelongsToAssociation
330
+ end
331
+ when :has_and_belongs_to_many
332
+ Associations::HasAndBelongsToManyAssociation
333
+ when :has_many
334
+ if options[:through]
335
+ Associations::HasManyThroughAssociation
336
+ else
337
+ Associations::HasManyAssociation
338
+ end
339
+ when :has_one
340
+ if options[:through]
341
+ Associations::HasOneThroughAssociation
342
+ else
343
+ Associations::HasOneAssociation
344
+ end
345
+ end
289
346
  end
290
347
 
291
348
  private
@@ -295,7 +352,7 @@ module ActiveRecord
295
352
  class_name
296
353
  end
297
354
 
298
- def derive_primary_key_name
355
+ def derive_foreign_key
299
356
  if belongs_to?
300
357
  "#{name}_id"
301
358
  elsif options[:as]
@@ -304,12 +361,20 @@ module ActiveRecord
304
361
  active_record.name.foreign_key
305
362
  end
306
363
  end
364
+
365
+ def primary_key(klass)
366
+ klass.primary_key || raise(UnknownPrimaryKey.new(klass))
367
+ end
307
368
  end
308
369
 
309
- # Holds all the meta-data about a :through association as it was specified in the Active Record class.
370
+ # Holds all the meta-data about a :through association as it was specified
371
+ # in the Active Record class.
310
372
  class ThroughReflection < AssociationReflection #:nodoc:
311
- # Gets the source of the through reflection. It checks both a singularized and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
312
- # (The <tt>:tags</tt> association on Tagging below.)
373
+ delegate :foreign_key, :foreign_type, :association_foreign_key,
374
+ :active_record_primary_key, :type, :to => :source_reflection
375
+
376
+ # Gets the source of the through reflection. It checks both a singularized
377
+ # and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
313
378
  #
314
379
  # class Post < ActiveRecord::Base
315
380
  # has_many :taggings
@@ -321,7 +386,7 @@ module ActiveRecord
321
386
  end
322
387
 
323
388
  # Returns the AssociationReflection object specified in the <tt>:through</tt> option
324
- # of a HasManyThrough or HasOneThrough association. Example:
389
+ # of a HasManyThrough or HasOneThrough association.
325
390
  #
326
391
  # class Post < ActiveRecord::Base
327
392
  # has_many :taggings
@@ -335,6 +400,86 @@ module ActiveRecord
335
400
  @through_reflection ||= active_record.reflect_on_association(options[:through])
336
401
  end
337
402
 
403
+ # Returns an array of reflections which are involved in this association. Each item in the
404
+ # array corresponds to a table which will be part of the query for this association.
405
+ #
406
+ # The chain is built by recursively calling #chain on the source reflection and the through
407
+ # reflection. The base case for the recursion is a normal association, which just returns
408
+ # [self] as its #chain.
409
+ def chain
410
+ @chain ||= begin
411
+ chain = source_reflection.chain + through_reflection.chain
412
+ chain[0] = self # Use self so we don't lose the information from :source_type
413
+ chain
414
+ end
415
+ end
416
+
417
+ # Consider the following example:
418
+ #
419
+ # class Person
420
+ # has_many :articles
421
+ # has_many :comment_tags, :through => :articles
422
+ # end
423
+ #
424
+ # class Article
425
+ # has_many :comments
426
+ # has_many :comment_tags, :through => :comments, :source => :tags
427
+ # end
428
+ #
429
+ # class Comment
430
+ # has_many :tags
431
+ # end
432
+ #
433
+ # There may be conditions on Person.comment_tags, Article.comment_tags and/or Comment.tags,
434
+ # but only Comment.tags will be represented in the #chain. So this method creates an array
435
+ # of conditions corresponding to the chain. Each item in the #conditions array corresponds
436
+ # to an item in the #chain, and is itself an array of conditions from an arbitrary number
437
+ # of relevant reflections, plus any :source_type or polymorphic :as constraints.
438
+ def conditions
439
+ @conditions ||= begin
440
+ conditions = source_reflection.conditions.map { |c| c.dup }
441
+
442
+ # Add to it the conditions from this reflection if necessary.
443
+ conditions.first << options[:conditions] if options[:conditions]
444
+
445
+ through_conditions = through_reflection.conditions
446
+
447
+ if options[:source_type]
448
+ through_conditions.first << { foreign_type => options[:source_type] }
449
+ end
450
+
451
+ # Recursively fill out the rest of the array from the through reflection
452
+ conditions += through_conditions
453
+
454
+ # And return
455
+ conditions
456
+ end
457
+ end
458
+
459
+ # The macro used by the source association
460
+ def source_macro
461
+ source_reflection.source_macro
462
+ end
463
+
464
+ # A through association is nested if there would be more than one join table
465
+ def nested?
466
+ chain.length > 2 || through_reflection.macro == :has_and_belongs_to_many
467
+ end
468
+
469
+ # We want to use the klass from this reflection, rather than just delegate straight to
470
+ # the source_reflection, because the source_reflection may be polymorphic. We still
471
+ # need to respect the source_reflection's :primary_key option, though.
472
+ def association_primary_key(klass = nil)
473
+ # Get the "actual" source reflection if the immediate source reflection has a
474
+ # source reflection itself
475
+ source_reflection = self.source_reflection
476
+ while source_reflection.source_reflection
477
+ source_reflection = source_reflection.source_reflection
478
+ end
479
+
480
+ source_reflection.options[:primary_key] || primary_key(klass || self.klass)
481
+ end
482
+
338
483
  # Gets an array of possible <tt>:through</tt> source reflection names:
339
484
  #
340
485
  # [:singularized, :pluralized]
@@ -343,11 +488,23 @@ module ActiveRecord
343
488
  @source_reflection_names ||= (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }
344
489
  end
345
490
 
491
+ def source_options
492
+ source_reflection.options
493
+ end
494
+
495
+ def through_options
496
+ through_reflection.options
497
+ end
498
+
346
499
  def check_validity!
347
500
  if through_reflection.nil?
348
501
  raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
349
502
  end
350
503
 
504
+ if through_reflection.options[:polymorphic]
505
+ raise HasManyThroughAssociationPolymorphicThroughError.new(active_record.name, self)
506
+ end
507
+
351
508
  if source_reflection.nil?
352
509
  raise HasManyThroughSourceAssociationNotFoundError.new(self)
353
510
  end
@@ -357,24 +514,16 @@ module ActiveRecord
357
514
  end
358
515
 
359
516
  if source_reflection.options[:polymorphic] && options[:source_type].nil?
360
- raise HasManyThroughAssociationPolymorphicError.new(active_record.name, self, source_reflection)
517
+ raise HasManyThroughAssociationPolymorphicSourceError.new(active_record.name, self, source_reflection)
361
518
  end
362
519
 
363
- unless [:belongs_to, :has_many, :has_one].include?(source_reflection.macro) && source_reflection.options[:through].nil?
364
- raise HasManyThroughSourceAssociationMacroError.new(self)
520
+ if macro == :has_one && through_reflection.collection?
521
+ raise HasOneThroughCantAssociateThroughCollection.new(active_record.name, self, through_reflection)
365
522
  end
366
523
 
367
524
  check_validity_of_inverse!
368
525
  end
369
526
 
370
- def through_reflection_primary_key
371
- through_reflection.belongs_to? ? through_reflection.klass.primary_key : through_reflection.primary_key_name
372
- end
373
-
374
- def through_reflection_primary_key_name
375
- through_reflection.primary_key_name if through_reflection.belongs_to?
376
- end
377
-
378
527
  private
379
528
  def derive_class_name
380
529
  # get the class_name of the belongs_to association of the through reflection