activerecord 1.0.0 → 3.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 (178) hide show
  1. data/CHANGELOG +5518 -76
  2. data/README.rdoc +222 -0
  3. data/examples/performance.rb +162 -0
  4. data/examples/simple.rb +14 -0
  5. data/lib/active_record/aggregations.rb +192 -80
  6. data/lib/active_record/association_preload.rb +403 -0
  7. data/lib/active_record/associations/association_collection.rb +545 -53
  8. data/lib/active_record/associations/association_proxy.rb +295 -0
  9. data/lib/active_record/associations/belongs_to_association.rb +91 -0
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +78 -0
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +127 -36
  12. data/lib/active_record/associations/has_many_association.rb +108 -84
  13. data/lib/active_record/associations/has_many_through_association.rb +116 -0
  14. data/lib/active_record/associations/has_one_association.rb +143 -0
  15. data/lib/active_record/associations/has_one_through_association.rb +40 -0
  16. data/lib/active_record/associations/through_association_scope.rb +154 -0
  17. data/lib/active_record/associations.rb +2086 -368
  18. data/lib/active_record/attribute_methods/before_type_cast.rb +33 -0
  19. data/lib/active_record/attribute_methods/dirty.rb +95 -0
  20. data/lib/active_record/attribute_methods/primary_key.rb +50 -0
  21. data/lib/active_record/attribute_methods/query.rb +39 -0
  22. data/lib/active_record/attribute_methods/read.rb +116 -0
  23. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -0
  24. data/lib/active_record/attribute_methods/write.rb +37 -0
  25. data/lib/active_record/attribute_methods.rb +60 -0
  26. data/lib/active_record/autosave_association.rb +369 -0
  27. data/lib/active_record/base.rb +1603 -721
  28. data/lib/active_record/callbacks.rb +176 -225
  29. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +365 -0
  30. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +113 -0
  31. data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
  32. data/lib/active_record/connection_adapters/abstract/database_statements.rb +329 -0
  33. data/lib/active_record/connection_adapters/abstract/query_cache.rb +81 -0
  34. data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -0
  35. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +739 -0
  36. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +543 -0
  37. data/lib/active_record/connection_adapters/abstract_adapter.rb +165 -279
  38. data/lib/active_record/connection_adapters/mysql_adapter.rb +594 -82
  39. data/lib/active_record/connection_adapters/postgresql_adapter.rb +988 -135
  40. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -0
  41. data/lib/active_record/connection_adapters/sqlite_adapter.rb +365 -71
  42. data/lib/active_record/counter_cache.rb +115 -0
  43. data/lib/active_record/dynamic_finder_match.rb +53 -0
  44. data/lib/active_record/dynamic_scope_match.rb +32 -0
  45. data/lib/active_record/errors.rb +172 -0
  46. data/lib/active_record/fixtures.rb +941 -105
  47. data/lib/active_record/locale/en.yml +40 -0
  48. data/lib/active_record/locking/optimistic.rb +172 -0
  49. data/lib/active_record/locking/pessimistic.rb +55 -0
  50. data/lib/active_record/log_subscriber.rb +48 -0
  51. data/lib/active_record/migration.rb +617 -0
  52. data/lib/active_record/named_scope.rb +138 -0
  53. data/lib/active_record/nested_attributes.rb +417 -0
  54. data/lib/active_record/observer.rb +105 -36
  55. data/lib/active_record/persistence.rb +291 -0
  56. data/lib/active_record/query_cache.rb +36 -0
  57. data/lib/active_record/railtie.rb +91 -0
  58. data/lib/active_record/railties/controller_runtime.rb +38 -0
  59. data/lib/active_record/railties/databases.rake +512 -0
  60. data/lib/active_record/reflection.rb +364 -87
  61. data/lib/active_record/relation/batches.rb +89 -0
  62. data/lib/active_record/relation/calculations.rb +286 -0
  63. data/lib/active_record/relation/finder_methods.rb +355 -0
  64. data/lib/active_record/relation/predicate_builder.rb +41 -0
  65. data/lib/active_record/relation/query_methods.rb +261 -0
  66. data/lib/active_record/relation/spawn_methods.rb +112 -0
  67. data/lib/active_record/relation.rb +393 -0
  68. data/lib/active_record/schema.rb +59 -0
  69. data/lib/active_record/schema_dumper.rb +195 -0
  70. data/lib/active_record/serialization.rb +60 -0
  71. data/lib/active_record/serializers/xml_serializer.rb +244 -0
  72. data/lib/active_record/session_store.rb +340 -0
  73. data/lib/active_record/test_case.rb +67 -0
  74. data/lib/active_record/timestamp.rb +88 -0
  75. data/lib/active_record/transactions.rb +329 -75
  76. data/lib/active_record/validations/associated.rb +48 -0
  77. data/lib/active_record/validations/uniqueness.rb +185 -0
  78. data/lib/active_record/validations.rb +58 -179
  79. data/lib/active_record/version.rb +9 -0
  80. data/lib/active_record.rb +100 -24
  81. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  82. data/lib/rails/generators/active_record/migration/templates/migration.rb +17 -0
  83. data/lib/rails/generators/active_record/model/model_generator.rb +38 -0
  84. data/lib/rails/generators/active_record/model/templates/migration.rb +16 -0
  85. data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
  86. data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
  87. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  88. data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
  89. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +24 -0
  90. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +16 -0
  91. data/lib/rails/generators/active_record.rb +27 -0
  92. metadata +216 -158
  93. data/README +0 -361
  94. data/RUNNING_UNIT_TESTS +0 -36
  95. data/dev-utils/eval_debugger.rb +0 -9
  96. data/examples/associations.rb +0 -87
  97. data/examples/shared_setup.rb +0 -15
  98. data/examples/validation.rb +0 -88
  99. data/install.rb +0 -60
  100. data/lib/active_record/deprecated_associations.rb +0 -70
  101. data/lib/active_record/support/class_attribute_accessors.rb +0 -43
  102. data/lib/active_record/support/class_inheritable_attributes.rb +0 -37
  103. data/lib/active_record/support/clean_logger.rb +0 -10
  104. data/lib/active_record/support/inflector.rb +0 -70
  105. data/lib/active_record/vendor/mysql.rb +0 -1117
  106. data/lib/active_record/vendor/simple.rb +0 -702
  107. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  108. data/lib/active_record/wrappings.rb +0 -59
  109. data/rakefile +0 -122
  110. data/test/abstract_unit.rb +0 -16
  111. data/test/aggregations_test.rb +0 -34
  112. data/test/all.sh +0 -8
  113. data/test/associations_test.rb +0 -477
  114. data/test/base_test.rb +0 -513
  115. data/test/class_inheritable_attributes_test.rb +0 -33
  116. data/test/connections/native_mysql/connection.rb +0 -24
  117. data/test/connections/native_postgresql/connection.rb +0 -24
  118. data/test/connections/native_sqlite/connection.rb +0 -24
  119. data/test/deprecated_associations_test.rb +0 -336
  120. data/test/finder_test.rb +0 -67
  121. data/test/fixtures/accounts/signals37 +0 -3
  122. data/test/fixtures/accounts/unknown +0 -2
  123. data/test/fixtures/auto_id.rb +0 -4
  124. data/test/fixtures/column_name.rb +0 -3
  125. data/test/fixtures/companies/first_client +0 -6
  126. data/test/fixtures/companies/first_firm +0 -4
  127. data/test/fixtures/companies/second_client +0 -6
  128. data/test/fixtures/company.rb +0 -37
  129. data/test/fixtures/company_in_module.rb +0 -33
  130. data/test/fixtures/course.rb +0 -3
  131. data/test/fixtures/courses/java +0 -2
  132. data/test/fixtures/courses/ruby +0 -2
  133. data/test/fixtures/customer.rb +0 -30
  134. data/test/fixtures/customers/david +0 -6
  135. data/test/fixtures/db_definitions/mysql.sql +0 -96
  136. data/test/fixtures/db_definitions/mysql2.sql +0 -4
  137. data/test/fixtures/db_definitions/postgresql.sql +0 -113
  138. data/test/fixtures/db_definitions/postgresql2.sql +0 -4
  139. data/test/fixtures/db_definitions/sqlite.sql +0 -85
  140. data/test/fixtures/db_definitions/sqlite2.sql +0 -4
  141. data/test/fixtures/default.rb +0 -2
  142. data/test/fixtures/developer.rb +0 -8
  143. data/test/fixtures/developers/david +0 -2
  144. data/test/fixtures/developers/jamis +0 -2
  145. data/test/fixtures/developers_projects/david_action_controller +0 -2
  146. data/test/fixtures/developers_projects/david_active_record +0 -2
  147. data/test/fixtures/developers_projects/jamis_active_record +0 -2
  148. data/test/fixtures/entrant.rb +0 -3
  149. data/test/fixtures/entrants/first +0 -3
  150. data/test/fixtures/entrants/second +0 -3
  151. data/test/fixtures/entrants/third +0 -3
  152. data/test/fixtures/fixture_database.sqlite +0 -0
  153. data/test/fixtures/fixture_database_2.sqlite +0 -0
  154. data/test/fixtures/movie.rb +0 -5
  155. data/test/fixtures/movies/first +0 -2
  156. data/test/fixtures/movies/second +0 -2
  157. data/test/fixtures/project.rb +0 -3
  158. data/test/fixtures/projects/action_controller +0 -2
  159. data/test/fixtures/projects/active_record +0 -2
  160. data/test/fixtures/reply.rb +0 -21
  161. data/test/fixtures/subscriber.rb +0 -5
  162. data/test/fixtures/subscribers/first +0 -2
  163. data/test/fixtures/subscribers/second +0 -2
  164. data/test/fixtures/topic.rb +0 -20
  165. data/test/fixtures/topics/first +0 -9
  166. data/test/fixtures/topics/second +0 -8
  167. data/test/fixtures_test.rb +0 -20
  168. data/test/inflector_test.rb +0 -104
  169. data/test/inheritance_test.rb +0 -125
  170. data/test/lifecycle_test.rb +0 -110
  171. data/test/modules_test.rb +0 -21
  172. data/test/multiple_db_test.rb +0 -46
  173. data/test/pk_test.rb +0 -57
  174. data/test/reflection_test.rb +0 -78
  175. data/test/thread_safety_test.rb +0 -33
  176. data/test/transactions_test.rb +0 -83
  177. data/test/unconnected_test.rb +0 -24
  178. data/test/validations_test.rb +0 -126
@@ -1,57 +1,63 @@
1
- require 'observer'
1
+ require 'active_support/core_ext/array/wrap'
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
+ # = Active Record Callbacks
5
+ #
6
+ # Callbacks are hooks into the life cycle of an Active Record object that allow you to trigger logic
7
+ # before or after an alteration of the object state. This can be used to make sure that associated and
8
+ # dependent objects are deleted when +destroy+ is called (by overwriting +before_destroy+) or to massage attributes
9
+ # before they're validated (by overwriting +before_validation+). As an example of the callbacks initiated, consider
10
+ # the <tt>Base#save</tt> call for a new record:
11
+ #
12
+ # * (-) <tt>save</tt>
13
+ # * (-) <tt>valid</tt>
14
+ # * (1) <tt>before_validation</tt>
15
+ # * (-) <tt>validate</tt>
16
+ # * (2) <tt>after_validation</tt>
17
+ # * (3) <tt>before_save</tt>
18
+ # * (4) <tt>before_create</tt>
19
+ # * (-) <tt>create</tt>
20
+ # * (5) <tt>after_create</tt>
21
+ # * (6) <tt>after_save</tt>
22
+ # * (7) <tt>after_commit</tt>
23
+ #
24
+ # Also, an <tt>after_rollback</tt> callback can be configured to be triggered whenever a rollback is issued.
25
+ # Check out <tt>ActiveRecord::Transactions</tt> for more details about <tt>after_commit</tt> and
26
+ # <tt>after_rollback</tt>.
27
+ #
28
+ # That's a total of ten callbacks, which gives you immense power to react and prepare for each state in the
29
+ # Active Record life cycle. The sequence for calling <tt>Base#save</tt> for an existing record is similar,
30
+ # except that each <tt>_on_create</tt> callback is replaced by the corresponding <tt>_on_update</tt> callback.
26
31
  #
27
32
  # Examples:
28
33
  # class CreditCard < ActiveRecord::Base
29
- # # Strip everything but digits, so the user can specify "555 234 34" or
34
+ # # Strip everything but digits, so the user can specify "555 234 34" or
30
35
  # # "5552-3434" or both will mean "55523434"
31
- # def before_validation_on_create
36
+ # before_validation(:on => :create) do
32
37
  # self.number = number.gsub(/[^0-9]/, "") if attribute_present?("number")
33
38
  # end
34
39
  # end
35
40
  #
36
41
  # class Subscription < ActiveRecord::Base
37
- # # Automatically assign the signup date
38
- # def before_create
39
- # self.signed_up_on = Date.today
40
- # end
42
+ # before_create :record_signup
43
+ #
44
+ # private
45
+ # def record_signup
46
+ # self.signed_up_on = Date.today
47
+ # end
41
48
  # end
42
49
  #
43
50
  # class Firm < ActiveRecord::Base
44
51
  # # 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
52
+ # before_destroy { |record| Person.destroy_all "firm_id = #{record.id}" }
53
+ # before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
54
+ # end
49
55
  #
50
56
  # == Inheritable callback queues
51
57
  #
52
- # Besides the overwriteable callback methods, it's also possible to register callbacks through the use of the callback macros.
53
- # Their main advantage is that the macros add behavior into a callback queue that is kept intact down through an inheritance
54
- # hierarchy. Example:
58
+ # Besides the overwritable callback methods, it's also possible to register callbacks through the
59
+ # use of the callback macros. Their main advantage is that the macros add behavior into a callback
60
+ # queue that is kept intact down through an inheritance hierarchy.
55
61
  #
56
62
  # class Topic < ActiveRecord::Base
57
63
  # before_destroy :destroy_author
@@ -61,9 +67,9 @@ module ActiveRecord
61
67
  # before_destroy :destroy_readers
62
68
  # end
63
69
  #
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
66
- # methods:
70
+ # Now, when <tt>Topic#destroy</tt> is run only +destroy_author+ is called. When <tt>Reply#destroy</tt> is
71
+ # run, both +destroy_author+ and +destroy_readers+ are called. Contrast this to the following situation
72
+ # where the +before_destroy+ methis is overriden:
67
73
  #
68
74
  # class Topic < ActiveRecord::Base
69
75
  # def before_destroy() destroy_author end
@@ -73,16 +79,21 @@ module ActiveRecord
73
79
  # def before_destroy() destroy_readers end
74
80
  # end
75
81
  #
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.
82
+ # In that case, <tt>Reply#destroy</tt> would only run +destroy_readers+ and _not_ +destroy_author+.
83
+ # So, use the callback macros when you want to ensure that a certain callback is called for the entire
84
+ # hierarchy, and use the regular overwriteable methods when you want to leave it up to each descendant
85
+ # to decide whether they want to call +super+ and trigger the inherited callbacks.
86
+ #
87
+ # *IMPORTANT:* In order for inheritance to work for the callback queues, you must specify the
88
+ # callbacks before specifying the associations. Otherwise, you might trigger the loading of a
89
+ # child before the parent has registered the callbacks and they won't be inherited.
79
90
  #
80
91
  # == Types of callbacks
81
92
  #
82
- # There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
83
- # 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
85
- # eval methods are deprecated.
93
+ # There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
94
+ # inline methods (using a proc), and inline eval methods (using a string). Method references and callback objects
95
+ # are the recommended approaches, inline methods using a proc are sometimes appropriate (such as for
96
+ # creating mix-ins), and inline eval methods are deprecated.
86
97
  #
87
98
  # The method reference callbacks work by specifying a protected or private method available in the object, like this:
88
99
  #
@@ -98,6 +109,37 @@ module ActiveRecord
98
109
  # The callback objects have methods named after the callback called with the record as the only parameter, such as:
99
110
  #
100
111
  # class BankAccount < ActiveRecord::Base
112
+ # before_save EncryptionWrapper.new
113
+ # after_save EncryptionWrapper.new
114
+ # after_initialize EncryptionWrapper.new
115
+ # end
116
+ #
117
+ # class EncryptionWrapper
118
+ # def before_save(record)
119
+ # record.credit_card_number = encrypt(record.credit_card_number)
120
+ # end
121
+ #
122
+ # def after_save(record)
123
+ # record.credit_card_number = decrypt(record.credit_card_number)
124
+ # end
125
+ #
126
+ # alias_method :after_find, :after_save
127
+ #
128
+ # private
129
+ # def encrypt(value)
130
+ # # Secrecy is committed
131
+ # end
132
+ #
133
+ # def decrypt(value)
134
+ # # Secrecy is unveiled
135
+ # end
136
+ # end
137
+ #
138
+ # So you specify the object you want messaged on a given callback. When that callback is triggered, the object has
139
+ # a method by the name of the callback messaged. You can make these callbacks more flexible by passing in other
140
+ # initialization data such as the name of the attribute to work with:
141
+ #
142
+ # class BankAccount < ActiveRecord::Base
101
143
  # before_save EncryptionWrapper.new("credit_card_number")
102
144
  # after_save EncryptionWrapper.new("credit_card_number")
103
145
  # after_initialize EncryptionWrapper.new("credit_card_number")
@@ -109,14 +151,14 @@ module ActiveRecord
109
151
  # end
110
152
  #
111
153
  # def before_save(record)
112
- # record.credit_card_number = encrypt(record.credit_card_number)
154
+ # record.send("#{@attribute}=", encrypt(record.send("#{@attribute}")))
113
155
  # end
114
156
  #
115
157
  # def after_save(record)
116
- # record.credit_card_number = decrypt(record.credit_card_number)
158
+ # record.send("#{@attribute}=", decrypt(record.send("#{@attribute}")))
117
159
  # end
118
- #
119
- # alias_method :after_initialize, :after_save
160
+ #
161
+ # alias_method :after_find, :after_save
120
162
  #
121
163
  # private
122
164
  # def encrypt(value)
@@ -124,214 +166,123 @@ module ActiveRecord
124
166
  # end
125
167
  #
126
168
  # def decrypt(value)
127
- # # Secrecy is unvieled
169
+ # # Secrecy is unveiled
128
170
  # end
129
171
  # end
130
172
  #
131
- # So you specify the object you want messaged on a given callback. When that callback is triggered, the object has
132
- # a method by the name of the callback messaged.
133
- #
134
- # The callback macros usually accept a symbol for the method they're supposed to run, but you can also pass a "method string",
135
- # which will then be evaluated within the binding of the callback. Example:
173
+ # The callback macros usually accept a symbol for the method they're supposed to run, but you can also
174
+ # pass a "method string", which will then be evaluated within the binding of the callback. Example:
136
175
  #
137
176
  # class Topic < ActiveRecord::Base
138
177
  # before_destroy 'self.class.delete_all "parent_id = #{id}"'
139
178
  # end
140
179
  #
141
- # Notice that single plings (') are used so the #{id} part isn't evaluated until the callback is triggered. Also note that these
142
- # inline callbacks can be stacked just like the regular ones:
180
+ # Notice that single quotes (') are used so the <tt>#{id}</tt> part isn't evaluated until the callback
181
+ # is triggered. Also note that these inline callbacks can be stacked just like the regular ones:
143
182
  #
144
183
  # class Topic < ActiveRecord::Base
145
- # before_destroy 'self.class.delete_all "parent_id = #{id}"',
184
+ # before_destroy 'self.class.delete_all "parent_id = #{id}"',
146
185
  # 'puts "Evaluated after parents are destroyed"'
147
186
  # end
148
187
  #
149
- # == The after_find and after_initialize exceptions
188
+ # == The +after_find+ and +after_initialize+ exceptions
189
+ #
190
+ # Because +after_find+ and +after_initialize+ are called for each object found and instantiated by a finder,
191
+ # such as <tt>Base.find(:all)</tt>, we've had to implement a simple performance constraint (50% more speed
192
+ # on a simple test case). Unlike all the other callbacks, +after_find+ and +after_initialize+ will only be
193
+ # run if an explicit implementation is defined (<tt>def after_find</tt>). In that case, all of the
194
+ # callback types will be called.
195
+ #
196
+ # == <tt>before_validation*</tt> returning statements
197
+ #
198
+ # If the returning value of a +before_validation+ callback can be evaluated to +false+, the process will be
199
+ # aborted and <tt>Base#save</tt> will return +false+. If Base#save! is called it will raise a
200
+ # ActiveRecord::RecordInvalid exception. Nothing will be appended to the errors object.
201
+ #
202
+ # == Canceling callbacks
203
+ #
204
+ # If a <tt>before_*</tt> callback returns +false+, all the later callbacks and the associated action are
205
+ # cancelled. If an <tt>after_*</tt> callback returns +false+, all the later callbacks are cancelled.
206
+ # Callbacks are generally run in the order they are defined, with the exception of callbacks defined as
207
+ # methods on the model, which are called last.
208
+ #
209
+ # == Transactions
210
+ #
211
+ # The entire callback chain of a +save+, <tt>save!</tt>, or +destroy+ call runs
212
+ # within a transaction. That includes <tt>after_*</tt> hooks. If everything
213
+ # goes fine a COMMIT is executed once the chain has been completed.
214
+ #
215
+ # If a <tt>before_*</tt> callback cancels the action a ROLLBACK is issued. You
216
+ # can also trigger a ROLLBACK raising an exception in any of the callbacks,
217
+ # including <tt>after_*</tt> hooks. Note, however, that in that case the client
218
+ # needs to be aware of it because an ordinary +save+ will raise such exception
219
+ # instead of quietly returning +false+.
220
+ #
221
+ # == Debugging callbacks
222
+ #
223
+ # To list the methods and procs registered with a particular callback, append <tt>_callback_chain</tt> to
224
+ # the callback name that you wish to list and send that to your class from the Rails console:
225
+ #
226
+ # >> Topic.after_save_callback_chain
227
+ # => [#<ActiveSupport::Callbacks::Callback:0x3f6a448
228
+ # @method=#<Proc:0x03f9a42c@/Users/foo/bar/app/models/topic.rb:43>, kind:after_save, identifiernil,
229
+ # options{}]
150
230
  #
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.
155
231
  module Callbacks
156
- CALLBACKS = %w(
157
- after_find after_initialize before_save after_save before_create after_create before_update after_update before_validation
158
- after_validation before_validation_on_create after_validation_on_create before_validation_on_update
159
- after_validation_on_update before_destroy after_destroy
160
- )
232
+ extend ActiveSupport::Concern
161
233
 
162
- def self.append_features(base) #:nodoc:
163
- super
234
+ CALLBACKS = [
235
+ :after_initialize, :after_find, :after_touch, :before_validation, :after_validation,
236
+ :before_save, :around_save, :after_save, :before_create, :around_create,
237
+ :after_create, :before_update, :around_update, :after_update,
238
+ :before_destroy, :around_destroy, :after_destroy, :after_commit, :after_rollback
239
+ ]
164
240
 
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
172
- end
241
+ included do
242
+ extend ActiveModel::Callbacks
243
+ include ActiveModel::Validations::Callbacks
173
244
 
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
192
- end
193
-
194
- CALLBACKS.each { |cb| base.class_eval("def self.#{cb}(*methods) write_inheritable_array(\"#{cb}\", methods) end") }
245
+ define_model_callbacks :initialize, :find, :touch, :only => :after
246
+ define_model_callbacks :save, :create, :update, :destroy
195
247
  end
196
248
 
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
249
+ module ClassMethods
250
+ def method_added(meth)
251
+ super
252
+ if CALLBACKS.include?(meth.to_sym)
253
+ ActiveSupport::Deprecation.warn("Base##{meth} has been deprecated, please use Base.#{meth} :method instead", caller[0,1])
254
+ send(meth.to_sym, meth.to_sym)
255
+ end
203
256
  end
204
257
  end
205
258
 
206
- # Is called when the object was instantiated by one of the finders, like Base.find.
207
- # def after_find() end
208
-
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)
259
+ def destroy #:nodoc:
260
+ _run_destroy_callbacks { super }
215
261
  end
216
-
217
- # Is called _before_ Base.save (regardless of whether it's a create or update save).
218
- def before_save() end
219
262
 
220
- # Is called _after_ Base.save (regardless of whether it's a create or update save).
221
- def after_save() end
222
- def create_or_update_with_callbacks #:nodoc:
223
- callback(:before_save)
224
- create_or_update_without_callbacks
225
- callback(:after_save)
263
+ def touch(*) #:nodoc:
264
+ _run_touch_callbacks { super }
226
265
  end
227
266
 
228
- # Is called _before_ Base.save on new objects that haven't been saved yet (no record exists).
229
- def before_create() end
230
-
231
- # Is called _after_ Base.save on new objects that haven't been saved yet (no record exists).
232
- def after_create() end
233
- def create_with_callbacks #:nodoc:
234
- callback(:before_create)
235
- create_without_callbacks
236
- callback(:after_create)
237
- end
238
-
239
- # Is called _before_ Base.save on existing objects that has a record.
240
- def before_update() end
241
-
242
- # Is called _after_ Base.save on existing objects that has a record.
243
- def after_update() end
244
-
245
- def update_with_callbacks #:nodoc:
246
- callback(:before_update)
247
- update_without_callbacks
248
- callback(:after_update)
249
- end
250
-
251
- # Is called _before_ Validations.validate (which is part of the Base.save call).
252
- def before_validation() end
253
-
254
- # Is called _after_ Validations.validate (which is part of the Base.save call).
255
- def after_validation() end
256
-
257
- # Is called _before_ Validations.validate (which is part of the Base.save call) on new objects
258
- # that haven't been saved yet (no record exists).
259
- def before_validation_on_create() end
260
-
261
- # Is called _after_ Validations.validate (which is part of the Base.save call) on new objects
262
- # that haven't been saved yet (no record exists).
263
- def after_validation_on_create() end
264
-
265
- # Is called _before_ Validations.validate (which is part of the Base.save call) on
266
- # existing objects that has a record.
267
- def before_validation_on_update() end
268
-
269
- # Is called _after_ Validations.validate (which is part of the Base.save call) on
270
- # existing objects that has a record.
271
- def after_validation_on_update() end
272
-
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
276
-
277
- result = valid_without_callbacks
278
-
279
- callback(:after_validation)
280
- if new_record? then callback(:after_validation_on_create) else callback(:after_validation_on_update) end
281
-
282
- return result
267
+ def deprecated_callback_method(symbol) #:nodoc:
268
+ if respond_to?(symbol, true)
269
+ ActiveSupport::Deprecation.warn("Overwriting #{symbol} in your models has been deprecated, please use Base##{symbol} :method_name instead")
270
+ send(symbol)
271
+ end
283
272
  end
284
273
 
285
- # Is called _before_ Base.destroy.
286
- def before_destroy() end
274
+ private
287
275
 
288
- # Is called _after_ Base.destroy (and all the attributes have been frozen).
289
- def after_destroy() end
290
- def destroy_with_callbacks #:nodoc:
291
- callback(:before_destroy)
292
- destroy_without_callbacks
293
- callback(:after_destroy)
276
+ def create_or_update #:nodoc:
277
+ _run_save_callbacks { super }
294
278
  end
295
279
 
296
- def callback(callback_method) #:nodoc:
297
- run_callbacks(callback_method)
298
- send(callback_method)
299
- notify(callback_method)
280
+ def create #:nodoc:
281
+ _run_create_callbacks { super }
300
282
  end
301
283
 
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
- )
320
- end
321
- 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)
284
+ def update(*) #:nodoc:
285
+ _run_update_callbacks { super }
335
286
  end
336
287
  end
337
- end
288
+ end