diff options
Diffstat (limited to 'guides/source/active_record_validations.md')
-rw-r--r-- | guides/source/active_record_validations.md | 963 |
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. |