From c0968a449bcc48a795da054a4a12d5cadc1c0fae Mon Sep 17 00:00:00 2001 From: Chris Toplisek Date: Fri, 31 Mar 2023 12:13:22 +0100 Subject: [PATCH 1/4] project setup --- DR_shop_manager_challenge | 385 +++++++++++++++++++++++++++++++ app.rb | 0 lib/database_connection.rb | 24 ++ lib/item.rb | 0 lib/item_repository.rb | 0 lib/order.rb | 0 lib/order_repository.rb | 0 spec/item_repository_spec.rb | 15 ++ spec/order_repository_spec.rb | 13 ++ spec/seeds_shop_manager.sql | 20 ++ spec/seeds_shop_manager_test.sql | 0 spec/spec_helper.rb | 3 + 12 files changed, 460 insertions(+) create mode 100644 DR_shop_manager_challenge create mode 100644 app.rb create mode 100644 lib/database_connection.rb create mode 100644 lib/item.rb create mode 100644 lib/item_repository.rb create mode 100644 lib/order.rb create mode 100644 lib/order_repository.rb create mode 100644 spec/item_repository_spec.rb create mode 100644 spec/order_repository_spec.rb create mode 100644 spec/seeds_shop_manager.sql create mode 100644 spec/seeds_shop_manager_test.sql diff --git a/DR_shop_manager_challenge b/DR_shop_manager_challenge new file mode 100644 index 00000000..8bbea788 --- /dev/null +++ b/DR_shop_manager_challenge @@ -0,0 +1,385 @@ +# social_network Deign Recipe (DR) + +## project setup + +```bash +rvm get stable +rvm use ruby --latest --install --default +gem install bundler +bundle init +bundle add rspec +rspec --init +mkdir lib +bundle add pg +touch lib/database_connection.rb +touch spec/seeds.sql +touch app.rb +``` +### DatabaseConnection class + +```ruby + # 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(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 +``` +### Add connect method to the spec/helper.rb + +```ruby + # file: spec/spec_helper.rb + + require 'database_connection' + + # Make sure this connects to your test database + # (its name should end with '_test') + DatabaseConnection.connect('your_database_name_test') +``` + +## 1. Design and create the Table + +### Extract nouns from the user stories or specification + +As a shop manager +So I can know which items I have in stock +I want to keep a list of my shop items with their name and unit price. + +As a shop manager +So I can know which items I have in stock +I want to know which quantity (a number) I have for each item. + +As a shop manager +So I can manage items +I want to be able to create a new item. + +As a shop manager +So I can know which orders were made +I want to keep a list of orders with their customer name. + +As a shop manager +So I can know which orders were made +I want to assign each order to their corresponding item. + +As a shop manager +So I can know which orders were made +I want to know on which date an order was placed. + +As a shop manager +So I can manage orders +I want to be able to create a new order. + +``` +(nouns) + +shop_item, item_name, price, item_quantity, list_of_orders, customer_name, order_id, order_date +``` + +## 2. Infer the Table Name and Columns + +Put the different nouns in this table. Replace the example with your own nouns. + +| Record | Properties | +| --------------------- | ------------------ | +| item | name, price, quantity +| order | customer, date, item_id + +1. Name of the first table (always plural): `items` + + Column names: `name`, `price`, `quantity` + +2. Name of the second table (always plural): `orders` + + Column names: `customer`, `date`, `item_id` + +## 3. Decide the column types. + +``` +Table: items +id: SERIAL +name: text +price: float +quantity: int + +Table: orders +id: SERIAL +customer: text +date: date +item_id: int +``` + +## 4. Decide on The Tables Relationship + +Most of the time, you'll be using a **one-to-many** relationship, and will need a **foreign key** on one of the two tables. + +To decide on which one, answer these two questions: + +1. Can one ITEMS have many ORDERS? (Yes) +2. Can one ORDERS have many ITEMS? (No) + +You'll then be able to say that: + +1. **[ITEMS] has many [ORDERS]** +2. And on the other side, **[ORDERS] belongs to [ITEMS]** +3. In that case, the foreign key is in the table [ORDERS] + +Replace the relevant bits in this example with your own: + +## 4. Write the SQL. + +```sql +-- file: albums_table.sql +-- Replace the table name, columm names and types. +-- Create the table without the foreign key first. +CREATE TABLE items ( + id SERIAL PRIMARY KEY, + name text, + price float, + quantity int +); + +-- Then the table with the foreign key first. +CREATE TABLE posts ( + id SERIAL PRIMARY KEY, + customer text, + date date, +-- The foreign key name is always {other_table_singular}_id + item_id int, + constraint fk_item foreign key(item_id) + references items(id) + on delete cascade +); + +``` + +## 5. Create the tables. + +```bash +1 createdb shop_manager +2 createdb shop_manager_test +``` + +```bash +psql -h 127.0.0.1 shop_manager < seeds_shop_manager.sql +psql -h 127.0.0.1 shop_manager_test < seeds_shop_manager_test.sql +``` + +## Make test Database for spec file + +Test Database Name: seeds_social_network_test.sql + +```sql + +# table names: 'items' +TRUNCATE TABLE items RESTART IDENTITY; + +INSERT INTO items ('name', 'price', 'quantity') VALUES ('hammer', 5.99, 10); +INSERT INTO items ('name', 'price', 'quantity') VALUES ('glue', 2.99, 5); + +# table names: 'orders' +TRUNCATE TABLE table_name RESTART IDENTITY; + +INSERT INTO table_name ('customer', 'date', 'item_id') VALUES ('chris', '08-Jan-1999', 1); +INSERT INTO table_name ('customer', 'date', 'item_id') VALUES ('tom', '09-Jan-2003', 1); + +``` + +## 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: items + +# Model class +# (in lib/item.rb) +class Item +end + +# Repository class +# (in lib/item_repository.rb) +class ItemRepository +end +``` +```ruby +Table name: orders + +# Model class +# (in lib/order.rb) +class Order +end + +# Repository class +# (in lib/order_repository.rb) +class OrderRepository +end +``` +## 4. Implement the Model class + +```ruby +Table name: items +# Model class +# (in lib/item.rb) +class Item + attr_accessor :id, :name, :price, :quantity +end + +Table name: orders +# Model class +# (in lib/order.rb) +class Order + attr_accessor :id, :customer, :date, :item_id +end +``` + +## 5. Define the Repository Class interface + +```ruby +Table name: items +# Repository class +# (in lib/iteem_repository.rb) +class StudentRepository + def all + sql = 'SELECT id, name, price, quantity FROM items;' + result = DatabaseConnection.exec_params(sql,[]) + end + + def find(id) + sql = 'SELECT id, name, price, quantity FROM items; WHERE id = $1;' + params = [id] + result = DatabaseConnection.exec_params(sql, params) + end + + def create(item) + sql = 'INSERT INTO items (name, price, quantity) VALUES ($1, $2, $3);' + params = [item.username, item.price, item.quantity] + result = DatabaseConnection.exec_params(sql, params) + end + + def delete(id) + sql = 'DELETE FROM items WHERE (id = $1);' + params = [id] + result = DatabaseConnection.exec_params(sql, params) + end + + def update(update) + sql = 'UPDATE items SET (quantity = $1) WHERE (id = $2);' + params = [update.quantity, update.id] + result = DatabaseConnection.exec_pramas(sql, params) + 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. + +```ruby + +# 1 +repo = UserAccountRepository.new +user = repo.all + +user.length # => +user[0].id # => +user[0].username # => +user[0].email # => + +# 2 +repo = UserAccountRepository.new +user = repo.find(1) + +user.id # => +user.username # => +user.email # => + +# 3 +repo = UserAccountRepository.new + +user = UserAccount.new +user.username = 'Chris' +user.email = 'chris@gmail.com' + +repo.create(user) + +all_users = repo.all + +all_users.last.username # => 'Chris' +all_users.last.email # => 'chris@gmail.com' + +# 4 +repo = UserAccountRepository.new + +delete_id = 1 +repo.delete(delete_id) + +all_user = repo.all +all_user.length # => 1 +all_user.first.id # => 2 + +# 5 +repo = UserAccountRepository.new + +user = repo.find(1) + +user.username = 'Toppy' +user.email = 'chris@gmail.com' + +repo.update(user) + +new_user = repo.find(1) + + +``` +## 7. Reload the SQL seeds before each test run + +```ruby + +# file: spec/item_repository_spec.rb + +def reset_user_account_table + seed_sql = File.read('spec/seeds_shop_manager_test.sql') + connection = PG.connect({ host: '127.0.0.1', dbname: 'shop_manager_test' }) + connection.exec(seed_sql) +end + +describe UserAccountRepository do + before(:each) do + reset_user_accounts_table + 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._ + + + +--- + +**How was this resource?** +[😫](https://airtable.com/shrUJ3t7KLMqVRFKR?prefill_Repository=makersacademy%2Fdatabases&prefill_File=resources%2Frepository_class_recipe_template.md&prefill_Sentiment=😫) [😕](https://airtable.com/shrUJ3t7KLMqVRFKR?prefill_Repository=makersacademy%2Fdatabases&prefill_File=resources%2Frepository_class_recipe_template.md&prefill_Sentiment=😕) [😐](https://airtable.com/shrUJ3t7KLMqVRFKR?prefill_Repository=makersacademy%2Fdatabases&prefill_File=resources%2Frepository_class_recipe_template.md&prefill_Sentiment=😐) [🙂](https://airtable.com/shrUJ3t7KLMqVRFKR?prefill_Repository=makersacademy%2Fdatabases&prefill_File=resources%2Frepository_class_recipe_template.md&prefill_Sentiment=🙂) [😀](https://airtable.com/shrUJ3t7KLMqVRFKR?prefill_Repository=makersacademy%2Fdatabases&prefill_File=resources%2Frepository_class_recipe_template.md&prefill_Sentiment=😀) +Click an emoji to tell us. + + \ No newline at end of file diff --git a/app.rb b/app.rb new file mode 100644 index 00000000..e69de29b diff --git a/lib/database_connection.rb b/lib/database_connection.rb new file mode 100644 index 00000000..f8db6758 --- /dev/null +++ b/lib/database_connection.rb @@ -0,0 +1,24 @@ + # 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(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 \ No newline at end of file diff --git a/lib/item.rb b/lib/item.rb new file mode 100644 index 00000000..e69de29b diff --git a/lib/item_repository.rb b/lib/item_repository.rb new file mode 100644 index 00000000..e69de29b diff --git a/lib/order.rb b/lib/order.rb new file mode 100644 index 00000000..e69de29b diff --git a/lib/order_repository.rb b/lib/order_repository.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/item_repository_spec.rb b/spec/item_repository_spec.rb new file mode 100644 index 00000000..66f382d5 --- /dev/null +++ b/spec/item_repository_spec.rb @@ -0,0 +1,15 @@ +# file: spec/item_repository_spec.rb + +requie 'item_repository' + +def reset_item_table + seed_sql = File.read('spec/seeds_shop_manager_test.sql') + connection = PG.connect({ host: '127.0.0.1', dbname: 'shop_manager_test' }) + connection.exec(seed_sql) +end + +describe ItemRepository do + before(:each) do + reset_item_table + end +end \ No newline at end of file diff --git a/spec/order_repository_spec.rb b/spec/order_repository_spec.rb new file mode 100644 index 00000000..c58fc5c4 --- /dev/null +++ b/spec/order_repository_spec.rb @@ -0,0 +1,13 @@ +# file: spec/order_repository_spec.rb + +def reset_order_table + seed_sql = File.read('spec/seeds_shop_manager_test.sql') + connection = PG.connect({ host: '127.0.0.1', dbname: 'shop_manager_test' }) + connection.exec(seed_sql) +end + +describe OrderRepository do + before(:each) do + reset_order_table + end +end diff --git a/spec/seeds_shop_manager.sql b/spec/seeds_shop_manager.sql new file mode 100644 index 00000000..46c38800 --- /dev/null +++ b/spec/seeds_shop_manager.sql @@ -0,0 +1,20 @@ +-- file: albums_table.sql + +CREATE TABLE items ( + id SERIAL PRIMARY KEY, + name text, + price float, + quantity int +); + +-- Then the table with the foreign key first. +CREATE TABLE posts ( + id SERIAL PRIMARY KEY, + customer text, + date date, +-- The foreign key name is always {other_table_singular}_id + order_id int, + constraint fk_order foreign key(order_id) + references items(id) + on delete cascade +); \ No newline at end of file diff --git a/spec/seeds_shop_manager_test.sql b/spec/seeds_shop_manager_test.sql new file mode 100644 index 00000000..e69de29b diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 252747d8..39e678f7 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,8 @@ require 'simplecov' require 'simplecov-console' +require 'database_connection' + +DatabaseConnection.connect('shop_manager_test') SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([ SimpleCov::Formatter::Console, From 3692d14decf28391bce7b8ac1a37b372081e53dc Mon Sep 17 00:00:00 2001 From: Chris Toplisek Date: Fri, 31 Mar 2023 16:31:37 +0100 Subject: [PATCH 2/4] project started and file system added --- DR_shop_manager_challenge | 114 +++++++++++++++++-------------- lib/item.rb | 3 + lib/item_repository.rb | 23 +++++++ lib/order_repository.rb | 3 + spec/item_repository_spec.rb | 13 +++- spec/order_repository_spec.rb | 2 + spec/seeds_shop_manager.sql | 7 +- spec/seeds_shop_manager_test.sql | 11 +++ 8 files changed, 122 insertions(+), 54 deletions(-) diff --git a/DR_shop_manager_challenge b/DR_shop_manager_challenge index 8bbea788..9ff71b56 100644 --- a/DR_shop_manager_challenge +++ b/DR_shop_manager_challenge @@ -179,7 +179,7 @@ CREATE TABLE posts ( ```bash psql -h 127.0.0.1 shop_manager < seeds_shop_manager.sql -psql -h 127.0.0.1 shop_manager_test < seeds_shop_manager_test.sql +psql -h 127.0.0.1 shop_manager_test < seeds_shop_manager.sql ``` ## Make test Database for spec file @@ -189,7 +189,9 @@ Test Database Name: seeds_social_network_test.sql ```sql # table names: 'items' -TRUNCATE TABLE items RESTART IDENTITY; +TRUNCATE TABLE items RESTART IDENTITY CASCADE; + +--cannot truncate a table referenced in a foreign key constraint-- Solution is to as (CASCADE) INSERT INTO items ('name', 'price', 'quantity') VALUES ('hammer', 5.99, 10); INSERT INTO items ('name', 'price', 'quantity') VALUES ('glue', 2.99, 5); @@ -199,9 +201,7 @@ TRUNCATE TABLE table_name RESTART IDENTITY; INSERT INTO table_name ('customer', 'date', 'item_id') VALUES ('chris', '08-Jan-1999', 1); INSERT INTO table_name ('customer', 'date', 'item_id') VALUES ('tom', '09-Jan-2003', 1); - ``` - ## 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. @@ -252,11 +252,15 @@ end ## 5. Define the Repository Class interface +#REPEAT BASE ON TABLES MAIN PROGRAM WANTS TO EQUIRY DATA!!! + +number of tables 2 (items) & (orders) + ```ruby Table name: items # Repository class -# (in lib/iteem_repository.rb) -class StudentRepository +# (in lib/Item_repository.rb) +class ItemRepository def all sql = 'SELECT id, name, price, quantity FROM items;' result = DatabaseConnection.exec_params(sql,[]) @@ -288,84 +292,94 @@ class StudentRepository end ``` -## 6. Write Test Examples +## 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. -```ruby +#REPEAT BASE ON TABLES MAIN PROGRAM WANTS TO EQUIRY DATA!!! + +number of tables 2 (items) & (orders) +```ruby # 1 -repo = UserAccountRepository.new -user = repo.all +repo = ItemRepository.new +item = repo.all -user.length # => -user[0].id # => -user[0].username # => -user[0].email # => +item.length # => 2 +item[0].id # => 1 +item[0].name # => hammer +item[0].price # => 5.99 +item[0].quantity # => 10 # 2 -repo = UserAccountRepository.new -user = repo.find(1) +repo = ItemRepository.new +user = repo.find(2) -user.id # => -user.username # => -user.email # => +item.length # => 1 +item[0].id # => 2 +item[0].name # => glue +item[0].price # => 2.99 +item[0].quantity # => 5 # 3 -repo = UserAccountRepository.new - -user = UserAccount.new -user.username = 'Chris' -user.email = 'chris@gmail.com' +repo = ItemRepository.new -repo.create(user) +item = ItemAccount.new +item.name = 'pencil' +item.price = 1.99 +item.quantity = 1 -all_users = repo.all +repo.create(item) +create_item = repo.all -all_users.last.username # => 'Chris' -all_users.last.email # => 'chris@gmail.com' +create_item.last.name # => pencil +create_item.last.price # => 1.99 # 4 -repo = UserAccountRepository.new +repo = ItemRepository.new delete_id = 1 repo.delete(delete_id) -all_user = repo.all -all_user.length # => 1 -all_user.first.id # => 2 +delete_item = repo.all +delete_item.length # => 1 +delete_item.first.id # => 2 # 5 -repo = UserAccountRepository.new +repo = ItemRepository.new -user = repo.find(1) +item = repo.find(1) -user.username = 'Toppy' -user.email = 'chris@gmail.com' +item.name = 'steal hammer' +item.price = 8.99 -repo.update(user) - -new_user = repo.find(1) +repo.update(item) +updated_item = repo.find(1) +updated_item.name # => steal hammer +updated_item.price # => 8.99 ``` ## 7. Reload the SQL seeds before each test run -```ruby +#REPEAT BASE ON TABLES MAIN PROGRAM WANTS TO EQUIRY DATA!!! + +number of tables 2 (items) & (orders) +```ruby # file: spec/item_repository_spec.rb -def reset_user_account_table - seed_sql = File.read('spec/seeds_shop_manager_test.sql') - connection = PG.connect({ host: '127.0.0.1', dbname: 'shop_manager_test' }) - connection.exec(seed_sql) +def reset_order_table + seed_sql = File.read('spec/seeds_shop_manager_test.sql') + connection = PG.connect({ host: '127.0.0.1', dbname: 'shop_manager_test' }) + connection.exec(seed_sql) +end + +describe OrderRepository do + before(:each) do + reset_order_table + end end - -describe UserAccountRepository do - before(:each) do - reset_user_accounts_table - end - # (your tests will go here). end ``` diff --git a/lib/item.rb b/lib/item.rb index e69de29b..6ca3f571 100644 --- a/lib/item.rb +++ b/lib/item.rb @@ -0,0 +1,3 @@ +class Item + attr_accessor :id, :name, :price, :quantity +end \ No newline at end of file diff --git a/lib/item_repository.rb b/lib/item_repository.rb index e69de29b..f03168e6 100644 --- a/lib/item_repository.rb +++ b/lib/item_repository.rb @@ -0,0 +1,23 @@ +require 'item' + +class ItemRepository + def all + + items = [] + + sql = 'SELECT id, name, price, quantity FROM items;' + result = DatabaseConnection.exec_params(sql,[]) + + item = Item.new + result.each do |sql_data| + + item.id = sql_data['id'] + item.name = sql_data['name'] + item.price = sql_data['price'] + item.quantity = sql_data['quantity'] + + items << item + end + return items + end +end \ No newline at end of file diff --git a/lib/order_repository.rb b/lib/order_repository.rb index e69de29b..40975f0f 100644 --- a/lib/order_repository.rb +++ b/lib/order_repository.rb @@ -0,0 +1,3 @@ +class OrderRepository + +end \ No newline at end of file diff --git a/spec/item_repository_spec.rb b/spec/item_repository_spec.rb index 66f382d5..fe985b46 100644 --- a/spec/item_repository_spec.rb +++ b/spec/item_repository_spec.rb @@ -1,6 +1,6 @@ # file: spec/item_repository_spec.rb -requie 'item_repository' +require './lib/item_repository' def reset_item_table seed_sql = File.read('spec/seeds_shop_manager_test.sql') @@ -12,4 +12,15 @@ def reset_item_table before(:each) do reset_item_table end + + it 'tests the the all method' do + repo = ItemRepository.new + item = repo.all + + expect(item.length).to eq 2 + expect(item[1].id).to eq '2' + expect(item[1].name).to eq 'glue' + expect(item[1].price).to eq '2.99' + expect(item[1].quantity).to eq '5' + end end \ No newline at end of file diff --git a/spec/order_repository_spec.rb b/spec/order_repository_spec.rb index c58fc5c4..48e7214c 100644 --- a/spec/order_repository_spec.rb +++ b/spec/order_repository_spec.rb @@ -1,5 +1,7 @@ # file: spec/order_repository_spec.rb +require 'order_repository' + def reset_order_table seed_sql = File.read('spec/seeds_shop_manager_test.sql') connection = PG.connect({ host: '127.0.0.1', dbname: 'shop_manager_test' }) diff --git a/spec/seeds_shop_manager.sql b/spec/seeds_shop_manager.sql index 46c38800..a1f3b0b3 100644 --- a/spec/seeds_shop_manager.sql +++ b/spec/seeds_shop_manager.sql @@ -1,4 +1,4 @@ --- file: albums_table.sql +-- file: /Users/christoplisek/Projects_Makers/Datab/spec/seeds_shop_manager.sql CREATE TABLE items ( id SERIAL PRIMARY KEY, @@ -8,7 +8,7 @@ CREATE TABLE items ( ); -- Then the table with the foreign key first. -CREATE TABLE posts ( +CREATE TABLE orders ( id SERIAL PRIMARY KEY, customer text, date date, @@ -17,4 +17,5 @@ CREATE TABLE posts ( constraint fk_order foreign key(order_id) references items(id) on delete cascade -); \ No newline at end of file +); + diff --git a/spec/seeds_shop_manager_test.sql b/spec/seeds_shop_manager_test.sql index e69de29b..98e515d4 100644 --- a/spec/seeds_shop_manager_test.sql +++ b/spec/seeds_shop_manager_test.sql @@ -0,0 +1,11 @@ +-- database_name shop_manager_test +-- file (./spec/seeds_shop_manager_test.sql) + +TRUNCATE TABLE items RESTART IDENTITY CASCADE; +TRUNCATE TABLE orders RESTART IDENTITY; + +INSERT INTO items (name, price, quantity) VALUES ('hammer', 5.99, 10); +INSERT INTO items (name, price, quantity) VALUES ('glue', 2.99, 5); + +INSERT INTO orders (customer, date, order_id) VALUES ('chris', '08-Jan-1999', 1); +INSERT INTO orders (customer, date, order_id) VALUES ('tom', '09-Jan-2003', 1); From df46fbea2752d8ffea9c80db4ba3d01dade2a705 Mon Sep 17 00:00:00 2001 From: Chris Toplisek Date: Sat, 1 Apr 2023 21:09:41 +0100 Subject: [PATCH 3/4] finished solo project --- DR_shop_manager_challenge | 8 +-- app.rb | 111 ++++++++++++++++++++++++++++++++++ lib/item_repository.rb | 30 +++++++-- lib/menu.rb | 10 +++ lib/order.rb | 3 + lib/order_repository.rb | 25 +++++++- spec/app_spec.rb | 95 +++++++++++++++++++++++++++++ spec/item_repository_spec.rb | 37 ++++++++++-- spec/order_repository_spec.rb | 34 +++++++++++ 9 files changed, 339 insertions(+), 14 deletions(-) create mode 100644 lib/menu.rb create mode 100644 spec/app_spec.rb diff --git a/DR_shop_manager_challenge b/DR_shop_manager_challenge index 9ff71b56..617818c4 100644 --- a/DR_shop_manager_challenge +++ b/DR_shop_manager_challenge @@ -274,7 +274,7 @@ class ItemRepository def create(item) sql = 'INSERT INTO items (name, price, quantity) VALUES ($1, $2, $3);' - params = [item.username, item.price, item.quantity] + params = [item.name, item.price, item.quantity] result = DatabaseConnection.exec_params(sql, params) end @@ -296,7 +296,7 @@ end Write Ruby code that defines the expected behaviour of the Repository class, following your design from the table written in step 5. -#REPEAT BASE ON TABLES MAIN PROGRAM WANTS TO EQUIRY DATA!!! +# REPEAT BASE ON TABLES MAIN PROGRAM WANTS TO EQUIRY DATA!!! number of tables 2 (items) & (orders) @@ -324,7 +324,7 @@ item[0].quantity # => 5 # 3 repo = ItemRepository.new -item = ItemAccount.new +item = Item.new item.name = 'pencil' item.price = 1.99 item.quantity = 1 @@ -362,7 +362,7 @@ updated_item.price # => 8.99 ``` ## 7. Reload the SQL seeds before each test run -#REPEAT BASE ON TABLES MAIN PROGRAM WANTS TO EQUIRY DATA!!! +# REPEAT BASE ON TABLES MAIN PROGRAM WANTS TO EQUIRY DATA!!! number of tables 2 (items) & (orders) diff --git a/app.rb b/app.rb index e69de29b..c8a4df29 100644 --- a/app.rb +++ b/app.rb @@ -0,0 +1,111 @@ +require_relative './lib/database_connection' +require_relative './lib/item_repository' +require_relative './lib/order_repository' + +class Application + + # The Application class initializer + # takes four arguments: + # * The database name to call `DatabaseConnection.connect` + # * the Kernel object as `io` (so we can mock the IO in our tests) + # * the AlbumRepository object (or a double of it) + # * the ArtistRepository object (or a double of it) + def initialize(database_name, io, order_repository, item_repository) + DatabaseConnection.connect(database_name) + @io = io + @order_repository = order_repository + @item_repository = item_repository + # binding.irb + end + + def print_program_menu + @io.puts 'Welcome to the shop manager!' + @io.puts '' + @io.puts 'What do you want to do?' + @io.puts ' 1 = List all shop items' + @io.puts ' 2 = Create a new item' + @io.puts ' 3 = List all orders' + @io.puts ' 4 = Create a new order' + @io.puts ' 9 = Exit' + end + + def shop_manager_program + code = @io.gets.to_i + # binding.irb + case code + when 1 + list_all_shop_items + when 2 + create_a_new_item + when 3 + list_all_orders + when 4 + create_a_new_order + when 9 + exit + else + @io.puts "I don't know what you meant, try again" + end + end + + def list_all_shop_items + all_items = @item_repository.all + all_items.each do | data | + @io.puts "#{data.id} - #{data.name}: #{data.price}: #{data.quantity}" + end + end + + def create_a_new_item + created_item = Item.new + @io.puts 'Enter name of item: ' + created_item.name = @io.gets.chomp + @io.puts 'Enter price of item: ' + created_item.price = @io.gets.chomp + @io.puts 'Enter quantity of item: ' + created_item.quantity = @io.gets.chomp + # binding.irb + + @item_repository.create(created_item) + end + + def list_all_orders + all_orders = @order_repository.all + all_orders.each do | data | + @io.puts "#{data.id} - #{data.customer}: #{data.date}: #{data.order_id}" + end + end + + def create_a_new_order + created_order = Order.new + @io.puts 'Enter customer order name: ' + created_order.customer = @io.gets.chomp + @io.puts 'Enter order date (YYYY-MM-DD): ' + created_order.date = @io.gets.chomp + @io.puts 'Enter order_id of item: ' + created_order.order_id = @io.gets.chomp + # binding.irb + + @order_repository.create(created_order) + end + + def run + loop do + print_program + shop_manager_program + end + end +end + +# Don't worry too much about this if statement. It is basically saying "only +# run the following code if this is the main file being run, instead of having +# been required or loaded by another file. +# If you want to learn more about __FILE__ and $0, see here: https://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Variables_and_Constants#Pre-defined_Variables +if __FILE__ == $0 + app = Application.new( + 'shop_manager', + Kernel, + OrderRepository.new, + ItemRepository.new + ) + app.run +end \ No newline at end of file diff --git a/lib/item_repository.rb b/lib/item_repository.rb index f03168e6..8667cd94 100644 --- a/lib/item_repository.rb +++ b/lib/item_repository.rb @@ -1,16 +1,13 @@ -require 'item' +require_relative 'item' class ItemRepository def all - items = [] - sql = 'SELECT id, name, price, quantity FROM items;' result = DatabaseConnection.exec_params(sql,[]) - item = Item.new result.each do |sql_data| - + item = Item.new item.id = sql_data['id'] item.name = sql_data['name'] item.price = sql_data['price'] @@ -20,4 +17,27 @@ def all end return items end + + def find(id) + + sql = 'SELECT id, name, price, quantity FROM items WHERE id = $1;' + params = [id] + result = DatabaseConnection.exec_params(sql, params) + + find_id = result[0] + + item = Item.new + item.id = find_id['id'] + item.name = find_id['name'] + item.price = find_id['price'] + item.quantity = find_id['quantity'] + + return item + end + + def create(item) + sql = 'INSERT INTO items (name, price, quantity) VALUES ($1, $2, $3);' + params = [item.name, item.price, item.quantity] + result = DatabaseConnection.exec_params(sql, params) + end end \ No newline at end of file diff --git a/lib/menu.rb b/lib/menu.rb new file mode 100644 index 00000000..52c7b5eb --- /dev/null +++ b/lib/menu.rb @@ -0,0 +1,10 @@ +class Menu + def print_menu + puts '1. Input the students' + puts '2. Show the students' + puts '3. Save students list to file' + puts '4. load students list from file' + puts '9. Exit' # 9 because we'll be adding more items + end + + \ No newline at end of file diff --git a/lib/order.rb b/lib/order.rb index e69de29b..ccd2783f 100644 --- a/lib/order.rb +++ b/lib/order.rb @@ -0,0 +1,3 @@ +class Order + attr_accessor :id, :customer, :date, :order_id +end \ No newline at end of file diff --git a/lib/order_repository.rb b/lib/order_repository.rb index 40975f0f..a9338b20 100644 --- a/lib/order_repository.rb +++ b/lib/order_repository.rb @@ -1,3 +1,26 @@ +require_relative 'order' + class OrderRepository - + def all + orders = [] + sql = 'SELECT id, customer, date, order_id FROM orders;' + result = DatabaseConnection.exec_params(sql,[]) + + result.each do |sql_data| + order = Order.new + order.id = sql_data['id'] + order.customer = sql_data['customer'] + order.date = sql_data['date'] + order.order_id = sql_data['order_id'] + + orders << order + end + return orders + end + + def create(order) + sql = 'INSERT INTO orders (customer, date, order_id) VALUES ($1, $2, $3);' + params = [order.customer, order.date, order.order_id] + DatabaseConnection.exec_params(sql, params) + end end \ No newline at end of file diff --git a/spec/app_spec.rb b/spec/app_spec.rb new file mode 100644 index 00000000..c89f73e2 --- /dev/null +++ b/spec/app_spec.rb @@ -0,0 +1,95 @@ +require './app' + +def reset_reset_table + seed_sql = File.read('spec/seeds_shop_manager_test.sql') + connection = PG.connect({ host: '127.0.0.1', dbname: 'shop_manager_test' }) + connection.exec(seed_sql) +end + +RSpec.describe Application do + context "testing the program methods" do + before(:each) do + reset_reset_table + end + + it 'Tests prints program menu' do + io = double :io + fake_item = double :items + fake_order = double :orders + program = Application.new('shop_manager_test', io, fake_order, fake_item) + + expect(io).to receive(:puts).with("Welcome to the shop manager!").ordered + expect(io).to receive(:puts).with("").ordered + expect(io).to receive(:puts).with("What do you want to do?").ordered + expect(io).to receive(:puts).with(" 1 = List all shop items").ordered + expect(io).to receive(:puts).with(" 2 = Create a new item").ordered + expect(io).to receive(:puts).with(" 3 = List all orders").ordered + expect(io).to receive(:puts).with(" 4 = Create a new order").ordered + expect(io).to receive(:puts).with(" 9 = Exit").ordered + + item = program.print_program_menu + end + + it "tests input '1' shop manager program" do + io = double :io + fake_item = ItemRepository.new + fake_order = OrderRepository.new + program_menu = Application.new('shop_manager_test', io, fake_order, fake_item) + expect(io).to receive(:gets).and_return('1').ordered + expect(io).to receive(:puts).with("1 - hammer: 5.99: 10") + expect(io).to receive(:puts).with("2 - glue: 2.99: 5") + output = program_menu.shop_manager_program + end + + it "tests input '3' shop manager program" do + io = double :io + fake_item = ItemRepository.new + fake_order = OrderRepository.new + program_menu = Application.new('shop_manager_test', io, fake_order, fake_item) + expect(io).to receive(:gets).and_return('3').ordered + expect(io).to receive(:puts).with("1 - chris: 1999-01-08: 1") + expect(io).to receive(:puts).with("2 - tom: 2003-01-09: 1") + output = program_menu.shop_manager_program + end + + it "tests input '2' shop manager program" do + io = double :io + fake_item = ItemRepository.new + fake_order = OrderRepository.new + program_menu = Application.new('shop_manager_test', io, fake_order, fake_item) + expect(io).to receive(:gets).and_return('2').ordered + expect(io).to receive(:puts).with("Enter name of item: ").ordered + expect(io).to receive(:gets).and_return('tape').ordered + expect(io).to receive(:puts).with("Enter price of item: ").ordered + expect(io).to receive(:gets).and_return('4.99').ordered + expect(io).to receive(:puts).with("Enter quantity of item: ").ordered + expect(io).to receive(:gets).and_return('60').ordered + output = program_menu.shop_manager_program + end + + it "tests input '4' shop manager program" do + io = double :io + fake_item = ItemRepository.new + fake_order = OrderRepository.new + program_menu = Application.new('shop_manager_test', io, fake_order, fake_item) + expect(io).to receive(:gets).and_return('4').ordered + expect(io).to receive(:puts).with("Enter customer order name: ").ordered + expect(io).to receive(:gets).and_return('dave').ordered + expect(io).to receive(:puts).with("Enter order date (YYYY-MM-DD): ").ordered + expect(io).to receive(:gets).and_return('2023-08-09').ordered + expect(io).to receive(:puts).with("Enter order_id of item: ").ordered + expect(io).to receive(:gets).and_return('1').ordered + output = program_menu.shop_manager_program + end + + it 'does match a charater in program menu' do + io = double :io + fake_item = ItemRepository.new + fake_order = OrderRepository.new + program_menu = Application.new('shop_manager_test', io, fake_order, fake_item) + expect(io).to receive(:gets).and_return('10').ordered + expect(io).to receive(:puts).with("I don't know what you meant, try again").ordered + output = program_menu.shop_manager_program + end + end +end \ No newline at end of file diff --git a/spec/item_repository_spec.rb b/spec/item_repository_spec.rb index fe985b46..25c2216e 100644 --- a/spec/item_repository_spec.rb +++ b/spec/item_repository_spec.rb @@ -18,9 +18,38 @@ def reset_item_table item = repo.all expect(item.length).to eq 2 - expect(item[1].id).to eq '2' - expect(item[1].name).to eq 'glue' - expect(item[1].price).to eq '2.99' - expect(item[1].quantity).to eq '5' + expect(item[0].id).to eq '1' + expect(item[0].name).to eq 'hammer' + expect(item[0].price).to eq '5.99' + expect(item[0].quantity).to eq '10' + end + + it 'tests the find method' do + repo = ItemRepository.new + item = repo.find(2) + + expect(item).to ( + having_attributes( + id: '2', + name: 'glue', + price: '2.99', + quantity: '5' + ) + ) + end + + it 'tests the create method' do + repo = ItemRepository.new + + item = Item.new + item.name = 'pencil' + item.price = 1.99 + item.quantity = 1 + + repo.create(item) + create_item = repo.all + + expect(create_item.last.name).to eq 'pencil' + expect(create_item.last.price).to eq '1.99' end end \ No newline at end of file diff --git a/spec/order_repository_spec.rb b/spec/order_repository_spec.rb index 48e7214c..f37a57a8 100644 --- a/spec/order_repository_spec.rb +++ b/spec/order_repository_spec.rb @@ -12,4 +12,38 @@ def reset_order_table before(:each) do reset_order_table end + + it 'tests all method' do + repo = OrderRepository.new + + orders = repo.all + + expect(orders.length).to eq 2 + expect(orders[0].id).to eq '1' + expect(orders[0].customer).to eq 'chris' + end + + it 'tests the create method' do + repo = OrderRepository.new + + new_order = Order.new + + new_order.id = '3' + new_order.customer = "sunny" + new_order.date = '1999-01-08' + new_order.order_id = '1' + + order = repo.create(new_order) + + created_order = repo.all + expect(created_order.last).to ( + having_attributes( + id: '3', + customer: 'sunny', + date: '1999-01-08', + order_id: '1' + ) + ) + end end + From 96978bdb5d3cc9fe14e6d043d1ff43ce9f02d816 Mon Sep 17 00:00:00 2001 From: Chris Toplisek Date: Sat, 1 Apr 2023 21:10:42 +0100 Subject: [PATCH 4/4] finished solo project --- lib/menu.rb | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 lib/menu.rb diff --git a/lib/menu.rb b/lib/menu.rb deleted file mode 100644 index 52c7b5eb..00000000 --- a/lib/menu.rb +++ /dev/null @@ -1,10 +0,0 @@ -class Menu - def print_menu - puts '1. Input the students' - puts '2. Show the students' - puts '3. Save students list to file' - puts '4. load students list from file' - puts '9. Exit' # 9 because we'll be adding more items - end - - \ No newline at end of file