Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

upgrading activerecord and activesupport

  • Loading branch information...
commit 9c6a164c86ca4bfd97367523620ee94b50038dd7 1 parent d87d99e
Aaron Bedra authored
Showing with 9,868 additions and 5,038 deletions.
  1. +36 −3 vendor/activerecord/CHANGELOG
  2. 0  vendor/activerecord/README
  3. +4 −5 vendor/activerecord/Rakefile
  4. +11 −10 vendor/activerecord/lib/active_record.rb
  5. +110 −38 vendor/activerecord/lib/active_record/aggregations.rb
  6. +116 −24 vendor/activerecord/lib/active_record/association_preload.rb
  7. +431 −216 vendor/activerecord/lib/active_record/associations.rb
  8. +101 −16 vendor/activerecord/lib/active_record/associations/association_collection.rb
  9. +65 −13 vendor/activerecord/lib/active_record/associations/association_proxy.rb
  10. +2 −2 vendor/activerecord/lib/active_record/associations/belongs_to_association.rb
  11. 0  vendor/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb
  12. +13 −3 vendor/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
  13. +28 −28 vendor/activerecord/lib/active_record/associations/has_many_association.rb
  14. +21 −19 vendor/activerecord/lib/active_record/associations/has_many_through_association.rb
  15. +24 −7 vendor/activerecord/lib/active_record/associations/has_one_association.rb
  16. +3 −4 vendor/activerecord/lib/active_record/associations/has_one_through_association.rb
  17. +13 −5 vendor/activerecord/lib/active_record/attribute_methods.rb
  18. +436 −213 vendor/activerecord/lib/active_record/base.rb
  19. +19 −8 vendor/activerecord/lib/active_record/calculations.rb
  20. +28 −9 vendor/activerecord/lib/active_record/callbacks.rb
  21. +355 −0 vendor/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
  22. +42 −215 vendor/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
  23. +30 −5 vendor/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
  24. +2 −1  vendor/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
  25. +48 −7 vendor/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
  26. +10 −4 vendor/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
  27. +67 −26 vendor/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
  28. +71 −45 vendor/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
  29. +155 −84 vendor/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
  30. +25 −7 vendor/activerecord/lib/active_record/dirty.rb
  31. +41 −0 vendor/activerecord/lib/active_record/dynamic_finder_match.rb
  32. +10 −9 vendor/activerecord/lib/active_record/fixtures.rb
  33. +26 −0 vendor/activerecord/lib/active_record/i18n_interpolation_deprecation.rb
  34. +54 −0 vendor/activerecord/lib/active_record/locale/en.yml
  35. +47 −10 vendor/activerecord/lib/active_record/migration.rb
  36. +29 −16 vendor/activerecord/lib/active_record/named_scope.rb
  37. +122 −54 vendor/activerecord/lib/active_record/reflection.rb
  38. +13 −7 vendor/activerecord/lib/active_record/schema_dumper.rb
  39. +18 −5 vendor/activerecord/lib/active_record/test_case.rb
  40. +89 −34 vendor/activerecord/lib/active_record/transactions.rb
  41. +271 −181 vendor/activerecord/lib/active_record/validations.rb
  42. +0 −1,214 vendor/activerecord/lib/active_record/vendor/mysql.rb
  43. +2 −2 vendor/activerecord/lib/active_record/version.rb
  44. +5 −0 vendor/activerecord/test/cases/active_schema_test_mysql.rb
  45. +6 −0 vendor/activerecord/test/cases/adapter_test.rb
  46. +0 −95 vendor/activerecord/test/cases/adapter_test_sqlserver.rb
  47. +39 −0 vendor/activerecord/test/cases/aggregations_test.rb
  48. +10 −0 vendor/activerecord/test/cases/associations/belongs_to_associations_test.rb
  49. +30 −12 vendor/activerecord/test/cases/associations/eager_load_nested_include_test.rb
  50. +61 −5 vendor/activerecord/test/cases/associations/eager_test.rb
  51. +15 −0 vendor/activerecord/test/cases/associations/extension_test.rb
  52. +77 −10 vendor/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
  53. +74 −7 vendor/activerecord/test/cases/associations/has_many_associations_test.rb
  54. +50 −3 vendor/activerecord/test/cases/associations/has_many_through_associations_test.rb
  55. +17 −0 vendor/activerecord/test/cases/associations/has_one_associations_test.rb
  56. +49 −1 vendor/activerecord/test/cases/associations/has_one_through_associations_test.rb
  57. 0  vendor/activerecord/test/cases/associations_test.rb
  58. +59 −4 vendor/activerecord/test/cases/attribute_methods_test.rb
  59. +111 −21 vendor/activerecord/test/cases/base_test.rb
  60. +1 −5 vendor/activerecord/test/cases/binary_test.rb
  61. +8 −3 vendor/activerecord/test/cases/calculations_test.rb
  62. +38 −0 vendor/activerecord/test/cases/callbacks_observers_test.rb
  63. +1 −1  vendor/activerecord/test/cases/connection_test_mysql.rb
  64. +32 −1 vendor/activerecord/test/cases/defaults_test.rb
  65. 0  vendor/activerecord/test/cases/deprecated_finder_test.rb
  66. +13 −0 vendor/activerecord/test/cases/dirty_test.rb
  67. +164 −6 vendor/activerecord/test/cases/finder_test.rb
  68. +32 −3 vendor/activerecord/test/cases/fixtures_test.rb
  69. +15 −0 vendor/activerecord/test/cases/helper.rb
  70. +41 −0 vendor/activerecord/test/cases/i18n_test.rb
  71. +3 −3 vendor/activerecord/test/cases/inheritance_test.rb
  72. 0  vendor/activerecord/test/cases/lifecycle_test.rb
  73. +4 −9 vendor/activerecord/test/cases/locking_test.rb
  74. +109 −2 vendor/activerecord/test/cases/method_scoping_test.rb
  75. +43 −8 vendor/activerecord/test/cases/migration_test.rb
  76. +25 −0 vendor/activerecord/test/cases/multiple_db_test.rb
  77. +74 −0 vendor/activerecord/test/cases/named_scope_test.rb
  78. +103 −0 vendor/activerecord/test/cases/pooled_connections_test.rb
  79. 0  vendor/activerecord/test/cases/readonly_test.rb
  80. +11 −3 vendor/activerecord/test/cases/reflection_test.rb
  81. +20 −0 vendor/activerecord/test/cases/reload_models_test.rb
  82. +25 −0 vendor/activerecord/test/cases/sanitize_test.rb
  83. +2 −2 vendor/activerecord/test/cases/schema_authorization_test_postgresql.rb
  84. +0 −23 vendor/activerecord/test/cases/table_name_test_sqlserver.rb
  85. +0 −48 vendor/activerecord/test/cases/threaded_connections_test.rb
  86. +62 −12 vendor/activerecord/test/cases/transactions_test.rb
  87. 0  vendor/activerecord/test/cases/unconnected_test.rb
  88. +921 −0 vendor/activerecord/test/cases/validations_i18n_test.rb
  89. +51 −33 vendor/activerecord/test/cases/validations_test.rb
  90. +1 −3 vendor/activerecord/test/connections/native_mysql/connection.rb
  91. +0 −358 vendor/activerecord/test/debug.log
  92. +1 −0  vendor/activerecord/test/fixtures/companies.yml
  93. +10 −1 vendor/activerecord/test/fixtures/customers.yml
  94. BIN  vendor/activerecord/test/fixtures/fixture_database.sqlite3
  95. BIN  vendor/activerecord/test/fixtures/fixture_database_2.sqlite3
  96. +5 −0 vendor/activerecord/test/fixtures/organizations.yml
  97. +10 −0 vendor/activerecord/test/migrations/broken/100_migration_that_raises_exception.rb
  98. +3 −0  vendor/activerecord/test/models/author.rb
  99. +3 −0  vendor/activerecord/test/models/category.rb
  100. +6 −0 vendor/activerecord/test/models/club.rb
  101. +25 −1 vendor/activerecord/test/models/company.rb
  102. +19 −1 vendor/activerecord/test/models/customer.rb
  103. +2 −0  vendor/activerecord/test/models/member.rb
  104. +4 −0 vendor/activerecord/test/models/member_detail.rb
  105. +4 −0 vendor/activerecord/test/models/organization.rb
  106. +1 −0  vendor/activerecord/test/models/parrot.rb
  107. +3 −0  vendor/activerecord/test/models/post.rb
  108. 0  vendor/activerecord/test/models/reply.rb
  109. +3 −0  vendor/activerecord/test/models/topic.rb
  110. +12 −1 vendor/activerecord/test/schema/schema.rb
  111. +0 −5 vendor/activerecord/test/schema/sqlserver_specific_schema.rb
  112. +68 −4 vendor/activesupport/CHANGELOG
  113. +6 −6 vendor/activesupport/lib/active_support.rb
  114. +13 −2 vendor/activesupport/lib/active_support/base64.rb
  115. +1 −1  vendor/activesupport/lib/active_support/basic_object.rb
  116. +15 −14 vendor/activesupport/lib/active_support/buffered_logger.rb
  117. +116 −40 vendor/activesupport/lib/active_support/cache.rb
  118. +7 −2 vendor/activesupport/lib/active_support/cache/compressed_mem_cache_store.rb
  119. +7 −5 vendor/activesupport/lib/active_support/cache/file_store.rb
  120. +48 −21 vendor/activesupport/lib/active_support/cache/mem_cache_store.rb
  121. +16 −2 vendor/activesupport/lib/active_support/cache/memory_store.rb
  122. +47 −0 vendor/activesupport/lib/active_support/cache/synchronized_memory_store.rb
  123. +14 −9 vendor/activesupport/lib/active_support/callbacks.rb
  124. +0 −127 vendor/activesupport/lib/active_support/clean_logger.rb
  125. +27 −0 vendor/activesupport/lib/active_support/core_ext/array/access.rb
  126. +12 −7 vendor/activesupport/lib/active_support/core_ext/array/conversions.rb
  127. +53 −10 vendor/activesupport/lib/active_support/core_ext/array/grouping.rb
  128. +3 −0  vendor/activesupport/lib/active_support/core_ext/base64/encoding.rb
  129. +18 −22 vendor/activesupport/lib/active_support/core_ext/bigdecimal/conversions.rb
  130. +5 −0 vendor/activesupport/lib/active_support/core_ext/blank.rb
  131. +5 −5 vendor/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb
  132. +3 −0  vendor/activesupport/lib/active_support/core_ext/date/behavior.rb
  133. +24 −9 vendor/activesupport/lib/active_support/core_ext/date/calculations.rb
  134. +19 −5 vendor/activesupport/lib/active_support/core_ext/date_time/calculations.rb
  135. +6 −0 vendor/activesupport/lib/active_support/core_ext/duplicable.rb
  136. +42 −4 vendor/activesupport/lib/active_support/core_ext/enumerable.rb
  137. +4 −20 vendor/activesupport/lib/active_support/core_ext/file.rb
  138. +46 −0 vendor/activesupport/lib/active_support/core_ext/file/atomic.rb
  139. +2 −0  vendor/activesupport/lib/active_support/core_ext/float.rb
  140. +27 −0 vendor/activesupport/lib/active_support/core_ext/float/time.rb
  141. +2 −1  vendor/activesupport/lib/active_support/core_ext/hash.rb
  142. +1 −1  vendor/activesupport/lib/active_support/core_ext/hash/conversions.rb
  143. +23 −0 vendor/activesupport/lib/active_support/core_ext/hash/deep_merge.rb
  144. +2 −2 vendor/activesupport/lib/active_support/core_ext/hash/except.rb
  145. +12 −5 vendor/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb
  146. +8 −5 vendor/activesupport/lib/active_support/core_ext/hash/slice.rb
  147. +2 −0  vendor/activesupport/lib/active_support/core_ext/integer.rb
  148. +45 −0 vendor/activesupport/lib/active_support/core_ext/integer/time.rb
  149. +4 −4 vendor/activesupport/lib/active_support/core_ext/kernel/debugger.rb
  150. +128 −1 vendor/activesupport/lib/active_support/core_ext/logger.rb
  151. +11 −1 vendor/activesupport/lib/active_support/core_ext/module.rb
  152. +71 −67 vendor/activesupport/lib/active_support/core_ext/module/aliasing.rb
  153. +32 −1 vendor/activesupport/lib/active_support/core_ext/module/delegation.rb
  154. +81 −66 vendor/activesupport/lib/active_support/core_ext/module/introspection.rb
  155. +8 −7 vendor/activesupport/lib/active_support/core_ext/module/model_naming.rb
  156. +39 −0 vendor/activesupport/lib/active_support/core_ext/module/synchronization.rb
  157. +0 −10 vendor/activesupport/lib/active_support/core_ext/numeric/time.rb
  158. +1 −0  vendor/activesupport/lib/active_support/core_ext/object.rb
  159. +33 −12 vendor/activesupport/lib/active_support/core_ext/object/extending.rb
  160. +1 −1  vendor/activesupport/lib/active_support/core_ext/object/instance_variables.rb
  161. +13 −0 vendor/activesupport/lib/active_support/core_ext/object/metaclass.rb
  162. +36 −21 vendor/activesupport/lib/active_support/core_ext/object/misc.rb
  163. +2 −1  vendor/activesupport/lib/active_support/core_ext/rexml.rb
  164. +6 −2 vendor/activesupport/lib/active_support/core_ext/string.rb
  165. +5 −5 vendor/activesupport/lib/active_support/core_ext/string/access.rb
  166. +13 −0 vendor/activesupport/lib/active_support/core_ext/string/behavior.rb
  167. +21 −2 vendor/activesupport/lib/active_support/core_ext/string/inflections.rb
  168. +81 −0 vendor/activesupport/lib/active_support/core_ext/string/multibyte.rb
  169. +0 −66 vendor/activesupport/lib/active_support/core_ext/string/unicode.rb
  170. +0 −1  vendor/activesupport/lib/active_support/core_ext/test.rb
  171. +39 −12 vendor/activesupport/lib/active_support/core_ext/time/calculations.rb
  172. +1 −0  vendor/activesupport/lib/active_support/core_ext/time/conversions.rb
  173. +1 −1  vendor/activesupport/lib/active_support/core_ext/time/zones.rb
  174. +178 −110 vendor/activesupport/lib/active_support/dependencies.rb
  175. +24 −4 vendor/activesupport/lib/active_support/deprecation.rb
  176. +122 −38 vendor/activesupport/lib/active_support/inflector.rb
  177. +1 −1  vendor/activesupport/lib/active_support/json.rb
  178. +9 −2 vendor/activesupport/lib/active_support/json/encoders/date.rb
  179. +9 −2 vendor/activesupport/lib/active_support/json/encoders/date_time.rb
  180. +10 −3 vendor/activesupport/lib/active_support/json/encoders/time.rb
  181. +32 −0 vendor/activesupport/lib/active_support/locale/en.yml
  182. +82 −0 vendor/activesupport/lib/active_support/memoizable.rb
  183. +31 −7 vendor/activesupport/lib/active_support/multibyte.rb
  184. +664 −122 vendor/activesupport/lib/active_support/multibyte/chars.rb
  185. +8 −0 vendor/activesupport/lib/active_support/multibyte/exceptions.rb
  186. +0 −149 vendor/activesupport/lib/active_support/multibyte/generators/generate_tables.rb
  187. +0 −9 vendor/activesupport/lib/active_support/multibyte/handlers/passthru_handler.rb
  188. +0 −564 vendor/activesupport/lib/active_support/multibyte/handlers/utf8_handler.rb
  189. +0 −43 vendor/activesupport/lib/active_support/multibyte/handlers/utf8_handler_proc.rb
  190. +71 −0 vendor/activesupport/lib/active_support/multibyte/unicode_database.rb
  191. +2 −10 vendor/activesupport/lib/active_support/option_merger.rb
  192. +15 −0 vendor/activesupport/lib/active_support/ordered_hash.rb
  193. +108 −0 vendor/activesupport/lib/active_support/rescuable.rb
  194. +197 −0 vendor/activesupport/lib/active_support/secure_random.rb
  195. +11 −1 vendor/activesupport/lib/active_support/string_inquirer.rb
  196. +16 −5 vendor/activesupport/lib/active_support/test_case.rb
  197. +6 −0 vendor/activesupport/lib/active_support/testing/core_ext/test.rb
  198. +14 −6 vendor/activesupport/lib/active_support/{ → testing}/core_ext/test/unit/assertions.rb
  199. +452 −0 vendor/activesupport/lib/active_support/testing/performance.rb
  200. +34 −7 vendor/activesupport/lib/active_support/testing/setup_and_teardown.rb
  201. +69 −45 vendor/activesupport/lib/active_support/time_with_zone.rb
  202. +12 −5 vendor/activesupport/lib/active_support/values/time_zone.rb
  203. BIN  vendor/activesupport/lib/active_support/values/unicode_tables.dat
  204. +13 −5 vendor/activesupport/lib/active_support/vendor.rb
  205. +1 −1  vendor/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb
  206. +194 −0 vendor/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n.rb
  207. +216 −0 vendor/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb
  208. +53 −0 vendor/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n/exceptions.rb
  209. +5 −5 ...or/activesupport/lib/active_support/vendor/{memcache-client-1.5.0 → memcache-client-1.5.1}/memcache.rb
  210. 0  vendor/activesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo.rb
  211. 0  vendor/activesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/data_timezone.rb
  212. 0  ...or/activesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/data_timezone_info.rb
  213. 0  ...esupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Africa/Algiers.rb
  214. 0  ...ivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Africa/Cairo.rb
  215. +1 −1  ...pport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Africa/Casablanca.rb
  216. 0  ...vesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Africa/Harare.rb
  217. 0  ...ort/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Africa/Johannesburg.rb
  218. 0  ...support/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Africa/Monrovia.rb
  219. 0  ...esupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Africa/Nairobi.rb
  220. +42 −42 ...ive_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/America/Argentina/Buenos_Aires.rb
  221. +86 −0 vendor/activesupport/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Argentina/San_Juan.rb
  222. 0  ...esupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/America/Bogota.rb
  223. 0  ...support/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/America/Caracas.rb
  224. 0  ...support/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/America/Chicago.rb
  225. 0  ...pport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/America/Chihuahua.rb
  226. 0  ...esupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/America/Denver.rb
  227. 0  ...support/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/America/Godthab.rb
  228. 0  ...pport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/America/Guatemala.rb
  229. 0  ...support/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/America/Halifax.rb
  230. 0  ...ctive_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/America/Indiana/Indianapolis.rb
  231. 0  ...esupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/America/Juneau.rb
  232. 0  ...esupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/America/La_Paz.rb
  233. 0  ...ivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/America/Lima.rb
  234. 0  ...ort/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/America/Los_Angeles.rb
  235. 0  ...upport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/America/Mazatlan.rb
  236. 0  ...ort/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/America/Mexico_City.rb
  237. 0  ...pport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/America/Monterrey.rb
  238. 0  ...upport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/America/New_York.rb
  239. 0  ...support/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/America/Phoenix.rb
  240. 0  ...esupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/America/Regina.rb
  241. 0  ...upport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/America/Santiago.rb
  242. +171 −0 vendor/activesupport/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Sao_Paulo.rb
  243. 0  ...upport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/America/St_Johns.rb
  244. 0  ...support/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/America/Tijuana.rb
  245. 0  ...tivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Almaty.rb
  246. 0  ...ivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Baghdad.rb
  247. 0  ...activesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Baku.rb
  248. 0  ...ivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Bangkok.rb
  249. 0  ...esupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Chongqing.rb
  250. +30 −0 vendor/activesupport/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Colombo.rb
  251. 0  ...ctivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Dhaka.rb
  252. 0  ...esupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Hong_Kong.rb
  253. 0  ...ivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Irkutsk.rb
  254. 0  ...ivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Jakarta.rb
  255. 0  ...esupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Jerusalem.rb
  256. 0  ...ctivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Kabul.rb
  257. 0  ...esupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Kamchatka.rb
  258. +1 −1  ...ivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Karachi.rb
  259. 0  ...vesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Katmandu.rb
  260. 0  ...ivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Kolkata.rb
  261. 0  ...upport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Krasnoyarsk.rb
  262. 0  ...pport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Kuala_Lumpur.rb
  263. 0  ...tivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Kuwait.rb
  264. 0  ...ivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Magadan.rb
  265. 0  ...tivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Muscat.rb
  266. 0  ...upport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Novosibirsk.rb
  267. 0  ...ivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Rangoon.rb
  268. 0  ...tivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Riyadh.rb
  269. 0  ...ctivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Seoul.rb
  270. 0  ...vesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Shanghai.rb
  271. 0  ...esupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Singapore.rb
  272. 0  ...tivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Taipei.rb
  273. 0  ...vesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Tashkent.rb
  274. 0  ...ivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Tbilisi.rb
  275. 0  ...tivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Tehran.rb
  276. 0  ...ctivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Tokyo.rb
  277. 0  ...upport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Ulaanbaatar.rb
  278. 0  ...tivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Urumqi.rb
  279. 0  ...upport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Vladivostok.rb
  280. 0  ...ivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Yakutsk.rb
  281. 0  ...port/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Yekaterinburg.rb
  282. 0  ...ivesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Yerevan.rb
  283. 0  ...support/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Atlantic/Azores.rb
  284. 0  ...ort/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Atlantic/Cape_Verde.rb
  285. 0  .../lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Atlantic/South_Georgia.rb
  286. 0  ...port/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Australia/Adelaide.rb
  287. 0  ...port/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Australia/Brisbane.rb
  288. 0  ...upport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Australia/Darwin.rb
  289. 0  ...upport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Australia/Hobart.rb
  290. 0  ...ort/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Australia/Melbourne.rb
  291. 0  ...support/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Australia/Perth.rb
  292. 0  ...upport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Australia/Sydney.rb
  293. 0  ...r/activesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Etc/UTC.rb
  294. 0  ...upport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Amsterdam.rb
  295. 0  ...vesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Athens.rb
  296. 0  ...support/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Belgrade.rb
  297. +1 −1  ...vesupport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Berlin.rb
  298. 0  ...pport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Bratislava.rb
  299. 0  ...support/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Brussels.rb
  300. 0  ...upport/lib/active_support/vendor/{tzinfo-0.3.9 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Bucharest.rb
Sorry, we could not display the entire diff because too many files (396) changed.
View
39 vendor/activerecord/CHANGELOG
@@ -1,16 +1,49 @@
-*2.1.1 (September 4th, 2008)*
+*2.2 (November 21st, 2008)*
-* Set config.active_record.timestamped_migrations = false to have migrations with numeric prefix instead of UTC timestamp. #446. [Andrew Stone, Nik Wakelin]
+* Ensure indices don't flip order in schema.rb #1266 [Jordi Bunster]
-* Fixed that create database statements would always include "DEFAULT NULL" (Nick Sieger) [#334]
+* Fixed that serialized strings should never be type-casted (i.e. turning "Yes" to a boolean) #857 [Andreas Korth]
+
+* Skip collection ids reader optimization if using :finder_sql [Jeremy Kemper]
+
+* Add Model#delete instance method, similar to Model.delete class method. #1086 [Hongli Lai]
+
+* MySQL: cope with quirky default values for not-null text columns. #1043 [Frederick Cheung]
+
+* Multiparameter attributes skip time zone conversion for time-only columns [#1030 state:resolved] [Geoff Buesing]
+
+* Base.skip_time_zone_conversion_for_attributes uses class_inheritable_accessor, so that subclasses don't overwrite Base [#346 state:resolved] [miloops]
+
+* Added find_last_by dynamic finder #762 [miloops]
+
+* Internal API: configurable association options and build_association method for reflections so plugins may extend and override. #985 [Hongli Lai]
+
+* Changed benchmarks to be reported in milliseconds [DHH]
+
+* Connection pooling. #936 [Nick Sieger]
+
+* Merge scoped :joins together instead of overwriting them. May expose scoping bugs in your code! #501 [Andrew White]
+
+* before_save, before_validation and before_destroy callbacks that return false will now ROLLBACK the transaction. Previously this would have been committed before the processing was aborted. #891 [Xavier Noria]
+
+* Transactional migrations for databases which support them. #834 [divoxx, Adam Wiggins, Tarmo Tänav]
+
+* Set config.active_record.timestamped_migrations = false to have migrations with numeric prefix instead of UTC timestamp. #446. [Andrew Stone, Nik Wakelin]
* change_column_default preserves the not-null constraint. #617 [Tarmo Tänav]
+* Fixed that create database statements would always include "DEFAULT NULL" (Nick Sieger) [#334]
+
* Add :tokenizer option to validates_length_of to specify how to split up the attribute string. #507. [David Lowenfels] Example :
# Ensure essay contains at least 100 words.
validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least %d words."), :tokenizer => lambda {|str| str.scan(/\w+/) }
+* Allow conditions on multiple tables to be specified using hash. [Pratik Naik]. Example:
+
+ User.all :joins => :items, :conditions => { :age => 10, :items => { :color => 'black' } }
+ Item.first :conditions => { :items => { :color => 'red' } }
+
* Always treat integer :limit as byte length. #420 [Tarmo Tänav]
* Partial updates don't update lock_version if nothing changed. #426 [Daniel Morrison]
View
0  vendor/activerecord/README 100755 → 100644
File mode changed
View
9 vendor/activerecord/Rakefile 100755 → 100644
@@ -5,7 +5,6 @@ require 'rake/rdoctask'
require 'rake/packagetask'
require 'rake/gempackagetask'
require 'rake/contrib/sshpublisher'
-require 'rake/contrib/rubyforgepublisher'
require File.join(File.dirname(__FILE__), 'lib', 'active_record', 'version')
require File.expand_path(File.dirname(__FILE__)) + "/test/config"
@@ -31,7 +30,7 @@ desc 'Run mysql, sqlite, and postgresql tests by default'
task :default => :test
desc 'Run mysql, sqlite, and postgresql tests'
-task :test => %w(test_mysql test_sqlite test_sqlite3 test_postgresql)
+task :test => %w(test_mysql test_sqlite3 test_postgresql)
for adapter in %w( mysql postgresql sqlite sqlite3 firebird db2 oracle sybase openbase frontbase )
Rake::TestTask.new("test_#{adapter}") { |t|
@@ -172,7 +171,7 @@ spec = Gem::Specification.new do |s|
s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
end
- s.add_dependency('activesupport', '= 2.1.1' + PKG_BUILD)
+ s.add_dependency('activesupport', '= 2.2.2' + PKG_BUILD)
s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite"
s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite"
@@ -226,8 +225,8 @@ end
desc "Publish the beta gem"
task :pgem => [:package] do
- Rake::SshFilePublisher.new("david@greed.loudthinking.com", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
- `ssh david@greed.loudthinking.com '/u/sites/gems/gemupdate.sh'`
+ Rake::SshFilePublisher.new("gems.rubyonrails.org", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
+ `ssh gems.rubyonrails.org '/u/sites/gems/gemupdate.sh'`
end
desc "Publish the API documentation"
View
21 vendor/activerecord/lib/active_record.rb 100755 → 100644
@@ -21,17 +21,14 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
-$:.unshift(File.dirname(__FILE__)) unless
- $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
-
-active_support_path = File.dirname(__FILE__) + "/../../activesupport/lib"
-if File.exist?(active_support_path)
- $:.unshift active_support_path
- require 'active_support'
-else
- require 'rubygems'
- gem 'activesupport'
+begin
require 'active_support'
+rescue LoadError
+ activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
+ if File.directory?(activesupport_path)
+ $:.unshift activesupport_path
+ require 'active_support'
+ end
end
require 'active_record/base'
@@ -54,6 +51,7 @@
require 'active_record/serialization'
require 'active_record/attribute_methods'
require 'active_record/dirty'
+require 'active_record/dynamic_finder_match'
ActiveRecord::Base.class_eval do
extend ActiveRecord::QueryCache
@@ -78,3 +76,6 @@
require 'active_record/connection_adapters/abstract_adapter'
require 'active_record/schema_dumper'
+
+require 'active_record/i18n_interpolation_deprecation'
+I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
View
148 vendor/activerecord/lib/active_record/aggregations.rb
@@ -10,10 +10,10 @@ def clear_aggregation_cache #:nodoc:
end unless self.new_record?
end
- # Active Record implements aggregation through a macro-like class method called +composed_of+ for representing attributes
+ # Active Record implements aggregation through a macro-like class method called +composed_of+ for representing attributes
# as value objects. It expresses relationships like "Account [is] composed of Money [among other things]" or "Person [is]
- # composed of [an] address". Each call to the macro adds a description of how the value objects are created from the
- # attributes of the entity object (when the entity is initialized either as a new object or from finding an existing object)
+ # composed of [an] address". Each call to the macro adds a description of how the value objects are created from the
+ # attributes of the entity object (when the entity is initialized either as a new object or from finding an existing object)
# and how it can be turned back into attributes (when the entity is saved to the database). Example:
#
# class Customer < ActiveRecord::Base
@@ -30,10 +30,10 @@ def clear_aggregation_cache #:nodoc:
# class Money
# include Comparable
# attr_reader :amount, :currency
- # EXCHANGE_RATES = { "USD_TO_DKK" => 6 }
- #
- # def initialize(amount, currency = "USD")
- # @amount, @currency = amount, currency
+ # EXCHANGE_RATES = { "USD_TO_DKK" => 6 }
+ #
+ # def initialize(amount, currency = "USD")
+ # @amount, @currency = amount, currency
# end
#
# def exchange_to(other_currency)
@@ -56,26 +56,26 @@ def clear_aggregation_cache #:nodoc:
#
# class Address
# attr_reader :street, :city
- # def initialize(street, city)
- # @street, @city = street, city
+ # def initialize(street, city)
+ # @street, @city = street, city
# end
#
- # def close_to?(other_address)
- # city == other_address.city
+ # def close_to?(other_address)
+ # city == other_address.city
# end
#
# def ==(other_address)
# city == other_address.city && street == other_address.street
# end
# end
- #
+ #
# Now it's possible to access attributes from the database through the value objects instead. If you choose to name the
# composition the same as the attribute's name, it will be the only way to access that attribute. That's the case with our
# +balance+ attribute. You interact with the value objects just like you would any other attribute, though:
#
# customer.balance = Money.new(20) # sets the Money value object and the attribute
# customer.balance # => Money value object
- # customer.balance.exchanged_to("DKK") # => Money.new(120, "DKK")
+ # customer.balance.exchange_to("DKK") # => Money.new(120, "DKK")
# customer.balance > Money.new(10) # => true
# customer.balance == Money.new(20) # => true
# customer.balance < Money.new(5) # => false
@@ -87,8 +87,8 @@ def clear_aggregation_cache #:nodoc:
# customer.address_city = "Copenhagen"
# customer.address # => Address.new("Hyancintvej", "Copenhagen")
# customer.address = Address.new("May Street", "Chicago")
- # customer.address_street # => "May Street"
- # customer.address_city # => "Chicago"
+ # customer.address_street # => "May Street"
+ # customer.address_city # => "Chicago"
#
# == Writing value objects
#
@@ -99,16 +99,55 @@ def clear_aggregation_cache #:nodoc:
# relational unique identifiers (such as primary keys). Normal ActiveRecord::Base classes are entity objects.
#
# It's also important to treat the value objects as immutable. Don't allow the Money object to have its amount changed after
- # creation. Create a new Money object with the new value instead. This is exemplified by the Money#exchanged_to method that
+ # creation. Create a new Money object with the new value instead. This is exemplified by the Money#exchange_to method that
# returns a new value object instead of changing its own values. Active Record won't persist value objects that have been
# changed through means other than the writer method.
#
- # The immutable requirement is enforced by Active Record by freezing any object assigned as a value object. Attempting to
+ # The immutable requirement is enforced by Active Record by freezing any object assigned as a value object. Attempting to
# change it afterwards will result in a ActiveSupport::FrozenObjectError.
- #
+ #
# Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not keeping value objects
# immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
#
+ # == Custom constructors and converters
+ #
+ # By default value objects are initialized by calling the <tt>new</tt> constructor of the value class passing each of the
+ # mapped attributes, in the order specified by the <tt>:mapping</tt> option, as arguments. If the value class doesn't support
+ # this convention then +composed_of+ allows a custom constructor to be specified.
+ #
+ # When a new value is assigned to the value object the default assumption is that the new value is an instance of the value
+ # class. Specifying a custom converter allows the new value to be automatically converted to an instance of value class if
+ # necessary.
+ #
+ # For example, the NetworkResource model has +network_address+ and +cidr_range+ attributes that should be aggregated using the
+ # NetAddr::CIDR value class (http://netaddr.rubyforge.org). The constructor for the value class is called +create+ and it
+ # expects a CIDR address string as a parameter. New values can be assigned to the value object using either another
+ # NetAddr::CIDR object, a string or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to
+ # meet these requirements:
+ #
+ # class NetworkResource < ActiveRecord::Base
+ # composed_of :cidr,
+ # :class_name => 'NetAddr::CIDR',
+ # :mapping => [ %w(network_address network), %w(cidr_range bits) ],
+ # :allow_nil => true,
+ # :constructor => Proc.new { |network_address, cidr_range| NetAddr::CIDR.create("#{network_address}/#{cidr_range}") },
+ # :converter => Proc.new { |value| NetAddr::CIDR.create(value.is_a?(Array) ? value.join('/') : value) }
+ # end
+ #
+ # # This calls the :constructor
+ # network_resource = NetworkResource.new(:network_address => '192.168.0.1', :cidr_range => 24)
+ #
+ # # These assignments will both use the :converter
+ # network_resource.cidr = [ '192.168.2.1', 8 ]
+ # network_resource.cidr = '192.168.0.1/24'
+ #
+ # # This assignment won't use the :converter as the value is already an instance of the value class
+ # network_resource.cidr = NetAddr::CIDR.create('192.168.2.1/8')
+ #
+ # # Saving and then reloading will use the :constructor on reload
+ # network_resource.save
+ # network_resource.reload
+ #
# == Finding records by a value object
#
# Once a +composed_of+ relationship is specified for a model, records can be loaded from the database by specifying an instance
@@ -122,47 +161,71 @@ module ClassMethods
# <tt>composed_of :address</tt> adds <tt>address</tt> and <tt>address=(new_address)</tt> methods.
#
# Options are:
- # * <tt>:class_name</tt> - specify the class name of the association. Use it only if that name can't be inferred
+ # * <tt>:class_name</tt> - Specifies the class name of the association. Use it only if that name can't be inferred
# from the part id. So <tt>composed_of :address</tt> will by default be linked to the Address class, but
# if the real class name is CompanyAddress, you'll have to specify it with this option.
- # * <tt>:mapping</tt> - specifies a number of mapping arrays (attribute, parameter) that bind an attribute name
- # to a constructor parameter on the value class.
- # * <tt>:allow_nil</tt> - specifies that the aggregate object will not be instantiated when all mapped
- # attributes are +nil+. Setting the aggregate class to +nil+ has the effect of writing +nil+ to all mapped attributes.
+ # * <tt>:mapping</tt> - Specifies the mapping of entity attributes to attributes of the value object. Each mapping
+ # is represented as an array where the first item is the name of the entity attribute and the second item is the
+ # name the attribute in the value object. The order in which mappings are defined determine the order in which
+ # attributes are sent to the value class constructor.
+ # * <tt>:allow_nil</tt> - Specifies that the value object will not be instantiated when all mapped
+ # attributes are +nil+. Setting the value object to +nil+ has the effect of writing +nil+ to all mapped attributes.
# This defaults to +false+.
- #
- # An optional block can be passed to convert the argument that is passed to the writer method into an instance of
- # <tt>:class_name</tt>. The block will only be called if the argument is not already an instance of <tt>:class_name</tt>.
+ # * <tt>:constructor</tt> - A symbol specifying the name of the constructor method or a Proc that is called to
+ # initialize the value object. The constructor is passed all of the mapped attributes, in the order that they
+ # are defined in the <tt>:mapping option</tt>, as arguments and uses them to instantiate a <tt>:class_name</tt> object.
+ # The default is <tt>:new</tt>.
+ # * <tt>:converter</tt> - A symbol specifying the name of a class method of <tt>:class_name</tt> or a Proc that is
+ # called when a new value is assigned to the value object. The converter is passed the single value that is used
+ # in the assignment and is only called if the new value is not an instance of <tt>:class_name</tt>.
#
# Option examples:
# composed_of :temperature, :mapping => %w(reading celsius)
- # composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount)) {|balance| balance.to_money }
+ # composed_of :balance, :class_name => "Money", :mapping => %w(balance amount), :converter => Proc.new { |balance| balance.to_money }
# composed_of :address, :mapping => [ %w(address_street street), %w(address_city city) ]
# composed_of :gps_location
# composed_of :gps_location, :allow_nil => true
+ # composed_of :ip_address,
+ # :class_name => 'IPAddr',
+ # :mapping => %w(ip to_i),
+ # :constructor => Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) },
+ # :converter => Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) }
#
def composed_of(part_id, options = {}, &block)
- options.assert_valid_keys(:class_name, :mapping, :allow_nil)
+ options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter)
name = part_id.id2name
- class_name = options[:class_name] || name.camelize
- mapping = options[:mapping] || [ name, name ]
+ class_name = options[:class_name] || name.camelize
+ mapping = options[:mapping] || [ name, name ]
mapping = [ mapping ] unless mapping.first.is_a?(Array)
- allow_nil = options[:allow_nil] || false
+ allow_nil = options[:allow_nil] || false
+ constructor = options[:constructor] || :new
+ converter = options[:converter] || block
+
+ ActiveSupport::Deprecation.warn('The conversion block has been deprecated, use the :converter option instead.', caller) if block_given?
+
+ reader_method(name, class_name, mapping, allow_nil, constructor)
+ writer_method(name, class_name, mapping, allow_nil, converter)
- reader_method(name, class_name, mapping, allow_nil)
- writer_method(name, class_name, mapping, allow_nil, block)
-
create_reflection(:composed_of, part_id, options, self)
end
private
- def reader_method(name, class_name, mapping, allow_nil)
+ def reader_method(name, class_name, mapping, allow_nil, constructor)
module_eval do
define_method(name) do |*args|
force_reload = args.first || false
if (instance_variable_get("@#{name}").nil? || force_reload) && (!allow_nil || mapping.any? {|pair| !read_attribute(pair.first).nil? })
- instance_variable_set("@#{name}", class_name.constantize.new(*mapping.collect {|pair| read_attribute(pair.first)}))
+ attrs = mapping.collect {|pair| read_attribute(pair.first)}
+ object = case constructor
+ when Symbol
+ class_name.constantize.send(constructor, *attrs)
+ when Proc, Method
+ constructor.call(*attrs)
+ else
+ raise ArgumentError, 'Constructor must be a symbol denoting the constructor method to call or a Proc to be invoked.'
+ end
+ instance_variable_set("@#{name}", object)
end
instance_variable_get("@#{name}")
end
@@ -170,14 +233,23 @@ def reader_method(name, class_name, mapping, allow_nil)
end
- def writer_method(name, class_name, mapping, allow_nil, conversion)
+ def writer_method(name, class_name, mapping, allow_nil, converter)
module_eval do
define_method("#{name}=") do |part|
if part.nil? && allow_nil
mapping.each { |pair| self[pair.first] = nil }
instance_variable_set("@#{name}", nil)
else
- part = conversion.call(part) unless part.is_a?(class_name.constantize) || conversion.nil?
+ unless part.is_a?(class_name.constantize) || converter.nil?
+ part = case converter
+ when Symbol
+ class_name.constantize.send(converter, part)
+ when Proc, Method
+ converter.call(part)
+ else
+ raise ArgumentError, 'Converter must be a symbol denoting the converter method to call or a Proc to be invoked.'
+ end
+ end
mapping.each { |pair| self[pair.first] = part.send(pair.last) }
instance_variable_set("@#{name}", part.freeze)
end
View
140 vendor/activerecord/lib/active_record/association_preload.rb
@@ -1,14 +1,88 @@
module ActiveRecord
+ # See ActiveRecord::AssociationPreload::ClassMethods for documentation.
module AssociationPreload #:nodoc:
def self.included(base)
base.extend(ClassMethods)
end
+ # Implements the details of eager loading of ActiveRecord associations.
+ # Application developers should not use this module directly.
+ #
+ # ActiveRecord::Base is extended with this module. The source code in
+ # ActiveRecord::Base references methods defined in this module.
+ #
+ # Note that 'eager loading' and 'preloading' are actually the same thing.
+ # However, there are two different eager loading strategies.
+ #
+ # The first one is by using table joins. This was only strategy available
+ # prior to Rails 2.1. Suppose that you have an Author model with columns
+ # 'name' and 'age', and a Book model with columns 'name' and 'sales'. Using
+ # this strategy, ActiveRecord would try to retrieve all data for an author
+ # and all of its books via a single query:
+ #
+ # SELECT * FROM authors
+ # LEFT OUTER JOIN books ON authors.id = books.id
+ # WHERE authors.name = 'Ken Akamatsu'
+ #
+ # However, this could result in many rows that contain redundant data. After
+ # having received the first row, we already have enough data to instantiate
+ # the Author object. In all subsequent rows, only the data for the joined
+ # 'books' table is useful; the joined 'authors' data is just redundant, and
+ # processing this redundant data takes memory and CPU time. The problem
+ # quickly becomes worse and worse as the level of eager loading increases
+ # (i.e. if ActiveRecord is to eager load the associations' assocations as
+ # well).
+ #
+ # The second strategy is to use multiple database queries, one for each
+ # level of association. Since Rails 2.1, this is the default strategy. In
+ # situations where a table join is necessary (e.g. when the +:conditions+
+ # option references an association's column), it will fallback to the table
+ # join strategy.
+ #
+ # See also ActiveRecord::Associations::ClassMethods, which explains eager
+ # loading in a more high-level (application developer-friendly) manner.
module ClassMethods
-
- # Loads the named associations for the activerecord record (or records) given
- # preload_options is passed only one level deep: don't pass to the child associations when associations is a Hash
protected
+
+ # Eager loads the named associations for the given ActiveRecord record(s).
+ #
+ # In this description, 'association name' shall refer to the name passed
+ # to an association creation method. For example, a model that specifies
+ # <tt>belongs_to :author</tt>, <tt>has_many :buyers</tt> has association
+ # names +:author+ and +:buyers+.
+ #
+ # == Parameters
+ # +records+ is an array of ActiveRecord::Base. This array needs not be flat,
+ # i.e. +records+ itself may also contain arrays of records. In any case,
+ # +preload_associations+ will preload the associations all records by
+ # flattening +records+.
+ #
+ # +associations+ specifies one or more associations that you want to
+ # preload. It may be:
+ # - a Symbol or a String which specifies a single association name. For
+ # example, specifiying +:books+ allows this method to preload all books
+ # for an Author.
+ # - an Array which specifies multiple association names. This array
+ # is processed recursively. For example, specifying <tt>[:avatar, :books]</tt>
+ # allows this method to preload an author's avatar as well as all of his
+ # books.
+ # - a Hash which specifies multiple association names, as well as
+ # association names for the to-be-preloaded association objects. For
+ # example, specifying <tt>{ :author => :avatar }</tt> will preload a
+ # book's author, as well as that author's avatar.
+ #
+ # +:associations+ has the same format as the +:include+ option for
+ # <tt>ActiveRecord::Base.find</tt>. So +associations+ could look like this:
+ #
+ # :books
+ # [ :books, :author ]
+ # { :author => :avatar }
+ # [ :books, { :author => :avatar } ]
+ #
+ # +preload_options+ contains options that will be passed to ActiveRecord#find
+ # (which is called under the hood for preloading records). But it is passed
+ # only one level deep in the +associations+ argument, i.e. it's not passed
+ # to the child associations when +associations+ is a Hash.
def preload_associations(records, associations, preload_options={})
records = [records].flatten.compact.uniq
return if records.empty?
@@ -30,13 +104,19 @@ def preload_associations(records, associations, preload_options={})
private
+ # Preloads a specific named association for the given records. This is
+ # called by +preload_associations+ as its base case.
def preload_one_association(records, association, preload_options={})
class_to_reflection = {}
# Not all records have the same class, so group then preload
# group on the reflection itself so that if various subclass share the same association then we do not split them
- # unncessarily
+ # unnecessarily
records.group_by {|record| class_to_reflection[record.class] ||= record.class.reflections[association]}.each do |reflection, records|
raise ConfigurationError, "Association named '#{ association }' was not found; perhaps you misspelled it?" unless reflection
+
+ # 'reflection.macro' can return 'belongs_to', 'has_many', etc. Thus,
+ # the following could call 'preload_belongs_to_association',
+ # 'preload_has_many_association', etc.
send("preload_#{reflection.macro}_association", records, reflection, preload_options)
end
end
@@ -77,12 +157,17 @@ def set_association_single_records(id_to_record_map, reflection_name, associated
end
end
- def construct_id_map(records)
+ # Given a collection of ActiveRecord objects, constructs a Hash which maps
+ # the objects' IDs to the relevant objects. Returns a 2-tuple
+ # <tt>(id_to_record_map, ids)</tt> where +id_to_record_map+ is the Hash,
+ # and +ids+ is an Array of record IDs.
+ def construct_id_map(records, primary_key=nil)
id_to_record_map = {}
ids = []
records.each do |record|
- ids << record.id
- mapped_records = (id_to_record_map[record.id.to_s] ||= [])
+ primary_key ||= record.class.primary_key
+ ids << record[primary_key]
+ mapped_records = (id_to_record_map[ids.last.to_s] ||= [])
mapped_records << record
end
ids.uniq!
@@ -95,8 +180,8 @@ def preload_has_and_belongs_to_many_association(records, reflection, preload_opt
records.each {|record| record.send(reflection.name).loaded}
options = reflection.options
- conditions = "t0.#{reflection.primary_key_name} IN (?)"
- conditions << append_conditions(options, preload_options)
+ conditions = "t0.#{reflection.primary_key_name} #{in_or_equals_for_ids(ids)}"
+ conditions << append_conditions(reflection, preload_options)
associated_records = reflection.klass.find(:all, :conditions => [conditions, ids],
:include => options[:include],
@@ -108,6 +193,7 @@ def preload_has_and_belongs_to_many_association(records, reflection, preload_opt
end
def preload_has_one_association(records, reflection, preload_options={})
+ return if records.first.send("loaded_#{reflection.name}?")
id_to_record_map, ids = construct_id_map(records)
options = reflection.options
records.each {|record| record.send("set_#{reflection.name}_target", nil)}
@@ -129,23 +215,25 @@ def preload_has_one_association(records, reflection, preload_options={})
end
def preload_has_many_association(records, reflection, preload_options={})
- id_to_record_map, ids = construct_id_map(records)
- records.each {|record| record.send(reflection.name).loaded}
+ return if records.first.send(reflection.name).loaded?
options = reflection.options
+ primary_key_name = reflection.through_reflection_primary_key_name
+ id_to_record_map, ids = construct_id_map(records, primary_key_name)
+ records.each {|record| record.send(reflection.name).loaded}
+
if options[:through]
through_records = preload_through_records(records, reflection, options[:through])
through_reflection = reflections[options[:through]]
- through_primary_key = through_reflection.primary_key_name
unless through_records.empty?
source = reflection.source_reflection.name
- #add conditions from reflection!
- through_records.first.class.preload_associations(through_records, source, reflection.options)
+ through_records.first.class.preload_associations(through_records, source, options)
through_records.each do |through_record|
- add_preloaded_records_to_collection(id_to_record_map[through_record[through_primary_key].to_s],
- reflection.name, through_record.send(source))
+ through_record_id = through_record[reflection.through_reflection_primary_key].to_s
+ add_preloaded_records_to_collection(id_to_record_map[through_record_id], reflection.name, through_record.send(source))
end
end
+
else
set_association_collection_records(id_to_record_map, reflection.name, find_associated_records(ids, reflection, preload_options),
reflection.primary_key_name)
@@ -185,6 +273,7 @@ def preload_through_records(records, reflection, through_association)
end
def preload_belongs_to_association(records, reflection, preload_options={})
+ return if records.first.send("loaded_#{reflection.name}?")
options = reflection.options
primary_key_name = reflection.primary_key_name
@@ -222,10 +311,8 @@ def preload_belongs_to_association(records, reflection, preload_options={})
table_name = klass.quoted_table_name
primary_key = klass.primary_key
- conditions = "#{table_name}.#{connection.quote_column_name(primary_key)} IN (?)"
- conditions << append_conditions(options, preload_options)
column_type = klass.columns.detect{|c| c.name == primary_key}.type
- ids = id_map.keys.uniq.map do |id|
+ ids = id_map.keys.map do |id|
if column_type == :integer
id.to_i
elsif column_type == :float
@@ -234,6 +321,8 @@ def preload_belongs_to_association(records, reflection, preload_options={})
id
end
end
+ conditions = "#{table_name}.#{connection.quote_column_name(primary_key)} #{in_or_equals_for_ids(ids)}"
+ conditions << append_conditions(reflection, preload_options)
associated_records = klass.find(:all, :conditions => [conditions, ids],
:include => options[:include],
:select => options[:select],
@@ -248,13 +337,13 @@ def find_associated_records(ids, reflection, preload_options)
table_name = reflection.klass.quoted_table_name
if interface = reflection.options[:as]
- conditions = "#{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_id"} IN (?) and #{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_type"} = '#{self.base_class.sti_name}'"
+ conditions = "#{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_id"} #{in_or_equals_for_ids(ids)} and #{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_type"} = '#{self.base_class.sti_name}'"
else
foreign_key = reflection.primary_key_name
- conditions = "#{reflection.klass.quoted_table_name}.#{foreign_key} IN (?)"
+ conditions = "#{reflection.klass.quoted_table_name}.#{foreign_key} #{in_or_equals_for_ids(ids)}"
end
- conditions << append_conditions(options, preload_options)
+ conditions << append_conditions(reflection, preload_options)
reflection.klass.find(:all,
:select => (preload_options[:select] || options[:select] || "#{table_name}.*"),
@@ -270,13 +359,16 @@ def interpolate_sql_for_preload(sql)
instance_eval("%@#{sql.gsub('@', '\@')}@")
end
- def append_conditions(options, preload_options)
+ def append_conditions(reflection, preload_options)
sql = ""
- sql << " AND (#{interpolate_sql_for_preload(sanitize_sql(options[:conditions]))})" if options[:conditions]
+ sql << " AND (#{interpolate_sql_for_preload(reflection.sanitized_conditions)})" if reflection.sanitized_conditions
sql << " AND (#{sanitize_sql preload_options[:conditions]})" if preload_options[:conditions]
sql
end
+ def in_or_equals_for_ids(ids)
+ ids.size > 1 ? "IN (?)" : "= ?"
+ end
end
end
end
View
647 vendor/activerecord/lib/active_record/associations.rb
@@ -73,6 +73,7 @@ def initialize(reflection)
end
end
+ # See ActiveRecord::Associations::ClassMethods for documentation.
module Associations # :nodoc:
def self.included(base)
base.extend(ClassMethods)
@@ -150,6 +151,7 @@ def clear_association_cache #:nodoc:
# #others.destroy_all | X | X | X
# #others.find(*args) | X | X | X
# #others.find_first | X | |
+ # #others.exist? | X | X | X
# #others.uniq | X | X | X
# #others.reset | X | X | X
#
@@ -505,6 +507,14 @@ def clear_association_cache #:nodoc:
#
# will load posts and eager load the +approved_comments+ association, which contains only those comments that have been approved.
#
+ # If you eager load an association with a specified <tt>:limit</tt> option, it will be ignored, returning all the associated objects:
+ #
+ # class Picture < ActiveRecord::Base
+ # has_many :most_recent_comments, :class_name => 'Comment', :order => 'id DESC', :limit => 10
+ # end
+ #
+ # Picture.find(:first, :include => :most_recent_comments).most_recent_comments # => returns all associated comments.
+ #
# When eager loaded, conditions are interpolated in the context of the model class, not the model instance. Conditions are lazily interpolated
# before the actual model exists.
#
@@ -582,12 +592,13 @@ def clear_association_cache #:nodoc:
# has_many :clients
# end
#
- # class Company < ActiveRecord::Base; end
+ # class Client < ActiveRecord::Base; end
# end
# end
#
- # When Firm#clients is called, it will in turn call <tt>MyApplication::Business::Company.find(firm.id)</tt>. If you want to associate
- # with a class in another module scope, this can be done by specifying the complete class name. Example:
+ # When <tt>Firm#clients</tt> is called, it will in turn call <tt>MyApplication::Business::Client.find_all_by_firm_id(firm.id)</tt>.
+ # If you want to associate with a class in another module scope, this can be done by specifying the complete class name.
+ # Example:
#
# module MyApplication
# module Business
@@ -611,32 +622,55 @@ def clear_association_cache #:nodoc:
# All of the association macros can be specialized through options. This makes cases more complex than the simple and guessable ones
# possible.
module ClassMethods
- # Adds the following methods for retrieval and query of collections of associated objects:
- # +collection+ is replaced with the symbol passed as the first argument, so
- # <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.
- # * <tt>collection(force_reload = false)</tt> - Returns an array of all the associated objects.
+ # Specifies a one-to-many association. The following methods for retrieval and query of
+ # collections of associated objects will be added:
+ #
+ # [collection(force_reload = false)]
+ # Returns an array of all the associated objects.
# An empty array is returned if none are found.
- # * <tt>collection<<(object, ...)</tt> - Adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
- # * <tt>collection.delete(object, ...)</tt> - Removes one or more objects from the collection by setting their foreign keys to +NULL+.
- # This will also destroy the objects if they're declared as +belongs_to+ and dependent on this model.
- # * <tt>collection=objects</tt> - Replaces the collections content by deleting and adding objects as appropriate.
- # * <tt>collection_singular_ids</tt> - Returns an array of the associated objects' ids
- # * <tt>collection_singular_ids=ids</tt> - Replace the collection with the objects identified by the primary keys in +ids+
- # * <tt>collection.clear</tt> - Removes every object from the collection. This destroys the associated objects if they
- # are associated with <tt>:dependent => :destroy</tt>, deletes them directly from the database if <tt>:dependent => :delete_all</tt>,
- # otherwise sets their foreign keys to +NULL+.
- # * <tt>collection.empty?</tt> - Returns +true+ if there are no associated objects.
- # * <tt>collection.size</tt> - Returns the number of associated objects.
- # * <tt>collection.find</tt> - Finds an associated object according to the same rules as Base.find.
- # * <tt>collection.build(attributes = {}, ...)</tt> - Returns one or more new objects of the collection type that have been instantiated
- # with +attributes+ and linked to this object through a foreign key, but have not yet been saved. *Note:* This only works if an
- # associated object already exists, not if it's +nil+!
- # * <tt>collection.create(attributes = {})</tt> - Returns a new object of the collection type that has been instantiated
- # with +attributes+, linked to this object through a foreign key, and that has already been saved (if it passed the validation).
- # *Note:* This only works if an associated object already exists, not if it's +nil+!
+ # [collection<<(object, ...)]
+ # Adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
+ # [collection.delete(object, ...)]
+ # Removes one or more objects from the collection by setting their foreign keys to +NULL+.
+ # Objects will be in addition destroyed if they're associated with <tt>:dependent => :destroy</tt>,
+ # and deleted if they're associated with <tt>:dependent => :delete_all</tt>.
+ # [collection=objects]
+ # Replaces the collections content by deleting and adding objects as appropriate.
+ # [collection_singular_ids]
+ # Returns an array of the associated objects' ids
+ # [collection_singular_ids=ids]
+ # Replace the collection with the objects identified by the primary keys in +ids+
+ # [collection.clear]
+ # Removes every object from the collection. This destroys the associated objects if they
+ # are associated with <tt>:dependent => :destroy</tt>, deletes them directly from the
+ # database if <tt>:dependent => :delete_all</tt>, otherwise sets their foreign keys to +NULL+.
+ # [collection.empty?]
+ # Returns +true+ if there are no associated objects.
+ # [collection.size]
+ # Returns the number of associated objects.
+ # [collection.find(...)]
+ # Finds an associated object according to the same rules as ActiveRecord::Base.find.
+ # [collection.exist?(...)]
+ # Checks whether an associated object with the given conditions exists.
+ # Uses the same rules as ActiveRecord::Base.exists?.
+ # [collection.build(attributes = {}, ...)]
+ # Returns one or more new objects of the collection type that have been instantiated
+ # with +attributes+ and linked to this object through a foreign key, but have not yet
+ # been saved. <b>Note:</b> This only works if an associated object already exists, not if
+ # it's +nil+!
+ # [collection.create(attributes = {})]
+ # Returns a new object of the collection type that has been instantiated
+ # with +attributes+, linked to this object through a foreign key, and that has already
+ # been saved (if it passed the validation). <b>Note:</b> This only works if an associated
+ # object already exists, not if it's +nil+!
+ #
+ # (*Note*: +collection+ is replaced with the symbol passed as the first argument, so
+ # <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.)
+ #
+ # === Example
#
# Example: A Firm class declares <tt>has_many :clients</tt>, which will add:
- # * <tt>Firm#clients</tt> (similar to <tt>Clients.find :all, :conditions => "firm_id = #{id}"</tt>)
+ # * <tt>Firm#clients</tt> (similar to <tt>Clients.find :all, :conditions => ["firm_id = ?", id]</tt>)
# * <tt>Firm#clients<<</tt>
# * <tt>Firm#clients.delete</tt>
# * <tt>Firm#clients=</tt>
@@ -646,52 +680,74 @@ module ClassMethods
# * <tt>Firm#clients.empty?</tt> (similar to <tt>firm.clients.size == 0</tt>)
# * <tt>Firm#clients.size</tt> (similar to <tt>Client.count "firm_id = #{id}"</tt>)
# * <tt>Firm#clients.find</tt> (similar to <tt>Client.find(id, :conditions => "firm_id = #{id}")</tt>)
+ # * <tt>Firm#clients.exist?(:name => 'ACME')</tt> (similar to <tt>Client.exist?(:name => 'ACME', :firm_id => firm.id)</tt>)
# * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>)
# * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)
# The declaration can also include an options hash to specialize the behavior of the association.
#
- # Options are:
- # * <tt>:class_name</tt> - Specify the class name of the association. Use it only if that name can't be inferred
+ # === Supported options
+ # [:class_name]
+ # Specify the class name of the association. Use it only if that name can't be inferred
# from the association name. So <tt>has_many :products</tt> will by default be linked to the Product class, but
# if the real class name is SpecialProduct, you'll have to specify it with this option.
- # * <tt>:conditions</tt> - Specify the conditions that the associated objects must meet in order to be included as a +WHERE+
+ # [:conditions]
+ # Specify the conditions that the associated objects must meet in order to be included as a +WHERE+
# SQL fragment, such as <tt>price > 5 AND name LIKE 'B%'</tt>. Record creations from the association are scoped if a hash
# is used. <tt>has_many :posts, :conditions => {:published => true}</tt> will create published posts with <tt>@blog.posts.create</tt>
# or <tt>@blog.posts.build</tt>.
- # * <tt>:order</tt> - Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
+ # [:order]
+ # Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
# such as <tt>last_name, first_name DESC</tt>.
- # * <tt>:foreign_key</tt> - Specify the foreign key used for the association. By default this is guessed to be the name
+ # [:foreign_key]
+ # Specify the foreign key used for the association. By default this is guessed to be the name
# of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_many+ association will use "person_id"
# as the default <tt>:foreign_key</tt>.
- # * <tt>:dependent</tt> - If set to <tt>:destroy</tt> all the associated objects are destroyed
+ # [:primary_key]
+ # Specify the method that returns the primary key used for the association. By default this is +id+.
+ # [:dependent]
+ # If set to <tt>:destroy</tt> all the associated objects are destroyed
# alongside this object by calling their +destroy+ method. If set to <tt>:delete_all</tt> all associated
# objects are deleted *without* calling their +destroy+ method. If set to <tt>:nullify</tt> all associated
# objects' foreign keys are set to +NULL+ *without* calling their +save+ callbacks. *Warning:* This option is ignored when also using
# the <tt>:through</tt> option.
- # * <tt>:finder_sql</tt> - Specify a complete SQL statement to fetch the association. This is a good way to go for complex
+ # [:finder_sql]
+ # Specify a complete SQL statement to fetch the association. This is a good way to go for complex
# associations that depend on multiple tables. Note: When this option is used, +find_in_collection+ is _not_ added.
- # * <tt>:counter_sql</tt> - Specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
+ # [:counter_sql]
+ # Specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
# specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
- # * <tt>:extend</tt> - Specify a named module for extending the proxy. See "Association extensions".
- # * <tt>:include</tt> - Specify second-order associations that should be eager loaded when the collection is loaded.
- # * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
- # * <tt>:limit</tt> - An integer determining the limit on the number of rows that should be returned.
- # * <tt>:offset</tt> - An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
- # * <tt>:select</tt> - By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if you, for example, want to do a join
- # but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will rise an error.
- # * <tt>:as</tt> - Specifies a polymorphic interface (See <tt>belongs_to</tt>).
- # * <tt>:through</tt> - Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
+ # [:extend]
+ # Specify a named module for extending the proxy. See "Association extensions".
+ # [:include]
+ # Specify second-order associations that should be eager loaded when the collection is loaded.
+ # [:group]
+ # An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
+ # [:limit]
+ # An integer determining the limit on the number of rows that should be returned.
+ # [:offset]
+ # An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
+ # [:select]
+ # By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if you, for example, want to do a join
+ # but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
+ # [:as]
+ # Specifies a polymorphic interface (See <tt>belongs_to</tt>).
+ # [:through]
+ # Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
# are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a <tt>belongs_to</tt>
# or <tt>has_many</tt> association on the join model.
- # * <tt>:source</tt> - Specifies the source association name used by <tt>has_many :through</tt> queries. Only use it if the name cannot be
+ # [:source]
+ # Specifies the source association name used by <tt>has_many :through</tt> queries. Only use it if the name cannot be
# inferred from the association. <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either <tt>:subscribers</tt> or
# <tt>:subscriber</tt> on Subscription, unless a <tt>:source</tt> is given.
- # * <tt>:source_type</tt> - Specifies type of the source association used by <tt>has_many :through</tt> queries where the source
+ # [:source_type]
+ # Specifies type of the source association used by <tt>has_many :through</tt> queries where the source
# association is a polymorphic +belongs_to+.
- # * <tt>:uniq</tt> - If true, duplicates will be omitted from the collection. Useful in conjunction with <tt>:through</tt>.
- # * <tt>:readonly</tt> - If true, all the associated objects are readonly through the association.
- # * <tt>:validate</tt> - If false, don't validate the associated objects when saving the parent object. true by default.
- #
+ # [:uniq]
+ # If true, duplicates will be omitted from the collection. Useful in conjunction with <tt>:through</tt>.
+ # [:readonly]
+ # If true, all the associated objects are readonly through the association.
+ # [:validate]
+ # If false, don't validate the associated objects when saving the parent object. true by default.
# Option examples:
# has_many :comments, :order => "posted_on"
# has_many :comments, :include => :author
@@ -722,56 +778,89 @@ def has_many(association_id, options = {}, &extension)
end
end
- # Adds the following methods for retrieval and query of a single associated object:
- # +association+ is replaced with the symbol passed as the first argument, so
- # <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>.
- # * <tt>association(force_reload = false)</tt> - Returns the associated object. +nil+ is returned if none is found.
- # * <tt>association=(associate)</tt> - Assigns the associate object, extracts the primary key, sets it as the foreign key,
+ # Specifies a one-to-one association with another class. This method should only be used
+ # if the other class contains the foreign key. If the current class contains the foreign key,
+ # then you should use +belongs_to+ instead. See also ActiveRecord::Associations::ClassMethods's overview
+ # on when to use has_one and when to use belongs_to.
+ #
+ # The following methods for retrieval and query of a single associated object will be added:
+ #
+ # [association(force_reload = false)]
+ # Returns the associated object. +nil+ is returned if none is found.
+ # [association=(associate)]
+ # Assigns the associate object, extracts the primary key, sets it as the foreign key,
# and saves the associate object.
- # * <tt>association.nil?</tt> - Returns +true+ if there is no associated object.
- # * <tt>build_association(attributes = {})</tt> - Returns a new object of the associated type that has been instantiated
- # with +attributes+ and linked to this object through a foreign key, but has not yet been saved. Note: This ONLY works if
- # an association already exists. It will NOT work if the association is +nil+.
- # * <tt>create_association(attributes = {})</tt> - Returns a new object of the associated type that has been instantiated
- # with +attributes+, linked to this object through a foreign key, and that has already been saved (if it passed the validation).
+ # [association.nil?]
+ # Returns +true+ if there is no associated object.
+ # [build_association(attributes = {})]
+ # Returns a new object of the associated type that has been instantiated
+ # with +attributes+ and linked to this object through a foreign key, but has not
+ # yet been saved. <b>Note:</b> This ONLY works if an association already exists.
+ # It will NOT work if the association is +nil+.
+ # [create_association(attributes = {})]
+ # Returns a new object of the associated type that has been instantiated
+ # with +attributes+, linked to this object through a foreign key, and that
+ # has already been saved (if it passed the validation).
#
- # Example: An Account class declares <tt>has_one :beneficiary</tt>, which will add:
+ # (+association+ is replaced with the symbol passed as the first argument, so
+ # <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>.)
+ #
+ # === Example
+ #
+ # An Account class declares <tt>has_one :beneficiary</tt>, which will add:
# * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.find(:first, :conditions => "account_id = #{id}")</tt>)
# * <tt>Account#beneficiary=(beneficiary)</tt> (similar to <tt>beneficiary.account_id = account.id; beneficiary.save</tt>)
# * <tt>Account#beneficiary.nil?</tt>
# * <tt>Account#build_beneficiary</tt> (similar to <tt>Beneficiary.new("account_id" => id)</tt>)
# * <tt>Account#create_beneficiary</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save; b</tt>)
#
+ # === Options
+ #
# The declaration can also include an options hash to specialize the behavior of the association.
#
# Options are:
- # * <tt>:class_name</tt> - Specify the class name of the association. Use it only if that name can't be inferred
+ # [:class_name]
+ # Specify the class name of the association. Use it only if that name can't be inferred
# from the association name. So <tt>has_one :manager</tt> will by default be linked to the Manager class, but
# if the real class name is Person, you'll have to specify it with this option.
- # * <tt>:conditions</tt> - Specify the conditions that the associated object must meet in order to be included as a +WHERE+
+ # [:conditions]
+ # Specify the conditions that the associated object must meet in order to be included as a +WHERE+
# SQL fragment, such as <tt>rank = 5</tt>.
- # * <tt>:order</tt> - Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
+ # [:order]
+ # Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
# such as <tt>last_name, first_name DESC</tt>.
- # * <tt>:dependent</tt> - If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
+ # [:dependent]
+ # If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
# <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method. If set to <tt>:nullify</tt>, the associated
# object's foreign key is set to +NULL+. Also, association is assigned.
- # * <tt>:foreign_key</tt> - Specify the foreign key used for the association. By default this is guessed to be the name
+ # [:foreign_key]
+ # Specify the foreign key used for the association. By default this is guessed to be the name
# of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_one+ association will use "person_id"
# as the default <tt>:foreign_key</tt>.
- # * <tt>:include</tt> - Specify second-order associations that should be eager loaded when this object is loaded.
- # * <tt>:as</tt> - Specifies a polymorphic interface (See <tt>belongs_to</tt>).
- # * <tt>:select</tt> - By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example, you want to do a join
+ # [:primary_key]
+ # Specify the method that returns the primary key used for the association. By default this is +id+.
+ # [:include]
+ # Specify second-order associations that should be eager loaded when this object is loaded.
+ # [:as]
+ # Specifies a polymorphic interface (See <tt>belongs_to</tt>).
+ # [:select]
+ # By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example, you want to do a join
# but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
- # * <tt>:through</tt>: Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
+ # [:through]
+ # Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
# are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a
# <tt>has_one</tt> or <tt>belongs_to</tt> association on the join model.
- # * <tt>:source</tt> - Specifies the source association name used by <tt>has_one :through</tt> queries. Only use it if the name cannot be
+ # [:source]
+ # Specifies the source association name used by <tt>has_one :through</tt> queries. Only use it if the name cannot be
# inferred from the association. <tt>has_one :favorite, :through => :favorites</tt> will look for a
# <tt>:favorite</tt> on Favorite, unless a <tt>:source</tt> is given.
- # * <tt>:source_type</tt> - Specifies type of the source association used by <tt>has_one :through</tt> queries where the source
+ # [:source_type]
+ # Specifies type of the source association used by <tt>has_one :through</tt> queries where the source
# association is a polymorphic +belongs_to+.
- # * <tt>:readonly</tt> - If true, the associated object is readonly through the association.
- # * <tt>:validate</tt> - If false, don't validate the associated object when saving the parent object. +false+ by default.
+ # [:readonly]
+ # If true, the associated object is readonly through the association.
+ # [:validate]
+ # If false, don't validate the associated object when saving the parent object. +false+ by default.
#
# Option examples:
# has_one :credit_card, :dependent => :destroy # destroys the associated credit card
@@ -793,10 +882,10 @@ def has_one(association_id, options = {})
method_name = "has_one_after_save_for_#{reflection.name}".to_sym
define_method(method_name) do
- association = instance_variable_get("#{ivar}") if instance_variable_defined?("#{ivar}")
+ association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
- if !association.nil? && (new_record? || association.new_record? || association["#{reflection.primary_key_name}"] != id)
- association["#{reflection.primary_key_name}"] = id
+ if !association.nil? && (new_record? || association.new_record? || association[reflection.primary_key_name] != id)
+ association[reflection.primary_key_name] = id
association.save(true)
end
end
@@ -811,18 +900,34 @@ def has_one(association_id, options = {})
end
end
- # Adds the following methods for retrieval and query for a single associated object for which this object holds an id:
- # +association+ is replaced with the symbol passed as the first argument, so
- # <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.
- # * <tt>association(force_reload = false)</tt> - Returns the associated object. +nil+ is returned if none is found.
- # * <tt>association=(associate)</tt> - Assigns the associate object, extracts the primary key, and sets it as the foreign key.
- # * <tt>association.nil?</tt> - Returns +true+ if there is no associated object.
- # * <tt>build_association(attributes = {})</tt> - Returns a new object of the associated type that has been instantiated
+ # Specifies a one-to-one association with another class. This method should only be used
+ # if this class contains the foreign key. If the other class contains the foreign key,
+ # then you should use +has_one+ instead. See also ActiveRecord::Associations::ClassMethods's overview
+ # on when to use +has_one+ and when to use +belongs_to+.
+ #
+ # Methods will be added for retrieval and query for a single associated object, for which
+ # this object holds an id:
+ #
+ # [association(force_reload = false)]
+ # Returns the associated object. +nil+ is returned if none is found.
+ # [association=(associate)]
+ # Assigns the associate object, extracts the primary key, and sets it as the foreign key.
+ # [association.nil?]
+ # Returns +true+ if there is no associated object.
+ # [build_association(attributes = {})]
+ # Returns a new object of the associated type that has been instantiated
# with +attributes+ and linked to this object through a foreign key, but has not yet been saved.
- # * <tt>create_association(attributes = {})</tt> - Returns a new object of the associated type that has been instantiated
- # with +attributes+, linked to this object through a foreign key, and that has already been saved (if it passed the validation).
+ # [create_association(attributes = {})]
+ # Returns a new object of the associated type that has been instantiated
+ # with +attributes+, linked to this object through a foreign key, and that
+ # has already been saved (if it passed the validation).
+ #
+ # (+association+ is replaced with the symbol passed as the first argument, so
+ # <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.)
+ #
+ # === Example
#
- # Example: A Post class declares <tt>belongs_to :author</tt>, which will add:
+ # A Post class declares <tt>belongs_to :author</tt>, which will add:
# * <tt>Post#author</tt> (similar to <tt>Author.find(author_id)</tt>)
# * <tt>Post#author=(author)</tt> (similar to <tt>post.author_id = author.id</tt>)
# * <tt>Post#author?</tt> (similar to <tt>post.author == some_author</tt>)
@@ -831,36 +936,45 @@ def has_one(association_id, options = {})
# * <tt>Post#create_author</tt> (similar to <tt>post.author = Author.new; post.author.save; post.author</tt>)
# The declaration can also include an options hash to specialize the behavior of the association.
#
- # Options are:
- # * <tt>:class_name</tt> - Specify the class name of the association. Use it only if that name can't be inferred
+ # === Options
+ #
+ # [:class_name]
+ # Specify the class name of the association. Use it only if that name can't be inferred
# from the association name. So <tt>has_one :author</tt> will by default be linked to the Author class, but
# if the real class name is Person, you'll have to specify it with this option.
- # * <tt>:conditions</tt> - Specify the conditions that the associated object must meet in order to be included as a +WHERE+
+ # [:conditions]
+ # Specify the conditions that the associated object must meet in order to be included as a +WHERE+
# SQL fragment, such as <tt>authorized = 1</tt>.
- # * <tt>:select</tt> - By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example, you want to do a join
+ # [:select]
+ # By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example, you want to do a join
# but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
- # * <tt>:foreign_key</tt> - Specify the foreign key used for the association. By default this is guessed to be the name
+ # [:foreign_key]
+ # Specify the foreign key used for the association. By default this is guessed to be the name
# of the association with an "_id" suffix. So a class that defines a <tt>belongs_to :person</tt> association will use
# "person_id" as the default <tt>:foreign_key</tt>. Similarly, <tt>belongs_to :favorite_person, :class_name => "Person"</tt>
# will use a foreign key of "favorite_person_id".
- # * <tt>:dependent</tt> - If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
+ # [:dependent]
+ # If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
# <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method. This option should not be specified when
# <tt>belongs_to</tt> is used in conjunction with a <tt>has_many</tt> relationship on another class because of the potential to leave
# orphaned records behind.
- # * <tt>:counter_cache</tt> - Caches the number of belonging objects on the associate class through the use of +increment_counter+
+ # [:counter_cache]
+ # Caches the number of belonging objects on the associate class through the use of +increment_counter+
# and +decrement_counter+. The counter cache is incremented when an object of this class is created and decremented when it's
# destroyed. This requires that a column named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging Comment class)
# is used on the associate class (such as a Post class). You can also specify a custom counter cache column by providing
# a column name instead of a +true+/+false+ value to this option (e.g., <tt>:counter_cache => :my_custom_counter</tt>.)
- # When creating a counter cache column, the database statement or migration must specify a default value of <tt>0</tt>, failing to do
- # this results in a counter with +NULL+ value, which will never increment.
# Note: Specifying a counter cache will add it to that model's list of readonly attributes using +attr_readonly+.
- # * <tt>:include</tt> - Specify second-order associations that should be eager loaded when this object is loaded.
- # * <tt>:polymorphic</tt> - Specify this association is a polymorphic association by passing +true+.
+ # [:include]
+ # Specify second-order associations that should be eager loaded when this object is loaded.
+ # [:polymorphic]
+ # Specify this association is a polymorphic association by passing +true+.
# Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
# to the +attr_readonly+ list in the associated classes (e.g. <tt>class Post; attr_readonly :comments_count; end</tt>).
- # * <tt>:readonly</tt> - If true, the associated object is readonly through the association.
- # * <tt>:validate</tt> - If false, don't validate the associated objects when saving the parent object. +false+ by default.
+ # [:readonly]
+ # If true, the associated object is readonly through the association.
+ # [:validate]
+ # If false, don't validate the associated objects when saving the parent object. +false+ by default.
#
# Option examples:
# belongs_to :firm, :foreign_key => "client_of"
@@ -880,7 +994,7 @@ def belongs_to(association_id, options = {})
method_name = "polymorphic_belongs_to_before_save_for_#{reflection.name}".to_sym
define_method(method_name) do
- association = instance_variable_get("#{ivar}") if instance_variable_defined?("#{ivar}")
+ association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
if association && association.target
if association.new_record?
@@ -888,8 +1002,8 @@ def belongs_to(association_id, options = {})
end
if association.updated?
- self["#{reflection.primary_key_name}"] = association.id
- self["#{reflection.options[:foreign_type]}"] = association.class.base_class.name.to_s
+ self[reflection.primary_key_name] = association.id
+ self[reflection.options[:foreign_type]] = association.class.base_class.name.to_s
end
end
end
@@ -901,7 +1015,7 @@ def belongs_to(association_id, options = {})
method_name = "belongs_to_before_save_for_#{reflection.name}".to_sym
define_method(method_name) do
- association = instance_variable_get("#{ivar}") if instance_variable_defined?("#{ivar}")
+ association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
if !association.nil?
if association.new_record?
@@ -909,7 +1023,7 @@ def belongs_to(association_id, options = {})
end
if association.updated?
- self["#{reflection.primary_key_name}"] = association.id
+ self[reflection.primary_key_name] = association.id
end
end
end
@@ -919,20 +1033,20 @@ def belongs_to(association_id, options = {})
# Create the callbacks to update counter cache
if options[:counter_cache]
cache_column = options[:counter_cache] == true ?
- "#{self.to_s.underscore.pluralize}_count" :
+ "#{self.to_s.demodulize.underscore.pluralize}_count" :
options[:counter_cache]
method_name = "belongs_to_counter_cache_after_create_for_#{reflection.name}".to_sym
define_method(method_name) do
- association = send("#{reflection.name}")
- association.class.increment_counter("#{cache_column}", send("#{reflection.primary_key_name}")) unless association.nil?
+ association = send(reflection.name)
+ association.class.increment_counter(cache_column, send(reflection.primary_key_name)) unless association.nil?
end
after_create method_name
method_name = "belongs_to_counter_cache_before_destroy_for_#{reflection.name}".to_sym
define_method(method_name) do
- association = send("#{reflection.name}")
- association.class.decrement_counter("#{cache_column}", send("#{reflection.primary_key_name}")) unless association.nil?
+ association = send(reflection.name)
+ association.class.decrement_counter(cache_column, send(reflection.primary_key_name)) unless association.nil?
end
before_destroy method_name
@@ -946,8 +1060,9 @@ def belongs_to(association_id, options = {})
configure_dependency_for_belongs_to(reflection)
end
- # Associates two classes via an intermediate join table. Unless the join table is explicitly specified as
- # an option, it is guessed using the lexical order of the class names. So a join between Developer and Project
+ # Specifies a many-to-many relationship with another class. This associates two classes via an
+ # intermediate join table. Unless the join table is explicitly specified as an option, it is
+ # guessed using the lexical order of the class names. So a join between Developer and Project
# will give the default join table name of "developers_projects" because "D" outranks "P". Note that this precedence
# is calculated using the <tt><</tt> operator for String. This means that if the strings are of different lengths,
# and the strings are equal when compared up to the shortest length, then the longer string is considered of higher
@@ -962,28 +1077,48 @@ def belongs_to(association_id, options = {})
# associations with attributes to a real join model (see introduction).
#
# Adds the following methods for retrieval and query:
- # +collection+ is replaced with the symbol passed as the first argument, so
- # <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>.
- # * <tt>collection(force_reload = false)</tt> - Returns an array of all the associated objects.
+ #
+ # [collection(force_reload = false)]
+ # Returns an array of all the associated objects.
# An empty array is returned if none are found.
- # * <tt>collection<<(object, ...)</tt> - Adds one or more objects to the collection by creating associations in the join table
+ # [collection<<(object, ...)]
+ # Adds one or more objects to the collection by creating associations in the join table
# (<tt>collection.push</tt> and <tt>collection.concat</tt> are aliases to this method).
- # * <tt>collection.delete(object, ...)</tt> - Removes one or more objects from the collection by removing their associations from the join table.
+ # [collection.delete(object, ...)]
+ # Removes one or more objects from the collection by removing their associations from the join table.
# This does not destroy the objects.
- # * <tt>collection=objects</tt> - Replaces the collection's content by deleting and adding objects as appropriate.
- # * <tt>collection_singular_ids</tt> - Returns an array of the associated objects' ids.
- # * <tt>collection_singular_ids=ids</tt> - Replace the collection by the objects identified by the primary keys in +ids+.
- # * <tt>collection.clear</tt> - Removes every object from the collection. This does not destroy the objects.
- # * <tt>collection.empty?</tt> - Returns +true+ if there are no associated objects.
- # * <tt>collection.size</tt> - Returns the number of associated objects.
- # * <tt>collection.find(id)</tt> - Finds an associated object responding to the +id+ and that
+ # [collection=objects]
+ # Replaces the collection's content by deleting and adding objects as appropriate.
+ # [collection_singular_ids]
+ # Returns an array of the associated objects' ids.
+ # [collection_singular_ids=ids]
+ # Replace the collection by the objects identified by the primary keys in +ids+.
+ # [collection.clear]
+ # Removes every object from the collection. This does not destroy the objects.
+ # [collection.empty?]
+ # Returns +true+ if there are no associated objects.
+ # [collection.size]
+ # Returns the number of associated objects.
+ # [collection.find(id)]
+ # Finds an associated object responding to the +id+ and that
# meets the condition that it has to be associated with this object.
- # * <tt>collection.build(attributes = {})</tt> - Returns a new object of the collection type that has been instantiated
+ # Uses the same rules as ActiveRecord::Base.find.
+ # [collection.exist?(...)]
+ # Checks whether an associated object with the given conditions exists.
+ # Uses the same rules as ActiveRecord::Base.exists?.
+ # [collection.build(attributes = {})]
+ # Returns a new object of the collection type that has been instantiated
# with +attributes+ and linked to this object through the join table, but has not yet been saved.
- # * <tt>collection.create(attributes = {})</tt> - Returns a new object of the collection type that has been instantiated
+ # [collection.create(attributes = {})]
+ # Returns a new object of the collection type that has been instantiated
# with +attributes+, linked to this object through the join table, and that has already been saved (if it passed the validation).
#
- # Example: A Developer class declares <tt>has_and_belongs_to_many :projects</tt>, which will add:
+ # (+collection+ is replaced with the symbol passed as the first argument, so
+ # <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>.)
+ #
+ # === Example
+ #
+ # A Developer class declares <tt>has_and_belongs_to_many :projects</tt>, which will add:
# * <tt>Developer#projects</tt>
# * <tt>Developer#projects<<</tt>
# * <tt>Developer#projects.delete</tt>
@@ -994,44 +1129,67 @@ def belongs_to(association_id, options = {})
# * <tt>Developer#projects.empty?</tt>
# * <tt>Developer#projects.size</tt>
# * <tt>Developer#projects.find(id)</tt>
+ # * <tt>Developer#clients.exist?(...)</tt>
# * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("project_id" => id)</tt>)
# * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("project_id" => id); c.save; c</tt>)
# The declaration may include an options hash to specialize the behavior of the association.
#
- # Options are:
- # * <tt>:class_name</tt> - Specify the class name of the association. Use it only if that name can't be inferred
+ # === Options
+ #
+ # [:class_name]
+ # Specify the class name of the association. Use it only if that name can't be inferred
# from the association name. So <tt>has_and_belongs_to_many :projects</tt> will by default be linked to the
# Project class, but if the real class name is SuperProject, you'll have to specify it with this option.
- # * <tt>:join_table</tt> - Specify the name of the join table if the default based on lexical order isn't what you want.
- # WARNING: If you're overwriting the table name of either class, the +table_name+ method MUST be declared underneath any
- # +has_and_belongs_to_many+ declaration in order to work.
- # * <tt>:foreign_key</tt> - Specify the foreign key used for the association. By default this is guessed to be the name
+ # [:join_table]
+ # Specify the name of the join table if the default based on lexical order isn't what you want.
+ # <b>WARNING:</b> If you're overwriting the table name of either class, the +table_name+ method
+ # MUST be declared underneath any +has_and_belongs_to_many+ declaration in order to work.
+ # [:foreign_key]
+ # Specify the foreign key used for the association. By default this is guessed to be the name
# of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_and_belongs_to_many+ association
# will use "person_id" as the default <tt>:foreign_key</tt>.
- # * <tt>:association_foreign_key</tt> - Specify the association foreign key used for the association. By default this is
+ # [:association_foreign_key]
+ # Specify the association foreign key used for the association. By default this is
# guessed to be the name of the associated class in lower-case and "_id" suffixed. So if the associated class is Project,
# the +has_and_belongs_to_many+ association will use "project_id" as the default <tt>:association_foreign_key</tt>.
- # * <tt>:conditions</tt> - Specify the conditions that the associated object must meet in order to be included as a +WHERE+
+ # [:conditions]
+ # Specify the conditions that the associated object must meet in order to be included as a +WHERE+
# SQL fragment, such as <tt>authorized = 1</tt>. Record creations from the association are scoped if a hash is used.
# <tt>has_many :posts, :conditions => {:published => true}</tt> will create published posts with <tt>@blog.posts.create</tt>
# or <tt>@blog.posts.build</tt>.
- # * <tt>:order</tt> - Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
+ # [:order]
+ # Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
# such as <tt>last_name, first_name DESC</tt>
- # * <tt>:uniq</tt> - If true, duplicate associated objects will be ignored by accessors and query methods.
- # * <tt>:finder_sql</tt> - Overwrite the default generated SQL statement used to fetch the association with a manual statement
- # * <tt>:delete_sql</tt> - Overwrite the default generated SQL statement used to remove links between the associated
+ # [:uniq]
+ # If true, duplicate associated objects will be ignored by accessors and query methods.
+ # [:finder_sql]
+ # Overwrite the default generated SQL statement used to fetch the association with a manual statement
+ # [:counter_sql]
+ # Specify a complete SQL statement to fetch the size of the association. If <tt>:finder_sql</tt> is
+ # specified but not <tt>:counter_sql</tt>, <tt>:counter_sql</tt> will be generated by replacing <tt>SELECT ... FROM</tt> with <tt>SELECT COUNT(*) FROM</tt>.
+ # [:delete_sql]
+ # Overwrite the default generated SQL statement used to remove links between the associated
# classes with a manual statement.
- # * <tt>:insert_sql</tt> - Overwrite the default generated SQL statement used to add links between the associated classes
+ # [:insert_sql]
+ # Overwrite the default generated SQL statement used to add links between the associated classes
# with a manual statement.
- # * <tt>:extend</tt> - Anonymous module for extending the proxy, see "Association extensions".
- # * <tt>:include</tt> - Specify second-order associations that should be eager loaded when the collection is loaded.
- # * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
- # * <tt>:limit</tt> - An integer determining the limit on the number of rows that should be returned.
- # * <tt>:offset</tt> - An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
- # * <tt>:select</tt> - By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example, you want to do a join
+ # [:extend]
+ # Anonymous module for extending the proxy, see "Association extensions".
+ # [:include]
+ # Specify second-order associations that should be eager loaded when the collection is loaded.
+ # [:group]
+ # An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
+ # [:limit]
+ # An integer determining the limit on the number of rows that should be returned.
+ # [:offset]
+ # An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
+ # [:select]
+ # By default, this is <tt>*</tt> as in <tt>SELECT * FROM</tt>, but can be changed if, for example, you want to do a join
# but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
- # * <tt>:readonly</tt> - If true, all the associated objects are readonly through the association.
- # * <tt>:validate</tt> - If false, don't validate the associated objects when saving the parent object. +true+ by default.
+ # [:readonly]
+ # If true, all the associated objects are readonly through the association.
+ # [:validate]
+ # If false, don't validate the associated objects when saving the parent object. +true+ by default.
#
# Option examples:
# has_and_belongs_to_many :projects
@@ -1063,12 +1221,11 @@ def destroy_without_callbacks
end
private
- # Generate a join table name from two provided tables names.
- # The order of names in join name is determined by lexical precedence.
- # join_table_name("members", "clubs")
- # => "clubs_members"
- # join_table_name("members", "special_clubs")
- # => "members_special_clubs"
+ # Generates a join table name from two provided table names.
+ # The names in the join table namesme end up in lexicographic order.
+ #
+ # join_table_name("members", "clubs") # => "clubs_members"
+ # join_table_name("members", "special_clubs") # => "members_special_clubs"
def join_table_name(first_table_name, second_table_name)
if first_table_name < second_table_name
join_table = "#{first_table_name}_#{second_table_name}"
@@ -1100,6 +1257,11 @@ def association_accessor_methods(reflection, association_proxy_class)
association.target.nil? ? nil : association
end
+ define_method("loaded_#{reflection.name}?") do
+ association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
+ association && association.loaded?
+ end
+
define_method("#{reflection.name}=") do |new_value|
association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
@@ -1142,7 +1304,11 @@ def collection_reader_method(reflection, association_proxy_class)
end
define_method("#{reflection.name.to_s.singularize}_ids") do
- send(reflection.name).map(&:id)
+ if send(reflection.name).loaded? || reflection.options[:finder_sql]
+ send(reflection.name).map(&:id)
+ else
+ send(reflection.name).all(:select => "#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").map(&:id)
+ end
end
end
@@ -1163,19 +1329,19 @@ def collection_accessor_methods(reflection, association_proxy_class, writer = tr
end
end
end
-
+
def add_single_associated_validation_callbacks(association_name)
method_name = "validate_associated_records_for_#{association_name}".to_sym
define_method(method_name) do
association = instance_variable_get("@#{association_name}")
if !association.nil?
- errors.add "#{association_name}" unless association.target.nil? || association.valid?
+ errors.add association_name unless association.target.nil? || association.valid?
end
end
-
+
validate method_name
end
-
+
def add_multiple_associated_validation_callbacks(association_name)
method_name = "validate_associated_records_for_#{association_name}".to_sym
ivar = "@#{association_name}"
@@ -1191,7 +1357,7 @@ def add_multiple_associated_validation_callbacks(association_name)
else
association.target.select { |record| record.new_record? }
end.each do |record|
- errors.add "#{association_name}" unless record.valid?
+ errors.add association_name unless record.valid?
end
end
end
@@ -1211,7 +1377,7 @@ def add_multiple_associated_save_callbacks(association_name)
method_name = "after_create_or_update_associated_records_for_#{association_name}".to_sym
define_method(method_name) do
- association = instance_variable_get("#{ivar}") if instance_variable_defined?("#{ivar}")
+ association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
records_to_save = if @new_record_before_save
association
@@ -1263,56 +1429,85 @@ def find_with_associations(options = {})
[]
end
+ # Creates before_destroy callback methods that nullify, delete or destroy
+ # has_many associated objects, according to the defined :dependent rule.
+ #
# See HasManyAssociation#delete_records. Dependent associations
# delete children, otherwise foreign key is set to NULL.
- def configure_dependency_for_has_many(reflection)
+ #
+ # The +extra_conditions+ parameter, which is not used within the main
+ # Active Record codebase, is meant to allow plugins to define extra
+ # finder conditions.
+ def configure_dependency_for_has_many(reflection, extra_conditions = nil)
if reflection.options.include?(:dependent)
# Add polymorphic type if the :as option is present
dependent_conditions = []
dependent_conditions << "#{reflection.primary_key_name} = \#{record.quoted_id}"
dependent_conditions << "#{reflection.options[:as]}_type = '#{base_class.name}'" if reflection.options[:as]
dependent_conditions << sanitize_sql(reflection.options[:conditions]) if reflection.options[:conditions]
+ dependent_conditions << extra_conditions if extra_conditions
dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ")
case reflection.options[:dependent]
when :destroy
method_name = "has_many_dependent_destroy_for_#{reflection.name}".to_sym
define_method(method_name) do
- send("#{reflection.name}").each { |o| o.destroy }
+ send(reflection.name).each { |o| o.destroy }
end
before_destroy method_name
when :delete_all
- module_eval "before_destroy { |record| #{reflection.class_name}.delete_all(%(#{dependent_conditions})) }"
+ module_eval %Q{
+ before_destroy do |record|
+ delete_all_has_many_dependencies(record,
+ "#{reflection.name}",
+ #{reflection.class_name},
+ "#{dependent_conditions}")
+ end
+ }
when :nullify
- module_eval "before_destroy { |record| #{reflection.class_name}.update_all(%(#{reflection.primary_key_name} = NULL), %(#{dependent_conditions})) }"
+ module_eval %Q{
+ before_destroy do |record|
+ nullify_has_many_dependencies(record,
+ "#{reflection.name}",
+ #{reflection.class_name},
+ "#{reflection.primary_key_name}",
+ "#{dependent_conditions}")
+ end
+ }
else
raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, or :nullify (#{reflection.options[:dependent].inspect})"
end
end
end
+ # Creates before_destroy callback methods that nullify, delete or destroy
+ # has_one associated objects, according to the defined :dependent rule.
def configure_dependency_for_has_one(reflection)
if reflection.options.include?(:dependent)
case reflection.options[:dependent]
when :destroy
method_name = "has_one_dependent_destroy_for_#{reflection.name}".to_sym
define_method(method_name) do
- association = send("#{reflection.name}")
+ association = send(reflection.name)
association.destroy unless association.nil?
end
before_destroy method_name
when :delete
method_name = "has_one_dependent_delete_for_#{reflection.name}".to_sym
define_method(method_name) do
- association = send("#{reflection.name}")
- association.class.delete(association.id) unless association.nil?
+ # Retrieve the associated object and delete it. The retrieval
+ # is necessary because there may be multiple associated objects
+ # with foreign keys pointing to this object, and we only want
+ # to delete the correct one, not all of them.
+ association = send(reflection.name)
+ association.delete unless association.nil?
end
before_destroy method_name
when :nullify
method_name = "has_one_dependent_nullify_for_#{reflection.name}".to_sym
define_method(method_name) do
- association = send("#{reflection.name}")
- association.update_attribute("#{reflection.primary_key_name}", nil) unless association.nil?
+ association = send(reflection.name)
+ association.update_attribute(reflection.primary_key_name, nil) unless association.nil?
end
before_destroy method_name
else
@@ -1327,15 +1522,15 @@ def configure_dependency_for_belongs_to(reflection)
when :destroy
method_name = "belongs_to_dependent_destroy_for_#{reflection.name}".to_sym
define_method(method_name) do
- association = send("#{reflection.name}")
+ association = send(reflection.name)
association.destroy unless association.nil?
end
before_destroy method_name
when :delete
method_name = "belongs_to_dependent_delete_for_#{reflection.name}".to_sym
define_method(method_name) do
- association = send("#{reflection.name}")
- association.class.delete(association.id) unless association.nil?
+ association = send(reflection.name)
+ association.delete unless association.nil?
end
before_destroy method_name
else
@@ -1344,32 +1539,46 @@ def configure_dependency_for_belongs_to(reflection)
end
end
- def create_has_many_reflection(association_id, options, &extension)
- options.assert_valid_keys(
- :class_name, :table_name, :foreign_key,
- :dependent,
- :select, :conditions, :include, :order, :group, :limit, :offset,
- :as, :through, :source, :source_type,
- :uniq,
- :finder_sql, :counter_sql,
- :before_add, :after_add, :before_remove, :after_remove,
- :extend, :readonly,
- :validate
- )
+ def delete_all_has_many_dependencies(record, reflection_name, association_class, dependent_conditions)
+ association_class.delete_all(dependent_conditions)
+ end
+ def nullify_has_many_dependencies(record, reflection_name, association_class, primary_key_name, dependent_conditions)
+ association_class.update_all("#{primary_key_name} = NULL", dependent_conditions)
+ end
+
+ mattr_accessor :valid_keys_for_has_many_association
+ @@valid_keys_for_has_many_association = [
+ :class_name, :table_name, :foreign_key, :primary_key,
+ :dependent,
+ :select, :conditions, :include, :order, :group, :limit, :offset,
+ :as, :through, :source, :source_type,
+ :uniq,
+ :finder_sql, :counter_sql,
+ :before_add, :after_add, :before_remove, :after_remove,
+ :extend, :readonly,
+ :validate
+ ]
+
+ def create_has_many_reflection(association_id, options, &extension)
+ options.assert_valid_keys(valid_keys_for_has_many_association)
options[:extend] = create_extension_modules(association_id, extension, options[:extend])
create_reflection(:has_many, association_id, options, self)
end
- def create_has_one_reflection(association_id, options)
- options.assert_valid_keys(
- :class_name, :foreign_key, :remote, :select, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :readonly, :validate
- )
+ mattr_accessor :valid_keys_for_has_one_association
+ @@valid_keys_for_has_one_association = [
+ :class_name, :foreign_key, :remote, :select, :conditions, :order,
+ :include, :dependent, :counter_cache, :extend, :as, :readonly,
+ :validate, :primary_key
+ ]
+ def create_has_one_reflection(association_id, options)
+ options.assert_valid_keys(valid_keys_for_has_one_association)
create_reflection(:has_one, association_id, options, self)
end
-
+
def create_has_one_through_reflection(association_id, options)
options.assert_valid_keys(
:class_name, :foreign_key, :remote, :select, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :through, :source, :source_type, :validate
@@ -1377,12 +1586,15 @@ def create_has_one_through_reflection(association_id, options)
create_reflection(:has_one, association_id, options, self)
end
- def create_belongs_to_reflection(association_id, options)
- options.assert_valid_keys(
- :class_name, :foreign_key, :foreign_type, :remote, :select, :conditions, :include, :dependent,
- :counter_cache, :extend, :polymorphic, :readonly, :validate
- )
+ mattr_accessor :valid_keys_for_belongs_to_association
+ @@valid_keys_for_belongs_to_association = [
+ :class_name, :foreign_key, :foreign_type, :remote, :select, :conditions,
+ :include, :dependent, :counter_cache, :extend, :polymorphic, :readonly,
+ :validate
+ ]
+ def create_belongs_to_reflection(association_id, options)
+ options.assert_valid_keys(valid_keys_for_belongs_to_association)
reflection = create_reflection(:belongs_to, association_id, options, self)
if options[:polymorphic]
@@ -1392,16 +1604,19 @@ def create_belongs_to_reflection(association_id, options)
reflection
end
+ mattr_accessor :valid_keys_for_has_and_belongs_to_many_association
+ @@valid_keys_for_has_and_belongs_to_many_association = [
+ :class_name, :table_name, :join_table, :foreign_key, :association_foreign_key,
+ :select, :conditions, :include, :order, :group, :limit, :offset,
+ :uniq,
+ :finder_sql, :counter_sql, :delete_sql, :insert_sql,
+ :before_add, :after_add, :before_remove, :after_remove,
+ :extend, :readonly,
+ :validate
+ ]
+
def create_has_and_belongs_to_many_reflection(association_id, options, &extension)
- options.assert_valid_keys(
- :class_name, :table_name, :join_table, :foreign_key, :association_foreign_key,
- :select, :conditions, :include, :order, :group, :limit, :offset,
- :uniq,
-