diff --git a/README.md b/README.md index 242f94be..6cac17bf 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,10 @@ +Note: +------- +I did not get to TDD this project as I wished for due to lack of time, it just seemed more important to me at this time to imagine and put in practice a whole shop_manager with the relevant classes, methods and DB access. I will definetly do a lot more on the testing next time. What a lovely jouney it has been to see it work in the end so close to what a real app would do. + + + + Shop Manager Project ================= diff --git a/lib/item.rb b/lib/item.rb new file mode 100644 index 00000000..8198292c --- /dev/null +++ b/lib/item.rb @@ -0,0 +1,24 @@ +class Item + def initialize(name, unit_price, quantity, item_id = 0) + @name = name + @unit_price = unit_price + @quantity = quantity + @item_id = item_id + end + + def name + @name + end + + def unit_price + @unit_price + end + + def quantity + @quantity + end + + def item_id + @item_id + end +end \ No newline at end of file diff --git a/lib/order.rb b/lib/order.rb new file mode 100644 index 00000000..94813ae2 --- /dev/null +++ b/lib/order.rb @@ -0,0 +1,19 @@ +class Order + def initialize(customer_name, item, order_id = 0) + @customer_name = customer_name + @item = item + @order_id = order_id + end + + def customer_name + @customer_name + end + + def item + @item + end + + def order_id + @order_id + end +end \ No newline at end of file diff --git a/lib/shop_manager.rb b/lib/shop_manager.rb new file mode 100644 index 00000000..acc45db8 --- /dev/null +++ b/lib/shop_manager.rb @@ -0,0 +1,149 @@ +require_relative './item' +require_relative './order' +require 'pg' + +class ShopManager + WHAT_TO_DO = 'What do you want to do? + 1 = list all shop items + 2 = create a new item + 3 = list all orders + 4 = create a new order' + + def initialize + db_connect + initiate_menu + end + + def initiate_menu(initial = true) + puts " + Welcome to the shop management program! + " if initial == true + puts " + #{WHAT_TO_DO} + " + answer = gets.chomp + case answer + when '1' + print_all_items(all_items) + when '2' + new_item_collect_data + when '3' + all_orders + when '4' + new_order_collect_data + else + initiate_menu(false) + end + end + + def new_item_collect_data + while true do + puts 'Enter the name for the new Item' + name = gets.chomp + break if name.count("a-zA-Z") > 0 + end + while true do + puts "Enter the price for the #{name}" + unit_price = gets.chomp + break if unit_price.to_i.is_a? Integer + end + while true do + puts "Enter the quantity for the #{name}" + quantity = gets.chomp + break if quantity.to_i.is_a? Integer + end + item = Item.new(name, unit_price, quantity) + new_item(item) + end + + def new_item(item) + # add item to items table + query = "INSERT INTO items(name, unit_price, quantity) + VALUES('#{item.name}', #{item.unit_price.to_i}, #{item.quantity.to_i});" + result = @conn_db.exec(query) + puts "Your item has been added succesfully" + end + + def db_connect() + @conn_db = PG.connect({ host: '127.0.0.1', dbname: 'shop_manager' }) + end + + def new_order_collect_data + while true do + puts 'New Order, please enter your name' + customer_name = gets.chomp + break if customer_name.count("a-zA-Z") > 4 && customer_name.split(' ').count >= 2 + end + items = all_items + while true do + puts "Please select you item's id" + item_id = gets.chomp + if item_id.to_i.is_a? Integer + return_item = items.find { |curr_item| curr_item.item_id == item_id } + if !return_item.nil? + item = Item.new(return_item.name, return_item.unit_price, return_item.quantity, return_item.item_id) + order = Order.new(customer_name, item) + new_order(order) + end + end + break unless return_item.nil? + end + end + + def new_order(order) + # add order to orders table + query = "INSERT INTO orders(customer_name, item_id) + VALUES('#{order.customer_name}', '#{order.item.item_id}') RETURNING *;" + @conn_db.exec(query) + puts "Your order has been placed succesfully, thank you!" + end + + private + def get_select_query(table) + "SELECT * FROM #{table};" + end + + def all_items + query = get_select_query('items') + items = @conn_db.exec(query) + items = items.map do |item| + Item.new(item['name'], item['unit_price'], item['quantity'], item['item_id']) + end + + items + end + + def orders_all + query = get_select_query('orders') + @conn_db.exec(query) + end + + def all_orders + items = all_items + orders = orders_all + orders = orders.map do |order| + item = items.find { |curr_item| curr_item.item_id == order['item_id'] } + Order.new(order["customer_name"], item, order["order_id"]) + end + print_all_orders(orders) + end + + def print_all_items(items) + puts "Here's a list of all shop items:" + #1 Super Shark Vacuum Cleaner - Unit price: 99 - Quantity: 30 + items.each do |item| + puts "Item id: #{item.item_id} - #{item.name} - Unit price: £#{item.unit_price}.00 - Quantity: #{item.quantity}" + end + end + + def print_all_orders(orders) + puts "Here's a list of all shop orders:" + #1 Super Shark Vacuum Cleaner - Unit price: 99 - Quantity: 30 + orders.each do |order| + puts "Order id: #{order.order_id} - #{order.customer_name} - Item: #{order.item.name} - Price: £#{order.item.unit_price}.00" + end + end +end + +ShopManager.new + diff --git a/recipe.md b/recipe.md new file mode 100644 index 00000000..d42e5c0d --- /dev/null +++ b/recipe.md @@ -0,0 +1,149 @@ +# Two Tables (Many-to-Many) Design Recipe Template + +_Copy this recipe template to design and create two related database tables having a Many-to-Many relationship._ + +## 1. Extract nouns from the user stories or specification + +```ruby +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: + +items, item_name, unit_price +``` + +## 2. Infer the Table Name and Columns + +Put the different nouns in this table. Replace the example with your own nouns. + +| Record | Properties | +| --------------------- | ------------------ | +| posts | title, content +| tags | name + +1. Name of the first table (always plural): `posts` + + Column names: `title`, `content` + +2. Name of the second table (always plural): `tags` + + Column names: `name` + +## 3. Decide the column types. + +[Here's a full documentation of PostgreSQL data types](https://www.postgresql.org/docs/current/datatype.html). + +Most of the time, you'll need either `text`, `int`, `bigint`, `numeric`, or `boolean`. If you're in doubt, do some research or ask your peers. + +Remember to **always** have the primary key `id` as a first column. Its type will always be `SERIAL`. + +``` +# EXAMPLE: + +Table: posts +id: SERIAL +title: text +content: text + +Table: tags +id: SERIAL +name: text +``` + +## 4. Design the Many-to-Many relationship + +Make sure you can answer YES to these two questions: + +1. Can one [TABLE ONE] have many [TABLE TWO]? (Yes/No) +2. Can one [TABLE TWO] have many [TABLE ONE]? (Yes/No) + +``` +# EXAMPLE + +1. Can one tag have many posts? YES +2. Can one post have many tags? YES +``` + +_If you would answer "No" to one of these questions, you'll probably have to implement a One-to-Many relationship, which is simpler. Use the relevant design recipe in that case._ + +## 5. Design the Join Table + +The join table usually contains two columns, which are two foreign keys, each one linking to a record in the two other tables. + +The naming convention is `table1_table2`. + +``` +# EXAMPLE + +Join table for tables: posts and tags +Join table name: posts_tags +Columns: post_id, tag_id +``` + +## 4. Write the SQL. + +```sql +-- EXAMPLE +-- file: posts_tags.sql + +-- Replace the table name, columm names and types. + +-- Create the first table. +CREATE TABLE items ( + item_id SERIAL PRIMARY KEY, + name text, + unit_price int, + quantity int +); + +-- Create the second table. +CREATE TABLE orders ( + order_id SERIAL PRIMARY KEY, + custumer_name text, + date TIMESTAMP not null defaul now(), + item_id int +); + +-- Create the join table. +CREATE TABLE items_orders ( + item_id int, + order_id int, + constraint fk_item foreign key(item_id) references items(id) on delete cascade, + constraint fk_order foreign key(order_id) references orders(id) on delete cascade, + PRIMARY KEY (item_id, order_id) +); + +``` + +## 5. Create the tables. + +```bash +psql -h 127.0.0.1 database_name < posts_tags.sql +```