aboutsummaryrefslogtreecommitdiffstats
path: root/guides/source/active_record_validations.md
diff options
context:
space:
mode:
Diffstat (limited to 'guides/source/active_record_validations.md')
-rw-r--r--guides/source/active_record_validations.md963
1 files changed, 963 insertions, 0 deletions
diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md
new file mode 100644
index 0000000000..2e2f0e4ea9
--- /dev/null
+++ b/guides/source/active_record_validations.md
@@ -0,0 +1,963 @@
+Active Record Validations
+=========================
+
+This guide teaches you how to validate the state of objects before they go into
+the database using Active Record's validations feature.
+
+After reading this guide, you will know:
+
+* How to use the built-in Active Record validation helpers.
+* How to create your own custom validation methods.
+* How to work with the error messages generated by the validation process.
+
+--------------------------------------------------------------------------------
+
+Validations Overview
+--------------------
+
+Here's an example of a very simple validation:
+
+```ruby
+class Person < ActiveRecord::Base
+ validates :name, presence: true
+end
+
+Person.create(name: "John Doe").valid? # => true
+Person.create(name: nil).valid? # => false
+```
+
+As you can see, our validation lets us know that our `Person` is not valid
+without a `name` attribute. The second `Person` will not be persisted to the
+database.
+
+Before we dig into more details, let's talk about how validations fit into the
+big picture of your application.
+
+### Why Use Validations?
+
+Validations are used to ensure that only valid data is saved into your
+database. For example, it may be important to your application to ensure that
+every user provides a valid email address and mailing address. Model-level
+validations are the best way to ensure that only valid data is saved into your
+database. They are database agnostic, cannot be bypassed by end users, and are
+convenient to test and maintain. Rails makes them easy to use, provides
+built-in helpers for common needs, and allows you to create your own validation
+methods as well.
+
+There are several other ways to validate data before it is saved into your
+database, including native database constraints, client-side validations,
+controller-level validations. Here's a summary of the pros and cons:
+
+* Database constraints and/or stored procedures make the validation mechanisms
+ database-dependent and can make testing and maintenance more difficult.
+ However, if your database is used by other applications, it may be a good
+ idea to use some constraints at the database level. Additionally,
+ database-level validations can safely handle some things (such as uniqueness
+ in heavily-used tables) that can be difficult to implement otherwise.
+* Client-side validations can be useful, but are generally unreliable if used
+ alone. If they are implemented using JavaScript, they may be bypassed if
+ JavaScript is turned off in the user's browser. However, if combined with
+ other techniques, client-side validation can be a convenient way to provide
+ users with immediate feedback as they use your site.
+* Controller-level validations can be tempting to use, but often become
+ unwieldy and difficult to test and maintain. Whenever possible, it's a good
+ idea to keep your controllers skinny, as it will make your application a
+ pleasure to work with in the long run.
+
+Choose these in certain, specific cases. It's the opinion of the Rails team
+that model-level validations are the most appropriate in most circumstances.
+
+### When Does Validation Happen?
+
+There are two kinds of Active Record objects: those that correspond to a row
+inside your database and those that do not. When you create a fresh object, for
+example using the `new` method, that object does not belong to the database
+yet. Once you call `save` upon that object it will be saved into the
+appropriate database table. Active Record uses the `new_record?` instance
+method to determine whether an object is already in the database or not.
+Consider the following simple Active Record class:
+
+```ruby
+class Person < ActiveRecord::Base
+end
+```
+
+We can see how it works by looking at some `rails console` output:
+
+```ruby
+$ rails console
+>> p = Person.new(name: "John Doe")
+=> #<Person id: nil, name: "John Doe", created_at: nil, updated_at: nil>
+>> p.new_record?
+=> true
+>> p.save
+=> true
+>> p.new_record?
+=> false
+```
+
+Creating and saving a new record will send an SQL `INSERT` operation to the
+database. Updating an existing record will send an SQL `UPDATE` operation
+instead. Validations are typically run before these commands are sent to the
+database. If any validations fail, the object will be marked as invalid and
+Active Record will not perform the `INSERT` or `UPDATE` operation. This avoids
+storing an invalid object in the database. You can choose to have specific
+validations run when an object is created, saved, or updated.
+
+CAUTION: There are many ways to change the state of an object in the database.
+Some methods will trigger validations, but some will not. This means that it's
+possible to save an object in the database in an invalid state if you aren't
+careful.
+
+The following methods trigger validations, and will save the object to the
+database only if the object is valid:
+
+* `create`
+* `create!`
+* `save`
+* `save!`
+* `update`
+* `update_attributes`
+* `update_attributes!`
+
+The bang versions (e.g. `save!`) raise an exception if the record is invalid.
+The non-bang versions don't: `save` and `update_attributes` return `false`,
+`create` and `update` just return the objects.
+
+### Skipping Validations
+
+The following methods skip validations, and will save the object to the
+database regardless of its validity. They should be used with caution.
+
+* `decrement!`
+* `decrement_counter`
+* `increment!`
+* `increment_counter`
+* `toggle!`
+* `touch`
+* `update_all`
+* `update_attribute`
+* `update_column`
+* `update_columns`
+* `update_counters`
+
+Note that `save` also has the ability to skip validations if passed `validate:
+false` as argument. This technique should be used with caution.
+
+* `save(validate: false)`
+
+### `valid?` and `invalid?`
+
+To verify whether or not an object is valid, Rails uses the `valid?` method.
+You can also use this method on your own. `valid?` triggers your validations
+and returns true if no errors were found in the object, and false otherwise.
+As you saw above:
+
+```ruby
+class Person < ActiveRecord::Base
+ validates :name, presence: true
+end
+
+Person.create(name: "John Doe").valid? # => true
+Person.create(name: nil).valid? # => false
+```
+
+After Active Record has performed validations, any errors found can be accessed
+through the `errors` instance method, which returns a collection of errors. By
+definition, an object is valid if this collection is empty after running
+validations.
+
+Note that an object instantiated with `new` will not report errors even if it's
+technically invalid, because validations are not run when using `new`.
+
+```ruby
+class Person < ActiveRecord::Base
+ validates :name, presence: true
+end
+
+>> p = Person.new
+#=> #<Person id: nil, name: nil>
+>> p.errors
+#=> {}
+
+>> p.valid?
+#=> false
+>> p.errors
+#=> {name:["can't be blank"]}
+
+>> p = Person.create
+#=> #<Person id: nil, name: nil>
+>> p.errors
+#=> {name:["can't be blank"]}
+
+>> p.save
+#=> false
+
+>> p.save!
+#=> ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
+
+>> Person.create!
+#=> ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
+```
+
+`invalid?` is simply the inverse of `valid?`. It triggers your validations,
+returning true if any errors were found in the object, and false otherwise.
+
+### `errors[]`
+
+To verify whether or not a particular attribute of an object is valid, you can
+use `errors[:attribute]`. It returns an array of all the errors for
+`:attribute`. If there are no errors on the specified attribute, an empty array
+is returned.
+
+This method is only useful _after_ validations have been run, because it only
+inspects the errors collection and does not trigger validations itself. It's
+different from the `ActiveRecord::Base#invalid?` method explained above because
+it doesn't verify the validity of the object as a whole. It only checks to see
+whether there are errors found on an individual attribute of the object.
+
+```ruby
+class Person < ActiveRecord::Base
+ validates :name, presence: true
+end
+
+>> Person.new.errors[:name].any? # => false
+>> Person.create.errors[:name].any? # => true
+```
+
+We'll cover validation errors in greater depth in the [Working with Validation
+Errors](#working-with-validation-errors) section. For now, let's turn to the
+built-in validation helpers that Rails provides by default.
+
+Validation Helpers
+------------------
+
+Active Record offers many pre-defined validation helpers that you can use
+directly inside your class definitions. These helpers provide common validation
+rules. Every time a validation fails, an error message is added to the object's
+`errors` collection, and this message is associated with the attribute being
+validated.
+
+Each helper accepts an arbitrary number of attribute names, so with a single
+line of code you can add the same kind of validation to several attributes.
+
+All of them accept the `:on` and `:message` options, which define when the
+validation should be run and what message should be added to the `errors`
+collection if it fails, respectively. The `:on` option takes one of the values
+`:save` (the default), `:create` or `:update`. There is a default error
+message for each one of the validation helpers. These messages are used when
+the `:message` option isn't specified. Let's take a look at each one of the
+available helpers.
+
+### `acceptance`
+
+This method validates that a checkbox on the user interface was checked when a
+form was submitted. This is typically used when the user needs to agree to your
+application's terms of service, confirm reading some text, or any similar
+concept. This validation is very specific to web applications and this
+'acceptance' does not need to be recorded anywhere in your database (if you
+don't have a field for it, the helper will just create a virtual attribute).
+
+```ruby
+class Person < ActiveRecord::Base
+ validates :terms_of_service, acceptance: true
+end
+```
+
+The default error message for this helper is _"must be accepted"_.
+
+It can receive an `:accept` option, which determines the value that will be
+considered acceptance. It defaults to "1" and can be easily changed.
+
+```ruby
+class Person < ActiveRecord::Base
+ validates :terms_of_service, acceptance: { accept: 'yes' }
+end
+```
+
+### `validates_associated`
+
+You should use this helper when your model has associations with other models
+and they also need to be validated. When you try to save your object, `valid?`
+will be called upon each one of the associated objects.
+
+```ruby
+class Library < ActiveRecord::Base
+ has_many :books
+ validates_associated :books
+end
+```
+
+This validation will work with all of the association types.
+
+CAUTION: Don't use `validates_associated` on both ends of your associations.
+They would call each other in an infinite loop.
+
+The default error message for `validates_associated` is _"is invalid"_. Note
+that each associated object will contain its own `errors` collection; errors do
+not bubble up to the calling model.
+
+### `confirmation`
+
+You should use this helper when you have two text fields that should receive
+exactly the same content. For example, you may want to confirm an email address
+or a password. This validation creates a virtual attribute whose name is the
+name of the field that has to be confirmed with "_confirmation" appended.
+
+```ruby
+class Person < ActiveRecord::Base
+ validates :email, confirmation: true
+end
+```
+
+In your view template you could use something like
+
+```erb
+<%= text_field :person, :email %>
+<%= text_field :person, :email_confirmation %>
+```
+
+This check is performed only if `email_confirmation` is not `nil`. To require
+confirmation, make sure to add a presence check for the confirmation attribute
+(we'll take a look at `presence` later on this guide):
+
+```ruby
+class Person < ActiveRecord::Base
+ validates :email, confirmation: true
+ validates :email_confirmation, presence: true
+end
+```
+
+The default error message for this helper is _"doesn't match confirmation"_.
+
+### `exclusion`
+
+This helper validates that the attributes' values are not included in a given
+set. In fact, this set can be any enumerable object.
+
+```ruby
+class Account < ActiveRecord::Base
+ validates :subdomain, exclusion: { in: %w(www us ca jp),
+ message: "Subdomain %{value} is reserved." }
+end
+```
+
+The `exclusion` helper has an option `:in` that receives the set of values that
+will not be accepted for the validated attributes. The `:in` option has an
+alias called `:within` that you can use for the same purpose, if you'd like to.
+This example uses the `:message` option to show how you can include the
+attribute's value.
+
+The default error message is _"is reserved"_.
+
+### `format`
+
+This helper validates the attributes' values by testing whether they match a
+given regular expression, which is specified using the `:with` option.
+
+```ruby
+class Product < ActiveRecord::Base
+ validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/,
+ message: "Only letters allowed" }
+end
+```
+
+The default error message is _"is invalid"_.
+
+### `inclusion`
+
+This helper validates that the attributes' values are included in a given set.
+In fact, this set can be any enumerable object.
+
+```ruby
+class Coffee < ActiveRecord::Base
+ validates :size, inclusion: { in: %w(small medium large),
+ message: "%{value} is not a valid size" }
+end
+```
+
+The `inclusion` helper has an option `:in` that receives the set of values that
+will be accepted. The `:in` option has an alias called `:within` that you can
+use for the same purpose, if you'd like to. The previous example uses the
+`:message` option to show how you can include the attribute's value.
+
+The default error message for this helper is _"is not included in the list"_.
+
+### `length`
+
+This helper validates the length of the attributes' values. It provides a
+variety of options, so you can specify length constraints in different ways:
+
+```ruby
+class Person < ActiveRecord::Base
+ validates :name, length: { minimum: 2 }
+ validates :bio, length: { maximum: 500 }
+ validates :password, length: { in: 6..20 }
+ validates :registration_number, length: { is: 6 }
+end
+```
+
+The possible length constraint options are:
+
+* `:minimum` - The attribute cannot have less than the specified length.
+* `:maximum` - The attribute cannot have more than the specified length.
+* `:in` (or `:within`) - The attribute length must be included in a given
+ interval. The value for this option must be a range.
+* `:is` - The attribute length must be equal to the given value.
+
+The default error messages depend on the type of length validation being
+performed. You can personalize these messages using the `:wrong_length`,
+`:too_long`, and `:too_short` options and `%{count}` as a placeholder for the
+number corresponding to the length constraint being used. You can still use the
+`:message` option to specify an error message.
+
+```ruby
+class Person < ActiveRecord::Base
+ validates :bio, length: { maximum: 1000,
+ too_long: "%{count} characters is the maximum allowed" }
+end
+```
+
+This helper counts characters by default, but you can split the value in a
+different way using the `:tokenizer` option:
+
+```ruby
+class Essay < ActiveRecord::Base
+ validates :content, length: {
+ minimum: 300,
+ maximum: 400,
+ tokenizer: lambda { |str| str.scan(/\w+/) },
+ too_short: "must have at least %{count} words",
+ too_long: "must have at most %{count} words"
+ }
+end
+```
+
+Note that the default error messages are plural (e.g., "is too short (minimum
+is %{count} characters)"). For this reason, when `:minimum` is 1 you should
+provide a personalized message or use `validates_presence_of` instead. When
+`:in` or `:within` have a lower limit of 1, you should either provide a
+personalized message or call `presence` prior to `length`.
+
+The `size` helper is an alias for `length`.
+
+### `numericality`
+
+This helper validates that your attributes have only numeric values. By
+default, it will match an optional sign followed by an integral or floating
+point number. To specify that only integral numbers are allowed set
+`:only_integer` to true.
+
+If you set `:only_integer` to `true`, then it will use the
+
+```ruby
+/\A[+-]?\d+\Z/
+```
+
+regular expression to validate the attribute's value. Otherwise, it will try to
+convert the value to a number using `Float`.
+
+WARNING. Note that the regular expression above allows a trailing newline
+character.
+
+```ruby
+class Player < ActiveRecord::Base
+ validates :points, numericality: true
+ validates :games_played, numericality: { only_integer: true }
+end
+```
+
+Besides `:only_integer`, this helper also accepts the following options to add
+constraints to acceptable values:
+
+* `:greater_than` - Specifies the value must be greater than the supplied
+ value. The default error message for this option is _"must be greater than
+ %{count}"_.
+* `:greater_than_or_equal_to` - Specifies the value must be greater than or
+ equal to the supplied value. The default error message for this option is
+ _"must be greater than or equal to %{count}"_.
+* `:equal_to` - Specifies the value must be equal to the supplied value. The
+ default error message for this option is _"must be equal to %{count}"_.
+* `:less_than` - Specifies the value must be less than the supplied value. The
+ default error message for this option is _"must be less than %{count}"_.
+* `:less_than_or_equal_to` - Specifies the value must be less than or equal the
+ supplied value. The default error message for this option is _"must be less
+ than or equal to %{count}"_.
+* `:odd` - Specifies the value must be an odd number if set to true. The
+ default error message for this option is _"must be odd"_.
+* `:even` - Specifies the value must be an even number if set to true. The
+ default error message for this option is _"must be even"_.
+
+The default error message is _"is not a number"_.
+
+### `presence`
+
+This helper validates that the specified attributes are not empty. It uses the
+`blank?` method to check if the value is either `nil` or a blank string, that
+is, a string that is either empty or consists of whitespace.
+
+```ruby
+class Person < ActiveRecord::Base
+ validates :name, :login, :email, presence: true
+end
+```
+
+If you want to be sure that an association is present, you'll need to test
+whether the foreign key used to map the association is present, and not the
+associated object itself.
+
+```ruby
+class LineItem < ActiveRecord::Base
+ belongs_to :order
+ validates :order, presence: true
+end
+```
+
+You should also be sure to have a proper `:inverse_of` as well:
+
+```ruby
+class Order < ActiveRecord::Base
+ has_many :line_items, inverse_of: :order
+end
+```
+
+If you validate the presence of an object associated via a `has_one` or
+`has_many` relationship, it will check that the object is neither `blank?` nor
+`marked_for_destruction?`.
+
+Since `false.blank?` is true, if you want to validate the presence of a boolean
+field you should use `validates :field_name, inclusion: { in: [true, false] }`.
+
+The default error message is _"can't be empty"_.
+
+### `uniqueness`
+
+This helper validates that the attribute's value is unique right before the
+object gets saved. It does not create a uniqueness constraint in the database,
+so it may happen that two different database connections create two records
+with the same value for a column that you intend to be unique. To avoid that,
+you must create a unique index in your database.
+
+```ruby
+class Account < ActiveRecord::Base
+ validates :email, uniqueness: true
+end
+```
+
+The validation happens by performing an SQL query into the model's table,
+searching for an existing record with the same value in that attribute.
+
+There is a `:scope` option that you can use to specify other attributes that
+are used to limit the uniqueness check:
+
+```ruby
+class Holiday < ActiveRecord::Base
+ validates :name, uniqueness: { scope: :year,
+ message: "should happen once per year" }
+end
+```
+
+There is also a `:case_sensitive` option that you can use to define whether the
+uniqueness constraint will be case sensitive or not. This option defaults to
+true.
+
+```ruby
+class Person < ActiveRecord::Base
+ validates :name, uniqueness: { case_sensitive: false }
+end
+```
+
+WARNING. Note that some databases are configured to perform case-insensitive
+searches anyway.
+
+The default error message is _"has already been taken"_.
+
+### `validates_with`
+
+This helper passes the record to a separate class for validation.
+
+```ruby
+class Person < ActiveRecord::Base
+ validates_with GoodnessValidator
+end
+
+class GoodnessValidator < ActiveModel::Validator
+ def validate(record)
+ if record.first_name == "Evil"
+ record.errors[:base] << "This person is evil"
+ end
+ end
+end
+```
+
+NOTE: Errors added to `record.errors[:base]` relate to the state of the record
+as a whole, and not to a specific attribute.
+
+The `validates_with` helper takes a class, or a list of classes to use for
+validation. There is no default error message for `validates_with`. You must
+manually add errors to the record's errors collection in the validator class.
+
+To implement the validate method, you must have a `record` parameter defined,
+which is the record to be validated.
+
+Like all other validations, `validates_with` takes the `:if`, `:unless` and
+`:on` options. If you pass any other options, it will send those options to the
+validator class as `options`:
+
+```ruby
+class Person < ActiveRecord::Base
+ validates_with GoodnessValidator, fields: [:first_name, :last_name]
+end
+
+class GoodnessValidator < ActiveModel::Validator
+ def validate(record)
+ if options[:fields].any?{|field| record.send(field) == "Evil" }
+ record.errors[:base] << "This person is evil"
+ end
+ end
+end
+```
+
+### `validates_each`
+
+This helper validates attributes against a block. It doesn't have a predefined
+validation function. You should create one using a block, and every attribute
+passed to `validates_each` will be tested against it. In the following example,
+we don't want names and surnames to begin with lower case.
+
+```ruby
+class Person < ActiveRecord::Base
+ validates_each :name, :surname do |record, attr, value|
+ record.errors.add(attr, 'must start with upper case') if value =~ /\A[a-z]/
+ end
+end
+```
+
+The block receives the record, the attribute's name and the attribute's value.
+You can do anything you like to check for valid data within the block. If your
+validation fails, you should add an error message to the model, therefore
+making it invalid.
+
+Common Validation Options
+-------------------------
+
+These are common validation options:
+
+### `:allow_nil`
+
+The `:allow_nil` option skips the validation when the value being validated is
+`nil`.
+
+```ruby
+class Coffee < ActiveRecord::Base
+ validates :size, inclusion: { in: %w(small medium large),
+ message: "%{value} is not a valid size" }, allow_nil: true
+end
+```
+
+TIP: `:allow_nil` is ignored by the presence validator.
+
+### `:allow_blank`
+
+The `:allow_blank` option is similar to the `:allow_nil` option. This option
+will let validation pass if the attribute's value is `blank?`, like `nil` or an
+empty string for example.
+
+```ruby
+class Topic < ActiveRecord::Base
+ validates :title, length: { is: 5 }, allow_blank: true
+end
+
+Topic.create("title" => "").valid? # => true
+Topic.create("title" => nil).valid? # => true
+```
+
+TIP: `:allow_blank` is ignored by the presence validator.
+
+### `:message`
+
+As you've already seen, the `:message` option lets you specify the message that
+will be added to the `errors` collection when validation fails. When this
+option is not used, Active Record will use the respective default error message
+for each validation helper.
+
+### `:on`
+
+The `:on` option lets you specify when the validation should happen. The
+default behavior for all the built-in validation helpers is to be run on save
+(both when you're creating a new record and when you're updating it). If you
+want to change it, you can use `on: :create` to run the validation only when a
+new record is created or `on: :update` to run the validation only when a record
+is updated.
+
+```ruby
+class Person < ActiveRecord::Base
+ # it will be possible to update email with a duplicated value
+ validates :email, uniqueness: true, on: :create
+
+ # it will be possible to create the record with a non-numerical age
+ validates :age, numericality: true, on: :update
+
+ # the default (validates on both create and update)
+ validates :name, presence: true, on: :save
+end
+```
+
+Strict Validations
+------------------
+
+You can also specify validations to be strict and raise
+`ActiveModel::StrictValidationFailed` when the object is invalid.
+
+```ruby
+class Person < ActiveRecord::Base
+ validates :name, presence: { strict: true }
+end
+
+Person.new.valid? # => ActiveModel::StrictValidationFailed: Name can't be blank
+```
+
+There is also an ability to pass custom exception to `:strict` option
+
+```ruby
+class Person < ActiveRecord::Base
+ validates :token, presence: true, uniqueness: true, strict: TokenGenerationException
+end
+
+Person.new.valid? # => TokenGenerationException: Token can't be blank
+```
+
+Conditional Validation
+----------------------
+
+Sometimes it will make sense to validate an object 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, a `Proc` or an `Array`. You may use the `:if`
+option when you want to specify when the validation **should** happen. If you
+want to specify when the validation **should not** happen, then you may use the
+`:unless` option.
+
+### Using a Symbol with `:if` and `:unless`
+
+You can associate the `:if` and `:unless` options with a symbol corresponding
+to the name of a method that will get called right before validation happens.
+This is the most commonly used option.
+
+```ruby
+class Order < ActiveRecord::Base
+ validates :card_number, presence: true, if: :paid_with_card?
+
+ def paid_with_card?
+ payment_type == "card"
+ end
+end
+```
+
+### Using a String with `:if` and `:unless`
+
+You can also use a string that will be evaluated using `eval` and needs to
+contain valid Ruby code. You should use this option only when the string
+represents a really short condition.
+
+```ruby
+class Person < ActiveRecord::Base
+ validates :surname, presence: true, if: "name.nil?"
+end
+```
+
+### Using a Proc with `:if` and `:unless`
+
+Finally, it's possible to associate `:if` and `:unless` with a `Proc` object
+which will be called. Using a `Proc` object gives you the ability to write an
+inline condition instead of a separate method. This option is best suited for
+one-liners.
+
+```ruby
+class Account < ActiveRecord::Base
+ validates :password, confirmation: true,
+ unless: Proc.new { |a| a.password.blank? }
+end
+```
+
+### Grouping Conditional validations
+
+Sometimes it is useful to have multiple validations use one condition, it can
+be easily achieved using `with_options`.
+
+```ruby
+class User < ActiveRecord::Base
+ with_options if: :is_admin? do |admin|
+ admin.validates :password, length: { minimum: 10 }
+ admin.validates :email, presence: true
+ end
+end
+```
+
+All validations inside of `with_options` block will have automatically passed
+the condition `if: :is_admin?`
+
+### Combining Validation Conditions
+
+On the other hand, when multiple conditions define whether or not a validation
+should happen, an `Array` can be used. Moreover, you can apply both `:if` and
+`:unless` to the same validation.
+
+```ruby
+class Computer < ActiveRecord::Base
+ validates :mouse, presence: true,
+ if: ["market.retail?", :desktop?]
+ unless: Proc.new { |c| c.trackpad.present? }
+end
+```
+
+The validation only runs when all the `:if` conditions and none of the
+`:unless` conditions are evaluated to `true`.
+
+Performing Custom Validations
+-----------------------------
+
+When the built-in validation helpers are not enough for your needs, you can
+write your own validators or validation methods as you prefer.
+
+### Custom Validators
+
+Custom validators are classes that extend `ActiveModel::Validator`. These
+classes must implement a `validate` method which takes a record as an argument
+and performs the validation on it. The custom validator is called using the
+`validates_with` method.
+
+```ruby
+class MyValidator < ActiveModel::Validator
+ def validate(record)
+ unless record.name.starts_with? 'X'
+ record.errors[:name] << 'Need a name starting with X please!'
+ end
+ end
+end
+
+class Person
+ include ActiveModel::Validations
+ validates_with MyValidator
+end
+```
+
+The easiest way to add custom validators for validating individual attributes
+is with the convenient `ActiveModel::EachValidator`. In this case, the custom
+validator class must implement a `validate_each` method which takes three
+arguments: record, attribute and value which correspond to the instance, the
+attribute to be validated and the value of the attribute in the passed
+instance.
+
+```ruby
+class EmailValidator < ActiveModel::EachValidator
+ def validate_each(record, attribute, value)
+ unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[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.present? && 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
+```
+
+Displaying Validation Errors in Views
+-------------------------------------
+
+Once you've created a model and added validations, if that model is created via
+a web form, you probably want to display an error message when one of the
+validations fail.
+
+Because every application handles this kind of thing differently, Rails does
+not include any view helpers to help you generate these messages directly.
+However, due to the rich number of methods Rails gives you to interact with
+validations in general, it's fairly easy to build your own. In addition, when
+generating a scaffold, Rails will put some ERB into the `_form.html.erb` that
+it generates that displays the full list of errors on that model.
+
+Assuming we have a model that's been saved in an instance variable named
+`@post`, it looks like this:
+
+```ruby
+<% if @post.errors.any? %>
+ <div id="error_explanation">
+ <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2>
+
+ <ul>
+ <% @post.errors.full_messages.each do |msg| %>
+ <li><%= msg %></li>
+ <% end %>
+ </ul>
+ </div>
+<% end %>
+```
+
+Furthermore, if you use the Rails form helpers to generate your forms, when
+a validation error occurs on a field, it will generate an extra `<div>` around
+the entry.
+
+```
+<div class="field_with_errors">
+ <input id="post_title" name="post[title]" size="30" type="text" value="">
+</div>
+```
+
+You can then style this div however you'd like. The default scaffold that
+Rails generates, for example, adds this CSS rule:
+
+```
+.field_with_errors {
+ padding: 2px;
+ background-color: red;
+ display: table;
+}
+```
+
+This means that any field with an error ends up with a 2 pixel red border.