\.)+[a-z]{2,})\z/i
record.errors[attribute] << (options[:message] || "is not an email")
end
end
end
class Person < ActiveRecord::Base
validates :email, :presence => true, :email => true
end
```
As shown in the example, you can also combine standard validations with your own custom validators.
### Custom Methods
You can also create methods that verify the state of your models and add messages to the +errors+ collection when they are invalid. You must then register these methods by using the +validate+ class method, passing in the symbols for the validation methods' names.
You can pass more than one symbol for each class method and the respective validations will be run in the same order as they were registered.
```ruby
class Invoice < ActiveRecord::Base
validate :expiration_date_cannot_be_in_the_past,
:discount_cannot_be_greater_than_total_value
def expiration_date_cannot_be_in_the_past
if !expiration_date.blank? and expiration_date < Date.today
errors.add(:expiration_date, "can't be in the past")
end
end
def discount_cannot_be_greater_than_total_value
if discount > total_value
errors.add(:discount, "can't be greater than total value")
end
end
end
```
By default such validations will run every time you call +valid?+. It is also possible to control when to run these custom validations by giving an +:on+ option to the +validate+ method, with either: +:create+ or +:update+.
```ruby
class Invoice < ActiveRecord::Base
validate :active_customer, :on => :create
def active_customer
errors.add(:customer_id, "is not active") unless customer.active?
end
end
```
You can even create your own validation helpers and reuse them in several different models. For example, an application that manages surveys may find it useful to express that a certain field corresponds to a set of choices:
```ruby
ActiveRecord::Base.class_eval do
def self.validates_as_choice(attr_name, n, options={})
validates attr_name, :inclusion => { { :in => 1..n }.merge!(options) }
end
end
```
Simply reopen +ActiveRecord::Base+ and define a class method like that. You'd typically put this code somewhere in +config/initializers+. You can use this helper like this:
```ruby
class Movie < ActiveRecord::Base
validates_as_choice :rating, 5
end
```
Working with Validation Errors
------------------------------
In addition to the +valid?+ and +invalid?+ methods covered earlier, Rails provides a number of methods for working with the +errors+ collection and inquiring about the validity of objects.
The following is a list of the most commonly used methods. Please refer to the +ActiveModel::Errors+ documentation for a list of all the available methods.
### +errors+
Returns an instance of the class +ActiveModel::Errors+ containing all errors. Each key is the attribute name and the value is an array of strings with all errors.
```ruby
class Person < ActiveRecord::Base
validates :name, :presence => true, :length => { :minimum => 3 }
end
person = Person.new
person.valid? # => false
person.errors
# => {:name => ["can't be blank", "is too short (minimum is 3 characters)"]}
person = Person.new(:name => "John Doe")
person.valid? # => true
person.errors # => []
```
### +errors[]+
+errors[]+ is used when you want to check the error messages for a specific attribute. It returns an array of strings with all error messages for the given attribute, each string with one error message. If there are no errors related to the attribute, it returns an empty array.
```ruby
class Person < ActiveRecord::Base
validates :name, :presence => true, :length => { :minimum => 3 }
end
person = Person.new(:name => "John Doe")
person.valid? # => true
person.errors[:name] # => []
person = Person.new(:name => "JD")
person.valid? # => false
person.errors[:name] # => ["is too short (minimum is 3 characters)"]
person = Person.new
person.valid? # => false
person.errors[:name]
# => ["can't be blank", "is too short (minimum is 3 characters)"]
```
### +errors.add+
The +add+ method lets you manually add messages that are related to particular attributes. You can use the +errors.full_messages+ or +errors.to_a+ methods to view the messages in the form they might be displayed to a user. Those particular messages get the attribute name prepended (and capitalized). +add+ receives the name of the attribute you want to add the message to, and the message itself.
```ruby
class Person < ActiveRecord::Base
def a_method_used_for_validation_purposes
errors.add(:name, "cannot contain the characters !@#%*()_-+=")
end
end
person = Person.create(:name => "!@#")
person.errors[:name]
# => ["cannot contain the characters !@#%*()_-+="]
person.errors.full_messages
# => ["Name cannot contain the characters !@#%*()_-+="]
```
Another way to do this is using +[]=+ setter
```ruby
class Person < ActiveRecord::Base
def a_method_used_for_validation_purposes
errors[:name] = "cannot contain the characters !@#%*()_-+="
end
end
person = Person.create(:name => "!@#")
person.errors[:name]
# => ["cannot contain the characters !@#%*()_-+="]
person.errors.to_a
# => ["Name cannot contain the characters !@#%*()_-+="]
```
### +errors[:base]+
You can add error messages that are related to the object's state as a whole, instead of being related to a specific attribute. You can use this method when you want to say that the object is invalid, no matter the values of its attributes. Since +errors[:base]+ is an array, you can simply add a string to it and it will be used as an error message.
```ruby
class Person < ActiveRecord::Base
def a_method_used_for_validation_purposes
errors[:base] << "This person is invalid because ..."
end
end
```
### +errors.clear+
The +clear+ method is used when you intentionally want to clear all the messages in the +errors+ collection. Of course, calling +errors.clear+ upon an invalid object won't actually make it valid: the +errors+ collection will now be empty, but the next time you call +valid?+ or any method that tries to save this object to the database, the validations will run again. If any of the validations fail, the +errors+ collection will be filled again.
```ruby
class Person < ActiveRecord::Base
validates :name, :presence => true, :length => { :minimum => 3 }
end
person = Person.new
person.valid? # => false
person.errors[:name]
# => ["can't be blank", "is too short (minimum is 3 characters)"]
person.errors.clear
person.errors.empty? # => true
p.save # => false
p.errors[:name]
# => ["can't be blank", "is too short (minimum is 3 characters)"]
```
### +errors.size+
The +size+ method returns the total number of error messages for the object.
```ruby
class Person < ActiveRecord::Base
validates :name, :presence => true, :length => { :minimum => 3 }
end
person = Person.new
person.valid? # => false
person.errors.size # => 2
person = Person.new(:name => "Andrea", :email => "andrea@example.com")
person.valid? # => true
person.errors.size # => 0
```
Displaying Validation Errors in the View
----------------------------------------
"DynamicForm":https://github.com/joelmoss/dynamic_form provides helpers to display the error messages of your models in your view templates.
You can install it as a gem by adding this line to your Gemfile:
```ruby
gem "dynamic_form"
```
Now you will have access to the two helper methods +error_messages+ and +error_messages_for+ in your view templates.
### +error_messages+ and +error_messages_for+
When creating a form with the +form_for+ helper, you can use the +error_messages+ method on the form builder to render all failed validation messages for the current model instance.
```ruby
class Product < ActiveRecord::Base
validates :description, :value, :presence => true
validates :value, :numericality => true, :allow_nil => true
end
```
```erb
<%= form_for(@product) do |f| %>
<%= f.error_messages %>
<%= f.label :description %>
<%= f.text_field :description %>
<%= f.label :value %>
<%= f.text_field :value %>
<%= f.submit "Create" %>
<% end %>
```
If you submit the form with empty fields, the result will be similar to the one shown below:
!images/error_messages.png(Error messages)!
NOTE: The appearance of the generated HTML will be different from the one shown, unless you have used scaffolding. See "Customizing the Error Messages CSS":#customizing-error-messages-css.
You can also use the +error_messages_for+ helper to display the error messages of a model assigned to a view template. It is very similar to the previous example and will achieve exactly the same result.
```erb
<%= error_messages_for :product %>
```
The displayed text for each error message will always be formed by the capitalized name of the attribute that holds the error, followed by the error message itself.
Both the +form.error_messages+ and the +error_messages_for+ helpers accept options that let you customize the +div+ element that holds the messages, change the header text, change the message below the header, and specify the tag used for the header element. For example,
```erb
<%= f.error_messages :header_message => "Invalid product!",
:message => "You'll need to fix the following fields:",
:header_tag => :h3 %>
```
results in:
!images/customized_error_messages.png(Customized error messages)!
If you pass +nil+ in any of these options, the corresponding section of the +div+ will be discarded.
### Customizing the Error Messages CSS
The selectors used to customize the style of error messages are:
* +.field_with_errors+ - Style for the form fields and labels with errors.
* +#error_explanation+ - Style for the +div+ element with the error messages.
* +#error_explanation h2+ - Style for the header of the +div+ element.
* +#error_explanation p+ - Style for the paragraph holding the message that appears right below the header of the +div+ element.
* +#error_explanation ul li+ - Style for the list items with individual error messages.
If scaffolding was used, file +app/assets/stylesheets/scaffolds.css.scss+ will have been generated automatically. This file defines the red-based styles you saw in the examples above.
The name of the class and the id can be changed with the +:class+ and +:id+ options, accepted by both helpers.
### Customizing the Error Messages HTML
By default, form fields with errors are displayed enclosed by a +div+ element with the +field_with_errors+ CSS class. However, it's possible to override that.
The way form fields with errors are treated is defined by +ActionView::Base.field_error_proc+. This is a +Proc+ that receives two parameters:
* A string with the HTML tag
* An instance of +ActionView::Helpers::InstanceTag+.
Below is a simple example where we change the Rails behavior to always display the error messages in front of each of the form fields in error. The error messages will be enclosed by a +span+ element with a +validation-error+ CSS class. There will be no +div+ element enclosing the +input+ element, so we get rid of that red border around the text field. You can use the +validation-error+ CSS class to style it anyway you want.
```ruby
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
errors = Array(instance.error_message).join(',')
%(#{html_tag} #{errors}).html_safe
end
```
The result looks like the following:
!images/validation_error_messages.png(Validation error messages)!
Callbacks Overview
------------------
Callbacks are methods that get called at certain moments of an object's life cycle. With callbacks it is possible to write code that will run whenever an Active Record object is created, saved, updated, deleted, validated, or loaded from the database.
### Callback Registration
In order to use the available callbacks, you need to register them. You can implement the callbacks as ordinary methods and use a macro-style class method to register them as callbacks:
```ruby
class User < ActiveRecord::Base
validates :login, :email, :presence => true
before_validation :ensure_login_has_a_value
protected
def ensure_login_has_a_value
if login.nil?
self.login = email unless email.blank?
end
end
end
```
The macro-style class methods can also receive a block. Consider using this style if the code inside your block is so short that it fits in a single line:
```ruby
class User < ActiveRecord::Base
validates :login, :email, :presence => true
before_create do |user|
user.name = user.login.capitalize if user.name.blank?
end
end
```
It is considered good practice to declare callback methods as protected or private. If left public, they can be called from outside of the model and violate the principle of object encapsulation.
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:
### Creating an Object
* +before_validation+
* +after_validation+
* +before_save+
* +around_save+
* +before_create+
* +around_create+
* +after_create+
* +after_save+
### Updating an Object
* +before_validation+
* +after_validation+
* +before_save+
* +around_save+
* +before_update+
* +around_update+
* +after_update+
* +after_save+
### Destroying an Object
* +before_destroy+
* +around_destroy+
* +after_destroy+
WARNING. +after_save+ runs both on create and update, but always _after_ the more specific callbacks +after_create+ and +after_update+, no matter the order in which the macro calls were executed.
### +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. +after_find+ is called before +after_initialize+ if both are defined.
The +after_initialize+ and +after_find+ callbacks have no +before_*+ counterparts, but they can be registered just like the other Active Record callbacks.
```ruby
class User < ActiveRecord::Base
after_initialize do |user|
puts "You have initialized an object!"
end
after_find do |user|
puts "You have found an object!"
end
end
>> User.new
You have initialized an object!
=> #
>> User.first
You have found an object!
You have initialized an object!
=> #
```
Running Callbacks
-----------------
The following methods trigger callbacks:
* +create+
* +create!+
* +decrement!+
* +destroy+
* +destroy_all+
* +increment!+
* +save+
* +save!+
* +save(:validate => 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_all_by_attribute+
* +find_by_attribute+
* +find_by_attribute!+
* +find_by_sql+
* +last+
The +after_initialize+ callback is triggered every time a new object of the class is initialized.
Skipping Callbacks
------------------
Just as with validations, it is 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+
* +increment+
* +increment_counter+
* +toggle+
* +touch+
* +update_column+
* +update_columns+
* +update_all+
* +update_counters+
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.
The whole callback chain is wrapped in a transaction. If any before callback method returns exactly +false+ or raises an exception, the execution chain gets halted and a ROLLBACK is issued; after callbacks can only accomplish that by raising an exception.
WARNING. Raising an arbitrary exception may break code that expects +save+ and its friends not to fail like that. The +ActiveRecord::Rollback+ exception is thought precisely to tell Active Record a rollback is going on. That one is internally captured but not reraised.
Relational Callbacks
--------------------
Callbacks work through model relationships, and can even be defined by them. Suppose an example where a user has many posts. A user's posts should be destroyed if the user is destroyed. Let's 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.posts.create!
=> #
>> user.destroy
Post destroyed
=> #
```
Conditional Callbacks
---------------------
As with validations, we can also make the calling of a callback method conditional on the satisfaction of a given predicate. We can do this using the +:if+ and +:unless+ options, which can take a symbol, a string, a +Proc+ or an +Array+. You may use the +:if+ option when you want to specify under which conditions the callback *should* be called. If you want to specify the conditions under which the callback *should not* be called, then you may use the +:unless+ option.
### Using +:if+ and +:unless+ with a +Symbol+
You can associate the +:if+ and +:unless+ options with a symbol corresponding to the name of a predicate method that will get called right before the callback. When using the +:if+ option, the callback won't be executed if the predicate method returns false; when using the +:unless+ option, the callback won't be executed if the predicate method returns true. This is the most common option. Using this form of registration it is also possible to register several different predicates that should be called to check if the callback should be executed.
```ruby
class Order < ActiveRecord::Base
before_save :normalize_card_number, :if => :paid_with_card?
end
```
### Using +:if+ and +:unless+ with a String
You can also use a string that will be evaluated using +eval+ and hence needs to contain valid Ruby code. You should use this option only when the string represents a really short condition:
```ruby
class Order < ActiveRecord::Base
before_save :normalize_card_number, :if => "paid_with_card?"
end
```
### Using +:if+ and +:unless+ with a +Proc+
Finally, it is possible to associate +:if+ and +:unless+ with a +Proc+ object. This option is best suited when writing short validation methods, usually one-liners:
```ruby
class Order < ActiveRecord::Base
before_save :normalize_card_number,
:if => Proc.new { |order| order.paid_with_card? }
end
```
### Multiple Conditions for Callbacks
When writing conditional callbacks, it is possible to mix both +:if+ and +:unless+ in the same callback declaration:
```ruby
class Comment < ActiveRecord::Base
after_create :send_email_to_author, :if => :author_wants_emails?,
:unless => Proc.new { |comment| comment.post.ignore_comments? }
end
```
Callback Classes
----------------
Sometimes the callback methods that you'll write will be useful enough to be reused by other models. Active Record makes it possible to create classes that encapsulate the callback methods, so it becomes very easy to reuse them.
Here's an example where we create a class with an +after_destroy+ callback for a +PictureFile+ model:
```ruby
class PictureFileCallbacks
def after_destroy(picture_file)
if File.exists?(picture_file.filepath)
File.delete(picture_file.filepath)
end
end
end
```
When declared inside a class, as above, the callback methods will receive the model object as a parameter. We can now use the callback class in the model:
```ruby
class PictureFile < ActiveRecord::Base
after_destroy PictureFileCallbacks.new
end
```
Note that we needed to instantiate a new +PictureFileCallbacks+ object, since we declared our callback as an instance method. This is particularly useful if the callbacks make use of the state of the instantiated object. Often, however, it will make more sense to declare the callbacks as class methods:
```ruby
class PictureFileCallbacks
def self.after_destroy(picture_file)
if File.exists?(picture_file.filepath)
File.delete(picture_file.filepath)
end
end
end
```
If the callback method is declared this way, it won't be necessary to instantiate a +PictureFileCallbacks+ object.
```ruby
class PictureFile < ActiveRecord::Base
after_destroy PictureFileCallbacks
end
```
You can declare as many callbacks as you want inside your callback classes.
Observers
---------
Observers are similar to callbacks, but with important differences. Whereas callbacks can pollute a model with code that isn't directly related to its purpose, observers allow you to add the same functionality without changing the code of the model. For example, it could be argued that a +User+ model should not include code to send registration confirmation emails. Whenever you use callbacks with code that isn't directly related to your model, you may want to consider creating an observer instead.
### Creating Observers
For example, imagine a +User+ model where we want to send an email every time a new user is created. Because sending emails is not directly related to our model's purpose, we should create an observer to contain the code implementing this functionality.
```bash
$ rails generate observer User
```
generates +app/models/user_observer.rb+ containing the observer class +UserObserver+:
```ruby
class UserObserver < ActiveRecord::Observer
end
```
You may now add methods to be called at the desired occasions:
```ruby
class UserObserver < ActiveRecord::Observer
def after_create(model)
# code to send confirmation email...
end
end
```
As with callback classes, the observer's methods receive the observed model as a parameter.
### Registering Observers
Observers are conventionally placed inside of your +app/models+ directory and registered in your application's +config/application.rb+ file. For example, the +UserObserver+ above would be saved as +app/models/user_observer.rb+ and registered in +config/application.rb+ this way:
```ruby
# Activate observers that should always be running.
config.active_record.observers = :user_observer
```
As usual, settings in +config/environments+ take precedence over those in +config/application.rb+. So, if you prefer that an observer doesn't run in all environments, you can simply register it in a specific environment instead.
### Sharing Observers
By default, Rails will simply strip "Observer" from an observer's name to find the model it should observe. However, observers can also be used to add behavior to more than one model, and thus it is possible to explicitly specify the models that our observer should observe:
```ruby
class MailerObserver < ActiveRecord::Observer
observe :registration, :user
def after_create(model)
# code to send confirmation email...
end
end
```
In this example, the +after_create+ method will be called whenever a +Registration+ or +User+ is created. Note that this new +MailerObserver+ would also need to be registered in +config/application.rb+ in order to take effect:
```ruby
# Activate observers that should always be running.
config.active_record.observers = :mailer_observer
```
Transaction Callbacks
---------------------
There are two additional callbacks that are triggered by the completion of a database transaction: +after_commit+ and +after_rollback+. These callbacks are very similar to the +after_save+ callback except that they don't execute until after database changes have either been committed or rolled back. They are most useful when your active record models need to interact with external systems which are not part of the database transaction.
Consider, for example, the previous example where the +PictureFile+ model needs to delete a file after the corresponding record is destroyed. If anything raises an exception after the +after_destroy+ callback is called and the transaction rolls back, the file will have been deleted and the model will be left in an inconsistent state. For example, suppose that +picture_file_2+ in the code below is not valid and the +save!+ method raises an error.
```ruby
PictureFile.transaction do
picture_file_1.destroy
picture_file_2.save!
end
```
By using the +after_commit+ callback we can account for this case.
```ruby
class PictureFile < ActiveRecord::Base
attr_accessor :delete_file
after_destroy do |picture_file|
picture_file.delete_file = picture_file.filepath
end
after_commit do |picture_file|
if picture_file.delete_file && File.exist?(picture_file.delete_file)
File.delete(picture_file.delete_file)
picture_file.delete_file = nil
end
end
end
```
The +after_commit+ and +after_rollback+ callbacks are guaranteed to be called for all models created, updated, or destroyed within a transaction block. If any exceptions are raised within one of these callbacks, they will be ignored so that they don't interfere with the other callbacks. As such, if your callback code could raise an exception, you'll need to rescue it and handle it appropriately within the callback.