diff --git a/Gemfile b/Gemfile index 99d8e519..cc0e0cc6 100644 --- a/Gemfile +++ b/Gemfile @@ -6,6 +6,8 @@ ruby '3.0.2' gem 'pg' gem 'sinatra' +gem 'webrick' + group :test do gem 'capybara' diff --git a/Gemfile.lock b/Gemfile.lock index 7d4eb449..4d11e03c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -79,6 +79,7 @@ GEM unicode-display_width (>= 1.1.1, < 3) tilt (2.0.10) unicode-display_width (2.0.0) + webrick (1.7.0) xpath (3.2.0) nokogiri (~> 1.8) @@ -93,6 +94,7 @@ DEPENDENCIES simplecov simplecov-console sinatra + webrick RUBY VERSION ruby 3.0.2p107 diff --git a/README.md b/README.md index f9638b66..91fac105 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,35 @@ -## Chitter Challenge - -* Challenge time: until the end of the day -* Feel free to use google, your notes, books etc but please work on your own -* Please raise a pull request when you start this challenge, and keep pushing updates as and when you make commits throughout the day -* There is _no expectation_ to finish all or any of the user stories, please use this time to reflect on where you feel you are with the skill and what may support your learning. -* If you get blocked, please reflect on what blocked you and any strategies you adopted that helped you make progress. +## CHITTER CHALLENGE We are going to write a small Twitter clone that will allow the users to post messages to a public stream. -## Set up +## SETTING UP THE DATABASE -To setup the database: +1. Connect to `psql` (you may need to install postgresql first by running `brew install postgres`) +2. Create the database using the psql command `CREATE DATABASE chitter` +3. Connect to the database using the pqsl command `\c chitter` +4. Run the query we have saved in the file **01_create_chitter_table.sql** +5. Run the query we have saved in the file **02_create_author_table.sql** +6. Run the query we have saved in the file **03_add_timestamp_to_peeps.sql** +7. Populate your table by running the queries saved in the file **04_insert_data_to_peeps.sql** and **05_insert_data_to_author.sql** -* Connect to psql -* Create the database using the psql command `CREATE DATABASE chitter;` -* Connect to the database using the psql command `\c chitter`; -* Run the query we have saved in the file 01_create_chitter_table.sql -* Populate your table with a row by running `INSERT INTO peeps (message) values ('This is a peep!');` +To check you have everything set up ok, please take a look at the peeps table and the author inside the chitter database. -To check you have everything set up ok, please take a look at the peeps table inside the chitter database. You should see one row in there. +## SETTING UP THE TEST DATABASE -To setup the test database: -* Connect to psql -* Create the database using the psql -command `CREATE DATABASE chitter_test;`; -* Connect to the database using the psql command `\c chitter_test` -* Run the query we have saved in the file 01_create_chitter_table.sql +1. Connect to `psql` +2. Create the database using the psql command `CREATE DATABASE chitter_test` +3. Connect to the database using the pqsl command `\c chitter_test` +4. Run the query we have saved in the file **01_create_chitter_table.sql** +5. Run the query we have saved in the file **02_create_author_table.sql** +6. Run the query we have saved in the file **03_add_timestamp_to_peeps.sql** -* `bundle install` -* `rspec` +## START THE APP -You should see 1 passing test. +- Run `bundle install` to add predefined package dependencies +- Run `rackup` and run the app on localhost 9292 +- Run `rspec` to test code -## User stories +## USER STORIES ``` As a Maker @@ -43,7 +40,7 @@ in a browser ``` As a Maker -So that I can let people know what I am doing +So that I can let people know what I am doing I want to post a message (peep) to chitter ``` @@ -52,6 +49,7 @@ As a Maker So that I can see when people are doing things I want to see the date the message was posted ``` + (Hint the database table will need to change to store the date too) ``` @@ -59,6 +57,7 @@ As a Maker So that I can easily see the latest peeps I want to see a list of peeps in reverse chronological order ``` + ``` As a Maker So that I can find relevant peeps diff --git a/app.rb b/app.rb index 2450fb92..0c5022f4 100644 --- a/app.rb +++ b/app.rb @@ -1,8 +1,36 @@ require 'sinatra/base' +require './lib/peep' + class Chitter < Sinatra::Base - get '/test' do - 'Test page' + + get '/' do + 'Chitter' + end + + get '/chitter' do + @peeps = Peep.all + erb :chitter + end + + get '/my_peeps' do + @peeps = Peep.own_peeps + erb :my_peeps + end + + get '/my_peeps/new_peep' do + erb :post_peep + end + + post '/my_peeps/create_peep' do + @peeps_posted = Peep.post(message: params[:message], author_id: params[:author_id]) + redirect ('/my_peeps') + end + + post '/chitter/search' do + @keyword = params[:filter] + @peeps_filtered = Peep.search(keyword: params[:filter]) + erb :filtered_results end run! if app_file == $0 diff --git a/db/migrations/01_create_chitter_table.sql b/db/migrations/01_create_chitter_table.sql index 6e077248..6a66f512 100644 --- a/db/migrations/01_create_chitter_table.sql +++ b/db/migrations/01_create_chitter_table.sql @@ -1 +1,5 @@ -CREATE TABLE peeps(id SERIAL PRIMARY KEY, message VARCHAR(60)); +CREATE TABLE peeps( + id SERIAL PRIMARY KEY, + message VARCHAR(60), + author_id +); \ No newline at end of file diff --git a/db/migrations/02_create_author_table.sql b/db/migrations/02_create_author_table.sql new file mode 100644 index 00000000..2b35b903 --- /dev/null +++ b/db/migrations/02_create_author_table.sql @@ -0,0 +1 @@ +CREATE TABLE author(id SERIAL PRIMARY KEY, name VARCHAR(60)); \ No newline at end of file diff --git a/db/migrations/03_add_timestamp_to_peeps.sql b/db/migrations/03_add_timestamp_to_peeps.sql new file mode 100644 index 00000000..788070c3 --- /dev/null +++ b/db/migrations/03_add_timestamp_to_peeps.sql @@ -0,0 +1,2 @@ +ALTER TABLE peeps +ADD created_at VARCHAR(60) DEFAULT to_char(NOW(), 'On dd-MM-yyyy at HH24:MI'); \ No newline at end of file diff --git a/db/migrations/04_insert_data_to_peeps.sql b/db/migrations/04_insert_data_to_peeps.sql new file mode 100644 index 00000000..898836f4 --- /dev/null +++ b/db/migrations/04_insert_data_to_peeps.sql @@ -0,0 +1,12 @@ +INSERT INTO peeps (message, author_id) +VALUES ('This is a peep', 2); +INSERT INTO peeps (message, author_id) +VALUES ('Hello world!', 1); +INSERT INTO peeps (message, author_id) +VALUES ('What a beautiful day today!', 1); +INSERT INTO peeps (message, author_id) +VALUES ('This Chitter thing is great you know!', 2); +INSERT INTO peeps (message, author_id) +VALUES ('This is a peep', 3); +INSERT INTO peeps (message, author_id) +VALUES ('This is another peep', 2); \ No newline at end of file diff --git a/db/migrations/05_insert_data_to_author.sql b/db/migrations/05_insert_data_to_author.sql new file mode 100644 index 00000000..366ef4c2 --- /dev/null +++ b/db/migrations/05_insert_data_to_author.sql @@ -0,0 +1,6 @@ +INSERT INTO author (id, name) +VALUES (1, 'Me'); +INSERT INTO author (id, name) +VALUES (2, 'The Real Donald Trump'); +INSERT INTO author (id, name) +VALUES (3, 'Joe Blogg'); \ No newline at end of file diff --git a/lib/database.rb b/lib/database.rb new file mode 100644 index 00000000..4ac8d010 --- /dev/null +++ b/lib/database.rb @@ -0,0 +1,14 @@ +class Database + + def self.setup(db_name) + @connection = PG.connect :dbname => db_name + end + + def self.current_connection + @connection + end + + def self.query(sql) + @connection.exec(sql) + end +end \ No newline at end of file diff --git a/lib/peep.rb b/lib/peep.rb new file mode 100644 index 00000000..8c53e62f --- /dev/null +++ b/lib/peep.rb @@ -0,0 +1,67 @@ +require 'pg' +require './lib/database' + +class Peep + + attr_reader :id, :message, :name, :created_at + + def initialize(id:, message:, name:, created_at:) + @id = id + @message = message + @name = name + @created_at = created_at + end + + def self.all + connection = select_db + result = connection.exec( + "SELECT peeps.id, peeps.message, author.name, peeps.created_at + FROM peeps JOIN author ON peeps.author_id = author.id + WHERE author.id != 1 + ORDER BY peeps.created_at DESC;") + result.map { |peep| + Peep.new(id: peep['id'], message: peep['message'], name: peep['name'], created_at: peep['created_at']) + } + end + + def self.own_peeps + connection = select_db + result = connection.exec(" + SELECT peeps.id, peeps.message, author.name, peeps.created_at + FROM peeps JOIN author ON peeps.author_id = author.id + WHERE author_id = 1 + ORDER BY peeps.created_at DESC;") + result.map { |peep| + Peep.new(id: peep['id'], message: peep['message'], name: peep['name'], created_at: peep['created_at']) + } + end + + def self.post(message:, author_id:) + connection = select_db + result = connection.exec_params( + "INSERT INTO peeps (message, author_id) + VALUES($1, $2) RETURNING id, message, author_id;", [message, 1]) + Peep.new(id: result[0]['id'], message: result[0]['message'], name: result[0]['name'], created_at: result[0]['created_at']) + end + + + def self.search(keyword:) + connection = select_db + result = connection.exec( + "SELECT peeps.id, peeps.message, author.name, peeps.created_at + FROM peeps JOIN author ON peeps.author_id = author.id + WHERE author.id != 1 AND peeps.message LIKE '%#{keyword}%' + ORDER BY peeps.created_at DESC;") + result.map { |peep| + Peep.new(id: peep['id'], message: peep['message'], name: peep['name'], created_at: peep['created_at']) + } + end + + def self.select_db + if ENV['ENVIRONMENT'] == 'test' + connection = PG.connect(dbname: 'chitter_test') + else + connection = PG.connect(dbname: 'chitter') + end + end +end \ No newline at end of file diff --git a/spec/database_helpers.rb b/spec/database_helpers.rb new file mode 100644 index 00000000..2bb1ec76 --- /dev/null +++ b/spec/database_helpers.rb @@ -0,0 +1,7 @@ +require 'pg' + +def persisted_data(id:) + connection = PG.connect(dbname: 'chitter_test') + result = connection.query("SELECT * FROM peeps WHERE id = #{id};") + result.first +end \ No newline at end of file diff --git a/spec/features/post_peeps_spec.rb b/spec/features/post_peeps_spec.rb new file mode 100644 index 00000000..dda43d64 --- /dev/null +++ b/spec/features/post_peeps_spec.rb @@ -0,0 +1,11 @@ +feature 'Posting a peep' do + scenario 'A user can post a peep' do + visit('/chitter') + click_button('My Peeps') + click_button('New Peep') + fill_in('message', with: 'I am posting for the first time!') + click_button('Post') + + expect(page).to have_content 'I am posting for the first time!' + end +end \ No newline at end of file diff --git a/spec/features/search_peeps_spec.rb b/spec/features/search_peeps_spec.rb new file mode 100644 index 00000000..eb1f0389 --- /dev/null +++ b/spec/features/search_peeps_spec.rb @@ -0,0 +1,18 @@ +feature "filter peeps" do + scenario "A user can search peeps by keywords" do + connection = PG.connect(dbname: 'chitter_test') + + # Add the test data + connection.exec("INSERT INTO peeps (message, author_id) VALUES ('Hello world!', 3);") + connection.exec("INSERT INTO peeps (message, author_id) VALUES('Hi Chitter! This is my first peep!', 2);") + connection.exec("INSERT INTO peeps (message, author_id) VALUES('This is a peep!', 2);") + + visit('/chitter') + fill_in('filter', with: 'peep') + click_button('Filter') + + expect(page).not_to have_content("Hello world!") + expect(page).to have_content("Hi Chitter! This is my first peep!") + expect(page).to have_content("This is a peep!") + end +end \ No newline at end of file diff --git a/spec/features/view_peeps_spec.rb b/spec/features/view_peeps_spec.rb new file mode 100644 index 00000000..196f216f --- /dev/null +++ b/spec/features/view_peeps_spec.rb @@ -0,0 +1,58 @@ +require './app' +require 'pg' + +RSpec.describe "Peeps" do + feature "view peeps" do + scenario "user can view all peeps in their browser" do + + connection = PG.connect(dbname: 'chitter_test') + + # Add the test data + connection.exec("INSERT INTO peeps (message, author_id) VALUES ('Hello world!', 3);") + connection.exec("INSERT INTO peeps (message, author_id) VALUES('Hi Chitter! This is my first peep!', 2);") + connection.exec("INSERT INTO peeps (message, author_id) VALUES('This is a peep!', 2);") + + visit('/chitter') + + expect(page).to have_content("Hello world!") + expect(page).to have_content("Hi Chitter! This is my first peep!") + expect(page).to have_content("This is a peep!") + expect(page).to have_content Time.now.strftime("On %d-%m-%Y at %H:%M") + end + end + + feature "view my own peeps" do + scenario "user can view their own peeps in their browser" do + Peep.post(message: "Cannot believe it is snowing in April", author_id: 1) + Peep.post(message: "Happy Friday everyone!", author_id: 1) + Peep.post(message: "Looking forward to the weekend!", author_id: 1) + + visit('/chitter') + click_button('My Peeps') + + expect(page).to have_content("Cannot believe it is snowing in April") + expect(page).to have_content("Looking forward to the weekend!") + expect(page).to have_content Time.now.strftime("On %d-%m-%Y at %H:%M") + end + end + + feature "back to all peeps from my peeps" do + scenario "user can go back to all peeps from their peeps" do + connection = PG.connect(dbname: 'chitter_test') + + # Add the test data + connection.exec("INSERT INTO peeps (message, author_id) VALUES ('Hello world!', 3);") + connection.exec("INSERT INTO peeps (message, author_id) VALUES('Hi Chitter! This is my first peep!', 2);") + connection.exec("INSERT INTO peeps (message, author_id) VALUES('This is a peep!', 2);") + + visit('/chitter') + click_button('My Peeps') + click_button('All Peeps') + + expect(page).to have_content("Hello world!") + expect(page).to have_content("Hi Chitter! This is my first peep!") + expect(page).to have_content("This is a peep!") + expect(page).to have_content Time.now.strftime("On %d-%m-%Y at %H:%M") + end + end +end \ No newline at end of file diff --git a/spec/peep_spec.rb b/spec/peep_spec.rb new file mode 100644 index 00000000..e4c85ae2 --- /dev/null +++ b/spec/peep_spec.rb @@ -0,0 +1,49 @@ +require 'peep' +require 'database_helpers' + +describe Peep do + describe '.all' do + it 'returns all peeps' do + connection = PG.connect(dbname: 'chitter_test') + + # Add the test data + connection.exec("INSERT INTO peeps (message, author_id) VALUES ('Hello world!', 3);") + connection.exec("INSERT INTO peeps (message, author_id) VALUES('Hi Chitter! This is my first peep!', 2);") + connection.exec("INSERT INTO peeps (message, author_id) VALUES('This is a peep!', 3);") + + peeps = Peep.all + + expect(peeps.length).to eq 3 + expect(peeps.first).to be_a Peep + expect(peeps.first.message).to eq 'Hello world!' + expect(peeps.first.name).to eq 'Joe Blogg' + expect(peeps.first.created_at).to eq Time.now.strftime("On %d-%m-%Y at %H:%M") + end + end + + describe '.own_peeps' do + it 'posts a peep and returns all my peeps' do + connection = PG.connect(dbname: 'chitter_test') + connection.exec("INSERT INTO peeps (message, author_id) VALUES ('Can''t believe it''s snowing in April!', 1);") + + peeps = Peep.own_peeps + + expect(peeps.first).to be_a Peep + expect(peeps.first.message).to eq 'Can\'t believe it\'s snowing in April!' + expect(peeps.first.name).to eq 'Laura Cab' + expect(peeps.first.created_at).to eq Time.now.strftime("On %d-%m-%Y at %H:%M") + end + end + + describe '.post' do + it 'creates a new peep' do + peep = Peep.post(message: 'Oh! It\'s raining today! Where is my umbrella?', author_id: 1) + persisted_data = persisted_data(id: peep.id) + + expect(peep).to be_a Peep + expect(peep.message).to eq 'Oh! It\'s raining today! Where is my umbrella?' + end + end +end + + \ No newline at end of file diff --git a/views/chitter.erb b/views/chitter.erb new file mode 100644 index 00000000..f2676f78 --- /dev/null +++ b/views/chitter.erb @@ -0,0 +1,130 @@ + + + + + + +