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)