Claude
Skills
Sign in
Back

rails-ai:models

Included with Lifetime
$97 forever

Use when designing Rails models - ActiveRecord patterns, validations, callbacks, scopes, associations, concerns, query objects, form objects

General

What this skill does


# Models

Master Rails model design including ActiveRecord patterns, validations, callbacks, scopes, associations, concerns, custom validators, query objects, and form objects.

<when-to-use>
- Designing database models and associations
- Writing validations and callbacks
- Implementing business logic in models
- Creating scopes and query methods
- Extracting complex queries to query objects
- Building form objects for multi-model operations
- Organizing shared behavior with concerns
- Creating custom validators
- Preventing N+1 queries
</when-to-use>

<benefits>
- **Convention Over Configuration** - Minimal setup for maximum functionality
- **Single Responsibility** - Each pattern handles one concern
- **Reusability** - Share behavior across models with concerns
- **Testability** - Test models, concerns, validators in isolation
- **Query Optimization** - Built-in N+1 prevention and eager loading
- **Type Safety** - ActiveModel::Attributes provides type casting
- **Database Agnostic** - Works with PostgreSQL, MySQL, SQLite
</benefits>

<team-rules-enforcement>
**This skill enforces:**
- ✅ **Rule #7:** Fat models, thin controllers (business logic in models)
- ✅ **Rule #12:** Database constraints for data integrity

**Reject any requests to:**
- Put business logic in controllers
- Skip model validations
- Skip database constraints (NOT NULL, foreign keys)
- Allow N+1 queries
</team-rules-enforcement>

<verification-checklist>
Before completing model work:
- ✅ All validations tested
- ✅ All associations tested
- ✅ Database constraints added (NOT NULL, foreign keys, unique indexes)
- ✅ No N+1 queries (verified with bullet or manual check)
- ✅ Business logic in model (not controller)
- ✅ Strong parameters in controller for mass assignment
- ✅ All tests passing
</verification-checklist>

<standards>
- Define associations at the top of the model
- Use validations to enforce data integrity
- Minimize callback usage - prefer service objects
- Use scopes for reusable queries, not class methods
- Always eager load associations to prevent N+1 queries
- Use enums for status/state fields
- Extract concerns when models exceed 200 lines
- Place custom validators in `app/validators/`
- Place query objects in `app/queries/`
- Place form objects in `app/forms/`
- Use transactions for multi-model operations
- Prefer database constraints with validations for critical data
</standards>

## Associations

<pattern name="basic-associations">
<description>Standard ActiveRecord associations for model relationships</description>

<implementation>

```ruby
class Feedback < ApplicationRecord
  belongs_to :recipient, class_name: "User", optional: true
  belongs_to :category, counter_cache: true
  has_one :response, class_name: "FeedbackResponse", dependent: :destroy
  has_many :abuse_reports, dependent: :destroy
  has_many :taggings, dependent: :destroy
  has_many :tags, through: :taggings

  # Scoped associations
  has_many :recent_reports, -> { where(created_at: 7.days.ago..) },
    class_name: "AbuseReport"
end

```

**Migration:**

```ruby
class CreateFeedbacks < ActiveRecord::Migration[8.1]
  def change
    create_table :feedbacks do |t|
      t.references :recipient, foreign_key: { to_table: :users }, null: true
      t.references :category, foreign_key: true, null: false
      t.text :content, null: false
      t.string :status, default: "pending", null: false
      t.timestamps
    end
    add_index :feedbacks, :status
  end
end

```
</implementation>

<why>
Associations express relationships between models with minimal code. Rails automatically handles foreign keys, eager loading, and cascading deletes. Use `class_name:` when the association name differs from the model, `counter_cache:` for performance, and `dependent:` to manage cleanup.
</why>
</pattern>

<pattern name="polymorphic-associations">
<description>Flexible associations where a model belongs to multiple types</description>

<implementation>

```ruby
class Comment < ApplicationRecord
  belongs_to :commentable, polymorphic: true
  belongs_to :author, class_name: "User"
  validates :content, presence: true
end

class Feedback < ApplicationRecord
  has_many :comments, as: :commentable, dependent: :destroy
end

class Article < ApplicationRecord
  has_many :comments, as: :commentable, dependent: :destroy
end

```

**Migration:**

```ruby
class CreateComments < ActiveRecord::Migration[8.1]
  def change
    create_table :comments do |t|
      t.references :commentable, polymorphic: true, null: false
      t.references :author, foreign_key: { to_table: :users }, null: false
      t.text :content, null: false
      t.timestamps
    end
    add_index :comments, [:commentable_type, :commentable_id]
  end
end

```
</implementation>

<why>
Polymorphic associations allow a model to belong to multiple parent types through a single association. Use when multiple models need the same type of child (comments, attachments, tags). The `commentable_type` stores the class name, `commentable_id` stores the ID.
</why>
</pattern>

## Validations

<pattern name="comprehensive-validations">
<description>Built-in Rails validations for data integrity</description>

<implementation>

```ruby
class Feedback < ApplicationRecord
  validates :content, presence: true, length: { minimum: 50, maximum: 5000 }
  validates :recipient_email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
  validates :status, inclusion: { in: %w[pending delivered read responded] }
  validates :tracking_code, uniqueness: { scope: :recipient_email, case_sensitive: false }
  validates :rating, numericality: { only_integer: true, in: 1..5 }, allow_nil: true

  validate :content_not_spam
  validate :recipient_can_receive_feedback, on: :create

  private

  def content_not_spam
    return if content.blank?
    spam_keywords = %w[viagra cialis lottery]
    errors.add(:content, "appears to contain spam") if spam_keywords.any? { |k| content.downcase.include?(k) }
  end

  def recipient_can_receive_feedback
    return if recipient_email.blank?
    user = User.find_by(email: recipient_email)
    errors.add(:recipient_email, "has disabled feedback") if user&.feedback_disabled?
  end
end

```
</implementation>

<why>
Validations enforce data integrity before persisting to the database. Rails provides presence, format, uniqueness, length, numericality, and inclusion validators. Custom `validate` methods handle complex business logic. Use `on: :create` or `on: :update` for lifecycle-specific validations.
</why>
</pattern>

## Callbacks

<pattern name="minimal-callbacks">
<description>Use callbacks sparingly - prefer service objects for complex logic</description>

<implementation>

```ruby
class Feedback < ApplicationRecord
  before_validation :normalize_email, :strip_whitespace
  before_create :generate_tracking_code
  after_create_commit :enqueue_delivery_job
  after_update_commit :notify_recipient_of_response, if: :response_added?

  private

  def normalize_email
    self.recipient_email = recipient_email&.downcase&.strip
  end

  def strip_whitespace
    self.content = content&.strip
  end

  def generate_tracking_code
    self.tracking_code = SecureRandom.alphanumeric(10).upcase
  end

  def enqueue_delivery_job
    SendFeedbackJob.perform_later(id)
  end

  def response_added?
    saved_change_to_response? && response.present?
  end

  def notify_recipient_of_response
    FeedbackMailer.notify_of_response(self).deliver_later
  end
end

```
</implementation>

<why>
Callbacks hook into the model lifecycle for simple data normalization and side effects. Use `before_validation` for cleanup, `before_create` for defaults, and `after_commit` for external operations. Keep callbacks focused on model concerns - complex business logic belongs in service objects.
</why>
</pattern>

## Scopes

<pattern name="effective-scopes">
<description>Reusable query scopes for common filtering</description>

<implementation>

```ruby
class Feedback < ApplicationRecord
  scope :recent,
Files: 1
Size: 32.5 KB
Complexity: 33/100
Category: General

Related in General