feat: add user registration functionality
- Add User model with validations for name and email - Implement UsersController with new action for signup - Create views for user signup and home page - Update routes to include signup path - Add bcrypt gem for password security - Include tests for user model and controller actions This commit establishes the foundation for user registration in the application, ensuring proper validation and security measures are in place. It also enhances the user experience by providing a dedicated signup page.
This commit is contained in:
parent
02b7393ed4
commit
cd558466be
2
Gemfile
2
Gemfile
@ -20,6 +20,8 @@ gem "bootstrap-sass", "~> 3.4.1"
|
||||
gem "sassc-rails", ">= 2.1.0"
|
||||
gem "jquery-rails", "~> 4.6.0"
|
||||
|
||||
gem 'bcrypt', '~> 3.1'
|
||||
|
||||
# Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword]
|
||||
# gem "bcrypt", "~> 3.1.7"
|
||||
|
||||
|
@ -79,6 +79,7 @@ GEM
|
||||
autoprefixer-rails (10.4.19.0)
|
||||
execjs (~> 2)
|
||||
base64 (0.2.0)
|
||||
bcrypt (3.1.20)
|
||||
bcrypt_pbkdf (1.1.1)
|
||||
bcrypt_pbkdf (1.1.1-arm64-darwin)
|
||||
bcrypt_pbkdf (1.1.1-x86_64-darwin)
|
||||
@ -433,6 +434,7 @@ PLATFORMS
|
||||
x86_64-linux-musl
|
||||
|
||||
DEPENDENCIES
|
||||
bcrypt (~> 3.1)
|
||||
bootsnap
|
||||
bootstrap-sass (~> 3.4.1)
|
||||
brakeman
|
||||
|
4
app/controllers/users_controller.rb
Normal file
4
app/controllers/users_controller.rb
Normal file
@ -0,0 +1,4 @@
|
||||
class UsersController < ApplicationController
|
||||
def new
|
||||
end
|
||||
end
|
2
app/helpers/users_helper.rb
Normal file
2
app/helpers/users_helper.rb
Normal file
@ -0,0 +1,2 @@
|
||||
module UsersHelper
|
||||
end
|
11
app/models/user.rb
Normal file
11
app/models/user.rb
Normal file
@ -0,0 +1,11 @@
|
||||
class User < ApplicationRecord
|
||||
# before_save { self.email = email.downcase }
|
||||
before_save { email.downcase! }
|
||||
validates :name, presence: true, length: { maximum: 50 }
|
||||
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
|
||||
validates :email, presence: true, length: { maximum: 255 },
|
||||
format: { with: VALID_EMAIL_REGEX },
|
||||
uniqueness: true
|
||||
has_secure_password
|
||||
validates :password, presence: true, length: { minimum: 6 }
|
||||
end
|
@ -7,7 +7,7 @@
|
||||
Sample application.
|
||||
</h2>
|
||||
|
||||
<%= link_to "Sing up now!", '#', class:"btn btn-lg btn-primary" %>
|
||||
<%= link_to "Sing up now!", signup_path, class:"btn btn-lg btn-primary" %>
|
||||
</div>
|
||||
<%= link_to image_tag("rails.svg", alt:"Rails logo", width: "200"),
|
||||
"https://rubyonrails.org/" %>
|
||||
|
3
app/views/users/new.html.erb
Normal file
3
app/views/users/new.html.erb
Normal file
@ -0,0 +1,3 @@
|
||||
<% provide(:title, 'Sign up') %>
|
||||
<h1>Sign up</h1>
|
||||
<p>This will be a signup page for new users.</p>
|
@ -1,4 +1,5 @@
|
||||
Rails.application.routes.draw do
|
||||
get "users/new"
|
||||
get "static_pages/home"
|
||||
get "static_pages/help"
|
||||
get "static_pages/about"
|
||||
@ -7,6 +8,7 @@ Rails.application.routes.draw do
|
||||
get '/help', to: 'static_pages#help'
|
||||
get '/about', to: 'static_pages#about'
|
||||
get '/contact', to: 'static_pages#contact'
|
||||
get '/signup', to: 'users#new'
|
||||
|
||||
root "static_pages#home"
|
||||
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
|
||||
|
10
db/migrate/20241231022507_create_users.rb
Normal file
10
db/migrate/20241231022507_create_users.rb
Normal file
@ -0,0 +1,10 @@
|
||||
class CreateUsers < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
create_table :users do |t|
|
||||
t.string :name
|
||||
t.string :email
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
5
db/migrate/20241231055957_add_index_to_users_email.rb
Normal file
5
db/migrate/20241231055957_add_index_to_users_email.rb
Normal file
@ -0,0 +1,5 @@
|
||||
class AddIndexToUsersEmail < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
add_index :users, :email, unique: true
|
||||
end
|
||||
end
|
@ -0,0 +1,5 @@
|
||||
class AddPasswordDigestToUsers < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
add_column :users, :password_digest, :string
|
||||
end
|
||||
end
|
22
db/schema.rb
generated
Normal file
22
db/schema.rb
generated
Normal file
@ -0,0 +1,22 @@
|
||||
# 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[8.0].define(version: 2024_12_31_060757) do
|
||||
create_table "users", force: :cascade do |t|
|
||||
t.string "name"
|
||||
t.string "email"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.string "password_digest"
|
||||
t.index ["email"], name: "index_users_on_email", unique: true
|
||||
end
|
||||
end
|
@ -9,25 +9,25 @@ class StaticPagesControllerTest < ActionDispatch::IntegrationTest
|
||||
test "should get root" do
|
||||
get root_path
|
||||
assert_response :success
|
||||
assert_select "title", "Home | #{@base_title}"
|
||||
assert_select "title", full_title("Home")
|
||||
end
|
||||
|
||||
test "should get help" do
|
||||
get help_path
|
||||
assert_response :success
|
||||
assert_select "title", "Help | #{@base_title}"
|
||||
assert_select "title", full_title("Help")
|
||||
end
|
||||
|
||||
test "Should get about" do
|
||||
get about_path
|
||||
assert_response :success
|
||||
assert_select "title", "About | #{@base_title}"
|
||||
assert_select "title", full_title("About")
|
||||
end
|
||||
|
||||
test "Should get contact" do
|
||||
get contact_path
|
||||
assert_response :success
|
||||
assert_select "title", "Contact | #{@base_title}"
|
||||
assert_select "title", full_title("Contact")
|
||||
end
|
||||
end
|
||||
|
||||
|
14
test/controllers/users_controller_test.rb
Normal file
14
test/controllers/users_controller_test.rb
Normal file
@ -0,0 +1,14 @@
|
||||
require "test_helper"
|
||||
|
||||
class UsersControllerTest < ActionDispatch::IntegrationTest
|
||||
test "should get new" do
|
||||
get signup_path
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "Should get sign up title" do
|
||||
get signup_path
|
||||
assert_response :success
|
||||
assert_select "title", full_title("Sign up")
|
||||
end
|
||||
end
|
5
test/fixtures/users.yml
vendored
Normal file
5
test/fixtures/users.yml
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
|
||||
|
||||
one:
|
||||
name: MyString
|
||||
email: MyString
|
76
test/models/user_test.rb
Normal file
76
test/models/user_test.rb
Normal file
@ -0,0 +1,76 @@
|
||||
require "test_helper"
|
||||
|
||||
class UserTest < ActiveSupport::TestCase
|
||||
# test "the truth" do
|
||||
# assert true
|
||||
# end
|
||||
def setup
|
||||
@user = User.new(name: "Example User", email: "user@example.com",
|
||||
password: "foobar", password_confirmation: "foobar")
|
||||
end
|
||||
|
||||
test "should be valid" do
|
||||
assert @user.valid?
|
||||
end
|
||||
|
||||
test "name should be present" do
|
||||
@user.name = " " * 6
|
||||
assert_not @user.valid?
|
||||
end
|
||||
|
||||
test "email should be present" do
|
||||
@user.email = " " * 6
|
||||
assert_not @user.valid?
|
||||
end
|
||||
|
||||
test "name should not be too long" do
|
||||
@user.name = "a" * 51
|
||||
assert_not @user.valid?
|
||||
end
|
||||
|
||||
test "email should not be too long" do
|
||||
@user.email = "a" * 244 + "@example.com"
|
||||
assert_not @user.valid?
|
||||
end
|
||||
|
||||
test "email validation should accept valid addresses" do
|
||||
valid_addresses = %w[user@example.com USER@foo.COM A_US-ER@foo.bar.org
|
||||
first.last@foo.cn alice+bob@baz.cn]
|
||||
valid_addresses.each do |valid_address|
|
||||
@user.email = valid_address
|
||||
assert @user.valid?, "#{valid_address.inspect} should be valid"
|
||||
end
|
||||
end
|
||||
|
||||
test "email validation should reject invalid addresses" do
|
||||
invalid_addresses = %w[user@example,com user_at_foo.org user.name@example.
|
||||
foo@bar_baz.com foo@bar+baz.com foo@bar...cc]
|
||||
invalid_addresses.each do |invalid_address|
|
||||
@user.email = invalid_address
|
||||
assert_not @user.valid?, "#{invalid_address.inspect} should be invalid"
|
||||
end
|
||||
end
|
||||
|
||||
test "email addresses should be unique" do
|
||||
duplicate_user = @user.dup
|
||||
@user.save
|
||||
assert_not duplicate_user.valid?
|
||||
end
|
||||
|
||||
test "email addresses should be saved as lower-case" do
|
||||
mixed_case_email = "Foo@ExAMPle.CoM"
|
||||
@user.email = mixed_case_email
|
||||
@user.save
|
||||
assert_equal mixed_case_email.downcase, @user.reload.email
|
||||
end
|
||||
|
||||
test "password should be present (non blank)" do
|
||||
@user.password = @user.password_confirmation = " " * 6
|
||||
assert_not @user.valid?
|
||||
end
|
||||
|
||||
test "password should have a minimum length" do
|
||||
@user.password = @user.password_confirmation = " " * 5
|
||||
assert_not @user.valid?
|
||||
end
|
||||
end
|
@ -11,6 +11,7 @@ module ActiveSupport
|
||||
|
||||
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
|
||||
fixtures :all
|
||||
include ApplicationHelper
|
||||
|
||||
# Add more helper methods to be used by all tests here...
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user