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 "turbo-rails"
|
||||||
gem "stimulus-rails"
|
gem "stimulus-rails"
|
||||||
gem "jbuilder"
|
gem "jbuilder"
|
||||||
# gem "bcrypt", "~> 3.1.7"
|
gem "bcrypt", "~> 3.1.7"
|
||||||
gem "tzinfo-data", platforms: %i[ windows jruby ]
|
gem "tzinfo-data", platforms: %i[ windows jruby ]
|
||||||
gem "bootsnap", require: false
|
gem "bootsnap", require: false
|
||||||
# gem "image_processing", "~> 1.2"
|
# gem "image_processing", "~> 1.2"
|
||||||
|
|
|
@ -78,6 +78,7 @@ GEM
|
||||||
addressable (2.8.6)
|
addressable (2.8.6)
|
||||||
public_suffix (>= 2.0.2, < 6.0)
|
public_suffix (>= 2.0.2, < 6.0)
|
||||||
base64 (0.2.0)
|
base64 (0.2.0)
|
||||||
|
bcrypt (3.1.20)
|
||||||
bigdecimal (3.1.7)
|
bigdecimal (3.1.7)
|
||||||
bindex (0.8.1)
|
bindex (0.8.1)
|
||||||
bootsnap (1.18.3)
|
bootsnap (1.18.3)
|
||||||
|
@ -250,6 +251,7 @@ PLATFORMS
|
||||||
x86_64-linux
|
x86_64-linux
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
bcrypt (~> 3.1.7)
|
||||||
bootsnap
|
bootsnap
|
||||||
capybara
|
capybara
|
||||||
debug
|
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
|
end
|
||||||
|
|
||||||
FileUtils.chdir APP_ROOT do
|
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 =="
|
puts "== Installing dependencies =="
|
||||||
system! "gem install bundler --conservative"
|
system! "gem install bundler --conservative"
|
||||||
system("bundle check") || system!("bundle install")
|
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 =="
|
puts "\n== Preparing database =="
|
||||||
system! "bin/rails db:prepare"
|
system! "bin/rails db:prepare"
|
||||||
|
|
||||||
|
puts "\n== Loading database fixtures =="
|
||||||
|
system! "bin/rails db:fixtures:load"
|
||||||
|
|
||||||
puts "\n== Removing old logs and tempfiles =="
|
puts "\n== Removing old logs and tempfiles =="
|
||||||
system! "bin/rails log:clear tmp:clear"
|
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.
|
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
|
||||||
fixtures :all
|
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
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue