From e7f3f8749372852c5b8c4083b0f9be68d8744de9 Mon Sep 17 00:00:00 2001 From: Charlie Parker Date: Thu, 11 May 2023 16:24:40 +0100 Subject: [PATCH] final --- Gemfile | 12 ++++ Gemfile.lock | 75 +++++++++++++++++-------- app.rb | 78 ++++++++++++++++++++++++++ config.ru | 2 + lib/database_connection.rb | 39 +++++++++++++ lib/peep.rb | 3 + lib/peep_repo.rb | 52 +++++++++++++++++ lib/user.rb | 3 + lib/user_repo.rb | 55 ++++++++++++++++++ spec/integration/app_sinatra_spec.rb | 83 ++++++++++++++++++++++++++++ spec/peep_spec.rb | 51 +++++++++++++++++ spec/seeds/posts_seeds.sql | 17 ++++++ spec/seeds/test_seeds.sql | 28 ++++++++++ spec/seeds/user_seeds.sql | 14 +++++ spec/spec_helper.rb | 11 ++++ spec/user_spec.rb | 74 +++++++++++++++++++++++++ views/index.erb | 33 +++++++++++ views/login.erb | 18 ++++++ views/signup.erb | 20 +++++++ 19 files changed, 645 insertions(+), 23 deletions(-) create mode 100644 app.rb create mode 100644 config.ru create mode 100644 lib/database_connection.rb create mode 100644 lib/peep.rb create mode 100644 lib/peep_repo.rb create mode 100644 lib/user.rb create mode 100644 lib/user_repo.rb create mode 100644 spec/integration/app_sinatra_spec.rb create mode 100644 spec/peep_spec.rb create mode 100644 spec/seeds/posts_seeds.sql create mode 100644 spec/seeds/test_seeds.sql create mode 100644 spec/seeds/user_seeds.sql create mode 100644 spec/user_spec.rb create mode 100644 views/index.erb create mode 100644 views/login.erb create mode 100644 views/signup.erb diff --git a/Gemfile b/Gemfile index b1a320395a..44d861af6a 100644 --- a/Gemfile +++ b/Gemfile @@ -11,3 +11,15 @@ end group :development, :test do gem 'rubocop', '1.20' end + +gem "pg", "~> 1.5" + +gem "rack-test", "~> 2.1" + +gem "webrick", "~> 1.8" + +gem 'sinatra', '~> 3.0', '>= 3.0.6' + +gem 'rack', '~> 2.2', '>= 2.2.4' + +gem "sinatra-contrib", "~> 3.0" diff --git a/Gemfile.lock b/Gemfile.lock index 66064703c7..4d2b343587 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,27 +3,36 @@ GEM specs: ansi (1.5.0) ast (2.4.2) - diff-lcs (1.4.4) + diff-lcs (1.5.0) docile (1.4.0) - parallel (1.20.1) - parser (3.0.2.0) + multi_json (1.15.0) + mustermann (3.0.0) + ruby2_keywords (~> 0.0.1) + parallel (1.23.0) + parser (3.2.2.1) ast (~> 2.4.1) - rainbow (3.0.0) - regexp_parser (2.1.1) + pg (1.5.3) + rack (2.2.7) + rack-protection (3.0.6) + rack + rack-test (2.1.0) + rack (>= 1.3) + rainbow (3.1.1) + regexp_parser (2.8.0) rexml (3.2.5) - rspec (3.10.0) - rspec-core (~> 3.10.0) - rspec-expectations (~> 3.10.0) - rspec-mocks (~> 3.10.0) - rspec-core (3.10.1) - rspec-support (~> 3.10.0) - rspec-expectations (3.10.1) + rspec (3.12.0) + rspec-core (~> 3.12.0) + rspec-expectations (~> 3.12.0) + rspec-mocks (~> 3.12.0) + rspec-core (3.12.2) + rspec-support (~> 3.12.0) + rspec-expectations (3.12.3) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) - rspec-mocks (3.10.2) + rspec-support (~> 3.12.0) + rspec-mocks (3.12.5) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) - rspec-support (3.10.2) + rspec-support (~> 3.12.0) + rspec-support (3.12.0) rubocop (1.20.0) parallel (~> 1.10) parser (>= 3.0.0.0) @@ -33,10 +42,11 @@ GEM rubocop-ast (>= 1.9.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.11.0) - parser (>= 3.0.1.1) - ruby-progressbar (1.11.0) - simplecov (0.21.2) + rubocop-ast (1.28.1) + parser (>= 3.2.1.0) + ruby-progressbar (1.13.0) + ruby2_keywords (0.0.5) + simplecov (0.22.0) docile (~> 1.1) simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) @@ -45,19 +55,38 @@ GEM simplecov terminal-table simplecov-html (0.12.3) - simplecov_json_formatter (0.1.3) - terminal-table (3.0.1) + simplecov_json_formatter (0.1.4) + sinatra (3.0.6) + mustermann (~> 3.0) + rack (~> 2.2, >= 2.2.4) + rack-protection (= 3.0.6) + tilt (~> 2.0) + sinatra-contrib (3.0.6) + multi_json + mustermann (~> 3.0) + rack-protection (= 3.0.6) + sinatra (= 3.0.6) + tilt (~> 2.0) + terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) - unicode-display_width (2.0.0) + tilt (2.1.0) + unicode-display_width (2.4.2) + webrick (1.8.1) PLATFORMS ruby DEPENDENCIES + pg (~> 1.5) + rack (~> 2.2, >= 2.2.4) + rack-test (~> 2.1) rspec rubocop (= 1.20) simplecov simplecov-console + sinatra (~> 3.0, >= 3.0.6) + sinatra-contrib (~> 3.0) + webrick (~> 1.8) RUBY VERSION ruby 3.0.2p107 diff --git a/app.rb b/app.rb new file mode 100644 index 0000000000..0c01bf1e5a --- /dev/null +++ b/app.rb @@ -0,0 +1,78 @@ +require 'date' +require 'sinatra' +require "sinatra/reloader" +require_relative 'lib/database_connection' +require_relative 'lib/peep_repo' +require_relative 'lib/user_repo' + +DatabaseConnection.connect + +class Application < Sinatra::Base + + enable :sessions + + configure :development do + register Sinatra::Reloader + end + + get '/' do + peeps + end + + post '/' do + return erb(:login) if session[:user_id].nil? + repo = PeepRepo.new + peep = Peep.new + peep.message = params['peep'] + peep.time = DateTime.now + peep.user_account_id = 1 + repo.create(peep) + peeps + end + + get '/signup' do + return erb(:signup) + end + + post '/signup' do + repo = UserRepo.new + new_user = User.new + new_user.username = params[:username] + new_user.password = params[:password] + new_user.email_address = params[:email] + new_user.name = params[:name] + ult = repo.create(new_user) + return erb(:login) if ult == false + return erb(:signup) + end + + get '/login' do + return erb(:login) + end + + post '/login' do + username = params[:username] + password = params[:password] + user = UserRepo.new.find_by_username(username) + return erb(:login) unless user.password == password + session[:user_id] = user.id + peeps + end + + get '/logout' do + session[:user_id] = nil + peeps + end + + private + + def peeps + @session = session[:user_id] + @peeps = PeepRepo.new.all.reverse + return erb(:index) + end + + def new_user + + end +end diff --git a/config.ru b/config.ru new file mode 100644 index 0000000000..af14ef717e --- /dev/null +++ b/config.ru @@ -0,0 +1,2 @@ +require './app' +run Application diff --git a/lib/database_connection.rb b/lib/database_connection.rb new file mode 100644 index 0000000000..9d43719114 --- /dev/null +++ b/lib/database_connection.rb @@ -0,0 +1,39 @@ + +require 'pg' + +# This class is a thin "wrapper" around the +# PG library. We'll use it in our project to interact +# with the database using SQL. + +class DatabaseConnection + # This method connects to PostgreSQL using the + # PG gem. We connect to 127.0.0.1, and select + # the database name given in argument. + def self.connect + # If the environment variable (set by Render) + # is present, use this to open the connection. + if ENV['DATABASE_URL'] != nil + @connection = PG.connect(ENV['DATABASE_URL']) + return + end + + if ENV['ENV'] == 'test' + database_name = 'test_chitter' + else + database_name = 'chitter' + end + @connection = PG.connect({ host: '127.0.0.1', dbname: database_name }) + end + + # This method executes an SQL query + # on the database, providing some optional parameters + # (you will learn a bit later about when to provide these parameters). + def self.exec_params(query, params) + if @connection.nil? + raise 'DatabaseConnection.exec_params: Cannot run a SQL query as the connection to'\ + 'the database was never opened. Did you make sure to call first the method '\ + '`DatabaseConnection.connect` in your app.rb file (or in your tests spec_helper.rb)?' + end + @connection.exec_params(query, params) + end +end diff --git a/lib/peep.rb b/lib/peep.rb new file mode 100644 index 0000000000..d9253ceb26 --- /dev/null +++ b/lib/peep.rb @@ -0,0 +1,3 @@ +class Peep + attr_accessor :id, :message, :time, :user_account_id, :username, :name +end diff --git a/lib/peep_repo.rb b/lib/peep_repo.rb new file mode 100644 index 0000000000..1174566792 --- /dev/null +++ b/lib/peep_repo.rb @@ -0,0 +1,52 @@ +require_relative 'peep' +require_relative 'database_connection' + +class PeepRepo + def initialize + @peeps = [] + end + + def all + sql = 'SELECT posts.*, user_accounts.username, user_accounts.name + FROM posts + INNER JOIN user_accounts + ON posts.user_account_id = user_accounts.id;' + result = DatabaseConnection.exec_params(sql, []) + result.each do |record| + @peeps << new_peep(record) + end + @peeps + end + + def find(id) + sql = 'SELECT * FROM posts WHERE id = $1;' + params = [id] + result = DatabaseConnection.exec_params(sql, params) + record = result[0] + peep = Peep.new + peep.id = record['id'] + peep.message = record['message'] + peep.time = record['time'] + peep.user_account_id = record['user_account_id'] + peep + end + + def create(peep) + sql = 'INSERT INTO posts (message, time, user_account_id) VALUES ($1, $2, $3);' + params = [peep.message, peep.time, peep.user_account_id] + DatabaseConnection.exec_params(sql, params) + end + + private + + def new_peep(record) + peep = Peep.new + peep.id = record['id'] + peep.message = record['message'] + peep.time = record['time'] + peep.user_account_id = record['user_account_id'] + peep.username = record['username'] + peep.name = record['name'] + peep + end +end diff --git a/lib/user.rb b/lib/user.rb new file mode 100644 index 0000000000..2fb32556a8 --- /dev/null +++ b/lib/user.rb @@ -0,0 +1,3 @@ +class User + attr_accessor :id, :email_address, :username, :name, :password +end diff --git a/lib/user_repo.rb b/lib/user_repo.rb new file mode 100644 index 0000000000..95103cfee4 --- /dev/null +++ b/lib/user_repo.rb @@ -0,0 +1,55 @@ +require_relative 'user' +require_relative 'database_connection' + +class UserRepo + def initialize + @users = [] + end + + def all + sql = 'SELECT * + FROM user_accounts;' + result = DatabaseConnection.exec_params(sql, []) + result.each do |record| + @users << new_user(record) + end + @users + end + + def create(user) + return true if check_unique(user.email_address, user.username) + sql = 'INSERT INTO user_accounts (email_address, username, name, password) + VALUES ($1, $2, $3, $4);' + params = [user.email_address, user.username, user.name, user.password] + DatabaseConnection.exec_params(sql, params) + return false + end + + def check_unique(email_address, username) + sql = 'SELECT email_address, username + FROM user_accounts + WHERE email_address = $1 OR username = $2;' + params = [email_address, username] + result = DatabaseConnection.exec_params(sql, params) + return !result.first.nil? + end + + def find_by_username(username) + sql = 'SELECT * FROM user_accounts WHERE username = $1;' + params = [username] + result = DatabaseConnection.exec_params(sql, params) + return new_user(result[0]) + end + + private + + def new_user(record) + user = User.new + user.id = record['id'] + user.email_address = record['email_address'] + user.username = record['username'] + user.name = record['name'] + user.password = record['password'] + user + end +end diff --git a/spec/integration/app_sinatra_spec.rb b/spec/integration/app_sinatra_spec.rb new file mode 100644 index 0000000000..2b08008faf --- /dev/null +++ b/spec/integration/app_sinatra_spec.rb @@ -0,0 +1,83 @@ +require "spec_helper" +require "rack/test" +require_relative '../../app' + +describe Application do + include Rack::Test::Methods + + let(:app) { Application.new } + + before(:each) do + reset_tables + end + + context 'GET /' do + it 'shows peeps already made' do + response = get('/') + expect(response.status).to eq 200 + expect(response.body).to include('

Shrek @fionalover420            2008-11-11 13:23:44

') + end + end + + context 'POST /' do + it 'redirects you to login page if you try to post' do + response = post('/', peep: 'peep test') + expect(response.status).to eq 200 + expect(response.body).to include("Username:") + end + + it 'posts a peep when logged in' do + response = post('/login', username: 'fionalover420', password: 'I_Love_Fiona_69') + response = post('/', peep: 'peep test') + expect(response.status).to eq 200 + expect(response.body).to include('peep test') + expect(response.body).to include('
') + end + end + + context 'GET /login' do + it 'logs the user in with the correct username' do + response = get('/login', username: 'fionalover420', password: 'I_Love_Fiona_69') + expect(response.status).to eq 200 + expect(response.body).to include("Username:") + end + end + + context 'POST /login' do + it 'returns the login page if the password is incorrect' do + response = post('/login', username: 'fionalover420', password: 'U_Love_Fiona_69') + expect(response.status).to eq 200 + expect(response.body).to include("Username:") + end + end + + context 'GET /logout' do + it 'returns the home page with a login button and logs the user out' do + response = get('/logout') + expect(response.status).to eq 200 + expect(response.body).to include('') + end + end + + context 'GET /signup' do + it 'returns the sign up page' do + response = get('/signup') + expect(response.status).to eq 200 + expect(response.body).to include("Email:
") + end + end + + context 'POST /signup' do + it 'returns the login page if signed up and theres no conflicts' do + response = post('/signup', username: 'singlemominyourarea', name: 'Fiona', password: 'newlysingle69', email: 'fiona@swamp.com') + expect(response.status).to eq 200 + expect(response.body).to include("") + end + + it 'returns the signup page if trying to signup with a taken email' do + response = post('/signup', username: 'singlemominyourarea', name: 'Fiona', password: 'newlysingle69', email: 'shrek@swamp.com') + expect(response.status).to eq 200 + expect(response.body).to include("") + end + end +end diff --git a/spec/peep_spec.rb b/spec/peep_spec.rb new file mode 100644 index 0000000000..e717d5c722 --- /dev/null +++ b/spec/peep_spec.rb @@ -0,0 +1,51 @@ +require 'peep_repo' + +describe PeepRepo do + before(:each) do + reset_tables + end + + context 'when using the method .all' do + it 'returns all the peeps in an array' do + repo = PeepRepo.new + result = repo.all + expect(result.length).to eq 1 + expect(result[0].id).to eq '1' + expect(result[0].message).to eq 'Fiona just shouted at me for not cleaning the outhouse' + expect(result[0].time).to eq '2008-11-11 13:23:44' + expect(result[0].user_account_id).to eq '1' + expect(result[0].username).to eq 'fionalover420' + expect(result[0].name).to eq 'Shrek' + end + end + + context 'when using the method .find' do + it 'returns the peep with id = 1' do + repo = PeepRepo.new + result = repo.find(1) + expect(result.id).to eq '1' + expect(result.message).to eq 'Fiona just shouted at me for not cleaning the outhouse' + expect(result.time).to eq '2008-11-11 13:23:44' + expect(result.user_account_id).to eq '1' + end + end + + context 'when using the method .create' do + it 'creates a peep that can be found in the database' do + repo = PeepRepo.new + new_peep = Peep.new + new_peep.message = 'Fiona is taking the kids :(' + new_peep.time = '2008-11-11 18:31:24' + new_peep.user_account_id = '1' + repo.create(new_peep) + result = repo.all + expect(result).to include( + have_attributes( + message: new_peep.message, + time: new_peep.time, + user_account_id: '1' + ) + ) + end + end +end diff --git a/spec/seeds/posts_seeds.sql b/spec/seeds/posts_seeds.sql new file mode 100644 index 0000000000..c000622d7b --- /dev/null +++ b/spec/seeds/posts_seeds.sql @@ -0,0 +1,17 @@ +DROP TABLE IF EXISTS posts; + +CREATE TABLE posts ( + id SERIAL PRIMARY KEY, + message text, + time TIMESTAMP, + user_account_id int, + constraint fk_user_account foreign key(user_account_id) + references user_account(id) + on delete cascade +); + + +TRUNCATE TABLE posts RESTART IDENTITY; + +INSERT INTO posts ("message", "time", "user_account_id") VALUES +('Fiona just shouted at me for not cleaning the outhouse', '2008-11-11 13:23:44', 1); \ No newline at end of file diff --git a/spec/seeds/test_seeds.sql b/spec/seeds/test_seeds.sql new file mode 100644 index 0000000000..7abb8cbfed --- /dev/null +++ b/spec/seeds/test_seeds.sql @@ -0,0 +1,28 @@ +DROP TABLE IF EXISTS user_accounts, posts; + +CREATE TABLE user_accounts ( + id SERIAL PRIMARY KEY, + email_address text, + username text, + name text, + password text +); + +CREATE TABLE posts ( + id SERIAL PRIMARY KEY, + message text, + time TIMESTAMP, + user_account_id int, + constraint fk_user_account foreign key(user_account_id) + references user_accounts(id) + on delete cascade +); + + +TRUNCATE TABLE user_accounts, posts RESTART IDENTITY; + +INSERT INTO user_accounts ("email_address", "username", "name", "password") VALUES +('shrek@swamp.com', 'fionalover420', 'Shrek', 'I_Love_Fiona_69'); + +INSERT INTO posts ("message", "time", "user_account_id") VALUES +('Fiona just shouted at me for not cleaning the outhouse', '2008-11-11 13:23:44', 1); \ No newline at end of file diff --git a/spec/seeds/user_seeds.sql b/spec/seeds/user_seeds.sql new file mode 100644 index 0000000000..8c70bc081a --- /dev/null +++ b/spec/seeds/user_seeds.sql @@ -0,0 +1,14 @@ +DROP TABLE IF EXISTS user_account; + +CREATE TABLE user_account ( + id SERIAL PRIMARY KEY, + email_address text, + username text, + name text, + password text +); + +TRUNCATE TABLE user_account RESTART IDENTITY; + +INSERT INTO user_account ("email_address", "username", "name", "password") VALUES +('shrek@swamp.com', 'fionalover420', 'Shrek', 'I_Love_Fiona_69'); \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 252747d899..a95f372e3e 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,16 @@ require 'simplecov' require 'simplecov-console' +require 'database_connection' + +ENV['ENV'] = 'test' + +DatabaseConnection.connect + +def reset_tables + seeds_sql = File.read('spec/seeds/test_seeds.sql') + connection = PG.connect({ host: '127.0.0.1', dbname: 'test_chitter' }) + connection.exec(seeds_sql) +end SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([ SimpleCov::Formatter::Console, diff --git a/spec/user_spec.rb b/spec/user_spec.rb new file mode 100644 index 0000000000..792ccf9947 --- /dev/null +++ b/spec/user_spec.rb @@ -0,0 +1,74 @@ +require 'user_repo' + +describe UserRepo do + before(:each) do + reset_tables + end + + context 'when using the method .all' do + it 'returns an array of all the users' do + repo = UserRepo.new + result = repo.all + expect(result.length).to eq 1 + expect(result[0].id).to eq '1' + expect(result[0].email_address).to eq 'shrek@swamp.com' + expect(result[0].username).to eq 'fionalover420' + expect(result[0].name).to eq 'Shrek' + expect(result[0].password).to eq 'I_Love_Fiona_69' + end + end + + context 'when using the method .create' do + it 'creates a user' do + repo = UserRepo.new + new_user = User.new + new_user.email_address = 'fiona@swamp.com' + new_user.username = 'singlemominyourarea' + new_user.name = 'Fiona' + new_user.password = 'newlysingle420blazed' + repo.create(new_user) + expect(repo.all).to include( + have_attributes( + email_address: new_user.email_address, + username: new_user.username, + name: new_user.name, + password: new_user.password + ) + ) + end + + it 'doesnt add a new user if the email address is already taken' do + repo = UserRepo.new + new_user = User.new + new_user.email_address = 'shrek@swamp.com' + new_user.username = 'singlefatherinyourarea' + new_user.name = 'Not Shrek' + new_user.password = 'pleasecomebackfiona21' + repo.create(new_user) + expect(repo.all.length).to eq 1 + end + + it 'doesnt add a new user if the username is already taken' do + repo = UserRepo.new + new_user = User.new + new_user.email_address = 'shrek2@swamp.com' + new_user.username = 'fionalover420' + new_user.name = 'Not Shrek' + new_user.password = 'pleasecomebackfiona21' + repo.create(new_user) + expect(repo.all.length).to eq 1 + end + end + + context 'when using the method .find_by_username' do + it 'returns the user information on the username given' do + repo = UserRepo.new + result = repo.find_by_username('fionalover420') + expect(result.id).to eq '1' + expect(result.email_address).to eq 'shrek@swamp.com' + expect(result.username).to eq 'fionalover420' + expect(result.name).to eq 'Shrek' + expect(result.password).to eq 'I_Love_Fiona_69' + end + end +end diff --git a/views/index.erb b/views/index.erb new file mode 100644 index 0000000000..bf0fcd8c4b --- /dev/null +++ b/views/index.erb @@ -0,0 +1,33 @@ + + + Chitter + + +<% if @session.nil? %> + + +
+<% else %> +
+ +
+<% end %> +
+ +
+





+
+ + +
+<% @peeps.each do |peep| %> +
+

<%= peep.name %> @<%= peep.username %>            <%= peep.time %>

+

<%= peep.message %>

+
+ <% end %> + + \ No newline at end of file diff --git a/views/login.erb b/views/login.erb new file mode 100644 index 0000000000..ff22eb5e9a --- /dev/null +++ b/views/login.erb @@ -0,0 +1,18 @@ + + + Chitter + + +
+ +
+





+
+ Username: + Password: + +
+ \ No newline at end of file diff --git a/views/signup.erb b/views/signup.erb new file mode 100644 index 0000000000..c845d471a2 --- /dev/null +++ b/views/signup.erb @@ -0,0 +1,20 @@ + + + Chitter + + +
+ +
+





+
+ Username:
+ Name:
+ Email:
+ Password:
+ +
+ \ No newline at end of file