activerecord_csi 2.3.5.p6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. data/CHANGELOG +5858 -0
  2. data/README +351 -0
  3. data/RUNNING_UNIT_TESTS +36 -0
  4. data/Rakefile +270 -0
  5. data/examples/associations.png +0 -0
  6. data/examples/performance.rb +162 -0
  7. data/install.rb +30 -0
  8. data/lib/active_record/aggregations.rb +261 -0
  9. data/lib/active_record/association_preload.rb +389 -0
  10. data/lib/active_record/associations/association_collection.rb +475 -0
  11. data/lib/active_record/associations/association_proxy.rb +278 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +76 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +53 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +143 -0
  15. data/lib/active_record/associations/has_many_association.rb +122 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +266 -0
  17. data/lib/active_record/associations/has_one_association.rb +133 -0
  18. data/lib/active_record/associations/has_one_through_association.rb +37 -0
  19. data/lib/active_record/associations.rb +2241 -0
  20. data/lib/active_record/attribute_methods.rb +388 -0
  21. data/lib/active_record/autosave_association.rb +364 -0
  22. data/lib/active_record/base.rb +3171 -0
  23. data/lib/active_record/batches.rb +81 -0
  24. data/lib/active_record/calculations.rb +311 -0
  25. data/lib/active_record/callbacks.rb +360 -0
  26. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +371 -0
  27. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +139 -0
  28. data/lib/active_record/connection_adapters/abstract/database_statements.rb +289 -0
  29. data/lib/active_record/connection_adapters/abstract/query_cache.rb +94 -0
  30. data/lib/active_record/connection_adapters/abstract/quoting.rb +69 -0
  31. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +722 -0
  32. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +434 -0
  33. data/lib/active_record/connection_adapters/abstract_adapter.rb +241 -0
  34. data/lib/active_record/connection_adapters/mysql_adapter.rb +630 -0
  35. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1113 -0
  36. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  37. data/lib/active_record/connection_adapters/sqlite_adapter.rb +453 -0
  38. data/lib/active_record/dirty.rb +183 -0
  39. data/lib/active_record/dynamic_finder_match.rb +41 -0
  40. data/lib/active_record/dynamic_scope_match.rb +25 -0
  41. data/lib/active_record/fixtures.rb +996 -0
  42. data/lib/active_record/i18n_interpolation_deprecation.rb +26 -0
  43. data/lib/active_record/locale/en.yml +58 -0
  44. data/lib/active_record/locking/optimistic.rb +148 -0
  45. data/lib/active_record/locking/pessimistic.rb +55 -0
  46. data/lib/active_record/migration.rb +566 -0
  47. data/lib/active_record/named_scope.rb +192 -0
  48. data/lib/active_record/nested_attributes.rb +392 -0
  49. data/lib/active_record/observer.rb +197 -0
  50. data/lib/active_record/query_cache.rb +33 -0
  51. data/lib/active_record/reflection.rb +320 -0
  52. data/lib/active_record/schema.rb +51 -0
  53. data/lib/active_record/schema_dumper.rb +182 -0
  54. data/lib/active_record/serialization.rb +101 -0
  55. data/lib/active_record/serializers/json_serializer.rb +91 -0
  56. data/lib/active_record/serializers/xml_serializer.rb +357 -0
  57. data/lib/active_record/session_store.rb +326 -0
  58. data/lib/active_record/test_case.rb +66 -0
  59. data/lib/active_record/timestamp.rb +71 -0
  60. data/lib/active_record/transactions.rb +235 -0
  61. data/lib/active_record/validations.rb +1135 -0
  62. data/lib/active_record/version.rb +9 -0
  63. data/lib/active_record.rb +84 -0
  64. data/lib/activerecord.rb +2 -0
  65. data/test/assets/example.log +1 -0
  66. data/test/assets/flowers.jpg +0 -0
  67. data/test/cases/aaa_create_tables_test.rb +24 -0
  68. data/test/cases/active_schema_test_mysql.rb +100 -0
  69. data/test/cases/active_schema_test_postgresql.rb +24 -0
  70. data/test/cases/adapter_test.rb +145 -0
  71. data/test/cases/aggregations_test.rb +167 -0
  72. data/test/cases/ar_schema_test.rb +32 -0
  73. data/test/cases/associations/belongs_to_associations_test.rb +425 -0
  74. data/test/cases/associations/callbacks_test.rb +161 -0
  75. data/test/cases/associations/cascaded_eager_loading_test.rb +131 -0
  76. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -0
  77. data/test/cases/associations/eager_load_nested_include_test.rb +130 -0
  78. data/test/cases/associations/eager_singularization_test.rb +145 -0
  79. data/test/cases/associations/eager_test.rb +834 -0
  80. data/test/cases/associations/extension_test.rb +62 -0
  81. data/test/cases/associations/habtm_join_table_test.rb +56 -0
  82. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +822 -0
  83. data/test/cases/associations/has_many_associations_test.rb +1134 -0
  84. data/test/cases/associations/has_many_through_associations_test.rb +346 -0
  85. data/test/cases/associations/has_one_associations_test.rb +330 -0
  86. data/test/cases/associations/has_one_through_associations_test.rb +209 -0
  87. data/test/cases/associations/inner_join_association_test.rb +93 -0
  88. data/test/cases/associations/join_model_test.rb +712 -0
  89. data/test/cases/associations_test.rb +262 -0
  90. data/test/cases/attribute_methods_test.rb +305 -0
  91. data/test/cases/autosave_association_test.rb +1142 -0
  92. data/test/cases/base_test.rb +2154 -0
  93. data/test/cases/batches_test.rb +61 -0
  94. data/test/cases/binary_test.rb +30 -0
  95. data/test/cases/calculations_test.rb +348 -0
  96. data/test/cases/callbacks_observers_test.rb +38 -0
  97. data/test/cases/callbacks_test.rb +438 -0
  98. data/test/cases/class_inheritable_attributes_test.rb +32 -0
  99. data/test/cases/column_alias_test.rb +17 -0
  100. data/test/cases/column_definition_test.rb +70 -0
  101. data/test/cases/connection_pool_test.rb +25 -0
  102. data/test/cases/connection_test_firebird.rb +8 -0
  103. data/test/cases/connection_test_mysql.rb +64 -0
  104. data/test/cases/copy_table_test_sqlite.rb +80 -0
  105. data/test/cases/database_statements_test.rb +12 -0
  106. data/test/cases/datatype_test_postgresql.rb +204 -0
  107. data/test/cases/date_time_test.rb +37 -0
  108. data/test/cases/default_test_firebird.rb +16 -0
  109. data/test/cases/defaults_test.rb +111 -0
  110. data/test/cases/deprecated_finder_test.rb +30 -0
  111. data/test/cases/dirty_test.rb +316 -0
  112. data/test/cases/finder_respond_to_test.rb +76 -0
  113. data/test/cases/finder_test.rb +1066 -0
  114. data/test/cases/fixtures_test.rb +656 -0
  115. data/test/cases/helper.rb +68 -0
  116. data/test/cases/i18n_test.rb +46 -0
  117. data/test/cases/inheritance_test.rb +262 -0
  118. data/test/cases/invalid_date_test.rb +24 -0
  119. data/test/cases/json_serialization_test.rb +205 -0
  120. data/test/cases/lifecycle_test.rb +193 -0
  121. data/test/cases/locking_test.rb +304 -0
  122. data/test/cases/method_scoping_test.rb +704 -0
  123. data/test/cases/migration_test.rb +1523 -0
  124. data/test/cases/migration_test_firebird.rb +124 -0
  125. data/test/cases/mixin_test.rb +96 -0
  126. data/test/cases/modules_test.rb +81 -0
  127. data/test/cases/multiple_db_test.rb +85 -0
  128. data/test/cases/named_scope_test.rb +361 -0
  129. data/test/cases/nested_attributes_test.rb +581 -0
  130. data/test/cases/pk_test.rb +119 -0
  131. data/test/cases/pooled_connections_test.rb +103 -0
  132. data/test/cases/query_cache_test.rb +123 -0
  133. data/test/cases/readonly_test.rb +107 -0
  134. data/test/cases/reflection_test.rb +194 -0
  135. data/test/cases/reload_models_test.rb +22 -0
  136. data/test/cases/repair_helper.rb +50 -0
  137. data/test/cases/reserved_word_test_mysql.rb +176 -0
  138. data/test/cases/sanitize_test.rb +25 -0
  139. data/test/cases/schema_authorization_test_postgresql.rb +75 -0
  140. data/test/cases/schema_dumper_test.rb +211 -0
  141. data/test/cases/schema_test_postgresql.rb +178 -0
  142. data/test/cases/serialization_test.rb +47 -0
  143. data/test/cases/synonym_test_oracle.rb +17 -0
  144. data/test/cases/timestamp_test.rb +75 -0
  145. data/test/cases/transactions_test.rb +522 -0
  146. data/test/cases/unconnected_test.rb +32 -0
  147. data/test/cases/validations_i18n_test.rb +955 -0
  148. data/test/cases/validations_test.rb +1640 -0
  149. data/test/cases/xml_serialization_test.rb +240 -0
  150. data/test/config.rb +5 -0
  151. data/test/connections/jdbc_jdbcderby/connection.rb +18 -0
  152. data/test/connections/jdbc_jdbch2/connection.rb +18 -0
  153. data/test/connections/jdbc_jdbchsqldb/connection.rb +18 -0
  154. data/test/connections/jdbc_jdbcmysql/connection.rb +26 -0
  155. data/test/connections/jdbc_jdbcpostgresql/connection.rb +26 -0
  156. data/test/connections/jdbc_jdbcsqlite3/connection.rb +25 -0
  157. data/test/connections/native_db2/connection.rb +25 -0
  158. data/test/connections/native_firebird/connection.rb +26 -0
  159. data/test/connections/native_frontbase/connection.rb +27 -0
  160. data/test/connections/native_mysql/connection.rb +25 -0
  161. data/test/connections/native_openbase/connection.rb +21 -0
  162. data/test/connections/native_oracle/connection.rb +27 -0
  163. data/test/connections/native_postgresql/connection.rb +25 -0
  164. data/test/connections/native_sqlite/connection.rb +25 -0
  165. data/test/connections/native_sqlite3/connection.rb +25 -0
  166. data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
  167. data/test/connections/native_sybase/connection.rb +23 -0
  168. data/test/fixtures/accounts.yml +29 -0
  169. data/test/fixtures/all/developers.yml +0 -0
  170. data/test/fixtures/all/people.csv +0 -0
  171. data/test/fixtures/all/tasks.yml +0 -0
  172. data/test/fixtures/author_addresses.yml +5 -0
  173. data/test/fixtures/author_favorites.yml +4 -0
  174. data/test/fixtures/authors.yml +9 -0
  175. data/test/fixtures/binaries.yml +132 -0
  176. data/test/fixtures/books.yml +7 -0
  177. data/test/fixtures/categories/special_categories.yml +9 -0
  178. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  179. data/test/fixtures/categories.yml +14 -0
  180. data/test/fixtures/categories_ordered.yml +7 -0
  181. data/test/fixtures/categories_posts.yml +23 -0
  182. data/test/fixtures/categorizations.yml +17 -0
  183. data/test/fixtures/clubs.yml +6 -0
  184. data/test/fixtures/comments.yml +59 -0
  185. data/test/fixtures/companies.yml +56 -0
  186. data/test/fixtures/computers.yml +4 -0
  187. data/test/fixtures/courses.yml +7 -0
  188. data/test/fixtures/customers.yml +26 -0
  189. data/test/fixtures/developers.yml +21 -0
  190. data/test/fixtures/developers_projects.yml +17 -0
  191. data/test/fixtures/edges.yml +6 -0
  192. data/test/fixtures/entrants.yml +14 -0
  193. data/test/fixtures/fixture_database.sqlite3 +0 -0
  194. data/test/fixtures/fixture_database_2.sqlite3 +0 -0
  195. data/test/fixtures/fk_test_has_fk.yml +3 -0
  196. data/test/fixtures/fk_test_has_pk.yml +2 -0
  197. data/test/fixtures/funny_jokes.yml +10 -0
  198. data/test/fixtures/items.yml +4 -0
  199. data/test/fixtures/jobs.yml +7 -0
  200. data/test/fixtures/legacy_things.yml +3 -0
  201. data/test/fixtures/mateys.yml +4 -0
  202. data/test/fixtures/member_types.yml +6 -0
  203. data/test/fixtures/members.yml +6 -0
  204. data/test/fixtures/memberships.yml +20 -0
  205. data/test/fixtures/minimalistics.yml +2 -0
  206. data/test/fixtures/mixed_case_monkeys.yml +6 -0
  207. data/test/fixtures/mixins.yml +29 -0
  208. data/test/fixtures/movies.yml +7 -0
  209. data/test/fixtures/naked/csv/accounts.csv +1 -0
  210. data/test/fixtures/naked/yml/accounts.yml +1 -0
  211. data/test/fixtures/naked/yml/companies.yml +1 -0
  212. data/test/fixtures/naked/yml/courses.yml +1 -0
  213. data/test/fixtures/organizations.yml +5 -0
  214. data/test/fixtures/owners.yml +7 -0
  215. data/test/fixtures/parrots.yml +27 -0
  216. data/test/fixtures/parrots_pirates.yml +7 -0
  217. data/test/fixtures/people.yml +15 -0
  218. data/test/fixtures/pets.yml +14 -0
  219. data/test/fixtures/pirates.yml +9 -0
  220. data/test/fixtures/posts.yml +52 -0
  221. data/test/fixtures/price_estimates.yml +7 -0
  222. data/test/fixtures/projects.yml +7 -0
  223. data/test/fixtures/readers.yml +9 -0
  224. data/test/fixtures/references.yml +17 -0
  225. data/test/fixtures/reserved_words/distinct.yml +5 -0
  226. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  227. data/test/fixtures/reserved_words/group.yml +14 -0
  228. data/test/fixtures/reserved_words/select.yml +8 -0
  229. data/test/fixtures/reserved_words/values.yml +7 -0
  230. data/test/fixtures/ships.yml +5 -0
  231. data/test/fixtures/sponsors.yml +9 -0
  232. data/test/fixtures/subscribers.yml +7 -0
  233. data/test/fixtures/subscriptions.yml +12 -0
  234. data/test/fixtures/taggings.yml +28 -0
  235. data/test/fixtures/tags.yml +7 -0
  236. data/test/fixtures/tasks.yml +7 -0
  237. data/test/fixtures/topics.yml +42 -0
  238. data/test/fixtures/toys.yml +4 -0
  239. data/test/fixtures/treasures.yml +10 -0
  240. data/test/fixtures/vertices.yml +4 -0
  241. data/test/fixtures/warehouse-things.yml +3 -0
  242. data/test/migrations/broken/100_migration_that_raises_exception.rb +10 -0
  243. data/test/migrations/decimal/1_give_me_big_numbers.rb +15 -0
  244. data/test/migrations/duplicate/1_people_have_last_names.rb +9 -0
  245. data/test/migrations/duplicate/2_we_need_reminders.rb +12 -0
  246. data/test/migrations/duplicate/3_foo.rb +7 -0
  247. data/test/migrations/duplicate/3_innocent_jointable.rb +12 -0
  248. data/test/migrations/duplicate_names/20080507052938_chunky.rb +7 -0
  249. data/test/migrations/duplicate_names/20080507053028_chunky.rb +7 -0
  250. data/test/migrations/interleaved/pass_1/3_innocent_jointable.rb +12 -0
  251. data/test/migrations/interleaved/pass_2/1_people_have_last_names.rb +9 -0
  252. data/test/migrations/interleaved/pass_2/3_innocent_jointable.rb +12 -0
  253. data/test/migrations/interleaved/pass_3/1_people_have_last_names.rb +9 -0
  254. data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +8 -0
  255. data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +12 -0
  256. data/test/migrations/missing/1000_people_have_middle_names.rb +9 -0
  257. data/test/migrations/missing/1_people_have_last_names.rb +9 -0
  258. data/test/migrations/missing/3_we_need_reminders.rb +12 -0
  259. data/test/migrations/missing/4_innocent_jointable.rb +12 -0
  260. data/test/migrations/valid/1_people_have_last_names.rb +9 -0
  261. data/test/migrations/valid/2_we_need_reminders.rb +12 -0
  262. data/test/migrations/valid/3_innocent_jointable.rb +12 -0
  263. data/test/models/author.rb +146 -0
  264. data/test/models/auto_id.rb +4 -0
  265. data/test/models/binary.rb +2 -0
  266. data/test/models/bird.rb +3 -0
  267. data/test/models/book.rb +4 -0
  268. data/test/models/categorization.rb +5 -0
  269. data/test/models/category.rb +34 -0
  270. data/test/models/citation.rb +6 -0
  271. data/test/models/club.rb +13 -0
  272. data/test/models/column_name.rb +3 -0
  273. data/test/models/comment.rb +29 -0
  274. data/test/models/company.rb +171 -0
  275. data/test/models/company_in_module.rb +61 -0
  276. data/test/models/computer.rb +3 -0
  277. data/test/models/contact.rb +16 -0
  278. data/test/models/contract.rb +5 -0
  279. data/test/models/course.rb +3 -0
  280. data/test/models/customer.rb +73 -0
  281. data/test/models/default.rb +2 -0
  282. data/test/models/developer.rb +101 -0
  283. data/test/models/edge.rb +5 -0
  284. data/test/models/entrant.rb +3 -0
  285. data/test/models/essay.rb +3 -0
  286. data/test/models/event.rb +3 -0
  287. data/test/models/guid.rb +2 -0
  288. data/test/models/item.rb +7 -0
  289. data/test/models/job.rb +5 -0
  290. data/test/models/joke.rb +3 -0
  291. data/test/models/keyboard.rb +3 -0
  292. data/test/models/legacy_thing.rb +3 -0
  293. data/test/models/matey.rb +4 -0
  294. data/test/models/member.rb +12 -0
  295. data/test/models/member_detail.rb +5 -0
  296. data/test/models/member_type.rb +3 -0
  297. data/test/models/membership.rb +9 -0
  298. data/test/models/minimalistic.rb +2 -0
  299. data/test/models/mixed_case_monkey.rb +3 -0
  300. data/test/models/movie.rb +5 -0
  301. data/test/models/order.rb +4 -0
  302. data/test/models/organization.rb +6 -0
  303. data/test/models/owner.rb +5 -0
  304. data/test/models/parrot.rb +16 -0
  305. data/test/models/person.rb +16 -0
  306. data/test/models/pet.rb +5 -0
  307. data/test/models/pirate.rb +70 -0
  308. data/test/models/post.rb +100 -0
  309. data/test/models/price_estimate.rb +3 -0
  310. data/test/models/project.rb +30 -0
  311. data/test/models/reader.rb +4 -0
  312. data/test/models/reference.rb +4 -0
  313. data/test/models/reply.rb +46 -0
  314. data/test/models/ship.rb +10 -0
  315. data/test/models/ship_part.rb +5 -0
  316. data/test/models/sponsor.rb +4 -0
  317. data/test/models/subject.rb +4 -0
  318. data/test/models/subscriber.rb +8 -0
  319. data/test/models/subscription.rb +4 -0
  320. data/test/models/tag.rb +7 -0
  321. data/test/models/tagging.rb +10 -0
  322. data/test/models/task.rb +3 -0
  323. data/test/models/topic.rb +80 -0
  324. data/test/models/toy.rb +6 -0
  325. data/test/models/treasure.rb +8 -0
  326. data/test/models/vertex.rb +9 -0
  327. data/test/models/warehouse_thing.rb +5 -0
  328. data/test/schema/mysql_specific_schema.rb +24 -0
  329. data/test/schema/postgresql_specific_schema.rb +114 -0
  330. data/test/schema/schema.rb +493 -0
  331. data/test/schema/schema2.rb +6 -0
  332. data/test/schema/sqlite_specific_schema.rb +25 -0
  333. metadata +420 -0
@@ -0,0 +1,360 @@
1
+ require 'observer'
2
+
3
+ module ActiveRecord
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 for a new record:
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. The sequence for calling <tt>Base#save</tt> an existing record is similar, except that each
26
+ # <tt>_on_create</tt> callback is replaced by the corresponding <tt>_on_update</tt> callback.
27
+ #
28
+ # Examples:
29
+ # class CreditCard < ActiveRecord::Base
30
+ # # Strip everything but digits, so the user can specify "555 234 34" or
31
+ # # "5552-3434" or both will mean "55523434"
32
+ # def before_validation_on_create
33
+ # self.number = number.gsub(/[^0-9]/, "") if attribute_present?("number")
34
+ # end
35
+ # end
36
+ #
37
+ # class Subscription < ActiveRecord::Base
38
+ # before_create :record_signup
39
+ #
40
+ # private
41
+ # def record_signup
42
+ # self.signed_up_on = Date.today
43
+ # end
44
+ # end
45
+ #
46
+ # class Firm < ActiveRecord::Base
47
+ # # Destroys the associated clients and people when the firm is destroyed
48
+ # before_destroy { |record| Person.destroy_all "firm_id = #{record.id}" }
49
+ # before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
50
+ # end
51
+ #
52
+ # == Inheritable callback queues
53
+ #
54
+ # Besides the overwritable callback methods, it's also possible to register callbacks through the use of the callback macros.
55
+ # Their main advantage is that the macros add behavior into a callback queue that is kept intact down through an inheritance
56
+ # hierarchy. Example:
57
+ #
58
+ # class Topic < ActiveRecord::Base
59
+ # before_destroy :destroy_author
60
+ # end
61
+ #
62
+ # class Reply < Topic
63
+ # before_destroy :destroy_readers
64
+ # end
65
+ #
66
+ # Now, when <tt>Topic#destroy</tt> is run only +destroy_author+ is called. When <tt>Reply#destroy</tt> is run, both +destroy_author+ and
67
+ # +destroy_readers+ are called. Contrast this to the situation where we've implemented the save behavior through overwriteable
68
+ # methods:
69
+ #
70
+ # class Topic < ActiveRecord::Base
71
+ # def before_destroy() destroy_author end
72
+ # end
73
+ #
74
+ # class Reply < Topic
75
+ # def before_destroy() destroy_readers end
76
+ # end
77
+ #
78
+ # In that case, <tt>Reply#destroy</tt> would only run +destroy_readers+ and _not_ +destroy_author+. So, use the callback macros when
79
+ # you want to ensure that a certain callback is called for the entire hierarchy, and use the regular overwriteable methods
80
+ # when you want to leave it up to each descendant to decide whether they want to call +super+ and trigger the inherited callbacks.
81
+ #
82
+ # *IMPORTANT:* In order for inheritance to work for the callback queues, you must specify the callbacks before specifying the
83
+ # associations. Otherwise, you might trigger the loading of a child before the parent has registered the callbacks and they won't
84
+ # be inherited.
85
+ #
86
+ # == Types of callbacks
87
+ #
88
+ # There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
89
+ # inline methods (using a proc), and inline eval methods (using a string). Method references and callback objects are the
90
+ # recommended approaches, inline methods using a proc are sometimes appropriate (such as for creating mix-ins), and inline
91
+ # eval methods are deprecated.
92
+ #
93
+ # The method reference callbacks work by specifying a protected or private method available in the object, like this:
94
+ #
95
+ # class Topic < ActiveRecord::Base
96
+ # before_destroy :delete_parents
97
+ #
98
+ # private
99
+ # def delete_parents
100
+ # self.class.delete_all "parent_id = #{id}"
101
+ # end
102
+ # end
103
+ #
104
+ # The callback objects have methods named after the callback called with the record as the only parameter, such as:
105
+ #
106
+ # class BankAccount < ActiveRecord::Base
107
+ # before_save EncryptionWrapper.new
108
+ # after_save EncryptionWrapper.new
109
+ # after_initialize EncryptionWrapper.new
110
+ # end
111
+ #
112
+ # class EncryptionWrapper
113
+ # def before_save(record)
114
+ # record.credit_card_number = encrypt(record.credit_card_number)
115
+ # end
116
+ #
117
+ # def after_save(record)
118
+ # record.credit_card_number = decrypt(record.credit_card_number)
119
+ # end
120
+ #
121
+ # alias_method :after_find, :after_save
122
+ #
123
+ # private
124
+ # def encrypt(value)
125
+ # # Secrecy is committed
126
+ # end
127
+ #
128
+ # def decrypt(value)
129
+ # # Secrecy is unveiled
130
+ # end
131
+ # end
132
+ #
133
+ # So you specify the object you want messaged on a given callback. When that callback is triggered, the object has
134
+ # a method by the name of the callback messaged. You can make these callbacks more flexible by passing in other
135
+ # initialization data such as the name of the attribute to work with:
136
+ #
137
+ # class BankAccount < ActiveRecord::Base
138
+ # before_save EncryptionWrapper.new("credit_card_number")
139
+ # after_save EncryptionWrapper.new("credit_card_number")
140
+ # after_initialize EncryptionWrapper.new("credit_card_number")
141
+ # end
142
+ #
143
+ # class EncryptionWrapper
144
+ # def initialize(attribute)
145
+ # @attribute = attribute
146
+ # end
147
+ #
148
+ # def before_save(record)
149
+ # record.send("#{@attribute}=", encrypt(record.send("#{@attribute}")))
150
+ # end
151
+ #
152
+ # def after_save(record)
153
+ # record.send("#{@attribute}=", decrypt(record.send("#{@attribute}")))
154
+ # end
155
+ #
156
+ # alias_method :after_find, :after_save
157
+ #
158
+ # private
159
+ # def encrypt(value)
160
+ # # Secrecy is committed
161
+ # end
162
+ #
163
+ # def decrypt(value)
164
+ # # Secrecy is unveiled
165
+ # end
166
+ # end
167
+ #
168
+ # The callback macros usually accept a symbol for the method they're supposed to run, but you can also pass a "method string",
169
+ # which will then be evaluated within the binding of the callback. Example:
170
+ #
171
+ # class Topic < ActiveRecord::Base
172
+ # before_destroy 'self.class.delete_all "parent_id = #{id}"'
173
+ # end
174
+ #
175
+ # Notice that single quotes (') are used so the <tt>#{id}</tt> part isn't evaluated until the callback is triggered. Also note that these
176
+ # inline callbacks can be stacked just like the regular ones:
177
+ #
178
+ # class Topic < ActiveRecord::Base
179
+ # before_destroy 'self.class.delete_all "parent_id = #{id}"',
180
+ # 'puts "Evaluated after parents are destroyed"'
181
+ # end
182
+ #
183
+ # == The +after_find+ and +after_initialize+ exceptions
184
+ #
185
+ # 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
186
+ # to implement a simple performance constraint (50% more speed on a simple test case). Unlike all the other callbacks, +after_find+ and
187
+ # +after_initialize+ will only be run if an explicit implementation is defined (<tt>def after_find</tt>). In that case, all of the
188
+ # callback types will be called.
189
+ #
190
+ # == <tt>before_validation*</tt> returning statements
191
+ #
192
+ # 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+.
193
+ # If Base#save! is called it will raise a ActiveRecord::RecordInvalid exception.
194
+ # Nothing will be appended to the errors object.
195
+ #
196
+ # == Canceling callbacks
197
+ #
198
+ # 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
199
+ # +false+, all the later callbacks are cancelled. Callbacks are generally run in the order they are defined, with the exception of callbacks
200
+ # defined as methods on the model, which are called last.
201
+ #
202
+ # == Transactions
203
+ #
204
+ # The entire callback chain of a +save+, <tt>save!</tt>, or +destroy+ call runs
205
+ # within a transaction. That includes <tt>after_*</tt> hooks. If everything
206
+ # goes fine a COMMIT is executed once the chain has been completed.
207
+ #
208
+ # If a <tt>before_*</tt> callback cancels the action a ROLLBACK is issued. You
209
+ # can also trigger a ROLLBACK raising an exception in any of the callbacks,
210
+ # including <tt>after_*</tt> hooks. Note, however, that in that case the client
211
+ # needs to be aware of it because an ordinary +save+ will raise such exception
212
+ # instead of quietly returning +false+.
213
+ module Callbacks
214
+ CALLBACKS = %w(
215
+ after_find after_initialize before_save after_save before_create after_create before_update after_update before_validation
216
+ after_validation before_validation_on_create after_validation_on_create before_validation_on_update
217
+ after_validation_on_update before_destroy after_destroy
218
+ )
219
+
220
+ def self.included(base) #:nodoc:
221
+ base.extend Observable
222
+
223
+ [:create_or_update, :valid?, :create, :update, :destroy].each do |method|
224
+ base.send :alias_method_chain, method, :callbacks
225
+ end
226
+
227
+ base.send :include, ActiveSupport::Callbacks
228
+ base.define_callbacks *CALLBACKS
229
+ end
230
+
231
+ # Is called when the object was instantiated by one of the finders, like <tt>Base.find</tt>.
232
+ #def after_find() end
233
+
234
+ # Is called after the object has been instantiated by a call to <tt>Base.new</tt>.
235
+ #def after_initialize() end
236
+
237
+ # Is called _before_ <tt>Base.save</tt> (regardless of whether it's a +create+ or +update+ save).
238
+ def before_save() end
239
+
240
+ # Is called _after_ <tt>Base.save</tt> (regardless of whether it's a +create+ or +update+ save).
241
+ # Note that this callback is still wrapped in the transaction around +save+. For example, if you
242
+ # invoke an external indexer at this point it won't see the changes in the database.
243
+ #
244
+ # class Contact < ActiveRecord::Base
245
+ # after_save { logger.info( 'New contact saved!' ) }
246
+ # end
247
+ def after_save() end
248
+ def create_or_update_with_callbacks #:nodoc:
249
+ return false if callback(:before_save) == false
250
+ if result = create_or_update_without_callbacks
251
+ callback(:after_save)
252
+ end
253
+ result
254
+ end
255
+ private :create_or_update_with_callbacks
256
+
257
+ # Is called _before_ <tt>Base.save</tt> on new objects that haven't been saved yet (no record exists).
258
+ def before_create() end
259
+
260
+ # Is called _after_ <tt>Base.save</tt> on new objects that haven't been saved yet (no record exists).
261
+ # Note that this callback is still wrapped in the transaction around +save+. For example, if you
262
+ # invoke an external indexer at this point it won't see the changes in the database.
263
+ def after_create() end
264
+ def create_with_callbacks #:nodoc:
265
+ return false if callback(:before_create) == false
266
+ result = create_without_callbacks
267
+ callback(:after_create)
268
+ result
269
+ end
270
+ private :create_with_callbacks
271
+
272
+ # Is called _before_ <tt>Base.save</tt> on existing objects that have a record.
273
+ def before_update() end
274
+
275
+ # Is called _after_ <tt>Base.save</tt> on existing objects that have a record.
276
+ # Note that this callback is still wrapped in the transaction around +save+. For example, if you
277
+ # invoke an external indexer at this point it won't see the changes in the database.
278
+ def after_update() end
279
+
280
+ def update_with_callbacks(*args) #:nodoc:
281
+ return false if callback(:before_update) == false
282
+ result = update_without_callbacks(*args)
283
+ callback(:after_update)
284
+ result
285
+ end
286
+ private :update_with_callbacks
287
+
288
+ # Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call).
289
+ def before_validation() end
290
+
291
+ # Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call).
292
+ def after_validation() end
293
+
294
+ # Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on new objects
295
+ # that haven't been saved yet (no record exists).
296
+ def before_validation_on_create() end
297
+
298
+ # Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on new objects
299
+ # that haven't been saved yet (no record exists).
300
+ def after_validation_on_create() end
301
+
302
+ # Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on
303
+ # existing objects that have a record.
304
+ def before_validation_on_update() end
305
+
306
+ # Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on
307
+ # existing objects that have a record.
308
+ def after_validation_on_update() end
309
+
310
+ def valid_with_callbacks? #:nodoc:
311
+ return false if callback(:before_validation) == false
312
+ if new_record? then result = callback(:before_validation_on_create) else result = callback(:before_validation_on_update) end
313
+ return false if false == result
314
+
315
+ result = valid_without_callbacks?
316
+
317
+ callback(:after_validation)
318
+ if new_record? then callback(:after_validation_on_create) else callback(:after_validation_on_update) end
319
+
320
+ return result
321
+ end
322
+
323
+ # Is called _before_ <tt>Base.destroy</tt>.
324
+ #
325
+ # Note: If you need to _destroy_ or _nullify_ associated records first,
326
+ # use the <tt>:dependent</tt> option on your associations.
327
+ def before_destroy() end
328
+
329
+ # Is called _after_ <tt>Base.destroy</tt> (and all the attributes have been frozen).
330
+ #
331
+ # class Contact < ActiveRecord::Base
332
+ # after_destroy { |record| logger.info( "Contact #{record.id} was destroyed." ) }
333
+ # end
334
+ def after_destroy() end
335
+ def destroy_with_callbacks #:nodoc:
336
+ return false if callback(:before_destroy) == false
337
+ result = destroy_without_callbacks
338
+ callback(:after_destroy)
339
+ result
340
+ end
341
+
342
+ private
343
+ def callback(method)
344
+ result = run_callbacks(method) { |result, object| false == result }
345
+
346
+ if result != false && respond_to_without_attributes?(method)
347
+ result = send(method)
348
+ end
349
+
350
+ notify(method)
351
+
352
+ return result
353
+ end
354
+
355
+ def notify(method) #:nodoc:
356
+ self.class.changed
357
+ self.class.notify_observers(method, self)
358
+ end
359
+ end
360
+ end