Active Record Models

Active Record is the M in MVC - the model - which is the layer of the system responsible for representing business data and logic. It is an implementation of the Active Record pattern which itself is a description of an Object Relational Mapping (ORM) system.

Active Record gives us several mechanisms, the most important being the ability to:

  • Represent models and their data
  • Represent associations between these models
  • Represent inheritance hierarchies through related models
  • Validate models before they get persisted to the database
  • Perform database operations in an object-oriented fashion

Models generation

Naming Conventions

Model / Class Table / Schema
Post posts
LineItem line_items
Deer deer
Mouse mice
Person people

Active Record Migrations

Migrations are a feature of Active Record that allows you to evolve your database schema over time. Rather than write schema modifications in pure SQL, migrations allow you to use an easy Ruby DSL to describe changes to your tables.

Overview

Schema Conventions

Add Migrations

db/migrate/20130602150030_add_last_name_to_users.rb db/migrate/20130602151002_add_receive_news_to_users.rb db/migrate/20130602151002_add_receive_news_to_users.rb

Methods for db structure changing

create_table change_table add_column change_column rename_column remove_column add_index remove_index drop_table

Column Types

Example Migration

Run run run ruuuuuuuuun

Running Migrations

Rollback Migrations

Active Record Objects

ActiveRecord functions

new, all, save

Rails 3

Rails 4

ActiveRecord functions

create, count, update_attributes

Active Record Associations

belongs_to

belongs_to

db/migrate/20130615164224_add_user_ref_to_posts.rb app/models/post.rb

has_many

has_many

app/models/user.rb

has_one

has_one

app/models/post_info.rb app/models/post.rb Creation PostInfo Creation using the association

has_many :through

has_many :through

db/migrate/20130615195018_create_blogs.rb db/migrate/20130615200154_create_subscriptions.rb db/migrate/20130616200237_add_blog_ref_to_posts.rb app/models/user.rb app/models/blog.rb app/models/subscription.rb app/models/post.rb

has_one :through

has_one :through

db/migrate/20130616211135_create_plans.rb db/migrate/20130616211306_create_user_plans.rb app/models/user.rb app/models/user_plan.rb app/models/plan.rb

has_and_belongs_to_many

has_and_belongs_to_many

Rails 4

db/migrate/20130615205723_create_posts_tags.rb

Change migration. Add common index and remove id.

db/migrate/20130615205723_create_posts_tags.rb app/models/post.rb app/models/tag.rb

Polymorphic Associations

Polymorphic Assosiations

db/migrate/20130616213914_create_pictures.rb app/models/picture.rb app/models/user.rb app/models/post.rb

Active Record Validations

When Does Validations Happen?

Skiping Validations

valid? and invalid?

errors[]

Presence

Acceptance

Do not this validation on the both sides of the association it causes endless cycle.

Confirmation

Inclusion

Exclusion

Format

Length

Numericality

Uniqueness

Validates Assosiated

validates_with

validate_each

General options

:allow_nil and :allow_blank are ignored by :presence

Condional validations

Custom Validators

Custom Methods

Validation Errors

errors.add

Active Record Callbacks

Callback registrations

Availiable Callbacks

Creating an Object

  1. before_validation
  2. after_validation
  3. before_save
  4. around_save
  5. before_create
  6. around_create
  7. after_create
  8. after_save

Updating an Object

  1. before_validation
  2. after_validation
  3. before_save
  4. around_save
  5. before_update
  6. around_update
  7. after_update
  8. after_save

Destroying an Object

  1. before_destroy
  2. around_destroy
  3. after_destroy

After_find and after_initialize

Running Callbacks

after_find

Skipping callbacks

Relational Callbacks

Conditional Callbacks

Callback Classes

Query Interface

Retrieving Objects from the Database

Retrieving a Single Object

Using a Primary Key take first last take, first, last, find_by returns nil if no matching record is found and no exception will be raised. take!, first!, last!, find_by! raise ActiveRecord::RecordNotFound if no matching record is found. find_by

Retrieving Multiple Objects

Using Multiple Primary Keys take first last

Retrieving Multiple Objects in Batches

find_each :batch_size :start find_in_batches

Conditions

String conditions Array conditions Placeholder conditions Hash conditions Range conditions Subset conditions NOT conditions

Ordering

ASC, DESC ordering by multiple fields

Selecting

distinct

Limit and Offset

limit offset

Group

Having

Overriding Conditions

except unscope only reorder reverse_order

Null Relation

Readonly

Locking Records for Update

Optimistic locking

Optimistic locking allows multiple users to access the same record for edits, and assumes a minimum of conflicts with the data. It does this by checking whether another process has made changes to a record since it was opened, an ActiveRecord::StaleObjectError exception is thrown if that has occurred and the update is ignored.

lock_version field should present. Each update to the record increments the lock_version column and the locking facilities ensure that records instantiated twice will let the last one saved raise a StaleObjectError if the first was also updated.

Locking Records for Update

Pessimistic locking

It provides support for row-level locking using SELECT … FOR UPDATE and other lock types.

Joining Tables

Using a String SQL Fragmen Joining a Single Association Joining Multiple Associations Joining Nested Associations (Single Level) Joining Nested Associations (Multiple Level) Specifying Conditions on the Joined Tables

Eager Loading Associations

Array of Multiple Associations Nested Associations Hash Specifying Conditions on Eager Loaded Associations

Scopes

Passing in arguments Merging of scopes If we do want the last where clause to win default_scope - will be applied across all queries - will be overridden by scope and where conditions Removing All Scoping

Find or Build a New Object

find_or_create_by find_or_initialize_by

Finding by SQL

find_by_sql select_all pluck ids

Existence of Objects

exists? any? and many? via a model via a relation via an association

Calculations

count average minimum maximum sum

Testing Models

A model spec should include tests for the following:

  • Validations
  • Associations
  • Callbacks
  • Class and instance methods

Tests configuration

Gemfile config/application.rb

Basic structure

app/models/user.rb app/mailers/user_mailer.rb spec/models/user_spec.rb

Database Cleaner

DatabaseCleaner is a set of strategies for cleaning your database in Ruby. The original use case was to ensure a clean state during tests. Each strategy is a small amount of code but is code that is usually needed in any ruby app that is testing with a database.

spec/spec_helper.rb

FactoryGirl

FactoryGirl is a library for setting up Ruby objects as test data.

Faker

Faker is used to easily generate fake data: names, addresses, phone numbers, etc.

Testing validations

Testing associations

Testing scopes

Testing callbacks

Shoulda matchers

Shoulda-matchers provides Test::Unit and RSpec-compatible one-liners that test common Rails functionality.

spec/models/user_spec.rb

Homework. Amazon.

Business logic structure:

Book

Category

Author

Rating

Customer

Order

OrderItem

Address

Country

CreditCard

Necessary to create a DB structure, models, associations and validations to models, autotests and factories for autotests.

/

#