DEV Community

Samuel Lubliner
Samuel Lubliner

Posted on

Belay Board App Part 1: Database

Step one

  • Start simple and test minimal viable product.
  • Add user, availabilities, requests
  • User can create and request an open availability
  • Set up calendar

Postgres

First I configured my databse.yml file to use postgres

default: &default adapter: postgresql encoding: unicode pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> development: <<: *default database: belay_board_development test: <<: *default database: belay_board_test production: <<: *default database: belay_board_production username: belay_board password: <%= ENV["BELAY_BOARD_DATABASE_PASSWORD"] %> 
Enter fullscreen mode Exit fullscreen mode

Run:
rake db:create

Before creating my tables, I created a new feature branch
Belay-Board main % git checkout -b feature/users

Users with Devise

rails generate devise:install

Create the Users table
rails generate devise user username private:boolean bio:text

This created a migration file, User model, and user routes. Devise also handles password and email flow.

Users migration file

add_index speeds up lookups of records

add .citext to email and username

# frozen_string_literal: true class DeviseCreateUsers < ActiveRecord::Migration[7.0] def change create_table :users do |t| enable_extension("citext") ## Database authenticatable t.citext :email, null: false, default: "" t.string :encrypted_password, null: false, default: "" ## Recoverable t.string :reset_password_token t.datetime :reset_password_sent_at ## Rememberable t.datetime :remember_created_at t.citext :username t.boolean :private t.text :bio t.timestamps null: false end add_index :users, :email, unique: true add_index :users, :reset_password_token, unique: true add_index :users, :username, unique: true end end 
Enter fullscreen mode Exit fullscreen mode

Run
rake db:migrate

class ApplicationController < ActionController::Base skip_forgery_protection before_action :authenticate_user! before_action :configure_permitted_parameters, if: :devise_controller? def configure_permitted_parameters devise_parameter_sanitizer.permit(:account_update, :keys => [:private, :bio]) end end 
Enter fullscreen mode Exit fullscreen mode

Availability Table

rails g scaffold availability user:references start_time:datetime end_time:datetime location:string description:string is_open:boolean

Update migration file

class CreateAvailabilities < ActiveRecord::Migration[7.0] def change create_table :availabilities do |t| t.references :user, null: false, foreign_key: true t.datetime :start_time, null: false t.datetime :end_time, null: false t.string :location t.string :description t.boolean :is_open, default: true t.timestamps end end end 
Enter fullscreen mode Exit fullscreen mode

Update Models

class User < ApplicationRecord #... has_many :availabilities, dependent: :destroy end 
Enter fullscreen mode Exit fullscreen mode
class Availability < ApplicationRecord belongs_to :user validates :start_time, presence: true validates :end_time, presence: true validate :end_time_after_start_time private def end_time_after_start_time return if end_time.blank? || start_time.blank? if end_time < start_time errors.add(:end_time, "must be after the start time") end end end 
Enter fullscreen mode Exit fullscreen mode

Run
rails db:migrate

Requests

rails g scaffold request status:integer availability:references sender:references

Migration

class CreateRequests < ActiveRecord::Migration[7.0] def change create_table :requests do |t| t.integer :status, default: 0 t.references :availability, null: false, foreign_key: true t.references :sender, null: false, foreign_key: { to_table: :users } t.timestamps end end end 
Enter fullscreen mode Exit fullscreen mode

## Request model

class CreateRequests < ActiveRecord::Migration[7.0] def change create_table :requests do |t| t.integer :status, default: 0 t.references :availability, null: false, foreign_key: true t.references :sender, null: false, foreign_key: { to_table: :users } t.timestamps end end end 
Enter fullscreen mode Exit fullscreen mode

class_name: 'User' is used because the association sender doesn't follow the Rails convention of class_name being the same as the association name.

User Model

class User < ApplicationRecord #... has_many :availabilities, dependent: :destroy has_many :sent_requests, class_name: 'Request', foreign_key: 'sender_id', dependent: :destroy has_many :received_requests, through: :availabilities, source: :requests #... end 
Enter fullscreen mode Exit fullscreen mode

Availability Model

class Availability < ApplicationRecord belongs_to :user has_many :requests, dependent: :destroy #... end 
Enter fullscreen mode Exit fullscreen mode

Run
rails db:migrate

Next Steps

Now I am ready to test out my domain model and create some sample data tasks. Populating my database with data will confirm that my associations are set up properly and will also expose any missing validations that should be added to my models.

Top comments (0)