diff --git a/Gemfile b/Gemfile index b1a320395a..fdac52c66f 100644 --- a/Gemfile +++ b/Gemfile @@ -11,3 +11,12 @@ 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 "pg", "~> 1.4" + +gem "bcrypt" diff --git a/Gemfile.lock b/Gemfile.lock index 66064703c7..f0a10ac592 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,11 +3,21 @@ GEM specs: ansi (1.5.0) ast (2.4.2) + bcrypt (3.1.18) 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.4.6) + rack (2.2.6.4) + 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 +46,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,18 +57,37 @@ 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 + bcrypt + pg (~> 1.4) + 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 diff --git a/app.rb b/app.rb new file mode 100644 index 0000000000..26f603e97d --- /dev/null +++ b/app.rb @@ -0,0 +1,100 @@ +require 'sinatra/base' +require 'sinatra/reloader' +require_relative 'lib/database_connection' +require_relative 'lib/user_repository' +require_relative 'lib/peep_repository' +require 'bcrypt' + +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 + user_repo = UserRepository.new + peep_repo = PeepRepository.new + peeps = peep_repo.all + + @complete_peep_info = [] + + peeps.each do |peep| + user = user_repo.find(peep.user_id) + peep_info = {peep: peep, name: user.name, username: user.username} + @complete_peep_info << peep_info + end + + return erb(:index) # add log in status logic + end + + get '/login' do + return erb(:login) + end + + post '/login' do + email = params[:email] + password = params[:password] + + user_repo = UserRepository.new + + user = user_repo.find_by_email(email) + stored_password = BCrypt::Password.new(user.password) + + if stored_password == password + session[:user_id] = user.id + return erb(:login_success) + else + return erb(:wrong_password) + end + end + + get '/peeps/new' do + if session[:user_id] == nil + return erb(:login) + else + return erb(:new_peep) + end + end + + post '/peeps' do + if session[:user_id] == nil + return erb(:login) + else + peep_repo = PeepRepository.new + new_peep = Peep.new + new_peep.time = Time.now + new_peep.content = params[:content] + new_peep.user_id = session[:user_id] + + peep_repo.create(new_peep) + + return erb(:new_peep_success) + end + end + + get '/signup' do + return erb(:signup) + end + + post '/signup' do + repo = UserRepository.new + new_user = User.new + + new_user.name = params[:name] # name and username must be unique + new_user.username = params[:username] + new_user.email = params[:email] + new_user.password = params[:password] + + repo.create(new_user) + + return erb(:signup_success) + end + + # add invalid_parameters? method +end 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..f7cc49dd3e --- /dev/null +++ b/lib/database_connection.rb @@ -0,0 +1,38 @@ +# 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['DATABASE_URL'] != nil + @connection = PG.connect(ENV['DATABASE_URL']) + return + end + + 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 diff --git a/lib/peep.rb b/lib/peep.rb new file mode 100644 index 0000000000..fb08566584 --- /dev/null +++ b/lib/peep.rb @@ -0,0 +1,3 @@ +class Peep + attr_accessor :id, :time, :content, :user_id +end diff --git a/lib/peep_repository.rb b/lib/peep_repository.rb new file mode 100644 index 0000000000..9a538d250f --- /dev/null +++ b/lib/peep_repository.rb @@ -0,0 +1,30 @@ +require_relative 'peep' + +class PeepRepository + def all + peeps = [] + + sql = 'SELECT id, time, content, user_id FROM peeps ORDER BY time DESC;' + result_set = DatabaseConnection.exec_params(sql, []) + + result_set.each do |record| + peep = Peep.new + peep.id = record['id'] + peep.time = record['time'] + peep.content = record['content'] + peep.user_id = record['user_id'] + + peeps << peep + end + + return peeps + end + + def create(peep) + sql = 'INSERT INTO peeps (time, content, user_id) VALUES ($1, $2, $3);' + params = [peep.time, peep.content, peep.user_id] + result_set = DatabaseConnection.exec_params(sql, params) + + return peep + end +end diff --git a/lib/user.rb b/lib/user.rb new file mode 100644 index 0000000000..9b8f2a11fb --- /dev/null +++ b/lib/user.rb @@ -0,0 +1,3 @@ +class User + attr_accessor :id, :name, :username, :email, :password +end diff --git a/lib/user_repository.rb b/lib/user_repository.rb new file mode 100644 index 0000000000..bf291fe712 --- /dev/null +++ b/lib/user_repository.rb @@ -0,0 +1,42 @@ +require_relative 'user' +require 'bcrypt' + +class UserRepository + def create(new_user) + encrypted_password = BCrypt::Password.create(new_user.password) + + sql = 'INSERT INTO users (name, username, email, password) VALUES ($1, $2, $3, $4);' + params = [new_user.name, new_user.username, new_user.email, encrypted_password] + DatabaseConnection.exec_params(sql, params) + + return new_user + end + + def find_by_email(email) + sql = 'SELECT id, name, username, email, password FROM users WHERE email = $1;' + result_set = DatabaseConnection.exec_params(sql, [email]) + + user = User.new + user.id = result_set[0]['id'] + user.name = result_set[0]['name'] + user.username = result_set[0]['username'] + user.email = result_set[0]['email'] + user.password = result_set[0]['password'] + + return user + end + + def find(id) + sql = 'SELECT id, name, username, email, password FROM users WHERE id = $1;' + result_set = DatabaseConnection.exec_params(sql, [id]) + + user = User.new + user.id = result_set[0]['id'] + user.name = result_set[0]['name'] + user.username = result_set[0]['username'] + user.email = result_set[0]['email'] + user.password = result_set[0]['password'] + + return user + end +end diff --git a/route_plan.md b/route_plan.md new file mode 100644 index 0000000000..6c642a0c78 --- /dev/null +++ b/route_plan.md @@ -0,0 +1,161 @@ +# {{ METHOD }} {{ PATH}} Route Design Recipe + +_Copy this design recipe template to test-drive a Sinatra route._ + +## 1. Design the Route Signature + +[x] Page: homepage +Request: GET / (peep_repo.all) +No parameters +Response (200 OK) +index view with list of peeps, login status, link to log in/out, links to sign up and create peep + +[x] Page: create new peep +Request: GET /peeps/new +No parameters +Response (200 OK) +new_peep view with form to create new peep + +[x] Page: new peep created (peep_repo.create(new_peep)) +Request: POST /peeps +Parameters: name, username, time, content +Response (200 OK) +new_peep_success view with link to homepage + +[x] Page: signup +Request: GET /signup +No parameters +Response (200 OK) +signup view with form to create a new user + +[x] Page: signup successful (user_repo.create(new_user)) +Request: POST /signup +Parameters: name, email, username, password +Response (200 OK) +signup success view with link to login + +[x] Page: log in +Request: GET /login +No parameters +Response (200 OK) +login view with form to log in + +[x] Page: log in successful (user_repo.find_by_email(email)) +Request: POST /login +Parameters: email, password +Response (200 OK) +login success view with link to homepage + +[ ] Page: log out +Request: GET /logout +No parameters +Response (200 OK) +logout view with button to log out + +[ ] Page: log out successful +Request: POST /logout +Parameters: user_id? +Response (200 OK) +Redirect to homepage + +You'll need to include: + * the HTTP method + * the path + * any query parameters (passed in the URL) + * or body parameters (passed in the request body) + +## 2. Design the Response + +The route might return different responses, depending on the result. + +For example, a route for a specific blog post (by its ID) might return `200 OK` if the post exists, but `404 Not Found` if the post is not found in the database. + +Your response might return plain text, JSON, or HTML code. + +_Replace the below with your own design. Think of all the different possible responses your route will return._ + +```html + + + + + + +

Post title

+
Post content
+ + +``` + +```html + + + + + + +

Sorry!

+
We couldn't find this post. Have a look at the homepage?
+ + +``` + +## 3. Write Examples + +_Replace these with your own design._ + +``` +# Request: + +GET /posts?id=1 + +# Expected response: + +Response for 200 OK +``` + +``` +# Request: + +GET /posts?id=276278 + +# Expected response: + +Response for 404 Not Found +``` + +## 4. Encode as Tests Examples + +```ruby +# EXAMPLE +# file: spec/integration/application_spec.rb + +require "spec_helper" + +describe Application do + include Rack::Test::Methods + + let(:app) { Application.new } + + context "GET /" do + it 'returns 200 OK' do + # Assuming the post with id 1 exists. + response = get('/posts?id=1') + + expect(response.status).to eq(200) + # expect(response.body).to eq(expected_response) + end + + it 'returns 404 Not Found' do + response = get('/posts?id=276278') + + expect(response.status).to eq(404) + # expect(response.body).to eq(expected_response) + end + end +end +``` + +## 5. Implement the Route + +Write the route and web server code to implement the route behaviour. diff --git a/spec/app_spec.rb b/spec/app_spec.rb new file mode 100644 index 0000000000..669f76a57c --- /dev/null +++ b/spec/app_spec.rb @@ -0,0 +1,109 @@ +require "spec_helper" +require "rack/test" +require_relative "../app" + +describe Application do + include Rack::Test::Methods + + let(:app) { Application.new } + + def reset_tables + seed_sql = File.read('spec/seeds/chitter_seeds.sql') + connection = PG.connect({ host: '127.0.0.1', dbname: 'chitter_challenge_test' }) + connection.exec(seed_sql) + end + + before(:each) do + reset_tables + end + + context "GET /" do + it "displays a list of all peeps in reverse chronological order" do + response = get("/") + expect(response.status).to eq 200 + expect(response.body).to include 'Content: This is my first peep!' + expect(response.body).to include 'Username: @mrbobby' + end + end + + context "GET /peeps/new" do + it "displays the create new peep page" do + # creates a user with a hashed password + response = post("/signup", name: 'Elton John', email: 'elton@john.com', username: 'rocketman', password: 'yellowbrickroad') + expect(response.status).to eq 200 + expect(response.body).to include 'Sign up successful!' + # logs that user in + response = post("/login", email: 'elton@john.com', password: 'yellowbrickroad') + expect(response.status).to eq 200 + expect(response.body).to include '

Log in successful!

' + # goes to the new peep page + response = get("/peeps/new") + expect(response.status).to eq 200 + expect(response.body).to include '

Create a new Peep

' + end + end + + context "POST /peeps" do + it "adds a new peep to the database" do + # creates a user with a hashed password + response = post("/signup", name: 'Elton John', email: 'elton@john.com', username: 'rocketman', password: 'yellowbrickroad') + expect(response.status).to eq 200 + expect(response.body).to include 'Sign up successful!' + # logs that user in + response = post("/login", email: 'elton@john.com', password: 'yellowbrickroad') + expect(response.status).to eq 200 + expect(response.body).to include '

Log in successful!

' + # creates a peep with that user + response = post("/peeps", time: '2023-04-12 11:11:00', content: 'Making Peeps', user_id: '3') + expect(response.status).to eq 200 + expect(response.body).to include 'New Peep created!' # add peep content to success page + end + end + + context "GET /login" do + it "displays the login page" do + response = get("/login") + expect(response.status).to eq 200 + expect(response.body).to include '

Log in

' + end + end + + context "POST /login" do + it "logs in the user with their credentials" do # seeded users weren't created with bcrypt so don't pass this test + response = post("/signup", name: 'Elton John', email: 'elton@john.com', username: 'rocketman', password: 'yellowbrickroad') + expect(response.status).to eq 200 + expect(response.body).to include 'Sign up successful!' + + response = post("/login", email: 'elton@john.com', password: 'yellowbrickroad') + expect(response.status).to eq 200 + expect(response.body).to include '

Log in successful!

' # add username to success page + end + + it "tells the user they entered the wrong password" do + response = post("/signup", name: 'Elton John', email: 'elton@john.com', username: 'rocketman', password: 'yellowbrickroad') + expect(response.status).to eq 200 + expect(response.body).to include 'Sign up successful!' + + response = post("login", email: 'elton@john.com', password: 'stillstanding') + expect(response.status).to eq 200 + expect(response.body).to include 'Wrong password.' + end + end + + context "GET /signup" do + it "displays the signup page" do + response = get("/signup") + expect(response.status).to eq 200 + expect(response.body).to include '
' + end + end + + context "POST /signup" do + it "creates a new user in the database" do + response = post("/signup", name: 'Elton John', email: 'elton@john.com', username: 'rocketman', password: 'yellowbrickroad') + expect(response.status).to eq 200 + expect(response.body).to include 'Sign up successful!' # add username to success page + end + end + +end diff --git a/spec/peep_repository_spec.rb b/spec/peep_repository_spec.rb new file mode 100644 index 0000000000..37394ebb38 --- /dev/null +++ b/spec/peep_repository_spec.rb @@ -0,0 +1,42 @@ +require 'peep' +require 'peep_repository' + +def reset_tables + seed_sql = File.read('spec/seeds/chitter_seeds.sql') + connection = PG.connect({ host: '127.0.0.1', dbname: 'chitter_challenge_test' }) + connection.exec(seed_sql) +end + +describe PeepRepository do + before(:each) do + reset_tables + end + + it 'finds all peeps in reverse chronological order' do + repo = PeepRepository.new + peeps = repo.all + + expect(peeps.length).to eq 2 + expect(peeps.first.content).to eq "Hello Chitter." + expect(peeps.first.id).to eq '2' + expect(peeps.first.user_id).to eq '2' + end + + it 'creates a new peep' do + repo = PeepRepository.new + + new_peep = Peep.new + new_peep.time = '2023-01-01 15:30:00' + new_peep.content = 'Peeps are cool' + new_peep.user_id = '2' + repo.create(new_peep) + + peeps = repo.all + + expect(peeps.length).to eq 3 + expect(peeps.last.time).to eq '2023-01-01 15:30:00' + expect(peeps.last.content).to eq 'Peeps are cool' + expect(peeps.last.user_id).to eq '2' + expect(peeps.last.id).to eq '3' + end +end diff --git a/spec/seeds/chitter_seeds.sql b/spec/seeds/chitter_seeds.sql new file mode 100644 index 0000000000..3ee5cb208a --- /dev/null +++ b/spec/seeds/chitter_seeds.sql @@ -0,0 +1,9 @@ +TRUNCATE TABLE users, peeps RESTART IDENTITY; + +INSERT INTO users ("name", "username", "email", "password") VALUES +('Alice Smith', 'malice12', 'alice.smith@gmail.com', 'password'), +('Bobby Blake', 'mrbobby', 'mrbobby@gmail.com', 'secret'); + +INSERT INTO peeps ("time", "content", "user_id") VALUES +('2023-03-20 12:22:00', 'This is my first peep!', '1'), +('2023-04-01 10:19:00', 'Hello Chitter.', '2'); diff --git a/spec/seeds/create_table_seeds.sql b/spec/seeds/create_table_seeds.sql new file mode 100644 index 0000000000..56d4ac8ade --- /dev/null +++ b/spec/seeds/create_table_seeds.sql @@ -0,0 +1,21 @@ +DROP TABLE IF EXISTS users; + +CREATE TABLE users ( + id SERIAL PRIMARY KEY, + name text, + username text, + email text, + password text +); + +DROP TABLE IF EXISTS peeps; + +CREATE TABLE peeps ( + id SERIAL PRIMARY KEY, + time TIMESTAMP, + content text, + user_id int, + constraint fk_user foreign key(user_id) + references users(id) + on delete cascade +); diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 252747d899..d5d9b36eff 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -15,3 +15,111 @@ puts "\e[33mTry it now! Just run: rubocop\e[0m" end end + +require 'database_connection' + +ENV['ENV'] = 'test' + +DatabaseConnection.connect + +# This file was generated by the `rspec --init` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# The generated `.rspec` file contains `--require spec_helper` which will cause +# this file to always be loaded, without a need to explicitly require it in any +# files. +# +# Given that it is always loaded, you are encouraged to keep this file as +# light-weight as possible. Requiring heavyweight dependencies from this file +# will add to the boot time of your test suite on EVERY test run, even for an +# individual file that may not need all of that loaded. Instead, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need +# it. +# +# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration + +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true + end + + # This option will default to `:apply_to_host_groups` in RSpec 4 (and will + # have no way to turn it off -- the option exists only for backwards + # compatibility in RSpec 3). It causes shared context metadata to be + # inherited by the metadata hash of host groups and examples, rather than + # triggering implicit auto-inclusion in groups with matching metadata. + config.shared_context_metadata_behavior = :apply_to_host_groups + +# The settings below are suggested to provide a good initial experience +# with RSpec, but feel free to customize to your heart's content. +=begin + # This allows you to limit a spec run to individual examples or groups + # you care about by tagging them with `:focus` metadata. When nothing + # is tagged with `:focus`, all examples get run. RSpec also provides + # aliases for `it`, `describe`, and `context` that include `:focus` + # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + config.filter_run_when_matching :focus + + # Allows RSpec to persist some state between runs in order to support + # the `--only-failures` and `--next-failure` CLI options. We recommend + # you configure your source control system to ignore this file. + config.example_status_persistence_file_path = "spec/examples.txt" + + # Limits the available syntax to the non-monkey patched syntax that is + # recommended. For more details, see: + # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ + # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode + config.disable_monkey_patching! + + # This setting enables warnings. It's recommended, but in some cases may + # be too noisy due to issues in dependencies. + config.warnings = true + + # Many RSpec users commonly either run the entire suite or an individual + # file, and it's useful to allow more verbose output when running an + # individual spec file. + if config.files_to_run.one? + # Use the documentation formatter for detailed output, + # unless a formatter has already been configured + # (e.g. via a command-line flag). + config.default_formatter = "doc" + end + + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + config.profile_examples = 10 + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = :random + + # Seed global randomization in this process using the `--seed` CLI option. + # Setting this allows you to use `--seed` to deterministically reproduce + # test failures related to randomization by passing the same `--seed` value + # as the one that triggered the failure. + Kernel.srand config.seed +=end +end diff --git a/spec/user_repository_spec.rb b/spec/user_repository_spec.rb new file mode 100644 index 0000000000..1f9830f9b1 --- /dev/null +++ b/spec/user_repository_spec.rb @@ -0,0 +1,47 @@ +require 'user' +require 'user_repository' + +def reset_tables + seed_sql = File.read('spec/seeds/chitter_seeds.sql') + connection = PG.connect({ host: '127.0.0.1', dbname: 'chitter_challenge_test' }) + connection.exec(seed_sql) +end + +describe UserRepository do + before(:each) do + reset_tables + end + + it 'creates a new user' do + repo = UserRepository.new + + new_user = User.new + new_user.name = "Chewbacca" + new_user.username = "chewie123" + new_user.email = "chewie123@gmail.com" + new_user.password = "lightsaber" + user = repo.create(new_user) + + expect(user.name).to eq "Chewbacca" + expect(user.username).to eq "chewie123" + expect(user.email).to eq "chewie123@gmail.com" + end + + it 'finds a user by email' do + repo = UserRepository.new + user = repo.find_by_email('alice.smith@gmail.com') + + expect(user.id).to eq '1' + expect(user.name).to eq 'Alice Smith' + expect(user.username).to eq 'malice12' + end + + it 'finds a user by id' do + repo = UserRepository.new + user = repo.find('1') + + expect(user.id).to eq '1' + expect(user.name).to eq 'Alice Smith' + expect(user.username).to eq 'malice12' + end +end diff --git a/views/index.erb b/views/index.erb new file mode 100644 index 0000000000..e40c99c9e3 --- /dev/null +++ b/views/index.erb @@ -0,0 +1,25 @@ + + + + + Chitter Homepage + + +

Chitter Homepage

+ <% @complete_peep_info.each do |peep_info| %> +
+ Name: <%= peep_info[:name] %>
+ Username: @<%= peep_info[:username] %>
+ Content: <%= peep_info[:peep].content %>
+ Time posted: <%= peep_info[:peep].time %> +

+ <% end %> + +

+ Create a Peep + Sign up + Log in + Log out +

+ + diff --git a/views/login.erb b/views/login.erb new file mode 100644 index 0000000000..3f4ec090c3 --- /dev/null +++ b/views/login.erb @@ -0,0 +1,18 @@ + + + + + Log in + + +

Log in

+ + + + + + + +
+ + diff --git a/views/login_success.erb b/views/login_success.erb new file mode 100644 index 0000000000..eb88c6cdcf --- /dev/null +++ b/views/login_success.erb @@ -0,0 +1,14 @@ + + + + + Log in successful + + +

Log in successful!

+ +

+ Go back to the homepage. +

+ + diff --git a/views/logout.erb b/views/logout.erb new file mode 100644 index 0000000000..8d8b9c3b5e --- /dev/null +++ b/views/logout.erb @@ -0,0 +1,13 @@ + + + + + Log out + + +

Log out

+

+ +

+ + diff --git a/views/new_peep.erb b/views/new_peep.erb new file mode 100644 index 0000000000..a620f3fb48 --- /dev/null +++ b/views/new_peep.erb @@ -0,0 +1,16 @@ + + + + + Create a new Peep + + +

Create a new Peep

+
+ + + + +
+ + diff --git a/views/new_peep_success.erb b/views/new_peep_success.erb new file mode 100644 index 0000000000..8acc3ab096 --- /dev/null +++ b/views/new_peep_success.erb @@ -0,0 +1,13 @@ + + + + + New Peep created + + +

+ New Peep created! + Go back to the homepage. +

+ + diff --git a/views/signup.erb b/views/signup.erb new file mode 100644 index 0000000000..6f369b96af --- /dev/null +++ b/views/signup.erb @@ -0,0 +1,22 @@ + + + + + Sign up + + +

Sign up

+
+ + + + + + + + + + +
+ + diff --git a/views/signup_success.erb b/views/signup_success.erb new file mode 100644 index 0000000000..18a546c6a6 --- /dev/null +++ b/views/signup_success.erb @@ -0,0 +1,13 @@ + + + + + Sign up successful + + +

+ Sign up successful! + Go back to the homepage. +

+ + diff --git a/views/wrong_password.erb b/views/wrong_password.erb new file mode 100644 index 0000000000..1a74cb2e0f --- /dev/null +++ b/views/wrong_password.erb @@ -0,0 +1,13 @@ + + + + + Wrong password. + + +

Wrong password.

+

+ Try logging in again. +

+ +