Ruby on Rails Interview Questions and Answers — Model — Part 3

9 min readOct 1, 2023


Ruby on Rails Interview Questions and Answers — Model — Part 3
  • How can you handle data seeding in a Rails application?
  • Explain the concept of Single Table Inheritance (STI) in Rails models and how it’s implemented.
  • What is the purpose of the `includes` method in Rails models, and how does it help optimize queries?
  • How can you handle data versioning or auditing in Rails models?
  • What are virtual attributes in Rails models, and when might you use them?

11. How can you handle data seeding in a Rails application?

Data seeding in a Ruby on Rails application involves populating your application’s database with initial or predefined data. This is typically done to ensure that the database has some data to work with when the application is first deployed or during development and testing. Rails provides several ways to handle data seeding:

Using Seed Files:

Rails provides a built-in mechanism for creating seed data using seed files. These files are written in Ruby and can be used to insert records into the database. Seed files are stored in the db/seeds.rb file by default, but you can create additional seed files as needed.

# db/seeds.rb

# Create a new user
User.create(name: "John Doe", email: "")

# Create some posts
5.times do |i|
Post.create(title: "Post #{i + 1}", content: "This is post number #{i + 1}")

To run the seed file, use the db:seed Rake task:

rails db:seed

Using Faker Gem

If you need to generate random or sample data for seeding, you can use the faker gem, which provides a wide range of methods to generate realistic-looking data. Install the gem by adding it to your Gemfile:

gem 'faker', '~> 2.18'

Then, use it in your seed file to create random data:

# db/seeds.rb

10.times do

Using Database Seeds

In some cases, you might have large or complex seed data that you want to manage separately from the seeds.rb file. You can create your own seed files in a directory of your choice (e.g., db/seeds/) and then load them from the seeds.rb file or any Rake task using the load method.

# db/seeds.rb

Dir[Rails.root.join('db/seeds/*.rb')].sort.each do |seed|
load seed

This allows you to organize your seed data into multiple files for better maintainability.

Using Third-Party Seed Libraries

There are also third-party libraries and tools available for handling data seeding in Rails applications, such as seedbank and factory_bot. These libraries provide additional features and capabilities for generating and managing seed data.

Using Database Seeds in Production

In production environments, it’s essential to exercise caution when running seed files, as they can modify existing data or insert large amounts of data. In production, it’s often better to use a separate data import process or script that can be run manually or as part of the deployment process.

12. Explain the concept of Single Table Inheritance (STI) in Rails models and how it’s implemented

Single Table Inheritance (STI) is a design pattern used in Ruby on Rails to implement polymorphism in database-backed models. It allows you to store multiple types of objects in a single database table while maintaining a clear inheritance hierarchy among them. This pattern is particularly useful when you have several models that share some common attributes but also have distinct attributes that are specific to each model.

Create a Parent Model

Start by creating a parent model that will serve as the base class for all the child models that will inherit from it. This parent model will represent the common attributes shared among the child models.

# app/models/vehicle.rb
class Vehicle < ApplicationRecord

Create Child Models

Create child models that inherit from the parent model. Each child model will represent a specific type of object and can have its own attributes in addition to the common attributes inherited from the parent.

# app/models/car.rb
class Car < Vehicle

# app/models/bike.rb
class Bike < Vehicle

In this example, both the Car and Bike models inherit from the Vehicle model.

Database Schema

In the database, you will have a single table (e.g., vehicles) that stores all the records for the different types of objects (cars, bikes, etc.).

To distinguish between different types of records, you’ll need a special column called a “type” column, which Rails automatically adds when you use STI.

CREATE TABLE vehicles (
type VARCHAR(255), -- This column is automatically added by Rails for STI
make VARCHAR(255),
model VARCHAR(255),
-- other shared attributes

Populate the Database:

You can create records for different types of vehicles using their respective child models. Rails will automatically set the type column in the database to the appropriate value.

# Creating a car record
car = Car.create(make: 'Toyota', model: 'Camry', year: 2022)

# Creating a bike record
bike = Bike.create(make: 'Trek', model: 'Mountain Bike', year: 2021)

Querying and Polymorphism

You can query the database for specific types of objects using the child models:

# Query for all cars
cars = Car.all

# Query for all bikes
bikes = Bike.all

Rails will automatically use the type column to identify and instantiate the correct child class when you retrieve records.

13. What is the purpose of the `includes` method in Rails models, and how does it help optimize queries?

The includes method in Rails is used to optimize database queries by performing eager loading of associated data. Its primary purpose is to reduce the number of database queries when you retrieve records and their associated data. Eager loading can significantly improve the performance of your application, especially when dealing with associations like has_many and has_and_belongs_to_many.

Here’s how the includes method works and why it helps optimize queries:

Lazy Loading by Default

By default, Rails uses lazy loading to fetch associated data. This means that when you retrieve a record, it doesn’t load its associated data immediately. Instead, it loads the associated data from the database when you first access it. This can lead to a phenomenon called the “N+1 query problem,” where a separate database query is executed for each record’s associated data, causing performance bottlenecks.

Eager Loading with includes

When you use the includes method in a query, Rails loads the associated data for all the records in one or a few queries, rather than issuing a separate query for each record. This reduces the total number of database queries and significantly improves query performance.

For example, suppose you have a Post model with many associated Comment records,

# Fetch posts and their comments using lazy loading (N+1 queries)
posts = Post.all
posts.each do |post|
comments = post.comments # Separate query for each post's comments

With includes, you can optimize this query,

# Fetch posts and their comments using eager loading
posts = Post.includes(:comments).all
posts.each do |post|
comments = post.comments # No additional queries for comments

Preventing N+1 Query Problems

Eager loading with includes is particularly helpful when you know you will be accessing associated data for a collection of records. It prevents N+1 query problems and ensures that you retrieve the data efficiently.

Customizing Eager Loading

You can also customize how associations are loaded using includes. For example, you can specify which associated models to load or apply additional conditions,

# Eager load only specific associations
posts = Post.includes(:comments, :author).all

# Eager load associations with conditions
posts = Post.includes(:comments).where('comments.created_at > ?', 1.week.ago)

14. How can you handle data versioning or auditing in Rails models?

Handling data versioning or auditing in Rails models involves keeping track of changes made to the data in your application over time. This can be useful for various purposes, such as auditing user actions, maintaining a history of changes, or implementing data rollback functionality. There are several ways to implement data versioning or auditing in Rails models

PaperTrail Gem

The PaperTrail gem is a popular choice for implementing versioning and auditing in Rails models. It allows you to track changes to your records and provides a simple and customizable API for retrieving historical versions of your data.

To use PaperTrail, you need to:

  • Add the gem to your Gemfile and run bundle install.
  • Generate and run a migration to add the necessary database tables.
  • Enable version tracking in your models using has_paper_trail.
  • Retrieve and manage historical versions using the versions association.
class Article < ApplicationRecord

You can then access the historical versions of an article using article.versions.

Custom Implementation

If you prefer more control over the versioning process, you can implement data versioning or auditing manually in your Rails models. This approach involves creating a separate model to store historical records of changes, such as an AuditLog model.

Here’s a simplified example of how you can manually implement data versioning

class Article < ApplicationRecord
after_save :create_audit_log


def create_audit_log
record_id: id,
changes: changes

class AuditLog < ApplicationRecord
# Fields: model_name, record_id, changes, user_id, timestamp, etc.

In this example, the create_audit_log method is called after saving an article, and it creates an AuditLog record to store information about the changes.

Database Triggers

Some databases, such as PostgreSQL, offer built-in support for triggers that can automatically log changes to records. You can set up database triggers to capture changes and store them in an audit table.

The specific implementation of database triggers varies depending on your database system, but it can be a powerful and efficient way to handle data versioning and auditing at the database level.

Third-Party Solutions

There are also third-party solutions and gems other than PaperTrail that offer data versioning and auditing capabilities, each with its own features and customization options. You can explore options based on your specific requirements and preferences.

The choice of how to handle data versioning or auditing in Rails models depends on the complexity of your application and your specific needs. PaperTrail is a commonly used gem that simplifies the process, while custom implementations or database triggers offer more flexibility and control. Consider your project’s requirements and choose the approach that best fits your use case.

15. What are virtual attributes in Rails models, and when might you use them?

Virtual attributes in Rails models are attributes that do not have corresponding columns in the database. They are not stored in the database like regular attributes but are defined in the model solely for the purpose of interacting with or representing data temporarily within the application. Virtual attributes are typically used to enhance the functionality or behavior of a model.

Derived or Computed Attributes

You can create virtual attributes that compute their values based on the values of other attributes in the model. For example, you might have a User model with first_name and last_name attributes, and you could create a virtual attribute called full_name to concatenate these two attributes.

class User < ApplicationRecord
def full_name
"#{first_name} #{last_name}"

Form Fields That Don’t Map Directly to Columns

When you have a form that includes fields that do not directly correspond to database columns, you can use virtual attributes to process and handle the form data. For instance, you might have a User model with a virtual attribute password_confirmation that is used for password validation during user registration.

class User < ApplicationRecord
attr_accessor :password_confirmation

validates :password, presence: true
validates :password_confirmation, presence: true
validate :password_match


def password_match
errors.add(:password_confirmation, 'does not match password') if password != password_confirmation

In this example, password_confirmation is not a database column, but it's used to ensure that the user's password matches its confirmation.

Temporary Data Storage

You can use virtual attributes to temporarily store data or flags in memory during an instance’s lifecycle. For instance, you might use a virtual attribute to mark an object as “read” or “unread” without altering the database state.

class Message < ApplicationRecord
attr_accessor :read

def mark_as_read
@read = true

Custom Behavior Based on User Input

Virtual attributes can be used to modify the behavior of a model based on user input. For instance, you might have a Product model with a virtual attribute discount_code that applies a discount based on user input during a checkout process.

class Product < ApplicationRecord
attr_accessor :discount_code

def apply_discount
# Logic to apply discount based on discount_code

Temporary Filtering or Sorting

You can create virtual attributes to assist with temporary filtering or sorting of data within the application without affecting the database. For instance, you might have a virtual attribute that calculates a score for a list of items and then sorts those items based on the calculated score.

class Item < ApplicationRecord
attr_accessor :score

def calculate_score
# Calculate a score for the item

Virtual attributes provide flexibility and versatility in modeling data and interactions within your Rails application. They allow you to extend and enhance the behavior of your models without the need for additional database columns, making your application more expressive and adaptable to various requirements.

I appreciate you taking the time to read this. Please follow me on Medium and subscribe to receive access to exclusive content in order to keep in touch and continue the discussion. Happy Reading!!!




Consultant | Freelancer | Ruby on Rails | ReactJS