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 needed in the past.

# ActiveRecord

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.

  • 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

# 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

  • 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.
    • Just add an integer column named lock_version to a given table, with a default value of zero.
    • Handle 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! 

# QueryingOther

  • Where
    • JSON field (Postgres)
      User.where('preferences @> ?', { newsletter: true }.to_json)
  • Bind Variables
    Message.where("subject LIKE :foo OR body LIKE :foo", foo: '%woah%')
  • With Joins
    User.joins(:posts).where(posts: { active: true })
  • Limit/Offset

    Aliased as take/skip

  • Select
    • 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

    LEFT OUTER JOIN to grab additional associated data. Delegates to eager_load when joining, and to preload when doing two queries (an additional WHERE IN)to get the associated data.With where

    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)
    • find_by_sql(String)
      • Returns 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)

# 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) }
  • Extenting 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

Search Results