ActiveRecord

ActiveRecord is "the core ORM (Object-Relational Mapping) library of Ruby on Rails" developed as the most essential database manipulation library in the Ruby ecosystem. Designed based on Martin Fowler's "Active Record" pattern, it automatically maps database tables to Ruby classes. Through an intuitive and readable API, it enables complex database operations without writing SQL statements, establishing itself as an indispensable tool for Rails developers.

ORMRubyRailsDatabaseActiveRecordSQLModel

GitHub Overview

rails/rails

Ruby on Rails

Stars57,276
Watchers2,311
Forks21,912
Created:April 11, 2008
Language:Ruby
License:MIT License

Topics

activejobactiverecordframeworkhtmlmvcrailsruby

Star History

rails/rails Star History
Data as of: 8/13/2025, 01:43 AM

Library

ActiveRecord

Overview

ActiveRecord is "the core ORM (Object-Relational Mapping) library of Ruby on Rails" developed as the most essential database manipulation library in the Ruby ecosystem. Designed based on Martin Fowler's "Active Record" pattern, it automatically maps database tables to Ruby classes. Through an intuitive and readable API, it enables complex database operations without writing SQL statements, establishing itself as an indispensable tool for Rails developers.

Details

ActiveRecord 2025 edition serves as the foundation of the Ruby on Rails framework with over 15 years of proven track record, providing the standard ORM solution for modern Ruby application development. Following the philosophy of Convention over Configuration, it offers powerful integration with relational databases with minimal setup. Rich features including migrations, validations, associations, scopes, and callbacks streamline enterprise-level database-driven application development. Supporting major database engines like MySQL, PostgreSQL, SQLite, and SQL Server, it provides a consistent development experience from development to production environments.

Key Features

  • Automatic Mapping: Automatic correspondence between table names and class names with automatic schema detection
  • Rich Associations: Intuitive relationship definitions like belongs_to, has_many, has_one, etc.
  • Smart Query API: Readable query construction with where, joins, includes, etc.
  • Migrations: Version control and automatic application of database schema changes
  • Validations: Model-level data integrity checking functionality
  • Transactions: Database transactions with automatic rollback functionality

Pros and Cons

Pros

  • Overwhelming adoption rate in Rails ecosystem with abundant documentation and learning resources
  • Configuration-less development and high productivity through Convention over Configuration
  • Intuitive expression of complex data relationships through rich association features
  • Simplified schema synchronization in team development through migration functionality
  • Construction of reusable query logic through scope functionality
  • Ensuring robust data integrity through validation functionality

Cons

  • Increased memory usage and performance constraints when processing large amounts of data
  • Limitations in expressing complex SQL queries requiring raw SQL in some cases
  • Prone to N+1 query problems requiring proper eager loading
  • Difficulty integrating with existing databases that don't follow table naming conventions
  • Deteriorated code maintainability due to oversized model classes
  • Limitations in utilizing database-specific features in some cases

Reference Pages

Code Examples

Basic Setup

# Gemfile
gem 'rails', '~> 7.1.0'

# Database configuration (config/database.yml)
development:
  adapter: postgresql
  database: myapp_development
  username: postgres
  password: password
  host: localhost
  port: 5432
  
# Generate model file
rails generate model User name:string email:string age:integer

# Run migration
rails db:migrate

# Test in Rails console
rails console

Model Definition and Basic Operations

# app/models/user.rb
class User < ApplicationRecord
  # Validations
  validates :name, presence: true, length: { minimum: 2 }
  validates :email, presence: true, uniqueness: true
  validates :age, presence: true, numericality: { greater_than: 0 }
  
  # Scopes
  scope :adults, -> { where('age >= ?', 18) }
  scope :by_name, ->(name) { where('name ILIKE ?', "%#{name}%") }
  
  # Callbacks
  before_save :normalize_name
  after_create :send_welcome_email
  
  private
  
  def normalize_name
    self.name = name.strip.titleize
  end
  
  def send_welcome_email
    UserMailer.welcome(self).deliver_now
  end
end

# Basic CRUD operations
# Create
user = User.new(name: "John Doe", email: "[email protected]", age: 30)
user.save!

# Or
user = User.create!(name: "Jane Smith", email: "[email protected]", age: 25)

# Read
user = User.find(1)  # Find by ID
user = User.find_by(email: "[email protected]")  # Find by condition
users = User.where(age: 20..30)  # Range specification
users = User.adults  # Using scope

# Update
user.update!(name: "John Smith")
User.where(age: 18).update_all(status: 'adult')

# Delete
user.destroy!
User.where('created_at < ?', 1.year.ago).delete_all

Advanced Query Operations

# Complex condition combinations
users = User.where(age: 20..30)
            .where.not(email: nil)
            .where("name ILIKE ?", "%John%")
            .order(:created_at)
            .limit(10)

# JOIN queries
users_with_posts = User.joins(:posts)
                      .where(posts: { published: true })
                      .distinct

# Using subqueries
active_users = User.where(
  id: Post.where('created_at > ?', 1.month.ago)
          .select(:user_id)
          .distinct
)

# Aggregate functions
User.count
User.where(age: 20..30).average(:age)
User.group(:department).count
User.having('COUNT(*) > 5').group(:city).count

# Using SQL fragments
User.where("age > ? AND created_at > ?", 18, 1.year.ago)
User.where("EXTRACT(year FROM created_at) = ?", 2024)

# Dynamic query construction
def search_users(params)
  query = User.all
  
  query = query.where('name ILIKE ?', "%#{params[:name]}%") if params[:name].present?
  query = query.where(age: params[:min_age]..params[:max_age]) if params[:min_age] && params[:max_age]
  query = query.where(department: params[:department]) if params[:department].present?
  query = query.order(params[:sort_by] || :created_at)
  
  query
end

# Usage example
users = search_users({
  name: "John",
  min_age: 20,
  max_age: 40,
  department: "engineering",
  sort_by: :name
})

Relationship Operations

# Association definitions
class User < ApplicationRecord
  has_many :posts, dependent: :destroy
  has_many :comments, dependent: :destroy
  has_many :authored_posts, class_name: 'Post', foreign_key: 'author_id'
  has_one :profile, dependent: :destroy
  belongs_to :department
  
  has_many :user_roles
  has_many :roles, through: :user_roles
  
  has_many :following_relationships, class_name: 'Follow', foreign_key: 'follower_id'
  has_many :following, through: :following_relationships, source: :followed
  
  has_many :follower_relationships, class_name: 'Follow', foreign_key: 'followed_id'
  has_many :followers, through: :follower_relationships, source: :follower
end

class Post < ApplicationRecord
  belongs_to :user
  belongs_to :author, class_name: 'User'
  has_many :comments, dependent: :destroy
  has_many :commenters, through: :comments, source: :user
  
  validates :title, presence: true
  validates :content, presence: true, length: { minimum: 10 }
  
  scope :published, -> { where(published: true) }
  scope :recent, -> { where('created_at > ?', 1.week.ago) }
end

class Profile < ApplicationRecord
  belongs_to :user
  validates :bio, length: { maximum: 500 }
end

class Department < ApplicationRecord
  has_many :users
  validates :name, presence: true, uniqueness: true
end

# Relationship operations
user = User.find(1)

# Creating related records
post = user.posts.create!(title: "Hello World", content: "My first post")
profile = user.create_profile!(bio: "Software Developer")

# Fetching related records (avoiding N+1 problem)
users_with_posts = User.includes(:posts).where(posts: { published: true })
users_with_profiles = User.includes(:profile, :department)

# Fetching nested relationships
users_with_comments = User.includes(posts: :comments)

# Conditional fetching of related records
users_with_recent_posts = User.joins(:posts)
                             .where(posts: { created_at: 1.week.ago.. })
                             .distinct

# Aggregating related records
users_with_post_counts = User.left_joins(:posts)
                            .group(:id)
                            .select('users.*, COUNT(posts.id) as posts_count')

# Many-to-many relationship operations
user = User.find(1)
role = Role.find_by(name: 'admin')

user.roles << role  # Add association
user.roles.delete(role)  # Remove association
user.role_ids = [1, 2, 3]  # Bulk set associations

Practical Examples

# Migration example
class CreateBlogSchema < ActiveRecord::Migration[7.1]
  def change
    # Users table
    create_table :users do |t|
      t.string :name, null: false
      t.string :email, null: false
      t.integer :age
      t.text :bio
      t.references :department, null: true, foreign_key: true
      t.timestamps
    end
    
    add_index :users, :email, unique: true
    add_index :users, :name
    
    # Posts table
    create_table :posts do |t|
      t.string :title, null: false
      t.text :content, null: false
      t.boolean :published, default: false
      t.references :user, null: false, foreign_key: true
      t.references :author, null: true, foreign_key: { to_table: :users }
      t.timestamps
    end
    
    add_index :posts, [:user_id, :published]
    add_index :posts, :created_at
    
    # Comments table
    create_table :comments do |t|
      t.text :content, null: false
      t.references :post, null: false, foreign_key: true
      t.references :user, null: false, foreign_key: true
      t.timestamps
    end
    
    # Departments table
    create_table :departments do |t|
      t.string :name, null: false
      t.text :description
      t.timestamps
    end
    
    add_index :departments, :name, unique: true
  end
end

# Controller usage example
class UsersController < ApplicationController
  before_action :set_user, only: [:show, :edit, :update, :destroy]
  
  def index
    @users = User.includes(:department, :posts)
                 .order(:name)
                 .page(params[:page])
  end
  
  def show
    @recent_posts = @user.posts.published.recent.limit(5)
  end
  
  def create
    @user = User.new(user_params)
    
    if @user.save
      redirect_to @user, notice: 'User was successfully created.'
    else
      render :new, status: :unprocessable_entity
    end
  end
  
  def update
    if @user.update(user_params)
      redirect_to @user, notice: 'User was successfully updated.'
    else
      render :edit, status: :unprocessable_entity
    end
  end
  
  def destroy
    @user.destroy
    redirect_to users_url, notice: 'User was successfully deleted.'
  end
  
  private
  
  def set_user
    @user = User.find(params[:id])
  end
  
  def user_params
    params.require(:user).permit(:name, :email, :age, :bio, :department_id)
  end
end

# Service object usage example
class UserStatsService
  def self.call(user)
    new(user).call
  end
  
  def initialize(user)
    @user = user
  end
  
  def call
    {
      total_posts: user.posts.count,
      published_posts: user.posts.published.count,
      total_comments: user.comments.count,
      followers_count: user.followers.count,
      following_count: user.following.count,
      average_post_length: average_post_length,
      most_commented_post: most_commented_post
    }
  end
  
  private
  
  attr_reader :user
  
  def average_post_length
    user.posts.published.average('LENGTH(content)')&.round(2) || 0
  end
  
  def most_commented_post
    user.posts
        .joins(:comments)
        .group('posts.id')
        .order('COUNT(comments.id) DESC')
        .limit(1)
        .select('posts.*, COUNT(comments.id) as comments_count')
        .first
  end
end

# Usage example
user = User.find(1)
stats = UserStatsService.call(user)
puts "Total posts: #{stats[:total_posts]}"
puts "Published posts: #{stats[:published_posts]}"
puts "Average post length: #{stats[:average_post_length]} characters"