Ruby on Rails: Validation, Queries, Layouts, and Styling in Rails April 15th, 2015

  • You can also validate any object, not just ActiveRecord objects
  • Declare rules using validation helpers (e.g. validates :title, presence: true … which validates that the title is there)
  • Lifecycle methods – save, update, and create
  • “Bang” method variants raise exception if object is not valid (e.g. save!, create!, update!)
  • include ActiveModel::Validations
  • Validations populate the errors object
  • Conditional validation: validate only if (or unless) certain conditions are true … unless: “pages.blank?”
  • You can bypass validation manually by passing validate: false argument … book.save(validate: false) … useful in specific use cases, e.g. save a “draft” object for a user to return and complete later
  • Optional fields with validation when filled out … validates :ssn, length: { is: 8 }, allow_blank: true
  • Create custom validation class that extends ActiveModel::Validator and use validates_with
  • You can also define methods in your class for specialized validation
    • Add directly to the errors object
    • Register them using validate method
      class Person < ActiveRecord::Base
        validate :birthday_cannot_be_in_the_future
      
        def birthday_cannot_be_in_the_future
          if birthday.present? && birthday > Date.today
            errors.add(:birthday, "can't be in the future")
          end
        end
      end
      
  • The Flash is a way to send messages to users like “Your order was placed” or “The item was removed from your cart”
    • Standard types
      • notice
      • alert
      • flash
  • Sessions store small amounts of data for each user that persist across requests (in cookies)
    • Stored in a hash
      • session[:user_id] = user.id
      • user = User.find(session[:user_id])
  • Active Record Query Interface
    • Main concept: ActiveRecord::Relation
    • Power comes from chaining multiple relations together and lazy evaluation of SQL queries
  • SQL = Structured Query Language and is used in all relational databases
  • From an earlier class… Book.all, Book.count, Book.find(42), Book.find_by(title: ‘The Hobbit’)…
    • These execute SQL queries immediately and do not return relations
  • You can chain multiple AND conditions … Book.where(‘author = ?’, ‘Tolkien’).where(‘pages < ?’, 400)
  • SQL injection alert!…
    • NEVER DO THIS… EVER… SERIOUSLY… Account.where(“number = ‘#{params[:acct_number]}'”)
    • What happens if the acct_number parameter (presumably from a malicious user) is ‘ OR ” = ‘?
    • SELECT “accounts”.* FROM “accounts” WHERE (number = ” OR ” = ”) … DOH!
  • Use anonymous parameters with ? to avoid SQL injections … Book.where(‘author = ? and pages < ?’, ‘Tolkien’, 400) = SELECT “books”.* FROM “books” WHERE (author = ‘Tolkien’ and pages < 400)
  • Scopes encapsulate common queries
    • Return ActiveRecord::Relation … so they can be chained together just like where, order, limit, etc.
    • Similar to defining class methods in a model
      • Scopes handle some edge cases
      • Scopes can make the intent more clear
  • Use find_by_sql if you need to do very complicated custom SQL queries not supported by ActiveRecord
  • Layouts reside in app/views/layouts
  • An application has an common layout named application.html.erb
    • yield inserts content from the view rendered by the controller
    • content_for produces content to be inserted into a “named” yield
  • Partials encapsulate separate content (header, footer, navigation bar, flashes, etc.)
  • The Asset Pipeline is the mechanism in Rails used to manage assets in different deployment environments (e.g. development, production)
    • 3 main features
      • Concatenate assets
      • Compress/minify assets
      • Abstract the asset implementation language
  • Asset “Fingerprinting” for cache busting in production
  • Manifests determine which assets to include and contain directives which tell Rails which files to require in order to build concatenated assets…
    • require_self requires code in the current file
    • require_tree recursively requires all files in a directory and subdirectories
    • require_directory does not recurse
  • When assets are precompiled for production mode, they are placed in public/assets by default
  • Kaminari gem provides easy pagination
    • Add gem ‘kaminari’ and ‘kaminari-bootstrap’ to Gemfile
      • gem ‘kaminari’
      • gem ‘kaminari-bootstrap’, ‘~> 3.0.1’
    • Then bin/bundle install
    • Run rails g kaminari:config (g is short for generate)
      • Override config.default_per_page option in kaminari_config.rb
    • Add route for friendly URLS
      resources :books do
        get 'page/:page', :action => :index, :on => :collection
      end
      
    • Call page method in controller
      • Book.order(:title).page(params[:page])
    • Populate in the view for index.html.erb
      • <%= paginate @books %>