Skip to content

Ruby on Rails Models Deep — Advanced Model Patterns

DodaTech Updated 2026-06-28 3 min read

In this tutorial, you will learn about Ruby on Rails Models Deep. We cover key concepts, practical examples, and best practices to help you master this topic.

Rails models encapsulate business logic with callbacks, validations, scopes, enums, associations, Serialization, and model concerns for reusable behavior sharing.

What You'll Learn

By the end of this tutorial, you'll implement advanced model patterns: callbacks with conditional logic, enums, delegation, STI, model concerns, and custom validators.

Why Model Patterns Matter

Rails models are the heart of the application. Well-structured models with concerns, callbacks, and scopes keep business logic organized and testable.

Real-World Use

A content management system uses STI for content types (Article, Video, Podcast), model concerns for publishable and reviewable behavior, and scopes for content filtering.

Models Path

flowchart LR
  A[Rails MVC] --> B[Models Deep]
  B --> C[Callbacks]
  B --> D[Enums]
  B --> E[STI]
  B --> F[Concerns]
  B --> G{You Are Here}
  style G fill:#f90,color:#fff

Callbacks with Conditions

Apply callbacks conditionally.

class Order < ApplicationRecord
  before_save :calculate_total, if: :items_changed?
  before_save :set_status, on: :create
  after_save :send_confirmation, unless: :test_order?
  around_save :log_changes

  private

  def items_changed?
    saved_change_to_order_items? || new_record?
  end

  def test_order?
    email&.include?("@example.com")
  end
end

Enums

Enums map integer columns to named values.

class Order < ApplicationRecord
  enum :status, {
    pending: 0,
    confirmed: 1,
    processing: 2,
    shipped: 3,
    delivered: 4,
    cancelled: 5,
  }, default: :pending, validate: true

  enum :payment_status, {
    unpaid: 0,
    paid: 1,
    refunded: 2,
    chargeback: 3,
  }

  scope :active, -> { where.not(status: :cancelled) }
  scope :needs_attention, -> { where(status: [:pending, :processing]) }
end

# Usage
order.pending?        # true/false
order.confirmed!      # Update to confirmed
Order.confirmed       # Where clause scope

Delegation

Delegate methods to associated objects.

class User < ApplicationRecord
  has_one :profile, dependent: :destroy

  delegate :bio, :website, :location, to: :profile, prefix: true
  delegate :avatar_url, to: :profile, allow_nil: true
  delegate :name, to: :profile, prefix: :user, allow_nil: true
end

class Profile < ApplicationRecord
  belongs_to :user
end

# Usage
user.profile_bio        # user.profile.bio
user.profile_avatar_url # user.profile&.avatar_url
user.user_name          # user.profile&.name

Single Table Inheritance

Share a table across related models.

class Content < ApplicationRecord
  self.inheritance_column = :type

  scope :published, -> { where(published: true) }

  def display_type
    type.demodulize
  end
end

class Article < Content
  validates :body, presence: true
end

class Video < Content
  validates :video_url, presence: true
end

class Podcast < Content
  validates :audio_url, presence: true
end

Common Mistakes

1. Callback Chain Overload

Too many callbacks on a single model make saving unpredictable. Use service objects.

2. Using Enum Integer Values Directly

Always reference enum values by name. Hardcoded integers break when the enum changes.

3. STI Without Discriminator Column

STI requires a type column. Rails defaults to the class name.

4. Delegation Without allow_nil

Delegating to a nil association raises NoMethodError. Use allow_nil: true.

5. Callbacks That Query the Database

Callbacks should be fast. Slow callbacks delay saves and cause timeouts.

Practice Questions

1. What callback runs before creating a new record?

before_create.

2. How do you define an enum in Rails 7+?

enum :status, { pending: 0, active: 1 }, default: :pending.

3. What is delegation in Rails?

Delegating method calls from one class to an associated object.

4. What is STI?

Single Table Inheritance - multiple models share a single database table with a type column.

5. Challenge: Create a model with callbacks, enums, and delegation.

class Subscription < ApplicationRecord
  belongs_to :user
  delegate :email, :name, to: :user, prefix: true, allow_nil: true

  enum :status, { trial: 0, active: 1, paused: 2, cancelled: 3 }

  before_create :set_trial_end
  after_save :notify_user, if: :saved_change_to_status?

  private

  def set_trial_end
    self.trial_ends_at = 14.days.from_now
  end

  def notify_user
    SubscriptionMailer.status_change(self).deliver_later
  end
end

FAQ

What is the difference between before_save and before_create?

before_save runs on both create and update. before_create runs only on create.

Can I have multiple callbacks of the same type?

Yes. Multiple callbacks run in the order they are defined.

What is the inheritance_column?

The column name used for STI. Default is :type. Can be customized.

How do I skip callbacks?

Use update_columns, increment!, or the skip_callback method.

Can enums be validated?

Yes. Use validate: true in the enum definition.

Mini Project: Content Management with STI

Build a content management system using STI.

class Content < ApplicationRecord
  belongs_to :author, class_name: "User"
  validates :title, presence: true
  scope :published, -> { where(published: true) }
end

class Article < Content
  validates :body, presence: true
end

class Video < Content
  validates :video_url, presence: true
end

class Podcast < Content
  validates :audio_url, presence: true
end

What's Next

Rails ActiveRecord Deep Rails Validations Deep Rails Associations Deep

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro