activerecord 1.0.0 → 2.0.0

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 (311) hide show
  1. data/CHANGELOG +4928 -3
  2. data/README +45 -46
  3. data/RUNNING_UNIT_TESTS +8 -11
  4. data/Rakefile +247 -0
  5. data/install.rb +8 -38
  6. data/lib/active_record/aggregations.rb +64 -49
  7. data/lib/active_record/associations/association_collection.rb +217 -47
  8. data/lib/active_record/associations/association_proxy.rb +159 -0
  9. data/lib/active_record/associations/belongs_to_association.rb +56 -0
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +155 -37
  12. data/lib/active_record/associations/has_many_association.rb +145 -75
  13. data/lib/active_record/associations/has_many_through_association.rb +283 -0
  14. data/lib/active_record/associations/has_one_association.rb +96 -0
  15. data/lib/active_record/associations.rb +1537 -304
  16. data/lib/active_record/attribute_methods.rb +328 -0
  17. data/lib/active_record/base.rb +2001 -588
  18. data/lib/active_record/calculations.rb +269 -0
  19. data/lib/active_record/callbacks.rb +169 -165
  20. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +308 -0
  21. data/lib/active_record/connection_adapters/abstract/database_statements.rb +171 -0
  22. data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
  23. data/lib/active_record/connection_adapters/abstract/quoting.rb +69 -0
  24. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +472 -0
  25. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +306 -0
  26. data/lib/active_record/connection_adapters/abstract_adapter.rb +125 -279
  27. data/lib/active_record/connection_adapters/mysql_adapter.rb +442 -77
  28. data/lib/active_record/connection_adapters/postgresql_adapter.rb +805 -135
  29. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  30. data/lib/active_record/connection_adapters/sqlite_adapter.rb +353 -69
  31. data/lib/active_record/fixtures.rb +946 -100
  32. data/lib/active_record/locking/optimistic.rb +144 -0
  33. data/lib/active_record/locking/pessimistic.rb +77 -0
  34. data/lib/active_record/migration.rb +417 -0
  35. data/lib/active_record/observer.rb +142 -32
  36. data/lib/active_record/query_cache.rb +23 -0
  37. data/lib/active_record/reflection.rb +163 -70
  38. data/lib/active_record/schema.rb +58 -0
  39. data/lib/active_record/schema_dumper.rb +171 -0
  40. data/lib/active_record/serialization.rb +98 -0
  41. data/lib/active_record/serializers/json_serializer.rb +71 -0
  42. data/lib/active_record/serializers/xml_serializer.rb +315 -0
  43. data/lib/active_record/timestamp.rb +41 -0
  44. data/lib/active_record/transactions.rb +87 -57
  45. data/lib/active_record/validations.rb +909 -122
  46. data/lib/active_record/vendor/db2.rb +362 -0
  47. data/lib/active_record/vendor/mysql.rb +126 -29
  48. data/lib/active_record/version.rb +9 -0
  49. data/lib/active_record.rb +35 -7
  50. data/lib/activerecord.rb +1 -0
  51. data/test/aaa_create_tables_test.rb +72 -0
  52. data/test/abstract_unit.rb +73 -5
  53. data/test/active_schema_test_mysql.rb +43 -0
  54. data/test/adapter_test.rb +105 -0
  55. data/test/adapter_test_sqlserver.rb +95 -0
  56. data/test/aggregations_test.rb +110 -16
  57. data/test/all.sh +2 -2
  58. data/test/ar_schema_test.rb +33 -0
  59. data/test/association_inheritance_reload.rb +14 -0
  60. data/test/associations/ar_joins_test.rb +0 -0
  61. data/test/associations/callbacks_test.rb +147 -0
  62. data/test/associations/cascaded_eager_loading_test.rb +110 -0
  63. data/test/associations/eager_singularization_test.rb +145 -0
  64. data/test/associations/eager_test.rb +442 -0
  65. data/test/associations/extension_test.rb +47 -0
  66. data/test/associations/inner_join_association_test.rb +88 -0
  67. data/test/associations/join_model_test.rb +553 -0
  68. data/test/associations_test.rb +1930 -267
  69. data/test/attribute_methods_test.rb +146 -0
  70. data/test/base_test.rb +1316 -84
  71. data/test/binary_test.rb +32 -0
  72. data/test/calculations_test.rb +251 -0
  73. data/test/callbacks_test.rb +400 -0
  74. data/test/class_inheritable_attributes_test.rb +3 -4
  75. data/test/column_alias_test.rb +17 -0
  76. data/test/connection_test_firebird.rb +8 -0
  77. data/test/connection_test_mysql.rb +30 -0
  78. data/test/connections/native_db2/connection.rb +25 -0
  79. data/test/connections/native_firebird/connection.rb +26 -0
  80. data/test/connections/native_frontbase/connection.rb +27 -0
  81. data/test/connections/native_mysql/connection.rb +21 -18
  82. data/test/connections/native_openbase/connection.rb +21 -0
  83. data/test/connections/native_oracle/connection.rb +27 -0
  84. data/test/connections/native_postgresql/connection.rb +17 -18
  85. data/test/connections/native_sqlite/connection.rb +17 -16
  86. data/test/connections/native_sqlite3/connection.rb +25 -0
  87. data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
  88. data/test/connections/native_sybase/connection.rb +23 -0
  89. data/test/copy_table_test_sqlite.rb +69 -0
  90. data/test/datatype_test_postgresql.rb +203 -0
  91. data/test/date_time_test.rb +37 -0
  92. data/test/default_test_firebird.rb +16 -0
  93. data/test/defaults_test.rb +67 -0
  94. data/test/deprecated_finder_test.rb +30 -0
  95. data/test/finder_test.rb +607 -32
  96. data/test/fixtures/accounts.yml +28 -0
  97. data/test/fixtures/all/developers.yml +0 -0
  98. data/test/fixtures/all/people.csv +0 -0
  99. data/test/fixtures/all/tasks.yml +0 -0
  100. data/test/fixtures/author.rb +107 -0
  101. data/test/fixtures/author_favorites.yml +4 -0
  102. data/test/fixtures/authors.yml +7 -0
  103. data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +1 -0
  104. data/test/fixtures/bad_fixtures/attr_with_spaces +1 -0
  105. data/test/fixtures/bad_fixtures/blank_line +3 -0
  106. data/test/fixtures/bad_fixtures/duplicate_attributes +3 -0
  107. data/test/fixtures/bad_fixtures/missing_value +1 -0
  108. data/test/fixtures/binaries.yml +132 -0
  109. data/test/fixtures/binary.rb +2 -0
  110. data/test/fixtures/book.rb +4 -0
  111. data/test/fixtures/books.yml +7 -0
  112. data/test/fixtures/categories/special_categories.yml +9 -0
  113. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  114. data/test/fixtures/categories.yml +14 -0
  115. data/test/fixtures/categories_ordered.yml +7 -0
  116. data/test/fixtures/categories_posts.yml +23 -0
  117. data/test/fixtures/categorization.rb +5 -0
  118. data/test/fixtures/categorizations.yml +17 -0
  119. data/test/fixtures/category.rb +26 -0
  120. data/test/fixtures/citation.rb +6 -0
  121. data/test/fixtures/comment.rb +23 -0
  122. data/test/fixtures/comments.yml +59 -0
  123. data/test/fixtures/companies.yml +55 -0
  124. data/test/fixtures/company.rb +81 -4
  125. data/test/fixtures/company_in_module.rb +32 -6
  126. data/test/fixtures/computer.rb +4 -0
  127. data/test/fixtures/computers.yml +4 -0
  128. data/test/fixtures/contact.rb +16 -0
  129. data/test/fixtures/courses.yml +7 -0
  130. data/test/fixtures/customer.rb +28 -3
  131. data/test/fixtures/customers.yml +17 -0
  132. data/test/fixtures/db_definitions/db2.drop.sql +33 -0
  133. data/test/fixtures/db_definitions/db2.sql +235 -0
  134. data/test/fixtures/db_definitions/db22.drop.sql +2 -0
  135. data/test/fixtures/db_definitions/db22.sql +5 -0
  136. data/test/fixtures/db_definitions/firebird.drop.sql +65 -0
  137. data/test/fixtures/db_definitions/firebird.sql +310 -0
  138. data/test/fixtures/db_definitions/firebird2.drop.sql +2 -0
  139. data/test/fixtures/db_definitions/firebird2.sql +6 -0
  140. data/test/fixtures/db_definitions/frontbase.drop.sql +33 -0
  141. data/test/fixtures/db_definitions/frontbase.sql +273 -0
  142. data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
  143. data/test/fixtures/db_definitions/frontbase2.sql +4 -0
  144. data/test/fixtures/db_definitions/openbase.drop.sql +2 -0
  145. data/test/fixtures/db_definitions/openbase.sql +318 -0
  146. data/test/fixtures/db_definitions/openbase2.drop.sql +2 -0
  147. data/test/fixtures/db_definitions/openbase2.sql +7 -0
  148. data/test/fixtures/db_definitions/oracle.drop.sql +67 -0
  149. data/test/fixtures/db_definitions/oracle.sql +330 -0
  150. data/test/fixtures/db_definitions/oracle2.drop.sql +2 -0
  151. data/test/fixtures/db_definitions/oracle2.sql +6 -0
  152. data/test/fixtures/db_definitions/postgresql.drop.sql +44 -0
  153. data/test/fixtures/db_definitions/postgresql.sql +217 -38
  154. data/test/fixtures/db_definitions/postgresql2.drop.sql +2 -0
  155. data/test/fixtures/db_definitions/postgresql2.sql +2 -2
  156. data/test/fixtures/db_definitions/schema.rb +354 -0
  157. data/test/fixtures/db_definitions/schema2.rb +11 -0
  158. data/test/fixtures/db_definitions/sqlite.drop.sql +33 -0
  159. data/test/fixtures/db_definitions/sqlite.sql +139 -5
  160. data/test/fixtures/db_definitions/sqlite2.drop.sql +2 -0
  161. data/test/fixtures/db_definitions/sqlite2.sql +1 -0
  162. data/test/fixtures/db_definitions/sybase.drop.sql +35 -0
  163. data/test/fixtures/db_definitions/sybase.sql +222 -0
  164. data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
  165. data/test/fixtures/db_definitions/sybase2.sql +5 -0
  166. data/test/fixtures/developer.rb +70 -6
  167. data/test/fixtures/developers.yml +21 -0
  168. data/test/fixtures/developers_projects/david_action_controller +2 -1
  169. data/test/fixtures/developers_projects/david_active_record +2 -1
  170. data/test/fixtures/developers_projects.yml +17 -0
  171. data/test/fixtures/edge.rb +5 -0
  172. data/test/fixtures/edges.yml +6 -0
  173. data/test/fixtures/entrants.yml +14 -0
  174. data/test/fixtures/example.log +1 -0
  175. data/test/fixtures/fk_test_has_fk.yml +3 -0
  176. data/test/fixtures/fk_test_has_pk.yml +2 -0
  177. data/test/fixtures/flowers.jpg +0 -0
  178. data/test/fixtures/funny_jokes.yml +10 -0
  179. data/test/fixtures/item.rb +7 -0
  180. data/test/fixtures/items.yml +4 -0
  181. data/test/fixtures/joke.rb +3 -0
  182. data/test/fixtures/keyboard.rb +3 -0
  183. data/test/fixtures/legacy_thing.rb +3 -0
  184. data/test/fixtures/legacy_things.yml +3 -0
  185. data/test/fixtures/matey.rb +4 -0
  186. data/test/fixtures/mateys.yml +4 -0
  187. data/test/fixtures/migrations/1_people_have_last_names.rb +9 -0
  188. data/test/fixtures/migrations/2_we_need_reminders.rb +12 -0
  189. data/test/fixtures/migrations/3_innocent_jointable.rb +12 -0
  190. data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
  191. data/test/fixtures/migrations_with_duplicate/1_people_have_last_names.rb +9 -0
  192. data/test/fixtures/migrations_with_duplicate/2_we_need_reminders.rb +12 -0
  193. data/test/fixtures/migrations_with_duplicate/3_foo.rb +7 -0
  194. data/test/fixtures/migrations_with_duplicate/3_innocent_jointable.rb +12 -0
  195. data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
  196. data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
  197. data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
  198. data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
  199. data/test/fixtures/minimalistic.rb +2 -0
  200. data/test/fixtures/minimalistics.yml +2 -0
  201. data/test/fixtures/mixed_case_monkey.rb +3 -0
  202. data/test/fixtures/mixed_case_monkeys.yml +6 -0
  203. data/test/fixtures/mixins.yml +29 -0
  204. data/test/fixtures/movies.yml +7 -0
  205. data/test/fixtures/naked/csv/accounts.csv +1 -0
  206. data/test/fixtures/naked/yml/accounts.yml +1 -0
  207. data/test/fixtures/naked/yml/companies.yml +1 -0
  208. data/test/fixtures/naked/yml/courses.yml +1 -0
  209. data/test/fixtures/order.rb +4 -0
  210. data/test/fixtures/parrot.rb +13 -0
  211. data/test/fixtures/parrots.yml +27 -0
  212. data/test/fixtures/parrots_pirates.yml +7 -0
  213. data/test/fixtures/people.yml +3 -0
  214. data/test/fixtures/person.rb +4 -0
  215. data/test/fixtures/pirate.rb +5 -0
  216. data/test/fixtures/pirates.yml +9 -0
  217. data/test/fixtures/post.rb +59 -0
  218. data/test/fixtures/posts.yml +48 -0
  219. data/test/fixtures/project.rb +27 -2
  220. data/test/fixtures/projects.yml +7 -0
  221. data/test/fixtures/reader.rb +4 -0
  222. data/test/fixtures/readers.yml +4 -0
  223. data/test/fixtures/reply.rb +18 -2
  224. data/test/fixtures/reserved_words/distinct.yml +5 -0
  225. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  226. data/test/fixtures/reserved_words/group.yml +14 -0
  227. data/test/fixtures/reserved_words/select.yml +8 -0
  228. data/test/fixtures/reserved_words/values.yml +7 -0
  229. data/test/fixtures/ship.rb +3 -0
  230. data/test/fixtures/ships.yml +5 -0
  231. data/test/fixtures/subject.rb +4 -0
  232. data/test/fixtures/subscriber.rb +4 -3
  233. data/test/fixtures/tag.rb +7 -0
  234. data/test/fixtures/tagging.rb +10 -0
  235. data/test/fixtures/taggings.yml +25 -0
  236. data/test/fixtures/tags.yml +7 -0
  237. data/test/fixtures/task.rb +3 -0
  238. data/test/fixtures/tasks.yml +7 -0
  239. data/test/fixtures/topic.rb +20 -3
  240. data/test/fixtures/topics.yml +22 -0
  241. data/test/fixtures/treasure.rb +4 -0
  242. data/test/fixtures/treasures.yml +10 -0
  243. data/test/fixtures/vertex.rb +9 -0
  244. data/test/fixtures/vertices.yml +4 -0
  245. data/test/fixtures_test.rb +574 -8
  246. data/test/inheritance_test.rb +113 -27
  247. data/test/json_serialization_test.rb +180 -0
  248. data/test/lifecycle_test.rb +56 -29
  249. data/test/locking_test.rb +273 -0
  250. data/test/method_scoping_test.rb +416 -0
  251. data/test/migration_test.rb +933 -0
  252. data/test/migration_test_firebird.rb +124 -0
  253. data/test/mixin_test.rb +95 -0
  254. data/test/modules_test.rb +23 -10
  255. data/test/multiple_db_test.rb +17 -3
  256. data/test/pk_test.rb +59 -15
  257. data/test/query_cache_test.rb +104 -0
  258. data/test/readonly_test.rb +107 -0
  259. data/test/reflection_test.rb +124 -27
  260. data/test/reserved_word_test_mysql.rb +177 -0
  261. data/test/schema_authorization_test_postgresql.rb +75 -0
  262. data/test/schema_dumper_test.rb +131 -0
  263. data/test/schema_test_postgresql.rb +64 -0
  264. data/test/serialization_test.rb +47 -0
  265. data/test/synonym_test_oracle.rb +17 -0
  266. data/test/table_name_test_sqlserver.rb +23 -0
  267. data/test/threaded_connections_test.rb +48 -0
  268. data/test/transactions_test.rb +227 -29
  269. data/test/unconnected_test.rb +14 -6
  270. data/test/validations_test.rb +1293 -32
  271. data/test/xml_serialization_test.rb +202 -0
  272. metadata +347 -143
  273. data/dev-utils/eval_debugger.rb +0 -9
  274. data/examples/associations.rb +0 -87
  275. data/examples/shared_setup.rb +0 -15
  276. data/examples/validation.rb +0 -88
  277. data/lib/active_record/deprecated_associations.rb +0 -70
  278. data/lib/active_record/support/class_attribute_accessors.rb +0 -43
  279. data/lib/active_record/support/class_inheritable_attributes.rb +0 -37
  280. data/lib/active_record/support/clean_logger.rb +0 -10
  281. data/lib/active_record/support/inflector.rb +0 -70
  282. data/lib/active_record/vendor/simple.rb +0 -702
  283. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  284. data/lib/active_record/wrappings.rb +0 -59
  285. data/rakefile +0 -122
  286. data/test/deprecated_associations_test.rb +0 -336
  287. data/test/fixtures/accounts/signals37 +0 -3
  288. data/test/fixtures/accounts/unknown +0 -2
  289. data/test/fixtures/companies/first_client +0 -6
  290. data/test/fixtures/companies/first_firm +0 -4
  291. data/test/fixtures/companies/second_client +0 -6
  292. data/test/fixtures/courses/java +0 -2
  293. data/test/fixtures/courses/ruby +0 -2
  294. data/test/fixtures/customers/david +0 -6
  295. data/test/fixtures/db_definitions/mysql.sql +0 -96
  296. data/test/fixtures/db_definitions/mysql2.sql +0 -4
  297. data/test/fixtures/developers/david +0 -2
  298. data/test/fixtures/developers/jamis +0 -2
  299. data/test/fixtures/entrants/first +0 -3
  300. data/test/fixtures/entrants/second +0 -3
  301. data/test/fixtures/entrants/third +0 -3
  302. data/test/fixtures/fixture_database.sqlite +0 -0
  303. data/test/fixtures/fixture_database_2.sqlite +0 -0
  304. data/test/fixtures/movies/first +0 -2
  305. data/test/fixtures/movies/second +0 -2
  306. data/test/fixtures/projects/action_controller +0 -2
  307. data/test/fixtures/projects/active_record +0 -2
  308. data/test/fixtures/topics/first +0 -9
  309. data/test/fixtures/topics/second +0 -8
  310. data/test/inflector_test.rb +0 -104
  311. data/test/thread_safety_test.rb +0 -33
@@ -1,32 +1,32 @@
1
1
  require 'observer'
2
2
 
3
3
  module ActiveRecord
4
- # Callbacks are hooks into the lifecycle of an Active Record object that allows you to trigger logic
5
- # before or after an alteration of the object state. This can be used to make sure that associated and
6
- # dependent objects are deleted when destroy is called (by overwriting before_destroy) or to massage attributes
7
- # before they're validated (by overwriting before_validation). As an example of the callbacks initiated, consider
8
- # the Base#save call:
9
- #
10
- # * (-) save
11
- # * (-) valid?
12
- # * (1) before_validation
13
- # * (2) before_validation_on_create
14
- # * (-) validate
15
- # * (-) validate_on_create
16
- # * (4) after_validation
17
- # * (5) after_validation_on_create
18
- # * (6) before_save
19
- # * (7) before_create
20
- # * (-) create
21
- # * (8) after_create
22
- # * (9) after_save
23
- #
24
- # That's a total of nine callbacks, which gives you immense power to react and prepare for each state in the
25
- # Active Record lifecyle.
4
+ # Callbacks are hooks into the lifecycle of an Active Record object that allow you to trigger logic
5
+ # before or after an alteration of the object state. This can be used to make sure that associated and
6
+ # dependent objects are deleted when destroy is called (by overwriting +before_destroy+) or to massage attributes
7
+ # before they're validated (by overwriting +before_validation+). As an example of the callbacks initiated, consider
8
+ # the <tt>Base#save</tt> call:
9
+ #
10
+ # * (-) <tt>save</tt>
11
+ # * (-) <tt>valid</tt>
12
+ # * (1) <tt>before_validation</tt>
13
+ # * (2) <tt>before_validation_on_create</tt>
14
+ # * (-) <tt>validate</tt>
15
+ # * (-) <tt>validate_on_create</tt>
16
+ # * (3) <tt>after_validation</tt>
17
+ # * (4) <tt>after_validation_on_create</tt>
18
+ # * (5) <tt>before_save</tt>
19
+ # * (6) <tt>before_create</tt>
20
+ # * (-) <tt>create</tt>
21
+ # * (7) <tt>after_create</tt>
22
+ # * (8) <tt>after_save</tt>
23
+ #
24
+ # That's a total of eight callbacks, which gives you immense power to react and prepare for each state in the
25
+ # Active Record lifecycle.
26
26
  #
27
27
  # Examples:
28
28
  # class CreditCard < ActiveRecord::Base
29
- # # Strip everything but digits, so the user can specify "555 234 34" or
29
+ # # Strip everything but digits, so the user can specify "555 234 34" or
30
30
  # # "5552-3434" or both will mean "55523434"
31
31
  # def before_validation_on_create
32
32
  # self.number = number.gsub(/[^0-9]/, "") if attribute_present?("number")
@@ -34,18 +34,19 @@ module ActiveRecord
34
34
  # end
35
35
  #
36
36
  # class Subscription < ActiveRecord::Base
37
- # # Automatically assign the signup date
38
- # def before_create
39
- # self.signed_up_on = Date.today
40
- # end
37
+ # before_create :record_signup
38
+ #
39
+ # private
40
+ # def record_signup
41
+ # self.signed_up_on = Date.today
42
+ # end
41
43
  # end
42
44
  #
43
45
  # class Firm < ActiveRecord::Base
44
46
  # # Destroys the associated clients and people when the firm is destroyed
45
- # def before_destroy
46
- # Client.destroy_all "client_of = #{id}"
47
- # Person.destroy_all "firm_id = #{id}"
48
- # end
47
+ # before_destroy { |record| Person.destroy_all "firm_id = #{record.id}" }
48
+ # before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
49
+ # end
49
50
  #
50
51
  # == Inheritable callback queues
51
52
  #
@@ -61,8 +62,8 @@ module ActiveRecord
61
62
  # before_destroy :destroy_readers
62
63
  # end
63
64
  #
64
- # Now, when Topic#destroy is run only +destroy_author+ is called. When Reply#destroy is run both +destroy_author+ and
65
- # +destroy_readers+ is called. Contrast this to the situation where we've implemented the save behavior through overwriteable
65
+ # Now, when <tt>Topic#destroy</tt> is run only +destroy_author+ is called. When <tt>Reply#destroy</tt> is run, both +destroy_author+ and
66
+ # +destroy_readers+ are called. Contrast this to the situation where we've implemented the save behavior through overwriteable
66
67
  # methods:
67
68
  #
68
69
  # class Topic < ActiveRecord::Base
@@ -73,15 +74,19 @@ module ActiveRecord
73
74
  # def before_destroy() destroy_readers end
74
75
  # end
75
76
  #
76
- # In that case, Reply#destroy would only run +destroy_readers+ and _not_ +destroy_author+. So use the callback macros when
77
- # you want to ensure that a certain callback is called for the entire hierarchy and the regular overwriteable methods when you
78
- # want to leave it up to each descendent to decide whether they want to call +super+ and trigger the inherited callbacks.
77
+ # In that case, <tt>Reply#destroy</tt> would only run +destroy_readers+ and _not_ +destroy_author+. So, use the callback macros when
78
+ # you want to ensure that a certain callback is called for the entire hierarchy, and use the regular overwriteable methods
79
+ # when you want to leave it up to each descendent to decide whether they want to call +super+ and trigger the inherited callbacks.
80
+ #
81
+ # *IMPORTANT:* In order for inheritance to work for the callback queues, you must specify the callbacks before specifying the
82
+ # associations. Otherwise, you might trigger the loading of a child before the parent has registered the callbacks and they won't
83
+ # be inherited.
79
84
  #
80
85
  # == Types of callbacks
81
86
  #
82
- # There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
87
+ # There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
83
88
  # inline methods (using a proc), and inline eval methods (using a string). Method references and callback objects are the
84
- # recommended approaches, inline methods using a proc is some times appropriate (such as for creating mix-ins), and inline
89
+ # recommended approaches, inline methods using a proc are sometimes appropriate (such as for creating mix-ins), and inline
85
90
  # eval methods are deprecated.
86
91
  #
87
92
  # The method reference callbacks work by specifying a protected or private method available in the object, like this:
@@ -115,8 +120,8 @@ module ActiveRecord
115
120
  # def after_save(record)
116
121
  # record.credit_card_number = decrypt(record.credit_card_number)
117
122
  # end
118
- #
119
- # alias_method :after_initialize, :after_save
123
+ #
124
+ # alias_method :after_find, :after_save
120
125
  #
121
126
  # private
122
127
  # def encrypt(value)
@@ -124,7 +129,7 @@ module ActiveRecord
124
129
  # end
125
130
  #
126
131
  # def decrypt(value)
127
- # # Secrecy is unvieled
132
+ # # Secrecy is unveiled
128
133
  # end
129
134
  # end
130
135
  #
@@ -138,200 +143,199 @@ module ActiveRecord
138
143
  # before_destroy 'self.class.delete_all "parent_id = #{id}"'
139
144
  # end
140
145
  #
141
- # Notice that single plings (') are used so the #{id} part isn't evaluated until the callback is triggered. Also note that these
146
+ # Notice that single quotes (') are used so the <tt>#{id}</tt> part isn't evaluated until the callback is triggered. Also note that these
142
147
  # inline callbacks can be stacked just like the regular ones:
143
148
  #
144
149
  # class Topic < ActiveRecord::Base
145
- # before_destroy 'self.class.delete_all "parent_id = #{id}"',
150
+ # before_destroy 'self.class.delete_all "parent_id = #{id}"',
146
151
  # 'puts "Evaluated after parents are destroyed"'
147
152
  # end
148
153
  #
149
- # == The after_find and after_initialize exceptions
154
+ # == The +after_find+ and +after_initialize+ exceptions
150
155
  #
151
- # Because after_find and after_initialize is called for each object instantiated found by a finder, such as Base.find_all, we've had
152
- # to implement a simple performance constraint (50% more speed on a simple test case). Unlike all the other callbacks, after_find and
153
- # after_initialize can only be declared using an explicit implementation. So using the inheritable callback queue for after_find and
154
- # after_initialize won't work.
156
+ # Because +after_find+ and +after_initialize+ are called for each object found and instantiated by a finder, such as <tt>Base.find(:all)</tt>, we've had
157
+ # to implement a simple performance constraint (50% more speed on a simple test case). Unlike all the other callbacks, +after_find+ and
158
+ # +after_initialize+ will only be run if an explicit implementation is defined (<tt>def after_find</tt>). In that case, all of the
159
+ # callback types will be called.
160
+ #
161
+ # == <tt>before_validation*</tt> returning statements
162
+ #
163
+ # If the returning value of a +before_validation+ callback can be evaluated to +false+, the process will be aborted and <tt>Base#save</tt> will return +false+.
164
+ # If <tt>Base#save!</tt> is called it will raise a +RecordNotSaved+ exception.
165
+ # Nothing will be appended to the errors object.
166
+ #
167
+ # == Canceling callbacks
168
+ #
169
+ # If a <tt>before_*</tt> callback returns +false+, all the later callbacks and the associated action are cancelled. If an <tt>after_*</tt> callback returns
170
+ # +false+, all the later callbacks are cancelled. Callbacks are generally run in the order they are defined, with the exception of callbacks
171
+ # defined as methods on the model, which are called last.
155
172
  module Callbacks
156
- CALLBACKS = %w(
157
- after_find after_initialize before_save after_save before_create after_create before_update after_update before_validation
173
+ CALLBACKS = %w(
174
+ after_find after_initialize before_save after_save before_create after_create before_update after_update before_validation
158
175
  after_validation before_validation_on_create after_validation_on_create before_validation_on_update
159
176
  after_validation_on_update before_destroy after_destroy
160
177
  )
161
178
 
162
- def self.append_features(base) #:nodoc:
163
- super
179
+ def self.included(base) #:nodoc:
180
+ base.extend Observable
164
181
 
165
- base.extend(ClassMethods)
166
- base.class_eval do
167
- class << self
168
- include Observable
169
- alias_method :instantiate_without_callbacks, :instantiate
170
- alias_method :instantiate, :instantiate_with_callbacks
171
- end
182
+ [:create_or_update, :valid?, :create, :update, :destroy].each do |method|
183
+ base.send :alias_method_chain, method, :callbacks
172
184
  end
173
185
 
174
- base.class_eval do
175
- alias_method :initialize_without_callbacks, :initialize
176
- alias_method :initialize, :initialize_with_callbacks
177
-
178
- alias_method :create_or_update_without_callbacks, :create_or_update
179
- alias_method :create_or_update, :create_or_update_with_callbacks
180
-
181
- alias_method :valid_without_callbacks, :valid?
182
- alias_method :valid?, :valid_with_callbacks
183
-
184
- alias_method :create_without_callbacks, :create
185
- alias_method :create, :create_with_callbacks
186
-
187
- alias_method :update_without_callbacks, :update
188
- alias_method :update, :update_with_callbacks
189
-
190
- alias_method :destroy_without_callbacks, :destroy
191
- alias_method :destroy, :destroy_with_callbacks
186
+ CALLBACKS.each do |method|
187
+ base.class_eval <<-"end_eval"
188
+ def self.#{method}(*callbacks, &block)
189
+ callbacks << block if block_given?
190
+ write_inheritable_array(#{method.to_sym.inspect}, callbacks)
191
+ end
192
+ end_eval
192
193
  end
193
-
194
- CALLBACKS.each { |cb| base.class_eval("def self.#{cb}(*methods) write_inheritable_array(\"#{cb}\", methods) end") }
195
194
  end
196
195
 
197
- module ClassMethods #:nodoc:
198
- def instantiate_with_callbacks(record)
199
- object = instantiate_without_callbacks(record)
200
- object.callback(:after_find) if object.respond_to_without_attributes?(:after_find)
201
- object.callback(:after_initialize) if object.respond_to_without_attributes?(:after_initialize)
202
- object
203
- end
204
- end
196
+ # Is called when the object was instantiated by one of the finders, like <tt>Base.find</tt>.
197
+ #def after_find() end
205
198
 
206
- # Is called when the object was instantiated by one of the finders, like Base.find.
207
- # def after_find() end
199
+ # Is called after the object has been instantiated by a call to <tt>Base.new</tt>.
200
+ #def after_initialize() end
208
201
 
209
- # Is called after the object has been instantiated by a call to Base.new.
210
- # def after_initialize() end
211
- def initialize_with_callbacks(attributes = nil) #:nodoc:
212
- initialize_without_callbacks(attributes)
213
- yield self if block_given?
214
- after_initialize if respond_to_without_attributes?(:after_initialize)
215
- end
216
-
217
- # Is called _before_ Base.save (regardless of whether it's a create or update save).
202
+ # Is called _before_ <tt>Base.save</tt> (regardless of whether it's a +create+ or +update+ save).
218
203
  def before_save() end
219
204
 
220
- # Is called _after_ Base.save (regardless of whether it's a create or update save).
205
+ # Is called _after_ <tt>Base.save</tt> (regardless of whether it's a +create+ or +update+ save).
206
+ #
207
+ # class Contact < ActiveRecord::Base
208
+ # after_save { logger.info( 'New contact saved!' ) }
209
+ # end
221
210
  def after_save() end
222
211
  def create_or_update_with_callbacks #:nodoc:
223
- callback(:before_save)
224
- create_or_update_without_callbacks
212
+ return false if callback(:before_save) == false
213
+ result = create_or_update_without_callbacks
225
214
  callback(:after_save)
215
+ result
226
216
  end
217
+ private :create_or_update_with_callbacks
227
218
 
228
- # Is called _before_ Base.save on new objects that haven't been saved yet (no record exists).
219
+ # Is called _before_ <tt>Base.save</tt> on new objects that haven't been saved yet (no record exists).
229
220
  def before_create() end
230
221
 
231
- # Is called _after_ Base.save on new objects that haven't been saved yet (no record exists).
222
+ # Is called _after_ <tt>Base.save</tt> on new objects that haven't been saved yet (no record exists).
232
223
  def after_create() end
233
224
  def create_with_callbacks #:nodoc:
234
- callback(:before_create)
235
- create_without_callbacks
225
+ return false if callback(:before_create) == false
226
+ result = create_without_callbacks
236
227
  callback(:after_create)
228
+ result
237
229
  end
230
+ private :create_with_callbacks
238
231
 
239
- # Is called _before_ Base.save on existing objects that has a record.
232
+ # Is called _before_ <tt>Base.save</tt> on existing objects that have a record.
240
233
  def before_update() end
241
234
 
242
- # Is called _after_ Base.save on existing objects that has a record.
235
+ # Is called _after_ <tt>Base.save</tt> on existing objects that have a record.
243
236
  def after_update() end
244
237
 
245
238
  def update_with_callbacks #:nodoc:
246
- callback(:before_update)
247
- update_without_callbacks
239
+ return false if callback(:before_update) == false
240
+ result = update_without_callbacks
248
241
  callback(:after_update)
242
+ result
249
243
  end
244
+ private :update_with_callbacks
250
245
 
251
- # Is called _before_ Validations.validate (which is part of the Base.save call).
246
+ # Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call).
252
247
  def before_validation() end
253
248
 
254
- # Is called _after_ Validations.validate (which is part of the Base.save call).
249
+ # Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call).
255
250
  def after_validation() end
256
251
 
257
- # Is called _before_ Validations.validate (which is part of the Base.save call) on new objects
252
+ # Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on new objects
258
253
  # that haven't been saved yet (no record exists).
259
254
  def before_validation_on_create() end
260
255
 
261
- # Is called _after_ Validations.validate (which is part of the Base.save call) on new objects
256
+ # Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on new objects
262
257
  # that haven't been saved yet (no record exists).
263
258
  def after_validation_on_create() end
264
259
 
265
- # Is called _before_ Validations.validate (which is part of the Base.save call) on
266
- # existing objects that has a record.
260
+ # Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on
261
+ # existing objects that have a record.
267
262
  def before_validation_on_update() end
268
263
 
269
- # Is called _after_ Validations.validate (which is part of the Base.save call) on
270
- # existing objects that has a record.
264
+ # Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on
265
+ # existing objects that have a record.
271
266
  def after_validation_on_update() end
272
267
 
273
- def valid_with_callbacks #:nodoc:
274
- callback(:before_validation)
275
- if new_record? then callback(:before_validation_on_create) else callback(:before_validation_on_update) end
268
+ def valid_with_callbacks? #:nodoc:
269
+ return false if callback(:before_validation) == false
270
+ if new_record? then result = callback(:before_validation_on_create) else result = callback(:before_validation_on_update) end
271
+ return false if result == false
276
272
 
277
- result = valid_without_callbacks
273
+ result = valid_without_callbacks?
278
274
 
279
275
  callback(:after_validation)
280
276
  if new_record? then callback(:after_validation_on_create) else callback(:after_validation_on_update) end
281
-
277
+
282
278
  return result
283
279
  end
284
280
 
285
- # Is called _before_ Base.destroy.
281
+ # Is called _before_ <tt>Base.destroy</tt>.
282
+ #
283
+ # Note: If you need to _destroy_ or _nullify_ associated records first,
284
+ # use the <tt>:dependent</tt> option on your associations.
286
285
  def before_destroy() end
287
286
 
288
- # Is called _after_ Base.destroy (and all the attributes have been frozen).
287
+ # Is called _after_ <tt>Base.destroy</tt> (and all the attributes have been frozen).
288
+ #
289
+ # class Contact < ActiveRecord::Base
290
+ # after_destroy { |record| logger.info( "Contact #{record.id} was destroyed." ) }
291
+ # end
289
292
  def after_destroy() end
290
293
  def destroy_with_callbacks #:nodoc:
291
- callback(:before_destroy)
292
- destroy_without_callbacks
294
+ return false if callback(:before_destroy) == false
295
+ result = destroy_without_callbacks
293
296
  callback(:after_destroy)
297
+ result
294
298
  end
295
299
 
296
- def callback(callback_method) #:nodoc:
297
- run_callbacks(callback_method)
298
- send(callback_method)
299
- notify(callback_method)
300
- end
301
-
302
- def run_callbacks(callback_method)
303
- filters = self.class.read_inheritable_attribute(callback_method.to_s)
304
- if filters.nil? then return end
305
- filters.each do |filter|
306
- if Symbol === filter
307
- self.send(filter)
308
- elsif String === filter
309
- eval(filter, binding)
310
- elsif filter_block?(filter)
311
- filter.call(self)
312
- elsif filter_class?(filter, callback_method)
313
- filter.send(callback_method, self)
314
- else
315
- raise(
316
- ActiveRecordError,
317
- "Filters need to be either a symbol, string (to be eval'ed), proc/method, or " +
318
- "class implementing a static filter method"
319
- )
300
+ private
301
+ def callback(method)
302
+ notify(method)
303
+
304
+ callbacks_for(method).each do |callback|
305
+ result = case callback
306
+ when Symbol
307
+ self.send(callback)
308
+ when String
309
+ eval(callback, binding)
310
+ when Proc, Method
311
+ callback.call(self)
312
+ else
313
+ if callback.respond_to?(method)
314
+ callback.send(method, self)
315
+ else
316
+ raise ActiveRecordError, "Callbacks must be a symbol denoting the method to call, a string to be evaluated, a block to be invoked, or an object responding to the callback method."
317
+ end
318
+ end
319
+ return false if result == false
320
320
  end
321
+
322
+ result = send(method) if respond_to_without_attributes?(method)
323
+
324
+ return result
325
+ end
326
+
327
+ def callbacks_for(method)
328
+ self.class.read_inheritable_attribute(method.to_sym) or []
329
+ end
330
+
331
+ def invoke_and_notify(method)
332
+ notify(method)
333
+ send(method) if respond_to_without_attributes?(method)
334
+ end
335
+
336
+ def notify(method) #:nodoc:
337
+ self.class.changed
338
+ self.class.notify_observers(method, self)
321
339
  end
322
- end
323
-
324
- def filter_block?(filter)
325
- filter.respond_to?("call") && (filter.arity == 1 || filter.arity == -1)
326
- end
327
-
328
- def filter_class?(filter, callback_method)
329
- filter.respond_to?(callback_method)
330
- end
331
-
332
- def notify(callback_method) #:nodoc:
333
- self.class.changed
334
- self.class.notify_observers(callback_method, self)
335
- end
336
340
  end
337
- end
341
+ end