This is essentially a record database inspired by WhoSampled, It shows connections based on artists who have sampled other artists, and vice versa
Note: Runs on Ruby on Rails on Ubuntu (WSL)
Terminal Commands
Open Project cd ~/repos/sampledb (navigate to directory) code . (Opens current folder in VSCode)
Install Dependencies (First time only) bundle install (installs dependencies)
Create Database (First time only) bin/rails db:create (creates the database itself) bin/rails db:migrate (migrates all the tables, columns, and relationships) bin/rails db:seed (adds data to the database, artist, songs, connections)
Start the Server bin/rails s (spins the up rails, app is now running)
Open Browser Open browser (Rails listens on http://localhost:3000)
Stop Server Ctrl C (Shut down and exits from terminal)
All users can view connections and data, to comment or create entries, user must be logged in.
users
id
email (unique, not null)
username (unique, not null)
timestamps
artists
id
name (not null, indexed)
timestamps
entries
id
user_id (FK → users, not null)
artist_id (FK → artists, not null)
title (not null, indexed)
bpm (integer ≥ 0, nullable allowed)
key (string, e.g., "A minor")
year (integer, not null)
notes (text)
video_url (string, optional)
timestamps
samples (self-referential join: entries <-> entries)
id
sampler_entry_id (FK → entries, not null)
sampled_entry_id (FK → entries, not null)
timestamps
unique [sampler_entry_id, sampled_entry_id] // sample relations must be unique
check sampler_entry_id ≠ sampled_entry_id // a song can't sample itself
sample_segments (timestamped sample moments within songs)
id
sample_id (FK → samples, not null)
start_time (integer, not null, in seconds)
timestamps
comments
id
user_id (FK → users, not null)
entry_id (FK → entries, not null)
body (text, not null)
timestamps
User
has_many :entries
has_many :comments
Artist
has_many :entries
Entry
belongs_to :user
belongs_to :artist
has_many :comments, dependent: :destroy
# Sampling (relationships)
# The song doing the sampling
has_many :samples_used,
class_name: "Sample",
foreign_key: :sampler_entry_id,
dependent: :destroy
has_many :sampled_entries,
through: :samples_used,
source: :sampled_entry
# The song being sampled
has_many :sampled_by,
class_name: "Sample",
foreign_key: :sampled_entry_id,
dependent: :destroy
has_many :sampled_by_entries,
through: :sampled_by,
source: :sampler_entry
Comment
belongs_to :user
belongs_to :entry
Sample
belongs_to :sampler_entry, class_name: "Entry"
belongs_to :sampled_entry, class_name: "Entry"
has_many :sample_segments, dependent: :destroy
SampleSegment
belongs_to :sample
Database-Level Constraints
--------------------------
- NOT NULL constraints on required fields
- Foreign key constraints for all associations
- Unique index on [:sampler_entry_id, :sampled_entry_id] in samples table
- CHECK constraints:
- bpm >= 0
- sampler_entry_id <> sampled_entry_id (no self-sampling)
- start_time >= 0
Model-Level Validations
------------------------
Presence:
- Entry.title → required
- Entry.user → must belong to a user
- Entry.artist → must belong to an artist
- SampleSegment.start_time → required
Numericality:
- Entry.bpm → must be a number ≥ 0 (allow_nil: true)
- SampleSegment.start_time → must be a number ≥ 0
Custom:
- Prevent sampling entries from the future
(i.e., sampled_entry.year must be ≤ sampler_entry.year)
Optional:
- Video URL → (Possible future validation for format)
erDiagram
USERS ||--o{ ENTRIES : "has many"
USERS ||--o{ COMMENTS : "has many"
ARTISTS ||--o{ ENTRIES : "has many"
ENTRIES ||--o{ COMMENTS : "has many"
%% Sampling relationships (entries↔entries via samples)
ENTRIES ||--o{ SAMPLES : "sampling_entry"
ENTRIES ||--o{ SAMPLES : "source_entry"
%% Sample segments (timing within source track)
SAMPLES ||--o{ SAMPLE_SEGMENTS : "has many"
USERS {
bigint id
string email
string username
datetime created_at
datetime updated_at
}
ARTISTS {
bigint id
string name
datetime created_at
datetime updated_at
}
ENTRIES {
bigint id
bigint user_id FK
bigint artist_id FK
string title
integer bpm
string key
integer year
text notes
string video_url
datetime created_at
datetime updated_at
}
COMMENTS {
bigint id
bigint user_id FK
bigint entry_id FK
text body
datetime created_at
datetime updated_at
}
SAMPLES {
bigint id
bigint sampling_entry_id FK
bigint source_entry_id FK
datetime created_at
datetime updated_at
}
SAMPLE_SEGMENTS {
bigint id
bigint sample_id FK
integer start_sec
datetime created_at
datetime updated_at
}