SlideShare a Scribd company logo
Ror lab. season 2
 - the 6th round -


Active Record
Validations

 September 15th, 2012

   Hyoseong Choi
     ROR Lab.
Contents
      Validations                         Callbacks
•   The Object Life Cycle          •   Callbacks Overview
•   Validations Overview           •   Available Callbacks
•   Validation Helpers             •   Running Callbacks
•   Common Validation              •   Skipping Callbacks
    Options                        •   Halting Execution
•   Conditional Validation         •   Relational Callbacks
•   Custom Validations             •   Conditional Callbacks
•   Working with Validation        •   Callback Classes
    Errors
                                   •   Observers
•   Displaying Validation Errors
                                   •   Transaction Callbacks
    in the View

                                                       ROR Lab.
Validation Levels

• Native DB constraints - DB-dependent
• Client-side validations - javascript ?
• Controller-level validations - keep skinny
• Model-level validations - the best way

                                       ROR Lab.
“new” method
• A new object instantiated      vs. persisted?

• not yet to store to database : new_record?
• no validation called, but ...



                                             ROR Lab.
Validation Time Point
                             ActiveRecord
               Web Server

                Model              DB
                Validation
                   save
                  create
                  update
  clients
         Model-level validations


                                   ROR Lab.
Validation Event
        Triggering validations                   Skipping validations
        •   .create                              •   .decrement!
        •   .create!                             •   #decrement_counter
        •   .save                                •   .increment!
        •   .save!                               •   #increment_counter
        •   #update                              •   .toggle!
        •   .update_attributes                   •   .touch
        •   .update_attributes!                  •   #update_all
                                                 •   .update_attribute deprecated
                                                 •   .update_column
                                                 •   #update_counters



Ref.:   ActiveRecord::Persistence   ActiveRecord::CounterCache
                                                                      ROR Lab.
Call Validations
• valid? or invalid?




                       ROR Lab.
valid? vs Errors




By definition,
an object is valid if this collection is empty
after running validations.

                                                 ROR Lab.
Validation Helpers
• acceptance     • validates_associated
• confirmation    • validates_each
• exclusion      • validates_with
• format
• inclusion           :on
                            • save(default)
                            • create
• length                    • update

• numericality     :message
• presence
• uniqueness
                                  ROR Lab.
Common Options

:allow_nil - nil
:allow_blank - nil or whitespace
:message - overriding default error message
:on - :create / :update / :save



                                     ROR Lab.
Conditional Validation
  :if and :unless

    • A Symbol : a method name
    • A String : a really short condition
    • A Proc : an inline condition
  Grouping conditions : with_options

                                        ROR Lab.
Custom Validations
 • Custom validators modules
   : inherited from Two
      ★ ActiveModel::Validator
      ★ ActiveModel::EachValidator
      ★ Get the “record” argument as a
        parameter
 •   Custom validation methods
 •   Custom validation helpers

                                     ROR Lab.
Working with
  Validation Errors
• errors
• errors.messages
• errors.full_messages( or errors.to_a)
• errors[:attr] : for a specific attribute
• errors.add(:attr, message)(or errors[:attr]=)
• errors[:base] : object’s state as a whole
• errors.clear : intentionally to clear
• errors.size : count of errors
                                         ROR Lab.
Displaying Validation
 Errors in the View
★ gem ‘dynamic_form’   ★ Error   Messages CSS

                         .field_with_errors
                         #errorExplanation
                         #errorExplanation h2
                         #errorExplanation p
                         #errorExplanation ul li




                                      ROR Lab.
Validation Errors :                                                https://github.com/joelmoss/dynamic_form




                                                                 Error CSS
                                                                                               #error_explanation

                                                                                                                          #error_explanation h2
                                                                                                             #error_explanation p
<%= form_for(@product) do |f| %>

 <% if @product.errors.any? %>
  <div id="error_explanation">                                                                   #error_explanation ul li
   <h2><%= pluralize(@product.errors.count, "error") %>
      prohibited this product from being saved:
   </h2>
   <ul>
   <% @product.errors.full_messages.each do |msg| %>
    <li><%= msg %></li>
   <% end %>
   </ul>
  </div>
 <% end %>                                                                                                 .field_with_errors




                                                                                                          generated by scaffold

                                                          apps/assets/stylesheets/scaffolds.css.scss
                                                                                                                                ROR Lab.
Validation Errors :                          https://github.com/joelmoss/dynamic_form




                     Error CSS
                                                   #error_explanation
       <%= form_for(@product) do |f| %>
                                                                              #error_explanation h2
        <% if @product.errors.any? %>                            #error_explanation p
         <div id="error_explanation">
          <h2><%= pluralize(@product.errors.count, "error") %>
                                                  #error_explanation ul li
             prohibited this product from being saved:
          </h2>
          <ul>
          <% @product.errors.full_messages.each do |msg| %>
           <li><%= msg %></li>                            .field_with_errors
          <% end %>
          </ul>
         </div>
        <% end %>


                                                              generated by scaffold

              apps/assets/stylesheets/scaffolds.css.scss
                                                                                    ROR Lab.
Validation Errors :              https://github.com/joelmoss/dynamic_form




                                Error CSS
apps/assets
/stylesheets
/scaffolds.css.scss                        #error_explanation

.field_with_errors {                                                   #error_explanation h2
  padding: 2px;
  background-color: red;                                 #error_explanation p
  display: table;
}

#error_explanation {                        #error_explanation ul li
 width: 450px;
 border: 2px solid red;
 padding: 7px;
 padding-bottom: 0;
 margin-bottom: 20px;
 background-color: #f0f0f0;
 h2 {                                                  .field_with_errors
   text-align: left;
   font-weight: bold;
   padding: 5px 5px 5px 15px;
   font-size: 12px;
   margin: -7px;
   margin-bottom: 0px;
   background-color: #c00;
   color: #fff;
 }
 ul li {
                                                      generated by scaffold
   font-size: 12px;
   list-style: square;



                                                                           ROR Lab.
Validation Errors :


       ‘dynamic_form’
   • f.error_messages or
   • error_messages_for :product

   <%= form_for(@product) do |f| %>

    <%= f.error_messages %>

   <% end %>




                                      ROR Lab.
Validation Errors :                          https://github.com/joelmoss/dynamic_form




                 ‘dynamic_form’

                                                           generated by dynamic_form
        r_ tag
       e
:h ead                         :header_message
                                             :message




            <%= f.error_messages
              :header_message => "Invalid product!",
              :message => "You'll need to fix the following fields:",
              :header_tag => :h3 %>



                                                                                          ROR Lab.
Validation Errors :                           https://github.com/joelmoss/dynamic_form




                Error HTML
 config/initializers/custom_error_message_html.rb                              field object

 ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
   errors = Array(instance.error_message).join(',')
   unless html_tag =~ /^<label/
    %(#{html_tag}<span class="validation-error">&nbsp;#{errors}</span>).html_safe
   else
   %(#{html_tag}).html_safe
  end
 end




                               #{html_tag}            .validation-error




                                                                                     ROR Lab.
Validation Errors :                                                    https://github.com/joelmoss/dynamic_form




                    Error HTML
      <% if @product.errors.any? %>
       <div id="error_explanation">
        <h2><%= pluralize(@product.errors.count, "error") %> prohibited this product from being saved:</h2>
       </div>
      <% end %>




                                                                                                              ROR Lab.
감사합니다.
 
appendix
 
Active Record
 Counter Cache

• #decrement_counter
• #increment_counter   skipping
• #reset_counters      validations

• #update_counters
                               ROR Lab.
decrement vs
 decrement!




               ROR Lab.
toggle vs
 toggle!




            ROR Lab.
touch




        ROR Lab.
#update_all




              ROR Lab.
.update_column




             ROR Lab.
#update




          ROR Lab.
Validation Helpers :


               acceptance
     • a checkbox : a real or virtual attribute
     • “must be accepted”
     class Person  ActiveRecord::Base
       validates :terms_of_service, :acceptance = true



     class Person  ActiveRecord::Base
       validates :terms_of_service,
              :acceptance = { :accept = 'yes' }




                                                          ROR Lab.
Validation Helpers :


validates_associated
    • should if associated with other models
    • “is invalid”
     class Library  ActiveRecord::Base
       has_many :books
       validates_associated :books




    • should use on one end of your
       associations

                                          ROR Lab.
Validation Helpers :


           confirmation
    • a virtual attribute appended with
      “_confirmation”
    • “doesn’t match confirmation”
     class Person  ActiveRecord::Base
       validates :email, :confirmation = true
       validates :email_confirmation, :presence = true



     %= text_field :person, :email %
     %= text_field :person, :email_confirmation %



                                                         ROR Lab.
Validation Helpers :


                   exclusion
    • not included in a given set
      :any enumerable object
    • “is reserved”
     class Account  ActiveRecord::Base
       validates :subdomain,
         :exclusion = {
         :in = %w(www us ca jp),
         :message = Subdomain %{value} is reserved. }




                                                           ROR Lab.
Validation Helpers :


                       format
    • match a given regular expression
    • “is invalid”

     class Product  ActiveRecord::Base
       validates :legacy_code,
              :format = { :with = /A[a-zA-Z]+z/,
              :message = Only letters allowed }




                                                       ROR Lab.
Validation Helpers :


                   inclusion
    • included in a given set
      : any enumerable object
    • “is not included in the list”
     class Coffee  ActiveRecord::Base
       validates :size,
           :inclusion = {
             :in = %w(small medium large),
          :message = %{value} is not a valid size
         }




                                                       ROR Lab.
Validation Helpers :


                         length                        (1/2)

   • length constraint options
      - :minimum, :maximum, :in/:within, :is
      - a placeholder - %{count}

  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 }




                                                               ROR Lab.
Validation Helpers :


                        length                     (2/2)

   • options
     - :wrong_length, :too_long, :too_short
     - a placeholder - %{count}
   class Essay  ActiveRecord::Base
     validates :content, :size = {
       :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
     }




                                                            ROR Lab.
Validation Helpers :


            numericality                                 (1/2)

    • only numeric values
      : (+/−) integer/floating point
    • “is not a number”
     class Person  ActiveRecord::Base
       validates :email, :confirmation = true
       validates :email_confirmation, :presence = true



     %= text_field :person, :email %
     %= text_field :person, :email_confirmation %



                                                         ROR Lab.
Validation Helpers :


               numericality                                                          (2/2)


     class Player  ActiveRecord::Base
       validates :points, :numericality = true
       validates :games_played,
            :numericality = { :only_integer = true }

                                                                   /A[+−]?d+Z/



   •:greater_than               ➡   “must   be   greater than %{count}”
   •:greater_than_or_equal_to   ➡   “must   be   greater than or equal to %{count}
   •:equal_to                   ➡   “must   be   equal to %{count}”
   •:less_than                  ➡   “must   be   less than %{count}”
   •:less_than_or_equal_to      ➡   “must   be   less than or equal to %{count}
   •:odd                        ➡   “must   be   odd”
   •:even                       ➡   “must   be   even”




                                                                                     ROR Lab.
Validation Helpers :


                   presence (1/2)
   • empty or whitespaces
   • “can’t be empty”
    class Person  ActiveRecord::Base
      validates :name, :login, :email, :presence = true
    end




    class LineItem  ActiveRecord::Base
      belongs_to :order
      validates :order_id, :presence = true



                                                           ROR Lab.
Validation Helpers :


                     presence (2/2)
   • empty or whitespaces
   • “can’t be empty”

                                               ✘
    class Person  ActiveRecord::Base
      validates :is_admin, :presence = true
    end              a boolean field ➞ false.blank? is true


    class Person  ActiveRecord::Base
      validates :is_admin,
             :inclusion = { :in = [true, false] }



                                                             ROR Lab.
Validation Helpers :


               uniqueness
   • add_index :table_name, :column_name, unique = true
   • “has already been taken”
    class Account  ActiveRecord::Base
      validates :email, :uniqueness = true                  other
    end                                                    attributes
    class Holiday  ActiveRecord::Base
      validates :name, :uniqueness = { :scope = :year,
        :message = should happen once per year }
    end
    class Person  ActiveRecord::Base
      validates :name,
           :uniqueness = { :case_sensitive = false }


                                                                ROR Lab.
Validation Helpers :


         validates_with (1/2)
   • a separate class for validation
   • no default validate error message
    class Person  ActiveRecord::Base
      validates_with GoodnessValidator,[:if/:unless/:on]
    end
     
    class GoodnessValidator  ActiveModel::Validator
      def validate(record)
        if record.first_name == Evil
          record.errors[:base]  This person is evil
        end
      end



                                                           ROR Lab.
Validation Helpers :


            validates_with (2/2)
   • any additional options ➞ options
  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




                                                                     ROR Lab.
Validation Helpers :


                   validates_each
            • no predefined validation function
               ➞a block
            • no default error message
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




                                                                                  ROR Lab.
Common Validation Options :


                  :allow_nil

    class Coffee  ActiveRecord::Base
      validates :size,
        :inclusion = { :in = %w(small medium large),
        :message = %{value} is not a valid size },
        :allow_nil = true




                                                         ROR Lab.
Common Validation Options :


            :allow_blank
    • nil or an empty string
    class Topic  ActiveRecord::Base
      validates :title,
             :length = { :is = 5 },
             :allow_blank = true
    end
     
    Topic.create(title = ).valid?  # = true




                                                    ROR Lab.
Common Validation Options :


                                      :on
          • when the validation should happen
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




                                                                        ROR Lab.
Conditional Validation


             :if  :unless                         (1/4)

    • Using a Symbol : a method name
    class Order  ActiveRecord::Base
      validates :card_number, :presence = true,
             :if = :paid_with_card?
     
      def paid_with_card?
        payment_type == card
      end




                                                    ROR Lab.
Conditional Validation


             :if  :unless                     (2/4)

    • Using a String : a really short condition
    class Person  ActiveRecord::Base
      validates :surname, :presence = true,
             :if = name.nil?




                                                ROR Lab.
Conditional Validation


              :if  :unless                             (3/4)

    • Using a Proc : an inline condition
    class Account  ActiveRecord::Base
      validates :password, :confirmation = true,
        :unless = Proc.new { |a| a.password.blank? }
                                    a model
                                     object




                                                         ROR Lab.
Conditional Validation


              :if  :unless                                (4/4)

    • grouping conditional validations
                                             condition
                                              object
   class User  ActiveRecord::Base
     with_options :if = :is_admin? do |admin|
       admin.validates :password, :length = { :minimum = 10 }
       admin.validates :email, :presence = true
     end




                                                              ROR Lab.
Custom Validations :


   Custom Validators (1/2)
   • to extend ActiveMode::Validator
   • to validate the state of whole record
   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




                                                                         ROR Lab.
Custom Validations :


   Custom Validators (2/2)
   • to extend ActiveMode::EachValidator
   • to validate individual attributes
  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
                                                     EmailValidator
  class Person  ActiveRecord::Base
    validates :email, :presence = true, :email = true
  end




                                                                               ROR Lab.
Custom Validations :


     Custom Methods (1/2)
   • to verify the state of models
  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                                                         EmailValidator
   
    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



                                                                                 ROR Lab.
Custom Validations :


     Custom Methods (2/2)
   • to verify the state of models
  class Invoice  ActiveRecord::Base
    validate :active_customer, :on = :create
   
    def active_customer
      errors.add(:customer_id, is not active)
                        unless customer.active?
    end




                                                  ROR Lab.
Custom Validations :


       Custom Helpers
   • to reuse in several different models
   • to put in config/initializers
  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



   class Movie  ActiveRecord::Base
     validates_as_choice :rating, 5
   end


                                                             ROR Lab.
Validation Errors :


                                errors
   • an instance of ActiveModel::Errors
   • key : attribute name
       value : an array of strings with all errors
   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 # = []



                                                                          ROR Lab.
Validation Errors :


                            errors[ ]
   • to check the error message of a specific
       attribute
   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)]




                                                                          ROR Lab.
Validation Errors :


                     errors.add                                         (1/2)

   • to manually add messages of a specific
      attribute
   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 !@#%*()_-+=]




                                                                          ROR Lab.
Validation Errors :


                    errors.add                                        (2/2)

                                  -or-
   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 !@#%*()_-+=]




                                                                        ROR Lab.
Validation Errors :


              errors[:base]
   • related to the object’s state as a whole
   • an array
   class Person  ActiveRecord::Base
     def a_method_used_for_validation_purposes
       errors[:base]  This person is invalid because ...
     end
   end




                                                               ROR Lab.

More Related Content

KEY
Active Record Validations, Season 1
PDF
Html5 : stockage local & synchronisation
PDF
On Failure and Resilience
PDF
Introduction to Active Record at MySQL Conference 2007
PDF
ActiveRecord Query Interface (1), Season 1
PDF
Make your app idea a reality with Ruby On Rails
PDF
6 reasons Jubilee could be a Rubyist's new best friend
PDF
Rails Performance
Active Record Validations, Season 1
Html5 : stockage local & synchronisation
On Failure and Resilience
Introduction to Active Record at MySQL Conference 2007
ActiveRecord Query Interface (1), Season 1
Make your app idea a reality with Ruby On Rails
6 reasons Jubilee could be a Rubyist's new best friend
Rails Performance

Viewers also liked (10)

PPT
ActiveWarehouse/ETL - BI & DW for Ruby/Rails
ODP
Performance Optimization of Rails Applications
PDF
Introduction to Ruby on Rails
PPTX
Neev Expertise in Ruby on Rails (RoR)
ODP
Ruby on Rails
PDF
Distributed Ruby and Rails
PDF
Ruby Beyond Rails
PDF
From a monolithic Ruby on Rails app to the JVM
PDF
Ruby On Rails Introduction
PDF
Design in Tech Report 2017
ActiveWarehouse/ETL - BI & DW for Ruby/Rails
Performance Optimization of Rails Applications
Introduction to Ruby on Rails
Neev Expertise in Ruby on Rails (RoR)
Ruby on Rails
Distributed Ruby and Rails
Ruby Beyond Rails
From a monolithic Ruby on Rails app to the JVM
Ruby On Rails Introduction
Design in Tech Report 2017
Ad

Similar to ActiveRecord Validations, Season 2 (20)

PDF
Ruby on Rails 101
PDF
Excellent
PDF
RubyOnRails-Cheatsheet-BlaineKendall
PDF
RubyOnRails-Cheatsheet-BlaineKendall
ZIP
Rails 3 (beta) Roundup
DOC
Wheels
KEY
Building Web Service Clients with ActiveModel
KEY
Building Web Service Clients with ActiveModel
PDF
Rails MVC by Sergiy Koshovyi
PDF
What I Have Learned from Organizing Remote Internship for Ruby developers
KEY
Active Record Form Helpers, Season 1
KEY
ActiveRecord Callbacks & Observers, Season 2
PDF
Where Does the Fat Goes? Utilizando Form Objects Para Simplificar seu Código
PDF
Ruby on Rails 3.1: Let's bring the fun back into web programing
KEY
UT on Rails3 2010- Week 4
KEY
Getting started with Rails (2), Season 2
PDF
LF_APIStrat17_Things I Wish People Told Me About Writing Docs
PDF
Things I Wish People Told Me About Writing Docs
PDF
Do You Need That Validation? Let Me Call You Back About It
PDF
Rails 3 Beginner to Builder 2011 Week 8
Ruby on Rails 101
Excellent
RubyOnRails-Cheatsheet-BlaineKendall
RubyOnRails-Cheatsheet-BlaineKendall
Rails 3 (beta) Roundup
Wheels
Building Web Service Clients with ActiveModel
Building Web Service Clients with ActiveModel
Rails MVC by Sergiy Koshovyi
What I Have Learned from Organizing Remote Internship for Ruby developers
Active Record Form Helpers, Season 1
ActiveRecord Callbacks & Observers, Season 2
Where Does the Fat Goes? Utilizando Form Objects Para Simplificar seu Código
Ruby on Rails 3.1: Let's bring the fun back into web programing
UT on Rails3 2010- Week 4
Getting started with Rails (2), Season 2
LF_APIStrat17_Things I Wish People Told Me About Writing Docs
Things I Wish People Told Me About Writing Docs
Do You Need That Validation? Let Me Call You Back About It
Rails 3 Beginner to Builder 2011 Week 8
Ad

More from RORLAB (20)

PDF
Getting Started with Rails (4)
PDF
Getting Started with Rails (3)
PDF
Getting Started with Rails (2)
PDF
Getting Started with Rails (1)
PDF
Self join in active record association
PDF
Asset Pipeline in Ruby on Rails
PDF
레일스가이드 한글번역 공개프로젝트 RORLabGuides 소개
PDF
Active Support Core Extension (3)
PDF
Active Support Core Extension (2)
PDF
Active Support Core Extensions (1)
PDF
Action Controller Overview, Season 2
PDF
Action View Form Helpers - 2, Season 2
PDF
Action View Form Helpers - 1, Season 2
PDF
Layouts and Rendering in Rails, Season 2
PDF
ActiveRecord Query Interface (2), Season 2
KEY
Active Record Query Interface (1), Season 2
KEY
Active Record Association (2), Season 2
KEY
ActiveRecord Association (1), Season 2
KEY
Rails Database Migration, Season 2
KEY
Getting started with Rails (4), Season 2
Getting Started with Rails (4)
Getting Started with Rails (3)
Getting Started with Rails (2)
Getting Started with Rails (1)
Self join in active record association
Asset Pipeline in Ruby on Rails
레일스가이드 한글번역 공개프로젝트 RORLabGuides 소개
Active Support Core Extension (3)
Active Support Core Extension (2)
Active Support Core Extensions (1)
Action Controller Overview, Season 2
Action View Form Helpers - 2, Season 2
Action View Form Helpers - 1, Season 2
Layouts and Rendering in Rails, Season 2
ActiveRecord Query Interface (2), Season 2
Active Record Query Interface (1), Season 2
Active Record Association (2), Season 2
ActiveRecord Association (1), Season 2
Rails Database Migration, Season 2
Getting started with Rails (4), Season 2

Recently uploaded (20)

PDF
cuic standard and advanced reporting.pdf
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PDF
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton
PDF
Encapsulation_ Review paper, used for researhc scholars
PDF
MIND Revenue Release Quarter 2 2025 Press Release
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PPT
Teaching material agriculture food technology
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PPTX
Understanding_Digital_Forensics_Presentation.pptx
DOCX
The AUB Centre for AI in Media Proposal.docx
PDF
KodekX | Application Modernization Development
PDF
Electronic commerce courselecture one. Pdf
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PDF
Network Security Unit 5.pdf for BCA BBA.
cuic standard and advanced reporting.pdf
Chapter 3 Spatial Domain Image Processing.pdf
Diabetes mellitus diagnosis method based random forest with bat algorithm
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton
Encapsulation_ Review paper, used for researhc scholars
MIND Revenue Release Quarter 2 2025 Press Release
The Rise and Fall of 3GPP – Time for a Sabbatical?
Teaching material agriculture food technology
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
Agricultural_Statistics_at_a_Glance_2022_0.pdf
Dropbox Q2 2025 Financial Results & Investor Presentation
Understanding_Digital_Forensics_Presentation.pptx
The AUB Centre for AI in Media Proposal.docx
KodekX | Application Modernization Development
Electronic commerce courselecture one. Pdf
Per capita expenditure prediction using model stacking based on satellite ima...
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
Network Security Unit 5.pdf for BCA BBA.

ActiveRecord Validations, Season 2

  • 1. Ror lab. season 2 - the 6th round - Active Record Validations September 15th, 2012 Hyoseong Choi ROR Lab.
  • 2. Contents Validations Callbacks • The Object Life Cycle • Callbacks Overview • Validations Overview • Available Callbacks • Validation Helpers • Running Callbacks • Common Validation • Skipping Callbacks Options • Halting Execution • Conditional Validation • Relational Callbacks • Custom Validations • Conditional Callbacks • Working with Validation • Callback Classes Errors • Observers • Displaying Validation Errors • Transaction Callbacks in the View ROR Lab.
  • 3. Validation Levels • Native DB constraints - DB-dependent • Client-side validations - javascript ? • Controller-level validations - keep skinny • Model-level validations - the best way ROR Lab.
  • 4. “new” method • A new object instantiated vs. persisted? • not yet to store to database : new_record? • no validation called, but ... ROR Lab.
  • 5. Validation Time Point ActiveRecord Web Server Model DB Validation save create update clients Model-level validations ROR Lab.
  • 6. Validation Event Triggering validations Skipping validations • .create • .decrement! • .create! • #decrement_counter • .save • .increment! • .save! • #increment_counter • #update • .toggle! • .update_attributes • .touch • .update_attributes! • #update_all • .update_attribute deprecated • .update_column • #update_counters Ref.: ActiveRecord::Persistence ActiveRecord::CounterCache ROR Lab.
  • 7. Call Validations • valid? or invalid? ROR Lab.
  • 8. valid? vs Errors By definition, an object is valid if this collection is empty after running validations. ROR Lab.
  • 9. Validation Helpers • acceptance • validates_associated • confirmation • validates_each • exclusion • validates_with • format • inclusion :on • save(default) • create • length • update • numericality :message • presence • uniqueness ROR Lab.
  • 10. Common Options :allow_nil - nil :allow_blank - nil or whitespace :message - overriding default error message :on - :create / :update / :save ROR Lab.
  • 11. Conditional Validation :if and :unless • A Symbol : a method name • A String : a really short condition • A Proc : an inline condition Grouping conditions : with_options ROR Lab.
  • 12. Custom Validations • Custom validators modules : inherited from Two ★ ActiveModel::Validator ★ ActiveModel::EachValidator ★ Get the “record” argument as a parameter • Custom validation methods • Custom validation helpers ROR Lab.
  • 13. Working with Validation Errors • errors • errors.messages • errors.full_messages( or errors.to_a) • errors[:attr] : for a specific attribute • errors.add(:attr, message)(or errors[:attr]=) • errors[:base] : object’s state as a whole • errors.clear : intentionally to clear • errors.size : count of errors ROR Lab.
  • 14. Displaying Validation Errors in the View ★ gem ‘dynamic_form’ ★ Error Messages CSS .field_with_errors #errorExplanation #errorExplanation h2 #errorExplanation p #errorExplanation ul li ROR Lab.
  • 15. Validation Errors : https://github.com/joelmoss/dynamic_form Error CSS #error_explanation #error_explanation h2 #error_explanation p <%= form_for(@product) do |f| %> <% if @product.errors.any? %> <div id="error_explanation"> #error_explanation ul li <h2><%= pluralize(@product.errors.count, "error") %> prohibited this product from being saved: </h2> <ul> <% @product.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> .field_with_errors generated by scaffold apps/assets/stylesheets/scaffolds.css.scss ROR Lab.
  • 16. Validation Errors : https://github.com/joelmoss/dynamic_form Error CSS #error_explanation <%= form_for(@product) do |f| %> #error_explanation h2 <% if @product.errors.any? %> #error_explanation p <div id="error_explanation"> <h2><%= pluralize(@product.errors.count, "error") %> #error_explanation ul li prohibited this product from being saved: </h2> <ul> <% @product.errors.full_messages.each do |msg| %> <li><%= msg %></li> .field_with_errors <% end %> </ul> </div> <% end %> generated by scaffold apps/assets/stylesheets/scaffolds.css.scss ROR Lab.
  • 17. Validation Errors : https://github.com/joelmoss/dynamic_form Error CSS apps/assets /stylesheets /scaffolds.css.scss #error_explanation .field_with_errors { #error_explanation h2 padding: 2px; background-color: red; #error_explanation p display: table; } #error_explanation { #error_explanation ul li width: 450px; border: 2px solid red; padding: 7px; padding-bottom: 0; margin-bottom: 20px; background-color: #f0f0f0; h2 { .field_with_errors text-align: left; font-weight: bold; padding: 5px 5px 5px 15px; font-size: 12px; margin: -7px; margin-bottom: 0px; background-color: #c00; color: #fff; } ul li { generated by scaffold font-size: 12px; list-style: square; ROR Lab.
  • 18. Validation Errors : ‘dynamic_form’ • f.error_messages or • error_messages_for :product <%= form_for(@product) do |f| %> <%= f.error_messages %> <% end %> ROR Lab.
  • 19. Validation Errors : https://github.com/joelmoss/dynamic_form ‘dynamic_form’ generated by dynamic_form r_ tag e :h ead :header_message :message <%= f.error_messages :header_message => "Invalid product!",   :message => "You'll need to fix the following fields:",   :header_tag => :h3 %> ROR Lab.
  • 20. Validation Errors : https://github.com/joelmoss/dynamic_form Error HTML config/initializers/custom_error_message_html.rb field object ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|   errors = Array(instance.error_message).join(',') unless html_tag =~ /^<label/    %(#{html_tag}<span class="validation-error">&nbsp;#{errors}</span>).html_safe else %(#{html_tag}).html_safe end end #{html_tag} .validation-error ROR Lab.
  • 21. Validation Errors : https://github.com/joelmoss/dynamic_form Error HTML <% if @product.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(@product.errors.count, "error") %> prohibited this product from being saved:</h2> </div> <% end %> ROR Lab.
  • 23.  
  • 25.  
  • 26. Active Record Counter Cache • #decrement_counter • #increment_counter skipping • #reset_counters validations • #update_counters ROR Lab.
  • 28. toggle vs toggle! ROR Lab.
  • 29. touch ROR Lab.
  • 30. #update_all ROR Lab.
  • 31. .update_column ROR Lab.
  • 32. #update ROR Lab.
  • 33. Validation Helpers : acceptance • a checkbox : a real or virtual attribute • “must be accepted” class Person ActiveRecord::Base   validates :terms_of_service, :acceptance = true class Person ActiveRecord::Base   validates :terms_of_service, :acceptance = { :accept = 'yes' } ROR Lab.
  • 34. Validation Helpers : validates_associated • should if associated with other models • “is invalid” class Library ActiveRecord::Base   has_many :books   validates_associated :books • should use on one end of your associations ROR Lab.
  • 35. Validation Helpers : confirmation • a virtual attribute appended with “_confirmation” • “doesn’t match confirmation” class Person ActiveRecord::Base   validates :email, :confirmation = true   validates :email_confirmation, :presence = true %= text_field :person, :email % %= text_field :person, :email_confirmation % ROR Lab.
  • 36. Validation Helpers : exclusion • not included in a given set :any enumerable object • “is reserved” class Account ActiveRecord::Base   validates :subdomain, :exclusion = { :in = %w(www us ca jp),     :message = Subdomain %{value} is reserved. } ROR Lab.
  • 37. Validation Helpers : format • match a given regular expression • “is invalid” class Product ActiveRecord::Base   validates :legacy_code, :format = { :with = /A[a-zA-Z]+z/,      :message = Only letters allowed } ROR Lab.
  • 38. Validation Helpers : inclusion • included in a given set : any enumerable object • “is not included in the list” class Coffee ActiveRecord::Base   validates :size, :inclusion = { :in = %w(small medium large),      :message = %{value} is not a valid size } ROR Lab.
  • 39. Validation Helpers : length (1/2) • length constraint options - :minimum, :maximum, :in/:within, :is - a placeholder - %{count} 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 } ROR Lab.
  • 40. Validation Helpers : length (2/2) • options - :wrong_length, :too_long, :too_short - a placeholder - %{count} class Essay ActiveRecord::Base   validates :content, :size = {     :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   } ROR Lab.
  • 41. Validation Helpers : numericality (1/2) • only numeric values : (+/−) integer/floating point • “is not a number” class Person ActiveRecord::Base   validates :email, :confirmation = true   validates :email_confirmation, :presence = true %= text_field :person, :email % %= text_field :person, :email_confirmation % ROR Lab.
  • 42. Validation Helpers : numericality (2/2) class Player ActiveRecord::Base   validates :points, :numericality = true   validates :games_played, :numericality = { :only_integer = true } /A[+−]?d+Z/ •:greater_than ➡ “must be greater than %{count}” •:greater_than_or_equal_to ➡ “must be greater than or equal to %{count} •:equal_to ➡ “must be equal to %{count}” •:less_than ➡ “must be less than %{count}” •:less_than_or_equal_to ➡ “must be less than or equal to %{count} •:odd ➡ “must be odd” •:even ➡ “must be even” ROR Lab.
  • 43. Validation Helpers : presence (1/2) • empty or whitespaces • “can’t be empty” class Person ActiveRecord::Base   validates :name, :login, :email, :presence = true end class LineItem ActiveRecord::Base   belongs_to :order   validates :order_id, :presence = true ROR Lab.
  • 44. Validation Helpers : presence (2/2) • empty or whitespaces • “can’t be empty” ✘ class Person ActiveRecord::Base   validates :is_admin, :presence = true end a boolean field ➞ false.blank? is true class Person ActiveRecord::Base   validates :is_admin, :inclusion = { :in = [true, false] } ROR Lab.
  • 45. Validation Helpers : uniqueness • add_index :table_name, :column_name, unique = true • “has already been taken” class Account ActiveRecord::Base   validates :email, :uniqueness = true other end attributes class Holiday ActiveRecord::Base   validates :name, :uniqueness = { :scope = :year,     :message = should happen once per year } end class Person ActiveRecord::Base   validates :name, :uniqueness = { :case_sensitive = false } ROR Lab.
  • 46. Validation Helpers : validates_with (1/2) • a separate class for validation • no default validate error message class Person ActiveRecord::Base   validates_with GoodnessValidator,[:if/:unless/:on] end   class GoodnessValidator ActiveModel::Validator   def validate(record)     if record.first_name == Evil       record.errors[:base] This person is evil     end   end ROR Lab.
  • 47. Validation Helpers : validates_with (2/2) • any additional options ➞ options 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 ROR Lab.
  • 48. Validation Helpers : validates_each • no predefined validation function ➞a block • no default error message 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 ROR Lab.
  • 49. Common Validation Options : :allow_nil class Coffee ActiveRecord::Base   validates :size, :inclusion = { :in = %w(small medium large),     :message = %{value} is not a valid size }, :allow_nil = true ROR Lab.
  • 50. Common Validation Options : :allow_blank • nil or an empty string class Topic ActiveRecord::Base   validates :title, :length = { :is = 5 }, :allow_blank = true end   Topic.create(title = ).valid?  # = true ROR Lab.
  • 51. Common Validation Options : :on • when the validation should happen 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 ROR Lab.
  • 52. Conditional Validation :if :unless (1/4) • Using a Symbol : a method name class Order ActiveRecord::Base   validates :card_number, :presence = true, :if = :paid_with_card?     def paid_with_card?     payment_type == card   end ROR Lab.
  • 53. Conditional Validation :if :unless (2/4) • Using a String : a really short condition class Person ActiveRecord::Base   validates :surname, :presence = true, :if = name.nil? ROR Lab.
  • 54. Conditional Validation :if :unless (3/4) • Using a Proc : an inline condition class Account ActiveRecord::Base   validates :password, :confirmation = true,     :unless = Proc.new { |a| a.password.blank? } a model object ROR Lab.
  • 55. Conditional Validation :if :unless (4/4) • grouping conditional validations condition object class User ActiveRecord::Base   with_options :if = :is_admin? do |admin|     admin.validates :password, :length = { :minimum = 10 }     admin.validates :email, :presence = true   end ROR Lab.
  • 56. Custom Validations : Custom Validators (1/2) • to extend ActiveMode::Validator • to validate the state of whole record 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 ROR Lab.
  • 57. Custom Validations : Custom Validators (2/2) • to extend ActiveMode::EachValidator • to validate individual attributes 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   EmailValidator class Person ActiveRecord::Base   validates :email, :presence = true, :email = true end ROR Lab.
  • 58. Custom Validations : Custom Methods (1/2) • to verify the state of models 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 EmailValidator     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 ROR Lab.
  • 59. Custom Validations : Custom Methods (2/2) • to verify the state of models class Invoice ActiveRecord::Base   validate :active_customer, :on = :create     def active_customer     errors.add(:customer_id, is not active) unless customer.active?   end ROR Lab.
  • 60. Custom Validations : Custom Helpers • to reuse in several different models • to put in config/initializers 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 class Movie ActiveRecord::Base   validates_as_choice :rating, 5 end ROR Lab.
  • 61. Validation Errors : errors • an instance of ActiveModel::Errors • key : attribute name value : an array of strings with all errors 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 # = [] ROR Lab.
  • 62. Validation Errors : errors[ ] • to check the error message of a specific attribute 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)] ROR Lab.
  • 63. Validation Errors : errors.add (1/2) • to manually add messages of a specific attribute 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 !@#%*()_-+=] ROR Lab.
  • 64. Validation Errors : errors.add (2/2) -or- 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 !@#%*()_-+=] ROR Lab.
  • 65. Validation Errors : errors[:base] • related to the object’s state as a whole • an array class Person ActiveRecord::Base   def a_method_used_for_validation_purposes     errors[:base] This person is invalid because ...   end end ROR Lab.
  • 66. Validation Errors : errors.clear • related to the object’s state as a whole • an array 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)] ROR Lab.
  • 67. Validation Errors : errors.size 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 ROR Lab.

Editor's Notes