In Part I, I went over why I want to build a social media marketing app, the languages and frameworks I plan to use, some user stories and how I got started.
At this point in the application building process, I made myself stop and do more thorough database planning. I decided on my models, their attributes and their relationships to each other. In an effort to keep this short and sweet, I’ll just highlight a few snippets of my model planning:
Users
- role string
- email string
- password_digest string
username string
has_many :created_posts, class_name: “Post”, foreign_key: :creator_id
has_many :assigned_posts, class_name: “Post”, foreign_key: :assignee_id
has_many :pages
Post
- platform string
- content text
- link string
page_id , foreign_key
belongs_to :creator, class_name: “User”, foreign_key: :creator_id
belongs_to :assignee, class_name: “User”, foreign_key: :assignee_id
belongs_to :page
I started with my User model. First I rolled my migration back, to add some attributes missing from my initial migration. This is what my final (for now) Create Users migration looks like:
class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
t.string :username
t.string :password_digest
t.string :email
t.string :role
t.timestamps
end
end
end
With my new model set up, I started writing my initial tests, based on how I want to define a valid User.
require 'rails_helper'
RSpec.describe User, type: :model do
subject {User.new(username:'Robert')}
before {subject.save}
it 'should have a username' do
subject.username = nil
expect(subject).to_not be_valid
end
it 'should have a unique username' do
user = User.create(username:'Sam')
subject.username = 'Sam'
expect(subject).to_not be_valid
end
it 'should have an email address' do
subject.email = nil
expect(subject).to_not be_valid
end
end
The the code snippet above, the tests are describing what a valid User should have. A User should have a username, so if a User instance doesn't have a username, it isn't valid.
To make sure my tests were all passing, I added validations to be user model.
class User < ApplicationRecord
validates :username, presence: true, uniqueness: true
validates :email, presence: true
has_secure_password
end
The last line of code in my User model is in reference to the bcrypt gem.
In an effort to not get ahead of myself, and build vertically, I decided that now was the perfect time to built out my first User controller action.
/app/controllers/users_controller.rb
class UsersController < ApplicationController
wrap_parameters :user, include: [:username, :password, :email]
def create
@user = User.new(user_params)
if @user.save
session[:user_id] = @user.id
render json: {user: @user.username ,status: :created}
else
render json: {error: @user.errors.full_messages.to_sentence, status: :unprocessable_entity}
end
end
private
def user_params
params.require(:user).permit(:username, :email, :password)
end
end
In the code snippet above, I define a private method that specifies what params we're permitting when the signup form is completed. To make sure this works, I'm also wrapping my parameters at the start of the controller. The user#create action saves valid users and renders an error when a user is invalid.
Then I hopped over to config/routes.rb to match my signup route to my new controller action.
Rails.application.routes.draw do
post '/signup', to: "users#create"
end
After completing this step, my first inclination was to build the signup form and test the create action manually. But we're learning and growing, so we wrote a request spec instead!
spec/requests/users_request_spec.rb
require 'rails_helper'
RSpec.describe "Users", type: :request do
describe "POST users#create" do
it "creates a new user with valid attributes" do
user_params = {
user: {
username: 'TaterTot',
email: 'tatertot@aol.com',
password: 'potatoes'
}
}
post '/signup', :params => user_params.to_json, :headers => { "Content-Type": "application/json" }
json = JSON.parse(response.body)
expect(response).to have_http_status(201)
end
it "renders an error with invalid attributes" do
user_params = {
user: {
username: 'TaterTot',
email: 'tatertot@aol.com'
}
}
post '/signup', :params => user_params.to_json, :headers => { "Content-Type": "application/json" }
json = JSON.parse(response.body)
expect(json["error"]).to include("Password can't be blank")
end
end
end
Ta-da! We are up to 6 passing examples (but who's counting)! In this post, I described the creation of my first model, its first corresponding controller action and a few request specs. In Part III, I'll continue the vertical build, connecting the React frontend, and building out the Signup component.
Top comments (0)