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,134 @@
1
+ module ActiveRecord
2
+ module Associations
3
+ class AssociationScope #:nodoc:
4
+ include JoinHelper
5
+
6
+ attr_reader :association, :alias_tracker
7
+
8
+ delegate :klass, :owner, :reflection, :interpolate, :to => :association
9
+ delegate :chain, :conditions, :options, :source_options, :active_record, :to => :reflection
10
+
11
+ def initialize(association)
12
+ @association = association
13
+ @alias_tracker = AliasTracker.new klass.connection
14
+ end
15
+
16
+ def scope
17
+ scope = klass.unscoped
18
+ scope = scope.extending(*Array.wrap(options[:extend]))
19
+
20
+ # It's okay to just apply all these like this. The options will only be present if the
21
+ # association supports that option; this is enforced by the association builder.
22
+ scope = scope.apply_finder_options(options.slice(
23
+ :readonly, :include, :order, :limit, :joins, :group, :having, :offset, :select))
24
+
25
+ if options[:through] && !options[:include]
26
+ scope = scope.includes(source_options[:include])
27
+ end
28
+
29
+ scope = scope.uniq if options[:uniq]
30
+
31
+ add_constraints(scope)
32
+ end
33
+
34
+ private
35
+
36
+ def add_constraints(scope)
37
+ tables = construct_tables
38
+
39
+ chain.each_with_index do |reflection, i|
40
+ table, foreign_table = tables.shift, tables.first
41
+
42
+ if reflection.source_macro == :has_and_belongs_to_many
43
+ join_table = tables.shift
44
+
45
+ scope = scope.joins(join(
46
+ join_table,
47
+ table[reflection.association_primary_key].
48
+ eq(join_table[reflection.association_foreign_key])
49
+ ))
50
+
51
+ table, foreign_table = join_table, tables.first
52
+ end
53
+
54
+ if reflection.source_macro == :belongs_to
55
+ if reflection.options[:polymorphic]
56
+ key = reflection.association_primary_key(klass)
57
+ else
58
+ key = reflection.association_primary_key
59
+ end
60
+
61
+ foreign_key = reflection.foreign_key
62
+ else
63
+ key = reflection.foreign_key
64
+ foreign_key = reflection.active_record_primary_key
65
+ end
66
+
67
+ conditions = self.conditions[i]
68
+
69
+ if reflection == chain.last
70
+ scope = scope.where(table[key].eq(owner[foreign_key]))
71
+
72
+ if reflection.type
73
+ scope = scope.where(table[reflection.type].eq(owner.class.base_class.name))
74
+ end
75
+
76
+ conditions.each do |condition|
77
+ if options[:through] && condition.is_a?(Hash)
78
+ condition = disambiguate_condition(table, condition)
79
+ end
80
+
81
+ scope = scope.where(interpolate(condition))
82
+ end
83
+ else
84
+ constraint = table[key].eq(foreign_table[foreign_key])
85
+
86
+ if reflection.type
87
+ type = chain[i + 1].klass.base_class.name
88
+ constraint = constraint.and(table[reflection.type].eq(type))
89
+ end
90
+
91
+ scope = scope.joins(join(foreign_table, constraint))
92
+
93
+ unless conditions.empty?
94
+ scope = scope.where(sanitize(conditions, table))
95
+ end
96
+ end
97
+ end
98
+
99
+ scope
100
+ end
101
+
102
+ def alias_suffix
103
+ reflection.name
104
+ end
105
+
106
+ def table_name_for(reflection)
107
+ if reflection == self.reflection
108
+ # If this is a polymorphic belongs_to, we want to get the klass from the
109
+ # association because it depends on the polymorphic_type attribute of
110
+ # the owner
111
+ klass.table_name
112
+ else
113
+ reflection.table_name
114
+ end
115
+ end
116
+
117
+ def disambiguate_condition(table, condition)
118
+ if condition.is_a?(Hash)
119
+ Hash[
120
+ condition.map do |k, v|
121
+ if v.is_a?(Hash)
122
+ [k, v]
123
+ else
124
+ [table.table_alias || table.name, { k => v }]
125
+ end
126
+ end
127
+ ]
128
+ else
129
+ condition
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
@@ -1,85 +1,78 @@
1
1
  module ActiveRecord
2
+ # = Active Record Belongs To Associations
2
3
  module Associations
3
- class BelongsToAssociation < AssociationProxy #:nodoc:
4
- def create(attributes = {})
5
- replace(@reflection.create_association(attributes))
6
- end
7
-
8
- def build(attributes = {})
9
- replace(@reflection.build_association(attributes))
10
- end
11
-
4
+ class BelongsToAssociation < SingularAssociation #:nodoc:
12
5
  def replace(record)
13
- counter_cache_name = @reflection.counter_cache_column
14
-
15
- if record.nil?
16
- if counter_cache_name && !@owner.new_record?
17
- @reflection.klass.decrement_counter(counter_cache_name, previous_record_id) if @owner[@reflection.primary_key_name]
18
- end
6
+ raise_on_type_mismatch(record) if record
19
7
 
20
- @target = @owner[@reflection.primary_key_name] = nil
21
- else
22
- raise_on_type_mismatch(record)
8
+ update_counters(record)
9
+ replace_keys(record)
10
+ set_inverse_instance(record)
23
11
 
24
- if counter_cache_name && !@owner.new_record?
25
- @reflection.klass.increment_counter(counter_cache_name, record.id)
26
- @reflection.klass.decrement_counter(counter_cache_name, @owner[@reflection.primary_key_name]) if @owner[@reflection.primary_key_name]
27
- end
28
-
29
- @target = (AssociationProxy === record ? record.target : record)
30
- @owner[@reflection.primary_key_name] = record_id(record) unless record.new_record?
31
- @updated = true
32
- end
12
+ @updated = true if record
33
13
 
34
- set_inverse_instance(record, @owner)
35
-
36
- loaded
37
- record
14
+ self.target = record
38
15
  end
39
-
16
+
40
17
  def updated?
41
18
  @updated
42
19
  end
43
-
20
+
44
21
  private
45
- def find_target
46
- find_method = if @reflection.options[:primary_key]
47
- "find_by_#{@reflection.options[:primary_key]}"
48
- else
49
- "find"
50
- end
51
- the_target = @reflection.klass.send(find_method,
52
- @owner[@reflection.primary_key_name],
53
- :select => @reflection.options[:select],
54
- :conditions => conditions,
55
- :include => @reflection.options[:include],
56
- :readonly => @reflection.options[:readonly]
57
- ) if @owner[@reflection.primary_key_name]
58
- set_inverse_instance(the_target, @owner)
59
- the_target
22
+
23
+ def find_target?
24
+ !loaded? && foreign_key_present? && klass
25
+ end
26
+
27
+ def update_counters(record)
28
+ counter_cache_name = reflection.counter_cache_column
29
+
30
+ if counter_cache_name && owner.persisted? && different_target?(record)
31
+ if record
32
+ record.class.increment_counter(counter_cache_name, record.id)
33
+ end
34
+
35
+ if foreign_key_present?
36
+ klass.decrement_counter(counter_cache_name, target_id)
37
+ end
38
+ end
60
39
  end
61
40
 
62
- def foreign_key_present
63
- !@owner[@reflection.primary_key_name].nil?
41
+ # Checks whether record is different to the current target, without loading it
42
+ def different_target?(record)
43
+ record.nil? && owner[reflection.foreign_key] ||
44
+ record && record.id != owner[reflection.foreign_key]
64
45
  end
65
46
 
66
- def record_id(record)
67
- record.send(@reflection.options[:primary_key] || :id)
47
+ def replace_keys(record)
48
+ if record
49
+ owner[reflection.foreign_key] = record[reflection.association_primary_key(record.class)]
50
+ else
51
+ owner[reflection.foreign_key] = nil
52
+ end
68
53
  end
69
54
 
70
- def previous_record_id
71
- @previous_record_id ||= if @reflection.options[:primary_key]
72
- previous_record = @owner.send(@reflection.name)
73
- previous_record.nil? ? nil : previous_record.id
74
- else
75
- @owner[@reflection.primary_key_name]
76
- end
55
+ def foreign_key_present?
56
+ owner[reflection.foreign_key]
77
57
  end
78
58
 
79
59
  # NOTE - for now, we're only supporting inverse setting from belongs_to back onto
80
60
  # has_one associations.
81
- def we_can_set_the_inverse_on_this?(record)
82
- @reflection.has_inverse? && @reflection.inverse_of.macro == :has_one
61
+ def invertible_for?(record)
62
+ inverse = inverse_reflection_for(record)
63
+ inverse && inverse.macro == :has_one
64
+ end
65
+
66
+ def target_id
67
+ if options[:primary_key]
68
+ owner.send(reflection.name).try(:id)
69
+ else
70
+ owner[reflection.foreign_key]
71
+ end
72
+ end
73
+
74
+ def stale_state
75
+ owner[reflection.foreign_key] && owner[reflection.foreign_key].to_s
83
76
  end
84
77
  end
85
78
  end
@@ -1,76 +1,34 @@
1
1
  module ActiveRecord
2
+ # = Active Record Belongs To Polymorphic Association
2
3
  module Associations
3
- class BelongsToPolymorphicAssociation < AssociationProxy #:nodoc:
4
- def replace(record)
5
- if record.nil?
6
- @target = @owner[@reflection.primary_key_name] = @owner[@reflection.options[:foreign_type]] = nil
7
- else
8
- @target = (AssociationProxy === record ? record.target : record)
9
-
10
- @owner[@reflection.primary_key_name] = record_id(record)
11
- @owner[@reflection.options[:foreign_type]] = record.class.base_class.name.to_s
12
-
13
- @updated = true
14
- end
15
-
16
- set_inverse_instance(record, @owner)
17
- loaded
18
- record
19
- end
20
-
21
- def updated?
22
- @updated
4
+ class BelongsToPolymorphicAssociation < BelongsToAssociation #:nodoc:
5
+ def klass
6
+ type = owner[reflection.foreign_type]
7
+ type.presence && type.constantize
23
8
  end
24
9
 
25
10
  private
26
11
 
27
- # NOTE - for now, we're only supporting inverse setting from belongs_to back onto
28
- # has_one associations.
29
- def we_can_set_the_inverse_on_this?(record)
30
- if @reflection.has_inverse?
31
- inverse_association = @reflection.polymorphic_inverse_of(record.class)
32
- inverse_association && inverse_association.macro == :has_one
33
- else
34
- false
35
- end
36
- end
37
-
38
- def set_inverse_instance(record, instance)
39
- return if record.nil? || !we_can_set_the_inverse_on_this?(record)
40
- inverse_relationship = @reflection.polymorphic_inverse_of(record.class)
41
- unless inverse_relationship.nil?
42
- record.send(:"set_#{inverse_relationship.name}_target", instance)
43
- end
12
+ def replace_keys(record)
13
+ super
14
+ owner[reflection.foreign_type] = record && record.class.base_class.name
44
15
  end
45
16
 
46
- def find_target
47
- return nil if association_class.nil?
48
-
49
- target =
50
- if @reflection.options[:conditions]
51
- association_class.find(
52
- @owner[@reflection.primary_key_name],
53
- :select => @reflection.options[:select],
54
- :conditions => conditions,
55
- :include => @reflection.options[:include]
56
- )
57
- else
58
- association_class.find(@owner[@reflection.primary_key_name], :select => @reflection.options[:select], :include => @reflection.options[:include])
59
- end
60
- set_inverse_instance(target, @owner)
61
- target
17
+ def different_target?(record)
18
+ super || record.class != klass
62
19
  end
63
20
 
64
- def foreign_key_present
65
- !@owner[@reflection.primary_key_name].nil?
21
+ def inverse_reflection_for(record)
22
+ reflection.polymorphic_inverse_of(record.class)
66
23
  end
67
24
 
68
- def record_id(record)
69
- record.send(@reflection.options[:primary_key] || :id)
25
+ def raise_on_type_mismatch(record)
26
+ # A polymorphic association cannot have a type mismatch, by definition
70
27
  end
71
28
 
72
- def association_class
73
- @owner[@reflection.options[:foreign_type]] ? @owner[@reflection.options[:foreign_type]].constantize : nil
29
+ def stale_state
30
+ foreign_key = super
31
+ foreign_key && [foreign_key.to_s, owner[reflection.foreign_type].to_s]
74
32
  end
75
33
  end
76
34
  end
@@ -0,0 +1,55 @@
1
+ module ActiveRecord::Associations::Builder
2
+ class Association #:nodoc:
3
+ class_attribute :valid_options
4
+ self.valid_options = [:class_name, :foreign_key, :select, :conditions, :include, :extend, :readonly, :validate]
5
+
6
+ # Set by subclasses
7
+ class_attribute :macro
8
+
9
+ attr_reader :model, :name, :options, :reflection
10
+
11
+ def self.build(model, name, options)
12
+ new(model, name, options).build
13
+ end
14
+
15
+ def initialize(model, name, options)
16
+ @model, @name, @options = model, name, options
17
+ end
18
+
19
+ def mixin
20
+ @model.generated_feature_methods
21
+ end
22
+
23
+ def build
24
+ validate_options
25
+ reflection = model.create_reflection(self.class.macro, name, options, model)
26
+ define_accessors
27
+ reflection
28
+ end
29
+
30
+ private
31
+
32
+ def validate_options
33
+ options.assert_valid_keys(self.class.valid_options)
34
+ end
35
+
36
+ def define_accessors
37
+ define_readers
38
+ define_writers
39
+ end
40
+
41
+ def define_readers
42
+ name = self.name
43
+ mixin.redefine_method(name) do |*params|
44
+ association(name).reader(*params)
45
+ end
46
+ end
47
+
48
+ def define_writers
49
+ name = self.name
50
+ mixin.redefine_method("#{name}=") do |value|
51
+ association(name).writer(value)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,88 @@
1
+ require 'active_support/core_ext/object/inclusion'
2
+
3
+ module ActiveRecord::Associations::Builder
4
+ class BelongsTo < SingularAssociation #:nodoc:
5
+ self.macro = :belongs_to
6
+
7
+ self.valid_options += [:foreign_type, :polymorphic, :touch]
8
+
9
+ def constructable?
10
+ !options[:polymorphic]
11
+ end
12
+
13
+ def build
14
+ reflection = super
15
+ add_counter_cache_callbacks(reflection) if options[:counter_cache]
16
+ add_touch_callbacks(reflection) if options[:touch]
17
+ configure_dependency
18
+ reflection
19
+ end
20
+
21
+ private
22
+
23
+ def add_counter_cache_callbacks(reflection)
24
+ cache_column = reflection.counter_cache_column
25
+ name = self.name
26
+
27
+ method_name = "belongs_to_counter_cache_after_create_for_#{name}"
28
+ mixin.redefine_method(method_name) do
29
+ record = send(name)
30
+ record.class.increment_counter(cache_column, record.id) unless record.nil?
31
+ end
32
+ model.after_create(method_name)
33
+
34
+ method_name = "belongs_to_counter_cache_before_destroy_for_#{name}"
35
+ mixin.redefine_method(method_name) do
36
+ record = send(name)
37
+
38
+ if record && !self.destroyed?
39
+ record.class.decrement_counter(cache_column, record.id)
40
+ end
41
+ end
42
+ model.before_destroy(method_name)
43
+
44
+ model.send(:module_eval,
45
+ "#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)", __FILE__, __LINE__
46
+ )
47
+ end
48
+
49
+ def add_touch_callbacks(reflection)
50
+ name = self.name
51
+ method_name = "belongs_to_touch_after_save_or_destroy_for_#{name}"
52
+ touch = options[:touch]
53
+
54
+ mixin.redefine_method(method_name) do
55
+ record = send(name)
56
+
57
+ unless record.nil?
58
+ if touch == true
59
+ record.touch
60
+ else
61
+ record.touch(touch)
62
+ end
63
+ end
64
+ end
65
+
66
+ model.after_save(method_name)
67
+ model.after_touch(method_name)
68
+ model.after_destroy(method_name)
69
+ end
70
+
71
+ def configure_dependency
72
+ if options[:dependent]
73
+ unless options[:dependent].in?([:destroy, :delete])
74
+ raise ArgumentError, "The :dependent option expects either :destroy or :delete (#{options[:dependent].inspect})"
75
+ end
76
+
77
+ method_name = "belongs_to_dependent_#{options[:dependent]}_for_#{name}"
78
+ model.send(:class_eval, <<-eoruby, __FILE__, __LINE__ + 1)
79
+ def #{method_name}
80
+ association = #{name}
81
+ association.#{options[:dependent]} if association
82
+ end
83
+ eoruby
84
+ model.after_destroy method_name
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,75 @@
1
+ module ActiveRecord::Associations::Builder
2
+ class CollectionAssociation < Association #:nodoc:
3
+ CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
4
+
5
+ self.valid_options += [
6
+ :table_name, :order, :group, :having, :limit, :offset, :uniq, :finder_sql,
7
+ :counter_sql, :before_add, :after_add, :before_remove, :after_remove
8
+ ]
9
+
10
+ attr_reader :block_extension
11
+
12
+ def self.build(model, name, options, &extension)
13
+ new(model, name, options, &extension).build
14
+ end
15
+
16
+ def initialize(model, name, options, &extension)
17
+ super(model, name, options)
18
+ @block_extension = extension
19
+ end
20
+
21
+ def build
22
+ wrap_block_extension
23
+ reflection = super
24
+ CALLBACKS.each { |callback_name| define_callback(callback_name) }
25
+ reflection
26
+ end
27
+
28
+ def writable?
29
+ true
30
+ end
31
+
32
+ private
33
+
34
+ def wrap_block_extension
35
+ options[:extend] = Array.wrap(options[:extend])
36
+
37
+ if block_extension
38
+ silence_warnings do
39
+ model.parent.const_set(extension_module_name, Module.new(&block_extension))
40
+ end
41
+ options[:extend].push("#{model.parent}::#{extension_module_name}".constantize)
42
+ end
43
+ end
44
+
45
+ def extension_module_name
46
+ @extension_module_name ||= "#{model.to_s.demodulize}#{name.to_s.camelize}AssociationExtension"
47
+ end
48
+
49
+ def define_callback(callback_name)
50
+ full_callback_name = "#{callback_name}_for_#{name}"
51
+
52
+ # TODO : why do i need method_defined? I think its because of the inheritance chain
53
+ model.class_attribute full_callback_name.to_sym unless model.method_defined?(full_callback_name)
54
+ model.send("#{full_callback_name}=", Array.wrap(options[callback_name.to_sym]))
55
+ end
56
+
57
+ def define_readers
58
+ super
59
+
60
+ name = self.name
61
+ mixin.redefine_method("#{name.to_s.singularize}_ids") do
62
+ association(name).ids_reader
63
+ end
64
+ end
65
+
66
+ def define_writers
67
+ super
68
+
69
+ name = self.name
70
+ mixin.redefine_method("#{name.to_s.singularize}_ids=") do |ids|
71
+ association(name).ids_writer(ids)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,57 @@
1
+ module ActiveRecord::Associations::Builder
2
+ class HasAndBelongsToMany < CollectionAssociation #:nodoc:
3
+ self.macro = :has_and_belongs_to_many
4
+
5
+ self.valid_options += [:join_table, :association_foreign_key, :delete_sql, :insert_sql]
6
+
7
+ def build
8
+ reflection = super
9
+ check_validity(reflection)
10
+ define_destroy_hook
11
+ reflection
12
+ end
13
+
14
+ private
15
+
16
+ def define_destroy_hook
17
+ name = self.name
18
+ model.send(:include, Module.new {
19
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
20
+ def destroy_associations
21
+ association(#{name.to_sym.inspect}).delete_all_on_destroy
22
+ super
23
+ end
24
+ RUBY
25
+ })
26
+ end
27
+
28
+ # TODO: These checks should probably be moved into the Reflection, and we should not be
29
+ # redefining the options[:join_table] value - instead we should define a
30
+ # reflection.join_table method.
31
+ def check_validity(reflection)
32
+ if reflection.association_foreign_key == reflection.foreign_key
33
+ raise ActiveRecord::HasAndBelongsToManyAssociationForeignKeyNeeded.new(reflection)
34
+ end
35
+
36
+ reflection.options[:join_table] ||= join_table_name(
37
+ model.send(:undecorated_table_name, model.to_s),
38
+ model.send(:undecorated_table_name, reflection.class_name)
39
+ )
40
+ end
41
+
42
+ # Generates a join table name from two provided table names.
43
+ # The names in the join table names end up in lexicographic order.
44
+ #
45
+ # join_table_name("members", "clubs") # => "clubs_members"
46
+ # join_table_name("members", "special_clubs") # => "members_special_clubs"
47
+ def join_table_name(first_table_name, second_table_name)
48
+ if first_table_name < second_table_name
49
+ join_table = "#{first_table_name}_#{second_table_name}"
50
+ else
51
+ join_table = "#{second_table_name}_#{first_table_name}"
52
+ end
53
+
54
+ model.table_name_prefix + join_table + model.table_name_suffix
55
+ end
56
+ end
57
+ end