From f63be42b4c178d4456bdb99b26bc929076daf39f Mon Sep 17 00:00:00 2001 From: Juan Rodriguez Date: Mon, 14 Jun 2021 08:24:34 -0500 Subject: [PATCH] feat: :sparkles: Create users Users migration, model, controller. user_id to links. signup view --- Gemfile | 2 +- Gemfile.lock | 7 +++--- README.md | 4 ++-- app/controllers/links_controller.rb | 8 ++++++- app/controllers/users_controller.rb | 23 +++++++++++++++++++ app/helpers/users_helper.rb | 15 ++++++++++++ .../controllers/links_controller.js | 9 ++++---- .../controllers/users_controller.js | 12 ++++++++++ app/models/link.rb | 2 ++ app/models/user.rb | 7 ++++++ app/views/links/_form.html.erb | 6 ++--- app/views/links/index.html.erb | 1 + app/views/users/new.html.erb | 10 ++++++++ config/routes.rb | 3 ++- db/migrate/20210614113058_create_users.rb | 12 ++++++++++ .../20210614114837_add_user_id_to_links.rb | 7 ++++++ db/schema.rb | 13 ++++++++++- test/controllers/users_controller_test.rb | 15 ++++++++++++ test/fixtures/users.yml | 9 ++++++++ test/models/user_test.rb | 9 ++++++++ yarn.lock | 6 ++--- 21 files changed, 159 insertions(+), 21 deletions(-) create mode 100644 app/controllers/users_controller.rb create mode 100644 app/helpers/users_helper.rb create mode 100644 app/javascript/controllers/users_controller.js create mode 100644 app/models/user.rb create mode 100644 app/views/users/new.html.erb create mode 100644 db/migrate/20210614113058_create_users.rb create mode 100644 db/migrate/20210614114837_add_user_id_to_links.rb create mode 100644 test/controllers/users_controller_test.rb create mode 100644 test/fixtures/users.yml create mode 100644 test/models/user_test.rb diff --git a/Gemfile b/Gemfile index 843c26f..eb717c5 100644 --- a/Gemfile +++ b/Gemfile @@ -25,7 +25,7 @@ gem 'jbuilder', '~> 2.5' # Use Redis adapter to run Action Cable in production # gem 'redis', '~> 4.0' # Use ActiveModel has_secure_password -# gem 'bcrypt', '~> 3.1.7' +gem 'bcrypt', '~> 3.1.7' # Use ActiveStorage variant # gem 'mini_magick', '~> 4.8' diff --git a/Gemfile.lock b/Gemfile.lock index 316d0e5..40814b5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -45,6 +45,8 @@ GEM addressable (2.7.0) public_suffix (>= 2.0.2, < 5.0) arel (9.0.0) + bcrypt (3.1.16) + bcrypt (3.1.16-java) bindex (0.8.1) bootsnap (1.7.5) msgpack (~> 1.0) @@ -171,9 +173,6 @@ GEM thor (1.1.0) thread_safe (0.3.6) thread_safe (0.3.6-java) - turbolinks (5.2.1) - turbolinks-source (~> 5.2) - turbolinks-source (5.2.0) tzinfo (1.2.9) thread_safe (~> 0.1) uglifier (4.2.0) @@ -208,6 +207,7 @@ PLATFORMS x86-mswin32 DEPENDENCIES + bcrypt (~> 3.1.7) bootsnap (>= 1.1.0) byebug capybara (>= 2.15) @@ -220,7 +220,6 @@ DEPENDENCIES simplecov spring spring-watcher-listen (~> 2.0.0) - turbolinks (~> 5) uglifier (>= 1.3.0) web-console (>= 3.3.0) webdrivers diff --git a/README.md b/README.md index 94918f9..753b755 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,8 @@ docker-compose run --rm app rubocop - [x] Stimulus setup - [x] Link controller (handle redirection) - [x] Main page with input box -- [ ] Create user model +- [x] Create user model - [ ] User unit tests - [ ] Add userId key to link model - [ ] Login and logout (sessions) -- [ ] Cache with redis? \ No newline at end of file +- [ ] Setup Redis for production cache_store \ No newline at end of file diff --git a/app/controllers/links_controller.rb b/app/controllers/links_controller.rb index 139c800..91ee16e 100644 --- a/app/controllers/links_controller.rb +++ b/app/controllers/links_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class LinksController < ApplicationController + before_action :authenticate, only: [:create] + def redirect @link = Link.find_by_slug(params[:slug]) @@ -13,7 +15,9 @@ class LinksController < ApplicationController end def create - @link = Link.find_or_create_by(url: link_params[:url]) + @link = Link.find_or_create_by(url: link_params[:url]) do |link| + link.user = @current_user if @current_user + end if @link.errors.any? render json: @link.errors, status: :unprocessable_entity @@ -22,6 +26,8 @@ class LinksController < ApplicationController end end + private + def link_params params.require(:link).permit(:url) end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb new file mode 100644 index 0000000..8921f74 --- /dev/null +++ b/app/controllers/users_controller.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class UsersController < ApplicationController + def new + @user = User.new + end + + def create + @user = User.create(user_params) + if @user.errors.any? + render json: @user.errors, status: :unprocessable_entity + else + session[:user_id] = @user.id + redirect_to '/' + end + end + + private + + def user_params + params.require(:user).permit(:username, :password) + end +end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb new file mode 100644 index 0000000..1b220e9 --- /dev/null +++ b/app/helpers/users_helper.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module UsersHelper + def current_user + User.find_by(id: session[:user_id]) + end + + def logged_in? + !current_user.nil? + end + + def authorized + redirect_to '/welcome' unless logged_in? + end +end diff --git a/app/javascript/controllers/links_controller.js b/app/javascript/controllers/links_controller.js index 1257723..32bbb8e 100644 --- a/app/javascript/controllers/links_controller.js +++ b/app/javascript/controllers/links_controller.js @@ -4,16 +4,15 @@ export default class extends Controller { static targets = ["url", "output"] onSuccess(event) { - event.preventDefault() - const [, , xhr] = event.detail this.outputTarget.innerHTML = xhr.response } onError(event) { - event.preventDefault() - const [data, ,] = event.detail - alert(data.url.join(' ')) + + const urlError = `Url: ${data.url.join(' ')}` + + alert(urlError) } } diff --git a/app/javascript/controllers/users_controller.js b/app/javascript/controllers/users_controller.js new file mode 100644 index 0000000..de20479 --- /dev/null +++ b/app/javascript/controllers/users_controller.js @@ -0,0 +1,12 @@ +import { Controller } from "stimulus" + +export default class extends Controller { + onError(event) { + const [data, ,] = event.detail + + const usernameError = `Username: ${data.username.join(' ')}` + const passwordError = `Password: ${data.username.join(' ')}` + + alert(`${usernameError}, ${passwordError}`) + } +} diff --git a/app/models/link.rb b/app/models/link.rb index ce6d281..a3181eb 100644 --- a/app/models/link.rb +++ b/app/models/link.rb @@ -18,4 +18,6 @@ class Link < ApplicationRecord def short Rails.application.routes.url_helpers.short_url(slug: slug) end + + belongs_to :user, optional: true end diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 0000000..b0655ca --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class User < ApplicationRecord + validates_uniqueness_of :username + + has_secure_password +end diff --git a/app/views/links/_form.html.erb b/app/views/links/_form.html.erb index 6bf6107..2a42566 100644 --- a/app/views/links/_form.html.erb +++ b/app/views/links/_form.html.erb @@ -1,14 +1,14 @@ -<%= form_with model: Link.new, url: links_path(@link), data: { action: 'ajax:success->links#onSuccess ajax:error->links#onError' } do |form| %> +<%= form_with model: Link.new, url: links_path(@link), data: { action: 'ajax:success->links#onSuccess ajax:error->links#onError' } do |f| %>
- <%= form.text_field :url, data: { target: "links.url" }, placeholder: true, class: "focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-md sm:text-sm border-gray-300"%> + <%= f.text_field :url, data: { target: "links.url" }, placeholder: true, class: "focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-md sm:text-sm border-gray-300"%>
- <%= form.submit "Generate", class: "inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" %> + <%= f.submit "Generate", class: "inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" %>
<% end %> \ No newline at end of file diff --git a/app/views/links/index.html.erb b/app/views/links/index.html.erb index 28c4227..28825d9 100644 --- a/app/views/links/index.html.erb +++ b/app/views/links/index.html.erb @@ -5,6 +5,7 @@
<%= render partial: "links/form" %>
+ <%= button_to "Sign Up", '/users/new', method: :get%>
diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb new file mode 100644 index 0000000..bcb4b9e --- /dev/null +++ b/app/views/users/new.html.erb @@ -0,0 +1,10 @@ +

Sign Up

+
+ <%= form_for @user, data: { action: 'ajax:error->users#onError' } do |f| %> + <%= f.label :username%>
+ <%= f.text_field :username%>
+ <%= f.label :password%>
+ <%= f.password_field :password%>
+ <%= f.submit %> + <%end%> +
\ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index ded7904..a6791f5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -5,5 +5,6 @@ Rails.application.routes.draw do get '/:slug', to: 'links#redirect', as: :short - resources :links, only: [:create] + resources :links, only: %i[create] + resources :users, only: %i[new create] end diff --git a/db/migrate/20210614113058_create_users.rb b/db/migrate/20210614113058_create_users.rb new file mode 100644 index 0000000..f218314 --- /dev/null +++ b/db/migrate/20210614113058_create_users.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class CreateUsers < ActiveRecord::Migration[5.2] + def change + create_table :users do |t| + t.string :username + t.string :password_digest + + t.timestamps + end + end +end diff --git a/db/migrate/20210614114837_add_user_id_to_links.rb b/db/migrate/20210614114837_add_user_id_to_links.rb new file mode 100644 index 0000000..9f4335d --- /dev/null +++ b/db/migrate/20210614114837_add_user_id_to_links.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddUserIdToLinks < ActiveRecord::Migration[5.2] + def change + add_reference :links, :user, foreign_key: true + end +end diff --git a/db/schema.rb b/db/schema.rb index abaec47..43b7b21 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -12,7 +12,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20_210_613_145_459) do +ActiveRecord::Schema.define(version: 20_210_614_114_837) do # These are extensions that must be enabled in order to support this database enable_extension 'plpgsql' @@ -22,6 +22,17 @@ ActiveRecord::Schema.define(version: 20_210_613_145_459) do t.integer 'click_counter', default: 0 t.datetime 'created_at', null: false t.datetime 'updated_at', null: false + t.bigint 'user_id' t.index ['slug'], name: 'index_links_on_slug' + t.index ['user_id'], name: 'index_links_on_user_id' end + + create_table 'users', force: :cascade do |t| + t.string 'username' + t.string 'password_digest' + t.datetime 'created_at', null: false + t.datetime 'updated_at', null: false + end + + add_foreign_key 'links', 'users' end diff --git a/test/controllers/users_controller_test.rb b/test/controllers/users_controller_test.rb new file mode 100644 index 0000000..6735f9c --- /dev/null +++ b/test/controllers/users_controller_test.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'test_helper' + +class UsersControllerTest < ActionDispatch::IntegrationTest + test 'should get new' do + get users_new_url + assert_response :success + end + + test 'should get create' do + get users_create_url + assert_response :success + end +end diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml new file mode 100644 index 0000000..0ce793b --- /dev/null +++ b/test/fixtures/users.yml @@ -0,0 +1,9 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + username: MyString + password_digest: MyString + +two: + username: MyString + password_digest: MyString diff --git a/test/models/user_test.rb b/test/models/user_test.rb new file mode 100644 index 0000000..5cc44ed --- /dev/null +++ b/test/models/user_test.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require 'test_helper' + +class UserTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/yarn.lock b/yarn.lock index 1993f92..2d64352 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6088,9 +6088,9 @@ postcss@^7, postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, po supports-color "^6.1.0" postcss@^8.2.1, postcss@^8.2.10, postcss@^8.2.15, postcss@^8.2.9: - version "8.3.2" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.2.tgz#ed3ec489f5428af5740cd6effcc216b4d455ee64" - integrity sha512-y1FK/AWdZlBF5lusS5j5l4/vF67+vQZt1SXPVJ32y1kRGDQyrs1zk32hG1cInRTu14P0V+orPz+ifwW/7rR4bg== + version "8.3.3" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.3.tgz#ef412a7a67e85c5b2c9f0ab3c4d9e8a3814d55cc" + integrity sha512-gnXd9C4bGKevvlNFd80I8WfxHX+g6MR+W2h19PlDNHUuT9248rHTvCIDeZI3Hvs5mB3gzXiNDwVK3S153WJbZA== dependencies: colorette "^1.2.2" nanoid "^3.1.23"