Models Overview
The Roadtrip Planner application uses a clean, domain-driven model architecture with three core entities that represent the business logic of trip planning.
Model Architecture
erDiagram
USERS {
id bigint PK
username string UK "Unique username"
password_digest string "bcrypt hashed password"
created_at timestamp
updated_at timestamp
}
ROAD_TRIPS {
id bigint PK
user_id bigint FK
name string "Trip name"
description text "Optional description"
created_at timestamp
updated_at timestamp
}
ROUTES {
id bigint PK
road_trip_id bigint FK
user_id bigint FK
starting_location string "Starting point"
destination string "End point"
datetime timestamp "Departure time"
distance decimal "Distance in km"
duration decimal "Duration in hours"
created_at timestamp
updated_at timestamp
}
USERS ||--o{ ROAD_TRIPS : "owns"
USERS ||--o{ ROUTES : "creates"
ROAD_TRIPS ||--o{ ROUTES : "contains"
Core Models
1. User Model
Purpose: Authentication and user management
File: app/models/user.rb
The User model handles authentication and serves as the owner of all trip-related data.
Key Features:
- Secure password authentication with bcrypt
- Username-based login system
- Owns all road trips and routes
- Data cleanup on deletion
2. RoadTrip Model
Purpose: Trip organization and planning
File: app/models/road_trip.rb
The RoadTrip model represents a complete trip plan that can contain multiple routes.
Key Features:
- Named trip organization
- Route aggregation and statistics
- User ownership and access control
- Trip duration calculation
3. Route Model
Purpose: Individual trip segments
File: app/models/route.rb
The Route model represents individual legs of a journey with detailed location and timing information.
Key Features:
- Location-to-location mapping
- Distance and duration calculation
- Overlap validation for trip planning
- GPX export compatibility
Model Relationships
Association Patterns
# User associations
class User < ApplicationRecord
has_many :road_trips, dependent: :destroy
has_many :routes, dependent: :destroy
end
# RoadTrip associations
class RoadTrip < ApplicationRecord
belongs_to :user
has_many :routes, dependent: :destroy
end
# Route associations
class Route < ApplicationRecord
belongs_to :road_trip
belongs_to :user
end
Data Integrity
The model design ensures data integrity through:
- Foreign Key Constraints: All child records reference parent IDs
- Dependent Destruction: Cascading deletes maintain referential integrity
- Validation Rules: Business logic prevents invalid data states
- User Ownership: Double-reference pattern ensures user access control
Validation Strategy
Security Validations
# User model validations
validates :username, presence: true,
length: { minimum: 3 },
uniqueness: { case_sensitive: false }
validates :password, presence: true,
length: { minimum: 8 },
format: { with: /\A(?=.*[a-zA-Z])(?=.*\d).*\z/ }
Business Logic Validations
# Route model business validations
validate :datetime_not_overlapping_with_other_routes
validate :user_matches_road_trip_user
Scopes and Query Patterns
User-Scoped Data Access
# Secure data access patterns
class RoadTrip < ApplicationRecord
scope :for_user, ->(user) { where(user: user) }
end
class Route < ApplicationRecord
scope :for_user, ->(user) { where(user: user) }
scope :ordered_by_datetime, -> { order(:datetime) }
end
Performance Optimization
# Efficient data loading
trips_with_routes = RoadTrip.includes(:routes).for_user(user)
recent_trips = RoadTrip.for_user(user).order(created_at: :desc).limit(10)
Business Logic Implementation
Calculated Fields
Models include business logic for derived data:
class RoadTrip < ApplicationRecord
# Aggregate route distances
def total_distance
routes.sum { |route| route.distance_in_km.to_f }.round(1)
end
# Calculate trip duration in days
def day_count
return 0 if routes.empty?
# Logic for calculating total trip days
end
end
Data Processing
class Route < ApplicationRecord
# Automatic distance/duration calculation
before_save :calculate_route_metrics, if: :locations_changed?
# Safe distance access with fallback
def distance_in_km
distance || calculate_and_save_route_metrics[:distance]
end
end
Model Testing Strategy
Test Coverage Areas
- Validations: All validation rules tested
- Associations: Relationship behavior verified
- Business Logic: Calculated fields and methods tested
- Security: Access control and data ownership tested
Example Test Structure
RSpec.describe User, type: :model do
describe 'validations' do
# Validation tests
end
describe 'associations' do
# Association tests
end
describe 'business logic' do
# Method and calculation tests
end
end
Database Schema Considerations
Index Strategy
# Optimized database indexes for common queries
add_index :road_trips, :user_id
add_index :routes, [:road_trip_id, :user_id]
add_index :routes, :datetime
add_index :users, :username, unique: true
Migration Patterns
# Safe migration practices
class CreateRoadTrips < ActiveRecord::Migration[8.0]
def change
create_table :road_trips do |t|
t.references :user, null: false, foreign_key: true
t.string :name, null: false
t.text :description
t.timestamps
end
add_index :road_trips, :user_id
end
end
Security Considerations
Access Control
- User Ownership: All data belongs to specific users
- Authorization Checks: Controllers verify user access
- Data Isolation: Scopes prevent cross-user data access
Input Validation
- SQL Injection Prevention: ActiveRecord parameterized queries
- Data Sanitization: Strong parameters in controllers
- Business Rule Enforcement: Model validations prevent invalid states
Performance Patterns
Efficient Queries
# Avoid N+1 queries
trips_with_routes = current_user.road_trips.includes(:routes)
# Use database aggregations
total_distance = current_user.routes.sum(:distance)
# Batch operations for efficiency
Route.where(road_trip: trip).update_all(updated_at: Time.current)
Caching Strategy
# Model-level caching for expensive calculations
def total_distance
Rails.cache.fetch("road_trip_#{id}_total_distance", expires_in: 1.hour) do
routes.sum { |route| route.distance_in_km.to_f }.round(1)
end
end
Future Enhancements
Planned Model Extensions
- Waypoints: Intermediate stops within routes
- Trip Sharing: Collaborative trip planning
- Trip Templates: Reusable trip patterns
- Accommodation: Hotel/lodging integration
- Expenses: Trip cost tracking
Scalability Considerations
- Pagination: Large trip lists with pagination
- Background Processing: Distance calculations in jobs
- API Support: JSON serialization for mobile apps
- Caching: Aggressive caching for read-heavy operations
The model architecture provides a solid foundation for the current trip planning features while maintaining flexibility for future enhancements and scaling needs.