mack-active_record 0.8.1 → 0.8.2

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