Add User model
This commit is contained in:
parent
1f52213c35
commit
2158426d88
2
Gemfile
2
Gemfile
|
@ -10,7 +10,7 @@ gem "importmap-rails"
|
|||
gem "turbo-rails"
|
||||
gem "stimulus-rails"
|
||||
gem "jbuilder"
|
||||
# gem "bcrypt", "~> 3.1.7"
|
||||
gem "bcrypt", "~> 3.1.7"
|
||||
gem "tzinfo-data", platforms: %i[ windows jruby ]
|
||||
gem "bootsnap", require: false
|
||||
# gem "image_processing", "~> 1.2"
|
||||
|
|
|
@ -78,6 +78,7 @@ GEM
|
|||
addressable (2.8.6)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
base64 (0.2.0)
|
||||
bcrypt (3.1.20)
|
||||
bigdecimal (3.1.7)
|
||||
bindex (0.8.1)
|
||||
bootsnap (1.18.3)
|
||||
|
@ -250,6 +251,7 @@ PLATFORMS
|
|||
x86_64-linux
|
||||
|
||||
DEPENDENCIES
|
||||
bcrypt (~> 3.1.7)
|
||||
bootsnap
|
||||
capybara
|
||||
debug
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
class User < ApplicationRecord
|
||||
has_secure_password
|
||||
generates_token_for :password_reset, expires_in: 4.hours do
|
||||
password_salt.last(10) # Invalidates when password changed
|
||||
end
|
||||
|
||||
validates :username,
|
||||
presence: true,
|
||||
uniqueness: true,
|
||||
length: { minimum: 3, maximum: 20 }
|
||||
normalizes :username, with: ->(username) { username.strip.downcase }
|
||||
validates :email,
|
||||
presence: true,
|
||||
uniqueness: true,
|
||||
length: { minimum: 5, maximum: 100 },
|
||||
format: { with: URI::MailTo::EMAIL_REGEXP,
|
||||
message: "must be a valid email address" }
|
||||
normalizes :email, with: ->(email) { email.strip.downcase }
|
||||
validates :first_name,
|
||||
presence: true,
|
||||
length: { maximum: 50 }
|
||||
validates :last_name,
|
||||
allow_nil: false,
|
||||
length: { maximum: 50 }
|
||||
|
||||
def full_name
|
||||
return first_name if last_name.blank?
|
||||
|
||||
"#{first_name} #{last_name}"
|
||||
end
|
||||
end
|
12
bin/setup
12
bin/setup
|
@ -9,22 +9,16 @@ def system!(*args)
|
|||
end
|
||||
|
||||
FileUtils.chdir APP_ROOT do
|
||||
# This script is a way to set up or update your development environment automatically.
|
||||
# This script is idempotent, so that you can run it at any time and get an expectable outcome.
|
||||
# Add necessary setup steps to this file.
|
||||
|
||||
puts "== Installing dependencies =="
|
||||
system! "gem install bundler --conservative"
|
||||
system("bundle check") || system!("bundle install")
|
||||
|
||||
# puts "\n== Copying sample files =="
|
||||
# unless File.exist?("config/database.yml")
|
||||
# FileUtils.cp "config/database.yml.sample", "config/database.yml"
|
||||
# end
|
||||
|
||||
puts "\n== Preparing database =="
|
||||
system! "bin/rails db:prepare"
|
||||
|
||||
puts "\n== Loading database fixtures =="
|
||||
system! "bin/rails db:fixtures:load"
|
||||
|
||||
puts "\n== Removing old logs and tempfiles =="
|
||||
system! "bin/rails log:clear tmp:clear"
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
class CreateUsers < ActiveRecord::Migration[7.1]
|
||||
def change
|
||||
create_table :users do |t|
|
||||
t.string :username, null: false, limit: 20
|
||||
t.string :password_digest, null: false, limit: 200
|
||||
t.string :email, null: false, limit: 100
|
||||
t.string :first_name, null: false, limit: 50
|
||||
t.string :last_name, null: false, limit: 50, default: ""
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
add_index :users, :username, unique: true
|
||||
add_index :users, :email, unique: true
|
||||
|
||||
add_check_constraint :users, "length(username) >= 3",
|
||||
name: "chk_username_min_length"
|
||||
add_check_constraint :users, "length(email) >= 5",
|
||||
name: "chk_email_min_length"
|
||||
add_check_constraint :users, "length(first_name) >= 1",
|
||||
name: "chk_first_name_min_length"
|
||||
end
|
||||
end
|
|
@ -0,0 +1,32 @@
|
|||
# This file is auto-generated from the current state of the database. Instead
|
||||
# of editing this file, please use the migrations feature of Active Record to
|
||||
# incrementally modify your database, and then regenerate this schema definition.
|
||||
#
|
||||
# This file is the source Rails uses to define your schema when running `bin/rails
|
||||
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
|
||||
# be faster and is potentially less error prone than running all of your
|
||||
# migrations from scratch. Old migrations may fail to apply correctly if those
|
||||
# migrations use external dependencies or application code.
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[7.1].define(version: 2024_04_13_152553) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
||||
create_table "users", force: :cascade do |t|
|
||||
t.string "username", limit: 20, null: false
|
||||
t.string "password_digest", limit: 200, null: false
|
||||
t.string "email", limit: 100, null: false
|
||||
t.string "first_name", limit: 50, null: false
|
||||
t.string "last_name", limit: 50, default: "", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["email"], name: "index_users_on_email", unique: true
|
||||
t.index ["username"], name: "index_users_on_username", unique: true
|
||||
t.check_constraint "length(email::text) >= 5", name: "chk_email_min_length"
|
||||
t.check_constraint "length(first_name::text) >= 1", name: "chk_first_name_min_length"
|
||||
t.check_constraint "length(username::text) >= 3", name: "chk_username_min_length"
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
trevor:
|
||||
username: tsv
|
||||
password_digest: <%= BCrypt::Password.create('password', cost: 5) %>
|
||||
email: trevor@example.com
|
||||
first_name: Trevor
|
||||
last_name: Vallender
|
||||
|
||||
<% 1.upto(10) do |i| %>
|
||||
user_<%= i %>:
|
||||
username: <%= "user_#{i}" %>
|
||||
password_digest: <%= BCrypt::Password.create('password', cost: 5) %>
|
||||
email: <%= "user_#{i}@example.com" %>
|
||||
first_name: <%= "User#{i}" %>
|
||||
last_name: <%= "User#{i}" %>
|
||||
<% end %>
|
|
@ -0,0 +1,48 @@
|
|||
require "test_helper"
|
||||
|
||||
class UserTest < ActiveSupport::TestCase
|
||||
test "full name returns first and last name" do
|
||||
assert_equal "Trevor Vallender", users(:trevor).full_name
|
||||
end
|
||||
|
||||
test "username must exist" do
|
||||
assert_must_exist(users(:trevor), "username")
|
||||
end
|
||||
|
||||
test "username must be unique" do
|
||||
user1 = User.first
|
||||
user2 = User.second
|
||||
user1.username = user2.username
|
||||
assert_not user1.valid?
|
||||
end
|
||||
|
||||
test "email must exist" do
|
||||
assert_must_exist(users(:trevor), "email")
|
||||
end
|
||||
|
||||
test "email must be unique" do
|
||||
user1 = User.first
|
||||
user2 = User.second
|
||||
user1.email = user2.email
|
||||
assert_not user1.valid?
|
||||
end
|
||||
|
||||
test "email must be a valid email" do
|
||||
user = users(:trevor)
|
||||
user.email = "trevor"
|
||||
assert_not user.valid?
|
||||
assert_includes user.errors[:email], "must be a valid email address"
|
||||
end
|
||||
|
||||
test "first name must exist" do
|
||||
assert_must_exist(users(:trevor), "first_name")
|
||||
end
|
||||
|
||||
test "password reset token is invalid after password changed" do
|
||||
user = users(:trevor)
|
||||
token = user.generate_token_for(:password_reset)
|
||||
assert_equal user, User.find_by_token_for(:password_reset, token)
|
||||
user.update(password: "new_password")
|
||||
assert_nil User.find_by_token_for(:password_reset, token)
|
||||
end
|
||||
end
|
|
@ -10,6 +10,12 @@ module ActiveSupport
|
|||
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
|
||||
fixtures :all
|
||||
|
||||
# Add more helper methods to be used by all tests here...
|
||||
def assert_must_exist(record, field)
|
||||
assert record.valid?
|
||||
record.attributes = { "#{field}": nil }
|
||||
assert_not record.valid?
|
||||
assert_includes record.errors[field.to_sym], "can't be blank"
|
||||
assert_raises { record.save(validate: false) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue