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

finish shop manager challenge #168

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 21 additions & 0 deletions app.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# file: app.rb

require_relative 'lib/database_connection'
require_relative 'lib/item_repository'
require_relative 'lib/order_repository'
# We need to give the database name to the method `connect`.
DatabaseConnection.connect('items_orders')

# Perform a SQL query on the database and get the result set.
# sql = 'SELECT id, name, unit_price, quantity FROM items;'
# result = DatabaseConnection.exec_params(sql, [])

# Print out each record from the result set .
# result.each do |record|
# p record
# end
# repo = ItemRepository.new
# p repo.all
repo = OrderRepository.new
p repo.find_with_items(1)

201 changes: 201 additions & 0 deletions items_class_design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
# Items Model and Repository Classes Design Recipe

_Copy this recipe template to design and implement Model and Repository classes for a database table._

## 1. Design and create the Table

If the table is already created in the database, you can skip this step.

Otherwise, [follow this recipe to design and create the SQL schema for your table](./single_table_design_recipe_template.md).

_In this template, we'll use an example table `students`_

```
# EXAMPLE

Table: items

Columns:
id | name | unit_price | quantity
```

## 2. Create Test SQL seeds

Your tests will depend on data stored in PostgreSQL to run.

If seed data is provided (or you already created it), you can skip this step.

```sql
-- EXAMPLE
-- (file: spec/seeds_{table_name}.sql)

-- Write your SQL seed here.

-- First, you'd need to truncate the table - this is so our table is emptied between each test run,
-- so we can start with a fresh state.
-- (RESTART IDENTITY resets the primary key)

TRUNCATE TABLE items RESTART IDENTITY; -- replace with your own table name.

-- Below this line there should only be `INSERT` statements.
-- Replace these statements with your own seed data.

INSERT INTO items (name, unit_price, quantity) VALUES ('book', '2', '100');
INSERT INTO items (name, unit_price, quantity) VALUES ('pen', '1', '200');
```

Run this SQL file on the database to truncate (empty) the table, and insert the seed data. Be mindful of the fact any existing records in the table will be deleted.

```bash
psql -h 127.0.0.1 items_orders_test < spec/seeds_items.sql
```

## 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
# EXAMPLE
# Table name: students

# Model class
# (in lib/item.rb)
class Items
end

# Repository class
# (in lib/item_repository.rb)
class ItemRepository
end
```

## 4. Implement the Model class

Define the attributes of your Model class. You can usually map the table columns to the attributes of the class, including primary and foreign keys.

````ruby
# EXAMPLE
# Table name: students

# Model class
# (in lib/item.rb)

class Item
attr_accessor :id, :name, :unit_price,:quantity
end




_You may choose to test-drive this class, but unless it contains any more logic than the example above, it is probably not needed._

## 5. Define the Repository Class interface

Your Repository class will need to implement methods for each "read" or "write" operation you'd like to run against the database.

Using comments, define the method signatures (arguments and return value) and what they do - write up the SQL queries that will be used by each method.

```ruby
# EXAMPLE
# Table name: items

# Repository class
# (in lib/item_repository.rb)

class ItemRepository

# Selecting all items
# No arguments
def all
# Executes the SQL query:
# SELECT id, name, unit_price, quantity FROM items;

# Returns an array of Item objects.
end

# Gets a single record by its ID
# One argument: the id (number)
def find(id)
# Executes the SQL query:
# SELECT id, name, cohort_name FROM students WHERE id = $1;

# Returns a single Student object.
end

# Add more methods below for each operation you'd like to implement.

# def create(student)
# end

# def update(student)
# end

# def delete(student)
# 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.

These examples will later be encoded as RSpec tests.

```ruby
# EXAMPLES

# 1
# Get all students

repo = ItemRepository.new

items = repo.all

items.length # => 2
items.first.name # => "book"

# 2
# Get a single student

repo = StudentRepository.new

student = repo.find(1)

student.id # => 1
student.name # => 'David'
student.cohort_name # => 'April 2022'

# Add more examples for each method
```

Encode this example as a test.

## 7. Reload the SQL seeds before each test run

Running the SQL code present in the seed file will empty the table and re-insert the seed data.

This is so you get a fresh table contents every time you run the test suite.

```ruby
# EXAMPLE

# file: spec/student_repository_spec.rb

def reset_students_table
seed_sql = File.read('spec/seeds_students.sql')
connection = PG.connect({ host: '127.0.0.1', dbname: 'students' })
connection.exec(seed_sql)
end

describe StudentRepository do
before(:each) do
reset_students_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._
54 changes: 54 additions & 0 deletions lib/app_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
require_relative 'database_connection'
require_relative 'item_repository'
require_relative 'order_repository'

class Application

def initialize(database_name, io = Kernel, item_repository = ItemRepository.new, order_repository = OrderRepository.new)
DatabaseConnection.connect(database_name)
@io = io
@item_repository = item_repository
@order_repository = order_repository
end

def run
@io.puts 'Welcome to the shop management program!'
list_choices
choice = @io.gets.chomp
case choice
when '1'
@item_repository.all.each_with_index do |item, i|
@io.puts "* #{i+1} - #{item.name}"
end
when '2'
# create a new item
when '3'
# list all orders
when '4'
# create a new order
end

end

private

def list_choices
@io.puts "\nWhat do you like 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.print 'Enter your choice: '
end

end

if __FILE__ == $0
app = Application.new(
'items_orders_test',
Kernel,
ItemRepository.new,
'OrderRepository.new'
)
app.run
end
28 changes: 28 additions & 0 deletions lib/database_connection.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# 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
4 changes: 4 additions & 0 deletions lib/item.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class Item
attr_accessor :id, :name, :unit_price, :quantity, :orders
end

38 changes: 38 additions & 0 deletions lib/item_repository.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
require_relative './item'

class ItemRepository

def all
sql = 'SELECT id, name, unit_price, quantity FROM items;'
result_set = DatabaseConnection.exec_params(sql, [])

items =[]
result_set.each do|record|
# item = Item.new
# item.id = record['id']
# item.name = record['name']
# item.unit_price= record['unit_price']
# item.quantity= record['quantity']
items << record_to_item_object(record)
end
return items

end

def record_to_item_object(record)
item = Item.new
item.id = record['id']
item.name = record['name']
item.unit_price= record['unit_price']
item.quantity= record['quantity']
return item
end

def create(item)
sql = 'INSERT INTO items (name, unit_price, quantity) VALUES($1, $2, $3);'
sql_params = [item.name, item.unit_price, item.quantity]
DatabaseConnection.exec_params(sql, sql_params)
return nil
end

end
7 changes: 7 additions & 0 deletions lib/order.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class Order
attr_accessor :id, :customer_name, :placed_date, :items

def initialize
@items = []
end
end
Loading