activerecord 2.3.18 → 3.2.22

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

Potentially problematic release.


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

Files changed (454) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1014 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +222 -0
  5. data/examples/performance.rb +100 -126
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record/aggregations.rb +93 -99
  8. data/lib/active_record/associations/alias_tracker.rb +76 -0
  9. data/lib/active_record/associations/association.rb +247 -0
  10. data/lib/active_record/associations/association_scope.rb +134 -0
  11. data/lib/active_record/associations/belongs_to_association.rb +54 -61
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +17 -59
  13. data/lib/active_record/associations/builder/association.rb +55 -0
  14. data/lib/active_record/associations/builder/belongs_to.rb +88 -0
  15. data/lib/active_record/associations/builder/collection_association.rb +75 -0
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +57 -0
  17. data/lib/active_record/associations/builder/has_many.rb +71 -0
  18. data/lib/active_record/associations/builder/has_one.rb +62 -0
  19. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  20. data/lib/active_record/associations/collection_association.rb +580 -0
  21. data/lib/active_record/associations/collection_proxy.rb +133 -0
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +39 -119
  23. data/lib/active_record/associations/has_many_association.rb +60 -79
  24. data/lib/active_record/associations/has_many_through_association.rb +127 -206
  25. data/lib/active_record/associations/has_one_association.rb +55 -114
  26. data/lib/active_record/associations/has_one_through_association.rb +25 -26
  27. data/lib/active_record/associations/join_dependency/join_association.rb +159 -0
  28. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  29. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  30. data/lib/active_record/associations/join_dependency.rb +214 -0
  31. data/lib/active_record/associations/join_helper.rb +55 -0
  32. data/lib/active_record/associations/preloader/association.rb +125 -0
  33. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  34. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  35. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  36. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  37. data/lib/active_record/associations/preloader/has_many_through.rb +15 -0
  38. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  39. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  40. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  41. data/lib/active_record/associations/preloader/through_association.rb +67 -0
  42. data/lib/active_record/associations/preloader.rb +181 -0
  43. data/lib/active_record/associations/singular_association.rb +64 -0
  44. data/lib/active_record/associations/through_association.rb +87 -0
  45. data/lib/active_record/associations.rb +693 -1337
  46. data/lib/active_record/attribute_assignment.rb +221 -0
  47. data/lib/active_record/attribute_methods/before_type_cast.rb +31 -0
  48. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
  49. data/lib/active_record/attribute_methods/dirty.rb +111 -0
  50. data/lib/active_record/attribute_methods/primary_key.rb +114 -0
  51. data/lib/active_record/attribute_methods/query.rb +39 -0
  52. data/lib/active_record/attribute_methods/read.rb +136 -0
  53. data/lib/active_record/attribute_methods/serialization.rb +120 -0
  54. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -0
  55. data/lib/active_record/attribute_methods/write.rb +70 -0
  56. data/lib/active_record/attribute_methods.rb +211 -339
  57. data/lib/active_record/autosave_association.rb +179 -149
  58. data/lib/active_record/base.rb +401 -2907
  59. data/lib/active_record/callbacks.rb +91 -176
  60. data/lib/active_record/coders/yaml_column.rb +41 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +236 -119
  62. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +110 -58
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +12 -11
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +175 -74
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -35
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +71 -21
  67. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +81 -311
  68. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +194 -78
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +130 -83
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +676 -0
  71. data/lib/active_record/connection_adapters/column.rb +296 -0
  72. data/lib/active_record/connection_adapters/mysql2_adapter.rb +280 -0
  73. data/lib/active_record/connection_adapters/mysql_adapter.rb +272 -493
  74. data/lib/active_record/connection_adapters/postgresql_adapter.rb +650 -405
  75. data/lib/active_record/connection_adapters/schema_cache.rb +69 -0
  76. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +30 -9
  77. data/lib/active_record/connection_adapters/sqlite_adapter.rb +276 -147
  78. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  79. data/lib/active_record/counter_cache.rb +123 -0
  80. data/lib/active_record/dynamic_finder_match.rb +41 -14
  81. data/lib/active_record/dynamic_matchers.rb +84 -0
  82. data/lib/active_record/dynamic_scope_match.rb +13 -15
  83. data/lib/active_record/errors.rb +195 -0
  84. data/lib/active_record/explain.rb +86 -0
  85. data/lib/active_record/explain_subscriber.rb +25 -0
  86. data/lib/active_record/fixtures/file.rb +65 -0
  87. data/lib/active_record/fixtures.rb +695 -770
  88. data/lib/active_record/identity_map.rb +162 -0
  89. data/lib/active_record/inheritance.rb +174 -0
  90. data/lib/active_record/integration.rb +60 -0
  91. data/lib/active_record/locale/en.yml +9 -27
  92. data/lib/active_record/locking/optimistic.rb +76 -73
  93. data/lib/active_record/locking/pessimistic.rb +32 -10
  94. data/lib/active_record/log_subscriber.rb +72 -0
  95. data/lib/active_record/migration/command_recorder.rb +105 -0
  96. data/lib/active_record/migration.rb +415 -205
  97. data/lib/active_record/model_schema.rb +368 -0
  98. data/lib/active_record/nested_attributes.rb +153 -63
  99. data/lib/active_record/observer.rb +27 -103
  100. data/lib/active_record/persistence.rb +376 -0
  101. data/lib/active_record/query_cache.rb +49 -8
  102. data/lib/active_record/querying.rb +58 -0
  103. data/lib/active_record/railtie.rb +131 -0
  104. data/lib/active_record/railties/console_sandbox.rb +6 -0
  105. data/lib/active_record/railties/controller_runtime.rb +49 -0
  106. data/lib/active_record/railties/databases.rake +659 -0
  107. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  108. data/lib/active_record/readonly_attributes.rb +26 -0
  109. data/lib/active_record/reflection.rb +269 -120
  110. data/lib/active_record/relation/batches.rb +90 -0
  111. data/lib/active_record/relation/calculations.rb +372 -0
  112. data/lib/active_record/relation/delegation.rb +49 -0
  113. data/lib/active_record/relation/finder_methods.rb +402 -0
  114. data/lib/active_record/relation/predicate_builder.rb +63 -0
  115. data/lib/active_record/relation/query_methods.rb +417 -0
  116. data/lib/active_record/relation/spawn_methods.rb +180 -0
  117. data/lib/active_record/relation.rb +537 -0
  118. data/lib/active_record/result.rb +40 -0
  119. data/lib/active_record/sanitization.rb +194 -0
  120. data/lib/active_record/schema.rb +9 -6
  121. data/lib/active_record/schema_dumper.rb +55 -32
  122. data/lib/active_record/scoping/default.rb +142 -0
  123. data/lib/active_record/scoping/named.rb +200 -0
  124. data/lib/active_record/scoping.rb +152 -0
  125. data/lib/active_record/serialization.rb +8 -91
  126. data/lib/active_record/serializers/xml_serializer.rb +43 -197
  127. data/lib/active_record/session_store.rb +129 -103
  128. data/lib/active_record/store.rb +52 -0
  129. data/lib/active_record/test_case.rb +30 -23
  130. data/lib/active_record/timestamp.rb +95 -52
  131. data/lib/active_record/transactions.rb +212 -66
  132. data/lib/active_record/translation.rb +22 -0
  133. data/lib/active_record/validations/associated.rb +43 -0
  134. data/lib/active_record/validations/uniqueness.rb +180 -0
  135. data/lib/active_record/validations.rb +43 -1106
  136. data/lib/active_record/version.rb +5 -4
  137. data/lib/active_record.rb +121 -48
  138. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  139. data/lib/rails/generators/active_record/migration/templates/migration.rb +34 -0
  140. data/lib/rails/generators/active_record/migration.rb +15 -0
  141. data/lib/rails/generators/active_record/model/model_generator.rb +47 -0
  142. data/lib/rails/generators/active_record/model/templates/migration.rb +15 -0
  143. data/lib/rails/generators/active_record/model/templates/model.rb +12 -0
  144. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  145. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  146. data/lib/rails/generators/active_record/observer/templates/observer.rb +4 -0
  147. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +25 -0
  148. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +12 -0
  149. data/lib/rails/generators/active_record.rb +25 -0
  150. metadata +187 -363
  151. data/CHANGELOG +0 -5904
  152. data/README +0 -351
  153. data/RUNNING_UNIT_TESTS +0 -36
  154. data/Rakefile +0 -268
  155. data/install.rb +0 -30
  156. data/lib/active_record/association_preload.rb +0 -406
  157. data/lib/active_record/associations/association_collection.rb +0 -533
  158. data/lib/active_record/associations/association_proxy.rb +0 -288
  159. data/lib/active_record/batches.rb +0 -85
  160. data/lib/active_record/calculations.rb +0 -321
  161. data/lib/active_record/dirty.rb +0 -183
  162. data/lib/active_record/named_scope.rb +0 -197
  163. data/lib/active_record/serializers/json_serializer.rb +0 -91
  164. data/lib/activerecord.rb +0 -2
  165. data/test/assets/example.log +0 -1
  166. data/test/assets/flowers.jpg +0 -0
  167. data/test/cases/aaa_create_tables_test.rb +0 -24
  168. data/test/cases/active_schema_test_mysql.rb +0 -122
  169. data/test/cases/active_schema_test_postgresql.rb +0 -24
  170. data/test/cases/adapter_test.rb +0 -144
  171. data/test/cases/aggregations_test.rb +0 -167
  172. data/test/cases/ar_schema_test.rb +0 -32
  173. data/test/cases/associations/belongs_to_associations_test.rb +0 -438
  174. data/test/cases/associations/callbacks_test.rb +0 -161
  175. data/test/cases/associations/cascaded_eager_loading_test.rb +0 -131
  176. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +0 -36
  177. data/test/cases/associations/eager_load_nested_include_test.rb +0 -131
  178. data/test/cases/associations/eager_load_nested_polymorphic_include.rb +0 -19
  179. data/test/cases/associations/eager_singularization_test.rb +0 -145
  180. data/test/cases/associations/eager_test.rb +0 -852
  181. data/test/cases/associations/extension_test.rb +0 -62
  182. data/test/cases/associations/habtm_join_table_test.rb +0 -56
  183. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +0 -827
  184. data/test/cases/associations/has_many_associations_test.rb +0 -1273
  185. data/test/cases/associations/has_many_through_associations_test.rb +0 -360
  186. data/test/cases/associations/has_one_associations_test.rb +0 -330
  187. data/test/cases/associations/has_one_through_associations_test.rb +0 -209
  188. data/test/cases/associations/inner_join_association_test.rb +0 -93
  189. data/test/cases/associations/inverse_associations_test.rb +0 -566
  190. data/test/cases/associations/join_model_test.rb +0 -712
  191. data/test/cases/associations_test.rb +0 -282
  192. data/test/cases/attribute_methods_test.rb +0 -305
  193. data/test/cases/autosave_association_test.rb +0 -1218
  194. data/test/cases/base_test.rb +0 -2166
  195. data/test/cases/batches_test.rb +0 -81
  196. data/test/cases/binary_test.rb +0 -30
  197. data/test/cases/calculations_test.rb +0 -360
  198. data/test/cases/callbacks_observers_test.rb +0 -38
  199. data/test/cases/callbacks_test.rb +0 -438
  200. data/test/cases/class_inheritable_attributes_test.rb +0 -32
  201. data/test/cases/column_alias_test.rb +0 -17
  202. data/test/cases/column_definition_test.rb +0 -70
  203. data/test/cases/connection_pool_test.rb +0 -25
  204. data/test/cases/connection_test_firebird.rb +0 -8
  205. data/test/cases/connection_test_mysql.rb +0 -65
  206. data/test/cases/copy_table_test_sqlite.rb +0 -80
  207. data/test/cases/counter_cache_test.rb +0 -84
  208. data/test/cases/database_statements_test.rb +0 -12
  209. data/test/cases/datatype_test_postgresql.rb +0 -204
  210. data/test/cases/date_time_test.rb +0 -37
  211. data/test/cases/default_test_firebird.rb +0 -16
  212. data/test/cases/defaults_test.rb +0 -111
  213. data/test/cases/deprecated_finder_test.rb +0 -30
  214. data/test/cases/dirty_test.rb +0 -316
  215. data/test/cases/finder_respond_to_test.rb +0 -76
  216. data/test/cases/finder_test.rb +0 -1098
  217. data/test/cases/fixtures_test.rb +0 -661
  218. data/test/cases/helper.rb +0 -68
  219. data/test/cases/i18n_test.rb +0 -46
  220. data/test/cases/inheritance_test.rb +0 -262
  221. data/test/cases/invalid_date_test.rb +0 -24
  222. data/test/cases/json_serialization_test.rb +0 -219
  223. data/test/cases/lifecycle_test.rb +0 -193
  224. data/test/cases/locking_test.rb +0 -350
  225. data/test/cases/method_scoping_test.rb +0 -704
  226. data/test/cases/migration_test.rb +0 -1649
  227. data/test/cases/migration_test_firebird.rb +0 -124
  228. data/test/cases/mixin_test.rb +0 -96
  229. data/test/cases/modules_test.rb +0 -109
  230. data/test/cases/multiple_db_test.rb +0 -85
  231. data/test/cases/named_scope_test.rb +0 -372
  232. data/test/cases/nested_attributes_test.rb +0 -840
  233. data/test/cases/pk_test.rb +0 -119
  234. data/test/cases/pooled_connections_test.rb +0 -103
  235. data/test/cases/query_cache_test.rb +0 -129
  236. data/test/cases/readonly_test.rb +0 -107
  237. data/test/cases/reflection_test.rb +0 -234
  238. data/test/cases/reload_models_test.rb +0 -22
  239. data/test/cases/repair_helper.rb +0 -50
  240. data/test/cases/reserved_word_test_mysql.rb +0 -176
  241. data/test/cases/sanitize_test.rb +0 -25
  242. data/test/cases/schema_authorization_test_postgresql.rb +0 -75
  243. data/test/cases/schema_dumper_test.rb +0 -211
  244. data/test/cases/schema_test_postgresql.rb +0 -178
  245. data/test/cases/serialization_test.rb +0 -47
  246. data/test/cases/sp_test_mysql.rb +0 -16
  247. data/test/cases/synonym_test_oracle.rb +0 -17
  248. data/test/cases/timestamp_test.rb +0 -75
  249. data/test/cases/transactions_test.rb +0 -543
  250. data/test/cases/unconnected_test.rb +0 -32
  251. data/test/cases/validations_i18n_test.rb +0 -925
  252. data/test/cases/validations_test.rb +0 -1684
  253. data/test/cases/xml_serialization_test.rb +0 -240
  254. data/test/cases/yaml_serialization_test.rb +0 -11
  255. data/test/config.rb +0 -5
  256. data/test/connections/jdbc_jdbcderby/connection.rb +0 -18
  257. data/test/connections/jdbc_jdbch2/connection.rb +0 -18
  258. data/test/connections/jdbc_jdbchsqldb/connection.rb +0 -18
  259. data/test/connections/jdbc_jdbcmysql/connection.rb +0 -26
  260. data/test/connections/jdbc_jdbcpostgresql/connection.rb +0 -26
  261. data/test/connections/jdbc_jdbcsqlite3/connection.rb +0 -25
  262. data/test/connections/native_db2/connection.rb +0 -25
  263. data/test/connections/native_firebird/connection.rb +0 -26
  264. data/test/connections/native_frontbase/connection.rb +0 -27
  265. data/test/connections/native_mysql/connection.rb +0 -25
  266. data/test/connections/native_openbase/connection.rb +0 -21
  267. data/test/connections/native_oracle/connection.rb +0 -27
  268. data/test/connections/native_postgresql/connection.rb +0 -21
  269. data/test/connections/native_sqlite/connection.rb +0 -25
  270. data/test/connections/native_sqlite3/connection.rb +0 -25
  271. data/test/connections/native_sqlite3/in_memory_connection.rb +0 -18
  272. data/test/connections/native_sybase/connection.rb +0 -23
  273. data/test/fixtures/accounts.yml +0 -29
  274. data/test/fixtures/all/developers.yml +0 -0
  275. data/test/fixtures/all/people.csv +0 -0
  276. data/test/fixtures/all/tasks.yml +0 -0
  277. data/test/fixtures/author_addresses.yml +0 -5
  278. data/test/fixtures/author_favorites.yml +0 -4
  279. data/test/fixtures/authors.yml +0 -9
  280. data/test/fixtures/binaries.yml +0 -132
  281. data/test/fixtures/books.yml +0 -7
  282. data/test/fixtures/categories/special_categories.yml +0 -9
  283. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +0 -4
  284. data/test/fixtures/categories.yml +0 -14
  285. data/test/fixtures/categories_ordered.yml +0 -7
  286. data/test/fixtures/categories_posts.yml +0 -23
  287. data/test/fixtures/categorizations.yml +0 -17
  288. data/test/fixtures/clubs.yml +0 -6
  289. data/test/fixtures/comments.yml +0 -59
  290. data/test/fixtures/companies.yml +0 -56
  291. data/test/fixtures/computers.yml +0 -4
  292. data/test/fixtures/courses.yml +0 -7
  293. data/test/fixtures/customers.yml +0 -26
  294. data/test/fixtures/developers.yml +0 -21
  295. data/test/fixtures/developers_projects.yml +0 -17
  296. data/test/fixtures/edges.yml +0 -6
  297. data/test/fixtures/entrants.yml +0 -14
  298. data/test/fixtures/faces.yml +0 -11
  299. data/test/fixtures/fk_test_has_fk.yml +0 -3
  300. data/test/fixtures/fk_test_has_pk.yml +0 -2
  301. data/test/fixtures/funny_jokes.yml +0 -10
  302. data/test/fixtures/interests.yml +0 -33
  303. data/test/fixtures/items.yml +0 -4
  304. data/test/fixtures/jobs.yml +0 -7
  305. data/test/fixtures/legacy_things.yml +0 -3
  306. data/test/fixtures/mateys.yml +0 -4
  307. data/test/fixtures/member_types.yml +0 -6
  308. data/test/fixtures/members.yml +0 -6
  309. data/test/fixtures/memberships.yml +0 -20
  310. data/test/fixtures/men.yml +0 -5
  311. data/test/fixtures/minimalistics.yml +0 -2
  312. data/test/fixtures/mixed_case_monkeys.yml +0 -6
  313. data/test/fixtures/mixins.yml +0 -29
  314. data/test/fixtures/movies.yml +0 -7
  315. data/test/fixtures/naked/csv/accounts.csv +0 -1
  316. data/test/fixtures/naked/yml/accounts.yml +0 -1
  317. data/test/fixtures/naked/yml/companies.yml +0 -1
  318. data/test/fixtures/naked/yml/courses.yml +0 -1
  319. data/test/fixtures/organizations.yml +0 -5
  320. data/test/fixtures/owners.yml +0 -7
  321. data/test/fixtures/parrots.yml +0 -27
  322. data/test/fixtures/parrots_pirates.yml +0 -7
  323. data/test/fixtures/people.yml +0 -15
  324. data/test/fixtures/pets.yml +0 -14
  325. data/test/fixtures/pirates.yml +0 -9
  326. data/test/fixtures/polymorphic_designs.yml +0 -19
  327. data/test/fixtures/polymorphic_prices.yml +0 -19
  328. data/test/fixtures/posts.yml +0 -52
  329. data/test/fixtures/price_estimates.yml +0 -7
  330. data/test/fixtures/projects.yml +0 -7
  331. data/test/fixtures/readers.yml +0 -9
  332. data/test/fixtures/references.yml +0 -17
  333. data/test/fixtures/reserved_words/distinct.yml +0 -5
  334. data/test/fixtures/reserved_words/distincts_selects.yml +0 -11
  335. data/test/fixtures/reserved_words/group.yml +0 -14
  336. data/test/fixtures/reserved_words/select.yml +0 -8
  337. data/test/fixtures/reserved_words/values.yml +0 -7
  338. data/test/fixtures/ships.yml +0 -5
  339. data/test/fixtures/sponsors.yml +0 -9
  340. data/test/fixtures/subscribers.yml +0 -7
  341. data/test/fixtures/subscriptions.yml +0 -12
  342. data/test/fixtures/taggings.yml +0 -28
  343. data/test/fixtures/tags.yml +0 -7
  344. data/test/fixtures/tasks.yml +0 -7
  345. data/test/fixtures/tees.yml +0 -4
  346. data/test/fixtures/ties.yml +0 -4
  347. data/test/fixtures/topics.yml +0 -42
  348. data/test/fixtures/toys.yml +0 -4
  349. data/test/fixtures/treasures.yml +0 -10
  350. data/test/fixtures/vertices.yml +0 -4
  351. data/test/fixtures/warehouse-things.yml +0 -3
  352. data/test/fixtures/zines.yml +0 -5
  353. data/test/migrations/broken/100_migration_that_raises_exception.rb +0 -10
  354. data/test/migrations/decimal/1_give_me_big_numbers.rb +0 -15
  355. data/test/migrations/duplicate/1_people_have_last_names.rb +0 -9
  356. data/test/migrations/duplicate/2_we_need_reminders.rb +0 -12
  357. data/test/migrations/duplicate/3_foo.rb +0 -7
  358. data/test/migrations/duplicate/3_innocent_jointable.rb +0 -12
  359. data/test/migrations/duplicate_names/20080507052938_chunky.rb +0 -7
  360. data/test/migrations/duplicate_names/20080507053028_chunky.rb +0 -7
  361. data/test/migrations/interleaved/pass_1/3_innocent_jointable.rb +0 -12
  362. data/test/migrations/interleaved/pass_2/1_people_have_last_names.rb +0 -9
  363. data/test/migrations/interleaved/pass_2/3_innocent_jointable.rb +0 -12
  364. data/test/migrations/interleaved/pass_3/1_people_have_last_names.rb +0 -9
  365. data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +0 -8
  366. data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +0 -12
  367. data/test/migrations/missing/1000_people_have_middle_names.rb +0 -9
  368. data/test/migrations/missing/1_people_have_last_names.rb +0 -9
  369. data/test/migrations/missing/3_we_need_reminders.rb +0 -12
  370. data/test/migrations/missing/4_innocent_jointable.rb +0 -12
  371. data/test/migrations/valid/1_people_have_last_names.rb +0 -9
  372. data/test/migrations/valid/2_we_need_reminders.rb +0 -12
  373. data/test/migrations/valid/3_innocent_jointable.rb +0 -12
  374. data/test/models/author.rb +0 -151
  375. data/test/models/auto_id.rb +0 -4
  376. data/test/models/binary.rb +0 -2
  377. data/test/models/bird.rb +0 -9
  378. data/test/models/book.rb +0 -4
  379. data/test/models/categorization.rb +0 -5
  380. data/test/models/category.rb +0 -34
  381. data/test/models/citation.rb +0 -6
  382. data/test/models/club.rb +0 -13
  383. data/test/models/column_name.rb +0 -3
  384. data/test/models/comment.rb +0 -29
  385. data/test/models/company.rb +0 -173
  386. data/test/models/company_in_module.rb +0 -78
  387. data/test/models/computer.rb +0 -3
  388. data/test/models/contact.rb +0 -16
  389. data/test/models/contract.rb +0 -5
  390. data/test/models/course.rb +0 -3
  391. data/test/models/customer.rb +0 -73
  392. data/test/models/default.rb +0 -2
  393. data/test/models/developer.rb +0 -101
  394. data/test/models/edge.rb +0 -5
  395. data/test/models/entrant.rb +0 -3
  396. data/test/models/essay.rb +0 -3
  397. data/test/models/event.rb +0 -3
  398. data/test/models/event_author.rb +0 -8
  399. data/test/models/face.rb +0 -7
  400. data/test/models/guid.rb +0 -2
  401. data/test/models/interest.rb +0 -5
  402. data/test/models/invoice.rb +0 -4
  403. data/test/models/item.rb +0 -7
  404. data/test/models/job.rb +0 -5
  405. data/test/models/joke.rb +0 -3
  406. data/test/models/keyboard.rb +0 -3
  407. data/test/models/legacy_thing.rb +0 -3
  408. data/test/models/line_item.rb +0 -3
  409. data/test/models/man.rb +0 -9
  410. data/test/models/matey.rb +0 -4
  411. data/test/models/member.rb +0 -12
  412. data/test/models/member_detail.rb +0 -5
  413. data/test/models/member_type.rb +0 -3
  414. data/test/models/membership.rb +0 -9
  415. data/test/models/minimalistic.rb +0 -2
  416. data/test/models/mixed_case_monkey.rb +0 -3
  417. data/test/models/movie.rb +0 -5
  418. data/test/models/order.rb +0 -4
  419. data/test/models/organization.rb +0 -6
  420. data/test/models/owner.rb +0 -5
  421. data/test/models/parrot.rb +0 -22
  422. data/test/models/person.rb +0 -16
  423. data/test/models/pet.rb +0 -5
  424. data/test/models/pirate.rb +0 -80
  425. data/test/models/polymorphic_design.rb +0 -3
  426. data/test/models/polymorphic_price.rb +0 -3
  427. data/test/models/post.rb +0 -102
  428. data/test/models/price_estimate.rb +0 -3
  429. data/test/models/project.rb +0 -30
  430. data/test/models/reader.rb +0 -4
  431. data/test/models/reference.rb +0 -4
  432. data/test/models/reply.rb +0 -46
  433. data/test/models/ship.rb +0 -19
  434. data/test/models/ship_part.rb +0 -7
  435. data/test/models/sponsor.rb +0 -4
  436. data/test/models/subject.rb +0 -4
  437. data/test/models/subscriber.rb +0 -8
  438. data/test/models/subscription.rb +0 -4
  439. data/test/models/tag.rb +0 -7
  440. data/test/models/tagging.rb +0 -10
  441. data/test/models/task.rb +0 -3
  442. data/test/models/tee.rb +0 -4
  443. data/test/models/tie.rb +0 -4
  444. data/test/models/topic.rb +0 -80
  445. data/test/models/toy.rb +0 -6
  446. data/test/models/treasure.rb +0 -8
  447. data/test/models/vertex.rb +0 -9
  448. data/test/models/warehouse_thing.rb +0 -5
  449. data/test/models/zine.rb +0 -3
  450. data/test/schema/mysql_specific_schema.rb +0 -31
  451. data/test/schema/postgresql_specific_schema.rb +0 -114
  452. data/test/schema/schema.rb +0 -550
  453. data/test/schema/schema2.rb +0 -6
  454. data/test/schema/sqlite_specific_schema.rb +0 -25
@@ -1,264 +1,185 @@
1
+ require 'active_support/core_ext/object/blank'
2
+
1
3
  module ActiveRecord
4
+ # = Active Record Has Many Through Association
2
5
  module Associations
3
6
  class HasManyThroughAssociation < HasManyAssociation #:nodoc:
4
- alias_method :new, :build
7
+ include ThroughAssociation
5
8
 
6
- def create!(attrs = nil)
7
- transaction do
8
- self << (object = attrs ? @reflection.klass.send(:with_scope, :create => attrs) { @reflection.create_association! } : @reflection.create_association!)
9
- object
10
- end
11
- end
9
+ def initialize(owner, reflection)
10
+ super
12
11
 
13
- def create(attrs = nil)
14
- transaction do
15
- object = if attrs
16
- @reflection.klass.send(:with_scope, :create => attrs) {
17
- @reflection.create_association
18
- }
19
- else
20
- @reflection.create_association
21
- end
22
- raise_on_type_mismatch(object)
23
- add_record_to_target_with_callbacks(object) do |r|
24
- insert_record(object, false)
25
- end
26
- object
27
- end
12
+ @through_records = {}
13
+ @through_association = nil
28
14
  end
29
15
 
30
- # Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and
31
- # calling collection.size if it has. If it's more likely than not that the collection does have a size larger than zero,
32
- # and you need to fetch that collection afterwards, it'll take one fewer SELECT query if you use #length.
16
+ # Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn't been
17
+ # loaded and calling collection.size if it has. If it's more likely than not that the collection does
18
+ # have a size larger than zero, and you need to fetch that collection afterwards, it'll take one fewer
19
+ # SELECT query if you use #length.
33
20
  def size
34
- return @owner.send(:read_attribute, cached_counter_attribute_name) if has_cached_counter?
35
- return @target.size if loaded?
36
- return count
37
- end
38
-
39
- protected
40
- def target_reflection_has_associated_record?
41
- if @reflection.through_reflection.macro == :belongs_to && @owner[@reflection.through_reflection.primary_key_name].blank?
42
- false
43
- else
44
- true
45
- end
21
+ if has_cached_counter?
22
+ owner.send(:read_attribute, cached_counter_attribute_name)
23
+ elsif loaded?
24
+ target.size
25
+ else
26
+ count
46
27
  end
28
+ end
47
29
 
48
- def construct_find_options!(options)
49
- options[:select] = construct_select(options[:select])
50
- options[:from] ||= construct_from
51
- options[:joins] = construct_joins(options[:joins])
52
- options[:include] = @reflection.source_reflection.options[:include] if options[:include].nil? && @reflection.source_reflection.options[:include]
53
- end
54
-
55
- def insert_record(record, force = true, validate = true)
56
- if record.new_record?
57
- if force
58
- record.save!
59
- else
60
- return false unless record.save(validate)
61
- end
30
+ def concat(*records)
31
+ unless owner.new_record?
32
+ records.flatten.each do |record|
33
+ raise_on_type_mismatch(record)
34
+ record.save! if record.new_record?
62
35
  end
63
- through_reflection = @reflection.through_reflection
64
- klass = through_reflection.klass
65
- @owner.send(@reflection.through_reflection.name).proxy_target << klass.send(:with_scope, :create => construct_join_attributes(record)) { through_reflection.create_association! }
66
36
  end
67
37
 
68
- # TODO - add dependent option support
69
- def delete_records(records)
70
- klass = @reflection.through_reflection.klass
71
- records.each do |associate|
72
- klass.delete_all(construct_join_attributes(associate))
73
- end
74
- end
38
+ super
39
+ end
75
40
 
76
- def find_target
77
- return [] unless target_reflection_has_associated_record?
78
- @reflection.klass.find(:all,
79
- :select => construct_select,
80
- :conditions => construct_conditions,
81
- :from => construct_from,
82
- :joins => construct_joins,
83
- :order => @reflection.options[:order],
84
- :limit => @reflection.options[:limit],
85
- :group => @reflection.options[:group],
86
- :readonly => @reflection.options[:readonly],
87
- :include => @reflection.options[:include] || @reflection.source_reflection.options[:include]
88
- )
89
- end
41
+ def insert_record(record, validate = true, raise = false)
42
+ ensure_not_nested
90
43
 
91
- # Construct attributes for associate pointing to owner.
92
- def construct_owner_attributes(reflection)
93
- if as = reflection.options[:as]
94
- { "#{as}_id" => @owner.id,
95
- "#{as}_type" => @owner.class.base_class.name.to_s }
44
+ if record.new_record?
45
+ if raise
46
+ record.save!(:validate => validate)
96
47
  else
97
- { reflection.primary_key_name => @owner.id }
48
+ return unless record.save(:validate => validate)
98
49
  end
99
50
  end
100
51
 
101
- # Construct attributes for :through pointing to owner and associate.
102
- def construct_join_attributes(associate)
103
- # TODO: revist this to allow it for deletion, supposing dependent option is supported
104
- raise ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection.new(@owner, @reflection) if [:has_one, :has_many].include?(@reflection.source_reflection.macro)
105
- join_attributes = construct_owner_attributes(@reflection.through_reflection).merge(@reflection.source_reflection.primary_key_name => associate.id)
106
- if @reflection.options[:source_type]
107
- join_attributes.merge!(@reflection.source_reflection.options[:foreign_type] => associate.class.base_class.name.to_s)
108
- end
109
- join_attributes
110
- end
52
+ save_through_record(record)
53
+ update_counter(1)
54
+ record
55
+ end
111
56
 
112
- # Associate attributes pointing to owner, quoted.
113
- def construct_quoted_owner_attributes(reflection)
114
- if as = reflection.options[:as]
115
- { "#{as}_id" => owner_quoted_id,
116
- "#{as}_type" => reflection.klass.quote_value(
117
- @owner.class.base_class.name.to_s,
118
- reflection.klass.columns_hash["#{as}_type"]) }
119
- elsif reflection.macro == :belongs_to
120
- { reflection.klass.primary_key => @owner[reflection.primary_key_name] }
121
- else
122
- { reflection.primary_key_name => owner_quoted_id }
123
- end
124
- end
57
+ # ActiveRecord::Relation#delete_all needs to support joins before we can use a
58
+ # SQL-only implementation.
59
+ alias delete_all_on_destroy delete_all
125
60
 
126
- # Build SQL conditions from attributes, qualified by table name.
127
- def construct_conditions
128
- table_name = @reflection.through_reflection.quoted_table_name
129
- conditions = construct_quoted_owner_attributes(@reflection.through_reflection).map do |attr, value|
130
- "#{table_name}.#{attr} = #{value}"
131
- end
132
- conditions << sql_conditions if sql_conditions
133
- "(" + conditions.join(') AND (') + ")"
134
- end
61
+ private
135
62
 
136
- def construct_from
137
- @reflection.quoted_table_name
63
+ def through_association
64
+ @through_association ||= owner.association(through_reflection.name)
138
65
  end
139
66
 
140
- def construct_select(custom_select = nil)
141
- distinct = "DISTINCT " if @reflection.options[:uniq]
142
- selected = custom_select || @reflection.options[:select] || "#{distinct}#{@reflection.quoted_table_name}.*"
143
- end
67
+ # We temporarily cache through record that has been build, because if we build a
68
+ # through record in build_record and then subsequently call insert_record, then we
69
+ # want to use the exact same object.
70
+ #
71
+ # However, after insert_record has been called, we clear the cache entry because
72
+ # we want it to be possible to have multiple instances of the same record in an
73
+ # association
74
+ def build_through_record(record)
75
+ @through_records[record.object_id] ||= begin
76
+ ensure_mutable
144
77
 
145
- def construct_joins(custom_joins = nil)
146
- polymorphic_join = nil
147
- if @reflection.source_reflection.macro == :belongs_to
148
- reflection_primary_key = @reflection.klass.primary_key
149
- source_primary_key = @reflection.source_reflection.primary_key_name
150
- if @reflection.options[:source_type]
151
- polymorphic_join = "AND %s.%s = %s" % [
152
- @reflection.through_reflection.quoted_table_name, "#{@reflection.source_reflection.options[:foreign_type]}",
153
- @owner.class.quote_value(@reflection.options[:source_type])
154
- ]
155
- end
156
- else
157
- reflection_primary_key = @reflection.source_reflection.primary_key_name
158
- source_primary_key = @reflection.through_reflection.klass.primary_key
159
- if @reflection.source_reflection.options[:as]
160
- polymorphic_join = "AND %s.%s = %s" % [
161
- @reflection.quoted_table_name, "#{@reflection.source_reflection.options[:as]}_type",
162
- @owner.class.quote_value(@reflection.through_reflection.klass.name)
163
- ]
164
- end
78
+ through_record = through_association.build
79
+ through_record.send("#{source_reflection.name}=", record)
80
+ through_record
165
81
  end
166
-
167
- "INNER JOIN %s ON %s.%s = %s.%s %s #{@reflection.options[:joins]} #{custom_joins}" % [
168
- @reflection.through_reflection.quoted_table_name,
169
- @reflection.quoted_table_name, reflection_primary_key,
170
- @reflection.through_reflection.quoted_table_name, source_primary_key,
171
- polymorphic_join
172
- ]
173
82
  end
174
83
 
175
- def construct_scope
176
- { :create => construct_owner_attributes(@reflection),
177
- :find => { :from => construct_from,
178
- :conditions => construct_conditions,
179
- :joins => construct_joins,
180
- :include => @reflection.options[:include],
181
- :select => construct_select,
182
- :order => @reflection.options[:order],
183
- :limit => @reflection.options[:limit],
184
- :readonly => @reflection.options[:readonly],
185
- } }
84
+ def save_through_record(record)
85
+ build_through_record(record).save!
86
+ ensure
87
+ @through_records.delete(record.object_id)
186
88
  end
187
89
 
188
- def construct_sql
189
- case
190
- when @reflection.options[:finder_sql]
191
- @finder_sql = interpolate_sql(@reflection.options[:finder_sql])
90
+ def build_record(attributes, options = {})
91
+ ensure_not_nested
192
92
 
193
- @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}"
194
- @finder_sql << " AND (#{conditions})" if conditions
195
- else
196
- @finder_sql = construct_conditions
93
+ record = super(attributes, options)
94
+
95
+ inverse = source_reflection.inverse_of
96
+ if inverse
97
+ if inverse.macro == :has_many
98
+ record.send(inverse.name) << build_through_record(record)
99
+ elsif inverse.macro == :has_one
100
+ record.send("#{inverse.name}=", build_through_record(record))
101
+ end
197
102
  end
198
103
 
199
- if @reflection.options[:counter_sql]
200
- @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
201
- elsif @reflection.options[:finder_sql]
202
- # replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
203
- @reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT (\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
204
- @counter_sql = interpolate_sql(@reflection.options[:counter_sql])
104
+ record
105
+ end
106
+
107
+ def target_reflection_has_associated_record?
108
+ if through_reflection.macro == :belongs_to && owner[through_reflection.foreign_key].blank?
109
+ false
205
110
  else
206
- @counter_sql = @finder_sql
111
+ true
207
112
  end
208
113
  end
209
114
 
210
- def conditions
211
- @conditions = build_conditions unless defined?(@conditions)
212
- @conditions
115
+ def update_through_counter?(method)
116
+ case method
117
+ when :destroy
118
+ !inverse_updates_counter_cache?(through_reflection)
119
+ when :nullify
120
+ false
121
+ else
122
+ true
123
+ end
213
124
  end
214
125
 
215
- def build_conditions
216
- association_conditions = @reflection.options[:conditions]
217
- through_conditions = build_through_conditions
218
- source_conditions = @reflection.source_reflection.options[:conditions]
219
- uses_sti = !@reflection.through_reflection.klass.descends_from_active_record?
126
+ def delete_records(records, method)
127
+ ensure_not_nested
220
128
 
221
- if association_conditions || through_conditions || source_conditions || uses_sti
222
- all = []
129
+ scope = through_association.scoped.where(construct_join_attributes(*records))
223
130
 
224
- [association_conditions, source_conditions].each do |conditions|
225
- all << interpolate_sql(sanitize_sql(conditions)) if conditions
226
- end
131
+ case method
132
+ when :destroy
133
+ count = scope.destroy_all.length
134
+ when :nullify
135
+ count = scope.update_all(source_reflection.foreign_key => nil)
136
+ else
137
+ count = scope.delete_all
138
+ end
227
139
 
228
- all << through_conditions if through_conditions
229
- all << build_sti_condition if uses_sti
140
+ delete_through_records(records)
230
141
 
231
- all.map { |sql| "(#{sql})" } * ' AND '
142
+ if source_reflection.options[:counter_cache]
143
+ counter = source_reflection.counter_cache_column
144
+ klass.decrement_counter counter, records.map(&:id)
232
145
  end
233
- end
234
146
 
235
- def build_through_conditions
236
- conditions = @reflection.through_reflection.options[:conditions]
237
- if conditions.is_a?(Hash)
238
- interpolate_sql(sanitize_sql(conditions)).gsub(
239
- @reflection.quoted_table_name,
240
- @reflection.through_reflection.quoted_table_name)
241
- elsif conditions
242
- interpolate_sql(sanitize_sql(conditions))
147
+ if through_reflection.macro == :has_many && update_through_counter?(method)
148
+ update_counter(-count, through_reflection)
243
149
  end
150
+
151
+ update_counter(-count)
244
152
  end
245
-
246
- def build_sti_condition
247
- @reflection.through_reflection.klass.send(:type_condition)
153
+
154
+ def through_records_for(record)
155
+ attributes = construct_join_attributes(record)
156
+ candidates = Array.wrap(through_association.target)
157
+ candidates.find_all { |c| c.attributes.slice(*attributes.keys) == attributes }
248
158
  end
249
159
 
250
- alias_method :sql_conditions, :conditions
160
+ def delete_through_records(records)
161
+ records.each do |record|
162
+ through_records = through_records_for(record)
163
+
164
+ if through_reflection.macro == :has_many
165
+ through_records.each { |r| through_association.target.delete(r) }
166
+ else
167
+ if through_records.include?(through_association.target)
168
+ through_association.target = nil
169
+ end
170
+ end
251
171
 
252
- def has_cached_counter?
253
- @owner.attribute_present?(cached_counter_attribute_name)
172
+ @through_records.delete(record.object_id)
173
+ end
254
174
  end
255
175
 
256
- def cached_counter_attribute_name
257
- "#{@reflection.name}_count"
176
+ def find_target
177
+ return [] unless target_reflection_has_associated_record?
178
+ scoped.all
258
179
  end
259
180
 
260
181
  # NOTE - not sure that we can actually cope with inverses here
261
- def we_can_set_the_inverse_on_this?(record)
182
+ def invertible_for?(record)
262
183
  false
263
184
  end
264
185
  end
@@ -1,141 +1,82 @@
1
+ require 'active_support/core_ext/object/inclusion'
2
+
1
3
  module ActiveRecord
4
+ # = Active Record Belongs To Has One Association
2
5
  module Associations
3
- class HasOneAssociation < BelongsToAssociation #:nodoc:
4
- def initialize(owner, reflection)
5
- super
6
- construct_sql
7
- end
6
+ class HasOneAssociation < SingularAssociation #:nodoc:
7
+ def replace(record, save = true)
8
+ raise_on_type_mismatch(record) if record
9
+ load_target
8
10
 
9
- def create(attrs = {}, replace_existing = true)
10
- new_record(replace_existing) do |reflection|
11
- attrs = merge_with_conditions(attrs)
12
- reflection.create_association(attrs)
11
+ # If target and record are nil, or target is equal to record,
12
+ # we don't need to have transaction.
13
+ if (target || record) && target != record
14
+ transaction_if(save) do
15
+ remove_target!(options[:dependent]) if target && !target.destroyed?
16
+
17
+ if record
18
+ set_owner_attributes(record)
19
+ set_inverse_instance(record)
20
+
21
+ if owner.persisted? && save && !record.save
22
+ nullify_owner_attributes(record)
23
+ set_owner_attributes(target) if target
24
+ raise RecordNotSaved, "Failed to save the new associated #{reflection.name}."
25
+ end
26
+ end
27
+ end
13
28
  end
14
- end
15
29
 
16
- def create!(attrs = {}, replace_existing = true)
17
- new_record(replace_existing) do |reflection|
18
- attrs = merge_with_conditions(attrs)
19
- reflection.create_association!(attrs)
20
- end
30
+ self.target = record
21
31
  end
22
32
 
23
- def build(attrs = {}, replace_existing = true)
24
- new_record(replace_existing) do |reflection|
25
- attrs = merge_with_conditions(attrs)
26
- reflection.build_association(attrs)
27
- end
28
- end
29
-
30
- def replace(obj, dont_save = false)
31
- load_target
32
-
33
- unless @target.nil? || @target == obj
34
- if dependent? && !dont_save
35
- case @reflection.options[:dependent]
33
+ def delete(method = options[:dependent])
34
+ if load_target
35
+ case method
36
36
  when :delete
37
- @target.delete unless @target.new_record?
38
- @owner.clear_association_cache
37
+ target.delete
39
38
  when :destroy
40
- @target.destroy unless @target.new_record?
41
- @owner.clear_association_cache
39
+ target.destroy
42
40
  when :nullify
43
- @target[@reflection.primary_key_name] = nil
44
- @target.save unless @owner.new_record? || @target.new_record?
45
- end
46
- else
47
- @target[@reflection.primary_key_name] = nil
48
- @target.save unless @owner.new_record? || @target.new_record?
41
+ target.update_attribute(reflection.foreign_key, nil)
49
42
  end
50
43
  end
51
-
52
- if obj.nil?
53
- @target = nil
54
- else
55
- raise_on_type_mismatch(obj)
56
- set_belongs_to_association_for(obj)
57
- @target = (AssociationProxy === obj ? obj.target : obj)
58
- end
59
-
60
- set_inverse_instance(obj, @owner)
61
- @loaded = true
62
-
63
- unless @owner.new_record? or obj.nil? or dont_save
64
- return (obj.save ? self : false)
65
- else
66
- return (obj.nil? ? nil : self)
67
- end
68
44
  end
69
45
 
70
- protected
71
- def owner_quoted_id
72
- if @reflection.options[:primary_key]
73
- @owner.class.quote_value(@owner.send(@reflection.options[:primary_key]))
74
- else
75
- @owner.quoted_id
76
- end
77
- end
78
-
79
46
  private
80
- def find_target
81
- the_target = @reflection.klass.find(:first,
82
- :conditions => @finder_sql,
83
- :select => @reflection.options[:select],
84
- :order => @reflection.options[:order],
85
- :include => @reflection.options[:include],
86
- :readonly => @reflection.options[:readonly]
87
- )
88
- set_inverse_instance(the_target, @owner)
89
- the_target
90
- end
91
47
 
92
- def construct_sql
93
- case
94
- when @reflection.options[:as]
95
- @finder_sql =
96
- "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{owner_quoted_id} AND " +
97
- "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}"
98
- else
99
- @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}"
100
- end
101
- @finder_sql << " AND (#{conditions})" if conditions
48
+ # The reason that the save param for replace is false, if for create (not just build),
49
+ # is because the setting of the foreign keys is actually handled by the scoping when
50
+ # the record is instantiated, and so they are set straight away and do not need to be
51
+ # updated within replace.
52
+ def set_new_record(record)
53
+ replace(record, false)
102
54
  end
103
-
104
- def construct_scope
105
- create_scoping = {}
106
- set_belongs_to_association_for(create_scoping)
107
- { :create => create_scoping }
108
- end
109
-
110
- def new_record(replace_existing)
111
- # Make sure we load the target first, if we plan on replacing the existing
112
- # instance. Otherwise, if the target has not previously been loaded
113
- # elsewhere, the instance we create will get orphaned.
114
- load_target if replace_existing
115
- record = @reflection.klass.send(:with_scope, :create => construct_scope[:create]) do
116
- yield @reflection
117
- end
118
55
 
119
- if replace_existing
120
- replace(record, true)
56
+ def remove_target!(method)
57
+ if method.in?([:delete, :destroy])
58
+ target.send(method)
121
59
  else
122
- record[@reflection.primary_key_name] = @owner.id unless @owner.new_record?
123
- self.target = record
124
- set_inverse_instance(record, @owner)
125
- end
60
+ nullify_owner_attributes(target)
126
61
 
127
- record
62
+ if target.persisted? && owner.persisted? && !target.save
63
+ set_owner_attributes(target)
64
+ raise RecordNotSaved, "Failed to remove the existing associated #{reflection.name}. " +
65
+ "The record failed to save when after its foreign key was set to nil."
66
+ end
67
+ end
128
68
  end
129
69
 
130
- def merge_with_conditions(attrs={})
131
- attrs ||= {}
132
- attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash)
133
- attrs
70
+ def nullify_owner_attributes(record)
71
+ record[reflection.foreign_key] = nil
134
72
  end
135
73
 
136
- def we_can_set_the_inverse_on_this?(record)
137
- inverse = @reflection.inverse_of
138
- return !inverse.nil?
74
+ def transaction_if(value)
75
+ if value
76
+ reflection.klass.transaction { yield }
77
+ else
78
+ yield
79
+ end
139
80
  end
140
81
  end
141
82
  end
@@ -1,37 +1,36 @@
1
1
  module ActiveRecord
2
+ # = Active Record Has One Through Association
2
3
  module Associations
3
- class HasOneThroughAssociation < HasManyThroughAssociation
4
+ class HasOneThroughAssociation < HasOneAssociation #:nodoc:
5
+ include ThroughAssociation
4
6
 
5
- def create_through_record(new_value) #nodoc:
6
- klass = @reflection.through_reflection.klass
7
+ def replace(record)
8
+ create_through_record(record)
9
+ self.target = record
10
+ end
7
11
 
8
- current_object = @owner.send(@reflection.through_reflection.name)
12
+ private
9
13
 
10
- if current_object
11
- new_value ? current_object.update_attributes(construct_join_attributes(new_value)) : current_object.destroy
12
- elsif new_value
13
- if @owner.new_record?
14
- self.target = new_value
15
- through_association = @owner.send(:association_instance_get, @reflection.through_reflection.name)
16
- through_association.build(construct_join_attributes(new_value))
17
- else
18
- @owner.send(@reflection.through_reflection.name, klass.create(construct_join_attributes(new_value)))
19
- end
20
- end
21
- end
14
+ def create_through_record(record)
15
+ ensure_not_nested
22
16
 
23
- private
24
- def find(*args)
25
- super(args.merge(:limit => 1))
26
- end
17
+ through_proxy = owner.association(through_reflection.name)
18
+ through_record = through_proxy.send(:load_target)
27
19
 
28
- def find_target
29
- super.first
30
- end
20
+ if through_record && !record
21
+ through_record.destroy
22
+ elsif record
23
+ attributes = construct_join_attributes(record)
31
24
 
32
- def reset_target!
33
- @target = nil
34
- end
25
+ if through_record
26
+ through_record.update_attributes(attributes)
27
+ elsif owner.new_record?
28
+ through_proxy.build(attributes)
29
+ else
30
+ through_proxy.create(attributes)
31
+ end
32
+ end
33
+ end
35
34
  end
36
35
  end
37
36
  end