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 Pull Request #2196

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
da281e4
Add database design recipe
aubreysalmins May 9, 2023
126ab38
Add table seeds
aubreysalmins May 9, 2023
13d25a5
Update table seeds
aubreysalmins May 9, 2023
8d253c2
Modify recipe
aubreysalmins May 9, 2023
e54671a
Update seeds with test data
aubreysalmins May 9, 2023
b4960e5
Add project files
aubreysalmins May 10, 2023
0edc6de
Create database connection and test database
aubreysalmins May 10, 2023
8fabf15
Add User and Peep class info
aubreysalmins May 10, 2023
8fd12cd
Add first test
aubreysalmins May 10, 2023
0677095
Sinatra setup
aubreysalmins May 10, 2023
71fa786
Add first test
aubreysalmins May 10, 2023
48353ec
Rubocop errors fixed
aubreysalmins May 10, 2023
2be3bba
First test passing
aubreysalmins May 10, 2023
bb62516
Fix peep class and adjust tests
aubreysalmins May 10, 2023
69c57bd
Wrote second test
aubreysalmins May 10, 2023
de4e5f9
Second test passing
aubreysalmins May 10, 2023
62425b5
Add all function to user repository
aubreysalmins May 10, 2023
87d46bb
Add find by id
aubreysalmins May 10, 2023
7038290
Add create user function
aubreysalmins May 10, 2023
2cc8fa3
Add homepage
aubreysalmins May 10, 2023
7dab1d6
Add peeps to homepage
aubreysalmins May 11, 2023
0f38220
Reverse display order of peeps
aubreysalmins May 11, 2023
7d5f23b
Add username to each peep
aubreysalmins May 11, 2023
7a3aa2b
New peep test added
aubreysalmins May 11, 2023
99f1b12
Update app.rb with new peep post route
aubreysalmins May 11, 2023
a0884af
Post test failing
aubreysalmins May 11, 2023
f346f0d
Post test passing
aubreysalmins May 11, 2023
ffa01c1
Add signup page
aubreysalmins May 11, 2023
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
5 changes: 5 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -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"
28 changes: 27 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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
40 changes: 40 additions & 0 deletions app.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
require 'sinatra/base'
require 'sinatra/reloader'
require_relative 'lib/peep_repository'
require_relative 'lib/user_repository'
require_relative 'lib/database_connection'

DatabaseConnection.connect

class Application < Sinatra::Base
# This allows the app code to refresh
# without having to restart the server.
configure :development do
register Sinatra::Reloader
end

get '/' do
peep_repo = PeepRepository.new
@user_repo = UserRepository.new
@peep_list = peep_repo.all
return erb(:index)
end

post '/' do
repo = PeepRepository.new
@user_repo = UserRepository.new
peep = Peep.new
peep.title = params[:title]
peep.content = params[:content]
peep.time_stamp = Time.new
peep.user_id = 1

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to tidy this up a bit more - by tidying, I mean moving some of the 'business logic' out of the app.rb - you could write something like peep = Peep.new(params[:title], params[:content], etc., passing in these parameters as arguments to a new Peep object, and storing the variables that way instead.

The benefit of doing this is that you push some of the responsibility to the Peep class instead of the app.rb. This will adhere more closely to 'Single Responsibility Principle', which guides us to try to give each class one overarching responsibility.

In the context of a full stack web app, the pattern we follow is Model View Controller, or MVC. In this case, app.rb is the Controller - it's responsible for responding to URL requests / button actions etc. and deciding which code to run - the Models are responsible for structuring our objects like Peeps and Users, and communicating with the database through the Repository classes. And the Views are responsible for what's visible to the user.

repo.create(peep)
@peep_list = repo.all
return erb(:index)
end

get "/signup" do
return erb(:signup)
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
31 changes: 31 additions & 0 deletions lib/database_connection.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
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_test'
else
database_name = 'chitter'
end
@connection = PG.connect({ host: '127.0.0.1', dbname: database_name })
end

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

class PeepRepository
def all
peeps = []
sql = 'SELECT * FROM peeps'
result_set = DatabaseConnection.exec_params(sql, [])
result_set.each do |record|
peep = Peep.new
peep.id = record['id'].to_i
peep.title = record['title']
peep.content = record['content']
peep.time_stamp = record['time_stamp']
peep.user_id = record['user_id'].to_i

peeps << peep
end
peeps
end

def create(peep)
sql = 'INSERT INTO peeps(title, content, time_stamp, user_id) VALUES($1, $2, $3, $4);'
params = [peep.title, peep.content, peep.time_stamp, peep.user_id]

new_peep = DatabaseConnection.exec_params(sql, params)
end
end
3 changes: 3 additions & 0 deletions lib/user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class User
attr_accessor :id, :name, :username, :email, :password
end
38 changes: 38 additions & 0 deletions lib/user_repository.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
require_relative 'user'

class UserRepository
def all
users = []

sql = 'SELECT * FROM users;'
result_set = DatabaseConnection.exec_params(sql, [])

result_set.each do |record|
user = User.new
user.id = record['id'].to_i
user.name = record['name']
user.username = record['username']
user.email = record['email_address']
user.password = record['password']
users << user
end
return users
end

def find(id)
sql = 'SELECT * FROM users WHERE id = $1;'
result = DatabaseConnection.exec_params(sql, [id])
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
end

def create(user)
sql = 'INSERT INTO users(name, username, email, password) VALUES($1, $2, $3, $4);'
params = [user.name, user.username, user.email, user.password]
result = DatabaseConnection.exec_params(sql, params)
end
end
59 changes: 59 additions & 0 deletions recipes/database_design_recipe
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
Two Tables Design Recipe Template

Copy this recipe template to design and create two related database tables from a specification.

1. Extract nouns from the user stories or specification

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

Nouns: peep, time, user, email, username, password

A user can have many peeps, therefore a peep belongs to a user so the foreign key belongs in the peeps table.

CREATE TABLE users (
id SERIAL PRIMARY KEY,
name text,
username text,
email text,
password text
);

CREATE TABLE peeps (
id SERIAL PRIMARY KEY,
title text,
content text,
user_id int,
constraint fk_user foreign key (user_id)
references users(id)
on delete cascade
);

psql -h 127.0.0.1 chitter < chitter_tables.sql

-

TRUNCATE TABLE users RESTART IDENTITY CASCADE;
TRUNCATE TABLE peeps RESTART IDENTITY CASCADE;

INSERT INTO users (name, username, email, password) VALUES ('Aubrey Salmins', 'aubreysalmins', '[email protected]', '123pswd');
INSERT INTO users (name, username, email, password) VALUES ('Budsy', 'meow-meow', '[email protected]', 'pswd234');

INSERT INTO peeps (title, content, user_id) VALUES ('Peep 1', 'Its a peep', 1);
INSERT INTO peeps (title, content, user_id) VALUES ('Meep', 'Meow', 2);

-

56 changes: 56 additions & 0 deletions spec/Integration/app_integration_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
require 'spec_helper'
require 'rack/test'
require_relative '../../app'

def reset_tables
seed_sql = File.read('spec/seeds/chitter_tables.sql')
connection = PG.connect({ host: '127.0.0.1', dbname: 'chitter_test' })
connection.exec(seed_sql)
end

describe Application do
before(:each) do
reset_tables
end
# This is so we can use rack-test helper methods.
include Rack::Test::Methods

# We need to declare the `app` value by instantiating the Application
# class so our tests work.
let(:app) { Application.new }

context '/' do
it 'should return the homepage' do
response = get('/')
expect(response.status).to eq(200)
expect(response.body).to include('<title>Chitter</title>')
expect(response.body).to include('<h1>Chitter</h1>')
expect(response.body).to include ('<form method="POST" action="/">')
end

it 'should return all peeps' do
response = get('/')
expect(response.status).to eq(200)
expect(response.body).to include "Peep 1"
expect(response.body).to include "Meow"
end
end

context 'POST /' do
it 'posts a new peep' do
response = post('/', title: 'New peep', content: 'new peep content')
expect(response.status).to eq 200
expect(response.body).to include 'New peep'
end
end

context 'GET /signup' do
it 'gets the signup page' do
response = get('/signup')

expect(response.status).to eq 200
expect(response.body).to include '<h2>Sign up</h2>'
expect(response.body).to include '<label>Password</label>'
end
end
end
38 changes: 38 additions & 0 deletions spec/peep_repository_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
require 'peep_repository'

def reset_tables
seed_sql = File.read('spec/seeds/chitter_tables.sql')
connection = PG.connect({ host: '127.0.0.1', dbname: 'chitter_test' })
connection.exec(seed_sql)
end

describe PeepRepository do
before(:each) do
reset_tables
end

it "finds all peeps" do
repo = PeepRepository.new

peeps = repo.all

expect(peeps.length).to eq(2)
expect(peeps.first.title).to eq('Peep 1')
expect(peeps.first.user_id).to eq(1)
expect(peeps.last.title).to eq('Meep')
expect(peeps.last.user_id).to eq(2)
end

it "creates a peep" do
repo = PeepRepository.new
peep = double(:peep, title: "Doubler", content: "This is a double", time_stamp: "2021-09-07 12:20:43", user_id: 1)
repo.create(peep)
peeps = repo.all

peeps = repo.all
expect(peeps.length).to eq 3
expect(peeps.last.title).to eq 'Doubler'
expect(peeps.last.content).to eq 'This is a double'
expect(peeps.last.time_stamp).to eq '2021-09-07 12:20:43'
end
end
Loading