diff options
Diffstat (limited to 'railties')
-rw-r--r-- | railties/guides/source/activerecord_validations_callbacks.textile | 197 |
1 files changed, 143 insertions, 54 deletions
diff --git a/railties/guides/source/activerecord_validations_callbacks.textile b/railties/guides/source/activerecord_validations_callbacks.textile index b5edf8c6d4..a890794fa1 100644 --- a/railties/guides/source/activerecord_validations_callbacks.textile +++ b/railties/guides/source/activerecord_validations_callbacks.textile @@ -722,12 +722,7 @@ The way form fields with errors are treated is defined by the +ActionView::Base. h3. Callbacks Overview -Callbacks are methods that get called at certain moments of an object's lifecycle. With callbacks it's possible to write code that will run whenever an Active Record object is created, saved, updated, deleted or loaded from the database. - -# TODO discuss what does/doesn't trigger callbacks, like we did in the validations section (e.g. destroy versus delete). -# Consider moving up to the (new) intro overview section, before getting into details. -# http://api.rubyonrails.org/classes/ActiveRecord/Base.html#M002220 -# http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html +Callbacks are methods that get called at certain moments of an object's lifecycle. With callbacks it's possible to write code that will run whenever an Active Record object is created, saved, updated, deleted, validated, or loaded from the database. h4. Callback Registration @@ -760,6 +755,147 @@ end It's considered good practice to declare callback methods as being protected or private. If left public, they can be called from outside of the model and violate the principle of object encapsulation. +h3. Available Callbacks + +Here is a list with all the available Active Record callbacks, listed in the same order in which they will get called during the respective operations: + +h4. Creating and/or Updating an Object + +* +before_validation+ +* +after_validation+ +* +before_save+ +* INSERT OR UPDATE OPERATION +* +after_save+ + +h4. Creating an Object + +* +before_validation_on_create+ +* +after_validation_on_create+ +* +before_create+ +* INSERT OPERATION +* +after_create+ + +h4. Updating an Object + +* +before_validation_on_update+ +* +after_validation_on_update+ +* +before_update+ +* UPDATE OPERATION +* +after_update+ + +h4. Destroying an Object + +* +before_destroy+ +* DELETE OPERATION +* +after_destroy+ + +h4. after_initialize and after_find + +The +after_initialize+ callback will be called whenever an Active Record object is instantiated, either by directly using +new+ or when a record is loaded from the database. It can be useful to avoid the need to directly override your Active Record +initialize+ method. + +The +after_find+ callback will be called whenever Active Record loads a record from the database. When used together with +after_initialize+ it will run first, since Active Record will first read the record from the database and them create the model object that will hold it. + +The +after_initialize+ and +after_find+ callbacks are a bit different from the others, since the only way to register those callbacks is by defining them as methods. If you try to register +after_initialize+ or +after_find+ using macro-style class methods, they will just be ignored. This behaviour is due to performance reasons, since +after_initialize+ and +after_find+ will both be called for each record found in the database, significantly slowing down the queries. + +<ruby> +class User < ActiveRecord::Base + def after_initialize + puts "You have initialized an object!" + end + + def after_find + puts "You have found an object!" + end +end + +>> User.new +You have initialized an object! +=> #<User id: nil> + +>> User.first +You have found an object! +You have initialized an object! +=> #<User id: 1> +</ruby> + +h3. Running Callbacks + +The following methods trigger callbacks: + +* +create+ +* +create!+ +* +decrement!+ +* +destroy+ +* +destroy_all+ +* +increment!+ +* +save+ +* +save!+ +* +save(false)+ +* +toggle!+ +* +update+ +* +update_attribute+ +* +update_attributes+ +* +update_attributes!+ +* +valid?+ + +Additionally, the +after_find+ callback is triggered by the following finder methods: + +* +all+ +* +first+ +* +find+ +* +find_by_<em>attribute</em>+ +* +find_by_<em>attribute</em>!+ +* +last+ + +The +after_initialize+ callback is triggered every time a new object of the class is initialized. + +h3. Skipping Callbacks + +Just as with validations, it's also possible to skip callbacks. These methods should be used with caution, however, because important business rules and application logic may be kept in callbacks. Bypassing them without understanding the potential implications may lead to invalid data. + +* +decrement+ +* +decrement_counter+ +* +delete+ +* +delete_all+ +* +find_by_sql+ +* +increment+ +* +increment_counter+ +* +toggle+ +* +update_all+ +* +update_counters+ + +h3. Halting Execution + +As you start registering new callbacks for your models, they will be queued for execution. This queue will include all your model's validations, the registered callbacks, and the database operation to be executed. + +If any callback methods return +false+ or raise an exception, the execution chain will be halted and the desired operation will not complete. This is because the whole callback chain is wrapped in a transaction, and raising an exception or returning +false+ fires a database ROLLBACK. + +h3. Relational Callbacks + +Callbacks work through model relationships, and can even be defined by them. Let's take an example where a User has_many Posts. In our example, a User's Posts should be destroyed if the User is destroyed. So, we'll add an after_destroy callback to the User model by way of its relationship to the Post model. + +<ruby> +class User < ActiveRecord::Base + has_many :posts, :dependent => :destroy +end + +class Post < ActiveRecord::Base + after_destroy :log_destroy_action + + def log_destroy_action + puts 'Post destroyed' + end +end + +>> user = User.first +=> #<User id: 1> +>> user.posts.create! +=> #<Post id: 1, user_id: 1> +>> user.destroy +Post destroyed +=> #<User id: 1> +</ruby> + h3. Conditional Callbacks Like in validations, we can also make our callbacks conditional, calling then only when a given predicate is satisfied. You can do that by using the +:if+ and +:unless+ options, which can take a symbol, a string or a Ruby Proc. You may use the +:if+ option when you want to specify when the callback *should* get called. If you want to specify when the callback *should not* be called, then you may use the +:unless+ option. @@ -806,54 +942,6 @@ class Comment < ActiveRecord::Base end </ruby> -h3. Available Callbacks - -Here is a list with all the available Active Record callbacks, listed in the same order in which they will get called during the respective operations. - -h4. Creating and/or Updating an Object - -* +before_validation+ -* +after_validation+ -* +before_save+ -* INSERT OR UPDATE OPERATION -* +after_save+ - -h4. Creating an Object - -* +before_validation_on_create+ -* +after_validation_on_create+ -* +before_create+ -* INSERT OPERATION -* +after_create+ - -h4. Updating an Object - -* +before_validation_on_update+ -* +after_validation_on_update+ -* +before_update+ -* UPDATE OPERATION -* +after_update+ - -h4. Destroying an Object - -* +before_destroy+ -* DELETE OPERATION -* +after_destroy+ - -CAUTION: The +before_destroy+ and +after_destroy+ callbacks will only be called if you delete the model using either the +destroy+ instance method or one of the +destroy+ or +destroy_all+ class methods of your Active Record class. If you use +delete+ or +delete_all+ no callback operations will run, since Active Record will not instantiate any objects, accessing the records to be deleted directly in the database. - -h4. after_initialize and after_find - -The +after_initialize+ callback will be called whenever an Active Record object is instantiated, either by directly using +new+ or when a record is loaded from the database. It can be useful to avoid the need to directly override your Active Record +initialize+ method. - -The +after_find+ callback will be called whenever Active Record loads a record from the database. When used together with +after_initialize+ it will run first, since Active Record will first read the record from the database and them create the model object that will hold it. - -The +after_initialize+ and +after_find+ callbacks are a bit different from the others, since the only way to register those callbacks is by defining them as methods. If you try to register +after_initialize+ or +after_find+ using macro-style class methods, they will just be ignored. This behaviour is due to performance reasons, since +after_initialize+ and +after_find+ will both be called for each record found in the database, significantly slowing down the queries. - -h3. Halting Execution - -As you start registering new callbacks for your models, they will be queued for execution. This queue will include all your model's validations, the registered callbacks and the database operation to be executed. However, if at any moment one of the +before_create+, +before_save+, +before_update+ or +before_destroy+ callback methods returns a boolean +false+ (not +nil+) value or raise and exception, this execution chain will be halted and the desired operation will not complete: your model will not get persisted in the database, or your records will not get deleted and so on. It's because the whole callback chain is wrapped in a transaction, so raising an exception or returning +false+ fires a database ROLLBACK. - h3. Callback Classes Sometimes the callback methods that you'll write will be useful enough to be reused at other models. Active Record makes it possible to create classes that encapsulate the callback methods, so it becomes very easy to reuse them. @@ -950,6 +1038,7 @@ h3. Changelog "Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213/tickets/26-active-record-validations-and-callbacks +* March 7, 2009: Callbacks revision by Trevor Turk * February 10, 2009: Observers revision by Trevor Turk * February 5, 2009: Initial revision by Trevor Turk * January 9, 2009: Initial version by "Cássio Marques":credits.html#cmarques |