diff options
Diffstat (limited to 'guides/source/active_record_validations.md')
-rw-r--r-- | guides/source/active_record_validations.md | 119 |
1 files changed, 66 insertions, 53 deletions
diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md index df39d3c5dc..8c832bafff 100644 --- a/guides/source/active_record_validations.md +++ b/guides/source/active_record_validations.md @@ -1,3 +1,5 @@ +**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.** + Active Record Validations ========================= @@ -85,7 +87,7 @@ end We can see how it works by looking at some `rails console` output: ```ruby -$ rails console +$ bin/rails console >> p = Person.new(name: "John Doe") => #<Person id: nil, name: "John Doe", created_at: nil, updated_at: nil> >> p.new_record? @@ -120,8 +122,8 @@ database only if the object is valid: * `update!` The bang versions (e.g. `save!`) raise an exception if the record is invalid. -The non-bang versions don't: `save` and `update` return `false`, -`create` and `update` just return the objects. +The non-bang versions don't, `save` and `update` return `false`, +`create` just returns the object. ### Skipping Validations @@ -162,8 +164,8 @@ 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 +through the `errors.messages` 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 @@ -175,28 +177,28 @@ class Person < ActiveRecord::Base end >> p = Person.new -#=> #<Person id: nil, name: nil> ->> p.errors -#=> {} +# => #<Person id: nil, name: nil> +>> p.errors.messages +# => {} >> p.valid? -#=> false ->> p.errors -#=> {name:["can't be blank"]} +# => false +>> p.errors.messages +# => {name:["can't be blank"]} >> p = Person.create -#=> #<Person id: nil, name: nil> ->> p.errors -#=> {name:["can't be blank"]} +# => #<Person id: nil, name: nil> +>> p.errors.messages +# => {name:["can't be blank"]} >> p.save -#=> false +# => false >> p.save! -#=> ActiveRecord::RecordInvalid: Validation failed: Name can't be blank +# => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank >> Person.create! -#=> ActiveRecord::RecordInvalid: Validation failed: Name can't be blank +# => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank ``` `invalid?` is simply the inverse of `valid?`. It triggers your validations, @@ -243,7 +245,7 @@ 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 +`: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. @@ -337,7 +339,7 @@ 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." } + message: "%{value} is reserved." } end ``` @@ -357,10 +359,12 @@ 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" } + message: "only allows letters" } end ``` +Alternatively, you can require that the specified attribute does _not_ match the regular expression by using the `:without` option. + The default error message is _"is invalid"_. ### `inclusion` @@ -425,7 +429,7 @@ class Essay < ActiveRecord::Base validates :content, length: { minimum: 300, maximum: 400, - tokenizer: lambda { |str| str.scan(/\w+/) }, + tokenizer: lambda { |str| str.split(/\s+/) }, too_short: "must have at least %{count} words", too_long: "must have at most %{count} words" } @@ -434,12 +438,10 @@ 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 +provide a personalized message or use `presence: true` 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 @@ -487,6 +489,8 @@ constraints to acceptable values: * `:even` - Specifies the value must be an even number if set to true. The default error message for this option is _"must be even"_. +NOTE: By default, `numericality` doesn't allow `nil` values. You can use `allow_nil: true` option to permit it. + The default error message is _"is not a number"_. ### `presence` @@ -526,9 +530,16 @@ If you validate the presence of an object associated via a `has_one` or `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] }`. +field you should use one of the following validations: + +```ruby +validates :boolean_field_name, presence: true +validates :boolean_field_name, inclusion: { in: [true, false] } +validates :boolean_field_name, exclusion: { in: [nil] } +``` -The default error message is _"can't be empty"_. +By using one of these validations, you will ensure the value will NOT be `nil` +which would result in a `NULL` value in most cases. ### `absence` @@ -577,7 +588,9 @@ 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. +you must create a unique index on both columns in your database. See +[the MySQL manual](http://dev.mysql.com/doc/refman/5.6/en/multiple-column-indexes.html) +for more details about multiple column indexes. ```ruby class Account < ActiveRecord::Base @@ -618,10 +631,6 @@ The default error message is _"has already been taken"_. 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" @@ -629,6 +638,10 @@ class GoodnessValidator < ActiveModel::Validator end end end + +class Person < ActiveRecord::Base + validates_with GoodnessValidator +end ``` NOTE: Errors added to `record.errors[:base]` relate to the state of the record @@ -646,10 +659,6 @@ Like all other validations, `validates_with` takes the `:if`, `:unless` and 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" } @@ -657,6 +666,10 @@ class GoodnessValidator < ActiveModel::Validator end end end + +class Person < ActiveRecord::Base + validates_with GoodnessValidator, fields: [:first_name, :last_name] +end ``` Note that the validator will be initialized *only once* for the whole application @@ -677,14 +690,14 @@ class GoodnessValidator def initialize(person) @person = person end - + def validate if some_complex_condition_involving_ivars_and_private_methods? @person.errors[:base] << "This person is evil" end end - - # … + + # ... end ``` @@ -698,7 +711,7 @@ 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]/ + record.errors.add(attr, 'must start with upper case') if value =~ /\A[[:lower:]]/ end end ``` @@ -736,8 +749,8 @@ class Topic < ActiveRecord::Base validates :title, length: { is: 5 }, allow_blank: true end -Topic.create("title" => "").valid? # => true -Topic.create("title" => nil).valid? # => true +Topic.create(title: "").valid? # => true +Topic.create(title: nil).valid? # => true ``` ### `:message` @@ -765,7 +778,7 @@ class Person < ActiveRecord::Base validates :age, numericality: true, on: :update # the default (validates on both create and update) - validates :name, presence: true, on: :save + validates :name, presence: true end ``` @@ -783,7 +796,7 @@ end Person.new.valid? # => ActiveModel::StrictValidationFailed: Name can't be blank ``` -There is also an ability to pass custom exception to `:strict` option +There is also an ability to pass custom exception to `:strict` option. ```ruby class Person < ActiveRecord::Base @@ -871,7 +884,7 @@ should happen, an `Array` can be used. Moreover, you can apply both `:if` and ```ruby class Computer < ActiveRecord::Base validates :mouse, presence: true, - if: ["market.retail?", :desktop?] + if: ["market.retail?", :desktop?], unless: Proc.new { |c| c.trackpad.present? } end ``` @@ -910,8 +923,8 @@ 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 +arguments: record, attribute, and value. These correspond to the instance, the +attribute to be validated, and the value of the attribute in the passed instance. ```ruby @@ -992,12 +1005,12 @@ end person = Person.new person.valid? # => false -person.errors +person.errors.messages # => {:name=>["can't be blank", "is too short (minimum is 3 characters)"]} person = Person.new(name: "John Doe") person.valid? # => true -person.errors # => [] +person.errors.messages # => {} ``` ### `errors[]` @@ -1129,15 +1142,15 @@ 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: +`@article`, it looks like this: ```ruby -<% if @post.errors.any? %> +<% if @article.errors.any? %> <div id="error_explanation"> - <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2> + <h2><%= pluralize(@article.errors.count, "error") %> prohibited this article from being saved:</h2> <ul> - <% @post.errors.full_messages.each do |msg| %> + <% @article.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> @@ -1151,7 +1164,7 @@ the entry. ``` <div class="field_with_errors"> - <input id="post_title" name="post[title]" size="30" type="text" value=""> + <input id="article_title" name="article[title]" size="30" type="text" value=""> </div> ``` |