Alex's Slip-box

These are my org-mode notes in sort of Zettelkasten style


:ID: 40FFCDB2-F065-4EDC-9DED-C3007827B470

This is a big ass dump of ActiveRecod stuff I’ve learned.

An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data. —Martin Fowler, Patterns of Enterprise Architecture

# ActiveRecord Configuration

# Override Table Name and Primary Key

  • Useful for when you building data models for a database you don’t control.
class Client < ApplicationRecord
  self.table_name = "CLIENT"
  self.primary_key = "CID"

# Pluralization

Rails.application.config.active_record.pluralize_table_names = false

# Other Customizations

Attributes of ActiveRecord::Base


# Override and Customize Accessor

  • write_attribute
  • read_attribute
def message=(txt)
  write_attribute(:message, txt + ' in bed')
#  or
def message=(txt)
  self[:message] = txt + ' in bed'

# Query Cache

By default, Rails attempts to optimize performance by turning on a simple query cache. It is a hash stored on the current thread, one for every active database connection.

The cache lives for the duration of a controller action.

See also

# Manual Query Cache

User.cache do
  puts User.first
  puts User.first
  puts User.first

# Attributes

# Hash Notation

  • AR objects can often be treated like a hash.
  • Can access attrs with bracket notation or call slice directly on the object.

# Typecasting


before_validation :fix_rate
def fix_rate
  self[:rate] ='$,','')

# Readonly

  • Set an attribute to be read only.
  • Doesn’t apply to new records getting saved for the first time.
  • Doesn’t raise errors when attempting to save a read only attribute.
attr_readonly :social_security_number

# has_secure_token

  • Generate a token.
  • Can assign a column name by passing a symbol representing the column name (defaults to :token)
  • If you declare the type of your column as token in a migration, then Rails will automatically add it as a string with a unique index.
class User < ApplicationRecord
>> user = User.create
>> => # < User id: … >> user.token = > “njHcvhKSwX9toZKEe9YETA8C”>> user.regenerate_token
has_secure_token :auth_token

# Attributes API

attribute :category, :string, default: 'n/a'
attribute :repeats, :boolean, default: false

# Custom Types

ActiveRecord::Type.register :inquiry,
class Inquiry < ActiveRecord::Type::String
  def type

  def cast(value)
    super.inquiry #  See docs for [String.inquiry](:note:059eadc0-9cd8-4955-bb41-b579e5877cb7)

class Event < ApplicationRecord
  attribute :repeats,

# Counting Associations


# Counter cache

  • Depends on a ~*_count column added to the parent table.
  • declared counter_cache adds callbacks to update the count cache on the parent
belongs_to :user, counter_cache: true

See also

# Store

Wrap key/value store columns (eg, jsonb) and define accessors. Add typecasting wit

module Asteroid
  class NearEarthObject < ApplicationRecord
    validates :name, :absolute_magnitude_h, :estimated_diameter, presence: true
    store :estimated_diameter, accessors: %i[kilometers meters miles feet]
    alias_attribute :id, :neo_reference_id

# Update

# Mass Updates


  • 2nd arg is an optional where clause. The args can be SQL strings as well.
Project.update_all({manager: 'Ron Campbell'}, technology: 'Rails')
user.post_ids = [1, 2, 3]

# Mass Delete

Timesheet.destroy([2, 3])
delete_all # skips callbacks (like dependent: :destroy)

# Skipping Validation or Callbacks

update_attribute: Update a single attribute and skip validation (still runs callbacks). update_column / update_columns: skips validation and callbacks touch: false: Skips setting the timestamps false)

validate: false

# Updating Timestamps (touch)

user.touch # => sets updated_at to now. user.touch(:viewed_at) #  sets viewed_at and updated_at to now.

# Timestamps via association

class User < ApplicationRecord
  belongs_to :client, touch: true
user.touch #  also calls user.client.touch

# Database Locking

See also Database Locks

# Optimistic Locking

Good for infrequent collisions. Database records are not actually locked. If two different model instances are loaded for the same record and saved differently, the first one wins while the second one raises ActiveRecord::StaleObject.

  • Add an integer column named lock_version to a given table, with a default value of zero.
  • Where the records are updated, add handlers for the ActiveRecord::StaleObject error

# Pessimistic Locking

Locks the records returns from a select statement. Works with transactions. Records are released when the transaction completes. Keep the transactions small to make sure they execute quickly.

Timesheet.transaction do
  t = Timesheet.lock.first
  t.approved = true!

Can also pass a locking clause"FOR UPDATE OF pa_requests NOWAIT")

# QueryingOther

# Where with JSON field (Postgres)

User.where('preferences @> ?', { newsletter: true }.to_json)

# Bind Variables

Message.where("subject LIKE :foo OR body LIKE :foo", foo: '%woah%')

# Limit/Offset

Aliased as take/skip


# Select

Using select for a limited set of columns could increase query performance by making it more likely the data will be queried using an efficient Index Only Scan.

  • Add calculated or composed columns
  • NOTE: the calculated column will show in attributes method or calling the method directly
b ='*', "mon_hrs + tues_hrs as two_day_total").first
b.two_day_total # => 16

# From (and Aliasing Table Names)'title').from(Topic.approved).to_sql
# => "SELECT title FROM (SELECT * FROM topics WHERE approved = 't')"

# Ignoring columns

class User < ApplicationRecord
  self.ignored_columns = %w(some_stupid_bs_column)

# Group

Usually used with select.

users ='name, SUM(cash) as money').group('name').to_a

# With having(clauses).


Dealer.joins(:quotes).group("").having('count( > 1')"created_at").having(["created_at > ?", 2.days.ago])
Person.having('min(age) > 17').group(:last_name).minimum(:age)

# Includes / Eager Loading / Preloading

Using includes will produce LEFT OUTER JOIN to grab additional associated data if there is a where condition. Otherwise will do two queries (an additional WHERE IN)to get the associated data.

Using strict_loading will raise an error on attempted lazy loading. In order to get access to associated records, one must eager load.

User.includes(:auctions).where(auctions: { name: 'Lumina' })

# find_or_create_by / create_with

Use retry in a begin / end block when rescuing ActiveRecord::RecordNotUnique to deal with race conditions.

User.create_with(active: true).find_or_create_by(first_name: 'Buster', ...)
User.find_or_create_by(first_name: 'Poppa') do |user|
  user.last_name = 'Smurf'

# Explain"").from(Asteroid::NearEarthObject.where(is_potentially_hazardous_asteroid: false), :a).explain

# Existence

  • exists?
  • any?
  • empty? (returns the count if not empty or true)
  • many? (more than one)
  • one?
  • None The query method returns Active-Record::NullRelation, which is an implementation of the Null Object pattern. It is to be used in instances where you have a method that returns a relation, but there is a condition in which you do not want the database to be queried. All subsequent chained conditions will work without issue, eliminating the need to continuously check whether the object your are working with is a relation.

# Other Noteworthy Query Methods

  • readonly
  • reorder
  • reverse_order
  • merge
  • only(*onlies)
  • except(*skips)
  • to_json (also to_xml and to_yaml)
  • unscope (useful for unscoping default scopes). Can take :from, :having, :joins, …etc
Member.where(name: "Fool", active: true).unscope(where: :name)
  • unscoped (removes all scopes including the default scope)
Timesheet.unscoped.where("created_at < ?", 1.year.ago)

# Query using SQL

# find_by_sql(String)

User.find_by_sql("select * from users limit 1")
  • Returns AR objects based on the SQL string arg
  • Use bind variables
  • Use this for writing complex SQL queries that don’t lend themselves well to using AR’s query API (like having to use CTEs)

# execute

  • This returns a PG::Result object.
  • This can save a lot of memory since it will not return AR objects.

# Using the Database Connection Directly

  • Using a connection object.
  • There’s lots of methods that can be called on the connect adapter object.
ActiveRecord::Base.connectionconn.tables #  => an array of the table names
ActiveRecord::Base.connection.execute("select * from asteroid_near_earth_objects").values
  • .select_rows(“select * from asteroid_orbit”)
  • .select_all
  • .select_one
  • .select_values

# Executing a SQL script in a file

  • Read the file, split on ‘;’ and execute each statement.
def execute_sql_file(path) split(';').each do |sql|
      ActiveRecord::Base.connection.execute(#{sql}") unless sql.blank?
    rescue ActiveRecord::StatementInvalid
        $stderr.puts "warning: #{$!}"

# Polymorphic Associations

# Doing SQL joins with polymorphic associations.

Activity.joins("LEFT JOIN users ON activities.owner_type = 'User' AND activities.owner_id =")
Activity.joins("LEFT JOIN managers ON activities.owner_type = 'Manager' AND activities.owner_id =")

# Associations

# has_many

  • returns ActiveRecord::CollectionProxy
    • @owner
    • @target
    • @reflection

# ActiveRecord::CollectionProxy Methods

Here’s a bunch of examples…*, "calc_something(col1, col2) as delta").to_a
  • before_add, after_add
has_many :unchangable_posts, class_name: "Post", before_add: ->(owner, record) { raise "Can't do it!" }
has_many :pending_comments, -> { where( approved: true) }, class_name: 'Comment'
class Client < ActiveRecord::Base
  has_many :timesheets, -> { distinct }, through: :billable_weeks
class User < ActiveRecord::Base
  has_many :timesheets
  has_one :latest_sheet, -> { order(' created_at desc') }, class_name: 'Timesheet'
belongs_to :project, -> { readonly }
belongs_to :post, -> { includes(:author) }

# Extending Associations


has_many :people do
  def named(full_name)
    first_name, last_name = full_name.split(" ", 2)
    where(first_name: first_name, last_name: last_name).first_or_create

Same thing, but using modules…

has_many :people, -> { extending(ByNameExtension, ByRecentExtension) }

# Association with null object pattern

belongs_to :automatic_payment_method, class_name: 'PaymentMethod'

def automatic_payment_method
  super ||

# Callback Classes

It is common enough to want to reuse callback code for more than one object that Rails gives you a way to write callback classes. All you have to do is: Pass a given callback queue an object that responds to the name of the callback and takes the model object as a parameter.

Soft delete example

class MarkDeleted
  def self.before_destroy(model)
    model.update_attribute(:deleted_at, Time.current)

class Account < ActiveRecord::Base
  before_destroy MarkDeleted

# Value Objects

Unlike with object identity, value objects are considered equal when their attributes are equal. Example of an Address type value object

class Person < ActiveRecord::Base
  def address
    @address || =, address_state)

  def address=(address)
    self[:address_city] =
    self[:address_state] = address.state
    @address = address

class Address
  attr_reader :city, :state

  def initialize(city, state)
    @city, @state = city, state

  def ==(other_address)
    city == && state == other_address.state

# Gems / Extensions

See also Ruby gems

# store_attribute

# Query logs

See also

Append or prepend comments to queries so you can find them in logs easier.


Search Results