From 1c8360f69998daf7de2bbdb0e07f62e3f059e5b8 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Fri, 2 Jun 2023 17:03:19 +0100 Subject: [PATCH 01/46] Project setup --- Gemfile | 5 +++++ Gemfile.lock | 28 +++++++++++++++++++++++++++- app.rb | 0 config.ru | 0 4 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 app.rb create mode 100644 config.ru diff --git a/Gemfile b/Gemfile index b1a320395a..866f834e6c 100644 --- a/Gemfile +++ b/Gemfile @@ -11,3 +11,8 @@ end group :development, :test do gem 'rubocop', '1.20' end + +gem "sinatra", "~> 3.0" +gem "sinatra-contrib", "~> 3.0" +gem "webrick", "~> 1.8" +gem "rack-test", "~> 2.1" diff --git a/Gemfile.lock b/Gemfile.lock index 66064703c7..cd1f844df8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -5,9 +5,17 @@ GEM ast (2.4.2) diff-lcs (1.4.4) docile (1.4.0) + multi_json (1.15.0) + mustermann (3.0.0) + ruby2_keywords (~> 0.0.1) parallel (1.20.1) parser (3.0.2.0) ast (~> 2.4.1) + rack (2.2.7) + rack-protection (3.0.6) + rack + rack-test (2.1.0) + rack (>= 1.3) rainbow (3.0.0) regexp_parser (2.1.1) rexml (3.2.5) @@ -36,6 +44,7 @@ GEM rubocop-ast (1.11.0) parser (>= 3.0.1.1) ruby-progressbar (1.11.0) + ruby2_keywords (0.0.5) simplecov (0.21.2) docile (~> 1.1) simplecov-html (~> 0.11) @@ -46,21 +55,38 @@ GEM terminal-table simplecov-html (0.12.3) simplecov_json_formatter (0.1.3) + 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.1) unicode-display_width (>= 1.1.1, < 3) + tilt (2.1.0) unicode-display_width (2.0.0) + webrick (1.8.1) PLATFORMS ruby DEPENDENCIES + rack-test (~> 2.1) rspec rubocop (= 1.20) simplecov simplecov-console + sinatra (~> 3.0) + sinatra-contrib (~> 3.0) + webrick (~> 1.8) RUBY VERSION ruby 3.0.2p107 BUNDLED WITH - 2.2.26 + 2.4.13 diff --git a/app.rb b/app.rb new file mode 100644 index 0000000000..e69de29bb2 diff --git a/config.ru b/config.ru new file mode 100644 index 0000000000..e69de29bb2 From 0e5c8a55aa59113e33f43caf4fa936aae995bcea Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Fri, 2 Jun 2023 17:09:35 +0100 Subject: [PATCH 02/46] File setup --- spec/user_repository_spec.rb | 0 spec/user_spec.rb | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 spec/user_repository_spec.rb create mode 100644 spec/user_spec.rb diff --git a/spec/user_repository_spec.rb b/spec/user_repository_spec.rb new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/user_spec.rb b/spec/user_spec.rb new file mode 100644 index 0000000000..e69de29bb2 From 07b50b419b90b102e04715830a4a647e61687222 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Fri, 2 Jun 2023 17:17:04 +0100 Subject: [PATCH 03/46] add user class, add unit test, pass test --- lib/models/user.rb | 11 +++++++++++ spec/user_spec.rb | 13 +++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 lib/models/user.rb diff --git a/lib/models/user.rb b/lib/models/user.rb new file mode 100644 index 0000000000..be337a662a --- /dev/null +++ b/lib/models/user.rb @@ -0,0 +1,11 @@ +class User + attr_accessor :id, :email, :password, :name, :username + + def initialize(id:, email:, password:, name:, username:) + @id = id + @email = email + @password = password + @name = name + @username = username + end +end \ No newline at end of file diff --git a/spec/user_spec.rb b/spec/user_spec.rb index e69de29bb2..f73a187ba8 100644 --- a/spec/user_spec.rb +++ b/spec/user_spec.rb @@ -0,0 +1,13 @@ +require_relative '../lib/models/user' + +RSpec.describe User do + let(:user) { User.new(id: 1, email: 'test@example.com', password: 'password', name: 'John Doe', username: 'johndoe') } + + it 'has attributes' do + expect(user.id).to eq(1) + expect(user.email).to eq('test@example.com') + expect(user.password).to eq('password') + expect(user.name).to eq('John Doe') + expect(user.username).to eq('johndoe') + end +end From d02368cee3c989687f692d9c785a1f52c4fb6061 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Fri, 2 Jun 2023 17:46:15 +0100 Subject: [PATCH 04/46] created main database schema --- seeds/chitter_schema.sql | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 seeds/chitter_schema.sql diff --git a/seeds/chitter_schema.sql b/seeds/chitter_schema.sql new file mode 100644 index 0000000000..bbd4788a7e --- /dev/null +++ b/seeds/chitter_schema.sql @@ -0,0 +1,43 @@ +-- Create the users table +CREATE TABLE users ( + id SERIAL PRIMARY KEY, + email VARCHAR(255) UNIQUE NOT NULL, + password VARCHAR(255) NOT NULL, + name VARCHAR(255) NOT NULL, + username VARCHAR(255) UNIQUE NOT NULL +); + +-- Create the peeps table +CREATE TABLE peeps ( + id SERIAL PRIMARY KEY, + content TEXT NOT NULL, + timestamp TIMESTAMP NOT NULL DEFAULT NOW(), + user_id INTEGER, + FOREIGN KEY (user_id) REFERENCES users(id) +); + +-- Create the replies table +CREATE TABLE replies ( + id SERIAL PRIMARY KEY, + content TEXT NOT NULL, + timestamp TIMESTAMP NOT NULL DEFAULT NOW(), + user_id INTEGER, + peep_id INTEGER, + FOREIGN KEY (user_id) REFERENCES users(id), + FOREIGN KEY (peep_id) REFERENCES peeps(id) +); + +-- Create the tags table +CREATE TABLE tags ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL +); + +-- Create the peep_tags table +CREATE TABLE peep_tags ( + peep_id INTEGER, + tag_id INTEGER, + FOREIGN KEY (peep_id) REFERENCES peeps(id), + FOREIGN KEY (tag_id) REFERENCES tags(id), + PRIMARY KEY (peep_id, tag_id) +); From 560ab8103ab4bb1b94387abddd7ecfd6af00252d Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Fri, 2 Jun 2023 19:58:54 +0100 Subject: [PATCH 05/46] add database connection --- lib/database_connection.rb | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 lib/database_connection.rb diff --git a/lib/database_connection.rb b/lib/database_connection.rb new file mode 100644 index 0000000000..d223102bdc --- /dev/null +++ b/lib/database_connection.rb @@ -0,0 +1,21 @@ +require 'pg' + +class DatabaseConnection + def self.connect + if ENV['ENV'] == 'test' + database_name = 'chitter_test' + else + database_name = 'chitter' + end + @connection = PG.connect(host: '127.0.0.1', dbname: database_name) + end + + 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 the method ' \ + '`DatabaseConnection.connect`?' + end + @connection.exec_params(query, params) + end +end From 393264645ef5aee0c3ac76387522fa384368b6f4 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Fri, 2 Jun 2023 19:59:31 +0100 Subject: [PATCH 06/46] add test sql seed --- seeds/chitter_testing_seed.sql | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 seeds/chitter_testing_seed.sql diff --git a/seeds/chitter_testing_seed.sql b/seeds/chitter_testing_seed.sql new file mode 100644 index 0000000000..c8fd4bfc5a --- /dev/null +++ b/seeds/chitter_testing_seed.sql @@ -0,0 +1,38 @@ +-- Reset identity for all tables +TRUNCATE TABLE users, peeps, replies, tags, peep_tags RESTART IDENTITY; + +-- Insert test data into the users table +INSERT INTO users (email, password, name, username) +VALUES + ('testuser1@example.com', 'password123', 'Test User 1', 'testuser1'), + ('testuser2@example.com', 'password456', 'Test User 2', 'testuser2'); + +-- Insert test data into the peeps table +INSERT INTO peeps (content, timestamp, user_id) +VALUES + ('Hello, this is my first peep!', NOW(), 1), + ('I love Chitter!', NOW(), 2), + ('Having a great day!', NOW(), 1); + +-- Insert test data into the replies table +INSERT INTO replies (content, timestamp, user_id, peep_id) +VALUES + ('That sounds awesome!', NOW(), 2, 1), + ('Me too!', NOW(), 1, 2), + ('Glad to hear that!', NOW(), 2, 3); + +-- Insert test data into the tags table +INSERT INTO tags (name) +VALUES + ('technology'), + ('coding'), + ('fun'); + +-- Insert test data into the peep_tags table +INSERT INTO peep_tags (peep_id, tag_id) +VALUES + (1, 1), + (1, 2), + (2, 3), + (3, 2), + (3, 3); From c959162a1c0d608747b15a3e0e35bf8a1c5790ea Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Fri, 2 Jun 2023 20:06:59 +0100 Subject: [PATCH 07/46] initital code structure for model and repository classes --- designs/main_design.md | 0 lib/models/peep.rb | 10 ++++++++++ lib/models/peep_tag.rb | 8 ++++++++ lib/models/reply.rb | 11 +++++++++++ lib/models/tag.rb | 8 ++++++++ lib/repositories/peep_repository.rb | 15 +++++++++++++++ lib/repositories/peep_tag_repository.rb | 15 +++++++++++++++ lib/repositories/reply_repository.rb | 11 +++++++++++ lib/repositories/tag_repository.rb | 11 +++++++++++ lib/repositories/user_repository.rb | 15 +++++++++++++++ 10 files changed, 104 insertions(+) create mode 100644 designs/main_design.md create mode 100644 lib/models/peep.rb create mode 100644 lib/models/peep_tag.rb create mode 100644 lib/models/reply.rb create mode 100644 lib/models/tag.rb create mode 100644 lib/repositories/peep_repository.rb create mode 100644 lib/repositories/peep_tag_repository.rb create mode 100644 lib/repositories/reply_repository.rb create mode 100644 lib/repositories/tag_repository.rb create mode 100644 lib/repositories/user_repository.rb diff --git a/designs/main_design.md b/designs/main_design.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lib/models/peep.rb b/lib/models/peep.rb new file mode 100644 index 0000000000..6fab548eb9 --- /dev/null +++ b/lib/models/peep.rb @@ -0,0 +1,10 @@ +class Peep + attr_accessor :id, :content, :timestamp, :user_id + + def initialize(id:, content:, timestamp:, user_id:) + @id = id + @content = content + @timestamp = timestamp + @user_id = user_id + end +end \ No newline at end of file diff --git a/lib/models/peep_tag.rb b/lib/models/peep_tag.rb new file mode 100644 index 0000000000..e29797e696 --- /dev/null +++ b/lib/models/peep_tag.rb @@ -0,0 +1,8 @@ +class PeepTag + attr_accessor :peep_id, :tag_id + + def initialize(peep_id:, tag_id:) + @peep_id = peep_id + @tag_id = tag_id + end +end \ No newline at end of file diff --git a/lib/models/reply.rb b/lib/models/reply.rb new file mode 100644 index 0000000000..bb28614d29 --- /dev/null +++ b/lib/models/reply.rb @@ -0,0 +1,11 @@ +class Reply + attr_accessor :id, :content, :timestamp, :user_id, :peep_id + + def initialize(id:, content:, timestamp:, user_id:, peep_id:) + @id = id + @content = content + @timestamp = timestamp + @user_id = user_id + @peep_id = peep_id + end +end \ No newline at end of file diff --git a/lib/models/tag.rb b/lib/models/tag.rb new file mode 100644 index 0000000000..7d49d6630d --- /dev/null +++ b/lib/models/tag.rb @@ -0,0 +1,8 @@ +class Tag + attr_accessor :id, :name + + def initialize(id:, name:) + @id = id + @name = name + end +end \ No newline at end of file diff --git a/lib/repositories/peep_repository.rb b/lib/repositories/peep_repository.rb new file mode 100644 index 0000000000..dccd7a3b4d --- /dev/null +++ b/lib/repositories/peep_repository.rb @@ -0,0 +1,15 @@ +require 'pg' + +class PeepRepository + def self.create(peep) + # Database insert query to create a new peep record + end + + def self.find_all + # Database query to retrieve all peeps in reverse chronological order + end + + def self.find_by_user(user_id) + # Database query to find all peeps by a specific user + end +end \ No newline at end of file diff --git a/lib/repositories/peep_tag_repository.rb b/lib/repositories/peep_tag_repository.rb new file mode 100644 index 0000000000..409c616caa --- /dev/null +++ b/lib/repositories/peep_tag_repository.rb @@ -0,0 +1,15 @@ +equire 'pg' + +class PeepTagRepository + def self.create(peep_tag) + # Database insert query to create a new peep_tag record + end + + def self.find_by_peep(peep_id) + # Database query to find all tags for a specific peep + end + + def self.find_by_tag(tag_id) + # Database query to find all peeps for a specific tag + end +end \ No newline at end of file diff --git a/lib/repositories/reply_repository.rb b/lib/repositories/reply_repository.rb new file mode 100644 index 0000000000..b017cb328d --- /dev/null +++ b/lib/repositories/reply_repository.rb @@ -0,0 +1,11 @@ +require 'pg' + +class ReplyRepository + def self.create(reply) + # Database insert query to create a new reply record + end + + def self.find_by_peep(peep_id) + # Database query to find all replies for a specific peep + end +end \ No newline at end of file diff --git a/lib/repositories/tag_repository.rb b/lib/repositories/tag_repository.rb new file mode 100644 index 0000000000..053f9f91c8 --- /dev/null +++ b/lib/repositories/tag_repository.rb @@ -0,0 +1,11 @@ +require 'pg' + +class TagRepository + def self.create(tag) + # Database insert query to create a new tag record + end + + def self.find_by_name(name) + # Database query to find a tag by name + end +end \ No newline at end of file diff --git a/lib/repositories/user_repository.rb b/lib/repositories/user_repository.rb new file mode 100644 index 0000000000..561a8ebc4e --- /dev/null +++ b/lib/repositories/user_repository.rb @@ -0,0 +1,15 @@ +require 'pg' + +class UserRepository + def self.create(user) + # Database insert query to create a new user record + end + + def self.find_by_email(email) + # Database query to find a user by email + end + + def self.find_by_username(username) + # Database query to find a user by username + end +end \ No newline at end of file From 49f1eb84087ba4059298f5f5d4e14a2facd2c67b Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Fri, 2 Jun 2023 22:01:41 +0100 Subject: [PATCH 08/46] add method to reset tables before each test --- spec/database_helper.rb | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 spec/database_helper.rb diff --git a/spec/database_helper.rb b/spec/database_helper.rb new file mode 100644 index 0000000000..a7b4c02445 --- /dev/null +++ b/spec/database_helper.rb @@ -0,0 +1,7 @@ +require 'pg' + +def reset_chitter_table + seed_sql = File.read('seeds/chitter_testing_seed.sql') + connection = PG.connect({ host: '127.0.0.1', dbname: 'chitter_test' }) + connection.exec(seed_sql) +end From 2427c27772cd9518d218453327a12823c661de90 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Fri, 2 Jun 2023 22:52:12 +0100 Subject: [PATCH 09/46] updated the model class and test for it --- lib/models/user.rb | 10 +--------- spec/user_spec.rb | 8 ++++++-- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/lib/models/user.rb b/lib/models/user.rb index be337a662a..70da660f64 100644 --- a/lib/models/user.rb +++ b/lib/models/user.rb @@ -1,11 +1,3 @@ class User - attr_accessor :id, :email, :password, :name, :username - - def initialize(id:, email:, password:, name:, username:) - @id = id - @email = email - @password = password - @name = name - @username = username - end + attr_accessor :id, :name, :username, :email, :password end \ No newline at end of file diff --git a/spec/user_spec.rb b/spec/user_spec.rb index f73a187ba8..f87e45bf75 100644 --- a/spec/user_spec.rb +++ b/spec/user_spec.rb @@ -1,10 +1,14 @@ require_relative '../lib/models/user' RSpec.describe User do - let(:user) { User.new(id: 1, email: 'test@example.com', password: 'password', name: 'John Doe', username: 'johndoe') } + let(:user) { User.new } it 'has attributes' do - expect(user.id).to eq(1) + user.email = 'test@example.com' + user.password = 'password' + user.name = 'John Doe' + user.username ='johndoe' + expect(user.email).to eq('test@example.com') expect(user.password).to eq('password') expect(user.name).to eq('John Doe') From 9c7a8fe138a58c5c3d0467e7ec202744958d76d5 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Fri, 2 Jun 2023 22:56:52 +0100 Subject: [PATCH 10/46] updated spec helper --- spec/spec_helper.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 252747d899..a4057e981c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,10 @@ require 'simplecov' require 'simplecov-console' +require 'database_connection' + +ENV['ENV'] = 'test' + +DatabaseConnection.connect SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([ SimpleCov::Formatter::Console, @@ -14,4 +19,4 @@ puts "\e[33mHave you considered running rubocop? It will help you improve your code!\e[0m" puts "\e[33mTry it now! Just run: rubocop\e[0m" end -end +end \ No newline at end of file From 4e5655d08dc4692d567b837e7827c88ac190bc65 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Fri, 2 Jun 2023 22:57:37 +0100 Subject: [PATCH 11/46] add new methods --- lib/repositories/user_repository.rb | 60 +++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/lib/repositories/user_repository.rb b/lib/repositories/user_repository.rb index 561a8ebc4e..97cdb664e3 100644 --- a/lib/repositories/user_repository.rb +++ b/lib/repositories/user_repository.rb @@ -1,15 +1,61 @@ -require 'pg' +require_relative '../models/user' +require 'database_connection' class UserRepository - def self.create(user) - # Database insert query to create a new user record + def self.create(name, username, email, password) + query = "INSERT INTO users (name, username, email, password) VALUES ($1, $2, $3, $4);" + DatabaseConnection.exec_params(query, [name, username, email, password]) + + return nil end def self.find_by_email(email) - # Database query to find a user by email + query = "SELECT id, name, username, email, password FROM users WHERE email = $1;" + result = DatabaseConnection.exec_params(query, [email]) + + return find_helper(result) + end + + def self.authenticate(email, password) + query = "SELECT id, name, username, email, password FROM users WHERE email = $1 AND password = $2;" + result = DatabaseConnection.exec_params(query, [email, password]) + return nil if result.ntuples.zero? + + return find_helper(result) + end + + def self.all + users = [] + query = "SELECT id, name, username, email, password FROM users;" + result = DatabaseConnection.exec_params(query, []) + result.each do |inst| + users << all_helper(inst) + end + return users end - def self.find_by_username(username) - # Database query to find a user by username + private + + def self.all_helper(inst) + user = User.new + user.id = inst['id'].to_i + user.name = inst['name'] + user.username = inst['username'] + user.email = inst['email'] + user.password = inst['password'] + + return user + end + + def self.find_helper(result) + return nil if result.ntuples.zero? + user = User.new + user.id = result[0]['id'].to_i + user.name = result[0]['name'] + user.username = result[0]['username'] + user.email = result[0]['email'] + user.password = result[0]['password'] + + return user end -end \ No newline at end of file +end From f3fbeba3cf93285687db8846d40c5f820f5023cc Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Fri, 2 Jun 2023 22:57:56 +0100 Subject: [PATCH 12/46] add new tests, pass tests --- Gemfile | 2 + Gemfile.lock | 2 + spec/user_repository_spec.rb | 74 ++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+) diff --git a/Gemfile b/Gemfile index 866f834e6c..e036fbc07c 100644 --- a/Gemfile +++ b/Gemfile @@ -16,3 +16,5 @@ gem "sinatra", "~> 3.0" gem "sinatra-contrib", "~> 3.0" gem "webrick", "~> 1.8" gem "rack-test", "~> 2.1" + +gem "pg", "~> 1.5" diff --git a/Gemfile.lock b/Gemfile.lock index cd1f844df8..9f104bfa5b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -11,6 +11,7 @@ GEM parallel (1.20.1) parser (3.0.2.0) ast (~> 2.4.1) + pg (1.5.3) rack (2.2.7) rack-protection (3.0.6) rack @@ -76,6 +77,7 @@ PLATFORMS ruby DEPENDENCIES + pg (~> 1.5) rack-test (~> 2.1) rspec rubocop (= 1.20) diff --git a/spec/user_repository_spec.rb b/spec/user_repository_spec.rb index e69de29bb2..e6eeae9c50 100644 --- a/spec/user_repository_spec.rb +++ b/spec/user_repository_spec.rb @@ -0,0 +1,74 @@ +require_relative '../lib/repositories/user_repository' +require_relative 'database_helper' + +describe UserRepository do + before(:each) do + reset_chitter_table + end + + context '.create' do + it 'creates a new user in the database' do + UserRepository.create('John Doe', 'johndoe', 'johndoe@example.com', 'password123') + user = UserRepository.find_by_email('johndoe@example.com') + + expect(user.name).to eq('John Doe') + expect(user.username).to eq('johndoe') + expect(user.email).to eq('johndoe@example.com') + expect(user.password).to eq('password123') + end + end + + context '.find_by_email' do + it 'returns the user with the given email' do + user = UserRepository.find_by_email('testuser1@example.com') + + expect(user).not_to be_nil + expect(user.name).to eq('Test User 1') + expect(user.username).to eq('testuser1') + expect(user.email).to eq('testuser1@example.com') + expect(user.password).to eq('password123') + end + + it 'returns nil if no user found with the given email' do + user = UserRepository.find_by_email('nonexistent@example.com') + + expect(user).to be_nil + end + end + + context '.authenticate' do + it 'returns the user if the email and password match' do + user = UserRepository.authenticate('testuser2@example.com', 'password456') + + expect(user).not_to be_nil + expect(user.name).to eq('Test User 2') + expect(user.username).to eq('testuser2') + expect(user.email).to eq('testuser2@example.com') + expect(user.password).to eq('password456') + end + + it 'returns nil if the email and password do not match' do + user = UserRepository.authenticate('testuser2@example.com', 'incorrect_password') + + expect(user).to be(nil) + end + end + + context '.all' do + it 'returns all users from the database' do + users = UserRepository.all + + expect(users.length).to eq(2) + + expect(users[0].name).to eq('Test User 1') + expect(users[0].username).to eq('testuser1') + expect(users[0].email).to eq('testuser1@example.com') + expect(users[0].password).to eq('password123') + + expect(users[1].name).to eq('Test User 2') + expect(users[1].username).to eq('testuser2') + expect(users[1].email).to eq('testuser2@example.com') + expect(users[1].password).to eq('password456') + end + end +end \ No newline at end of file From a59835797ebf7f10b4544bc88d0a37ec0c3f5012 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sat, 3 Jun 2023 16:50:15 +0100 Subject: [PATCH 13/46] add new methods --- lib/repositories/peep_repository.rb | 78 ++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 7 deletions(-) diff --git a/lib/repositories/peep_repository.rb b/lib/repositories/peep_repository.rb index dccd7a3b4d..d3b3f064ee 100644 --- a/lib/repositories/peep_repository.rb +++ b/lib/repositories/peep_repository.rb @@ -1,15 +1,79 @@ -require 'pg' +require_relative '../models/peep' +require 'database_connection' class PeepRepository - def self.create(peep) - # Database insert query to create a new peep record + def self.create(content, timestamp, user_id) + query = "INSERT INTO peeps (content, timestamp, user_id) VALUES ($1, $2, $3);" + DatabaseConnection.exec_params(query, [content, timestamp, user_id]) + + return nil + end + + def self.find(peep_id) + query = "SELECT id, content, timestamp, user_id FROM peeps WHERE id = $1;" + result = DatabaseConnection.exec_params(query, [peep_id]) + + build_peep(result[0]) end - def self.find_all - # Database query to retrieve all peeps in reverse chronological order + def self.all + peeps = [] + query = "SELECT id, content, timestamp, user_id FROM peeps;" + result = DatabaseConnection.exec_params(query, []) + result.each do |inst| + peeps << build_peep(inst) + end + peeps + end + + def self.sort_by_timestamp(peeps) + peeps.sort_by! { |peep| peep.timestamp } end def self.find_by_user(user_id) - # Database query to find all peeps by a specific user + peeps = [] + query = "SELECT id, content, timestamp, user_id FROM peeps WHERE user_id = $1;" + result = DatabaseConnection.exec_params(query, [user_id]) + result.each do |inst| + peeps << build_peep(inst) + end + peeps + end + + def self.find_by_tag(tag) + peeps = [] + query = "SELECT peeps.id, peeps.content, peeps.timestamp, peeps.user_id FROM peeps + JOIN peep_tags ON peeps.id = peep_tags.peep_id + JOIN tags ON peep_tags.tag_id = tags.id + WHERE tags.name = $1;" + result = DatabaseConnection.exec_params(query, [tag]) + result.each do |inst| + peeps << build_peep(inst) + end + peeps + end + + def self.update(content, id) + query = "UPDATE peeps SET content = $1 WHERE id = $2;" + DatabaseConnection.exec_params(query, [content, id]) + end + + def self.delete(id) + query = "DELETE FROM peeps WHERE id = $1;" + DatabaseConnection.exec_params(query, [id]) + + return nil + end + + private + + def self.build_peep(inst) + return nil if inst.nil? + peep = Peep.new + peep.id = inst['id'].to_i + peep.content = inst['content'] + peep.timestamp = inst['timestamp'] + peep.user_id = inst['user_id'].to_i + return peep end -end \ No newline at end of file +end From c54dcb73acbe5fc75460770e2b285f4e4a7b13b1 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sat, 3 Jun 2023 16:50:44 +0100 Subject: [PATCH 14/46] add new tests, pass tests --- spec/peep_repository_spec.rb | 194 +++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 spec/peep_repository_spec.rb diff --git a/spec/peep_repository_spec.rb b/spec/peep_repository_spec.rb new file mode 100644 index 0000000000..83599b4782 --- /dev/null +++ b/spec/peep_repository_spec.rb @@ -0,0 +1,194 @@ +require_relative '../lib/repositories/peep_repository' +require_relative 'database_helper' + +RSpec.describe PeepRepository do + before(:each) do + reset_chitter_table + end + + context '.create' do + it 'creates a new peep in the database' do + content = 'This is a test peep' + timestamp = '2023-06-01 12:34:56' + user_id = 1 + + expect(DatabaseConnection).to receive(:exec_params) + .with( + 'INSERT INTO peeps (content, timestamp, user_id) VALUES ($1, $2, $3);', + [content, timestamp, user_id] + ) + + PeepRepository.create(content, timestamp, user_id) + end + end + + context '.find' do + it 'returns the peep with the given id' do + peep_id = 1 + query_result = [{ 'id' => 1, 'content' => 'Test Peep', 'timestamp' => '2023-06-01 12:34:56', 'user_id' => 1 }] + + expect(DatabaseConnection).to receive(:exec_params) + .with('SELECT id, content, timestamp, user_id FROM peeps WHERE id = $1;', [peep_id]) + .and_return(query_result) + + result = PeepRepository.find(peep_id) + + expect(result).to be_a(Peep) + expect(result.id).to eq(1) + expect(result.content).to eq('Test Peep') + expect(result.timestamp).to eq('2023-06-01 12:34:56') + expect(result.user_id).to eq(1) + end + + it 'returns nil if no peep found with the given id' do + peep_id = 5 + + expect(DatabaseConnection).to receive(:exec_params) + .with('SELECT id, content, timestamp, user_id FROM peeps WHERE id = $1;', [peep_id]) + .and_return([]) + + result = PeepRepository.find(peep_id) + + expect(result).to be_nil + end + end + + context '.all' do + it 'returns all peeps in the database' do + query_result = [ + { 'id' => 1, 'content' => 'Peep 1', 'timestamp' => '2023-06-01 12:34:56', 'user_id' => 1 }, + { 'id' => 2, 'content' => 'Peep 2', 'timestamp' => '2023-06-01 12:35:00', 'user_id' => 2 } + ] + + expect(DatabaseConnection).to receive(:exec_params) + .with('SELECT id, content, timestamp, user_id FROM peeps;', []) + .and_return(query_result) + + results = PeepRepository.all + + expect(results.length).to eq(2) + + expect(results[0]).to be_a(Peep) + expect(results[0].id).to eq(1) + expect(results[0].content).to eq('Peep 1') + expect(results[0].timestamp).to eq('2023-06-01 12:34:56') + expect(results[0].user_id).to eq(1) + + expect(results[1]).to be_a(Peep) + expect(results[1].id).to eq(2) + expect(results[1].content).to eq('Peep 2') + expect(results[1].timestamp).to eq('2023-06-01 12:35:00') + expect(results[1].user_id).to eq(2) + end + + it 'returns an empty array if no peeps found' do + expect(DatabaseConnection).to receive(:exec_params) + .with('SELECT id, content, timestamp, user_id FROM peeps;', []) + .and_return([]) + + results = PeepRepository.all + + expect(results).to eq([]) + end + end + + context '.sort_by_timestamp' do + it 'sorts peeps by timestamp in ascending order' do + peep1 = Peep.new + peep1.timestamp = '2023-06-01 12:35:00' + + peep2 = Peep.new + peep2.timestamp = '2023-06-01 12:34:56' + + peep3 = Peep.new + peep3.timestamp = '2023-06-01 12:36:00' + + peeps = [peep1, peep2, peep3] + + sorted_peeps = PeepRepository.sort_by_timestamp(peeps) + + expect(sorted_peeps).to eq([peep2, peep1, peep3]) + end + end + + context '.find_by_user' do + it 'returns peeps belonging to the specified user' do + + # Create some sample peeps belonging to user 1 + user_3 = UserRepository.create('John Doe', 'johndoe', 'johndoe@example.com', 'password123') + peep1 = PeepRepository.create('Peep 1', '2023-06-01 12:00:00', 3) + peep2 = PeepRepository.create('Peep 2', '2023-06-01 13:00:00', 3) + peep3 = PeepRepository.create('Peep 3', '2023-06-01 14:00:00', 2) + + # Retrieve peeps for user 1 + result = PeepRepository.find_by_user(3) + # Verify if only peeps belonging to user 1 are returned + expect(result).to be_an(Array) + expect(result.length).to eq(2) + expect(result[0].content).to eq('Peep 1') + expect(result[1].content).to eq('Peep 2') + expect(result).not_to include(peep3) + end + end + + context '.find_by_tag' do + xit 'returns peeps associated with the specified tag' do + tag = 'ruby' + + # Create some sample peeps associated with the 'ruby' tag + peep1 = PeepRepository.create('Peep 1', '2023-06-01 12:00:00', 1) + peep2 = PeepRepository.create('Peep 2', '2023-06-01 13:00:00', 1) + peep3 = PeepRepository.create('Peep 3', '2023-06-01 14:00:00', 2) + + # Associate peeps with the 'ruby' tag + TagRepository.create(peep1.id, tag) + TagRepository.create(peep2.id, tag) + + # Retrieve peeps associated with the 'ruby' tag + result = PeepRepository.find_by_tag(tag) + + # Verify if only peeps associated with the 'ruby' tag are returned + expect(result).to be_an(Array) + expect(result.length).to eq(2) + expect(result).to include(peep1, peep2) + expect(result).not_to include(peep3) + end + end + + context '.update' do + it 'updates the content of the specified peep' do + peep = Peep.new + peep.content = 'Original content' + peep.timestamp = '2023-06-01 12:00:00' + peep.user_id = 1 + + PeepRepository.create(peep.content, peep.timestamp, peep.user_id) + updated_content = 'Updated content' + + + # Update the content of the peep + PeepRepository.update(updated_content, PeepRepository.all.last.id) + + # Retrieve the updated peep + updated_peep = PeepRepository.find(PeepRepository.all.last.id) + + # Verify if the peep's content is updated + expect(updated_peep.content).to eq(updated_content) + end + end + + context '.delete' do + it 'deletes the specified peep' do + peep = PeepRepository.create('Peep to delete', '2023-06-01 12:00:00', 1) + + # Delete the peep + PeepRepository.delete(PeepRepository.all.last.id) + + # Try to find the deleted peep + deleted_peep = PeepRepository.find(PeepRepository.all.last.id) + + # Verify if the deleted peep is nil + expect(deleted_peep.content).not_to eq('Peep to delete') + end + end +end \ No newline at end of file From 85a12e7879cd476a69405842992aaae81c4c25aa Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sat, 3 Jun 2023 16:51:09 +0100 Subject: [PATCH 15/46] updated model class --- lib/models/peep.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/models/peep.rb b/lib/models/peep.rb index 6fab548eb9..bf076b5905 100644 --- a/lib/models/peep.rb +++ b/lib/models/peep.rb @@ -1,10 +1,4 @@ class Peep attr_accessor :id, :content, :timestamp, :user_id - def initialize(id:, content:, timestamp:, user_id:) - @id = id - @content = content - @timestamp = timestamp - @user_id = user_id - end end \ No newline at end of file From 1ec7b9c29651c1ddb6bb1a0e69bdb8e6293d051f Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sat, 3 Jun 2023 16:52:00 +0100 Subject: [PATCH 16/46] refactor --- spec/user_repository_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/user_repository_spec.rb b/spec/user_repository_spec.rb index e6eeae9c50..52c2457a95 100644 --- a/spec/user_repository_spec.rb +++ b/spec/user_repository_spec.rb @@ -1,7 +1,7 @@ require_relative '../lib/repositories/user_repository' require_relative 'database_helper' -describe UserRepository do +RSpec.describe UserRepository do before(:each) do reset_chitter_table end From 8240038144d1b34b219364a3b0d720fe00c59b8a Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sat, 3 Jun 2023 16:52:28 +0100 Subject: [PATCH 17/46] updated model class --- lib/models/tag.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/models/tag.rb b/lib/models/tag.rb index 7d49d6630d..fb6dd50388 100644 --- a/lib/models/tag.rb +++ b/lib/models/tag.rb @@ -1,8 +1,3 @@ class Tag attr_accessor :id, :name - - def initialize(id:, name:) - @id = id - @name = name - end end \ No newline at end of file From 4979cee1982f1c172f018fc3396203171e9f101c Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sat, 3 Jun 2023 17:21:14 +0100 Subject: [PATCH 18/46] add new methods --- lib/repositories/tag_repository.rb | 48 ++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/lib/repositories/tag_repository.rb b/lib/repositories/tag_repository.rb index 053f9f91c8..e2df6e6f13 100644 --- a/lib/repositories/tag_repository.rb +++ b/lib/repositories/tag_repository.rb @@ -1,11 +1,47 @@ -require 'pg' +require_relative '../models/tag' +require 'database_connection' class TagRepository - def self.create(tag) - # Database insert query to create a new tag record + def self.create(name) + query = "INSERT INTO tags (name) VALUES ($1) RETURNING id;" + DatabaseConnection.exec_params(query, [name]) + nil end - def self.find_by_name(name) - # Database query to find a tag by name + def self.find(tag_id) + query = "SELECT id, name FROM tags WHERE id = $1;" + result = DatabaseConnection.exec_params(query, [tag_id]) + + build_tag(result[0]) + end + + def self.all + tags = [] + query = "SELECT id, name FROM tags;" + result = DatabaseConnection.exec_params(query, []) + result.each do |tag| + tags << build_tag(tag) + end + tags + end + + def self.update(name, id) + query = "UPDATE tags SET name = $1 WHERE id = $2;" + DatabaseConnection.exec_params(query, [name, id]) + end + + def self.delete(id) + query = "DELETE FROM tags WHERE id = $1;" + DatabaseConnection.exec_params(query, [id]) + end + + private + + def self.build_tag(inst) + return nil if inst.nil? + tag = Tag.new + tag.id = inst['id'].to_i + tag.name = inst['name'] + tag end -end \ No newline at end of file +end From a6671f5a34b2a4f59ac4be00b631aa2f97a6ae45 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sat, 3 Jun 2023 17:21:48 +0100 Subject: [PATCH 19/46] add new tests, pass tests --- spec/tag_repository_spec.rb | 116 ++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 spec/tag_repository_spec.rb diff --git a/spec/tag_repository_spec.rb b/spec/tag_repository_spec.rb new file mode 100644 index 0000000000..3f09c67e06 --- /dev/null +++ b/spec/tag_repository_spec.rb @@ -0,0 +1,116 @@ +require_relative '../lib/repositories/tag_repository' +require_relative 'database_helper' + +RSpec.describe TagRepository do + before(:each) do + reset_chitter_table + end + + context '.create' do + it 'creates a new tag in the database' do + name = 'Test Tag' + + expect(DatabaseConnection).to receive(:exec_params) + .with('INSERT INTO tags (name) VALUES ($1) RETURNING id;', [name]) + + result = TagRepository.create(name) + + expect(result).to be_nil + end + end + + context '.find' do + it 'returns the tag with the given id' do + tag_id = 1 + query_result = [{ 'id' => 1, 'name' => 'Test Tag' }] + + expect(DatabaseConnection).to receive(:exec_params) + .with('SELECT id, name FROM tags WHERE id = $1;', [tag_id]) + .and_return(query_result) + + result = TagRepository.find(tag_id) + + expect(result).to be_a(Tag) + expect(result.id).to eq(1) + expect(result.name).to eq('Test Tag') + end + + it 'returns nil if no tag found with the given id' do + tag_id = 5 + + expect(DatabaseConnection).to receive(:exec_params) + .with('SELECT id, name FROM tags WHERE id = $1;', [tag_id]) + .and_return([]) + + result = TagRepository.find(tag_id) + + expect(result).to be_nil + end + end + + context '.all' do + it 'returns all tags from the database' do + query_result = [ + { 'id' => 1, 'name' => 'Tag 1' }, + { 'id' => 2, 'name' => 'Tag 2' }, + { 'id' => 3, 'name' => 'Tag 3' } + ] + + expect(DatabaseConnection).to receive(:exec_params) + .with('SELECT id, name FROM tags;', []) + .and_return(query_result) + + result = TagRepository.all + + expect(result).to be_an(Array) + expect(result.size).to eq(3) + + expect(result[0]).to be_a(Tag) + expect(result[0].id).to eq(1) + expect(result[0].name).to eq('Tag 1') + + expect(result[1]).to be_a(Tag) + expect(result[1].id).to eq(2) + expect(result[1].name).to eq('Tag 2') + + expect(result[2]).to be_a(Tag) + expect(result[2].id).to eq(3) + expect(result[2].name).to eq('Tag 3') + end + + it 'returns an empty array if no tags found in the database' do + expect(DatabaseConnection).to receive(:exec_params) + .with('SELECT id, name FROM tags;', []) + .and_return([]) + + result = TagRepository.all + + expect(result).to be_an(Array) + expect(result).to be_empty + end + end + + context '.update' do + it 'updates the name of the specified tag' do + tag_id = 1 + updated_name = 'Updated Tag' + + expect(DatabaseConnection).to receive(:exec_params) + .with('UPDATE tags SET name = $1 WHERE id = $2;', [updated_name, tag_id]) + + TagRepository.update(updated_name, tag_id) + end + end + + context '.delete' do + it 'deletes the specified tag' do + tag_id = 1 + + expect(DatabaseConnection).to receive(:exec_params) + .with('DELETE FROM tags WHERE id = $1;', [tag_id]) + + TagRepository.delete(tag_id) + end + end + +end From b54b2f968613b0e73603f0e2c0e4ee8d67776821 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sat, 3 Jun 2023 18:43:35 +0100 Subject: [PATCH 20/46] add new methods --- lib/repositories/peep_repository.rb | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/lib/repositories/peep_repository.rb b/lib/repositories/peep_repository.rb index d3b3f064ee..74a1ba29a0 100644 --- a/lib/repositories/peep_repository.rb +++ b/lib/repositories/peep_repository.rb @@ -40,19 +40,6 @@ def self.find_by_user(user_id) peeps end - def self.find_by_tag(tag) - peeps = [] - query = "SELECT peeps.id, peeps.content, peeps.timestamp, peeps.user_id FROM peeps - JOIN peep_tags ON peeps.id = peep_tags.peep_id - JOIN tags ON peep_tags.tag_id = tags.id - WHERE tags.name = $1;" - result = DatabaseConnection.exec_params(query, [tag]) - result.each do |inst| - peeps << build_peep(inst) - end - peeps - end - def self.update(content, id) query = "UPDATE peeps SET content = $1 WHERE id = $2;" DatabaseConnection.exec_params(query, [content, id]) From d4c794f33f410d31f8671f482b808cd8a717c5dd Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sat, 3 Jun 2023 18:44:24 +0100 Subject: [PATCH 21/46] add new methods, add new tests, pass tests --- lib/models/peep_tag.rb | 5 -- lib/repositories/peep_tag_repository.rb | 55 +++++++++++-- spec/peep_repository_spec.rb | 24 ------ spec/peep_tag_repository_spec.rb | 102 ++++++++++++++++++++++++ 4 files changed, 151 insertions(+), 35 deletions(-) create mode 100644 spec/peep_tag_repository_spec.rb diff --git a/lib/models/peep_tag.rb b/lib/models/peep_tag.rb index e29797e696..c9443cb731 100644 --- a/lib/models/peep_tag.rb +++ b/lib/models/peep_tag.rb @@ -1,8 +1,3 @@ class PeepTag attr_accessor :peep_id, :tag_id - - def initialize(peep_id:, tag_id:) - @peep_id = peep_id - @tag_id = tag_id - end end \ No newline at end of file diff --git a/lib/repositories/peep_tag_repository.rb b/lib/repositories/peep_tag_repository.rb index 409c616caa..cee782d8e8 100644 --- a/lib/repositories/peep_tag_repository.rb +++ b/lib/repositories/peep_tag_repository.rb @@ -1,15 +1,58 @@ -equire 'pg' +require_relative '../models/peep_tag' +require 'database_connection' +require_relative 'tag_repository' +require_relative 'peep_repository' class PeepTagRepository - def self.create(peep_tag) - # Database insert query to create a new peep_tag record + def self.create(peep_id, tag_id) + query = "INSERT INTO peep_tags (peep_id, tag_id) VALUES ($1, $2);" + DatabaseConnection.exec_params(query, [peep_id, tag_id]) end def self.find_by_peep(peep_id) - # Database query to find all tags for a specific peep + query = <<~SQL + SELECT pt.peep_id, pt.tag_id, t.name + FROM peep_tags pt + JOIN tags t ON pt.tag_id = t.id + WHERE pt.peep_id = $1; + SQL + + result = DatabaseConnection.exec_params(query, [peep_id]) + build_peep_tags(result) end def self.find_by_tag(tag_id) - # Database query to find all peeps for a specific tag + query = <<~SQL + SELECT pt.peep_id, pt.tag_id, t.name + FROM peep_tags pt + JOIN tags t ON pt.tag_id = t.id + WHERE pt.tag_id = $1; + SQL + + result = DatabaseConnection.exec_params(query, [tag_id]) + build_peep_tags(result) + end + + def self.delete(peep_id, tag_id) + query = "DELETE FROM peep_tags WHERE peep_id = $1 AND tag_id = $2;" + DatabaseConnection.exec_params(query, [peep_id, tag_id]) + end + + private + + def self.build_peep_tags(results) + peep_tags = [] + results.each do |result| + peep_id = result['peep_id'].to_i + tag_id = result['tag_id'].to_i + + peep_tag = PeepTag.new + peep_tag.peep_id = peep_id + peep_tag.tag_id = tag_id + + peep_tags << peep_tag + end + peep_tags end -end \ No newline at end of file + +end diff --git a/spec/peep_repository_spec.rb b/spec/peep_repository_spec.rb index 83599b4782..7e5e2bcd4f 100644 --- a/spec/peep_repository_spec.rb +++ b/spec/peep_repository_spec.rb @@ -131,30 +131,6 @@ end end - context '.find_by_tag' do - xit 'returns peeps associated with the specified tag' do - tag = 'ruby' - - # Create some sample peeps associated with the 'ruby' tag - peep1 = PeepRepository.create('Peep 1', '2023-06-01 12:00:00', 1) - peep2 = PeepRepository.create('Peep 2', '2023-06-01 13:00:00', 1) - peep3 = PeepRepository.create('Peep 3', '2023-06-01 14:00:00', 2) - - # Associate peeps with the 'ruby' tag - TagRepository.create(peep1.id, tag) - TagRepository.create(peep2.id, tag) - - # Retrieve peeps associated with the 'ruby' tag - result = PeepRepository.find_by_tag(tag) - - # Verify if only peeps associated with the 'ruby' tag are returned - expect(result).to be_an(Array) - expect(result.length).to eq(2) - expect(result).to include(peep1, peep2) - expect(result).not_to include(peep3) - end - end - context '.update' do it 'updates the content of the specified peep' do peep = Peep.new diff --git a/spec/peep_tag_repository_spec.rb b/spec/peep_tag_repository_spec.rb new file mode 100644 index 0000000000..b3e1a5af97 --- /dev/null +++ b/spec/peep_tag_repository_spec.rb @@ -0,0 +1,102 @@ +require_relative '../lib/repositories/peep_tag_repository' +require_relative 'database_helper' + +RSpec.describe PeepTagRepository do + before(:each) do + reset_chitter_table + end + + context '.create' do + it 'creates a new peep tag link in the database' do + peep_id = 1 + tag_id = 1 + + expect(DatabaseConnection).to receive(:exec_params) + .with('INSERT INTO peep_tags (peep_id, tag_id) VALUES ($1, $2);', [peep_id, tag_id]) + + PeepTagRepository.create(peep_id, tag_id) + end + end + + context '.find_by_peep' do + it 'returns an array of tags linked to the specified peep' do + peep_id = 1 + query_result = [{ 'peep_id' => 1, 'tag_id' => 1, 'name' => 'Tag1' }, { 'peep_id' => 1, 'tag_id' => 2, 'name' => 'Tag2' }] + + expect(DatabaseConnection).to receive(:exec_params) + .with("SELECT pt.peep_id, pt.tag_id, t.name\nFROM peep_tags pt\nJOIN tags t ON pt.tag_id = t.id\nWHERE pt.peep_id = $1;\n", [peep_id]) + .and_return(query_result) + + result = PeepTagRepository.find_by_peep(peep_id) + + expect(result).to be_an(Array) + expect(result.length).to eq(2) + expect(result[0]).to be_a(PeepTag) + expect(result[0].peep_id).to eq(1) + expect(result[0].tag_id).to eq(1) + expect(result[1]).to be_a(PeepTag) + expect(result[1].peep_id).to eq(1) + expect(result[1].tag_id).to eq(2) + end + + it 'returns an empty array if no tags are linked to the specified peep' do + peep_id = 5 + + expect(DatabaseConnection).to receive(:exec_params) + .with("SELECT pt.peep_id, pt.tag_id, t.name\nFROM peep_tags pt\nJOIN tags t ON pt.tag_id = t.id\nWHERE pt.peep_id = $1;\n", [peep_id]) + .and_return([]) + + result = PeepTagRepository.find_by_peep(peep_id) + + expect(result).to be_an(Array) + expect(result).to be_empty + end + end + + context '.find_by_tag' do + it 'returns an array of peeps linked to the specified tag' do + tag_id = 1 + query_result = [{ 'peep_id' => 1, 'tag_id' => 1, 'name' => 'Tag1' }, { 'peep_id' => 2, 'tag_id' => 1, 'name' => 'Tag1' }] + + expect(DatabaseConnection).to receive(:exec_params) + .with("SELECT pt.peep_id, pt.tag_id, t.name\nFROM peep_tags pt\nJOIN tags t ON pt.tag_id = t.id\nWHERE pt.tag_id = $1;\n", [tag_id]) + .and_return(query_result) + + result = PeepTagRepository.find_by_tag(tag_id) + + expect(result).to be_an(Array) + expect(result.length).to eq(2) + expect(result[0]).to be_a(PeepTag) + expect(result[0].peep_id).to eq(1) + expect(result[0].tag_id).to eq(1) + expect(result[1]).to be_a(PeepTag) + expect(result[1].peep_id).to eq(2) + expect(result[1].tag_id).to eq(1) + end + + it 'returns an empty array if no peeps are linked to the specified tag' do + tag_id = 5 + + expect(DatabaseConnection).to receive(:exec_params) + .with("SELECT pt.peep_id, pt.tag_id, t.name\nFROM peep_tags pt\nJOIN tags t ON pt.tag_id = t.id\nWHERE pt.tag_id = $1;\n", [tag_id]) + .and_return([]) + + result = PeepTagRepository.find_by_tag(tag_id) + + expect(result).to be_an(Array) + expect(result).to be_empty + end + end + + context '.delete' do + it 'deletes the peep tag link from the database' do + peep_id = 1 + tag_id = 1 + + expect(DatabaseConnection).to receive(:exec_params) + .with('DELETE FROM peep_tags WHERE peep_id = $1 AND tag_id = $2;', [peep_id, tag_id]) + + PeepTagRepository.delete(peep_id, tag_id) + end + end +end \ No newline at end of file From e5e45a6fa87c67e66eac1a8343f5cc738e555820 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sat, 3 Jun 2023 19:33:15 +0100 Subject: [PATCH 22/46] add new methods, add new tests, pass tests --- lib/repositories/reply_repository.rb | 52 +++++++++++-- spec/reply_repository_spec.rb | 111 +++++++++++++++++++++++++++ 2 files changed, 158 insertions(+), 5 deletions(-) create mode 100644 spec/reply_repository_spec.rb diff --git a/lib/repositories/reply_repository.rb b/lib/repositories/reply_repository.rb index b017cb328d..e3db92f9d1 100644 --- a/lib/repositories/reply_repository.rb +++ b/lib/repositories/reply_repository.rb @@ -1,11 +1,53 @@ -require 'pg' +require_relative '../models/reply' +require 'database_connection' class ReplyRepository - def self.create(reply) - # Database insert query to create a new reply record + def self.create(content, user_id, peep_id) + timestamp = Time.now.strftime('%Y-%m-%d %H:%M:%S') + query = "INSERT INTO replies (content, timestamp, user_id, peep_id) VALUES ($1, $2, $3, $4) RETURNING id;" + result = DatabaseConnection.exec_params(query, [content, timestamp, user_id, peep_id]) + id = result[0]['id'].to_i + Reply.new(id: id, content: content, timestamp: timestamp, user_id: user_id, peep_id: peep_id) + end + + def self.find_by_id(id) + query = "SELECT * FROM replies WHERE id = $1;" + result = DatabaseConnection.exec_params(query, [id]) + build_reply(result[0]) if result.any? + end + + def self.update(id, content) + query = "UPDATE replies SET content = $1 WHERE id = $2;" + DatabaseConnection.exec_params(query, [content, id]) + end + + def self.delete(id) + query = "DELETE FROM replies WHERE id = $1;" + DatabaseConnection.exec_params(query, [id]) end def self.find_by_peep(peep_id) - # Database query to find all replies for a specific peep + query = "SELECT * FROM replies WHERE peep_id = $1;" + result = DatabaseConnection.exec_params(query, [peep_id]) + build_replies(result) + end + + private + + def self.build_reply(result) + id = result['id'].to_i + content = result['content'] + timestamp = result['timestamp'] + user_id = result['user_id'].to_i + peep_id = result['peep_id'].to_i + Reply.new(id: id, content: content, timestamp: timestamp, user_id: user_id, peep_id: peep_id) + end + + def self.build_replies(results) + replies = [] + results.each do |result| + replies << build_reply(result) + end + replies end -end \ No newline at end of file +end diff --git a/spec/reply_repository_spec.rb b/spec/reply_repository_spec.rb new file mode 100644 index 0000000000..6419ecce90 --- /dev/null +++ b/spec/reply_repository_spec.rb @@ -0,0 +1,111 @@ +require_relative '../lib/repositories/reply_repository' +require_relative 'database_helper' + +RSpec.describe ReplyRepository do + before(:each) do + reset_chitter_table + end + + context '.create' do + it 'creates a new reply in the database' do + content = 'This is a reply.' + user_id = 1 + peep_id = 1 + + reply = ReplyRepository.create(content, user_id, peep_id) + + expect(reply).to be_a(Reply) + expect(reply.id).not_to be_nil + expect(reply.content).to eq(content) + expect(reply.user_id).to eq(user_id) + expect(reply.peep_id).to eq(peep_id) + end + end + + context '.find_by_id' do + context 'when reply with the given id exists' do + it 'returns the reply' do + content = 'This is a reply.' + user_id = 1 + peep_id = 1 + + created_reply = ReplyRepository.create(content, user_id, peep_id) + found_reply = ReplyRepository.find_by_id(created_reply.id) + + expect(found_reply).to be_a(Reply) + expect(found_reply.id).to eq(created_reply.id) + expect(found_reply.content).to eq(content) + expect(found_reply.user_id).to eq(user_id) + expect(found_reply.peep_id).to eq(peep_id) + end + end + + context 'when reply with the given id does not exist' do + it 'returns nil' do + reply = ReplyRepository.find_by_id(999) + + expect(reply).to be_nil + end + end + end + + context '.update' do + it 'updates the content of the reply' do + content = 'Initial content.' + updated_content = 'Updated content.' + user_id = 1 + peep_id = 1 + + reply = ReplyRepository.create(content, user_id, peep_id) + ReplyRepository.update(reply.id, updated_content) + + updated_reply = ReplyRepository.find_by_id(reply.id) + + expect(updated_reply).to be_a(Reply) + expect(updated_reply.id).to eq(reply.id) + expect(updated_reply.content).to eq(updated_content) + expect(updated_reply.user_id).to eq(user_id) + expect(updated_reply.peep_id).to eq(peep_id) + end + end + + context '.delete' do + it 'deletes the reply from the database' do + content = 'This is a reply.' + user_id = 1 + peep_id = 1 + + reply = ReplyRepository.create(content, user_id, peep_id) + ReplyRepository.delete(reply.id) + + deleted_reply = ReplyRepository.find_by_id(reply.id) + + expect(deleted_reply).to be_nil + end + end + + context '.find_by_peep' do + it 'returns all replies associated with the specified peep' do + peep_id = 2 + + reply1 = ReplyRepository.create('Reply 1', 1, peep_id) + reply2 = ReplyRepository.create('Reply 2', 2, peep_id) + + replies = ReplyRepository.find_by_peep(peep_id) + expect(replies).to be_an(Array) + expect(replies.length).to eq(3) + + expect(replies[1]).to be_a(Reply) + expect(replies[1].id).to eq(reply1.id) + expect(replies[1].content).to eq(reply1.content) + expect(replies[1].user_id).to eq(reply1.user_id) + expect(replies[1].peep_id).to eq(reply1.peep_id) + + expect(replies[2]).to be_a(Reply) + expect(replies[2].id).to eq(reply2.id) + expect(replies[2].content).to eq(reply2.content) + expect(replies[2].user_id).to eq(reply2.user_id) + expect(replies[2].peep_id).to eq(reply2.peep_id) + end + end +end From 1f67bea33a8a5857e8272a460768381e4d04ac21 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sat, 3 Jun 2023 19:40:41 +0100 Subject: [PATCH 23/46] now peep captures time when created, updated tests, pass tests --- lib/repositories/peep_repository.rb | 3 ++- spec/peep_repository_spec.rb | 25 ++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/repositories/peep_repository.rb b/lib/repositories/peep_repository.rb index 74a1ba29a0..cd30f97610 100644 --- a/lib/repositories/peep_repository.rb +++ b/lib/repositories/peep_repository.rb @@ -2,7 +2,8 @@ require 'database_connection' class PeepRepository - def self.create(content, timestamp, user_id) + def self.create(content, user_id) + timestamp = Time.now.strftime('%Y-%m-%d %H:%M:%S') query = "INSERT INTO peeps (content, timestamp, user_id) VALUES ($1, $2, $3);" DatabaseConnection.exec_params(query, [content, timestamp, user_id]) diff --git a/spec/peep_repository_spec.rb b/spec/peep_repository_spec.rb index 7e5e2bcd4f..3b34e5edc0 100644 --- a/spec/peep_repository_spec.rb +++ b/spec/peep_repository_spec.rb @@ -9,16 +9,16 @@ context '.create' do it 'creates a new peep in the database' do content = 'This is a test peep' - timestamp = '2023-06-01 12:34:56' user_id = 1 - expect(DatabaseConnection).to receive(:exec_params) - .with( - 'INSERT INTO peeps (content, timestamp, user_id) VALUES ($1, $2, $3);', - [content, timestamp, user_id] - ) + expect(DatabaseConnection).to receive(:exec_params) do |query, params| + expect(query).to include('INSERT INTO peeps (content, timestamp, user_id) VALUES') + expect(params[0]).to eq(content) + expect(params[1]).to be_a(String) # Check if the timestamp is a string + expect(params[2]).to eq(user_id) + end - PeepRepository.create(content, timestamp, user_id) + PeepRepository.create(content, user_id) end end @@ -116,9 +116,9 @@ # Create some sample peeps belonging to user 1 user_3 = UserRepository.create('John Doe', 'johndoe', 'johndoe@example.com', 'password123') - peep1 = PeepRepository.create('Peep 1', '2023-06-01 12:00:00', 3) - peep2 = PeepRepository.create('Peep 2', '2023-06-01 13:00:00', 3) - peep3 = PeepRepository.create('Peep 3', '2023-06-01 14:00:00', 2) + peep1 = PeepRepository.create('Peep 1', 3) + peep2 = PeepRepository.create('Peep 2', 3) + peep3 = PeepRepository.create('Peep 3', 2) # Retrieve peeps for user 1 result = PeepRepository.find_by_user(3) @@ -135,10 +135,9 @@ it 'updates the content of the specified peep' do peep = Peep.new peep.content = 'Original content' - peep.timestamp = '2023-06-01 12:00:00' peep.user_id = 1 - PeepRepository.create(peep.content, peep.timestamp, peep.user_id) + PeepRepository.create(peep.content, peep.user_id) updated_content = 'Updated content' @@ -155,7 +154,7 @@ context '.delete' do it 'deletes the specified peep' do - peep = PeepRepository.create('Peep to delete', '2023-06-01 12:00:00', 1) + peep = PeepRepository.create('Peep to delete', 1) # Delete the peep PeepRepository.delete(PeepRepository.all.last.id) From 9c31e45ee1c4f5399609111e86256310ce330438 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sun, 4 Jun 2023 14:01:36 +0100 Subject: [PATCH 24/46] add new erb files --- views/index.erb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 views/index.erb diff --git a/views/index.erb b/views/index.erb new file mode 100644 index 0000000000..0497012000 --- /dev/null +++ b/views/index.erb @@ -0,0 +1,22 @@ + + + + CyberTwitter + + + +
+ +
+

Welcome to CyberTwitter

+

Connect and share in a cyberpunk world

+
+ + +
+
+
+ + From eeed35d35954c5f0b8c945416fe0466012e187b5 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sun, 4 Jun 2023 14:03:26 +0100 Subject: [PATCH 25/46] updated methods --- lib/repositories/peep_repository.rb | 2 +- lib/repositories/peep_tag_repository.rb | 48 ++++++++++++++++++++++++- lib/repositories/reply_repository.rb | 2 +- lib/repositories/tag_repository.rb | 2 +- lib/repositories/user_repository.rb | 8 ++++- 5 files changed, 57 insertions(+), 5 deletions(-) diff --git a/lib/repositories/peep_repository.rb b/lib/repositories/peep_repository.rb index cd30f97610..f88bfe58ce 100644 --- a/lib/repositories/peep_repository.rb +++ b/lib/repositories/peep_repository.rb @@ -1,5 +1,5 @@ require_relative '../models/peep' -require 'database_connection' + class PeepRepository def self.create(content, user_id) diff --git a/lib/repositories/peep_tag_repository.rb b/lib/repositories/peep_tag_repository.rb index cee782d8e8..e6395e60cc 100644 --- a/lib/repositories/peep_tag_repository.rb +++ b/lib/repositories/peep_tag_repository.rb @@ -1,5 +1,4 @@ require_relative '../models/peep_tag' -require 'database_connection' require_relative 'tag_repository' require_relative 'peep_repository' @@ -21,6 +20,31 @@ def self.find_by_peep(peep_id) build_peep_tags(result) end + def self.find_peeps_with_tags + query = <<~SQL + SELECT p.id, p.content, p.timestamp, p.user_id, t.name + FROM peeps p + LEFT JOIN peep_tags pt ON p.id = pt.peep_id + LEFT JOIN tags t ON pt.tag_id = t.id; + SQL + + result = DatabaseConnection.exec(query) + build_peeps_with_tags(result) + end + + def self.find_by_user(user_id) + query = <<~SQL + SELECT pt.peep_id, pt.tag_id, t.name + FROM peep_tags pt + JOIN tags t ON pt.tag_id = t.id + JOIN peeps p ON pt.peep_id = p.id + WHERE p.user_id = $1; + SQL + + result = DatabaseConnection.exec_params(query, [user_id]) + build_peep_tags(result) + end + def self.find_by_tag(tag_id) query = <<~SQL SELECT pt.peep_id, pt.tag_id, t.name @@ -54,5 +78,27 @@ def self.build_peep_tags(results) end peep_tags end + + def self.build_peeps_with_tags(results) + peeps = [] + results.each do |result| + peep = Peep.new + peep.id = result['id'].to_i + peep.content = result['content'] + peep.timestamp = result['timestamp'] + peep.user_id = result['user_id'].to_i + peep.author = UserRepository.find(peep.user_id) + + tag_name = result['name'] + unless tag_name.nil? + tag = Tag.new + tag.name = tag_name + peep.tags << tag + end + + peeps << peep + end + peeps + end end diff --git a/lib/repositories/reply_repository.rb b/lib/repositories/reply_repository.rb index e3db92f9d1..e5b9e26f31 100644 --- a/lib/repositories/reply_repository.rb +++ b/lib/repositories/reply_repository.rb @@ -1,5 +1,5 @@ require_relative '../models/reply' -require 'database_connection' + class ReplyRepository def self.create(content, user_id, peep_id) diff --git a/lib/repositories/tag_repository.rb b/lib/repositories/tag_repository.rb index e2df6e6f13..1c55a2ea5c 100644 --- a/lib/repositories/tag_repository.rb +++ b/lib/repositories/tag_repository.rb @@ -1,5 +1,5 @@ require_relative '../models/tag' -require 'database_connection' + class TagRepository def self.create(name) diff --git a/lib/repositories/user_repository.rb b/lib/repositories/user_repository.rb index 97cdb664e3..80da328675 100644 --- a/lib/repositories/user_repository.rb +++ b/lib/repositories/user_repository.rb @@ -1,5 +1,4 @@ require_relative '../models/user' -require 'database_connection' class UserRepository def self.create(name, username, email, password) @@ -9,6 +8,13 @@ def self.create(name, username, email, password) return nil end + def self.find(id) + query = "SELECT id, name, username, email, password FROM users WHERE id = $1;" + result = DatabaseConnection.exec_params(query, [id]) + + return find_helper(result) + end + def self.find_by_email(email) query = "SELECT id, name, username, email, password FROM users WHERE email = $1;" result = DatabaseConnection.exec_params(query, [email]) From 835859f4ba59856fd44ba8ec0bf83612ada882c5 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sun, 4 Jun 2023 14:03:51 +0100 Subject: [PATCH 26/46] new method --- lib/models/peep.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/models/peep.rb b/lib/models/peep.rb index bf076b5905..b4878c6436 100644 --- a/lib/models/peep.rb +++ b/lib/models/peep.rb @@ -1,4 +1,11 @@ class Peep attr_accessor :id, :content, :timestamp, :user_id + def author + UserRepository.find(user_id) + end + + def tags + PeepTagRepository.find_by_peep(id) + end end \ No newline at end of file From 21374d1ff5fc07291844df1fc7a1cb53f8bb9209 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sun, 4 Jun 2023 14:04:44 +0100 Subject: [PATCH 27/46] add new route --- app.rb | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/app.rb b/app.rb index e69de29bb2..b6fe3eb711 100644 --- a/app.rb +++ b/app.rb @@ -0,0 +1,34 @@ +require 'sinatra' +require 'base64' +require 'sinatra/base' +require 'sinatra/reloader' +require 'securerandom' +require_relative 'lib/database_connection' +require_relative 'routes/users' +require_relative 'routes/feed' + +DatabaseConnection.connect + +class Application < Sinatra::Base + configure :development do + register Sinatra::Reloader + also_reload 'lib/repositories/user_repository' + also_reload 'lib/repositories/tag_repository' + also_reload 'lib/repositories/peep_tag_repository' + also_reload 'lib/repositories/peep_repository' + also_reload 'lib/repositories/reply_repository' + end + + configure do + enable :sessions + set :session_secret, "5cdde102f6f68294e1cff23f341aaaaf2d2725453eaccc8ebc239629e724fc53" + end + + use Users + use Feed + + get '/' do + erb :index + end + +end From d67fa94289002a6ca78e039897bdfe17307fcea4 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sun, 4 Jun 2023 14:05:02 +0100 Subject: [PATCH 28/46] config --- config.ru | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config.ru b/config.ru index e69de29bb2..b7ae19f7be 100644 --- a/config.ru +++ b/config.ru @@ -0,0 +1,2 @@ +require_relative 'app' +run Application \ No newline at end of file From 4e87c6fbe1eb0fe6b0d4b40b786663ffe25edac2 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sun, 4 Jun 2023 14:05:18 +0100 Subject: [PATCH 29/46] new tests --- spec/users_route_spec.rb | 114 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 spec/users_route_spec.rb diff --git a/spec/users_route_spec.rb b/spec/users_route_spec.rb new file mode 100644 index 0000000000..2823ff2b6b --- /dev/null +++ b/spec/users_route_spec.rb @@ -0,0 +1,114 @@ +require 'rspec' +require 'rack/test' +require_relative 'database_helper' +require_relative '../app' + +RSpec.describe 'Users' do + include Rack::Test::Methods + + def app + Users + end + + let(:user_params) do + { + name: 'John Doe', + username: 'johndoe', + email: 'john@example.com', + password: 'password' + } + end + + before(:each) do + reset_chitter_table + end + + describe 'POST /signup' do + it 'creates a new user and sets the session user_id' do + expect(UserRepository).to receive(:create).with( + user_params[:name], + user_params[:username], + user_params[:email], + user_params[:password] + ).and_return('user_id') + + post '/signup', user_params + + expect(last_response).to be_redirect + expect(last_response.location).to include('/profile') + expect(last_request.session[:user_id]).to eq('user_id') + end + end + + describe 'POST /login' do + let(:existing_user) { double('User', id: 'user_id') } + + before do + allow(UserRepository).to receive(:authenticate).and_return(existing_user) + end + + context 'with valid credentials' do + it 'sets the session user_id and redirects to profile' do + post '/login', email: user_params[:email], password: user_params[:password] + + expect(last_response).to be_redirect + expect(last_response.location).to include('/profile') + expect(last_request.session[:user_id]).to eq('user_id') + end + end + + context 'with invalid credentials' do + before do + allow(UserRepository).to receive(:authenticate).and_return(nil) + end + + it 'redirects to login page' do + post '/login', email: user_params[:email], password: user_params[:password] + + expect(last_response).to be_redirect + expect(last_response.location).to include('/login') + expect(last_request.session[:user_id]).to be_nil + end + end + end + + describe 'GET /profile' do + context 'when user is logged in' do + let(:user) { double('User', id: 'user_id') } + + before do + allow(UserRepository).to receive(:find).and_return(user) + allow(PeepRepository).to receive(:find_by_user).and_return([]) + get '/profile', {}, { 'rack.session' => { user_id: 'user_id' } } + end + + it 'renders the profile page' do + expect(last_response).to be_ok + expect(last_response.body).to include('Profile') + end + + it 'assigns the user and peeps to the view' do + expect(last_request.env['sinatra.template'].instance_variable_get(:@user)).to eq(user) + expect(last_request.env['sinatra.template'].instance_variable_get(:@peeps)).to eq([]) + end + end + + context 'when user is not logged in' do + it 'redirects to login page' do + get '/profile' + expect(last_response).to be_redirect + expect(last_response.location).to include('/login') + end + end + end + + describe 'GET /logout' do + it 'clears the session and redirects to login page' do + get '/logout', {}, { 'rack.session' => { user_id: 'user_id' } } + + expect(last_response).to be_redirect + expect(last_response.location).to include('/login') + expect(last_request.session[:user_id]).to be_nil + end + end +end \ No newline at end of file From 678ed6951580efc7d2c004a71c1cb11d3f18dd59 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sun, 4 Jun 2023 14:05:57 +0100 Subject: [PATCH 30/46] new erb view files --- routes/views/feed.erb | 72 ++++++++++++++++++++++++++++++++++++++++ routes/views/login.erb | 27 +++++++++++++++ routes/views/peep.erb | 37 +++++++++++++++++++++ routes/views/profile.erb | 27 +++++++++++++++ routes/views/reply.erb | 12 +++++++ routes/views/signup.erb | 35 +++++++++++++++++++ 6 files changed, 210 insertions(+) create mode 100644 routes/views/feed.erb create mode 100644 routes/views/login.erb create mode 100644 routes/views/peep.erb create mode 100644 routes/views/profile.erb create mode 100644 routes/views/reply.erb create mode 100644 routes/views/signup.erb diff --git a/routes/views/feed.erb b/routes/views/feed.erb new file mode 100644 index 0000000000..8691553ae6 --- /dev/null +++ b/routes/views/feed.erb @@ -0,0 +1,72 @@ + + + + + + Global Feed + + + +

Global Feed

+ + <% if logged_in? %> +
+

Create a Peep

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

<%= @user_repo.find(peep.user_id).name %>

+

@<%= @user_repo.find(peep.user_id).username %>

+
+
+

<%= peep.content %>

+
+
+

<%= peep.timestamp %>

+ <% @peep_tag_repo.find_by_peep(peep.id).each do |peep_tag| %> + <%= @tag_repo.find(peep_tag.tag_id).name %> + <% end %> +
+
+ <% @reply_repo.find_by_peep(peep.id).each do |reply| %> +
+
+

<%= @user_repo.find(reply.user_id).name %>

+

@<%= @user_repo.find(reply.user_id).username %>

+
+
+

<%= reply.content %>

+
+
+

<%= reply.timestamp %>

+
+
+ <% end %> +
+ <% if logged_in? %> +
+
+ + + +
+
+ <% end %> +
+ <% end %> + + Back to Profile + + diff --git a/routes/views/login.erb b/routes/views/login.erb new file mode 100644 index 0000000000..12333b3c03 --- /dev/null +++ b/routes/views/login.erb @@ -0,0 +1,27 @@ + + + + CyberTwitter - Log In + + + + + + diff --git a/routes/views/peep.erb b/routes/views/peep.erb new file mode 100644 index 0000000000..20d9dda069 --- /dev/null +++ b/routes/views/peep.erb @@ -0,0 +1,37 @@ +
+
+ + <%= @user_repo.find(peep.user_id).name %> + (@<%= @user_repo.find(peep.user_id).username %>) + + <%= peep.timestamp %> +
+
+ <%= peep.content %> +
+ <% tags = @peep_tag_repo.find_by_peep(peep.id) %> + <% unless tags.empty? %> +
+ Tags: + <% tags.each do |tag| %> + <%= @tag_repo.find(tag.tag_id).name %> + <% end %> +
+ <% end %> +
+
+ + + +
+
+ <% replies = @reply_repo.find_by_peep(peep.id) %> + <% unless replies.empty? %> +
+

Replies:

+ <% replies.each do |reply| %> + <%= erb :reply, locals: { reply: reply } %> + <% end %> +
+ <% end %> +
diff --git a/routes/views/profile.erb b/routes/views/profile.erb new file mode 100644 index 0000000000..a24f5c37a4 --- /dev/null +++ b/routes/views/profile.erb @@ -0,0 +1,27 @@ + + + + CyberTwitter - Profile + + + + <% if @user %> +
+

Welcome, <%= @user.username %>!

+

Your Peeps:

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

<%= peep.content %>

+

Posted by: <%= peep.user_id %>

+
+ <% end %> + Logout +
+ <% else %> +

You are not logged in.

+ Log in +   + Sign up + <% end %> + + diff --git a/routes/views/reply.erb b/routes/views/reply.erb new file mode 100644 index 0000000000..04d5e3e54e --- /dev/null +++ b/routes/views/reply.erb @@ -0,0 +1,12 @@ +
+
+ + <%= @user_repo.find(reply.user_id).name %> + (@<%= @user_repo.find(reply.user_id).username %>) + + <%= reply.timestamp %> +
+
+ <%= reply.content %> +
+
diff --git a/routes/views/signup.erb b/routes/views/signup.erb new file mode 100644 index 0000000000..b99970a8d9 --- /dev/null +++ b/routes/views/signup.erb @@ -0,0 +1,35 @@ + + + + CyberTwitter - Sign Up + + + + + + From 99f6e3dd5ecb3f33a9b4279d0040cb8532621dbd Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sun, 4 Jun 2023 14:06:24 +0100 Subject: [PATCH 31/46] working routes --- routes/users.rb | 66 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 routes/users.rb diff --git a/routes/users.rb b/routes/users.rb new file mode 100644 index 0000000000..6f300ea703 --- /dev/null +++ b/routes/users.rb @@ -0,0 +1,66 @@ +require 'sinatra/base' +require_relative '../lib/repositories/user_repository' +require_relative '../lib/repositories/peep_repository' + +class Users < Sinatra::Base + enable :sessions + set :session_secret, "5cdde102f6f68294e1cff23f341aaaaf2d2725453eaccc8ebc239629e724fc53" + + get '/signup' do + erb :signup + end + + post '/signup' do + name = params[:name] + username = params[:username] + email = params[:email] + password = params[:password] + + UserRepository.create(name, username, email, password) + + redirect '/login' + end + + get '/login' do + erb :login + end + + post '/login' do + email = params[:email] + password = params[:password] + + user = UserRepository.authenticate(email, password) + + if user + session[:user_id] = user.id + redirect '/profile' + else + redirect '/login' + end + end + + get '/profile' do + if logged_in? + @user = current_user + @peeps = PeepRepository.find_by_user(@user.id) + erb :profile + else + redirect '/login' + end + end + + get '/logout' do + session.clear + redirect '/login' + end + + private + + def logged_in? + !session[:user_id].nil? + end + + def current_user + UserRepository.find(session[:user_id]) + end +end From 5e03666148d2ddc520d8140d3817789177b66ba1 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sun, 4 Jun 2023 14:06:56 +0100 Subject: [PATCH 32/46] new routes --- routes/feed.rb | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 routes/feed.rb diff --git a/routes/feed.rb b/routes/feed.rb new file mode 100644 index 0000000000..f60e1f75a3 --- /dev/null +++ b/routes/feed.rb @@ -0,0 +1,61 @@ +require 'sinatra/base' +require_relative '../lib/repositories/user_repository' +require_relative '../lib/repositories/peep_repository' +require_relative '../lib/repositories/tag_repository' +require_relative '../lib/repositories/reply_repository' +require_relative '../lib/repositories/peep_tag_repository' + +class Feed < Sinatra::Base + enable :sessions + set :session_secret, "5cdde102f6f68294e1cff23f341aaaaf2d2725453eaccc8ebc239629e724fc53" + + get '/feed' do + if logged_in? + @peeps = PeepRepository.all + @sorted_peeps = PeepRepository.sort_by_timestamp(@peeps) + @user_repo = UserRepository + @tag_repo = TagRepository + @reply_repo = ReplyRepository + @peep_tag_repo = PeepTagRepository + erb :feed + else + redirect '/login' + end + end + + post '/reply' do + if logged_in? + content = params[:content] + user_id = session[:user_id] + peep_id = params[:peep_id].to_i + + ReplyRepository.create(content, user_id, peep_id) + redirect '/feed' + else + redirect '/login' + end + end + + post '/create_peep' do + if logged_in? + content = params[:content] + user_id = session[:user_id] + + PeepRepository.create(content, user_id) + + redirect '/feed' + else + redirect '/login' + end + end + + private + + def logged_in? + !session[:user_id].nil? + end + + def current_user + UserRepository.find(session[:user_id]) + end +end From bd8725f5bef59825ef96d021bd70616cefb34a47 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sun, 4 Jun 2023 14:07:29 +0100 Subject: [PATCH 33/46] refactor --- lib/database_connection.rb | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/lib/database_connection.rb b/lib/database_connection.rb index d223102bdc..943b24c0c8 100644 --- a/lib/database_connection.rb +++ b/lib/database_connection.rb @@ -1,20 +1,39 @@ +# file: lib/database_connection.rb + 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 = 'chitter_test' else database_name = 'chitter' end - @connection = PG.connect(host: '127.0.0.1', dbname: database_name) + @connection = PG.connect({ host: '127.0.0.1', dbname: database_name }) end - def self.exec_params(query, params = []) + # 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 the method ' \ - '`DatabaseConnection.connect`?' + 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 From c7b1cea78fddcf1ffa7fe594dd6b04eb29e4c086 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sun, 4 Jun 2023 18:18:41 +0100 Subject: [PATCH 34/46] add new route to see pop up window with info in it --- routes/feed.rb | 6 ++ routes/views/feed.erb | 162 ++++++++++++++++++++++++------------- routes/views/user_info.erb | 11 +++ 3 files changed, 123 insertions(+), 56 deletions(-) create mode 100644 routes/views/user_info.erb diff --git a/routes/feed.rb b/routes/feed.rb index f60e1f75a3..a5ba126e99 100644 --- a/routes/feed.rb +++ b/routes/feed.rb @@ -49,6 +49,12 @@ class Feed < Sinatra::Base end end + get '/user_info/:user_id' do + user_id = params[:user_id].to_i + user = UserRepository.find(user_id) + erb :user_info, locals: { user: user } + end + private def logged_in? diff --git a/routes/views/feed.erb b/routes/views/feed.erb index 8691553ae6..d1f21986af 100644 --- a/routes/views/feed.erb +++ b/routes/views/feed.erb @@ -1,71 +1,121 @@ - - Global Feed - + + + + -

Global Feed

- - <% if logged_in? %> -
-

Create a Peep

-
- - -
-
- <% else %> - - <% end %> +
+

Global Feed

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

<%= @user_repo.find(peep.user_id).name %>

-

@<%= @user_repo.find(peep.user_id).username %>

-
-
-

<%= peep.content %>

-
-
-

<%= peep.timestamp %>

- <% @peep_tag_repo.find_by_peep(peep.id).each do |peep_tag| %> - <%= @tag_repo.find(peep_tag.tag_id).name %> - <% end %> -
-
- <% @reply_repo.find_by_peep(peep.id).each do |reply| %> -
-
-

<%= @user_repo.find(reply.user_id).name %>

-

@<%= @user_repo.find(reply.user_id).username %>

-
-
-

<%= reply.content %>

-
-
-

<%= reply.timestamp %>

-
-
- <% end %> -
+
+
<% if logged_in? %> -
-
- - - +
+

Create a Peep

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

+

@<%= @user_repo.find(peep.user_id).username %>

+
+
+

<%= peep.content %>

+
+
+

<%= peep.timestamp %>

+ <% @peep_tag_repo.find_by_peep(peep.id).each do |peep_tag| %> + <%= @tag_repo.find(peep_tag.tag_id).name %> + <% end %> +
+
+ <% @reply_repo.find_by_peep(peep.id).each do |reply| %> +
+
+

+

@<%= @user_repo.find(reply.user_id).username %>

+
+
+

<%= reply.content %>

+
+
+

<%= reply.timestamp %>

+
+
+ <% end %> +
+ <% if logged_in? %> +
+
+ + + +
+
+ <% end %> +
<% end %>
- <% end %> +
+ +
+ +
Back to Profile diff --git a/routes/views/user_info.erb b/routes/views/user_info.erb new file mode 100644 index 0000000000..f603d9f987 --- /dev/null +++ b/routes/views/user_info.erb @@ -0,0 +1,11 @@ + + From 8fdd5b53e954d35c90b7f4037fccb13f9ae11b29 Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sun, 4 Jun 2023 18:19:12 +0100 Subject: [PATCH 35/46] updated design --- public/css/style.css | 97 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 public/css/style.css diff --git a/public/css/style.css b/public/css/style.css new file mode 100644 index 0000000000..13af46abea --- /dev/null +++ b/public/css/style.css @@ -0,0 +1,97 @@ +body { + font-family: 'Roboto', sans-serif; + font-size: 16px; + line-height: 1.5; + background-color: #0e0e0e; + color: #fff; +} + +header { + background-image: linear-gradient(90deg, #29abe0, #1e7ea8); + padding: 20px; + text-align: center; +} + +h1 { + font-size: 24px; + margin-bottom: 10px; +} + +main { + display: flex; + justify-content: center; + align-items: flex-start; + min-height: 100vh; +} + +.feed-container { + width: 800px; + margin: 20px; +} + +.peep { + background-color: #222; + border-radius: 10px; + padding: 20px; + margin-bottom: 20px; +} + +.author-info { + margin-bottom: 10px; +} + +.user-info { + cursor: pointer; + text-decoration: underline; +} + +.peep-content { + margin-bottom: 10px; +} + +.peep-meta { + display: flex; + align-items: center; + margin-bottom: 10px; +} + +.tag { + background-color: #1e7ea8; + color: #fff; + padding: 5px 10px; + border-radius: 5px; + margin-right: 10px; +} + +.replies { + margin-left: 40px; +} + +.reply { + background-color: #333; + border-radius: 10px; + padding: 10px; + margin-bottom: 10px; +} + +.reply-meta { + margin-top: 10px; +} + +.reply-form { + margin-top: 20px; +} + + +/* Additional Cyberpunk Styling */ +.signup-container { + box-shadow: 0 0 20px rgba(0, 0, 0, 0.4); +} + +.signup-form input { + box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); +} + +.signup-form input[type="submit"]:hover { + background-color: #0e5d80; +} From 6cb4212ae2f6586a52a1d2a10127f5fba5c4e92f Mon Sep 17 00:00:00 2001 From: Yevhen Shakhrai Date: Sun, 4 Jun 2023 18:19:52 +0100 Subject: [PATCH 36/46] solved missing path to the style.css --- routes/views/login.erb | 2 +- routes/views/profile.erb | 2 +- routes/views/signup.erb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/routes/views/login.erb b/routes/views/login.erb index 12333b3c03..1d59aa99c6 100644 --- a/routes/views/login.erb +++ b/routes/views/login.erb @@ -2,7 +2,7 @@ CyberTwitter - Log In - +