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..898aa12beb 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.12 diff --git a/README.md b/README.md index 465eda879b..192f99e7cf 100644 --- a/README.md +++ b/README.md @@ -1,123 +1,30 @@ -Chitter Challenge -================= +# chitter-challenge -* Feel free to use Google, your notes, books, etc. but work on your own -* If you refer to the solution of another coach or student, please put a link to that in your README -* If you have a partial solution, **still check in a partial solution** -* You must submit a pull request to this repo with your code by 10am Monday morning +A Twitter clone build as part of the Makers Academy development course -Challenge: -------- - -As usual please start by forking this repo. - -We are going to write a small Twitter clone that will allow the users to post messages to a public stream. - -Features: -------- +## How to use +```shell +bundle install +rackup ``` -STRAIGHT UP - -As a Maker -So that I can let people know what I am doing -I want to post a message (peep) to chitter - -As a maker -So that I can see what others are saying -I want to see all peeps in reverse chronological order - -As a Maker -So that I can better appreciate the context of a peep -I want to see the time at which it was made - -As a Maker -So that I can post messages on Chitter as me -I want to sign up for Chitter - -HARDER - -As a Maker -So that only I can post messages on Chitter as me -I want to log in to Chitter - -As a Maker -So that I can avoid others posting messages on Chitter as me -I want to log out of Chitter - -ADVANCED - -As a Maker -So that I can stay constantly tapped in to the shouty box of Chitter -I want to receive an email if I am tagged in a Peep -``` - -Technical Approach: ------ - -In the last two weeks, you integrated a database using the `pg` gem and Repository classes. You also implemented small web applications using Sinatra, RSpec, HTML and ERB views to make dynamic webpages. You can continue to use this approach when building Chitter Challenge. -You can refer to the [guidance on Modelling and Planning a web application](https://github.com/makersacademy/web-applications/blob/main/pills/modelling_and_planning_web_application.md), to help you in planning the different web pages you will need to implement this challenge. If you'd like to deploy your app to Heroku so other people can use it, [you can follow this guidance](https://github.com/makersacademy/web-applications/blob/main/html_challenges/07_deploying.md). +## Pending TODOs +- Login and Profile ID functionality +- Post Peeps -If you'd like more technical challenge now, try using an [Object Relational Mapper](https://en.wikipedia.org/wiki/Object-relational_mapping) as the database interface, instead of implementing your own Repository classes. -Some useful resources: -**Ruby Object Mapper** -- [ROM](https://rom-rb.org/) +## Built with +- Ruby +- Rspec +- Sinatra +- PostgreSQL +- Render -**ActiveRecord** -- [ActiveRecord ORM](https://guides.rubyonrails.org/active_record_basics.html) -- [Sinatra & ActiveRecord setup](https://learn.co/lessons/sinatra-activerecord-setup) +## Contributions -Notes on functionality: ------- +CSS Style sheet from [Caroline Evans](https://github.com/cvass1) -* You don't have to be logged in to see the peeps. -* Makers sign up to chitter with their email, password, name and a username (e.g. samm@makersacademy.com, password123, Sam Morgan, sjmog). -* The username and email are unique. -* Peeps (posts to chitter) have the name of the maker and their user handle. -* Your README should indicate the technologies used, and give instructions on how to install and run the tests. - -Bonus: ------ - -If you have time you can implement the following: - -* In order to start a conversation as a maker I want to reply to a peep from another maker. - -And/Or: - -* Work on the CSS to make it look good. - -Good luck and let the chitter begin! - -Code Review ------------ - -In code review we'll be hoping to see: - -* All tests passing -* High [Test coverage](https://github.com/makersacademy/course/blob/main/pills/test_coverage.md) (>95% is good) -* The code is elegant: every class has a clear responsibility, methods are short etc. - -Reviewers will potentially be using this [code review rubric](docs/review.md). Referring to this rubric in advance may make the challenge somewhat easier. You should be the judge of how much challenge you want at this moment. - -Notes on test coverage ----------------------- - -Please ensure you have the following **AT THE TOP** of your spec_helper.rb in order to have test coverage stats generated -on your pull request: - -```ruby -require 'simplecov' -require 'simplecov-console' - -SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([ - SimpleCov::Formatter::Console, - # Want a nice code coverage website? Uncomment this next line! - # SimpleCov::Formatter::HTMLFormatter -]) -SimpleCov.start -``` +## Contact +- [Jack Skates](mailto:skatesproduction@gmail.com) -You can see your test coverage when you run your tests. If you want this in a graphical form, uncomment the `HTMLFormatter` line and see what happens! diff --git a/app.rb b/app.rb new file mode 100644 index 0000000000..61827b746f --- /dev/null +++ b/app.rb @@ -0,0 +1,69 @@ +require 'sinatra/base' +require 'sinatra/reloader' +require 'date' +require 'bcrypt' +require_relative 'lib/database_connection' +require_relative 'lib/user_repository' +require_relative 'lib/peep_repository' + +DatabaseConnection.connect + +class Application < Sinatra::Base + enable :sessions + # This allows the app code to refresh + # without having to restart the server. + configure :development do + register Sinatra::Reloader + also_reload 'lib/user_repository' + also_reload 'lib/peep_repository' + end + + get '/' do + if session[:user_id] != nil + return redirect('/userpage') + end + peep_repo = PeepRepository.new + @users = UserRepository.new + @peeps = peep_repo.all.sort_by!{ |peep| peep.time }.reverse! + return erb(:index) + end + + post '/logged_in' do + end + + + get '/logged_in' do + end + + get '/peeps' do + peep_repo = PeepRepository.new + @users = UserRepository.new + @peeps = peep_repo.all.sort_by!{ |peep| peep.time }.reverse! + return erb(:peeps) + end + + get '/signup' do + return erb(:signup) + end + + post '/signup' do + + users = UserRepository.new + user = User.new + last_user = users.all.last + user_id = (last_user.id) - 1 + user.name = params[:name] + user.email = params[:email] + user.username = params[:username] + user.password = params[:password] + session[:user_id] = user.id + + users.create(user) + + user = users.find_by_username(user.username) + session[:user_id] = user.id + + redirect "/logged_in" + + end +end diff --git a/chitter_model_and_repository_design_recipe.md b/chitter_model_and_repository_design_recipe.md new file mode 100644 index 0000000000..1bed67b0ca --- /dev/null +++ b/chitter_model_and_repository_design_recipe.md @@ -0,0 +1,295 @@ +# Chitter Peeps & Users - Model and Repository Classes Design Recipe + + +## 1. Design and create the Table + + +``` +Table: peeps + +Columns: +id | content | time | user_id + +Table: users + +Columns: +id | name | email | username + +``` + +## 2. Create Test SQL seeds + +Your tests will depend on data stored in PostgreSQL to run. + +If seed data is provided (or you already created it), you can skip this step. + +```sql +-- (file: spec/seeds_chitter.sql) + +-- First, you'd need to truncate the table - this is so our table is emptied between each test run, +-- so we can start with a fresh state. +-- (RESTART IDENTITY resets the primary key) + +TRUNCATE TABLE peeps, users, peeps_users RESTART IDENTITY; + +-- Below this line there should only be `INSERT` statements. +-- Replace these statements with your own seed data. + +INSERT INTO users (name, email, username) VALUES ('Jack', 'jack@email.com', 'skates'); +INSERT INTO users (name, email, username) VALUES ('Dave', 'dave@email.com', 'dave123'); + + +INSERT INTO peeps (content, time, user_id) VALUES ('This is the first Peep', '20230506 12:22:09 PM', 1); +INSERT INTO peeps (content, time, user_id) VALUES ('This is the second Peep', '20230507 05:45:35 PM', 1); +INSERT INTO peeps (content, time, user_id) VALUES ('This is the third Peep', '20230508 09:42:01 AM', 2); +INSERT INTO peeps (content, time, user_id) VALUES ('This is the forth Peep', '20230509 11:12:59 PM', 2); + +``` + +Run this SQL file on the database to truncate (empty) the table, and insert the seed data. Be mindful of the fact any existing records in the table will be deleted. + +```bash +psql -h 127.0.0.1 chitter_test < spec/seeds_chitter.sql +``` + +## 3. Define the class names + +Usually, the Model class name will be the capitalised table name (single instead of plural). The same name is then suffixed by `Repository` for the Repository class name. + +```ruby +# Table name: peeps + +# Model class +# (in lib/peep.rb) +class Peep +end + +# Repository class +# (in lib/peep_repository.rb) +class PeepRepository +end + +# Table name: users + +# Model class +# (in lib/user.rb) +class User +end + +# Repository class +# (in lib/user_repository.rb) +class UserRepository +end +``` + +## 4. Implement the Model class + +Define the attributes of your Model class. You can usually map the table columns to the attributes of the class, including primary and foreign keys. + +```ruby + +# Table name: peeps + +# Model class +# (in lib/peep.rb) + +class Peep + # Replace the attributes by your own columns. + attr_accessor :id, :content, :time, :user_id +end + +# The keyword attr_accessor is a special Ruby feature +# which allows us to set and get attributes on an object, + +# Table name: users + +# Model class +# (in lib/user.rb) + +class User + # Replace the attributes by your own columns. + attr_accessor :id, :name, :email, :username +end + +``` + +*You may choose to test-drive this class, but unless it contains any more logic than the example above, it is probably not needed.* + +## 5. Define the Repository Class interface + +Your Repository class will need to implement methods for each "read" or "write" operation you'd like to run against the database. + +Using comments, define the method signatures (arguments and return value) and what they do - write up the SQL queries that will be used by each method. + +```ruby +# Table name: peeps + +# Repository class +# (in lib/peep_repository.rb) + +class PeepRepository + + # Selecting all records + # No arguments + def all + # Executes the SQL query: + # SELECT id, content, time, user_id FROM peeps; + + # Returns an array of Peep objects. + end + + def create(peep) + # Executes the SQL query: + # INSERT INTO peeps (content, time, user_id) VALUES ($1, $2, $3); + + # returns nil + end + + def find_by_owner(user_id) + # Executes the SQL query: + # SELECT id, content, time, user_id FROM peeps WHERE user_id = $1; + + # Returns an array of Peep objects. + end + +end + +# Table name: users + +# Repository class +# (in lib/user_repository.rb) + +class UserRepository + + def find(username) + # Executes the SQL query: + # SELECT id, name, email, username FROM users WHERE username = $1; + + # Returns a User object. + end + + def create(user) + # Executes the SQL query: + # INSERT INTO users (name, email, username) VALUES ($1, $2, $3); + + # returns nil + end +end +``` + +## 6. Write Test Examples + +Write Ruby code that defines the expected behaviour of the Repository class, following your design from the table written in step 5. + +These examples will later be encoded as RSpec tests. + +```ruby +# Peeps testing +# 1 +# Get all peeps +repo = PeepRepository.new +peeps = repo.all + +expect(peeps.length).to eq 4 +expect(peeps.first.content).to eq 'This is the first Peep' +expect(peeps.first.time).to eq '2023-05-06 12:22:09' +expect(peeps.first.user_id).to eq 1 + +# 2 +# creates a new peep +repo = PeepRepository.new + +peep = Peep.new +peep.content = 'Jack here with a third peep!' +peep.time = '2023-05-10 14:56:09' +peep.user_id = '1' + +repo.create(peep) + +peeps = repo.all +last_peep = peeps.last + +expect(last_peep.content).to eq 'Jack here with a third peep!' +expect(last_peep.time).to eq '2023-05-10 14:56:09' +expect(last_peep.user_id).to eq '1' + +# 3 +# find all peeps by the same user +repo = PeepRepository.new +jack_peeps = repo.find_by_owner(1) +expect(jack_peeps.length).to eq 2 +expect(jack_peeps.first.content).to eq 'This is the first Peep' +expect(jack_peeps.first.time).to eq '2023-05-06 12:22:09' +expect(jack_peeps[1].content).to eq 'This is the second Peep' + + +# User testing +# 1 +# get all users +repo = UserRepository.new +users = repo.all + +expect(users.length).to eq 2 +expect(users.first.name).to eq 'Jack' +expect(users.first.email).to eq 'jack@email.com' +expect(users.first.username).to eq 'skates' + + +# 2 +# find a user with the username +repo = UserRepository.new +user = repo.find('skates') +expect(user.username).to eq 'skates' +expect(user.name).to eq 'Jack' +expect(user.email).to eq 'jack@email.com' + +# 3 +# create a new user and add it to the database + +``` + +Encode this example as a test. + +## 7. Reload the SQL seeds before each test run + +Running the SQL code present in the seed file will empty the table and re-insert the seed data. + +This is so you get a fresh table contents every time you run the test suite. + +```ruby +# file: spec/peep_repository_spec.rb + +def reset_chitter_tables + seed_sql = File.read('spec/seeds_chitter.sql') + connection = PG.connect({ host: '127.0.0.1', dbname: 'chitter_test' }) + connection.exec(seed_sql) +end + +describe PeepRepository do + before(:each) do + reset_chitter_tables + end + + # (your tests will go here). +end + +# file: spec/user_repository_spec.rb + +def reset_chitter_tables + seed_sql = File.read('spec/seeds_chitter.sql') + connection = PG.connect({ host: '127.0.0.1', dbname: 'chitter_test' }) + connection.exec(seed_sql) +end + +describe UserRepository do + before(:each) do + reset_chitter_tables + end + + # (your tests will go here). +end +``` + +## 8. Test-drive and implement the Repository class behaviour + +_After each test you write, follow the test-driving process of red, green, refactor to implement the behaviour._ \ No newline at end of file diff --git a/chitter_two_tables_design_recipe.md b/chitter_two_tables_design_recipe.md new file mode 100644 index 0000000000..82a288a86e --- /dev/null +++ b/chitter_two_tables_design_recipe.md @@ -0,0 +1,138 @@ +# Chitter - Two Tables Design Recipe Template + +## 1. Extract nouns from the user stories or specification + +``` +STRAIGHT UP + +As a Maker +So that I can let people know what I am doing +I want to post a message (peep) to chitter + +As a maker +So that I can see what others are saying +I want to see all peeps in reverse chronological order + +As a Maker +So that I can better appreciate the context of a peep +I want to see the time at which it was made + +As a Maker +So that I can post messages on Chitter as me +I want to sign up for Chitter + +HARDER + +As a Maker +So that only I can post messages on Chitter as me +I want to log in to Chitter + +As a Maker +So that I can avoid others posting messages on Chitter as me +I want to log out of Chitter + +ADVANCED + +As a Maker +So that I can stay constantly tapped in to the shouty box of Chitter +I want to receive an email if I am tagged in a Peep +``` + +``` +Nouns: + +peep, time, sign up, log in, log out, tagged, email +``` + +## 2. Infer the Table Name and Columns + +Put the different nouns in this table. Replace the example with your own nouns. + +| Record | Properties | +| --------------------- | ---------------------- | +| peep | content, time, user_id +| user | name, email, username + + +1. Name of the first table (always plural): `peeps` + + Column names: `content`, `time`, `user_id` + +2. Name of the second table (always plural): `users` + + Column names: `name`, `email` , `username` + +## 3. Decide the column types. + +[Here's a full documentation of PostgreSQL data types](https://www.postgresql.org/docs/current/datatype.html). + +Most of the time, you'll need either `text`, `int`, `bigint`, `numeric`, or `boolean`. If you're in doubt, do some research or ask your peers. + +Remember to **always** have the primary key `id` as a first column. Its type will always be `SERIAL`. + +``` +# EXAMPLE: + +Table: peeps +id: SERIAL +content: text +time: datetime +user_id: int + +Table: users +id: SERIAL +name: text +email: text +username: text +``` + +## 4. Design the Many-to-Many relationship + +Make sure you can answer YES to these two questions: + +1. Can one [TABLE ONE] have many [TABLE TWO]? (Yes/No) +2. Can one [TABLE TWO] have many [TABLE ONE]? (Yes/No) + +``` +1. Can one peep have many users? NO +2. Can one user have many peeps? YES +``` + +_If you would answer "No" to one of these questions, you'll probably have to implement a One-to-Many relationship, which is simpler. Use the relevant design recipe in that case._ + + +## 4. Write the SQL. + +```sql +-- file: peeps_users.sql + +DROP TABLE peeps; +DROP TABLE users; + + +-- Create the first table. +CREATE TABLE users ( + id SERIAL PRIMARY KEY, + name text, + email text, + username text +); + +-- Create the second table. +CREATE TABLE peeps ( + id SERIAL PRIMARY KEY, + content text, + time timestamp, + user_id int, + constraint fk_user foreign key(user_id) references users(id) on delete cascade +); + + +``` + +## 5. Create the tables. + +```bash +psql -h 127.0.0.1 chitter < spec/peeps_users.sql +psql -h 127.0.0.1 chitter_test < spec/peeps_users.sql +``` \ No newline at end of file diff --git a/config.ru b/config.ru new file mode 100644 index 0000000000..c41dba5056 --- /dev/null +++ b/config.ru @@ -0,0 +1,3 @@ +# file: config.ru +require './app' +run Application diff --git a/lib/database_connection.rb b/lib/database_connection.rb new file mode 100644 index 0000000000..a5e31564a6 --- /dev/null +++ b/lib/database_connection.rb @@ -0,0 +1,45 @@ +# 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 }) + end + + # def self.connect(database_name) + # @connection = PG.connect({ host: '127.0.0.1', dbname: database_name }) + # end + + # This method executes an SQL query + # on the database, providing some optional parameters + # (you will learn a bit later about when to provide these parameters). + def self.exec_params(query, params) + if @connection.nil? + raise 'DatabaseConnection.exec_params: Cannot run a SQL query as the connection to'\ + 'the database was never opened. Did you make sure to call first the method '\ + '`DatabaseConnection.connect` in your app.rb file (or in your tests spec_helper.rb)?' + end + @connection.exec_params(query, params) + end +end diff --git a/lib/peep.rb b/lib/peep.rb new file mode 100644 index 0000000000..1a733a67ec --- /dev/null +++ b/lib/peep.rb @@ -0,0 +1,4 @@ +class Peep + + attr_accessor :id, :content, :time, :user_id +end diff --git a/lib/peep_repository.rb b/lib/peep_repository.rb new file mode 100644 index 0000000000..875a27349e --- /dev/null +++ b/lib/peep_repository.rb @@ -0,0 +1,39 @@ +require_relative 'peep' + +class PeepRepository + def all + sql = "SELECT id, content, time, user_id FROM peeps;" + result = DatabaseConnection.exec_params(sql, []) + peeps_array = [] + result.each do |row| + peep = Peep.new + peep.id, peep.content, peep.time, peep.user_id = + row['id'], row['content'], row['time'], row['user_id'] + peeps_array << peep + end + return peeps_array + end + + def create(peep) + sql = 'INSERT INTO peeps (content, time, user_id) VALUES ($1, $2, $3);' + params = [peep.content, peep.time, peep.user_id] + + DatabaseConnection.exec_params(sql, params) + + return nil + end + + def find_by_owner(user_id) + sql = 'SELECT id, content, time, user_id FROM peeps WHERE user_id = $1;' + result = DatabaseConnection.exec_params(sql, [user_id]) + peeps_array = [] + result.each do |row| + peep = Peep.new + peep.id, peep.content, peep.time, peep.user_id = + row['id'], row['content'], row['time'], row['user_id'] + peeps_array << peep + end + return peeps_array + end + +end diff --git a/lib/user.rb b/lib/user.rb new file mode 100644 index 0000000000..427bbae7e9 --- /dev/null +++ b/lib/user.rb @@ -0,0 +1,6 @@ + +class User + + attr_accessor :id, :name, :email, :username, :password + +end diff --git a/lib/user_repository.rb b/lib/user_repository.rb new file mode 100644 index 0000000000..f1aecce99b --- /dev/null +++ b/lib/user_repository.rb @@ -0,0 +1,64 @@ +require_relative 'user' +require 'bcrypt' + +class UserRepository + def all + sql = 'SELECT id, name, email, username FROM users;' + result = DatabaseConnection.exec_params(sql, []) + user_array = [] + result.each do |row| + user = User.new + user.id, user.name, user.email, user.username = + row['id'].to_i, row['name'], row['email'], row['username'] + user_array << user + end + return user_array + end + + def find(username) + sql = 'SELECT id, name, email, username FROM users WHERE username = $1;' + param = [username] + result = DatabaseConnection.exec_params(sql, param) + record = result.first + user = User.new + user.id = record['id'].to_i + user.name = record['name'] + user.email = record['email'] + user.username = record['username'] + return user + end + + def create(user) + # Executes the SQL query: + sql = 'INSERT INTO users (name, email, username, password) VALUES ($1, $2, $3, $4);' + params = [user.name, user.email, user.username, user.password] + + DatabaseConnection.exec_params(sql, params) + + return nil + end + + def find_by_id(id) + sql = 'SELECT id, name, email, username FROM users WHERE id = $1;' + result = DatabaseConnection.exec_params(sql, [id]) + record = result.first + user = User.new + user.id = record['id'].to_i + user.name = record['name'] + user.email = record['email'] + user.username = record['username'] + return user.username + end + + def find_by_username(username) + sql = 'SELECT id, name, email, username, password FROM users WHERE username = $1;' + result = DatabaseConnection.exec_params(sql, [username]) + record = result.first + user = User.new + user.id = record['id'] + user.name = record['name'] + user.email = record['email'] + user.username = record['username'] + return user + end +end diff --git a/public/chit_chat.png b/public/chit_chat.png new file mode 100644 index 0000000000..03d14916ed Binary files /dev/null and b/public/chit_chat.png differ diff --git a/public/style.css b/public/style.css new file mode 100644 index 0000000000..6abc505d81 --- /dev/null +++ b/public/style.css @@ -0,0 +1,90 @@ +/* style.css */ + + +body { + font-family: Space Mono; + margin: 0; +} + +h1, +h2, +h3 { + color: #404042; + font-family: Space Mono; +} + +p { + color: #404042; + opacity: .7; +} + +.form { + border-style: solid; + padding: 10px; +} + +.container { + width: 800px; + margin: auto; +} + +.peep { + width: 600px; + margin: 20px auto; + border-style: solid; + padding: 5px; +} + +.banner { + background-image: linear-gradient(rgba(249, 153, 196, 0.4), rgba(246, 193, 233, 0.4)), url('/chit_chat.png'); + background-size: cover; + background-position: center; + padding: 20px 0; + text-align: center; +} + +.userbanner { + padding: 10px 0; + text-align: center; +} + +.banner h1 { + color: white; + font-size: 45px; + margin: 0; +} + +.banner h3 { + color: white; +} + +.banner p { + color: white; + font-size: 24px; +} + +.button-blue { + background-color: #1A48E3; + color: white; + padding: 16px 48px; + border-radius: 4px; + display: inline-block; + text-decoration: none; + transition: .3s; +} + +.button-blue:hover { + background-color: #0D1EC6; + color: white; +} + + +.footer { + background: #404042; + text-align: center; + padding: 24px 0; +} + +.footer p { + color: white; +} \ No newline at end of file diff --git a/spec/integration/app_spec.rb b/spec/integration/app_spec.rb new file mode 100644 index 0000000000..efb0d49773 --- /dev/null +++ b/spec/integration/app_spec.rb @@ -0,0 +1,46 @@ +require_relative '../../app' +require 'spec_helper' +require 'rack/test' + +RSpec.describe Application do + include Rack::Test::Methods + + let(:app) {Application.new} + + def reset_chitter_tables + seed_sql = File.read('spec/seeds_chitter.sql') + connection = PG.connect({ host: '127.0.0.1', dbname: 'chitter_test' }) + connection.exec(seed_sql) + end + + before(:each) do + reset_chitter_tables + end + + context 'GET /' do + it 'should return 200 and correct body' do + response = get('/') + + expect(response.status).to eq 200 + expect(response.body).to include '
<%=@users.find_by_id(peep.user_id)%>
+<%= peep.content%>
+<%= peep.time%>
+ <%end%> +<%=@users.find_by_id(peep.user_id)%>
+<%= peep.content%>
+<%= peep.time%>
+ <%end%> +