Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chitter-challenge #2199

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,14 @@ 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"

gem "time", "~> 0.2.2"

gem "pg", "~> 1.5"

gem "bcrypt", "~> 3.1"
34 changes: 34 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,22 @@ GEM
specs:
ansi (1.5.0)
ast (2.4.2)
bcrypt (3.1.18)
date (3.3.3)
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)
pg (1.5.3)
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)
Expand Down Expand Up @@ -36,6 +47,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)
Expand All @@ -46,18 +58,40 @@ 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)
time (0.2.2)
date
unicode-display_width (2.0.0)
webrick (1.8.1)

PLATFORMS
ruby

DEPENDENCIES
bcrypt (~> 3.1)
pg (~> 1.5)
rack-test (~> 2.1)
rspec
rubocop (= 1.20)
simplecov
simplecov-console
sinatra (~> 3.0)
sinatra-contrib (~> 3.0)
time (~> 0.2.2)
webrick (~> 1.8)

RUBY VERSION
ruby 3.0.2p107
Expand Down
67 changes: 66 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,69 @@
Chitter Challenge
# Chitter Challenge

![homepage](./images/homepage.png)

![shoutybox when logged in](./images/shoutybox.png)

![signup page](./images/sign-up.png)


## To Run App
```
git clone https://github.com/veritywong/chitter-challenge.git
cd chitter-challenge
bundle install
createdb chitter_test
psql -h 127.0.0.1 chitter_challenge < spec/tables_seeds.sql
rspec
rackup
http://localhost:('Port in use')/
```

## Description
This app is the start of a Twitter clone, that allows users to post messages to a public stream. In order to post they must signup and login, however anybody can view the public stream on the 'Shouty Box'.

## Approach
This was the first web app that we created on the course, so the goal was to get a good understanding of sending HTTP requests from the frontend and how they were then run in the backend.

I begun by following a design recipe to decide database structure, which in this case includes posts and users tables, which have a many to many relationship.
From there I used TDD to create the user repository and post repository, implementing methods towards creating CRUD functionality.

Next I worked between the app.rb file and HTML files, completing the features such as signup, login and posting a new peep.

Passwords are encrypted using bcrypt, and sessions are used to know whether a user is logged in and to determine what they can view.

## Diagrams
This diagram shows the differente pages in the app. The user can only post or view their account when logged in.

![Account Diagram](./images/diagram.png)

Below is a diagram of clicking the link for the stream of posts:

![Account Diagram](./images/shoutybox-diagram.png)

## Technology Used
* Ruby
* Sinatra & ERB
* BCrypt
* PostgreSQL
* HTML & CSS

## Tests
This is a screen shot of the passing tests and test coverage.

![Account Diagram](./images/passing-tests.png)


# Initial Project Information

Below is the instructuctions given at the start of the project.

Gems added:
time
sinatra sinatra-contrib webrick rack-test
pg
bcrypt

=================

* Feel free to use Google, your notes, books, etc. but work on your own
Expand Down
123 changes: 123 additions & 0 deletions app.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# file: app.rb
require 'sinatra/base'
require 'sinatra/reloader'
require 'time'
require_relative 'lib/database_connection'
require_relative 'lib/user_repository'
require_relative 'lib/post_repository'


DatabaseConnection.connect

class Application < Sinatra::Base
enable :sessions

configure :development do
register Sinatra::Reloader
also_reload 'lib/user_repository'
also_reload 'lib/post_repository'
end

get '/' do
return erb(:index)
end

get '/login' do
return erb(:login)
end

post '/login' do
email = params[:email]
password = params[:password]

repo = UserRepository.new
@user = repo.find_by_email(email)

# login_result = repo.sign_in(email, password)

if repo.sign_in(email, password) == true
session[:user_id] = @user.id

return erb(:login_success)
else
status 400
return erb(:login_error)
end
end

get '/account_page' do
posts = PostRepository.new
@users = UserRepository.new

@user = @users.find(session[:user_id])
@peeps = posts.all.reverse

if session[:user_id] == nil
return redirect('/login')
else
return erb(:account_page)
end
end

get '/signup' do
return erb(:signup)
end

post '/signup' do
users = UserRepository.new
new_user = User.new
new_user.name = params[:name]
new_user.username = params[:username]
new_user.email = params[:email]
new_user.password = params[:password]

users.create(new_user)

if new_user.name == '' || new_user.name == '<script>' ||
new_user.username == '' || new_user.username == '<script>'
status 400
return erb(:signup_error)
else
return erb(:signup_confirmation)
end

end

get '/shoutybox' do
posts = PostRepository.new
@users = UserRepository.new

@peeps = posts.all.reverse

return erb(:shoutybox)
end

post '/peep' do
posts = PostRepository.new

new_post = Post.new
new_post.peep = params[:peep]
new_post.time = Time.now
new_post.user_id = session[:user_id]

posts.create(new_post)

if new_post.peep == '' || new_post.peep == '<script>'
status 400
return erb(:peep_new)
else
posts = PostRepository.new
@users = UserRepository.new

@peeps = posts.all.reverse
return erb(:shoutybox)
end
end

get '/logout' do
session[:user_id] = nil
return erb(:index)
end

end

3 changes: 3 additions & 0 deletions config.ru
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# file: config.ru
require './app'
run Application
Binary file added images/diagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/homepage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/passing-tests.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/shoutybox-diagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/shoutybox.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/sign-up.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 33 additions & 0 deletions lib/database_connection.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# 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 ENV['ENV'] == 'test'
database_name = 'chitter_challenge_test'
else
database_name = 'chitter_challenge'
end
@connection = PG.connect({ host: '127.0.0.1', dbname: database_name })
end

# This method executes an SQL query
# on the database, providing some optional parameters
# (you will learn a bit later about when to provide these parameters).
def self.exec_params(query, params)
if @connection.nil?
raise 'DatabaseConnection.exec_params: Cannot run a SQL query as the connection to'\
'the database was never opened. Did you make sure to call first the method '\
'`DatabaseConnection.connect` in your app.rb file (or in your tests spec_helper.rb)?'
end
@connection.exec_params(query, params)
end
end
3 changes: 3 additions & 0 deletions lib/post.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class Post
attr_accessor :id, :peep, :time, :user_id
end
64 changes: 64 additions & 0 deletions lib/post_repository.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
require_relative './post'
require_relative './user_repository'

class PostRepository

def all
posts = []

sql = 'SELECT posts.id,
posts.peep,
posts.time,
users.id AS user_id,
users.name,
users.username
FROM posts
JOIN users
ON users.id = posts.user_id;'

# 'SELECT id, peep, time, user_id FROM posts
# JOIN users on posts.user_id = users.id
# WHERE users.id = $1;'

result_set = DatabaseConnection.exec_params(sql, [])

result_set.each do |record|
post = Post.new
post.id = record['id']
post.peep = record['peep']
post.time = record['time']
post.user_id = record['user_id']

posts << post
end

posts
end


def create(post)
sql = 'INSERT INTO posts (peep, time, user_id) VALUES ($1, $2, $3);'
result_set = DatabaseConnection.exec_params(sql, [post.peep, post.time, post.user_id])

return post
end

def find(id)
sql = 'SELECT id, peep, time, user_id FROM posts WHERE id = $1;'
sql_params = [id]
result = DatabaseConnection.exec_params(sql, sql_params)
record = result[0]

post = Post.new
post.id = record['id']
post.peep = record['peep']
post.time = record['time']
post.user_id = record['user_id']

return post
end

end

# posts = PostRepository.new
# posts.all
Loading