diff --git a/Gemfile b/Gemfile index a8f5c18..250069b 100644 --- a/Gemfile +++ b/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" diff --git a/Gemfile.lock b/Gemfile.lock index f83fe37..21fccc9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -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 diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb new file mode 100644 index 0000000..2ec9ecc --- /dev/null +++ b/app/controllers/users_controller.rb @@ -0,0 +1,4 @@ +class UsersController < ApplicationController + def new + end +end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb new file mode 100644 index 0000000..2310a24 --- /dev/null +++ b/app/helpers/users_helper.rb @@ -0,0 +1,2 @@ +module UsersHelper +end diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 0000000..6c288ab --- /dev/null +++ b/app/models/user.rb @@ -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 diff --git a/app/views/static_pages/home.html.erb b/app/views/static_pages/home.html.erb index 3939179..696fc08 100644 --- a/app/views/static_pages/home.html.erb +++ b/app/views/static_pages/home.html.erb @@ -7,7 +7,7 @@ Sample application. - <%= link_to "Sing up now!", '#', class:"btn btn-lg btn-primary" %> + <%= link_to "Sing up now!", signup_path, class:"btn btn-lg btn-primary" %> <%= link_to image_tag("rails.svg", alt:"Rails logo", width: "200"), "https://rubyonrails.org/" %> diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb new file mode 100644 index 0000000..9c841e8 --- /dev/null +++ b/app/views/users/new.html.erb @@ -0,0 +1,3 @@ +<% provide(:title, 'Sign up') %> +

Sign up

+

This will be a signup page for new users.

diff --git a/config/routes.rb b/config/routes.rb index 8cd3f39..6f6cf03 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -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 diff --git a/db/migrate/20241231022507_create_users.rb b/db/migrate/20241231022507_create_users.rb new file mode 100644 index 0000000..3b80eb0 --- /dev/null +++ b/db/migrate/20241231022507_create_users.rb @@ -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 diff --git a/db/migrate/20241231055957_add_index_to_users_email.rb b/db/migrate/20241231055957_add_index_to_users_email.rb new file mode 100644 index 0000000..a8e4ed8 --- /dev/null +++ b/db/migrate/20241231055957_add_index_to_users_email.rb @@ -0,0 +1,5 @@ +class AddIndexToUsersEmail < ActiveRecord::Migration[8.0] + def change + add_index :users, :email, unique: true + end +end diff --git a/db/migrate/20241231060757_add_password_digest_to_users.rb b/db/migrate/20241231060757_add_password_digest_to_users.rb new file mode 100644 index 0000000..81c7c9e --- /dev/null +++ b/db/migrate/20241231060757_add_password_digest_to_users.rb @@ -0,0 +1,5 @@ +class AddPasswordDigestToUsers < ActiveRecord::Migration[8.0] + def change + add_column :users, :password_digest, :string + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000..94fd020 --- /dev/null +++ b/db/schema.rb @@ -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 diff --git a/test/controllers/static_pages_controller_test.rb b/test/controllers/static_pages_controller_test.rb index 4562acd..54aa596 100644 --- a/test/controllers/static_pages_controller_test.rb +++ b/test/controllers/static_pages_controller_test.rb @@ -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 diff --git a/test/controllers/users_controller_test.rb b/test/controllers/users_controller_test.rb new file mode 100644 index 0000000..8d9af29 --- /dev/null +++ b/test/controllers/users_controller_test.rb @@ -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 diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml new file mode 100644 index 0000000..46789ac --- /dev/null +++ b/test/fixtures/users.yml @@ -0,0 +1,5 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + name: MyString + email: MyString diff --git a/test/models/user_test.rb b/test/models/user_test.rb new file mode 100644 index 0000000..e1aee96 --- /dev/null +++ b/test/models/user_test.rb @@ -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 diff --git a/test/test_helper.rb b/test/test_helper.rb index 3daa94e..e10ed8c 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -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