activerecord 2.3.18 → 3.0.0.beta

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 (378) hide show
  1. data/CHANGELOG +105 -34
  2. data/examples/performance.rb +3 -39
  3. data/examples/simple.rb +14 -0
  4. data/lib/active_record.rb +81 -47
  5. data/lib/active_record/aggregations.rb +1 -3
  6. data/lib/active_record/association_preload.rb +39 -54
  7. data/lib/active_record/associations.rb +262 -419
  8. data/lib/active_record/associations/association_collection.rb +85 -100
  9. data/lib/active_record/associations/association_proxy.rb +20 -18
  10. data/lib/active_record/associations/belongs_to_association.rb +8 -8
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +13 -35
  12. data/lib/active_record/associations/has_many_association.rb +11 -19
  13. data/lib/active_record/associations/has_many_through_association.rb +30 -183
  14. data/lib/active_record/associations/has_one_association.rb +10 -10
  15. data/lib/active_record/associations/has_one_through_association.rb +13 -11
  16. data/lib/active_record/associations/through_association_scope.rb +153 -0
  17. data/lib/active_record/attribute_methods.rb +17 -370
  18. data/lib/active_record/attribute_methods/before_type_cast.rb +33 -0
  19. data/lib/active_record/attribute_methods/dirty.rb +87 -0
  20. data/lib/active_record/attribute_methods/primary_key.rb +44 -0
  21. data/lib/active_record/attribute_methods/query.rb +37 -0
  22. data/lib/active_record/attribute_methods/read.rb +116 -0
  23. data/lib/active_record/attribute_methods/time_zone_conversion.rb +60 -0
  24. data/lib/active_record/attribute_methods/write.rb +37 -0
  25. data/lib/active_record/autosave_association.rb +20 -41
  26. data/lib/active_record/base.rb +357 -1180
  27. data/lib/active_record/batches.rb +10 -16
  28. data/lib/active_record/callbacks.rb +66 -126
  29. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +17 -13
  30. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +5 -25
  31. data/lib/active_record/connection_adapters/abstract/database_statements.rb +4 -43
  32. data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -2
  33. data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -4
  34. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  35. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +18 -72
  36. data/lib/active_record/connection_adapters/abstract_adapter.rb +16 -49
  37. data/lib/active_record/connection_adapters/mysql_adapter.rb +15 -27
  38. data/lib/active_record/connection_adapters/postgresql_adapter.rb +84 -46
  39. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +9 -3
  40. data/lib/active_record/connection_adapters/sqlite_adapter.rb +34 -65
  41. data/lib/active_record/fixtures.rb +21 -25
  42. data/lib/active_record/locale/en.yml +9 -27
  43. data/lib/active_record/locking/optimistic.rb +16 -48
  44. data/lib/active_record/migration.rb +59 -46
  45. data/lib/active_record/named_scope.rb +85 -92
  46. data/lib/active_record/nested_attributes.rb +18 -24
  47. data/lib/active_record/observer.rb +18 -94
  48. data/lib/active_record/railtie.rb +83 -0
  49. data/lib/active_record/railties/controller_runtime.rb +38 -0
  50. data/lib/active_record/railties/databases.rake +477 -0
  51. data/lib/active_record/railties/subscriber.rb +27 -0
  52. data/lib/active_record/reflection.rb +2 -15
  53. data/lib/active_record/relation.rb +339 -0
  54. data/lib/active_record/relation/calculations.rb +259 -0
  55. data/lib/active_record/relation/finder_methods.rb +315 -0
  56. data/lib/active_record/relation/predicate_builder.rb +46 -0
  57. data/lib/active_record/relation/query_methods.rb +218 -0
  58. data/lib/active_record/relation/spawn_methods.rb +102 -0
  59. data/lib/active_record/schema_dumper.rb +10 -6
  60. data/lib/active_record/serialization.rb +31 -74
  61. data/lib/active_record/serializers/xml_serializer.rb +33 -121
  62. data/lib/active_record/session_store.rb +1 -9
  63. data/lib/active_record/test_case.rb +1 -3
  64. data/lib/active_record/timestamp.rb +7 -5
  65. data/lib/active_record/transactions.rb +9 -9
  66. data/lib/active_record/validations.rb +51 -1100
  67. data/lib/active_record/validations/associated.rb +47 -0
  68. data/lib/active_record/validations/uniqueness.rb +181 -0
  69. data/lib/active_record/version.rb +3 -3
  70. data/lib/generators/active_record.rb +30 -0
  71. data/lib/generators/active_record/migration/migration_generator.rb +25 -0
  72. data/lib/generators/active_record/migration/templates/migration.rb +11 -0
  73. data/lib/generators/active_record/model/model_generator.rb +33 -0
  74. data/lib/generators/active_record/model/templates/migration.rb +16 -0
  75. data/lib/generators/active_record/model/templates/model.rb +5 -0
  76. data/lib/generators/active_record/observer/observer_generator.rb +15 -0
  77. data/lib/generators/active_record/observer/templates/observer.rb +2 -0
  78. data/lib/generators/active_record/session_migration/session_migration_generator.rb +24 -0
  79. data/lib/generators/active_record/session_migration/templates/migration.rb +16 -0
  80. metadata +67 -325
  81. data/RUNNING_UNIT_TESTS +0 -36
  82. data/Rakefile +0 -268
  83. data/install.rb +0 -30
  84. data/lib/active_record/calculations.rb +0 -321
  85. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -57
  86. data/lib/active_record/dirty.rb +0 -183
  87. data/lib/active_record/serializers/json_serializer.rb +0 -91
  88. data/lib/activerecord.rb +0 -2
  89. data/test/assets/example.log +0 -1
  90. data/test/assets/flowers.jpg +0 -0
  91. data/test/cases/aaa_create_tables_test.rb +0 -24
  92. data/test/cases/active_schema_test_mysql.rb +0 -122
  93. data/test/cases/active_schema_test_postgresql.rb +0 -24
  94. data/test/cases/adapter_test.rb +0 -144
  95. data/test/cases/aggregations_test.rb +0 -167
  96. data/test/cases/ar_schema_test.rb +0 -32
  97. data/test/cases/associations/belongs_to_associations_test.rb +0 -438
  98. data/test/cases/associations/callbacks_test.rb +0 -161
  99. data/test/cases/associations/cascaded_eager_loading_test.rb +0 -131
  100. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +0 -36
  101. data/test/cases/associations/eager_load_nested_include_test.rb +0 -131
  102. data/test/cases/associations/eager_load_nested_polymorphic_include.rb +0 -19
  103. data/test/cases/associations/eager_singularization_test.rb +0 -145
  104. data/test/cases/associations/eager_test.rb +0 -852
  105. data/test/cases/associations/extension_test.rb +0 -62
  106. data/test/cases/associations/habtm_join_table_test.rb +0 -56
  107. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +0 -827
  108. data/test/cases/associations/has_many_associations_test.rb +0 -1273
  109. data/test/cases/associations/has_many_through_associations_test.rb +0 -360
  110. data/test/cases/associations/has_one_associations_test.rb +0 -330
  111. data/test/cases/associations/has_one_through_associations_test.rb +0 -209
  112. data/test/cases/associations/inner_join_association_test.rb +0 -93
  113. data/test/cases/associations/inverse_associations_test.rb +0 -566
  114. data/test/cases/associations/join_model_test.rb +0 -712
  115. data/test/cases/associations_test.rb +0 -282
  116. data/test/cases/attribute_methods_test.rb +0 -305
  117. data/test/cases/autosave_association_test.rb +0 -1218
  118. data/test/cases/base_test.rb +0 -2166
  119. data/test/cases/batches_test.rb +0 -81
  120. data/test/cases/binary_test.rb +0 -30
  121. data/test/cases/calculations_test.rb +0 -360
  122. data/test/cases/callbacks_observers_test.rb +0 -38
  123. data/test/cases/callbacks_test.rb +0 -438
  124. data/test/cases/class_inheritable_attributes_test.rb +0 -32
  125. data/test/cases/column_alias_test.rb +0 -17
  126. data/test/cases/column_definition_test.rb +0 -70
  127. data/test/cases/connection_pool_test.rb +0 -25
  128. data/test/cases/connection_test_firebird.rb +0 -8
  129. data/test/cases/connection_test_mysql.rb +0 -65
  130. data/test/cases/copy_table_test_sqlite.rb +0 -80
  131. data/test/cases/counter_cache_test.rb +0 -84
  132. data/test/cases/database_statements_test.rb +0 -12
  133. data/test/cases/datatype_test_postgresql.rb +0 -204
  134. data/test/cases/date_time_test.rb +0 -37
  135. data/test/cases/default_test_firebird.rb +0 -16
  136. data/test/cases/defaults_test.rb +0 -111
  137. data/test/cases/deprecated_finder_test.rb +0 -30
  138. data/test/cases/dirty_test.rb +0 -316
  139. data/test/cases/finder_respond_to_test.rb +0 -76
  140. data/test/cases/finder_test.rb +0 -1098
  141. data/test/cases/fixtures_test.rb +0 -661
  142. data/test/cases/helper.rb +0 -68
  143. data/test/cases/i18n_test.rb +0 -46
  144. data/test/cases/inheritance_test.rb +0 -262
  145. data/test/cases/invalid_date_test.rb +0 -24
  146. data/test/cases/json_serialization_test.rb +0 -219
  147. data/test/cases/lifecycle_test.rb +0 -193
  148. data/test/cases/locking_test.rb +0 -350
  149. data/test/cases/method_scoping_test.rb +0 -704
  150. data/test/cases/migration_test.rb +0 -1649
  151. data/test/cases/migration_test_firebird.rb +0 -124
  152. data/test/cases/mixin_test.rb +0 -96
  153. data/test/cases/modules_test.rb +0 -109
  154. data/test/cases/multiple_db_test.rb +0 -85
  155. data/test/cases/named_scope_test.rb +0 -372
  156. data/test/cases/nested_attributes_test.rb +0 -840
  157. data/test/cases/pk_test.rb +0 -119
  158. data/test/cases/pooled_connections_test.rb +0 -103
  159. data/test/cases/query_cache_test.rb +0 -129
  160. data/test/cases/readonly_test.rb +0 -107
  161. data/test/cases/reflection_test.rb +0 -234
  162. data/test/cases/reload_models_test.rb +0 -22
  163. data/test/cases/repair_helper.rb +0 -50
  164. data/test/cases/reserved_word_test_mysql.rb +0 -176
  165. data/test/cases/sanitize_test.rb +0 -25
  166. data/test/cases/schema_authorization_test_postgresql.rb +0 -75
  167. data/test/cases/schema_dumper_test.rb +0 -211
  168. data/test/cases/schema_test_postgresql.rb +0 -178
  169. data/test/cases/serialization_test.rb +0 -47
  170. data/test/cases/sp_test_mysql.rb +0 -16
  171. data/test/cases/synonym_test_oracle.rb +0 -17
  172. data/test/cases/timestamp_test.rb +0 -75
  173. data/test/cases/transactions_test.rb +0 -543
  174. data/test/cases/unconnected_test.rb +0 -32
  175. data/test/cases/validations_i18n_test.rb +0 -925
  176. data/test/cases/validations_test.rb +0 -1684
  177. data/test/cases/xml_serialization_test.rb +0 -240
  178. data/test/cases/yaml_serialization_test.rb +0 -11
  179. data/test/config.rb +0 -5
  180. data/test/connections/jdbc_jdbcderby/connection.rb +0 -18
  181. data/test/connections/jdbc_jdbch2/connection.rb +0 -18
  182. data/test/connections/jdbc_jdbchsqldb/connection.rb +0 -18
  183. data/test/connections/jdbc_jdbcmysql/connection.rb +0 -26
  184. data/test/connections/jdbc_jdbcpostgresql/connection.rb +0 -26
  185. data/test/connections/jdbc_jdbcsqlite3/connection.rb +0 -25
  186. data/test/connections/native_db2/connection.rb +0 -25
  187. data/test/connections/native_firebird/connection.rb +0 -26
  188. data/test/connections/native_frontbase/connection.rb +0 -27
  189. data/test/connections/native_mysql/connection.rb +0 -25
  190. data/test/connections/native_openbase/connection.rb +0 -21
  191. data/test/connections/native_oracle/connection.rb +0 -27
  192. data/test/connections/native_postgresql/connection.rb +0 -21
  193. data/test/connections/native_sqlite/connection.rb +0 -25
  194. data/test/connections/native_sqlite3/connection.rb +0 -25
  195. data/test/connections/native_sqlite3/in_memory_connection.rb +0 -18
  196. data/test/connections/native_sybase/connection.rb +0 -23
  197. data/test/fixtures/accounts.yml +0 -29
  198. data/test/fixtures/all/developers.yml +0 -0
  199. data/test/fixtures/all/people.csv +0 -0
  200. data/test/fixtures/all/tasks.yml +0 -0
  201. data/test/fixtures/author_addresses.yml +0 -5
  202. data/test/fixtures/author_favorites.yml +0 -4
  203. data/test/fixtures/authors.yml +0 -9
  204. data/test/fixtures/binaries.yml +0 -132
  205. data/test/fixtures/books.yml +0 -7
  206. data/test/fixtures/categories.yml +0 -14
  207. data/test/fixtures/categories/special_categories.yml +0 -9
  208. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +0 -4
  209. data/test/fixtures/categories_ordered.yml +0 -7
  210. data/test/fixtures/categories_posts.yml +0 -23
  211. data/test/fixtures/categorizations.yml +0 -17
  212. data/test/fixtures/clubs.yml +0 -6
  213. data/test/fixtures/comments.yml +0 -59
  214. data/test/fixtures/companies.yml +0 -56
  215. data/test/fixtures/computers.yml +0 -4
  216. data/test/fixtures/courses.yml +0 -7
  217. data/test/fixtures/customers.yml +0 -26
  218. data/test/fixtures/developers.yml +0 -21
  219. data/test/fixtures/developers_projects.yml +0 -17
  220. data/test/fixtures/edges.yml +0 -6
  221. data/test/fixtures/entrants.yml +0 -14
  222. data/test/fixtures/faces.yml +0 -11
  223. data/test/fixtures/fk_test_has_fk.yml +0 -3
  224. data/test/fixtures/fk_test_has_pk.yml +0 -2
  225. data/test/fixtures/funny_jokes.yml +0 -10
  226. data/test/fixtures/interests.yml +0 -33
  227. data/test/fixtures/items.yml +0 -4
  228. data/test/fixtures/jobs.yml +0 -7
  229. data/test/fixtures/legacy_things.yml +0 -3
  230. data/test/fixtures/mateys.yml +0 -4
  231. data/test/fixtures/member_types.yml +0 -6
  232. data/test/fixtures/members.yml +0 -6
  233. data/test/fixtures/memberships.yml +0 -20
  234. data/test/fixtures/men.yml +0 -5
  235. data/test/fixtures/minimalistics.yml +0 -2
  236. data/test/fixtures/mixed_case_monkeys.yml +0 -6
  237. data/test/fixtures/mixins.yml +0 -29
  238. data/test/fixtures/movies.yml +0 -7
  239. data/test/fixtures/naked/csv/accounts.csv +0 -1
  240. data/test/fixtures/naked/yml/accounts.yml +0 -1
  241. data/test/fixtures/naked/yml/companies.yml +0 -1
  242. data/test/fixtures/naked/yml/courses.yml +0 -1
  243. data/test/fixtures/organizations.yml +0 -5
  244. data/test/fixtures/owners.yml +0 -7
  245. data/test/fixtures/parrots.yml +0 -27
  246. data/test/fixtures/parrots_pirates.yml +0 -7
  247. data/test/fixtures/people.yml +0 -15
  248. data/test/fixtures/pets.yml +0 -14
  249. data/test/fixtures/pirates.yml +0 -9
  250. data/test/fixtures/polymorphic_designs.yml +0 -19
  251. data/test/fixtures/polymorphic_prices.yml +0 -19
  252. data/test/fixtures/posts.yml +0 -52
  253. data/test/fixtures/price_estimates.yml +0 -7
  254. data/test/fixtures/projects.yml +0 -7
  255. data/test/fixtures/readers.yml +0 -9
  256. data/test/fixtures/references.yml +0 -17
  257. data/test/fixtures/reserved_words/distinct.yml +0 -5
  258. data/test/fixtures/reserved_words/distincts_selects.yml +0 -11
  259. data/test/fixtures/reserved_words/group.yml +0 -14
  260. data/test/fixtures/reserved_words/select.yml +0 -8
  261. data/test/fixtures/reserved_words/values.yml +0 -7
  262. data/test/fixtures/ships.yml +0 -5
  263. data/test/fixtures/sponsors.yml +0 -9
  264. data/test/fixtures/subscribers.yml +0 -7
  265. data/test/fixtures/subscriptions.yml +0 -12
  266. data/test/fixtures/taggings.yml +0 -28
  267. data/test/fixtures/tags.yml +0 -7
  268. data/test/fixtures/tasks.yml +0 -7
  269. data/test/fixtures/tees.yml +0 -4
  270. data/test/fixtures/ties.yml +0 -4
  271. data/test/fixtures/topics.yml +0 -42
  272. data/test/fixtures/toys.yml +0 -4
  273. data/test/fixtures/treasures.yml +0 -10
  274. data/test/fixtures/vertices.yml +0 -4
  275. data/test/fixtures/warehouse-things.yml +0 -3
  276. data/test/fixtures/zines.yml +0 -5
  277. data/test/migrations/broken/100_migration_that_raises_exception.rb +0 -10
  278. data/test/migrations/decimal/1_give_me_big_numbers.rb +0 -15
  279. data/test/migrations/duplicate/1_people_have_last_names.rb +0 -9
  280. data/test/migrations/duplicate/2_we_need_reminders.rb +0 -12
  281. data/test/migrations/duplicate/3_foo.rb +0 -7
  282. data/test/migrations/duplicate/3_innocent_jointable.rb +0 -12
  283. data/test/migrations/duplicate_names/20080507052938_chunky.rb +0 -7
  284. data/test/migrations/duplicate_names/20080507053028_chunky.rb +0 -7
  285. data/test/migrations/interleaved/pass_1/3_innocent_jointable.rb +0 -12
  286. data/test/migrations/interleaved/pass_2/1_people_have_last_names.rb +0 -9
  287. data/test/migrations/interleaved/pass_2/3_innocent_jointable.rb +0 -12
  288. data/test/migrations/interleaved/pass_3/1_people_have_last_names.rb +0 -9
  289. data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +0 -8
  290. data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +0 -12
  291. data/test/migrations/missing/1000_people_have_middle_names.rb +0 -9
  292. data/test/migrations/missing/1_people_have_last_names.rb +0 -9
  293. data/test/migrations/missing/3_we_need_reminders.rb +0 -12
  294. data/test/migrations/missing/4_innocent_jointable.rb +0 -12
  295. data/test/migrations/valid/1_people_have_last_names.rb +0 -9
  296. data/test/migrations/valid/2_we_need_reminders.rb +0 -12
  297. data/test/migrations/valid/3_innocent_jointable.rb +0 -12
  298. data/test/models/author.rb +0 -151
  299. data/test/models/auto_id.rb +0 -4
  300. data/test/models/binary.rb +0 -2
  301. data/test/models/bird.rb +0 -9
  302. data/test/models/book.rb +0 -4
  303. data/test/models/categorization.rb +0 -5
  304. data/test/models/category.rb +0 -34
  305. data/test/models/citation.rb +0 -6
  306. data/test/models/club.rb +0 -13
  307. data/test/models/column_name.rb +0 -3
  308. data/test/models/comment.rb +0 -29
  309. data/test/models/company.rb +0 -173
  310. data/test/models/company_in_module.rb +0 -78
  311. data/test/models/computer.rb +0 -3
  312. data/test/models/contact.rb +0 -16
  313. data/test/models/contract.rb +0 -5
  314. data/test/models/course.rb +0 -3
  315. data/test/models/customer.rb +0 -73
  316. data/test/models/default.rb +0 -2
  317. data/test/models/developer.rb +0 -101
  318. data/test/models/edge.rb +0 -5
  319. data/test/models/entrant.rb +0 -3
  320. data/test/models/essay.rb +0 -3
  321. data/test/models/event.rb +0 -3
  322. data/test/models/event_author.rb +0 -8
  323. data/test/models/face.rb +0 -7
  324. data/test/models/guid.rb +0 -2
  325. data/test/models/interest.rb +0 -5
  326. data/test/models/invoice.rb +0 -4
  327. data/test/models/item.rb +0 -7
  328. data/test/models/job.rb +0 -5
  329. data/test/models/joke.rb +0 -3
  330. data/test/models/keyboard.rb +0 -3
  331. data/test/models/legacy_thing.rb +0 -3
  332. data/test/models/line_item.rb +0 -3
  333. data/test/models/man.rb +0 -9
  334. data/test/models/matey.rb +0 -4
  335. data/test/models/member.rb +0 -12
  336. data/test/models/member_detail.rb +0 -5
  337. data/test/models/member_type.rb +0 -3
  338. data/test/models/membership.rb +0 -9
  339. data/test/models/minimalistic.rb +0 -2
  340. data/test/models/mixed_case_monkey.rb +0 -3
  341. data/test/models/movie.rb +0 -5
  342. data/test/models/order.rb +0 -4
  343. data/test/models/organization.rb +0 -6
  344. data/test/models/owner.rb +0 -5
  345. data/test/models/parrot.rb +0 -22
  346. data/test/models/person.rb +0 -16
  347. data/test/models/pet.rb +0 -5
  348. data/test/models/pirate.rb +0 -80
  349. data/test/models/polymorphic_design.rb +0 -3
  350. data/test/models/polymorphic_price.rb +0 -3
  351. data/test/models/post.rb +0 -102
  352. data/test/models/price_estimate.rb +0 -3
  353. data/test/models/project.rb +0 -30
  354. data/test/models/reader.rb +0 -4
  355. data/test/models/reference.rb +0 -4
  356. data/test/models/reply.rb +0 -46
  357. data/test/models/ship.rb +0 -19
  358. data/test/models/ship_part.rb +0 -7
  359. data/test/models/sponsor.rb +0 -4
  360. data/test/models/subject.rb +0 -4
  361. data/test/models/subscriber.rb +0 -8
  362. data/test/models/subscription.rb +0 -4
  363. data/test/models/tag.rb +0 -7
  364. data/test/models/tagging.rb +0 -10
  365. data/test/models/task.rb +0 -3
  366. data/test/models/tee.rb +0 -4
  367. data/test/models/tie.rb +0 -4
  368. data/test/models/topic.rb +0 -80
  369. data/test/models/toy.rb +0 -6
  370. data/test/models/treasure.rb +0 -8
  371. data/test/models/vertex.rb +0 -9
  372. data/test/models/warehouse_thing.rb +0 -5
  373. data/test/models/zine.rb +0 -3
  374. data/test/schema/mysql_specific_schema.rb +0 -31
  375. data/test/schema/postgresql_specific_schema.rb +0 -114
  376. data/test/schema/schema.rb +0 -550
  377. data/test/schema/schema2.rb +0 -6
  378. data/test/schema/sqlite_specific_schema.rb +0 -25
@@ -1,98 +1,15 @@
1
+ require 'active_support/core_ext/enumerable'
2
+
1
3
  module ActiveRecord
2
4
  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
5
+ extend ActiveSupport::Concern
6
+ include ActiveModel::AttributeMethods
16
7
 
17
- # Declare and check for suffixed attribute methods.
18
8
  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
49
- end
50
-
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)
55
- end
56
-
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
-
67
9
  # Generates all the attribute related methods for columns in the database
68
10
  # accessors, mutators and query methods.
69
11
  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
80
- end
81
-
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
91
-
92
- unless instance_method_already_implemented?("#{name}?")
93
- define_question_method(name)
94
- end
95
- end
12
+ super(columns_hash.keys)
96
13
  end
97
14
 
98
15
  # Checks whether the method is defined in the model or any of its subclasses
@@ -100,304 +17,34 @@ module ActiveRecord
100
17
  # method is defined by Active Record though.
101
18
  def instance_method_already_implemented?(method_name)
102
19
  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
20
+ @_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 {|m| m.to_s }.to_set
21
+ @@_defined_activerecord_methods ||= (ActiveRecord::Base.public_instance_methods(false) | ActiveRecord::Base.private_instance_methods(false) | ActiveRecord::Base.protected_instance_methods(false)).map{|m| m.to_s }.to_set
106
22
  raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord" if @@_defined_activerecord_methods.include?(method_name)
107
23
  @_defined_class_methods.include?(method_name)
108
24
  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}
117
- end
118
-
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
124
- end
125
-
126
- # Returns +true+ if the provided attribute is being cached.
127
- def cache_attribute?(attr_name)
128
- cached_attributes.include?(attr_name)
129
- end
130
-
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
185
-
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}="
188
- end
189
-
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}="
216
- end
217
-
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
224
-
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
234
- end
235
- end
236
- end # ClassMethods
237
-
25
+ end
238
26
 
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
27
  def method_missing(method_id, *args, &block)
248
- method_name = method_id.to_s
249
-
250
- if self.class.private_method_defined?(method_name)
251
- raise NoMethodError.new("Attempt to call private method", method_name, args)
252
- end
253
-
254
28
  # If we haven't generated any methods yet, generate them, then
255
29
  # see if we've created the method we're looking for.
256
- if !self.class.generated_methods?
30
+ if !self.class.attribute_methods_generated?
257
31
  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)
269
- else
270
- super
271
- end
272
- elsif @attributes.include?(method_name)
273
- read_attribute(method_name)
32
+ method_name = method_id.to_s
33
+ guard_private_attribute_method!(method_name, args)
34
+ send(method_id, *args, &block)
274
35
  else
275
36
  super
276
37
  end
277
38
  end
278
39
 
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
295
- end
296
- end
297
-
298
- def read_attribute_before_type_cast(attr_name)
299
- @attributes[attr_name]
300
- end
301
-
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]
305
- end
306
-
307
- # Returns the unserialized object of the attribute.
308
- def unserialize_attribute(attr_name)
309
- unserialized_object = object_from_yaml(@attributes[attr_name])
310
-
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
317
- 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)
327
- else
328
- @attributes[attr_name] = value
329
- end
330
- end
331
-
332
-
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
351
- 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)
378
- end
40
+ def respond_to?(*args)
41
+ self.class.define_attribute_methods
379
42
  super
380
43
  end
381
44
 
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
392
-
393
- # Handle *= for method_missing.
394
- def attribute=(attribute_name, value)
395
- write_attribute(attribute_name, value)
396
- end
397
-
398
- # Handle *_before_type_cast for method_missing.
399
- def attribute_before_type_cast(attribute_name)
400
- read_attribute_before_type_cast(attribute_name)
45
+ protected
46
+ def attribute_method?(attr_name)
47
+ attr_name == 'id' || attributes.include?(attr_name)
401
48
  end
402
49
  end
403
50
  end
@@ -0,0 +1,33 @@
1
+ module ActiveRecord
2
+ module AttributeMethods
3
+ module BeforeTypeCast
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ attribute_method_suffix "_before_type_cast"
8
+ end
9
+
10
+ def read_attribute_before_type_cast(attr_name)
11
+ @attributes[attr_name]
12
+ end
13
+
14
+ # Returns a hash of attributes before typecasting and deserialization.
15
+ def attributes_before_type_cast
16
+ self.attribute_names.inject({}) do |attrs, name|
17
+ attrs[name] = read_attribute_before_type_cast(name)
18
+ attrs
19
+ end
20
+ end
21
+
22
+ private
23
+ # Handle *_before_type_cast for method_missing.
24
+ def attribute_before_type_cast(attribute_name)
25
+ if attribute_name == 'id'
26
+ read_attribute_before_type_cast(self.class.primary_key)
27
+ else
28
+ read_attribute_before_type_cast(attribute_name)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end