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,34 +1,48 @@
1
+ require 'active_support/core_ext/array'
2
+ require 'active_support/core_ext/hash/except'
3
+ require 'active_support/core_ext/object/metaclass'
4
+
1
5
  module ActiveRecord
2
6
  module NamedScope
3
- # All subclasses of ActiveRecord::Base have one named scope:
4
- # * <tt>scoped</tt> - which allows for the creation of anonymous \scopes, on the fly: <tt>Shirt.scoped(:conditions => {:color => 'red'}).scoped(:include => :washing_instructions)</tt>
5
- #
6
- # These anonymous \scopes tend to be useful when procedurally generating complex queries, where passing
7
- # intermediate values (scopes) around as first-class objects is convenient.
8
- #
9
- # You can define a scope that applies to all finders using ActiveRecord::Base.default_scope.
10
- def self.included(base)
11
- base.extend ClassMethods
12
- end
7
+ extend ActiveSupport::Concern
13
8
 
14
9
  module ClassMethods
15
- def scopes
16
- read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
10
+ # Returns a relation if invoked without any arguments.
11
+ #
12
+ # posts = Post.scoped
13
+ # posts.size # Fires "select count(*) from posts" and returns the count
14
+ # posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects
15
+ #
16
+ # Returns an anonymous named scope if any options are supplied.
17
+ #
18
+ # shirts = Shirt.scoped(:conditions => {:color => 'red'})
19
+ # shirts = shirts.scoped(:include => :washing_instructions)
20
+ #
21
+ # Anonymous \scopes tend to be useful when procedurally generating complex queries, where passing
22
+ # intermediate values (scopes) around as first-class objects is convenient.
23
+ #
24
+ # You can define a scope that applies to all finders using ActiveRecord::Base.default_scope.
25
+ def scoped(options = {}, &block)
26
+ if options.present?
27
+ Scope.init(self, options, &block)
28
+ else
29
+ current_scoped_methods ? unscoped.merge(current_scoped_methods) : unscoped.spawn
30
+ end
17
31
  end
18
32
 
19
- def scoped(scope, &block)
20
- Scope.new(self, scope, &block)
33
+ def scopes
34
+ read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
21
35
  end
22
36
 
23
37
  # Adds a class method for retrieving and querying objects. A scope represents a narrowing of a database query,
24
38
  # such as <tt>:conditions => {:color => :red}, :select => 'shirts.*', :include => :washing_instructions</tt>.
25
39
  #
26
40
  # class Shirt < ActiveRecord::Base
27
- # named_scope :red, :conditions => {:color => 'red'}
28
- # named_scope :dry_clean_only, :joins => :washing_instructions, :conditions => ['washing_instructions.dry_clean_only = ?', true]
41
+ # scope :red, :conditions => {:color => 'red'}
42
+ # scope :dry_clean_only, :joins => :washing_instructions, :conditions => ['washing_instructions.dry_clean_only = ?', true]
29
43
  # end
30
- #
31
- # The above calls to <tt>named_scope</tt> define class methods Shirt.red and Shirt.dry_clean_only. Shirt.red,
44
+ #
45
+ # The above calls to <tt>scope</tt> define class methods Shirt.red and Shirt.dry_clean_only. Shirt.red,
32
46
  # in effect, represents the query <tt>Shirt.find(:all, :conditions => {:color => 'red'})</tt>.
33
47
  #
34
48
  # Unlike <tt>Shirt.find(...)</tt>, however, the object returned by Shirt.red is not an Array; it resembles the association object
@@ -54,7 +68,7 @@ module ActiveRecord
54
68
  # Named \scopes can also be procedural:
55
69
  #
56
70
  # class Shirt < ActiveRecord::Base
57
- # named_scope :colored, lambda { |color|
71
+ # scope :colored, lambda { |color|
58
72
  # { :conditions => { :color => color } }
59
73
  # }
60
74
  # end
@@ -64,7 +78,7 @@ module ActiveRecord
64
78
  # Named \scopes can also have extensions, just as with <tt>has_many</tt> declarations:
65
79
  #
66
80
  # class Shirt < ActiveRecord::Base
67
- # named_scope :red, :conditions => {:color => 'red'} do
81
+ # scope :red, :conditions => {:color => 'red'} do
68
82
  # def dom_id
69
83
  # 'red_shirts'
70
84
  # end
@@ -76,122 +90,101 @@ module ActiveRecord
76
90
  # <tt>proxy_options</tt> method on the proxy itself.
77
91
  #
78
92
  # class Shirt < ActiveRecord::Base
79
- # named_scope :colored, lambda { |color|
93
+ # scope :colored, lambda { |color|
80
94
  # { :conditions => { :color => color } }
81
95
  # }
82
96
  # end
83
97
  #
84
98
  # expected_options = { :conditions => { :colored => 'red' } }
85
99
  # assert_equal expected_options, Shirt.colored('red').proxy_options
86
- def named_scope(name, options = {}, &block)
100
+ def scope(name, options = {}, &block)
87
101
  name = name.to_sym
88
102
 
103
+ if !scopes[name] && respond_to?(name, true)
104
+ raise ArgumentError, "Cannot define scope :#{name} because #{self.name}.#{name} method already exists."
105
+ end
106
+
89
107
  scopes[name] = lambda do |parent_scope, *args|
90
- Scope.new(parent_scope, case options
91
- when Hash
108
+ Scope.init(parent_scope, case options
109
+ when Hash, Relation
92
110
  options
93
111
  when Proc
94
- if self.model_name != parent_scope.model_name
95
- options.bind(parent_scope).call(*args)
96
- else
97
- options.call(*args)
98
- end
112
+ options.call(*args)
99
113
  end, &block)
100
114
  end
101
-
102
- singleton_class.send :define_method, name do |*args|
103
- scopes[name].call(self, *args)
115
+ metaclass.instance_eval do
116
+ define_method name do |*args|
117
+ scopes[name].call(self, *args)
118
+ end
104
119
  end
105
120
  end
106
- end
107
121
 
108
- class Scope
109
- attr_reader :proxy_scope, :proxy_options, :current_scoped_methods_when_defined
110
- NON_DELEGATE_METHODS = %w(nil? send object_id class extend find size count sum average maximum minimum paginate first last empty? any? respond_to?).to_set
111
- [].methods.each do |m|
112
- unless m =~ /^__/ || NON_DELEGATE_METHODS.include?(m.to_s)
113
- delegate m, :to => :proxy_found
114
- end
122
+ def named_scope(*args, &block)
123
+ ActiveSupport::Deprecation.warn("Base.named_scope has been deprecated, please use Base.scope instead", caller)
124
+ scope(*args, &block)
115
125
  end
126
+ end
116
127
 
117
- delegate :scopes, :with_scope, :scoped_methods, :to => :proxy_scope
128
+ class Scope < Relation
129
+ attr_accessor :current_scoped_methods_when_defined
118
130
 
119
- def initialize(proxy_scope, options, &block)
120
- options ||= {}
121
- [options[:extend]].flatten.each { |extension| extend extension } if options[:extend]
122
- extend Module.new(&block) if block_given?
123
- unless (Scope === proxy_scope || ActiveRecord::Associations::AssociationCollection === proxy_scope)
124
- @current_scoped_methods_when_defined = proxy_scope.send(:current_scoped_methods)
125
- end
126
- @proxy_scope, @proxy_options = proxy_scope, options.except(:extend)
127
- end
131
+ delegate :scopes, :with_scope, :with_exclusive_scope, :scoped_methods, :scoped, :to => :klass
128
132
 
129
- def reload
130
- load_found; self
131
- end
133
+ def self.init(klass, options, &block)
134
+ relation = new(klass, klass.arel_table)
132
135
 
133
- def first(*args)
134
- if args.first.kind_of?(Integer) || (@found && !args.first.kind_of?(Hash))
135
- proxy_found.first(*args)
136
+ scope = if options.is_a?(Hash)
137
+ klass.scoped.apply_finder_options(options.except(:extend))
136
138
  else
137
- find(:first, *args)
139
+ options ? klass.scoped.merge(options) : klass.scoped
138
140
  end
139
- end
140
141
 
141
- def last(*args)
142
- if args.first.kind_of?(Integer) || (@found && !args.first.kind_of?(Hash))
143
- proxy_found.last(*args)
144
- else
145
- find(:last, *args)
146
- end
147
- end
142
+ relation = relation.merge(scope)
148
143
 
149
- def size
150
- @found ? @found.length : count
151
- end
144
+ Array.wrap(options[:extend]).each {|extension| relation.send(:extend, extension) } if options.is_a?(Hash)
145
+ relation.send(:extend, Module.new(&block)) if block_given?
152
146
 
153
- def empty?
154
- @found ? @found.empty? : count.zero?
147
+ relation.current_scoped_methods_when_defined = klass.send(:current_scoped_methods)
148
+ relation
155
149
  end
156
150
 
157
- def respond_to?(method, include_private = false)
158
- super || @proxy_scope.respond_to?(method, include_private)
151
+ def first(*args)
152
+ if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash))
153
+ to_a.first(*args)
154
+ else
155
+ args.first.present? ? apply_finder_options(args.first).first : super
156
+ end
159
157
  end
160
158
 
161
- def any?
162
- if block_given?
163
- proxy_found.any? { |*block_args| yield(*block_args) }
159
+ def last(*args)
160
+ if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash))
161
+ to_a.last(*args)
164
162
  else
165
- !empty?
163
+ args.first.present? ? apply_finder_options(args.first).last : super
166
164
  end
167
165
  end
168
166
 
169
- protected
170
- def proxy_found
171
- @found || load_found
167
+ def ==(other)
168
+ other.respond_to?(:to_ary) ? to_a == other.to_a : false
172
169
  end
173
170
 
174
171
  private
172
+
175
173
  def method_missing(method, *args, &block)
176
- if scopes.include?(method)
177
- scopes[method].call(self, *args)
178
- else
179
- with_scope({:find => proxy_options, :create => proxy_options[:conditions].is_a?(Hash) ? proxy_options[:conditions] : {}}, :reverse_merge) do
180
- method = :new if method == :build
181
- if current_scoped_methods_when_defined && !scoped_methods.include?(current_scoped_methods_when_defined)
182
- with_scope current_scoped_methods_when_defined do
183
- proxy_scope.send(method, *args, &block)
184
- end
174
+ if klass.respond_to?(method)
175
+ with_scope(self) do
176
+ if current_scoped_methods_when_defined && !scoped_methods.include?(current_scoped_methods_when_defined) && !scopes.include?(method)
177
+ with_scope(current_scoped_methods_when_defined) { klass.send(method, *args, &block) }
185
178
  else
186
- proxy_scope.send(method, *args, &block)
179
+ klass.send(method, *args, &block)
187
180
  end
188
181
  end
182
+ else
183
+ super
189
184
  end
190
185
  end
191
186
 
192
- def load_found
193
- @found = find(:all)
194
- end
195
187
  end
188
+
196
189
  end
197
190
  end
@@ -1,12 +1,16 @@
1
+ require 'active_support/core_ext/hash/except'
2
+ require 'active_support/core_ext/object/try'
3
+
1
4
  module ActiveRecord
2
5
  module NestedAttributes #:nodoc:
3
6
  class TooManyRecords < ActiveRecordError
4
7
  end
5
8
 
6
- def self.included(base)
7
- base.extend(ClassMethods)
8
- base.class_inheritable_accessor :nested_attributes_options, :instance_writer => false
9
- base.nested_attributes_options = {}
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ class_inheritable_accessor :nested_attributes_options, :instance_writer => false
13
+ self.nested_attributes_options = {}
10
14
  end
11
15
 
12
16
  # == Nested Attributes
@@ -45,14 +49,14 @@ module ActiveRecord
45
49
  # create the member and avatar in one go:
46
50
  #
47
51
  # params = { :member => { :name => 'Jack', :avatar_attributes => { :icon => 'smiling' } } }
48
- # member = Member.create(params)
52
+ # member = Member.create(params[:member])
49
53
  # member.avatar.id # => 2
50
54
  # member.avatar.icon # => 'smiling'
51
55
  #
52
56
  # It also allows you to update the avatar through the member:
53
57
  #
54
- # params = { :member' => { :avatar_attributes => { :id => '2', :icon => 'sad' } } }
55
- # member.update_attributes params['member']
58
+ # params = { :member => { :avatar_attributes => { :id => '2', :icon => 'sad' } } }
59
+ # member.update_attributes params[:member]
56
60
  # member.avatar.icon # => 'sad'
57
61
  #
58
62
  # By default you will only be able to set and update attributes on the
@@ -71,7 +75,7 @@ module ActiveRecord
71
75
  # member.avatar_attributes = { :id => '2', :_destroy => '1' }
72
76
  # member.avatar.marked_for_destruction? # => true
73
77
  # member.save
74
- # member.avatar #=> nil
78
+ # member.reload.avatar #=> nil
75
79
  #
76
80
  # Note that the model will _not_ be destroyed until the parent is saved.
77
81
  #
@@ -175,7 +179,7 @@ module ActiveRecord
175
179
  # member.posts.detect { |p| p.id == 2 }.marked_for_destruction? # => true
176
180
  # member.posts.length #=> 2
177
181
  # member.save
178
- # member.posts.length # => 1
182
+ # member.reload.posts.length # => 1
179
183
  #
180
184
  # === Saving
181
185
  #
@@ -239,11 +243,11 @@ module ActiveRecord
239
243
  # def pirate_attributes=(attributes)
240
244
  # assign_nested_attributes_for_one_to_one_association(:pirate, attributes)
241
245
  # end
242
- class_eval <<-EOS, __FILE__, __LINE__ + 1
246
+ class_eval %{
243
247
  def #{association_name}_attributes=(attributes)
244
248
  assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
245
249
  end
246
- EOS
250
+ }, __FILE__, __LINE__
247
251
  else
248
252
  raise ArgumentError, "No association found for name `#{association_name}'. Has it been defined yet?"
249
253
  end
@@ -315,7 +319,7 @@ module ActiveRecord
315
319
  # })
316
320
  #
317
321
  # Will update the name of the Person with ID 1, build a new associated
318
- # person with the name `John', and mark the associatied Person with ID 2
322
+ # person with the name `John', and mark the associated Person with ID 2
319
323
  # for destruction.
320
324
  #
321
325
  # Also accepts an Array of attribute hashes:
@@ -340,24 +344,14 @@ module ActiveRecord
340
344
  attributes_collection = attributes_collection.sort_by { |index, _| index.to_i }.map { |_, attributes| attributes }
341
345
  end
342
346
 
343
- association = send(association_name)
344
-
345
- existing_records = if association.loaded?
346
- association.to_a
347
- else
348
- attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact
349
- attribute_ids.present? ? association.all(:conditions => {association.primary_key => attribute_ids}) : []
350
- end
351
-
352
347
  attributes_collection.each do |attributes|
353
348
  attributes = attributes.with_indifferent_access
354
349
 
355
350
  if attributes['id'].blank?
356
351
  unless reject_new_record?(association_name, attributes)
357
- association.build(attributes.except(*UNASSIGNABLE_KEYS))
352
+ send(association_name).build(attributes.except(*UNASSIGNABLE_KEYS))
358
353
  end
359
- elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
360
- association.send(:add_record_to_target_with_callbacks, existing_record) unless association.loaded?
354
+ elsif existing_record = send(association_name).detect { |record| record.id.to_s == attributes['id'].to_s }
361
355
  assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
362
356
  else
363
357
  raise_nested_attributes_record_not_found(association_name, attributes['id'])
@@ -1,59 +1,6 @@
1
- require 'singleton'
2
- require 'set'
1
+ require 'active_support/core_ext/class/attribute'
3
2
 
4
3
  module ActiveRecord
5
- module Observing # :nodoc:
6
- def self.included(base)
7
- base.extend ClassMethods
8
- end
9
-
10
- module ClassMethods
11
- # Activates the observers assigned. Examples:
12
- #
13
- # # Calls PersonObserver.instance
14
- # ActiveRecord::Base.observers = :person_observer
15
- #
16
- # # Calls Cacher.instance and GarbageCollector.instance
17
- # ActiveRecord::Base.observers = :cacher, :garbage_collector
18
- #
19
- # # Same as above, just using explicit class references
20
- # ActiveRecord::Base.observers = Cacher, GarbageCollector
21
- #
22
- # Note: Setting this does not instantiate the observers yet. +instantiate_observers+ is
23
- # called during startup, and before each development request.
24
- def observers=(*observers)
25
- @observers = observers.flatten
26
- end
27
-
28
- # Gets the current observers.
29
- def observers
30
- @observers ||= []
31
- end
32
-
33
- # Instantiate the global Active Record observers.
34
- def instantiate_observers
35
- return if @observers.blank?
36
- @observers.each do |observer|
37
- if observer.respond_to?(:to_sym) # Symbol or String
38
- observer.to_s.camelize.constantize.instance
39
- elsif observer.respond_to?(:instance)
40
- observer.instance
41
- else
42
- raise ArgumentError, "#{observer} must be a lowercase, underscored class name (or an instance of the class itself) responding to the instance method. Example: Person.observers = :big_brother # calls BigBrother.instance"
43
- end
44
- end
45
- end
46
-
47
- protected
48
- # Notify observers when the observed class is subclassed.
49
- def inherited(subclass)
50
- super
51
- changed
52
- notify_observers :observed_class_inherited, subclass
53
- end
54
- end
55
- end
56
-
57
4
  # Observer classes respond to lifecycle callbacks to implement trigger-like
58
5
  # behavior outside the original class. This is a great way to reduce the
59
6
  # clutter that normally comes when the model class is burdened with
@@ -139,58 +86,35 @@ module ActiveRecord
139
86
  # load their observers by calling <tt>ModelObserver.instance</tt> before. Observers are
140
87
  # singletons and that call instantiates and registers them.
141
88
  #
142
- class Observer
143
- include Singleton
144
-
145
- class << self
146
- # Attaches the observer to the supplied model classes.
147
- def observe(*models)
148
- models.flatten!
149
- models.collect! { |model| model.is_a?(Symbol) ? model.to_s.camelize.constantize : model }
150
- define_method(:observed_classes) { Set.new(models) }
151
- end
152
-
153
- # The class observed by default is inferred from the observer's class name:
154
- # assert_equal Person, PersonObserver.observed_class
155
- def observed_class
156
- if observed_class_name = name[/(.*)Observer/, 1]
157
- observed_class_name.constantize
158
- else
159
- nil
160
- end
161
- end
162
- end
89
+ class Observer < ActiveModel::Observer
90
+ class_attribute :observed_methods
91
+ self.observed_methods = []
163
92
 
164
- # Start observing the declared classes and their subclasses.
165
93
  def initialize
166
- Set.new(observed_classes + observed_subclasses).each { |klass| add_observer! klass }
94
+ super
95
+ observed_subclasses.each { |klass| add_observer!(klass) }
167
96
  end
168
97
 
169
- # Send observed_method(object) if the method exists.
170
- def update(observed_method, object) #:nodoc:
171
- send(observed_method, object) if respond_to?(observed_method)
172
- end
173
-
174
- # Special method sent by the observed class when it is inherited.
175
- # Passes the new subclass.
176
- def observed_class_inherited(subclass) #:nodoc:
177
- self.class.observe(observed_classes + [subclass])
178
- add_observer!(subclass)
98
+ def self.method_added(method)
99
+ self.observed_methods += [method] if ActiveRecord::Callbacks::CALLBACKS.include?(method.to_sym)
179
100
  end
180
101
 
181
102
  protected
182
- def observed_classes
183
- Set.new([self.class.observed_class].compact.flatten)
184
- end
185
-
186
103
  def observed_subclasses
187
104
  observed_classes.sum([]) { |klass| klass.send(:subclasses) }
188
105
  end
189
106
 
190
107
  def add_observer!(klass)
191
- klass.add_observer(self)
192
- if respond_to?(:after_find) && !klass.method_defined?(:after_find)
193
- klass.class_eval 'def after_find() end'
108
+ super
109
+
110
+ # Check if a notifier callback was already added to the given class. If
111
+ # it was not, add it.
112
+ self.class.observed_methods.each do |method|
113
+ callback = :"_notify_observers_for_#{method}"
114
+ if (klass.instance_methods & [callback, callback.to_s]).empty?
115
+ klass.class_eval "def #{callback}; notify_observers(:#{method}); end"
116
+ klass.send(method, callback)
117
+ end
194
118
  end
195
119
  end
196
120
  end