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,2967 @@
1
+ require 'yaml'
2
+ require 'set'
3
+
4
+ module ActiveRecord #:nodoc:
5
+ # Generic Active Record exception class.
6
+ class ActiveRecordError < StandardError
7
+ end
8
+
9
+ # Raised when the single-table inheritance mechanism fails to locate the subclass
10
+ # (for example due to improper usage of column that +inheritance_column+ points to).
11
+ class SubclassNotFound < ActiveRecordError #:nodoc:
12
+ end
13
+
14
+ # Raised when an object assigned to an association has an incorrect type.
15
+ #
16
+ # class Ticket < ActiveRecord::Base
17
+ # has_many :patches
18
+ # end
19
+ #
20
+ # class Patch < ActiveRecord::Base
21
+ # belongs_to :ticket
22
+ # end
23
+ #
24
+ # # Comments are not patches, this assignment raises AssociationTypeMismatch.
25
+ # @ticket.patches << Comment.new(:content => "Please attach tests to your patch.")
26
+ class AssociationTypeMismatch < ActiveRecordError
27
+ end
28
+
29
+ # Raised when unserialized object's type mismatches one specified for serializable field.
30
+ class SerializationTypeMismatch < ActiveRecordError
31
+ end
32
+
33
+ # Raised when adapter not specified on connection (or configuration file <tt>config/database.yml</tt> misses adapter field).
34
+ class AdapterNotSpecified < ActiveRecordError
35
+ end
36
+
37
+ # Raised when Active Record cannot find database adapter specified in <tt>config/database.yml</tt> or programmatically.
38
+ class AdapterNotFound < ActiveRecordError
39
+ end
40
+
41
+ # Raised when connection to the database could not been established (for example when <tt>connection=</tt> is given a nil object).
42
+ class ConnectionNotEstablished < ActiveRecordError
43
+ end
44
+
45
+ # Raised when Active Record cannot find record by given id or set of ids.
46
+ class RecordNotFound < ActiveRecordError
47
+ end
48
+
49
+ # Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
50
+ # saved because record is invalid.
51
+ class RecordNotSaved < ActiveRecordError
52
+ end
53
+
54
+ # Raised when SQL statement cannot be executed by the database (for example, it's often the case for MySQL when Ruby driver used is too old).
55
+ class StatementInvalid < ActiveRecordError
56
+ end
57
+
58
+ # Raised when number of bind variables in statement given to <tt>:condition</tt> key (for example, when using +find+ method)
59
+ # does not match number of expected variables.
60
+ #
61
+ # For example, in
62
+ #
63
+ # Location.find :all, :conditions => ["lat = ? AND lng = ?", 53.7362]
64
+ #
65
+ # two placeholders are given but only one variable to fill them.
66
+ class PreparedStatementInvalid < ActiveRecordError
67
+ end
68
+
69
+ # Raised on attempt to save stale record. Record is stale when it's being saved in another query after
70
+ # instantiation, for example, when two users edit the same wiki page and one starts editing and saves
71
+ # the page before the other.
72
+ #
73
+ # Read more about optimistic locking in ActiveRecord::Locking module RDoc.
74
+ class StaleObjectError < ActiveRecordError
75
+ end
76
+
77
+ # Raised when association is being configured improperly or
78
+ # user tries to use offset and limit together with has_many or has_and_belongs_to_many associations.
79
+ class ConfigurationError < ActiveRecordError
80
+ end
81
+
82
+ # Raised on attempt to update record that is instantiated as read only.
83
+ class ReadOnlyRecord < ActiveRecordError
84
+ end
85
+
86
+ # ActiveRecord::Transactions::ClassMethods.transaction uses this exception
87
+ # to distinguish a deliberate rollback from other exceptional situations.
88
+ # Normally, raising an exception will cause the +transaction+ method to rollback
89
+ # the database transaction *and* pass on the exception. But if you raise an
90
+ # ActiveRecord::Rollback exception, then the database transaction will be rolled back,
91
+ # without passing on the exception.
92
+ #
93
+ # For example, you could do this in your controller to rollback a transaction:
94
+ #
95
+ # class BooksController < ActionController::Base
96
+ # def create
97
+ # Book.transaction do
98
+ # book = Book.new(params[:book])
99
+ # book.save!
100
+ # if today_is_friday?
101
+ # # The system must fail on Friday so that our support department
102
+ # # won't be out of job. We silently rollback this transaction
103
+ # # without telling the user.
104
+ # raise ActiveRecord::Rollback, "Call tech support!"
105
+ # end
106
+ # end
107
+ # # ActiveRecord::Rollback is the only exception that won't be passed on
108
+ # # by ActiveRecord::Base.transaction, so this line will still be reached
109
+ # # even on Friday.
110
+ # redirect_to root_url
111
+ # end
112
+ # end
113
+ class Rollback < ActiveRecordError
114
+ end
115
+
116
+ # Raised when attribute has a name reserved by Active Record (when attribute has name of one of Active Record instance methods).
117
+ class DangerousAttributeError < ActiveRecordError
118
+ end
119
+
120
+ # Raised when you've tried to access a column which wasn't loaded by your finder.
121
+ # Typically this is because <tt>:select</tt> has been specified.
122
+ class MissingAttributeError < NoMethodError
123
+ end
124
+
125
+ # Raised when unknown attributes are supplied via mass assignment.
126
+ class UnknownAttributeError < NoMethodError
127
+ end
128
+
129
+ # Raised when an error occurred while doing a mass assignment to an attribute through the
130
+ # <tt>attributes=</tt> method. The exception has an +attribute+ property that is the name of the
131
+ # offending attribute.
132
+ class AttributeAssignmentError < ActiveRecordError
133
+ attr_reader :exception, :attribute
134
+ def initialize(message, exception, attribute)
135
+ @exception = exception
136
+ @attribute = attribute
137
+ @message = message
138
+ end
139
+ end
140
+
141
+ # Raised when there are multiple errors while doing a mass assignment through the +attributes+
142
+ # method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
143
+ # objects, each corresponding to the error while assigning to an attribute.
144
+ class MultiparameterAssignmentErrors < ActiveRecordError
145
+ attr_reader :errors
146
+ def initialize(errors)
147
+ @errors = errors
148
+ end
149
+ end
150
+
151
+ # Active Record objects don't specify their attributes directly, but rather infer them from the table definition with
152
+ # which they're linked. Adding, removing, and changing attributes and their type is done directly in the database. Any change
153
+ # is instantly reflected in the Active Record objects. The mapping that binds a given Active Record class to a certain
154
+ # database table will happen automatically in most common cases, but can be overwritten for the uncommon ones.
155
+ #
156
+ # See the mapping rules in table_name and the full example in link:files/README.html for more insight.
157
+ #
158
+ # == Creation
159
+ #
160
+ # Active Records accept constructor parameters either in a hash or as a block. The hash method is especially useful when
161
+ # you're receiving the data from somewhere else, like an HTTP request. It works like this:
162
+ #
163
+ # user = User.new(:name => "David", :occupation => "Code Artist")
164
+ # user.name # => "David"
165
+ #
166
+ # You can also use block initialization:
167
+ #
168
+ # user = User.new do |u|
169
+ # u.name = "David"
170
+ # u.occupation = "Code Artist"
171
+ # end
172
+ #
173
+ # And of course you can just create a bare object and specify the attributes after the fact:
174
+ #
175
+ # user = User.new
176
+ # user.name = "David"
177
+ # user.occupation = "Code Artist"
178
+ #
179
+ # == Conditions
180
+ #
181
+ # Conditions can either be specified as a string, array, or hash representing the WHERE-part of an SQL statement.
182
+ # The array form is to be used when the condition input is tainted and requires sanitization. The string form can
183
+ # be used for statements that don't involve tainted data. The hash form works much like the array form, except
184
+ # only equality and range is possible. Examples:
185
+ #
186
+ # class User < ActiveRecord::Base
187
+ # def self.authenticate_unsafely(user_name, password)
188
+ # find(:first, :conditions => "user_name = '#{user_name}' AND password = '#{password}'")
189
+ # end
190
+ #
191
+ # def self.authenticate_safely(user_name, password)
192
+ # find(:first, :conditions => [ "user_name = ? AND password = ?", user_name, password ])
193
+ # end
194
+ #
195
+ # def self.authenticate_safely_simply(user_name, password)
196
+ # find(:first, :conditions => { :user_name => user_name, :password => password })
197
+ # end
198
+ # end
199
+ #
200
+ # The <tt>authenticate_unsafely</tt> method inserts the parameters directly into the query and is thus susceptible to SQL-injection
201
+ # attacks if the <tt>user_name</tt> and +password+ parameters come directly from an HTTP request. The <tt>authenticate_safely</tt> and
202
+ # <tt>authenticate_safely_simply</tt> both will sanitize the <tt>user_name</tt> and +password+ before inserting them in the query,
203
+ # which will ensure that an attacker can't escape the query and fake the login (or worse).
204
+ #
205
+ # When using multiple parameters in the conditions, it can easily become hard to read exactly what the fourth or fifth
206
+ # question mark is supposed to represent. In those cases, you can resort to named bind variables instead. That's done by replacing
207
+ # the question marks with symbols and supplying a hash with values for the matching symbol keys:
208
+ #
209
+ # Company.find(:first, :conditions => [
210
+ # "id = :id AND name = :name AND division = :division AND created_at > :accounting_date",
211
+ # { :id => 3, :name => "37signals", :division => "First", :accounting_date => '2005-01-01' }
212
+ # ])
213
+ #
214
+ # Similarly, a simple hash without a statement will generate conditions based on equality with the SQL AND
215
+ # operator. For instance:
216
+ #
217
+ # Student.find(:all, :conditions => { :first_name => "Harvey", :status => 1 })
218
+ # Student.find(:all, :conditions => params[:student])
219
+ #
220
+ # A range may be used in the hash to use the SQL BETWEEN operator:
221
+ #
222
+ # Student.find(:all, :conditions => { :grade => 9..12 })
223
+ #
224
+ # An array may be used in the hash to use the SQL IN operator:
225
+ #
226
+ # Student.find(:all, :conditions => { :grade => [9,11,12] })
227
+ #
228
+ # == Overwriting default accessors
229
+ #
230
+ # All column values are automatically available through basic accessors on the Active Record object, but sometimes you
231
+ # want to specialize this behavior. This can be done by overwriting the default accessors (using the same
232
+ # name as the attribute) and calling <tt>read_attribute(attr_name)</tt> and <tt>write_attribute(attr_name, value)</tt> to actually change things.
233
+ # Example:
234
+ #
235
+ # class Song < ActiveRecord::Base
236
+ # # Uses an integer of seconds to hold the length of the song
237
+ #
238
+ # def length=(minutes)
239
+ # write_attribute(:length, minutes.to_i * 60)
240
+ # end
241
+ #
242
+ # def length
243
+ # read_attribute(:length) / 60
244
+ # end
245
+ # end
246
+ #
247
+ # You can alternatively use <tt>self[:attribute]=(value)</tt> and <tt>self[:attribute]</tt> instead of <tt>write_attribute(:attribute, value)</tt> and
248
+ # <tt>read_attribute(:attribute)</tt> as a shorter form.
249
+ #
250
+ # == Attribute query methods
251
+ #
252
+ # In addition to the basic accessors, query methods are also automatically available on the Active Record object.
253
+ # Query methods allow you to test whether an attribute value is present.
254
+ #
255
+ # For example, an Active Record User with the <tt>name</tt> attribute has a <tt>name?</tt> method that you can call
256
+ # to determine whether the user has a name:
257
+ #
258
+ # user = User.new(:name => "David")
259
+ # user.name? # => true
260
+ #
261
+ # anonymous = User.new(:name => "")
262
+ # anonymous.name? # => false
263
+ #
264
+ # == Accessing attributes before they have been typecasted
265
+ #
266
+ # Sometimes you want to be able to read the raw attribute data without having the column-determined typecast run its course first.
267
+ # That can be done by using the <tt><attribute>_before_type_cast</tt> accessors that all attributes have. For example, if your Account model
268
+ # has a <tt>balance</tt> attribute, you can call <tt>account.balance_before_type_cast</tt> or <tt>account.id_before_type_cast</tt>.
269
+ #
270
+ # This is especially useful in validation situations where the user might supply a string for an integer field and you want to display
271
+ # the original string back in an error message. Accessing the attribute normally would typecast the string to 0, which isn't what you
272
+ # want.
273
+ #
274
+ # == Dynamic attribute-based finders
275
+ #
276
+ # Dynamic attribute-based finders are a cleaner way of getting (and/or creating) objects by simple queries without turning to SQL. They work by
277
+ # appending the name of an attribute to <tt>find_by_</tt>, <tt>find_last_by_</tt>, or <tt>find_all_by_</tt>, so you get finders like <tt>Person.find_by_user_name</tt>,
278
+ # <tt>Person.find_all_by_last_name</tt>, and <tt>Payment.find_by_transaction_id</tt>. So instead of writing
279
+ # <tt>Person.find(:first, :conditions => ["user_name = ?", user_name])</tt>, you just do <tt>Person.find_by_user_name(user_name)</tt>.
280
+ # And instead of writing <tt>Person.find(:all, :conditions => ["last_name = ?", last_name])</tt>, you just do <tt>Person.find_all_by_last_name(last_name)</tt>.
281
+ #
282
+ # It's also possible to use multiple attributes in the same find by separating them with "_and_", so you get finders like
283
+ # <tt>Person.find_by_user_name_and_password</tt> or even <tt>Payment.find_by_purchaser_and_state_and_country</tt>. So instead of writing
284
+ # <tt>Person.find(:first, :conditions => ["user_name = ? AND password = ?", user_name, password])</tt>, you just do
285
+ # <tt>Person.find_by_user_name_and_password(user_name, password)</tt>.
286
+ #
287
+ # It's even possible to use all the additional parameters to find. For example, the full interface for <tt>Payment.find_all_by_amount</tt>
288
+ # is actually <tt>Payment.find_all_by_amount(amount, options)</tt>. And the full interface to <tt>Person.find_by_user_name</tt> is
289
+ # actually <tt>Person.find_by_user_name(user_name, options)</tt>. So you could call <tt>Payment.find_all_by_amount(50, :order => "created_on")</tt>.
290
+ # Also you may call <tt>Payment.find_last_by_amount(amount, options)</tt> returning the last record matching that amount and options.
291
+ #
292
+ # The same dynamic finder style can be used to create the object if it doesn't already exist. This dynamic finder is called with
293
+ # <tt>find_or_create_by_</tt> and will return the object if it already exists and otherwise creates it, then returns it. Protected attributes won't be set unless they are given in a block. For example:
294
+ #
295
+ # # No 'Summer' tag exists
296
+ # Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer")
297
+ #
298
+ # # Now the 'Summer' tag does exist
299
+ # Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer")
300
+ #
301
+ # # Now 'Bob' exist and is an 'admin'
302
+ # User.find_or_create_by_name('Bob', :age => 40) { |u| u.admin = true }
303
+ #
304
+ # Use the <tt>find_or_initialize_by_</tt> finder if you want to return a new record without saving it first. Protected attributes won't be set unless they are given in a block. For example:
305
+ #
306
+ # # No 'Winter' tag exists
307
+ # winter = Tag.find_or_initialize_by_name("Winter")
308
+ # winter.new_record? # true
309
+ #
310
+ # To find by a subset of the attributes to be used for instantiating a new object, pass a hash instead of
311
+ # a list of parameters. For example:
312
+ #
313
+ # Tag.find_or_create_by_name(:name => "rails", :creator => current_user)
314
+ #
315
+ # That will either find an existing tag named "rails", or create a new one while setting the user that created it.
316
+ #
317
+ # == Saving arrays, hashes, and other non-mappable objects in text columns
318
+ #
319
+ # Active Record can serialize any object in text columns using YAML. To do so, you must specify this with a call to the class method +serialize+.
320
+ # This makes it possible to store arrays, hashes, and other non-mappable objects without doing any additional work. Example:
321
+ #
322
+ # class User < ActiveRecord::Base
323
+ # serialize :preferences
324
+ # end
325
+ #
326
+ # user = User.create(:preferences => { "background" => "black", "display" => large })
327
+ # User.find(user.id).preferences # => { "background" => "black", "display" => large }
328
+ #
329
+ # You can also specify a class option as the second parameter that'll raise an exception if a serialized object is retrieved as a
330
+ # descendent of a class not in the hierarchy. Example:
331
+ #
332
+ # class User < ActiveRecord::Base
333
+ # serialize :preferences, Hash
334
+ # end
335
+ #
336
+ # user = User.create(:preferences => %w( one two three ))
337
+ # User.find(user.id).preferences # raises SerializationTypeMismatch
338
+ #
339
+ # == Single table inheritance
340
+ #
341
+ # Active Record allows inheritance by storing the name of the class in a column that by default is named "type" (can be changed
342
+ # by overwriting <tt>Base.inheritance_column</tt>). This means that an inheritance looking like this:
343
+ #
344
+ # class Company < ActiveRecord::Base; end
345
+ # class Firm < Company; end
346
+ # class Client < Company; end
347
+ # class PriorityClient < Client; end
348
+ #
349
+ # When you do <tt>Firm.create(:name => "37signals")</tt>, this record will be saved in the companies table with type = "Firm". You can then
350
+ # fetch this row again using <tt>Company.find(:first, "name = '37signals'")</tt> and it will return a Firm object.
351
+ #
352
+ # If you don't have a type column defined in your table, single-table inheritance won't be triggered. In that case, it'll work just
353
+ # like normal subclasses with no special magic for differentiating between them or reloading the right type with find.
354
+ #
355
+ # Note, all the attributes for all the cases are kept in the same table. Read more:
356
+ # http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
357
+ #
358
+ # == Connection to multiple databases in different models
359
+ #
360
+ # Connections are usually created through ActiveRecord::Base.establish_connection and retrieved by ActiveRecord::Base.connection.
361
+ # All classes inheriting from ActiveRecord::Base will use this connection. But you can also set a class-specific connection.
362
+ # For example, if Course is an ActiveRecord::Base, but resides in a different database, you can just say <tt>Course.establish_connection</tt>
363
+ # and Course and all of its subclasses will use this connection instead.
364
+ #
365
+ # This feature is implemented by keeping a connection pool in ActiveRecord::Base that is a Hash indexed by the class. If a connection is
366
+ # requested, the retrieve_connection method will go up the class-hierarchy until a connection is found in the connection pool.
367
+ #
368
+ # == Exceptions
369
+ #
370
+ # * ActiveRecordError - Generic error class and superclass of all other errors raised by Active Record.
371
+ # * AdapterNotSpecified - The configuration hash used in <tt>establish_connection</tt> didn't include an
372
+ # <tt>:adapter</tt> key.
373
+ # * AdapterNotFound - The <tt>:adapter</tt> key used in <tt>establish_connection</tt> specified a non-existent adapter
374
+ # (or a bad spelling of an existing one).
375
+ # * AssociationTypeMismatch - The object assigned to the association wasn't of the type specified in the association definition.
376
+ # * SerializationTypeMismatch - The serialized object wasn't of the class specified as the second parameter.
377
+ # * ConnectionNotEstablished+ - No connection has been established. Use <tt>establish_connection</tt> before querying.
378
+ # * RecordNotFound - No record responded to the +find+ method. Either the row with the given ID doesn't exist
379
+ # or the row didn't meet the additional restrictions. Some +find+ calls do not raise this exception to signal
380
+ # nothing was found, please check its documentation for further details.
381
+ # * StatementInvalid - The database server rejected the SQL statement. The precise error is added in the message.
382
+ # * MultiparameterAssignmentErrors - Collection of errors that occurred during a mass assignment using the
383
+ # <tt>attributes=</tt> method. The +errors+ property of this exception contains an array of AttributeAssignmentError
384
+ # objects that should be inspected to determine which attributes triggered the errors.
385
+ # * AttributeAssignmentError - An error occurred while doing a mass assignment through the <tt>attributes=</tt> method.
386
+ # You can inspect the +attribute+ property of the exception object to determine which attribute triggered the error.
387
+ #
388
+ # *Note*: The attributes listed are class-level attributes (accessible from both the class and instance level).
389
+ # So it's possible to assign a logger to the class through <tt>Base.logger=</tt> which will then be used by all
390
+ # instances in the current object space.
391
+ class Base
392
+ # Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then passed
393
+ # on to any new database connections made and which can be retrieved on both a class and instance level by calling +logger+.
394
+ cattr_accessor :logger, :instance_writer => false
395
+
396
+ def self.inherited(child) #:nodoc:
397
+ @@subclasses[self] ||= []
398
+ @@subclasses[self] << child
399
+ super
400
+ end
401
+
402
+ def self.reset_subclasses #:nodoc:
403
+ nonreloadables = []
404
+ subclasses.each do |klass|
405
+ unless ActiveSupport::Dependencies.autoloaded? klass
406
+ nonreloadables << klass
407
+ next
408
+ end
409
+ klass.instance_variables.each { |var| klass.send(:remove_instance_variable, var) }
410
+ klass.instance_methods(false).each { |m| klass.send :undef_method, m }
411
+ end
412
+ @@subclasses = {}
413
+ nonreloadables.each { |klass| (@@subclasses[klass.superclass] ||= []) << klass }
414
+ end
415
+
416
+ @@subclasses = {}
417
+
418
+ # Contains the database configuration - as is typically stored in config/database.yml -
419
+ # as a Hash.
420
+ #
421
+ # For example, the following database.yml...
422
+ #
423
+ # development:
424
+ # adapter: sqlite3
425
+ # database: db/development.sqlite3
426
+ #
427
+ # production:
428
+ # adapter: sqlite3
429
+ # database: db/production.sqlite3
430
+ #
431
+ # ...would result in ActiveRecord::Base.configurations to look like this:
432
+ #
433
+ # {
434
+ # 'development' => {
435
+ # 'adapter' => 'sqlite3',
436
+ # 'database' => 'db/development.sqlite3'
437
+ # },
438
+ # 'production' => {
439
+ # 'adapter' => 'sqlite3',
440
+ # 'database' => 'db/production.sqlite3'
441
+ # }
442
+ # }
443
+ cattr_accessor :configurations, :instance_writer => false
444
+ @@configurations = {}
445
+
446
+ # Accessor for the prefix type that will be prepended to every primary key column name. The options are :table_name and
447
+ # :table_name_with_underscore. If the first is specified, the Product class will look for "productid" instead of "id" as
448
+ # the primary column. If the latter is specified, the Product class will look for "product_id" instead of "id". Remember
449
+ # that this is a global setting for all Active Records.
450
+ cattr_accessor :primary_key_prefix_type, :instance_writer => false
451
+ @@primary_key_prefix_type = nil
452
+
453
+ # Accessor for the name of the prefix string to prepend to every table name. So if set to "basecamp_", all
454
+ # table names will be named like "basecamp_projects", "basecamp_people", etc. This is a convenient way of creating a namespace
455
+ # for tables in a shared database. By default, the prefix is the empty string.
456
+ cattr_accessor :table_name_prefix, :instance_writer => false
457
+ @@table_name_prefix = ""
458
+
459
+ # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
460
+ # "people_basecamp"). By default, the suffix is the empty string.
461
+ cattr_accessor :table_name_suffix, :instance_writer => false
462
+ @@table_name_suffix = ""
463
+
464
+ # Indicates whether table names should be the pluralized versions of the corresponding class names.
465
+ # If true, the default table name for a Product class will be +products+. If false, it would just be +product+.
466
+ # See table_name for the full rules on table/class naming. This is true, by default.
467
+ cattr_accessor :pluralize_table_names, :instance_writer => false
468
+ @@pluralize_table_names = true
469
+
470
+ # Determines whether to use ANSI codes to colorize the logging statements committed by the connection adapter. These colors
471
+ # make it much easier to overview things during debugging (when used through a reader like +tail+ and on a black background), but
472
+ # may complicate matters if you use software like syslog. This is true, by default.
473
+ cattr_accessor :colorize_logging, :instance_writer => false
474
+ @@colorize_logging = true
475
+
476
+ # Determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling dates and times from the database.
477
+ # This is set to :local by default.
478
+ cattr_accessor :default_timezone, :instance_writer => false
479
+ @@default_timezone = :local
480
+
481
+ # Specifies the format to use when dumping the database schema with Rails'
482
+ # Rakefile. If :sql, the schema is dumped as (potentially database-
483
+ # specific) SQL statements. If :ruby, the schema is dumped as an
484
+ # ActiveRecord::Schema file which can be loaded into any database that
485
+ # supports migrations. Use :ruby if you want to have different database
486
+ # adapters for, e.g., your development and test environments.
487
+ cattr_accessor :schema_format , :instance_writer => false
488
+ @@schema_format = :ruby
489
+
490
+ # Specify whether or not to use timestamps for migration numbers
491
+ cattr_accessor :timestamped_migrations , :instance_writer => false
492
+ @@timestamped_migrations = true
493
+
494
+ # Determine whether to store the full constant name including namespace when using STI
495
+ superclass_delegating_accessor :store_full_sti_class
496
+ self.store_full_sti_class = false
497
+
498
+ class << self # Class methods
499
+ # Find operates with four different retrieval approaches:
500
+ #
501
+ # * Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]).
502
+ # If no record can be found for all of the listed ids, then RecordNotFound will be raised.
503
+ # * Find first - This will return the first record matched by the options used. These options can either be specific
504
+ # conditions or merely an order. If no record can be matched, +nil+ is returned. Use
505
+ # <tt>Model.find(:first, *args)</tt> or its shortcut <tt>Model.first(*args)</tt>.
506
+ # * Find last - This will return the last record matched by the options used. These options can either be specific
507
+ # conditions or merely an order. If no record can be matched, +nil+ is returned. Use
508
+ # <tt>Model.find(:last, *args)</tt> or its shortcut <tt>Model.last(*args)</tt>.
509
+ # * Find all - This will return all the records matched by the options used.
510
+ # If no records are found, an empty array is returned. Use
511
+ # <tt>Model.find(:all, *args)</tt> or its shortcut <tt>Model.all(*args)</tt>.
512
+ #
513
+ # All approaches accept an options hash as their last parameter.
514
+ #
515
+ # ==== Parameters
516
+ #
517
+ # * <tt>:conditions</tt> - An SQL fragment like "administrator = 1", <tt>[ "user_name = ?", username ]</tt>, or <tt>["user_name = :user_name", { :user_name => user_name }]</tt>. See conditions in the intro.
518
+ # * <tt>:order</tt> - An SQL fragment like "created_at DESC, name".
519
+ # * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
520
+ # * <tt>:limit</tt> - An integer determining the limit on the number of rows that should be returned.
521
+ # * <tt>:offset</tt> - An integer determining the offset from where the rows should be fetched. So at 5, it would skip rows 0 through 4.
522
+ # * <tt>:joins</tt> - Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed)
523
+ # or named associations in the same form used for the <tt>:include</tt> option, which will perform an <tt>INNER JOIN</tt> on the associated table(s).
524
+ # If the value is a string, then the records will be returned read-only since they will have attributes that do not correspond to the table's columns.
525
+ # Pass <tt>:readonly => false</tt> to override.
526
+ # * <tt>:include</tt> - Names associations that should be loaded alongside. The symbols named refer
527
+ # to already defined associations. See eager loading under Associations.
528
+ # * <tt>:select</tt> - By default, this is "*" as in "SELECT * FROM", but can be changed if you, for example, want to do a join but not
529
+ # include the joined columns. Takes a string with the SELECT SQL fragment (e.g. "id, name").
530
+ # * <tt>:from</tt> - By default, this is the table name of the class, but can be changed to an alternate table name (or even the name
531
+ # of a database view).
532
+ # * <tt>:readonly</tt> - Mark the returned records read-only so they cannot be saved or updated.
533
+ # * <tt>:lock</tt> - An SQL fragment like "FOR UPDATE" or "LOCK IN SHARE MODE".
534
+ # <tt>:lock => true</tt> gives connection's default exclusive lock, usually "FOR UPDATE".
535
+ #
536
+ # ==== Examples
537
+ #
538
+ # # find by id
539
+ # Person.find(1) # returns the object for ID = 1
540
+ # Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6)
541
+ # Person.find([7, 17]) # returns an array for objects with IDs in (7, 17)
542
+ # Person.find([1]) # returns an array for the object with ID = 1
543
+ # Person.find(1, :conditions => "administrator = 1", :order => "created_on DESC")
544
+ #
545
+ # Note that returned records may not be in the same order as the ids you
546
+ # provide since database rows are unordered. Give an explicit <tt>:order</tt>
547
+ # to ensure the results are sorted.
548
+ #
549
+ # ==== Examples
550
+ #
551
+ # # find first
552
+ # Person.find(:first) # returns the first object fetched by SELECT * FROM people
553
+ # Person.find(:first, :conditions => [ "user_name = ?", user_name])
554
+ # Person.find(:first, :conditions => [ "user_name = :u", { :u => user_name }])
555
+ # Person.find(:first, :order => "created_on DESC", :offset => 5)
556
+ #
557
+ # # find last
558
+ # Person.find(:last) # returns the last object fetched by SELECT * FROM people
559
+ # Person.find(:last, :conditions => [ "user_name = ?", user_name])
560
+ # Person.find(:last, :order => "created_on DESC", :offset => 5)
561
+ #
562
+ # # find all
563
+ # Person.find(:all) # returns an array of objects for all the rows fetched by SELECT * FROM people
564
+ # Person.find(:all, :conditions => [ "category IN (?)", categories], :limit => 50)
565
+ # Person.find(:all, :conditions => { :friends => ["Bob", "Steve", "Fred"] }
566
+ # Person.find(:all, :offset => 10, :limit => 10)
567
+ # Person.find(:all, :include => [ :account, :friends ])
568
+ # Person.find(:all, :group => "category")
569
+ #
570
+ # Example for find with a lock: Imagine two concurrent transactions:
571
+ # each will read <tt>person.visits == 2</tt>, add 1 to it, and save, resulting
572
+ # in two saves of <tt>person.visits = 3</tt>. By locking the row, the second
573
+ # transaction has to wait until the first is finished; we get the
574
+ # expected <tt>person.visits == 4</tt>.
575
+ #
576
+ # Person.transaction do
577
+ # person = Person.find(1, :lock => true)
578
+ # person.visits += 1
579
+ # person.save!
580
+ # end
581
+ def find(*args)
582
+ options = args.extract_options!
583
+ validate_find_options(options)
584
+ set_readonly_option!(options)
585
+
586
+ case args.first
587
+ when :first then find_initial(options)
588
+ when :last then find_last(options)
589
+ when :all then find_every(options)
590
+ else find_from_ids(args, options)
591
+ end
592
+ end
593
+
594
+ # A convenience wrapper for <tt>find(:first, *args)</tt>. You can pass in all the
595
+ # same arguments to this method as you can to <tt>find(:first)</tt>.
596
+ def first(*args)
597
+ find(:first, *args)
598
+ end
599
+
600
+ # A convenience wrapper for <tt>find(:last, *args)</tt>. You can pass in all the
601
+ # same arguments to this method as you can to <tt>find(:last)</tt>.
602
+ def last(*args)
603
+ find(:last, *args)
604
+ end
605
+
606
+ # This is an alias for find(:all). You can pass in all the same arguments to this method as you can
607
+ # to find(:all)
608
+ def all(*args)
609
+ find(:all, *args)
610
+ end
611
+
612
+ # Executes a custom SQL query against your database and returns all the results. The results will
613
+ # be returned as an array with columns requested encapsulated as attributes of the model you call
614
+ # this method from. If you call <tt>Product.find_by_sql</tt> then the results will be returned in
615
+ # a Product object with the attributes you specified in the SQL query.
616
+ #
617
+ # If you call a complicated SQL query which spans multiple tables the columns specified by the
618
+ # SELECT will be attributes of the model, whether or not they are columns of the corresponding
619
+ # table.
620
+ #
621
+ # The +sql+ parameter is a full SQL query as a string. It will be called as is, there will be
622
+ # no database agnostic conversions performed. This should be a last resort because using, for example,
623
+ # MySQL specific terms will lock you to using that particular database engine or require you to
624
+ # change your call if you switch engines.
625
+ #
626
+ # ==== Examples
627
+ # # A simple SQL query spanning multiple tables
628
+ # Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id"
629
+ # > [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "first_name"=>"Quentin"}>, ...]
630
+ #
631
+ # # You can use the same string replacement techniques as you can with ActiveRecord#find
632
+ # Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
633
+ # > [#<Post:0x36bff9c @attributes={"first_name"=>"The Cheap Man Buys Twice"}>, ...]
634
+ def find_by_sql(sql)
635
+ connection.select_all(sanitize_sql(sql), "#{name} Load").collect! { |record| instantiate(record) }
636
+ end
637
+
638
+ # Checks whether a record exists in the database that matches conditions given. These conditions
639
+ # can either be a single integer representing a primary key id to be found, or a condition to be
640
+ # matched like using ActiveRecord#find.
641
+ #
642
+ # The +id_or_conditions+ parameter can be an Integer or a String if you want to search the primary key
643
+ # column of the table for a matching id, or if you're looking to match against a condition you can use
644
+ # an Array or a Hash.
645
+ #
646
+ # Possible gotcha: You can't pass in a condition as a string e.g. "name = 'Jamie'", this would be
647
+ # sanitized and then queried against the primary key column as "id = 'name = \'Jamie"
648
+ #
649
+ # ==== Examples
650
+ # Person.exists?(5)
651
+ # Person.exists?('5')
652
+ # Person.exists?(:name => "David")
653
+ # Person.exists?(['name LIKE ?', "%#{query}%"])
654
+ def exists?(id_or_conditions)
655
+ connection.select_all(
656
+ construct_finder_sql(
657
+ :select => "#{quoted_table_name}.#{primary_key}",
658
+ :conditions => expand_id_conditions(id_or_conditions),
659
+ :limit => 1
660
+ ),
661
+ "#{name} Exists"
662
+ ).size > 0
663
+ end
664
+
665
+ # Creates an object (or multiple objects) and saves it to the database, if validations pass.
666
+ # The resulting object is returned whether the object was saved successfully to the database or not.
667
+ #
668
+ # The +attributes+ parameter can be either be a Hash or an Array of Hashes. These Hashes describe the
669
+ # attributes on the objects that are to be created.
670
+ #
671
+ # ==== Examples
672
+ # # Create a single new object
673
+ # User.create(:first_name => 'Jamie')
674
+ #
675
+ # # Create an Array of new objects
676
+ # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])
677
+ #
678
+ # # Create a single object and pass it into a block to set other attributes.
679
+ # User.create(:first_name => 'Jamie') do |u|
680
+ # u.is_admin = false
681
+ # end
682
+ #
683
+ # # Creating an Array of new objects using a block, where the block is executed for each object:
684
+ # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u|
685
+ # u.is_admin = false
686
+ # end
687
+ def create(attributes = nil, &block)
688
+ if attributes.is_a?(Array)
689
+ attributes.collect { |attr| create(attr, &block) }
690
+ else
691
+ object = new(attributes)
692
+ yield(object) if block_given?
693
+ object.save
694
+ object
695
+ end
696
+ end
697
+
698
+ # Updates an object (or multiple objects) and saves it to the database, if validations pass.
699
+ # The resulting object is returned whether the object was saved successfully to the database or not.
700
+ #
701
+ # ==== Parameters
702
+ #
703
+ # * +id+ - This should be the id or an array of ids to be updated.
704
+ # * +attributes+ - This should be a Hash of attributes to be set on the object, or an array of Hashes.
705
+ #
706
+ # ==== Examples
707
+ #
708
+ # # Updating one record:
709
+ # Person.update(15, { :user_name => 'Samuel', :group => 'expert' })
710
+ #
711
+ # # Updating multiple records:
712
+ # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
713
+ # Person.update(people.keys, people.values)
714
+ def update(id, attributes)
715
+ if id.is_a?(Array)
716
+ idx = -1
717
+ id.collect { |one_id| idx += 1; update(one_id, attributes[idx]) }
718
+ else
719
+ object = find(id)
720
+ object.update_attributes(attributes)
721
+ object
722
+ end
723
+ end
724
+
725
+ # Delete an object (or multiple objects) where the +id+ given matches the primary_key. A SQL +DELETE+ command
726
+ # is executed on the database which means that no callbacks are fired off running this. This is an efficient method
727
+ # of deleting records that don't need cleaning up after or other actions to be taken.
728
+ #
729
+ # Objects are _not_ instantiated with this method, and so +:dependent+ rules
730
+ # defined on associations are not honered.
731
+ #
732
+ # ==== Parameters
733
+ #
734
+ # * +id+ - Can be either an Integer or an Array of Integers.
735
+ #
736
+ # ==== Examples
737
+ #
738
+ # # Delete a single object
739
+ # Todo.delete(1)
740
+ #
741
+ # # Delete multiple objects
742
+ # todos = [1,2,3]
743
+ # Todo.delete(todos)
744
+ def delete(id)
745
+ delete_all([ "#{connection.quote_column_name(primary_key)} IN (?)", id ])
746
+ end
747
+
748
+ # Destroy an object (or multiple objects) that has the given id, the object is instantiated first,
749
+ # therefore all callbacks and filters are fired off before the object is deleted. This method is
750
+ # less efficient than ActiveRecord#delete but allows cleanup methods and other actions to be run.
751
+ #
752
+ # This essentially finds the object (or multiple objects) with the given id, creates a new object
753
+ # from the attributes, and then calls destroy on it.
754
+ #
755
+ # ==== Parameters
756
+ #
757
+ # * +id+ - Can be either an Integer or an Array of Integers.
758
+ #
759
+ # ==== Examples
760
+ #
761
+ # # Destroy a single object
762
+ # Todo.destroy(1)
763
+ #
764
+ # # Destroy multiple objects
765
+ # todos = [1,2,3]
766
+ # Todo.destroy(todos)
767
+ def destroy(id)
768
+ if id.is_a?(Array)
769
+ id.map { |one_id| destroy(one_id) }
770
+ else
771
+ find(id).destroy
772
+ end
773
+ end
774
+
775
+ # Updates all records with details given if they match a set of conditions supplied, limits and order can
776
+ # also be supplied. This method constructs a single SQL UPDATE statement and sends it straight to the
777
+ # database. It does not instantiate the involved models and it does not trigger Active Record callbacks.
778
+ #
779
+ # ==== Parameters
780
+ #
781
+ # * +updates+ - A string of column and value pairs that will be set on any records that match conditions.
782
+ # What goes into the SET clause.
783
+ # * +conditions+ - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro for more info.
784
+ # * +options+ - Additional options are <tt>:limit</tt> and <tt>:order</tt>, see the examples for usage.
785
+ #
786
+ # ==== Examples
787
+ #
788
+ # # Update all billing objects with the 3 different attributes given
789
+ # Billing.update_all( "category = 'authorized', approved = 1, author = 'David'" )
790
+ #
791
+ # # Update records that match our conditions
792
+ # Billing.update_all( "author = 'David'", "title LIKE '%Rails%'" )
793
+ #
794
+ # # Update records that match our conditions but limit it to 5 ordered by date
795
+ # Billing.update_all( "author = 'David'", "title LIKE '%Rails%'",
796
+ # :order => 'created_at', :limit => 5 )
797
+ def update_all(updates, conditions = nil, options = {})
798
+ sql = "UPDATE #{quoted_table_name} SET #{sanitize_sql_for_assignment(updates)} "
799
+
800
+ scope = scope(:find)
801
+
802
+ select_sql = ""
803
+ add_conditions!(select_sql, conditions, scope)
804
+
805
+ if options.has_key?(:limit) || (scope && scope[:limit])
806
+ # Only take order from scope if limit is also provided by scope, this
807
+ # is useful for updating a has_many association with a limit.
808
+ add_order!(select_sql, options[:order], scope)
809
+
810
+ add_limit!(select_sql, options, scope)
811
+ sql.concat(connection.limited_update_conditions(select_sql, quoted_table_name, connection.quote_column_name(primary_key)))
812
+ else
813
+ add_order!(select_sql, options[:order], nil)
814
+ sql.concat(select_sql)
815
+ end
816
+
817
+ connection.update(sql, "#{name} Update")
818
+ end
819
+
820
+ # Destroys the records matching +conditions+ by instantiating each record and calling their +destroy+ method.
821
+ # This means at least 2*N database queries to destroy N records, so avoid +destroy_all+ if you are deleting
822
+ # many records. If you want to simply delete records without worrying about dependent associations or
823
+ # callbacks, use the much faster +delete_all+ method instead.
824
+ #
825
+ # ==== Parameters
826
+ #
827
+ # * +conditions+ - Conditions are specified the same way as with +find+ method.
828
+ #
829
+ # ==== Example
830
+ #
831
+ # Person.destroy_all("last_login < '2004-04-04'")
832
+ #
833
+ # This loads and destroys each person one by one, including its dependent associations and before_ and
834
+ # after_destroy callbacks.
835
+ #
836
+ # +conditions+ can be anything that +find+ also accepts:
837
+ #
838
+ # Person.destroy_all(:last_login => 6.hours.ago)
839
+ def destroy_all(conditions = nil)
840
+ find(:all, :conditions => conditions).each { |object| object.destroy }
841
+ end
842
+
843
+ # Deletes the records matching +conditions+ without instantiating the records first, and hence not
844
+ # calling the +destroy+ method nor invoking callbacks. This is a single SQL DELETE statement that
845
+ # goes straight to the database, much more efficient than +destroy_all+. Be careful with relations
846
+ # though, in particular <tt>:dependent</tt> rules defined on associations are not honored.
847
+ #
848
+ # ==== Parameters
849
+ #
850
+ # * +conditions+ - Conditions are specified the same way as with +find+ method.
851
+ #
852
+ # ==== Example
853
+ #
854
+ # Post.delete_all("person_id = 5 AND (category = 'Something' OR category = 'Else')")
855
+ # Post.delete_all(["person_id = ? AND (category = ? OR category = ?)", 5, 'Something', 'Else'])
856
+ #
857
+ # Both calls delete the affected posts all at once with a single DELETE statement. If you need to destroy dependent
858
+ # associations or call your <tt>before_*</tt> or +after_destroy+ callbacks, use the +destroy_all+ method instead.
859
+ def delete_all(conditions = nil)
860
+ sql = "DELETE FROM #{quoted_table_name} "
861
+ add_conditions!(sql, conditions, scope(:find))
862
+ connection.delete(sql, "#{name} Delete all")
863
+ end
864
+
865
+ # Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
866
+ # The use of this method should be restricted to complicated SQL queries that can't be executed
867
+ # using the ActiveRecord::Calculations class methods. Look into those before using this.
868
+ #
869
+ # ==== Parameters
870
+ #
871
+ # * +sql+ - An SQL statement which should return a count query from the database, see the example below.
872
+ #
873
+ # ==== Examples
874
+ #
875
+ # Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
876
+ def count_by_sql(sql)
877
+ sql = sanitize_conditions(sql)
878
+ connection.select_value(sql, "#{name} Count").to_i
879
+ end
880
+
881
+ # A generic "counter updater" implementation, intended primarily to be
882
+ # used by increment_counter and decrement_counter, but which may also
883
+ # be useful on its own. It simply does a direct SQL update for the record
884
+ # with the given ID, altering the given hash of counters by the amount
885
+ # given by the corresponding value:
886
+ #
887
+ # ==== Parameters
888
+ #
889
+ # * +id+ - The id of the object you wish to update a counter on.
890
+ # * +counters+ - An Array of Hashes containing the names of the fields
891
+ # to update as keys and the amount to update the field by as values.
892
+ #
893
+ # ==== Examples
894
+ #
895
+ # # For the Post with id of 5, decrement the comment_count by 1, and
896
+ # # increment the action_count by 1
897
+ # Post.update_counters 5, :comment_count => -1, :action_count => 1
898
+ # # Executes the following SQL:
899
+ # # UPDATE posts
900
+ # # SET comment_count = comment_count - 1,
901
+ # # action_count = action_count + 1
902
+ # # WHERE id = 5
903
+ def update_counters(id, counters)
904
+ updates = counters.inject([]) { |list, (counter_name, increment)|
905
+ sign = increment < 0 ? "-" : "+"
906
+ list << "#{connection.quote_column_name(counter_name)} = COALESCE(#{connection.quote_column_name(counter_name)}, 0) #{sign} #{increment.abs}"
907
+ }.join(", ")
908
+ update_all(updates, "#{connection.quote_column_name(primary_key)} = #{quote_value(id)}")
909
+ end
910
+
911
+ # Increment a number field by one, usually representing a count.
912
+ #
913
+ # This is used for caching aggregate values, so that they don't need to be computed every time.
914
+ # For example, a DiscussionBoard may cache post_count and comment_count otherwise every time the board is
915
+ # shown it would have to run an SQL query to find how many posts and comments there are.
916
+ #
917
+ # ==== Parameters
918
+ #
919
+ # * +counter_name+ - The name of the field that should be incremented.
920
+ # * +id+ - The id of the object that should be incremented.
921
+ #
922
+ # ==== Examples
923
+ #
924
+ # # Increment the post_count column for the record with an id of 5
925
+ # DiscussionBoard.increment_counter(:post_count, 5)
926
+ def increment_counter(counter_name, id)
927
+ update_counters(id, counter_name => 1)
928
+ end
929
+
930
+ # Decrement a number field by one, usually representing a count.
931
+ #
932
+ # This works the same as increment_counter but reduces the column value by 1 instead of increasing it.
933
+ #
934
+ # ==== Parameters
935
+ #
936
+ # * +counter_name+ - The name of the field that should be decremented.
937
+ # * +id+ - The id of the object that should be decremented.
938
+ #
939
+ # ==== Examples
940
+ #
941
+ # # Decrement the post_count column for the record with an id of 5
942
+ # DiscussionBoard.decrement_counter(:post_count, 5)
943
+ def decrement_counter(counter_name, id)
944
+ update_counters(id, counter_name => -1)
945
+ end
946
+
947
+
948
+ # Attributes named in this macro are protected from mass-assignment,
949
+ # such as <tt>new(attributes)</tt>,
950
+ # <tt>update_attributes(attributes)</tt>, or
951
+ # <tt>attributes=(attributes)</tt>.
952
+ #
953
+ # Mass-assignment to these attributes will simply be ignored, to assign
954
+ # to them you can use direct writer methods. This is meant to protect
955
+ # sensitive attributes from being overwritten by malicious users
956
+ # tampering with URLs or forms.
957
+ #
958
+ # class Customer < ActiveRecord::Base
959
+ # attr_protected :credit_rating
960
+ # end
961
+ #
962
+ # customer = Customer.new("name" => David, "credit_rating" => "Excellent")
963
+ # customer.credit_rating # => nil
964
+ # customer.attributes = { "description" => "Jolly fellow", "credit_rating" => "Superb" }
965
+ # customer.credit_rating # => nil
966
+ #
967
+ # customer.credit_rating = "Average"
968
+ # customer.credit_rating # => "Average"
969
+ #
970
+ # To start from an all-closed default and enable attributes as needed,
971
+ # have a look at +attr_accessible+.
972
+ def attr_protected(*attributes)
973
+ write_inheritable_attribute(:attr_protected, Set.new(attributes.map(&:to_s)) + (protected_attributes || []))
974
+ end
975
+
976
+ # Returns an array of all the attributes that have been protected from mass-assignment.
977
+ def protected_attributes # :nodoc:
978
+ read_inheritable_attribute(:attr_protected)
979
+ end
980
+
981
+ # Specifies a white list of model attributes that can be set via
982
+ # mass-assignment, such as <tt>new(attributes)</tt>,
983
+ # <tt>update_attributes(attributes)</tt>, or
984
+ # <tt>attributes=(attributes)</tt>
985
+ #
986
+ # This is the opposite of the +attr_protected+ macro: Mass-assignment
987
+ # will only set attributes in this list, to assign to the rest of
988
+ # attributes you can use direct writer methods. This is meant to protect
989
+ # sensitive attributes from being overwritten by malicious users
990
+ # tampering with URLs or forms. If you'd rather start from an all-open
991
+ # default and restrict attributes as needed, have a look at
992
+ # +attr_protected+.
993
+ #
994
+ # class Customer < ActiveRecord::Base
995
+ # attr_accessible :name, :nickname
996
+ # end
997
+ #
998
+ # customer = Customer.new(:name => "David", :nickname => "Dave", :credit_rating => "Excellent")
999
+ # customer.credit_rating # => nil
1000
+ # customer.attributes = { :name => "Jolly fellow", :credit_rating => "Superb" }
1001
+ # customer.credit_rating # => nil
1002
+ #
1003
+ # customer.credit_rating = "Average"
1004
+ # customer.credit_rating # => "Average"
1005
+ def attr_accessible(*attributes)
1006
+ write_inheritable_attribute(:attr_accessible, Set.new(attributes.map(&:to_s)) + (accessible_attributes || []))
1007
+ end
1008
+
1009
+ # Returns an array of all the attributes that have been made accessible to mass-assignment.
1010
+ def accessible_attributes # :nodoc:
1011
+ read_inheritable_attribute(:attr_accessible)
1012
+ end
1013
+
1014
+ # Attributes listed as readonly can be set for a new record, but will be ignored in database updates afterwards.
1015
+ def attr_readonly(*attributes)
1016
+ write_inheritable_attribute(:attr_readonly, Set.new(attributes.map(&:to_s)) + (readonly_attributes || []))
1017
+ end
1018
+
1019
+ # Returns an array of all the attributes that have been specified as readonly.
1020
+ def readonly_attributes
1021
+ read_inheritable_attribute(:attr_readonly)
1022
+ end
1023
+
1024
+ # If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object,
1025
+ # then specify the name of that attribute using this method and it will be handled automatically.
1026
+ # The serialization is done through YAML. If +class_name+ is specified, the serialized object must be of that
1027
+ # class on retrieval or SerializationTypeMismatch will be raised.
1028
+ #
1029
+ # ==== Parameters
1030
+ #
1031
+ # * +attr_name+ - The field name that should be serialized.
1032
+ # * +class_name+ - Optional, class name that the object type should be equal to.
1033
+ #
1034
+ # ==== Example
1035
+ # # Serialize a preferences attribute
1036
+ # class User
1037
+ # serialize :preferences
1038
+ # end
1039
+ def serialize(attr_name, class_name = Object)
1040
+ serialized_attributes[attr_name.to_s] = class_name
1041
+ end
1042
+
1043
+ # Returns a hash of all the attributes that have been specified for serialization as keys and their class restriction as values.
1044
+ def serialized_attributes
1045
+ read_inheritable_attribute(:attr_serialized) or write_inheritable_attribute(:attr_serialized, {})
1046
+ end
1047
+
1048
+
1049
+ # Guesses the table name (in forced lower-case) based on the name of the class in the inheritance hierarchy descending
1050
+ # directly from ActiveRecord::Base. So if the hierarchy looks like: Reply < Message < ActiveRecord::Base, then Message is used
1051
+ # to guess the table name even when called on Reply. The rules used to do the guess are handled by the Inflector class
1052
+ # in Active Support, which knows almost all common English inflections. You can add new inflections in config/initializers/inflections.rb.
1053
+ #
1054
+ # Nested classes are given table names prefixed by the singular form of
1055
+ # the parent's table name. Enclosing modules are not considered.
1056
+ #
1057
+ # ==== Examples
1058
+ #
1059
+ # class Invoice < ActiveRecord::Base; end;
1060
+ # file class table_name
1061
+ # invoice.rb Invoice invoices
1062
+ #
1063
+ # class Invoice < ActiveRecord::Base; class Lineitem < ActiveRecord::Base; end; end;
1064
+ # file class table_name
1065
+ # invoice.rb Invoice::Lineitem invoice_lineitems
1066
+ #
1067
+ # module Invoice; class Lineitem < ActiveRecord::Base; end; end;
1068
+ # file class table_name
1069
+ # invoice/lineitem.rb Invoice::Lineitem lineitems
1070
+ #
1071
+ # Additionally, the class-level +table_name_prefix+ is prepended and the
1072
+ # +table_name_suffix+ is appended. So if you have "myapp_" as a prefix,
1073
+ # the table name guess for an Invoice class becomes "myapp_invoices".
1074
+ # Invoice::Lineitem becomes "myapp_invoice_lineitems".
1075
+ #
1076
+ # You can also overwrite this class method to allow for unguessable
1077
+ # links, such as a Mouse class with a link to a "mice" table. Example:
1078
+ #
1079
+ # class Mouse < ActiveRecord::Base
1080
+ # set_table_name "mice"
1081
+ # end
1082
+ def table_name
1083
+ reset_table_name
1084
+ end
1085
+
1086
+ def reset_table_name #:nodoc:
1087
+ base = base_class
1088
+
1089
+ name =
1090
+ # STI subclasses always use their superclass' table.
1091
+ unless self == base
1092
+ base.table_name
1093
+ else
1094
+ # Nested classes are prefixed with singular parent table name.
1095
+ if parent < ActiveRecord::Base && !parent.abstract_class?
1096
+ contained = parent.table_name
1097
+ contained = contained.singularize if parent.pluralize_table_names
1098
+ contained << '_'
1099
+ end
1100
+ name = "#{table_name_prefix}#{contained}#{undecorated_table_name(base.name)}#{table_name_suffix}"
1101
+ end
1102
+
1103
+ set_table_name(name)
1104
+ name
1105
+ end
1106
+
1107
+ # Defines the primary key field -- can be overridden in subclasses. Overwriting will negate any effect of the
1108
+ # primary_key_prefix_type setting, though.
1109
+ def primary_key
1110
+ reset_primary_key
1111
+ end
1112
+
1113
+ def reset_primary_key #:nodoc:
1114
+ key = get_primary_key(base_class.name)
1115
+ set_primary_key(key)
1116
+ key
1117
+ end
1118
+
1119
+ def get_primary_key(base_name) #:nodoc:
1120
+ key = 'id'
1121
+ case primary_key_prefix_type
1122
+ when :table_name
1123
+ key = base_name.to_s.foreign_key(false)
1124
+ when :table_name_with_underscore
1125
+ key = base_name.to_s.foreign_key
1126
+ end
1127
+ key
1128
+ end
1129
+
1130
+ # Defines the column name for use with single table inheritance
1131
+ # -- can be set in subclasses like so: self.inheritance_column = "type_id"
1132
+ def inheritance_column
1133
+ @inheritance_column ||= "type".freeze
1134
+ end
1135
+
1136
+ # Lazy-set the sequence name to the connection's default. This method
1137
+ # is only ever called once since set_sequence_name overrides it.
1138
+ def sequence_name #:nodoc:
1139
+ reset_sequence_name
1140
+ end
1141
+
1142
+ def reset_sequence_name #:nodoc:
1143
+ default = connection.default_sequence_name(table_name, primary_key)
1144
+ set_sequence_name(default)
1145
+ default
1146
+ end
1147
+
1148
+ # Sets the table name to use to the given value, or (if the value
1149
+ # is nil or false) to the value returned by the given block.
1150
+ #
1151
+ # class Project < ActiveRecord::Base
1152
+ # set_table_name "project"
1153
+ # end
1154
+ def set_table_name(value = nil, &block)
1155
+ define_attr_method :table_name, value, &block
1156
+ end
1157
+ alias :table_name= :set_table_name
1158
+
1159
+ # Sets the name of the primary key column to use to the given value,
1160
+ # or (if the value is nil or false) to the value returned by the given
1161
+ # block.
1162
+ #
1163
+ # class Project < ActiveRecord::Base
1164
+ # set_primary_key "sysid"
1165
+ # end
1166
+ def set_primary_key(value = nil, &block)
1167
+ define_attr_method :primary_key, value, &block
1168
+ end
1169
+ alias :primary_key= :set_primary_key
1170
+
1171
+ # Sets the name of the inheritance column to use to the given value,
1172
+ # or (if the value # is nil or false) to the value returned by the
1173
+ # given block.
1174
+ #
1175
+ # class Project < ActiveRecord::Base
1176
+ # set_inheritance_column do
1177
+ # original_inheritance_column + "_id"
1178
+ # end
1179
+ # end
1180
+ def set_inheritance_column(value = nil, &block)
1181
+ define_attr_method :inheritance_column, value, &block
1182
+ end
1183
+ alias :inheritance_column= :set_inheritance_column
1184
+
1185
+ # Sets the name of the sequence to use when generating ids to the given
1186
+ # value, or (if the value is nil or false) to the value returned by the
1187
+ # given block. This is required for Oracle and is useful for any
1188
+ # database which relies on sequences for primary key generation.
1189
+ #
1190
+ # If a sequence name is not explicitly set when using Oracle or Firebird,
1191
+ # it will default to the commonly used pattern of: #{table_name}_seq
1192
+ #
1193
+ # If a sequence name is not explicitly set when using PostgreSQL, it
1194
+ # will discover the sequence corresponding to your primary key for you.
1195
+ #
1196
+ # class Project < ActiveRecord::Base
1197
+ # set_sequence_name "projectseq" # default would have been "project_seq"
1198
+ # end
1199
+ def set_sequence_name(value = nil, &block)
1200
+ define_attr_method :sequence_name, value, &block
1201
+ end
1202
+ alias :sequence_name= :set_sequence_name
1203
+
1204
+ # Turns the +table_name+ back into a class name following the reverse rules of +table_name+.
1205
+ def class_name(table_name = table_name) # :nodoc:
1206
+ # remove any prefix and/or suffix from the table name
1207
+ class_name = table_name[table_name_prefix.length..-(table_name_suffix.length + 1)].camelize
1208
+ class_name = class_name.singularize if pluralize_table_names
1209
+ class_name
1210
+ end
1211
+
1212
+ # Indicates whether the table associated with this class exists
1213
+ def table_exists?
1214
+ connection.table_exists?(table_name)
1215
+ end
1216
+
1217
+ # Returns an array of column objects for the table associated with this class.
1218
+ def columns
1219
+ unless defined?(@columns) && @columns
1220
+ @columns = connection.columns(table_name, "#{name} Columns")
1221
+ @columns.each { |column| column.primary = column.name == primary_key }
1222
+ end
1223
+ @columns
1224
+ end
1225
+
1226
+ # Returns a hash of column objects for the table associated with this class.
1227
+ def columns_hash
1228
+ @columns_hash ||= columns.inject({}) { |hash, column| hash[column.name] = column; hash }
1229
+ end
1230
+
1231
+ # Returns an array of column names as strings.
1232
+ def column_names
1233
+ @column_names ||= columns.map { |column| column.name }
1234
+ end
1235
+
1236
+ # Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
1237
+ # and columns used for single table inheritance have been removed.
1238
+ def content_columns
1239
+ @content_columns ||= columns.reject { |c| c.primary || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
1240
+ end
1241
+
1242
+ # Returns a hash of all the methods added to query each of the columns in the table with the name of the method as the key
1243
+ # and true as the value. This makes it possible to do O(1) lookups in respond_to? to check if a given method for attribute
1244
+ # is available.
1245
+ def column_methods_hash #:nodoc:
1246
+ @dynamic_methods_hash ||= column_names.inject(Hash.new(false)) do |methods, attr|
1247
+ attr_name = attr.to_s
1248
+ methods[attr.to_sym] = attr_name
1249
+ methods["#{attr}=".to_sym] = attr_name
1250
+ methods["#{attr}?".to_sym] = attr_name
1251
+ methods["#{attr}_before_type_cast".to_sym] = attr_name
1252
+ methods
1253
+ end
1254
+ end
1255
+
1256
+ # Resets all the cached information about columns, which will cause them
1257
+ # to be reloaded on the next request.
1258
+ #
1259
+ # The most common usage pattern for this method is probably in a migration,
1260
+ # when just after creating a table you want to populate it with some default
1261
+ # values, eg:
1262
+ #
1263
+ # class CreateJobLevels < ActiveRecord::Migration
1264
+ # def self.up
1265
+ # create_table :job_levels do |t|
1266
+ # t.integer :id
1267
+ # t.string :name
1268
+ #
1269
+ # t.timestamps
1270
+ # end
1271
+ #
1272
+ # JobLevel.reset_column_information
1273
+ # %w{assistant executive manager director}.each do |type|
1274
+ # JobLevel.create(:name => type)
1275
+ # end
1276
+ # end
1277
+ #
1278
+ # def self.down
1279
+ # drop_table :job_levels
1280
+ # end
1281
+ # end
1282
+ def reset_column_information
1283
+ generated_methods.each { |name| undef_method(name) }
1284
+ @column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @generated_methods = @inheritance_column = nil
1285
+ end
1286
+
1287
+ def reset_column_information_and_inheritable_attributes_for_all_subclasses#:nodoc:
1288
+ subclasses.each { |klass| klass.reset_inheritable_attributes; klass.reset_column_information }
1289
+ end
1290
+
1291
+ def self_and_descendents_from_active_record#nodoc:
1292
+ klass = self
1293
+ classes = [klass]
1294
+ while klass != klass.base_class
1295
+ classes << klass = klass.superclass
1296
+ end
1297
+ classes
1298
+ rescue
1299
+ # OPTIMIZE this rescue is to fix this test: ./test/cases/reflection_test.rb:56:in `test_human_name_for_column'
1300
+ # Appearantly the method base_class causes some trouble.
1301
+ # It now works for sure.
1302
+ [self]
1303
+ end
1304
+
1305
+ # Transforms attribute key names into a more humane format, such as "First name" instead of "first_name". Example:
1306
+ # Person.human_attribute_name("first_name") # => "First name"
1307
+ # This used to be depricated in favor of humanize, but is now preferred, because it automatically uses the I18n
1308
+ # module now.
1309
+ # Specify +options+ with additional translating options.
1310
+ def human_attribute_name(attribute_key_name, options = {})
1311
+ defaults = self_and_descendents_from_active_record.map do |klass|
1312
+ :"#{klass.name.underscore}.#{attribute_key_name}"
1313
+ end
1314
+ defaults << options[:default] if options[:default]
1315
+ defaults.flatten!
1316
+ defaults << attribute_key_name.humanize
1317
+ options[:count] ||= 1
1318
+ I18n.translate(defaults.shift, options.merge(:default => defaults, :scope => [:activerecord, :attributes]))
1319
+ end
1320
+
1321
+ # Transform the modelname into a more humane format, using I18n.
1322
+ # Defaults to the basic humanize method.
1323
+ # Default scope of the translation is activerecord.models
1324
+ # Specify +options+ with additional translating options.
1325
+ def human_name(options = {})
1326
+ defaults = self_and_descendents_from_active_record.map do |klass|
1327
+ :"#{klass.name.underscore}"
1328
+ end
1329
+ defaults << self.name.humanize
1330
+ I18n.translate(defaults.shift, {:scope => [:activerecord, :models], :count => 1, :default => defaults}.merge(options))
1331
+ end
1332
+
1333
+ # True if this isn't a concrete subclass needing a STI type condition.
1334
+ def descends_from_active_record?
1335
+ if superclass.abstract_class?
1336
+ superclass.descends_from_active_record?
1337
+ else
1338
+ superclass == Base || !columns_hash.include?(inheritance_column)
1339
+ end
1340
+ end
1341
+
1342
+ def finder_needs_type_condition? #:nodoc:
1343
+ # This is like this because benchmarking justifies the strange :false stuff
1344
+ :true == (@finder_needs_type_condition ||= descends_from_active_record? ? :false : :true)
1345
+ end
1346
+
1347
+ # Returns a string like 'Post id:integer, title:string, body:text'
1348
+ def inspect
1349
+ if self == Base
1350
+ super
1351
+ elsif abstract_class?
1352
+ "#{super}(abstract)"
1353
+ elsif table_exists?
1354
+ attr_list = columns.map { |c| "#{c.name}: #{c.type}" } * ', '
1355
+ "#{super}(#{attr_list})"
1356
+ else
1357
+ "#{super}(Table doesn't exist)"
1358
+ end
1359
+ end
1360
+
1361
+
1362
+ def quote_value(value, column = nil) #:nodoc:
1363
+ connection.quote(value,column)
1364
+ end
1365
+
1366
+ # Used to sanitize objects before they're used in an SQL SELECT statement. Delegates to <tt>connection.quote</tt>.
1367
+ def sanitize(object) #:nodoc:
1368
+ connection.quote(object)
1369
+ end
1370
+
1371
+ # Log and benchmark multiple statements in a single block. Example:
1372
+ #
1373
+ # Project.benchmark("Creating project") do
1374
+ # project = Project.create("name" => "stuff")
1375
+ # project.create_manager("name" => "David")
1376
+ # project.milestones << Milestone.find(:all)
1377
+ # end
1378
+ #
1379
+ # The benchmark is only recorded if the current level of the logger is less than or equal to the <tt>log_level</tt>,
1380
+ # which makes it easy to include benchmarking statements in production software that will remain inexpensive because
1381
+ # the benchmark will only be conducted if the log level is low enough.
1382
+ #
1383
+ # The logging of the multiple statements is turned off unless <tt>use_silence</tt> is set to false.
1384
+ def benchmark(title, log_level = Logger::DEBUG, use_silence = true)
1385
+ if logger && logger.level <= log_level
1386
+ result = nil
1387
+ seconds = Benchmark.realtime { result = use_silence ? silence { yield } : yield }
1388
+ logger.add(log_level, "#{title} (#{'%.1f' % (seconds * 1000)}ms)")
1389
+ result
1390
+ else
1391
+ yield
1392
+ end
1393
+ end
1394
+
1395
+ # Silences the logger for the duration of the block.
1396
+ def silence
1397
+ old_logger_level, logger.level = logger.level, Logger::ERROR if logger
1398
+ yield
1399
+ ensure
1400
+ logger.level = old_logger_level if logger
1401
+ end
1402
+
1403
+ # Overwrite the default class equality method to provide support for association proxies.
1404
+ def ===(object)
1405
+ object.is_a?(self)
1406
+ end
1407
+
1408
+ # Returns the base AR subclass that this class descends from. If A
1409
+ # extends AR::Base, A.base_class will return A. If B descends from A
1410
+ # through some arbitrarily deep hierarchy, B.base_class will return A.
1411
+ def base_class
1412
+ class_of_active_record_descendant(self)
1413
+ end
1414
+
1415
+ # Set this to true if this is an abstract class (see <tt>abstract_class?</tt>).
1416
+ attr_accessor :abstract_class
1417
+
1418
+ # Returns whether this class is a base AR class. If A is a base class and
1419
+ # B descends from A, then B.base_class will return B.
1420
+ def abstract_class?
1421
+ defined?(@abstract_class) && @abstract_class == true
1422
+ end
1423
+
1424
+ def respond_to?(method_id, include_private = false)
1425
+ if match = DynamicFinderMatch.match(method_id)
1426
+ return true if all_attributes_exists?(match.attribute_names)
1427
+ end
1428
+ super
1429
+ end
1430
+
1431
+ def sti_name
1432
+ store_full_sti_class ? name : name.demodulize
1433
+ end
1434
+
1435
+ # Merges conditions so that the result is a valid +condition+
1436
+ def merge_conditions(*conditions)
1437
+ segments = []
1438
+
1439
+ conditions.each do |condition|
1440
+ unless condition.blank?
1441
+ sql = sanitize_sql(condition)
1442
+ segments << sql unless sql.blank?
1443
+ end
1444
+ end
1445
+
1446
+ "(#{segments.join(') AND (')})" unless segments.empty?
1447
+ end
1448
+
1449
+ private
1450
+ def find_initial(options)
1451
+ options.update(:limit => 1)
1452
+ find_every(options).first
1453
+ end
1454
+
1455
+ def find_last(options)
1456
+ order = options[:order]
1457
+
1458
+ if order
1459
+ order = reverse_sql_order(order)
1460
+ elsif !scoped?(:find, :order)
1461
+ order = "#{table_name}.#{primary_key} DESC"
1462
+ end
1463
+
1464
+ if scoped?(:find, :order)
1465
+ scoped_order = reverse_sql_order(scope(:find, :order))
1466
+ scoped_methods.select { |s| s[:find].update(:order => scoped_order) }
1467
+ end
1468
+
1469
+ find_initial(options.merge({ :order => order }))
1470
+ end
1471
+
1472
+ def reverse_sql_order(order_query)
1473
+ reversed_query = order_query.split(/,/).each { |s|
1474
+ if s.match(/\s(asc|ASC)$/)
1475
+ s.gsub!(/\s(asc|ASC)$/, ' DESC')
1476
+ elsif s.match(/\s(desc|DESC)$/)
1477
+ s.gsub!(/\s(desc|DESC)$/, ' ASC')
1478
+ elsif !s.match(/\s(asc|ASC|desc|DESC)$/)
1479
+ s.concat(' DESC')
1480
+ end
1481
+ }.join(',')
1482
+ end
1483
+
1484
+ def find_every(options)
1485
+ include_associations = merge_includes(scope(:find, :include), options[:include])
1486
+
1487
+ if include_associations.any? && references_eager_loaded_tables?(options)
1488
+ records = find_with_associations(options)
1489
+ else
1490
+ records = find_by_sql(construct_finder_sql(options))
1491
+ if include_associations.any?
1492
+ preload_associations(records, include_associations)
1493
+ end
1494
+ end
1495
+
1496
+ records.each { |record| record.readonly! } if options[:readonly]
1497
+
1498
+ records
1499
+ end
1500
+
1501
+ def find_from_ids(ids, options)
1502
+ expects_array = ids.first.kind_of?(Array)
1503
+ return ids.first if expects_array && ids.first.empty?
1504
+
1505
+ ids = ids.flatten.compact.uniq
1506
+
1507
+ case ids.size
1508
+ when 0
1509
+ raise RecordNotFound, "Couldn't find #{name} without an ID"
1510
+ when 1
1511
+ result = find_one(ids.first, options)
1512
+ expects_array ? [ result ] : result
1513
+ else
1514
+ find_some(ids, options)
1515
+ end
1516
+ end
1517
+
1518
+ def find_one(id, options)
1519
+ conditions = " AND (#{sanitize_sql(options[:conditions])})" if options[:conditions]
1520
+ options.update :conditions => "#{quoted_table_name}.#{connection.quote_column_name(primary_key)} = #{quote_value(id,columns_hash[primary_key])}#{conditions}"
1521
+
1522
+ # Use find_every(options).first since the primary key condition
1523
+ # already ensures we have a single record. Using find_initial adds
1524
+ # a superfluous :limit => 1.
1525
+ if result = find_every(options).first
1526
+ result
1527
+ else
1528
+ raise RecordNotFound, "Couldn't find #{name} with ID=#{id}#{conditions}"
1529
+ end
1530
+ end
1531
+
1532
+ def find_some(ids, options)
1533
+ conditions = " AND (#{sanitize_sql(options[:conditions])})" if options[:conditions]
1534
+ ids_list = ids.map { |id| quote_value(id,columns_hash[primary_key]) }.join(',')
1535
+ options.update :conditions => "#{quoted_table_name}.#{connection.quote_column_name(primary_key)} IN (#{ids_list})#{conditions}"
1536
+
1537
+ result = find_every(options)
1538
+
1539
+ # Determine expected size from limit and offset, not just ids.size.
1540
+ expected_size =
1541
+ if options[:limit] && ids.size > options[:limit]
1542
+ options[:limit]
1543
+ else
1544
+ ids.size
1545
+ end
1546
+
1547
+ # 11 ids with limit 3, offset 9 should give 2 results.
1548
+ if options[:offset] && (ids.size - options[:offset] < expected_size)
1549
+ expected_size = ids.size - options[:offset]
1550
+ end
1551
+
1552
+ if result.size == expected_size
1553
+ result
1554
+ else
1555
+ raise RecordNotFound, "Couldn't find all #{name.pluralize} with IDs (#{ids_list})#{conditions} (found #{result.size} results, but was looking for #{expected_size})"
1556
+ end
1557
+ end
1558
+
1559
+ # Finder methods must instantiate through this method to work with the
1560
+ # single-table inheritance model that makes it possible to create
1561
+ # objects of different types from the same table.
1562
+ def instantiate(record)
1563
+ object =
1564
+ if subclass_name = record[inheritance_column]
1565
+ # No type given.
1566
+ if subclass_name.empty?
1567
+ allocate
1568
+
1569
+ else
1570
+ # Ignore type if no column is present since it was probably
1571
+ # pulled in from a sloppy join.
1572
+ unless columns_hash.include?(inheritance_column)
1573
+ allocate
1574
+
1575
+ else
1576
+ begin
1577
+ compute_type(subclass_name).allocate
1578
+ rescue NameError
1579
+ raise SubclassNotFound,
1580
+ "The single-table inheritance mechanism failed to locate the subclass: '#{record[inheritance_column]}'. " +
1581
+ "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
1582
+ "Please rename this column if you didn't intend it to be used for storing the inheritance class " +
1583
+ "or overwrite #{self.to_s}.inheritance_column to use another column for that information."
1584
+ end
1585
+ end
1586
+ end
1587
+ else
1588
+ allocate
1589
+ end
1590
+
1591
+ object.instance_variable_set("@attributes", record)
1592
+ object.instance_variable_set("@attributes_cache", Hash.new)
1593
+
1594
+ if object.respond_to_without_attributes?(:after_find)
1595
+ object.send(:callback, :after_find)
1596
+ end
1597
+
1598
+ if object.respond_to_without_attributes?(:after_initialize)
1599
+ object.send(:callback, :after_initialize)
1600
+ end
1601
+
1602
+ object
1603
+ end
1604
+
1605
+ # Nest the type name in the same module as this class.
1606
+ # Bar is "MyApp::Business::Bar" relative to MyApp::Business::Foo
1607
+ def type_name_with_module(type_name)
1608
+ if store_full_sti_class
1609
+ type_name
1610
+ else
1611
+ (/^::/ =~ type_name) ? type_name : "#{parent.name}::#{type_name}"
1612
+ end
1613
+ end
1614
+
1615
+ def default_select(qualified)
1616
+ if qualified
1617
+ quoted_table_name + '.*'
1618
+ else
1619
+ '*'
1620
+ end
1621
+ end
1622
+
1623
+ def construct_finder_sql(options)
1624
+ scope = scope(:find)
1625
+ sql = "SELECT #{options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))} "
1626
+ sql << "FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} "
1627
+
1628
+ add_joins!(sql, options[:joins], scope)
1629
+ add_conditions!(sql, options[:conditions], scope)
1630
+
1631
+ add_group!(sql, options[:group], scope)
1632
+ add_order!(sql, options[:order], scope)
1633
+ add_limit!(sql, options, scope)
1634
+ add_lock!(sql, options, scope)
1635
+
1636
+ sql
1637
+ end
1638
+
1639
+ # Merges includes so that the result is a valid +include+
1640
+ def merge_includes(first, second)
1641
+ (safe_to_array(first) + safe_to_array(second)).uniq
1642
+ end
1643
+
1644
+ def merge_joins(*joins)
1645
+ if joins.any?{|j| j.is_a?(String) || array_of_strings?(j) }
1646
+ joins = joins.collect do |join|
1647
+ join = [join] if join.is_a?(String)
1648
+ unless array_of_strings?(join)
1649
+ join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, join, nil)
1650
+ join = join_dependency.join_associations.collect { |assoc| assoc.association_join }
1651
+ end
1652
+ join
1653
+ end
1654
+ joins.flatten.uniq
1655
+ else
1656
+ joins.collect{|j| safe_to_array(j)}.flatten.uniq
1657
+ end
1658
+ end
1659
+
1660
+ # Object#to_a is deprecated, though it does have the desired behavior
1661
+ def safe_to_array(o)
1662
+ case o
1663
+ when NilClass
1664
+ []
1665
+ when Array
1666
+ o
1667
+ else
1668
+ [o]
1669
+ end
1670
+ end
1671
+
1672
+ def array_of_strings?(o)
1673
+ o.is_a?(Array) && o.all?{|obj| obj.is_a?(String)}
1674
+ end
1675
+
1676
+ def add_order!(sql, order, scope = :auto)
1677
+ scope = scope(:find) if :auto == scope
1678
+ scoped_order = scope[:order] if scope
1679
+ if order
1680
+ sql << " ORDER BY #{order}"
1681
+ sql << ", #{scoped_order}" if scoped_order
1682
+ else
1683
+ sql << " ORDER BY #{scoped_order}" if scoped_order
1684
+ end
1685
+ end
1686
+
1687
+ def add_group!(sql, group, scope = :auto)
1688
+ if group
1689
+ sql << " GROUP BY #{group}"
1690
+ else
1691
+ scope = scope(:find) if :auto == scope
1692
+ if scope && (scoped_group = scope[:group])
1693
+ sql << " GROUP BY #{scoped_group}"
1694
+ end
1695
+ end
1696
+ end
1697
+
1698
+ # The optional scope argument is for the current <tt>:find</tt> scope.
1699
+ def add_limit!(sql, options, scope = :auto)
1700
+ scope = scope(:find) if :auto == scope
1701
+
1702
+ if scope
1703
+ options[:limit] ||= scope[:limit]
1704
+ options[:offset] ||= scope[:offset]
1705
+ end
1706
+
1707
+ connection.add_limit_offset!(sql, options)
1708
+ end
1709
+
1710
+ # The optional scope argument is for the current <tt>:find</tt> scope.
1711
+ # The <tt>:lock</tt> option has precedence over a scoped <tt>:lock</tt>.
1712
+ def add_lock!(sql, options, scope = :auto)
1713
+ scope = scope(:find) if :auto == scope
1714
+ options = options.reverse_merge(:lock => scope[:lock]) if scope
1715
+ connection.add_lock!(sql, options)
1716
+ end
1717
+
1718
+ # The optional scope argument is for the current <tt>:find</tt> scope.
1719
+ def add_joins!(sql, joins, scope = :auto)
1720
+ scope = scope(:find) if :auto == scope
1721
+ merged_joins = scope && scope[:joins] && joins ? merge_joins(scope[:joins], joins) : (joins || scope && scope[:joins])
1722
+ case merged_joins
1723
+ when Symbol, Hash, Array
1724
+ if array_of_strings?(merged_joins)
1725
+ sql << merged_joins.join(' ') + " "
1726
+ else
1727
+ join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, merged_joins, nil)
1728
+ sql << " #{join_dependency.join_associations.collect { |assoc| assoc.association_join }.join} "
1729
+ end
1730
+ when String
1731
+ sql << " #{merged_joins} "
1732
+ end
1733
+ end
1734
+
1735
+ # Adds a sanitized version of +conditions+ to the +sql+ string. Note that the passed-in +sql+ string is changed.
1736
+ # The optional scope argument is for the current <tt>:find</tt> scope.
1737
+ def add_conditions!(sql, conditions, scope = :auto)
1738
+ scope = scope(:find) if :auto == scope
1739
+ conditions = [conditions]
1740
+ conditions << scope[:conditions] if scope
1741
+ conditions << type_condition if finder_needs_type_condition?
1742
+ merged_conditions = merge_conditions(*conditions)
1743
+ sql << "WHERE #{merged_conditions} " unless merged_conditions.blank?
1744
+ end
1745
+
1746
+ def type_condition(table_alias=nil)
1747
+ quoted_table_alias = self.connection.quote_table_name(table_alias || table_name)
1748
+ quoted_inheritance_column = connection.quote_column_name(inheritance_column)
1749
+ type_condition = subclasses.inject("#{quoted_table_alias}.#{quoted_inheritance_column} = '#{sti_name}' ") do |condition, subclass|
1750
+ condition << "OR #{quoted_table_alias}.#{quoted_inheritance_column} = '#{subclass.sti_name}' "
1751
+ end
1752
+
1753
+ " (#{type_condition}) "
1754
+ end
1755
+
1756
+ # Guesses the table name, but does not decorate it with prefix and suffix information.
1757
+ def undecorated_table_name(class_name = base_class.name)
1758
+ table_name = class_name.to_s.demodulize.underscore
1759
+ table_name = table_name.pluralize if pluralize_table_names
1760
+ table_name
1761
+ end
1762
+
1763
+ # Enables dynamic finders like find_by_user_name(user_name) and find_by_user_name_and_password(user_name, password) that are turned into
1764
+ # find(:first, :conditions => ["user_name = ?", user_name]) and find(:first, :conditions => ["user_name = ? AND password = ?", user_name, password])
1765
+ # respectively. Also works for find(:all) by using find_all_by_amount(50) that is turned into find(:all, :conditions => ["amount = ?", 50]).
1766
+ #
1767
+ # It's even possible to use all the additional parameters to find. For example, the full interface for find_all_by_amount
1768
+ # is actually find_all_by_amount(amount, options).
1769
+ #
1770
+ # This also enables you to initialize a record if it is not found, such as find_or_initialize_by_amount(amount)
1771
+ # or find_or_create_by_user_and_password(user, password).
1772
+ #
1773
+ # Each dynamic finder or initializer/creator is also defined in the class after it is first invoked, so that future
1774
+ # attempts to use it do not run through method_missing.
1775
+ def method_missing(method_id, *arguments, &block)
1776
+ if match = DynamicFinderMatch.match(method_id)
1777
+ attribute_names = match.attribute_names
1778
+ super unless all_attributes_exists?(attribute_names)
1779
+ if match.finder?
1780
+ finder = match.finder
1781
+ bang = match.bang?
1782
+ self.class_eval %{
1783
+ def self.#{method_id}(*args)
1784
+ options = args.extract_options!
1785
+ attributes = construct_attributes_from_arguments([:#{attribute_names.join(',:')}], args)
1786
+ finder_options = { :conditions => attributes }
1787
+ validate_find_options(options)
1788
+ set_readonly_option!(options)
1789
+
1790
+ #{'result = ' if bang}if options[:conditions]
1791
+ with_scope(:find => finder_options) do
1792
+ find(:#{finder}, options)
1793
+ end
1794
+ else
1795
+ find(:#{finder}, options.merge(finder_options))
1796
+ end
1797
+ #{'result || raise(RecordNotFound)' if bang}
1798
+ end
1799
+ }, __FILE__, __LINE__
1800
+ send(method_id, *arguments)
1801
+ elsif match.instantiator?
1802
+ instantiator = match.instantiator
1803
+ self.class_eval %{
1804
+ def self.#{method_id}(*args)
1805
+ guard_protected_attributes = false
1806
+
1807
+ if args[0].is_a?(Hash)
1808
+ guard_protected_attributes = true
1809
+ attributes = args[0].with_indifferent_access
1810
+ find_attributes = attributes.slice(*[:#{attribute_names.join(',:')}])
1811
+ else
1812
+ find_attributes = attributes = construct_attributes_from_arguments([:#{attribute_names.join(',:')}], args)
1813
+ end
1814
+
1815
+ options = { :conditions => find_attributes }
1816
+ set_readonly_option!(options)
1817
+
1818
+ record = find(:first, options)
1819
+
1820
+ if record.nil?
1821
+ record = self.new { |r| r.send(:attributes=, attributes, guard_protected_attributes) }
1822
+ #{'yield(record) if block_given?'}
1823
+ #{'record.save' if instantiator == :create}
1824
+ record
1825
+ else
1826
+ record
1827
+ end
1828
+ end
1829
+ }, __FILE__, __LINE__
1830
+ send(method_id, *arguments, &block)
1831
+ end
1832
+ else
1833
+ super
1834
+ end
1835
+ end
1836
+
1837
+ def construct_attributes_from_arguments(attribute_names, arguments)
1838
+ attributes = {}
1839
+ attribute_names.each_with_index { |name, idx| attributes[name] = arguments[idx] }
1840
+ attributes
1841
+ end
1842
+
1843
+ # Similar in purpose to +expand_hash_conditions_for_aggregates+.
1844
+ def expand_attribute_names_for_aggregates(attribute_names)
1845
+ expanded_attribute_names = []
1846
+ attribute_names.each do |attribute_name|
1847
+ unless (aggregation = reflect_on_aggregation(attribute_name.to_sym)).nil?
1848
+ aggregate_mapping(aggregation).each do |field_attr, aggregate_attr|
1849
+ expanded_attribute_names << field_attr
1850
+ end
1851
+ else
1852
+ expanded_attribute_names << attribute_name
1853
+ end
1854
+ end
1855
+ expanded_attribute_names
1856
+ end
1857
+
1858
+ def all_attributes_exists?(attribute_names)
1859
+ attribute_names = expand_attribute_names_for_aggregates(attribute_names)
1860
+ attribute_names.all? { |name| column_methods_hash.include?(name.to_sym) }
1861
+ end
1862
+
1863
+ def attribute_condition(argument)
1864
+ case argument
1865
+ when nil then "IS ?"
1866
+ when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope then "IN (?)"
1867
+ when Range then "BETWEEN ? AND ?"
1868
+ else "= ?"
1869
+ end
1870
+ end
1871
+
1872
+ # Interpret Array and Hash as conditions and anything else as an id.
1873
+ def expand_id_conditions(id_or_conditions)
1874
+ case id_or_conditions
1875
+ when Array, Hash then id_or_conditions
1876
+ else sanitize_sql(primary_key => id_or_conditions)
1877
+ end
1878
+ end
1879
+
1880
+
1881
+ # Defines an "attribute" method (like +inheritance_column+ or
1882
+ # +table_name+). A new (class) method will be created with the
1883
+ # given name. If a value is specified, the new method will
1884
+ # return that value (as a string). Otherwise, the given block
1885
+ # will be used to compute the value of the method.
1886
+ #
1887
+ # The original method will be aliased, with the new name being
1888
+ # prefixed with "original_". This allows the new method to
1889
+ # access the original value.
1890
+ #
1891
+ # Example:
1892
+ #
1893
+ # class A < ActiveRecord::Base
1894
+ # define_attr_method :primary_key, "sysid"
1895
+ # define_attr_method( :inheritance_column ) do
1896
+ # original_inheritance_column + "_id"
1897
+ # end
1898
+ # end
1899
+ def define_attr_method(name, value=nil, &block)
1900
+ sing = class << self; self; end
1901
+ sing.send :alias_method, "original_#{name}", name
1902
+ if block_given?
1903
+ sing.send :define_method, name, &block
1904
+ else
1905
+ # use eval instead of a block to work around a memory leak in dev
1906
+ # mode in fcgi
1907
+ sing.class_eval "def #{name}; #{value.to_s.inspect}; end"
1908
+ end
1909
+ end
1910
+
1911
+ protected
1912
+ # Scope parameters to method calls within the block. Takes a hash of method_name => parameters hash.
1913
+ # method_name may be <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameters may include the <tt>:conditions</tt>, <tt>:joins</tt>,
1914
+ # <tt>:include</tt>, <tt>:offset</tt>, <tt>:limit</tt>, and <tt>:readonly</tt> options. <tt>:create</tt> parameters are an attributes hash.
1915
+ #
1916
+ # class Article < ActiveRecord::Base
1917
+ # def self.create_with_scope
1918
+ # with_scope(:find => { :conditions => "blog_id = 1" }, :create => { :blog_id => 1 }) do
1919
+ # find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1
1920
+ # a = create(1)
1921
+ # a.blog_id # => 1
1922
+ # end
1923
+ # end
1924
+ # end
1925
+ #
1926
+ # In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of
1927
+ # <tt>:conditions</tt> and <tt>:include</tt> options in <tt>:find</tt>, which are merged.
1928
+ #
1929
+ # class Article < ActiveRecord::Base
1930
+ # def self.find_with_scope
1931
+ # with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }, :create => { :blog_id => 1 }) do
1932
+ # with_scope(:find => { :limit => 10 })
1933
+ # find(:all) # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
1934
+ # end
1935
+ # with_scope(:find => { :conditions => "author_id = 3" })
1936
+ # find(:all) # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
1937
+ # end
1938
+ # end
1939
+ # end
1940
+ # end
1941
+ #
1942
+ # You can ignore any previous scopings by using the <tt>with_exclusive_scope</tt> method.
1943
+ #
1944
+ # class Article < ActiveRecord::Base
1945
+ # def self.find_with_exclusive_scope
1946
+ # with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }) do
1947
+ # with_exclusive_scope(:find => { :limit => 10 })
1948
+ # find(:all) # => SELECT * from articles LIMIT 10
1949
+ # end
1950
+ # end
1951
+ # end
1952
+ # end
1953
+ #
1954
+ # *Note*: the +:find+ scope also has effect on update and deletion methods,
1955
+ # like +update_all+ and +delete_all+.
1956
+ def with_scope(method_scoping = {}, action = :merge, &block)
1957
+ method_scoping = method_scoping.method_scoping if method_scoping.respond_to?(:method_scoping)
1958
+
1959
+ # Dup first and second level of hash (method and params).
1960
+ method_scoping = method_scoping.inject({}) do |hash, (method, params)|
1961
+ hash[method] = (params == true) ? params : params.dup
1962
+ hash
1963
+ end
1964
+
1965
+ method_scoping.assert_valid_keys([ :find, :create ])
1966
+
1967
+ if f = method_scoping[:find]
1968
+ f.assert_valid_keys(VALID_FIND_OPTIONS)
1969
+ set_readonly_option! f
1970
+ end
1971
+
1972
+ # Merge scopings
1973
+ if action == :merge && current_scoped_methods
1974
+ method_scoping = current_scoped_methods.inject(method_scoping) do |hash, (method, params)|
1975
+ case hash[method]
1976
+ when Hash
1977
+ if method == :find
1978
+ (hash[method].keys + params.keys).uniq.each do |key|
1979
+ merge = hash[method][key] && params[key] # merge if both scopes have the same key
1980
+ if key == :conditions && merge
1981
+ hash[method][key] = merge_conditions(params[key], hash[method][key])
1982
+ elsif key == :include && merge
1983
+ hash[method][key] = merge_includes(hash[method][key], params[key]).uniq
1984
+ elsif key == :joins && merge
1985
+ hash[method][key] = merge_joins(params[key], hash[method][key])
1986
+ else
1987
+ hash[method][key] = hash[method][key] || params[key]
1988
+ end
1989
+ end
1990
+ else
1991
+ hash[method] = params.merge(hash[method])
1992
+ end
1993
+ else
1994
+ hash[method] = params
1995
+ end
1996
+ hash
1997
+ end
1998
+ end
1999
+
2000
+ self.scoped_methods << method_scoping
2001
+
2002
+ begin
2003
+ yield
2004
+ ensure
2005
+ self.scoped_methods.pop
2006
+ end
2007
+ end
2008
+
2009
+ # Works like with_scope, but discards any nested properties.
2010
+ def with_exclusive_scope(method_scoping = {}, &block)
2011
+ with_scope(method_scoping, :overwrite, &block)
2012
+ end
2013
+
2014
+ def subclasses #:nodoc:
2015
+ @@subclasses[self] ||= []
2016
+ @@subclasses[self] + extra = @@subclasses[self].inject([]) {|list, subclass| list + subclass.subclasses }
2017
+ end
2018
+
2019
+ # Test whether the given method and optional key are scoped.
2020
+ def scoped?(method, key = nil) #:nodoc:
2021
+ if current_scoped_methods && (scope = current_scoped_methods[method])
2022
+ !key || scope.has_key?(key)
2023
+ end
2024
+ end
2025
+
2026
+ # Retrieve the scope for the given method and optional key.
2027
+ def scope(method, key = nil) #:nodoc:
2028
+ if current_scoped_methods && (scope = current_scoped_methods[method])
2029
+ key ? scope[key] : scope
2030
+ end
2031
+ end
2032
+
2033
+ def scoped_methods #:nodoc:
2034
+ Thread.current[:"#{self}_scoped_methods"] ||= []
2035
+ end
2036
+
2037
+ def current_scoped_methods #:nodoc:
2038
+ scoped_methods.last
2039
+ end
2040
+
2041
+ # Returns the class type of the record using the current module as a prefix. So descendents of
2042
+ # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
2043
+ def compute_type(type_name)
2044
+ modularized_name = type_name_with_module(type_name)
2045
+ silence_warnings do
2046
+ begin
2047
+ class_eval(modularized_name, __FILE__, __LINE__)
2048
+ rescue NameError
2049
+ class_eval(type_name, __FILE__, __LINE__)
2050
+ end
2051
+ end
2052
+ end
2053
+
2054
+ # Returns the class descending directly from Active Record in the inheritance hierarchy.
2055
+ def class_of_active_record_descendant(klass)
2056
+ if klass.superclass == Base || klass.superclass.abstract_class?
2057
+ klass
2058
+ elsif klass.superclass.nil?
2059
+ raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
2060
+ else
2061
+ class_of_active_record_descendant(klass.superclass)
2062
+ end
2063
+ end
2064
+
2065
+ # Returns the name of the class descending directly from Active Record in the inheritance hierarchy.
2066
+ def class_name_of_active_record_descendant(klass) #:nodoc:
2067
+ klass.base_class.name
2068
+ end
2069
+
2070
+ # Accepts an array, hash, or string of SQL conditions and sanitizes
2071
+ # them into a valid SQL fragment for a WHERE clause.
2072
+ # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
2073
+ # { :name => "foo'bar", :group_id => 4 } returns "name='foo''bar' and group_id='4'"
2074
+ # "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
2075
+ def sanitize_sql_for_conditions(condition)
2076
+ return nil if condition.blank?
2077
+
2078
+ case condition
2079
+ when Array; sanitize_sql_array(condition)
2080
+ when Hash; sanitize_sql_hash_for_conditions(condition)
2081
+ else condition
2082
+ end
2083
+ end
2084
+ alias_method :sanitize_sql, :sanitize_sql_for_conditions
2085
+
2086
+ # Accepts an array, hash, or string of SQL conditions and sanitizes
2087
+ # them into a valid SQL fragment for a SET clause.
2088
+ # { :name => nil, :group_id => 4 } returns "name = NULL , group_id='4'"
2089
+ def sanitize_sql_for_assignment(assignments)
2090
+ case assignments
2091
+ when Array; sanitize_sql_array(assignments)
2092
+ when Hash; sanitize_sql_hash_for_assignment(assignments)
2093
+ else assignments
2094
+ end
2095
+ end
2096
+
2097
+ def aggregate_mapping(reflection)
2098
+ mapping = reflection.options[:mapping] || [reflection.name, reflection.name]
2099
+ mapping.first.is_a?(Array) ? mapping : [mapping]
2100
+ end
2101
+
2102
+ # Accepts a hash of SQL conditions and replaces those attributes
2103
+ # that correspond to a +composed_of+ relationship with their expanded
2104
+ # aggregate attribute values.
2105
+ # Given:
2106
+ # class Person < ActiveRecord::Base
2107
+ # composed_of :address, :class_name => "Address",
2108
+ # :mapping => [%w(address_street street), %w(address_city city)]
2109
+ # end
2110
+ # Then:
2111
+ # { :address => Address.new("813 abc st.", "chicago") }
2112
+ # # => { :address_street => "813 abc st.", :address_city => "chicago" }
2113
+ def expand_hash_conditions_for_aggregates(attrs)
2114
+ expanded_attrs = {}
2115
+ attrs.each do |attr, value|
2116
+ unless (aggregation = reflect_on_aggregation(attr.to_sym)).nil?
2117
+ mapping = aggregate_mapping(aggregation)
2118
+ mapping.each do |field_attr, aggregate_attr|
2119
+ if mapping.size == 1 && !value.respond_to?(aggregate_attr)
2120
+ expanded_attrs[field_attr] = value
2121
+ else
2122
+ expanded_attrs[field_attr] = value.send(aggregate_attr)
2123
+ end
2124
+ end
2125
+ else
2126
+ expanded_attrs[attr] = value
2127
+ end
2128
+ end
2129
+ expanded_attrs
2130
+ end
2131
+
2132
+ # Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
2133
+ # { :name => "foo'bar", :group_id => 4 }
2134
+ # # => "name='foo''bar' and group_id= 4"
2135
+ # { :status => nil, :group_id => [1,2,3] }
2136
+ # # => "status IS NULL and group_id IN (1,2,3)"
2137
+ # { :age => 13..18 }
2138
+ # # => "age BETWEEN 13 AND 18"
2139
+ # { 'other_records.id' => 7 }
2140
+ # # => "`other_records`.`id` = 7"
2141
+ # { :other_records => { :id => 7 } }
2142
+ # # => "`other_records`.`id` = 7"
2143
+ # And for value objects on a composed_of relationship:
2144
+ # { :address => Address.new("123 abc st.", "chicago") }
2145
+ # # => "address_street='123 abc st.' and address_city='chicago'"
2146
+ def sanitize_sql_hash_for_conditions(attrs, table_name = quoted_table_name)
2147
+ attrs = expand_hash_conditions_for_aggregates(attrs)
2148
+
2149
+ conditions = attrs.map do |attr, value|
2150
+ unless value.is_a?(Hash)
2151
+ attr = attr.to_s
2152
+
2153
+ # Extract table name from qualified attribute names.
2154
+ if attr.include?('.')
2155
+ table_name, attr = attr.split('.', 2)
2156
+ table_name = connection.quote_table_name(table_name)
2157
+ end
2158
+
2159
+ "#{table_name}.#{connection.quote_column_name(attr)} #{attribute_condition(value)}"
2160
+ else
2161
+ sanitize_sql_hash_for_conditions(value, connection.quote_table_name(attr.to_s))
2162
+ end
2163
+ end.join(' AND ')
2164
+
2165
+ replace_bind_variables(conditions, expand_range_bind_variables(attrs.values))
2166
+ end
2167
+ alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
2168
+
2169
+ # Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
2170
+ # { :status => nil, :group_id => 1 }
2171
+ # # => "status = NULL , group_id = 1"
2172
+ def sanitize_sql_hash_for_assignment(attrs)
2173
+ attrs.map do |attr, value|
2174
+ "#{connection.quote_column_name(attr)} = #{quote_bound_value(value)}"
2175
+ end.join(', ')
2176
+ end
2177
+
2178
+ # Accepts an array of conditions. The array has each value
2179
+ # sanitized and interpolated into the SQL statement.
2180
+ # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
2181
+ def sanitize_sql_array(ary)
2182
+ statement, *values = ary
2183
+ if values.first.is_a?(Hash) and statement =~ /:\w+/
2184
+ replace_named_bind_variables(statement, values.first)
2185
+ elsif statement.include?('?')
2186
+ replace_bind_variables(statement, values)
2187
+ else
2188
+ statement % values.collect { |value| connection.quote_string(value.to_s) }
2189
+ end
2190
+ end
2191
+
2192
+ alias_method :sanitize_conditions, :sanitize_sql
2193
+
2194
+ def replace_bind_variables(statement, values) #:nodoc:
2195
+ raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
2196
+ bound = values.dup
2197
+ statement.gsub('?') { quote_bound_value(bound.shift) }
2198
+ end
2199
+
2200
+ def replace_named_bind_variables(statement, bind_vars) #:nodoc:
2201
+ statement.gsub(/(:?):([a-zA-Z]\w*)/) do
2202
+ if $1 == ':' # skip postgresql casts
2203
+ $& # return the whole match
2204
+ elsif bind_vars.include?(match = $2.to_sym)
2205
+ quote_bound_value(bind_vars[match])
2206
+ else
2207
+ raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
2208
+ end
2209
+ end
2210
+ end
2211
+
2212
+ def expand_range_bind_variables(bind_vars) #:nodoc:
2213
+ expanded = []
2214
+
2215
+ bind_vars.each do |var|
2216
+ next if var.is_a?(Hash)
2217
+
2218
+ if var.is_a?(Range)
2219
+ expanded << var.first
2220
+ expanded << var.last
2221
+ else
2222
+ expanded << var
2223
+ end
2224
+ end
2225
+
2226
+ expanded
2227
+ end
2228
+
2229
+ def quote_bound_value(value) #:nodoc:
2230
+ if value.respond_to?(:map) && !value.acts_like?(:string)
2231
+ if value.respond_to?(:empty?) && value.empty?
2232
+ connection.quote(nil)
2233
+ else
2234
+ value.map { |v| connection.quote(v) }.join(',')
2235
+ end
2236
+ else
2237
+ connection.quote(value)
2238
+ end
2239
+ end
2240
+
2241
+ def raise_if_bind_arity_mismatch(statement, expected, provided) #:nodoc:
2242
+ unless expected == provided
2243
+ raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
2244
+ end
2245
+ end
2246
+
2247
+ VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset,
2248
+ :order, :select, :readonly, :group, :from, :lock ]
2249
+
2250
+ def validate_find_options(options) #:nodoc:
2251
+ options.assert_valid_keys(VALID_FIND_OPTIONS)
2252
+ end
2253
+
2254
+ def set_readonly_option!(options) #:nodoc:
2255
+ # Inherit :readonly from finder scope if set. Otherwise,
2256
+ # if :joins is not blank then :readonly defaults to true.
2257
+ unless options.has_key?(:readonly)
2258
+ if scoped_readonly = scope(:find, :readonly)
2259
+ options[:readonly] = scoped_readonly
2260
+ elsif !options[:joins].blank? && !options[:select]
2261
+ options[:readonly] = true
2262
+ end
2263
+ end
2264
+ end
2265
+
2266
+ def encode_quoted_value(value) #:nodoc:
2267
+ quoted_value = connection.quote(value)
2268
+ quoted_value = "'#{quoted_value[1..-2].gsub(/\'/, "\\\\'")}'" if quoted_value.include?("\\\'") # (for ruby mode) "
2269
+ quoted_value
2270
+ end
2271
+ end
2272
+
2273
+ public
2274
+ # New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
2275
+ # attributes but not yet saved (pass a hash with key names matching the associated table column names).
2276
+ # In both instances, valid attribute keys are determined by the column names of the associated table --
2277
+ # hence you can't have attributes that aren't part of the table columns.
2278
+ def initialize(attributes = nil)
2279
+ @attributes = attributes_from_column_definition
2280
+ @attributes_cache = {}
2281
+ @new_record = true
2282
+ ensure_proper_type
2283
+ self.attributes = attributes unless attributes.nil?
2284
+ self.class.send(:scope, :create).each { |att,value| self.send("#{att}=", value) } if self.class.send(:scoped?, :create)
2285
+ result = yield self if block_given?
2286
+ callback(:after_initialize) if respond_to_without_attributes?(:after_initialize)
2287
+ result
2288
+ end
2289
+
2290
+ # A model instance's primary key is always available as model.id
2291
+ # whether you name it the default 'id' or set it to something else.
2292
+ def id
2293
+ attr_name = self.class.primary_key
2294
+ column = column_for_attribute(attr_name)
2295
+
2296
+ self.class.send(:define_read_method, :id, attr_name, column)
2297
+ # now that the method exists, call it
2298
+ self.send attr_name.to_sym
2299
+
2300
+ end
2301
+
2302
+ # Returns a String, which Action Pack uses for constructing an URL to this
2303
+ # object. The default implementation returns this record's id as a String,
2304
+ # or nil if this record's unsaved.
2305
+ #
2306
+ # For example, suppose that you have a Users model, and that you have a
2307
+ # <tt>map.resources :users</tt> route. Normally, +users_path+ will
2308
+ # construct an URI with the user object's 'id' in it:
2309
+ #
2310
+ # user = User.find_by_name('Phusion')
2311
+ # user_path(path) # => "/users/1"
2312
+ #
2313
+ # You can override +to_param+ in your model to make +users_path+ construct
2314
+ # an URI using the user's name instead of the user's id:
2315
+ #
2316
+ # class User < ActiveRecord::Base
2317
+ # def to_param # overridden
2318
+ # name
2319
+ # end
2320
+ # end
2321
+ #
2322
+ # user = User.find_by_name('Phusion')
2323
+ # user_path(path) # => "/users/Phusion"
2324
+ def to_param
2325
+ # We can't use alias_method here, because method 'id' optimizes itself on the fly.
2326
+ (id = self.id) ? id.to_s : nil # Be sure to stringify the id for routes
2327
+ end
2328
+
2329
+ # Returns a cache key that can be used to identify this record.
2330
+ #
2331
+ # ==== Examples
2332
+ #
2333
+ # Product.new.cache_key # => "products/new"
2334
+ # Product.find(5).cache_key # => "products/5" (updated_at not available)
2335
+ # Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
2336
+ def cache_key
2337
+ case
2338
+ when new_record?
2339
+ "#{self.class.model_name.cache_key}/new"
2340
+ when timestamp = self[:updated_at]
2341
+ "#{self.class.model_name.cache_key}/#{id}-#{timestamp.to_s(:number)}"
2342
+ else
2343
+ "#{self.class.model_name.cache_key}/#{id}"
2344
+ end
2345
+ end
2346
+
2347
+ def id_before_type_cast #:nodoc:
2348
+ read_attribute_before_type_cast(self.class.primary_key)
2349
+ end
2350
+
2351
+ def quoted_id #:nodoc:
2352
+ quote_value(id, column_for_attribute(self.class.primary_key))
2353
+ end
2354
+
2355
+ # Sets the primary ID.
2356
+ def id=(value)
2357
+ write_attribute(self.class.primary_key, value)
2358
+ end
2359
+
2360
+ # Returns true if this object hasn't been saved yet -- that is, a record for the object doesn't exist yet.
2361
+ def new_record?
2362
+ defined?(@new_record) && @new_record
2363
+ end
2364
+
2365
+ # :call-seq:
2366
+ # save(perform_validation = true)
2367
+ #
2368
+ # Saves the model.
2369
+ #
2370
+ # If the model is new a record gets created in the database, otherwise
2371
+ # the existing record gets updated.
2372
+ #
2373
+ # If +perform_validation+ is true validations run. If any of them fail
2374
+ # the action is cancelled and +save+ returns +false+. If the flag is
2375
+ # false validations are bypassed altogether. See
2376
+ # ActiveRecord::Validations for more information.
2377
+ #
2378
+ # There's a series of callbacks associated with +save+. If any of the
2379
+ # <tt>before_*</tt> callbacks return +false+ the action is cancelled and
2380
+ # +save+ returns +false+. See ActiveRecord::Callbacks for further
2381
+ # details.
2382
+ def save
2383
+ create_or_update
2384
+ end
2385
+
2386
+ # Saves the model.
2387
+ #
2388
+ # If the model is new a record gets created in the database, otherwise
2389
+ # the existing record gets updated.
2390
+ #
2391
+ # With <tt>save!</tt> validations always run. If any of them fail
2392
+ # ActiveRecord::RecordInvalid gets raised. See ActiveRecord::Validations
2393
+ # for more information.
2394
+ #
2395
+ # There's a series of callbacks associated with <tt>save!</tt>. If any of
2396
+ # the <tt>before_*</tt> callbacks return +false+ the action is cancelled
2397
+ # and <tt>save!</tt> raises ActiveRecord::RecordNotSaved. See
2398
+ # ActiveRecord::Callbacks for further details.
2399
+ def save!
2400
+ create_or_update || raise(RecordNotSaved)
2401
+ end
2402
+
2403
+ # Deletes the record in the database and freezes this instance to reflect that no changes should
2404
+ # be made (since they can't be persisted).
2405
+ #
2406
+ # Unlike #destroy, this method doesn't run any +before_delete+ and +after_delete+
2407
+ # callbacks, nor will it enforce any association +:dependent+ rules.
2408
+ #
2409
+ # In addition to deleting this record, any defined +before_delete+ and +after_delete+
2410
+ # callbacks are run, and +:dependent+ rules defined on associations are run.
2411
+ def delete
2412
+ self.class.delete(id) unless new_record?
2413
+ freeze
2414
+ end
2415
+
2416
+ # Deletes the record in the database and freezes this instance to reflect that no changes should
2417
+ # be made (since they can't be persisted).
2418
+ def destroy
2419
+ unless new_record?
2420
+ connection.delete(
2421
+ "DELETE FROM #{self.class.quoted_table_name} " +
2422
+ "WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quoted_id}",
2423
+ "#{self.class.name} Destroy"
2424
+ )
2425
+ end
2426
+
2427
+ freeze
2428
+ end
2429
+
2430
+ # Returns a clone of the record that hasn't been assigned an id yet and
2431
+ # is treated as a new record. Note that this is a "shallow" clone:
2432
+ # it copies the object's attributes only, not its associations.
2433
+ # The extent of a "deep" clone is application-specific and is therefore
2434
+ # left to the application to implement according to its need.
2435
+ def clone
2436
+ attrs = clone_attributes(:read_attribute_before_type_cast)
2437
+ attrs.delete(self.class.primary_key)
2438
+ record = self.class.new
2439
+ record.send :instance_variable_set, '@attributes', attrs
2440
+ record
2441
+ end
2442
+
2443
+ # Returns an instance of the specified +klass+ with the attributes of the current record. This is mostly useful in relation to
2444
+ # single-table inheritance structures where you want a subclass to appear as the superclass. This can be used along with record
2445
+ # identification in Action Pack to allow, say, <tt>Client < Company</tt> to do something like render <tt>:partial => @client.becomes(Company)</tt>
2446
+ # to render that instance using the companies/company partial instead of clients/client.
2447
+ #
2448
+ # Note: The new instance will share a link to the same attributes as the original class. So any change to the attributes in either
2449
+ # instance will affect the other.
2450
+ def becomes(klass)
2451
+ returning klass.new do |became|
2452
+ became.instance_variable_set("@attributes", @attributes)
2453
+ became.instance_variable_set("@attributes_cache", @attributes_cache)
2454
+ became.instance_variable_set("@new_record", new_record?)
2455
+ end
2456
+ end
2457
+
2458
+ # Updates a single attribute and saves the record without going through the normal validation procedure.
2459
+ # This is especially useful for boolean flags on existing records. The regular +update_attribute+ method
2460
+ # in Base is replaced with this when the validations module is mixed in, which it is by default.
2461
+ def update_attribute(name, value)
2462
+ send(name.to_s + '=', value)
2463
+ save(false)
2464
+ end
2465
+
2466
+ # Updates all the attributes from the passed-in Hash and saves the record. If the object is invalid, the saving will
2467
+ # fail and false will be returned.
2468
+ def update_attributes(attributes)
2469
+ self.attributes = attributes
2470
+ save
2471
+ end
2472
+
2473
+ # Updates an object just like Base.update_attributes but calls save! instead of save so an exception is raised if the record is invalid.
2474
+ def update_attributes!(attributes)
2475
+ self.attributes = attributes
2476
+ save!
2477
+ end
2478
+
2479
+ # Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1).
2480
+ # The increment is performed directly on the underlying attribute, no setter is invoked.
2481
+ # Only makes sense for number-based attributes. Returns +self+.
2482
+ def increment(attribute, by = 1)
2483
+ self[attribute] ||= 0
2484
+ self[attribute] += by
2485
+ self
2486
+ end
2487
+
2488
+ # Wrapper around +increment+ that saves the record. This method differs from
2489
+ # its non-bang version in that it passes through the attribute setter.
2490
+ # Saving is not subjected to validation checks. Returns +true+ if the
2491
+ # record could be saved.
2492
+ def increment!(attribute, by = 1)
2493
+ increment(attribute, by).update_attribute(attribute, self[attribute])
2494
+ end
2495
+
2496
+ # Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1).
2497
+ # The decrement is performed directly on the underlying attribute, no setter is invoked.
2498
+ # Only makes sense for number-based attributes. Returns +self+.
2499
+ def decrement(attribute, by = 1)
2500
+ self[attribute] ||= 0
2501
+ self[attribute] -= by
2502
+ self
2503
+ end
2504
+
2505
+ # Wrapper around +decrement+ that saves the record. This method differs from
2506
+ # its non-bang version in that it passes through the attribute setter.
2507
+ # Saving is not subjected to validation checks. Returns +true+ if the
2508
+ # record could be saved.
2509
+ def decrement!(attribute, by = 1)
2510
+ decrement(attribute, by).update_attribute(attribute, self[attribute])
2511
+ end
2512
+
2513
+ # Assigns to +attribute+ the boolean opposite of <tt>attribute?</tt>. So
2514
+ # if the predicate returns +true+ the attribute will become +false+. This
2515
+ # method toggles directly the underlying value without calling any setter.
2516
+ # Returns +self+.
2517
+ def toggle(attribute)
2518
+ self[attribute] = !send("#{attribute}?")
2519
+ self
2520
+ end
2521
+
2522
+ # Wrapper around +toggle+ that saves the record. This method differs from
2523
+ # its non-bang version in that it passes through the attribute setter.
2524
+ # Saving is not subjected to validation checks. Returns +true+ if the
2525
+ # record could be saved.
2526
+ def toggle!(attribute)
2527
+ toggle(attribute).update_attribute(attribute, self[attribute])
2528
+ end
2529
+
2530
+ # Reloads the attributes of this object from the database.
2531
+ # The optional options argument is passed to find when reloading so you
2532
+ # may do e.g. record.reload(:lock => true) to reload the same record with
2533
+ # an exclusive row lock.
2534
+ def reload(options = nil)
2535
+ clear_aggregation_cache
2536
+ clear_association_cache
2537
+ @attributes.update(self.class.find(self.id, options).instance_variable_get('@attributes'))
2538
+ @attributes_cache = {}
2539
+ self
2540
+ end
2541
+
2542
+ # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
2543
+ # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
2544
+ # (Alias for the protected read_attribute method).
2545
+ def [](attr_name)
2546
+ read_attribute(attr_name)
2547
+ end
2548
+
2549
+ # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
2550
+ # (Alias for the protected write_attribute method).
2551
+ def []=(attr_name, value)
2552
+ write_attribute(attr_name, value)
2553
+ end
2554
+
2555
+ # Allows you to set all the attributes at once by passing in a hash with keys
2556
+ # matching the attribute names (which again matches the column names).
2557
+ #
2558
+ # If +guard_protected_attributes+ is true (the default), then sensitive
2559
+ # attributes can be protected from this form of mass-assignment by using
2560
+ # the +attr_protected+ macro. Or you can alternatively specify which
2561
+ # attributes *can* be accessed with the +attr_accessible+ macro. Then all the
2562
+ # attributes not included in that won't be allowed to be mass-assigned.
2563
+ #
2564
+ # class User < ActiveRecord::Base
2565
+ # attr_protected :is_admin
2566
+ # end
2567
+ #
2568
+ # user = User.new
2569
+ # user.attributes = { :username => 'Phusion', :is_admin => true }
2570
+ # user.username # => "Phusion"
2571
+ # user.is_admin? # => false
2572
+ #
2573
+ # user.send(:attributes=, { :username => 'Phusion', :is_admin => true }, false)
2574
+ # user.is_admin? # => true
2575
+ def attributes=(new_attributes, guard_protected_attributes = true)
2576
+ return if new_attributes.nil?
2577
+ attributes = new_attributes.dup
2578
+ attributes.stringify_keys!
2579
+
2580
+ multi_parameter_attributes = []
2581
+ attributes = remove_attributes_protected_from_mass_assignment(attributes) if guard_protected_attributes
2582
+
2583
+ attributes.each do |k, v|
2584
+ if k.include?("(")
2585
+ multi_parameter_attributes << [ k, v ]
2586
+ else
2587
+ respond_to?(:"#{k}=") ? send(:"#{k}=", v) : raise(UnknownAttributeError, "unknown attribute: #{k}")
2588
+ end
2589
+ end
2590
+
2591
+ assign_multiparameter_attributes(multi_parameter_attributes)
2592
+ end
2593
+
2594
+
2595
+ # Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
2596
+ def attributes
2597
+ self.attribute_names.inject({}) do |attrs, name|
2598
+ attrs[name] = read_attribute(name)
2599
+ attrs
2600
+ end
2601
+ end
2602
+
2603
+ # Returns a hash of attributes before typecasting and deserialization.
2604
+ def attributes_before_type_cast
2605
+ self.attribute_names.inject({}) do |attrs, name|
2606
+ attrs[name] = read_attribute_before_type_cast(name)
2607
+ attrs
2608
+ end
2609
+ end
2610
+
2611
+ # Format attributes nicely for inspect.
2612
+ def attribute_for_inspect(attr_name)
2613
+ value = read_attribute(attr_name)
2614
+
2615
+ if value.is_a?(String) && value.length > 50
2616
+ "#{value[0..50]}...".inspect
2617
+ elsif value.is_a?(Date) || value.is_a?(Time)
2618
+ %("#{value.to_s(:db)}")
2619
+ else
2620
+ value.inspect
2621
+ end
2622
+ end
2623
+
2624
+ # Returns true if the specified +attribute+ has been set by the user or by a database load and is neither
2625
+ # nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings).
2626
+ def attribute_present?(attribute)
2627
+ value = read_attribute(attribute)
2628
+ !value.blank?
2629
+ end
2630
+
2631
+ # Returns true if the given attribute is in the attributes hash
2632
+ def has_attribute?(attr_name)
2633
+ @attributes.has_key?(attr_name.to_s)
2634
+ end
2635
+
2636
+ # Returns an array of names for the attributes available on this object sorted alphabetically.
2637
+ def attribute_names
2638
+ @attributes.keys.sort
2639
+ end
2640
+
2641
+ # Returns the column object for the named attribute.
2642
+ def column_for_attribute(name)
2643
+ self.class.columns_hash[name.to_s]
2644
+ end
2645
+
2646
+ # Returns true if the +comparison_object+ is the same object, or is of the same type and has the same id.
2647
+ def ==(comparison_object)
2648
+ comparison_object.equal?(self) ||
2649
+ (comparison_object.instance_of?(self.class) &&
2650
+ comparison_object.id == id &&
2651
+ !comparison_object.new_record?)
2652
+ end
2653
+
2654
+ # Delegates to ==
2655
+ def eql?(comparison_object)
2656
+ self == (comparison_object)
2657
+ end
2658
+
2659
+ # Delegates to id in order to allow two records of the same type and id to work with something like:
2660
+ # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
2661
+ def hash
2662
+ id.hash
2663
+ end
2664
+
2665
+ # Freeze the attributes hash such that associations are still accessible, even on destroyed records.
2666
+ def freeze
2667
+ @attributes.freeze; self
2668
+ end
2669
+
2670
+ # Returns +true+ if the attributes hash has been frozen.
2671
+ def frozen?
2672
+ @attributes.frozen?
2673
+ end
2674
+
2675
+ # Returns +true+ if the record is read only. Records loaded through joins with piggy-back
2676
+ # attributes will be marked as read only since they cannot be saved.
2677
+ def readonly?
2678
+ defined?(@readonly) && @readonly == true
2679
+ end
2680
+
2681
+ # Marks this record as read only.
2682
+ def readonly!
2683
+ @readonly = true
2684
+ end
2685
+
2686
+ # Returns the contents of the record as a nicely formatted string.
2687
+ def inspect
2688
+ attributes_as_nice_string = self.class.column_names.collect { |name|
2689
+ if has_attribute?(name) || new_record?
2690
+ "#{name}: #{attribute_for_inspect(name)}"
2691
+ end
2692
+ }.compact.join(", ")
2693
+ "#<#{self.class} #{attributes_as_nice_string}>"
2694
+ end
2695
+
2696
+ private
2697
+ def create_or_update
2698
+ raise ReadOnlyRecord if readonly?
2699
+ result = new_record? ? create : update
2700
+ result != false
2701
+ end
2702
+
2703
+ # Updates the associated record with values matching those of the instance attributes.
2704
+ # Returns the number of affected rows.
2705
+ def update(attribute_names = @attributes.keys)
2706
+ quoted_attributes = attributes_with_quotes(false, false, attribute_names)
2707
+ return 0 if quoted_attributes.empty?
2708
+ connection.update(
2709
+ "UPDATE #{self.class.quoted_table_name} " +
2710
+ "SET #{quoted_comma_pair_list(connection, quoted_attributes)} " +
2711
+ "WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quote_value(id)}",
2712
+ "#{self.class.name} Update"
2713
+ )
2714
+ end
2715
+
2716
+ # Creates a record with values matching those of the instance attributes
2717
+ # and returns its id.
2718
+ def create
2719
+ if self.id.nil? && connection.prefetch_primary_key?(self.class.table_name)
2720
+ self.id = connection.next_sequence_value(self.class.sequence_name)
2721
+ end
2722
+
2723
+ quoted_attributes = attributes_with_quotes
2724
+
2725
+ statement = if quoted_attributes.empty?
2726
+ connection.empty_insert_statement(self.class.table_name)
2727
+ else
2728
+ "INSERT INTO #{self.class.quoted_table_name} " +
2729
+ "(#{quoted_column_names.join(', ')}) " +
2730
+ "VALUES(#{quoted_attributes.values.join(', ')})"
2731
+ end
2732
+
2733
+ self.id = connection.insert(statement, "#{self.class.name} Create",
2734
+ self.class.primary_key, self.id, self.class.sequence_name)
2735
+
2736
+ @new_record = false
2737
+ id
2738
+ end
2739
+
2740
+ # Sets the attribute used for single table inheritance to this class name if this is not the ActiveRecord::Base descendent.
2741
+ # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to do Reply.new without having to
2742
+ # set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself. No such attribute would be set for objects of the
2743
+ # Message class in that example.
2744
+ def ensure_proper_type
2745
+ unless self.class.descends_from_active_record?
2746
+ write_attribute(self.class.inheritance_column, self.class.sti_name)
2747
+ end
2748
+ end
2749
+
2750
+ def convert_number_column_value(value)
2751
+ if value == false
2752
+ 0
2753
+ elsif value == true
2754
+ 1
2755
+ elsif value.is_a?(String) && value.blank?
2756
+ nil
2757
+ else
2758
+ value
2759
+ end
2760
+ end
2761
+
2762
+ def remove_attributes_protected_from_mass_assignment(attributes)
2763
+ safe_attributes =
2764
+ if self.class.accessible_attributes.nil? && self.class.protected_attributes.nil?
2765
+ attributes.reject { |key, value| attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
2766
+ elsif self.class.protected_attributes.nil?
2767
+ attributes.reject { |key, value| !self.class.accessible_attributes.include?(key.gsub(/\(.+/, "")) || attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
2768
+ elsif self.class.accessible_attributes.nil?
2769
+ attributes.reject { |key, value| self.class.protected_attributes.include?(key.gsub(/\(.+/,"")) || attributes_protected_by_default.include?(key.gsub(/\(.+/, "")) }
2770
+ else
2771
+ raise "Declare either attr_protected or attr_accessible for #{self.class}, but not both."
2772
+ end
2773
+
2774
+ removed_attributes = attributes.keys - safe_attributes.keys
2775
+
2776
+ if removed_attributes.any?
2777
+ log_protected_attribute_removal(removed_attributes)
2778
+ end
2779
+
2780
+ safe_attributes
2781
+ end
2782
+
2783
+ # Removes attributes which have been marked as readonly.
2784
+ def remove_readonly_attributes(attributes)
2785
+ unless self.class.readonly_attributes.nil?
2786
+ attributes.delete_if { |key, value| self.class.readonly_attributes.include?(key.gsub(/\(.+/,"")) }
2787
+ else
2788
+ attributes
2789
+ end
2790
+ end
2791
+
2792
+ def log_protected_attribute_removal(*attributes)
2793
+ logger.debug "WARNING: Can't mass-assign these protected attributes: #{attributes.join(', ')}"
2794
+ end
2795
+
2796
+ # The primary key and inheritance column can never be set by mass-assignment for security reasons.
2797
+ def attributes_protected_by_default
2798
+ default = [ self.class.primary_key, self.class.inheritance_column ]
2799
+ default << 'id' unless self.class.primary_key.eql? 'id'
2800
+ default
2801
+ end
2802
+
2803
+ # Returns a copy of the attributes hash where all the values have been safely quoted for use in
2804
+ # an SQL statement.
2805
+ def attributes_with_quotes(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys)
2806
+ quoted = {}
2807
+ connection = self.class.connection
2808
+ attribute_names.each do |name|
2809
+ if (column = column_for_attribute(name)) && (include_primary_key || !column.primary)
2810
+ value = read_attribute(name)
2811
+
2812
+ # We need explicit to_yaml because quote() does not properly convert Time/Date fields to YAML.
2813
+ if value && self.class.serialized_attributes.has_key?(name) && (value.acts_like?(:date) || value.acts_like?(:time))
2814
+ value = value.to_yaml
2815
+ end
2816
+
2817
+ quoted[name] = connection.quote(value, column)
2818
+ end
2819
+ end
2820
+ include_readonly_attributes ? quoted : remove_readonly_attributes(quoted)
2821
+ end
2822
+
2823
+ # Quote strings appropriately for SQL statements.
2824
+ def quote_value(value, column = nil)
2825
+ self.class.connection.quote(value, column)
2826
+ end
2827
+
2828
+ # Interpolate custom SQL string in instance context.
2829
+ # Optional record argument is meant for custom insert_sql.
2830
+ def interpolate_sql(sql, record = nil)
2831
+ instance_eval("%@#{sql.gsub('@', '\@')}@")
2832
+ end
2833
+
2834
+ # Initializes the attributes array with keys matching the columns from the linked table and
2835
+ # the values matching the corresponding default value of that column, so
2836
+ # that a new instance, or one populated from a passed-in Hash, still has all the attributes
2837
+ # that instances loaded from the database would.
2838
+ def attributes_from_column_definition
2839
+ self.class.columns.inject({}) do |attributes, column|
2840
+ attributes[column.name] = column.default unless column.name == self.class.primary_key
2841
+ attributes
2842
+ end
2843
+ end
2844
+
2845
+ # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
2846
+ # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
2847
+ # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
2848
+ # written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
2849
+ # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum, f for Float,
2850
+ # s for String, and a for Array. If all the values for a given attribute are empty, the attribute will be set to nil.
2851
+ def assign_multiparameter_attributes(pairs)
2852
+ execute_callstack_for_multiparameter_attributes(
2853
+ extract_callstack_for_multiparameter_attributes(pairs)
2854
+ )
2855
+ end
2856
+
2857
+ def instantiate_time_object(name, values)
2858
+ if self.class.send(:create_time_zone_conversion_attribute?, name, column_for_attribute(name))
2859
+ Time.zone.local(*values)
2860
+ else
2861
+ Time.time_with_datetime_fallback(@@default_timezone, *values)
2862
+ end
2863
+ end
2864
+
2865
+ def execute_callstack_for_multiparameter_attributes(callstack)
2866
+ errors = []
2867
+ callstack.each do |name, values|
2868
+ klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
2869
+ if values.empty?
2870
+ send(name + "=", nil)
2871
+ else
2872
+ begin
2873
+ value = if Time == klass
2874
+ instantiate_time_object(name, values)
2875
+ elsif Date == klass
2876
+ begin
2877
+ Date.new(*values)
2878
+ rescue ArgumentError => ex # if Date.new raises an exception on an invalid date
2879
+ instantiate_time_object(name, values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
2880
+ end
2881
+ else
2882
+ klass.new(*values)
2883
+ end
2884
+
2885
+ send(name + "=", value)
2886
+ rescue => ex
2887
+ errors << AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name)
2888
+ end
2889
+ end
2890
+ end
2891
+ unless errors.empty?
2892
+ raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
2893
+ end
2894
+ end
2895
+
2896
+ def extract_callstack_for_multiparameter_attributes(pairs)
2897
+ attributes = { }
2898
+
2899
+ for pair in pairs
2900
+ multiparameter_name, value = pair
2901
+ attribute_name = multiparameter_name.split("(").first
2902
+ attributes[attribute_name] = [] unless attributes.include?(attribute_name)
2903
+
2904
+ unless value.empty?
2905
+ attributes[attribute_name] <<
2906
+ [ find_parameter_position(multiparameter_name), type_cast_attribute_value(multiparameter_name, value) ]
2907
+ end
2908
+ end
2909
+
2910
+ attributes.each { |name, values| attributes[name] = values.sort_by{ |v| v.first }.collect { |v| v.last } }
2911
+ end
2912
+
2913
+ def type_cast_attribute_value(multiparameter_name, value)
2914
+ multiparameter_name =~ /\([0-9]*([a-z])\)/ ? value.send("to_" + $1) : value
2915
+ end
2916
+
2917
+ def find_parameter_position(multiparameter_name)
2918
+ multiparameter_name.scan(/\(([0-9]*).*\)/).first.first
2919
+ end
2920
+
2921
+ # Returns a comma-separated pair list, like "key1 = val1, key2 = val2".
2922
+ def comma_pair_list(hash)
2923
+ hash.inject([]) { |list, pair| list << "#{pair.first} = #{pair.last}" }.join(", ")
2924
+ end
2925
+
2926
+ def quoted_column_names(attributes = attributes_with_quotes)
2927
+ connection = self.class.connection
2928
+ attributes.keys.collect do |column_name|
2929
+ connection.quote_column_name(column_name)
2930
+ end
2931
+ end
2932
+
2933
+ def self.quoted_table_name
2934
+ self.connection.quote_table_name(self.table_name)
2935
+ end
2936
+
2937
+ def quote_columns(quoter, hash)
2938
+ hash.inject({}) do |quoted, (name, value)|
2939
+ quoted[quoter.quote_column_name(name)] = value
2940
+ quoted
2941
+ end
2942
+ end
2943
+
2944
+ def quoted_comma_pair_list(quoter, hash)
2945
+ comma_pair_list(quote_columns(quoter, hash))
2946
+ end
2947
+
2948
+ def object_from_yaml(string)
2949
+ return string unless string.is_a?(String) && string =~ /^---/
2950
+ YAML::load(string) rescue string
2951
+ end
2952
+
2953
+ def clone_attributes(reader_method = :read_attribute, attributes = {})
2954
+ self.attribute_names.inject(attributes) do |attrs, name|
2955
+ attrs[name] = clone_attribute_value(reader_method, name)
2956
+ attrs
2957
+ end
2958
+ end
2959
+
2960
+ def clone_attribute_value(reader_method, attribute_name)
2961
+ value = send(reader_method, attribute_name)
2962
+ value.duplicable? ? value.clone : value
2963
+ rescue TypeError, NoMethodError
2964
+ value
2965
+ end
2966
+ end
2967
+ end