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"] %>
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
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
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
Update Models
class User < ApplicationRecord
#...
has_many :availabilities, dependent: :destroy
end
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
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
## 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
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
Availability Model
class Availability < ApplicationRecord
belongs_to :user
has_many :requests, dependent: :destroy
#...
end
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)