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

Viewers also liked (10)

PPT
ActiveWarehouse/ETL - BI & DW for Ruby/Rails
Paul Gallagher
 
ODP
Performance Optimization of Rails Applications
Serge Smetana
 
PDF
Introduction to Ruby on Rails
Agnieszka Figiel
 
PPTX
Neev Expertise in Ruby on Rails (RoR)
Neev Technologies
 
ODP
Ruby on Rails
Aizat Faiz
 
PDF
Distributed Ruby and Rails
Wen-Tien Chang
 
PDF
Ruby Beyond Rails
Gaveen Prabhasara
 
PDF
From a monolithic Ruby on Rails app to the JVM
Phil Calçado
 
PDF
Ruby On Rails Introduction
Thomas Fuchs
 
PDF
Design in Tech Report 2017
John Maeda
 
ActiveWarehouse/ETL - BI & DW for Ruby/Rails
Paul Gallagher
 
Performance Optimization of Rails Applications
Serge Smetana
 
Introduction to Ruby on Rails
Agnieszka Figiel
 
Neev Expertise in Ruby on Rails (RoR)
Neev Technologies
 
Ruby on Rails
Aizat Faiz
 
Distributed Ruby and Rails
Wen-Tien Chang
 
Ruby Beyond Rails
Gaveen Prabhasara
 
From a monolithic Ruby on Rails app to the JVM
Phil Calçado
 
Ruby On Rails Introduction
Thomas Fuchs
 
Design in Tech Report 2017
John Maeda
 

Similar to ActiveRecord Validations, Season 2 (20)

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

More from RORLAB (20)

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

Recently uploaded (20)

PDF
Julia Furst Morgado The Lazy Guide to Kubernetes with EKS Auto Mode + Karpenter
AWS Chicago
 
PDF
Market Insight : ETH Dominance Returns
CIFDAQ
 
PDF
Productivity Management Software | Workstatus
Lovely Baghel
 
PDF
Human-centred design in online workplace learning and relationship to engagem...
Tracy Tang
 
PDF
NewMind AI Journal - Weekly Chronicles - July'25 Week II
NewMind AI
 
PPTX
Top Managed Service Providers in Los Angeles
Captain IT
 
PDF
CIFDAQ Market Insight for 14th July 2025
CIFDAQ
 
PDF
Meetup Kickoff & Welcome - Rohit Yadav, CSIUG Chairman
ShapeBlue
 
PDF
2025-07-15 EMEA Volledig Inzicht Dutch Webinar
ThousandEyes
 
PDF
OpenInfra ID 2025 - Are Containers Dying? Rethinking Isolation with MicroVMs.pdf
Muhammad Yuga Nugraha
 
PPTX
The Yotta x CloudStack Advantage: Scalable, India-First Cloud
ShapeBlue
 
PDF
Generative AI in Healthcare: Benefits, Use Cases & Challenges
Lily Clark
 
PPTX
Extensions Framework (XaaS) - Enabling Orchestrate Anything
ShapeBlue
 
PPTX
UI5Con 2025 - Beyond UI5 Controls with the Rise of Web Components
Wouter Lemaire
 
PDF
Bitcoin+ Escalando sin concesiones - Parte 1
Fernando Paredes García
 
PPTX
Building and Operating a Private Cloud with CloudStack and LINBIT CloudStack ...
ShapeBlue
 
PPTX
python advanced data structure dictionary with examples python advanced data ...
sprasanna11
 
PDF
UiPath vs Other Automation Tools Meeting Presentation.pdf
Tracy Dixon
 
PDF
Ampere Offers Energy-Efficient Future For AI And Cloud
ShapeBlue
 
PDF
Market Wrap for 18th July 2025 by CIFDAQ
CIFDAQ
 
Julia Furst Morgado The Lazy Guide to Kubernetes with EKS Auto Mode + Karpenter
AWS Chicago
 
Market Insight : ETH Dominance Returns
CIFDAQ
 
Productivity Management Software | Workstatus
Lovely Baghel
 
Human-centred design in online workplace learning and relationship to engagem...
Tracy Tang
 
NewMind AI Journal - Weekly Chronicles - July'25 Week II
NewMind AI
 
Top Managed Service Providers in Los Angeles
Captain IT
 
CIFDAQ Market Insight for 14th July 2025
CIFDAQ
 
Meetup Kickoff & Welcome - Rohit Yadav, CSIUG Chairman
ShapeBlue
 
2025-07-15 EMEA Volledig Inzicht Dutch Webinar
ThousandEyes
 
OpenInfra ID 2025 - Are Containers Dying? Rethinking Isolation with MicroVMs.pdf
Muhammad Yuga Nugraha
 
The Yotta x CloudStack Advantage: Scalable, India-First Cloud
ShapeBlue
 
Generative AI in Healthcare: Benefits, Use Cases & Challenges
Lily Clark
 
Extensions Framework (XaaS) - Enabling Orchestrate Anything
ShapeBlue
 
UI5Con 2025 - Beyond UI5 Controls with the Rise of Web Components
Wouter Lemaire
 
Bitcoin+ Escalando sin concesiones - Parte 1
Fernando Paredes García
 
Building and Operating a Private Cloud with CloudStack and LINBIT CloudStack ...
ShapeBlue
 
python advanced data structure dictionary with examples python advanced data ...
sprasanna11
 
UiPath vs Other Automation Tools Meeting Presentation.pdf
Tracy Dixon
 
Ampere Offers Energy-Efficient Future For AI And Cloud
ShapeBlue
 
Market Wrap for 18th July 2025 by CIFDAQ
CIFDAQ
 

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 = [email protected]) person.valid? # = true person.errors.size # = 0 ROR Lab.

Editor's Notes