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,403 +1,275 @@
1
+ require 'active_support/core_ext/enumerable'
2
+ require 'active_support/deprecation'
3
+
1
4
  module ActiveRecord
5
+ # = Active Record Attribute Methods
2
6
  module AttributeMethods #:nodoc:
3
- DEFAULT_SUFFIXES = %w(= ? _before_type_cast)
4
- ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date]
5
-
6
- def self.included(base)
7
- base.extend ClassMethods
8
- base.attribute_method_suffix(*DEFAULT_SUFFIXES)
9
- base.cattr_accessor :attribute_types_cached_by_default, :instance_writer => false
10
- base.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
11
- base.cattr_accessor :time_zone_aware_attributes, :instance_writer => false
12
- base.time_zone_aware_attributes = false
13
- base.class_inheritable_accessor :skip_time_zone_conversion_for_attributes, :instance_writer => false
14
- base.skip_time_zone_conversion_for_attributes = []
15
- end
16
-
17
- # Declare and check for suffixed attribute methods.
18
- module ClassMethods
19
- # Declares a method available for all attributes with the given suffix.
20
- # Uses +method_missing+ and <tt>respond_to?</tt> to rewrite the method
21
- #
22
- # #{attr}#{suffix}(*args, &block)
23
- #
24
- # to
25
- #
26
- # attribute#{suffix}(#{attr}, *args, &block)
27
- #
28
- # An <tt>attribute#{suffix}</tt> instance method must exist and accept at least
29
- # the +attr+ argument.
30
- #
31
- # For example:
32
- #
33
- # class Person < ActiveRecord::Base
34
- # attribute_method_suffix '_changed?'
35
- #
36
- # private
37
- # def attribute_changed?(attr)
38
- # ...
39
- # end
40
- # end
41
- #
42
- # person = Person.find(1)
43
- # person.name_changed? # => false
44
- # person.name = 'Hubert'
45
- # person.name_changed? # => true
46
- def attribute_method_suffix(*suffixes)
47
- attribute_method_suffixes.concat suffixes
48
- rebuild_attribute_method_regexp
7
+ extend ActiveSupport::Concern
8
+ include ActiveModel::AttributeMethods
9
+
10
+ included do
11
+ include Read
12
+ include Write
13
+ include BeforeTypeCast
14
+ include Query
15
+ include PrimaryKey
16
+ include TimeZoneConversion
17
+ include Dirty
18
+ include Serialization
19
+ include DeprecatedUnderscoreRead
20
+
21
+ # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
22
+ # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
23
+ # (Alias for the protected read_attribute method).
24
+ def [](attr_name)
25
+ read_attribute(attr_name)
49
26
  end
50
27
 
51
- # Returns MatchData if method_name is an attribute method.
52
- def match_attribute_method?(method_name)
53
- rebuild_attribute_method_regexp unless defined?(@@attribute_method_regexp) && @@attribute_method_regexp
54
- @@attribute_method_regexp.match(method_name)
28
+ # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
29
+ # (Alias for the protected write_attribute method).
30
+ def []=(attr_name, value)
31
+ write_attribute(attr_name, value)
55
32
  end
33
+ end
56
34
 
57
-
58
- # Contains the names of the generated attribute methods.
59
- def generated_methods #:nodoc:
60
- @generated_methods ||= Set.new
61
- end
62
-
63
- def generated_methods?
64
- !generated_methods.empty?
65
- end
66
-
35
+ module ClassMethods
67
36
  # Generates all the attribute related methods for columns in the database
68
37
  # accessors, mutators and query methods.
69
38
  def define_attribute_methods
70
- return if generated_methods?
71
- columns_hash.each do |name, column|
72
- unless instance_method_already_implemented?(name)
73
- if self.serialized_attributes[name]
74
- define_read_method_for_serialized_attribute(name)
75
- elsif create_time_zone_conversion_attribute?(name, column)
76
- define_read_method_for_time_zone_conversion(name)
77
- else
78
- define_read_method(name.to_sym, name, column)
79
- end
39
+ unless defined?(@attribute_methods_mutex)
40
+ msg = "It looks like something (probably a gem/plugin) is overriding the " \
41
+ "ActiveRecord::Base.inherited method. It is important that this hook executes so " \
42
+ "that your models are set up correctly. A workaround has been added to stop this " \
43
+ "causing an error in 3.2, but future versions will simply not work if the hook is " \
44
+ "overridden. If you are using Kaminari, please upgrade as it is known to have had " \
45
+ "this problem.\n\n"
46
+ msg << "The following may help track down the problem:"
47
+
48
+ meth = method(:inherited)
49
+ if meth.respond_to?(:source_location)
50
+ msg << " #{meth.source_location.inspect}"
51
+ else
52
+ msg << " #{meth.inspect}"
80
53
  end
54
+ msg << "\n\n"
81
55
 
82
- unless instance_method_already_implemented?("#{name}=")
83
- if self.serialized_attributes[name]
84
- define_write_method_for_serialized_attribute(name)
85
- elsif create_time_zone_conversion_attribute?(name, column)
86
- define_write_method_for_time_zone_conversion(name)
87
- else
88
- define_write_method(name.to_sym)
89
- end
90
- end
56
+ ActiveSupport::Deprecation.warn(msg)
91
57
 
92
- unless instance_method_already_implemented?("#{name}?")
93
- define_question_method(name)
94
- end
58
+ @attribute_methods_mutex = Mutex.new
95
59
  end
96
- end
97
60
 
98
- # Checks whether the method is defined in the model or any of its subclasses
99
- # that also derive from Active Record. Raises DangerousAttributeError if the
100
- # method is defined by Active Record though.
101
- def instance_method_already_implemented?(method_name)
102
- method_name = method_name.to_s
103
- return true if method_name =~ /^id(=$|\?$|$)/
104
- @_defined_class_methods ||= ancestors.first(ancestors.index(ActiveRecord::Base)).sum([]) { |m| m.public_instance_methods(false) | m.private_instance_methods(false) | m.protected_instance_methods(false) }.map(&:to_s).to_set
105
- @@_defined_activerecord_methods ||= (ActiveRecord::Base.public_instance_methods(false) | ActiveRecord::Base.private_instance_methods(false) | ActiveRecord::Base.protected_instance_methods(false)).map(&:to_s).to_set
106
- raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord" if @@_defined_activerecord_methods.include?(method_name)
107
- @_defined_class_methods.include?(method_name)
108
- end
109
-
110
- alias :define_read_methods :define_attribute_methods
111
-
112
- # +cache_attributes+ allows you to declare which converted attribute values should
113
- # be cached. Usually caching only pays off for attributes with expensive conversion
114
- # methods, like time related columns (e.g. +created_at+, +updated_at+).
115
- def cache_attributes(*attribute_names)
116
- attribute_names.each {|attr| cached_attributes << attr.to_s}
61
+ # Use a mutex; we don't want two thread simaltaneously trying to define
62
+ # attribute methods.
63
+ @attribute_methods_mutex.synchronize do
64
+ return if attribute_methods_generated?
65
+ superclass.define_attribute_methods unless self == base_class
66
+ super(column_names)
67
+ column_names.each { |name| define_external_attribute_method(name) }
68
+ @attribute_methods_generated = true
69
+ end
117
70
  end
118
71
 
119
- # Returns the attributes which are cached. By default time related columns
120
- # with datatype <tt>:datetime, :timestamp, :time, :date</tt> are cached.
121
- def cached_attributes
122
- @cached_attributes ||=
123
- columns.select{|c| attribute_types_cached_by_default.include?(c.type)}.map(&:name).to_set
72
+ def attribute_methods_generated?
73
+ @attribute_methods_generated ||= false
124
74
  end
125
75
 
126
- # Returns +true+ if the provided attribute is being cached.
127
- def cache_attribute?(attr_name)
128
- cached_attributes.include?(attr_name)
76
+ # We will define the methods as instance methods, but will call them as singleton
77
+ # methods. This allows us to use method_defined? to check if the method exists,
78
+ # which is fast and won't give any false positives from the ancestors (because
79
+ # there are no ancestors).
80
+ def generated_external_attribute_methods
81
+ @generated_external_attribute_methods ||= Module.new { extend self }
129
82
  end
130
83
 
131
- private
132
- # Suffixes a, ?, c become regexp /(a|\?|c)$/
133
- def rebuild_attribute_method_regexp
134
- suffixes = attribute_method_suffixes.map { |s| Regexp.escape(s) }
135
- @@attribute_method_regexp = /(#{suffixes.join('|')})\z/.freeze
136
- end
137
-
138
- # Default to =, ?, _before_type_cast
139
- def attribute_method_suffixes
140
- @@attribute_method_suffixes ||= []
141
- end
142
-
143
- def create_time_zone_conversion_attribute?(name, column)
144
- time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(name.to_sym) && [:datetime, :timestamp].include?(column.type)
145
- end
146
-
147
- # Define an attribute reader method. Cope with nil column.
148
- def define_read_method(symbol, attr_name, column)
149
- cast_code = column.type_cast_code('v') if column
150
- access_code = cast_code ? "(v=@attributes['#{attr_name}']) && #{cast_code}" : "@attributes['#{attr_name}']"
151
-
152
- unless attr_name.to_s == self.primary_key.to_s
153
- access_code = access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless @attributes.has_key?('#{attr_name}'); ")
154
- end
155
-
156
- if cache_attribute?(attr_name)
157
- access_code = "@attributes_cache['#{attr_name}'] ||= (#{access_code})"
158
- end
159
- evaluate_attribute_method attr_name, "def #{symbol}; #{access_code}; end"
160
- end
161
-
162
- # Define read method for serialized attribute.
163
- def define_read_method_for_serialized_attribute(attr_name)
164
- evaluate_attribute_method attr_name, "def #{attr_name}; unserialize_attribute('#{attr_name}'); end"
165
- end
166
-
167
- # Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
168
- # This enhanced read method automatically converts the UTC time stored in the database to the time zone stored in Time.zone.
169
- def define_read_method_for_time_zone_conversion(attr_name)
170
- method_body = <<-EOV
171
- def #{attr_name}(reload = false)
172
- cached = @attributes_cache['#{attr_name}']
173
- return cached if cached && !reload
174
- time = read_attribute('#{attr_name}')
175
- @attributes_cache['#{attr_name}'] = time.acts_like?(:time) ? time.in_time_zone : time
176
- end
177
- EOV
178
- evaluate_attribute_method attr_name, method_body
179
- end
180
-
181
- # Defines a predicate method <tt>attr_name?</tt>.
182
- def define_question_method(attr_name)
183
- evaluate_attribute_method attr_name, "def #{attr_name}?; query_attribute('#{attr_name}'); end", "#{attr_name}?"
184
- end
84
+ def undefine_attribute_methods
85
+ super
86
+ @attribute_methods_generated = false
87
+ end
185
88
 
186
- def define_write_method(attr_name)
187
- evaluate_attribute_method attr_name, "def #{attr_name}=(new_value);write_attribute('#{attr_name}', new_value);end", "#{attr_name}="
89
+ def instance_method_already_implemented?(method_name)
90
+ if dangerous_attribute_method?(method_name)
91
+ raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord"
188
92
  end
189
93
 
190
- # Defined for all serialized attributes. Disallows assigning already serialized YAML.
191
- def define_write_method_for_serialized_attribute(attr_name)
192
- method_body = <<-EOV
193
- def #{attr_name}=(value)
194
- if value.is_a?(String) and value =~ /^---/
195
- raise ActiveRecordError, "You tried to assign already serialized content to #{attr_name}. This is disabled due to security issues."
196
- end
197
- write_attribute(:#{attr_name}, value)
198
- end
199
- EOV
200
- evaluate_attribute_method attr_name, method_body, "#{attr_name}="
201
- end
202
-
203
- # Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
204
- # This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone.
205
- def define_write_method_for_time_zone_conversion(attr_name)
206
- method_body = <<-EOV
207
- def #{attr_name}=(time)
208
- unless time.acts_like?(:time)
209
- time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
210
- end
211
- time = time.in_time_zone rescue nil if time
212
- write_attribute(:#{attr_name}, time)
213
- end
214
- EOV
215
- evaluate_attribute_method attr_name, method_body, "#{attr_name}="
94
+ if superclass == Base
95
+ super
96
+ else
97
+ # If B < A and A defines its own attribute method, then we don't want to overwrite that.
98
+ defined = method_defined_within?(method_name, superclass, superclass.generated_attribute_methods)
99
+ defined && !ActiveRecord::Base.method_defined?(method_name) || super
216
100
  end
101
+ end
217
102
 
218
- # Evaluate the definition for an attribute related method
219
- def evaluate_attribute_method(attr_name, method_definition, method_name=attr_name)
220
-
221
- unless method_name.to_s == primary_key.to_s
222
- generated_methods << method_name
223
- end
103
+ # A method name is 'dangerous' if it is already defined by Active Record, but
104
+ # not by any ancestors. (So 'puts' is not dangerous but 'save' is.)
105
+ def dangerous_attribute_method?(name)
106
+ method_defined_within?(name, Base)
107
+ end
224
108
 
225
- begin
226
- class_eval(method_definition, __FILE__)
227
- rescue SyntaxError => err
228
- generated_methods.delete(attr_name)
229
- if logger
230
- logger.warn "Exception occurred during reader method compilation."
231
- logger.warn "Maybe #{attr_name} is not a valid Ruby identifier?"
232
- logger.warn err.message
233
- end
109
+ def method_defined_within?(name, klass, sup = klass.superclass)
110
+ if klass.method_defined?(name) || klass.private_method_defined?(name)
111
+ if sup.method_defined?(name) || sup.private_method_defined?(name)
112
+ klass.instance_method(name).owner != sup.instance_method(name).owner
113
+ else
114
+ true
234
115
  end
116
+ else
117
+ false
235
118
  end
236
- end # ClassMethods
237
-
119
+ end
238
120
 
239
- # Allows access to the object attributes, which are held in the <tt>@attributes</tt> hash, as though they
240
- # were first-class methods. So a Person class with a name attribute can use Person#name and
241
- # Person#name= and never directly use the attributes hash -- except for multiple assigns with
242
- # ActiveRecord#attributes=. A Milestone class can also ask Milestone#completed? to test that
243
- # the completed attribute is not +nil+ or 0.
244
- #
245
- # It's also possible to instantiate related objects, so a Client class belonging to the clients
246
- # table with a +master_id+ foreign key can instantiate master through Client#master.
247
- def method_missing(method_id, *args, &block)
248
- method_name = method_id.to_s
121
+ def attribute_method?(attribute)
122
+ super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, '')))
123
+ end
249
124
 
250
- if self.class.private_method_defined?(method_name)
251
- raise NoMethodError.new("Attempt to call private method", method_name, args)
125
+ # Returns an array of column names as strings if it's not
126
+ # an abstract class and table exists.
127
+ # Otherwise it returns an empty array.
128
+ def attribute_names
129
+ @attribute_names ||= if !abstract_class? && table_exists?
130
+ column_names
131
+ else
132
+ []
133
+ end
252
134
  end
135
+ end
253
136
 
254
- # If we haven't generated any methods yet, generate them, then
255
- # see if we've created the method we're looking for.
256
- if !self.class.generated_methods?
137
+ # If we haven't generated any methods yet, generate them, then
138
+ # see if we've created the method we're looking for.
139
+ def method_missing(method, *args, &block)
140
+ unless self.class.attribute_methods_generated?
257
141
  self.class.define_attribute_methods
258
- if self.class.generated_methods.include?(method_name)
259
- return self.send(method_id, *args, &block)
260
- end
261
- end
262
-
263
- if self.class.primary_key.to_s == method_name
264
- id
265
- elsif md = self.class.match_attribute_method?(method_name)
266
- attribute_name, method_type = md.pre_match, md.to_s
267
- if @attributes.include?(attribute_name)
268
- __send__("attribute#{method_type}", attribute_name, *args, &block)
142
+
143
+ if respond_to_without_attributes?(method)
144
+ send(method, *args, &block)
269
145
  else
270
146
  super
271
147
  end
272
- elsif @attributes.include?(method_name)
273
- read_attribute(method_name)
274
148
  else
275
149
  super
276
150
  end
277
151
  end
278
152
 
279
- # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
280
- # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
281
- def read_attribute(attr_name)
282
- attr_name = attr_name.to_s
283
- if !(value = @attributes[attr_name]).nil?
284
- if column = column_for_attribute(attr_name)
285
- if unserializable_attribute?(attr_name, column)
286
- unserialize_attribute(attr_name)
287
- else
288
- column.type_cast(value)
289
- end
290
- else
291
- value
292
- end
293
- else
294
- nil
153
+ def attribute_missing(match, *args, &block)
154
+ if self.class.columns_hash[match.attr_name]
155
+ ActiveSupport::Deprecation.warn(
156
+ "The method `#{match.method_name}', matching the attribute `#{match.attr_name}' has " \
157
+ "dispatched through method_missing. This shouldn't happen, because `#{match.attr_name}' " \
158
+ "is a column of the table. If this error has happened through normal usage of Active " \
159
+ "Record (rather than through your own code or external libraries), please report it as " \
160
+ "a bug."
161
+ )
295
162
  end
163
+
164
+ super
296
165
  end
297
166
 
298
- def read_attribute_before_type_cast(attr_name)
299
- @attributes[attr_name]
167
+ def respond_to?(name, include_private = false)
168
+ self.class.define_attribute_methods unless self.class.attribute_methods_generated?
169
+ super
300
170
  end
301
171
 
302
- # Returns true if the attribute is of a text column and marked for serialization.
303
- def unserializable_attribute?(attr_name, column)
304
- column.text? && self.class.serialized_attributes[attr_name]
172
+ # Returns true if the given attribute is in the attributes hash
173
+ def has_attribute?(attr_name)
174
+ @attributes.has_key?(attr_name.to_s)
305
175
  end
306
176
 
307
- # Returns the unserialized object of the attribute.
308
- def unserialize_attribute(attr_name)
309
- unserialized_object = object_from_yaml(@attributes[attr_name])
177
+ # Returns an array of names for the attributes available on this object.
178
+ def attribute_names
179
+ @attributes.keys
180
+ end
310
181
 
311
- if unserialized_object.is_a?(self.class.serialized_attributes[attr_name]) || unserialized_object.nil?
312
- @attributes.frozen? ? unserialized_object : @attributes[attr_name] = unserialized_object
313
- else
314
- raise SerializationTypeMismatch,
315
- "#{attr_name} was supposed to be a #{self.class.serialized_attributes[attr_name]}, but was a #{unserialized_object.class.to_s}"
316
- end
182
+ # Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
183
+ def attributes
184
+ attrs = {}
185
+ attribute_names.each { |name| attrs[name] = read_attribute(name) }
186
+ attrs
317
187
  end
318
-
319
-
320
- # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+. Empty strings for fixnum and float
321
- # columns are turned into +nil+.
322
- def write_attribute(attr_name, value)
323
- attr_name = attr_name.to_s
324
- @attributes_cache.delete(attr_name)
325
- if (column = column_for_attribute(attr_name)) && column.number?
326
- @attributes[attr_name] = convert_number_column_value(value)
188
+
189
+ # Returns an <tt>#inspect</tt>-like string for the value of the
190
+ # attribute +attr_name+. String attributes are truncated upto 50
191
+ # characters, and Date and Time attributes are returned in the
192
+ # <tt>:db</tt> format. Other attributes return the value of
193
+ # <tt>#inspect</tt> without modification.
194
+ #
195
+ # person = Person.create!(:name => "David Heinemeier Hansson " * 3)
196
+ #
197
+ # person.attribute_for_inspect(:name)
198
+ # # => '"David Heinemeier Hansson David Heinemeier Hansson D..."'
199
+ #
200
+ # person.attribute_for_inspect(:created_at)
201
+ # # => '"2009-01-12 04:48:57"'
202
+ def attribute_for_inspect(attr_name)
203
+ value = read_attribute(attr_name)
204
+
205
+ if value.is_a?(String) && value.length > 50
206
+ "#{value[0..50]}...".inspect
207
+ elsif value.is_a?(Date) || value.is_a?(Time)
208
+ %("#{value.to_s(:db)}")
327
209
  else
328
- @attributes[attr_name] = value
210
+ value.inspect
329
211
  end
330
212
  end
331
213
 
214
+ # Returns true if the specified +attribute+ has been set by the user or by a database load and is neither
215
+ # nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings).
216
+ def attribute_present?(attribute)
217
+ value = read_attribute(attribute)
218
+ !value.nil? && !(value.respond_to?(:empty?) && value.empty?)
219
+ end
332
220
 
333
- def query_attribute(attr_name)
334
- unless value = read_attribute(attr_name)
335
- false
336
- else
337
- column = self.class.columns_hash[attr_name]
338
- if column.nil?
339
- if Numeric === value || value !~ /[^0-9]/
340
- !value.to_i.zero?
341
- else
342
- return false if ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES.include?(value)
343
- !value.blank?
344
- end
345
- elsif column.number?
346
- !value.zero?
347
- else
348
- !value.blank?
349
- end
350
- end
221
+ # Returns the column object for the named attribute.
222
+ def column_for_attribute(name)
223
+ self.class.columns_hash[name.to_s]
351
224
  end
352
-
353
- # A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
354
- # <tt>person.respond_to?(:name=)</tt>, and <tt>person.respond_to?(:name?)</tt>
355
- # which will all return +true+.
356
- alias :respond_to_without_attributes? :respond_to?
357
- def respond_to?(method, include_private_methods = false)
358
- method_name = method.to_s
359
- if super
360
- return true
361
- elsif !include_private_methods && super(method, true)
362
- # If we're here than we haven't found among non-private methods
363
- # but found among all methods. Which means that given method is private.
364
- return false
365
- elsif !self.class.generated_methods?
366
- self.class.define_attribute_methods
367
- if self.class.generated_methods.include?(method_name)
368
- return true
369
- end
370
- end
371
-
372
- if @attributes.nil?
373
- return super
374
- elsif @attributes.include?(method_name)
375
- return true
376
- elsif md = self.class.match_attribute_method?(method_name)
377
- return true if @attributes.include?(md.pre_match)
225
+
226
+ protected
227
+
228
+ def clone_attributes(reader_method = :read_attribute, attributes = {})
229
+ attribute_names.each do |name|
230
+ attributes[name] = clone_attribute_value(reader_method, name)
378
231
  end
379
- super
232
+ attributes
380
233
  end
381
234
 
382
- private
383
-
384
- def missing_attribute(attr_name, stack)
385
- raise ActiveRecord::MissingAttributeError, "missing attribute: #{attr_name}", stack
386
- end
387
-
388
- # Handle *? for method_missing.
389
- def attribute?(attribute_name)
390
- query_attribute(attribute_name)
391
- end
235
+ def clone_attribute_value(reader_method, attribute_name)
236
+ value = send(reader_method, attribute_name)
237
+ value.duplicable? ? value.clone : value
238
+ rescue TypeError, NoMethodError
239
+ value
240
+ end
392
241
 
393
- # Handle *= for method_missing.
394
- def attribute=(attribute_name, value)
395
- write_attribute(attribute_name, value)
396
- end
242
+ # Returns a copy of the attributes hash where all the values have been safely quoted for use in
243
+ # an Arel insert/update method.
244
+ def arel_attributes_values(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys)
245
+ attrs = {}
246
+ klass = self.class
247
+ arel_table = klass.arel_table
248
+
249
+ attribute_names.each do |name|
250
+ if (column = column_for_attribute(name)) && (include_primary_key || !column.primary)
397
251
 
398
- # Handle *_before_type_cast for method_missing.
399
- def attribute_before_type_cast(attribute_name)
400
- read_attribute_before_type_cast(attribute_name)
252
+ if include_readonly_attributes || !self.class.readonly_attributes.include?(name)
253
+
254
+ value = if klass.serialized_attributes.include?(name)
255
+ @attributes[name].serialized_value
256
+ else
257
+ # FIXME: we need @attributes to be used consistently.
258
+ # If the values stored in @attributes were already type
259
+ # casted, this code could be simplified
260
+ read_attribute(name)
261
+ end
262
+
263
+ attrs[arel_table[name]] = value
264
+ end
265
+ end
401
266
  end
267
+
268
+ attrs
269
+ end
270
+
271
+ def attribute_method?(attr_name)
272
+ attr_name == 'id' || (defined?(@attributes) && @attributes.include?(attr_name))
273
+ end
402
274
  end
403
275
  end