diff --git a/Gemfile b/Gemfile index 1f478929..ed9f0190 100644 --- a/Gemfile +++ b/Gemfile @@ -3,3 +3,6 @@ ruby '2.0.0' gem 'rspec', '~> 2.14.1' gem 'pry-byebug' +gem 'sinatra', '~> 1.4.5' +gem 'pg' +gem 'sinatra-reloader' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index db516937..ebaa436a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,36 +1,60 @@ GEM remote: https://rubygems.org/ specs: + backports (3.6.4) + byebug (3.5.1) + columnize (~> 0.8) + debugger-linecache (~> 1.2) + slop (~> 3.6) coderay (1.1.0) - columnize (0.3.6) - debugger (1.6.5) - columnize (>= 0.3.1) - debugger-linecache (~> 1.2.0) - debugger-ruby_core_source (~> 1.3.1) + columnize (0.8.9) debugger-linecache (1.2.0) - debugger-ruby_core_source (1.3.1) diff-lcs (1.2.5) method_source (0.8.2) - pry (0.9.12.6) - coderay (~> 1.0) - method_source (~> 0.8) + multi_json (1.10.1) + pg (0.17.1) + pry (0.10.1) + coderay (~> 1.1.0) + method_source (~> 0.8.1) slop (~> 3.4) - pry-debugger (0.2.2) - debugger (~> 1.3) - pry (~> 0.9.10) + pry-byebug (2.0.0) + byebug (~> 3.4) + pry (~> 0.10) + rack (1.5.2) + rack-protection (1.5.3) + rack + rack-test (0.6.2) + rack (>= 1.0) rspec (2.14.1) rspec-core (~> 2.14.0) rspec-expectations (~> 2.14.0) rspec-mocks (~> 2.14.0) - rspec-core (2.14.7) + rspec-core (2.14.8) rspec-expectations (2.14.5) diff-lcs (>= 1.1.3, < 2.0) - rspec-mocks (2.14.5) - slop (3.4.7) + rspec-mocks (2.14.6) + sinatra (1.4.5) + rack (~> 1.4) + rack-protection (~> 1.4) + tilt (~> 1.3, >= 1.3.4) + sinatra-contrib (1.4.2) + backports (>= 2.0) + multi_json + rack-protection + rack-test + sinatra (~> 1.4.0) + tilt (~> 1.3) + sinatra-reloader (1.0) + sinatra-contrib + slop (3.6.0) + tilt (1.4.1) PLATFORMS ruby DEPENDENCIES - pry-debugger (~> 0.2.2) - rspec (~> 2.14.0) + pg + pry-byebug + rspec (~> 2.14.1) + sinatra (~> 1.4.5) + sinatra-reloader diff --git a/lib/library_plus.rb b/lib/library_plus.rb new file mode 100644 index 00000000..0ce23e58 --- /dev/null +++ b/lib/library_plus.rb @@ -0,0 +1,51 @@ +require 'pg' + +module Library + def self.create_db_connection(dbname) + PG.connect(host: 'localhost', dbname: dbname) + end + + def self.clear_db(db) + db.exec <<-SQL + DELETE FROM users; + DELETE FROM books; + DELETE FROM checkouts; + /* TODO: Clear rest of the tables (books, etc.) */ + SQL + end + + def self.create_tables(db) + db.exec <<-SQL + CREATE TABLE IF NOT EXISTS users( + id SERIAL PRIMARY KEY, + name VARCHAR + ); + CREATE TABLE IF NOT EXISTS books( + id SERIAL PRIMARY KEY, + title VARCHAR, + author VARCHAR, + status VARCHAR DEFAULT ('available') + ); + CREATE TABLE IF NOT EXISTS checkouts( + id SERIAL PRIMARY KEY, + user_id INTEGER, + book_id INTEGER, + status VARCHAR DEFAULT ('checked_out'), + created_at TIMESTAMP without time zone DEFAULT NOW()); + /* TODO: Create rest of the tables (books, etc.) */ + SQL + + end + + def self.drop_tables(db) + db.exec <<-SQL + DROP TABLE users; + DROP TABLE books; + DROP TABLE checkouts; + /* TODO: Drop rest of the tables (books, etc.) */ + SQL + end +end + +require_relative 'library_plus/book_repo' +require_relative 'library_plus/user_repo' diff --git a/lib/library_plus/book_repo.rb b/lib/library_plus/book_repo.rb new file mode 100644 index 00000000..0a84e82c --- /dev/null +++ b/lib/library_plus/book_repo.rb @@ -0,0 +1,86 @@ +module Library + class BookRepo + + def self.all(db) + db.exec("SELECT * FROM books;").to_a + end + + def self.save(db, book_data) + if book_data['id'] + sql = %q[UPDATE books + SET title = $1 AND SET author = $2 + WHERE id = $3 + RETURNING *; + ] + result = db.exec(sql, [book_data['title'], book_data['author'], book_data['id']]) + else + sql = %q[INSERT INTO books (title, author) + VALUES ($1, $2) + RETURNING *; + ] + result = db.exec(sql, [book_data['title'], book_data['author']]) + end + result.first + end + + def self.status(db, book_id) + sql = %q[UPDATE books + SET status = $1 + WHERE id = $2 + RETURNING *; + ] + book = self.find(db, book_id) + if book['status'] == 'available' + result = db.exec(sql, ['checked_out', book_id]) + else + result = db.exec(sql, ['available', book_id]) + end + result.first + end + + def self.find(db, book_id) + result = db.exec("SELECT * FROM books WHERE id = $1;", [book_id]) + result.first + end + + def self.destroy(db, book_id) + db.exec("DELETE FROM books WHERE id = $1;", [book_id]) + end + + def self.check_out_book(db, user_id, book_id) + sql = %q[INSERT INTO checkouts (user_id, book_id) + VALUES ($1, $2) + RETURNING *; + ] + result = db.exec(sql, [user_id, book_id]) + result.first + end + + def self.check_in_book(db, book_id) + sql = %q[UPDATE checkouts + SET status = 'available' + WHERE book_id = $1 + RETURNING *; + ] + result = db.exec(sql, [book_id]) + result.first + end + + def self.check_out_history_book(db, book_id) + sql = %q[SELECT * from checkouts + WHERE book_id = $1 + ORDER By created_at; + ] + result = db.exec(sql, [book_id]) + end + + def self.check_out_history_user(db, user_id) + sql = %q[SELECT * from checkouts + WHERE user_id = $1 + ORDER BY created_at; + ] + result = db.exec(sql, [book_id]) + end + + end +end \ No newline at end of file diff --git a/lib/library_plus/user_repo.rb b/lib/library_plus/user_repo.rb new file mode 100644 index 00000000..1c8616dd --- /dev/null +++ b/lib/library_plus/user_repo.rb @@ -0,0 +1,35 @@ +module Library + class UserRepo + + def self.all(db) + # Other code should not have to deal with the PG:Result. + # Therefore, convert the results into a plain array. + db.exec("SELECT * FROM users").to_a + end + + def self.find(db, user_id) + result = db.exec("SELECT * FROM users WHERE id = $1;", [user_id]) + result.first + # TODO: Insert SQL statement + end + + def self.save(db, user_data) + if user_data['id'] + result = db.exec("UPDATE users SET name = $1 WHERE id = $2 RETURNING *;",[user_data['name'], user_data['id']]) + # TODO: Update SQL statement + else + result = db.exec("INSERT INTO users (name) VALUES ($1) RETURNING *;", [user_data['name']]) + # TODO: Insert SQL statement + end + result.first + + end + + def self.destroy(db, user_id) + db.exec("DELETE FROM users WHERE id = $1;", [user_id]) + + # TODO: Delete SQL statement + end + + end +end \ No newline at end of file diff --git a/server.rb b/server.rb new file mode 100644 index 00000000..71532d3f --- /dev/null +++ b/server.rb @@ -0,0 +1,83 @@ +require 'sinatra' +require 'sinatra/reloader' +require './lib/library_plus' + +set :bind, '0.0.0.0' # This is needed for Vagrant + +get '/' do + erb :index +end + +get '/users' do + db = Library.create_db_connection('library_dev') + @users = Library::UserRepo.all(db) + erb :"users/index" +end + +post '/users' do + db = Library.create_db_connection('library_dev') + if params['user_name'] + user = {'name' => params["user_name"]} + Library::UserRepo.save(db, user) + elsif params['user_id'] + id = params["user_id"] + Library::UserRepo.destroy(db, id) + end + redirect to('/users') +end + +get '/users/:id' do + id = params['id'] + db = Library.create_db_connection('library_dev') + @user = Library::UserRepo.find(db, id) + erb :"users/show" +end + +get '/books' do + db = Library.create_db_connection('library_dev') + @books = Library::BookRepo.all(db) + erb :"books/index" +end + +post '/books' do + db = Library.create_db_connection('library_dev') + puts params + if params['book_title'] && params['book_author'] + user = {'title' => params['book_title'], 'author' => params['book_author']} + Library::BookRepo.save(db, user) + elsif params['book_id'] + id = params["book_id"] + Library::BookRepo.destroy(db, id) + end + redirect to('/books') +end + +get '/books/:id' do + id = params['id'] + db = Library.create_db_connection('library_dev') + @book = Library::BookRepo.find(db, id) + @users = Library::UserRepo.all(db) + @history = Library::BookRepo.check_out_history_book(db, params['id']) + erb :"books/show" +end + +post '/books/:id/checkout' do + db = Library.create_db_connection('library_dev') + Library::BookRepo.check_out_book(db, params['user_id'], params['id']) + Library::BookRepo.status(db, params['id']) + redirect to('/books/' + params['id']) +end + +post '/books/:id/checkin' do + db = Library.create_db_connection('library_dev') + Library::BookRepo.check_in_book(db, params['id']) + Library::BookRepo.status(db, params['id']) + redirect to('/books/' + params['id']) +end + +# get '/books/:id/history' do +# puts params['id'] +# db = Library.create_db_connection('library_dev') +# @history = Library::BookRepo.check_out_history_book(db, params['id']) +# erb :"books/show" +# end \ No newline at end of file diff --git a/spec/.gitkeep b/spec/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/spec/repos/user_repo_spec.rb b/spec/repos/user_repo_spec.rb new file mode 100644 index 00000000..d907dc5c --- /dev/null +++ b/spec/repos/user_repo_spec.rb @@ -0,0 +1,65 @@ +require 'spec_helper' + +describe Library::UserRepo do + + def user_count(db) + db.exec("SELECT COUNT(*) FROM users")[0]["count"].to_i + end + + let(:db) { Library.create_db_connection('library_test') } + + before(:each) do + Library.clear_db(db) + end + + it "gets all users" do + db.exec("INSERT INTO users (name) VALUES ($1)", ["Alice"]) + db.exec("INSERT INTO users (name) VALUES ($1)", ["Bob"]) + + users = Library::UserRepo.all(db) + expect(users).to be_a Array + expect(users.count).to eq 2 + + names = users.map {|u| u['name'] } + expect(names).to include "Alice", "Bob" + end + + it "creates users" do + expect(user_count(db)).to eq 0 + + user = Library::UserRepo.save(db, { 'name' => "Alice" }) + expect(user['id']).to_not be_nil + expect(user['name']).to eq "Alice" + + # Check for persistence + expect(user_count(db)).to eq 1 + + user = db.exec("SELECT * FROM users")[0] + expect(user['name']).to eq "Alice" + end + + it "finds users" do + user = Library::UserRepo.save(db, { 'name' => "Alice" }) + retrieved_user = Library::UserRepo.find(db, user['id']) + expect(retrieved_user['name']).to eq "Alice" + end + + it "updates users" do + user1 = Library::UserRepo.save(db, { 'name' => "Alice" }) + user2 = Library::UserRepo.save(db, { 'id' => user1['id'], 'name' => "Alicia" }) + expect(user2['id']).to eq(user1['id']) + expect(user2['name']).to eq "Alicia" + + # Check for persistence + user3 = Library::UserRepo.find(db, user1['id']) + expect(user3['name']).to eq "Alicia" + end + + it "destroys users" do + user = Library::UserRepo.save(db, { 'name' => "Alice" }) + expect(user_count(db)).to eq 1 + + Library::UserRepo.destroy(db, user['id']) + expect(user_count(db)).to eq 0 + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 00000000..962938ee --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,13 @@ +require 'library_plus' + +RSpec.configure do |config| + config.treat_symbols_as_metadata_keys_with_true_values = true + config.run_all_when_everything_filtered = true + config.filter_run :focus + + # 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' +end diff --git a/views/index.erb b/views/index.erb new file mode 100644 index 00000000..9c1f103a --- /dev/null +++ b/views/index.erb @@ -0,0 +1,6 @@ +
Welcome to your freedom!
+ +Manage Users! +Manage Books! \ No newline at end of file