activerecord 2.3.18 → 3.2.22

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

Potentially problematic release.


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

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