forked from georgebarrett/book_shop_psql
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ea7958a
commit 2a0fbac
Showing
11 changed files
with
430 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
--require spec_helper |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# frozen_string_literal: true | ||
|
||
source "https://rubygems.org" | ||
|
||
# gem "rails" | ||
|
||
gem "rspec", "~> 3.12" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
GEM | ||
remote: https://rubygems.org/ | ||
specs: | ||
diff-lcs (1.5.0) | ||
rspec (3.12.0) | ||
rspec-core (~> 3.12.0) | ||
rspec-expectations (~> 3.12.0) | ||
rspec-mocks (~> 3.12.0) | ||
rspec-core (3.12.2) | ||
rspec-support (~> 3.12.0) | ||
rspec-expectations (3.12.3) | ||
diff-lcs (>= 1.2.0, < 2.0) | ||
rspec-support (~> 3.12.0) | ||
rspec-mocks (3.12.5) | ||
diff-lcs (>= 1.2.0, < 2.0) | ||
rspec-support (~> 3.12.0) | ||
rspec-support (3.12.0) | ||
|
||
PLATFORMS | ||
arm64-darwin-21 | ||
|
||
DEPENDENCIES | ||
rspec (~> 3.12) | ||
|
||
BUNDLED WITH | ||
2.4.12 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
require_relative './lib/database_connection' | ||
require_relative './lib/book_repository' | ||
|
||
# We need to give the database name to the method `connect`. | ||
DatabaseConnection.connect('book_store') | ||
|
||
# Perform a SQL query on the database and get the result set. | ||
# sql = 'SELECT id, title FROM books;' | ||
# result = DatabaseConnection.exec_params(sql, []) | ||
|
||
book_repository = BookRepository.new | ||
|
||
# Print out each record from the result set . | ||
book_repository.all.each do |record| | ||
p record.id + " - " + record.title + " - " + record.author_name | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
# {{BOOK STORE}} 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 | ||
|
||
already done | ||
``` | ||
## 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.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 books 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 books (title, author_name) VALUES ('Piranesi', 'Suzanne Clarke'); | ||
INSERT INTO books (title, author_name) VALUES ('Born a Crime', 'Trevor Noah'); | ||
``` | ||
|
||
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 book_store_test < seeds.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: books | ||
|
||
# Model class | ||
# (in lib/book.rb) | ||
class Book | ||
end | ||
|
||
# Repository class | ||
# (in lib/book_repository.rb) | ||
class BookRepository | ||
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: books | ||
|
||
# Model class | ||
# (in lib/book.rb) | ||
|
||
class Book | ||
|
||
# Replace the attributes by your own columns. | ||
attr_accessor :id, :title, :author_name | ||
end | ||
|
||
# The keyword attr_accessor is a special Ruby feature | ||
# which allows us to set and get attributes on an object, | ||
# here's an example: | ||
# | ||
# student = Student.new | ||
# student.name = 'Jo' | ||
# student.name | ||
``` | ||
|
||
*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: books | ||
|
||
# Repository class | ||
# (in lib/book_repository.rb) | ||
|
||
class BookRepository | ||
|
||
# Selecting all records | ||
# No arguments | ||
def all | ||
# Executes the SQL query: | ||
# SELECT id, title, author_name FROM books; | ||
|
||
# Returns an array of Book objects. | ||
end | ||
|
||
# Gets a single record by its ID | ||
# One argument: the id (number) | ||
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 = BookRepository.new | ||
|
||
books = repo.all | ||
|
||
books.length # => 2 | ||
|
||
books[0].id # => 1 | ||
books[0].title # => 'Piranesi' | ||
books[0].author_name # => 'Suzanne Clarke' | ||
|
||
books[1].id # => 2 | ||
books[1].title # => 'Born a Crime' | ||
books[1].author_name # => 'Trevor Noah' | ||
|
||
|
||
``` | ||
|
||
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/book_repository_spec.rb | ||
|
||
def reset_books_table | ||
seed_sql = File.read('spec/seeds.sql') | ||
connection = PG.connect({ host: '127.0.0.1', dbname: 'books_test' }) | ||
connection.exec(seed_sql) | ||
end | ||
|
||
describe BookRepository do | ||
before(:each) do | ||
reset_books_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._ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
class Book | ||
|
||
# Replace the attributes by your own columns. | ||
attr_accessor :id, :title, :author_name | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
require_relative './book' | ||
require_relative './database_connection' | ||
|
||
class BookRepository | ||
def all | ||
sql = 'SELECT id, title, author_name FROM books;' | ||
result = DatabaseConnection.exec_params(sql, []) | ||
|
||
books = [] | ||
|
||
result.each do |record| | ||
book = Book.new | ||
|
||
book.id = record['id'] | ||
book.title = record['title'] | ||
book.author_name = record['author_name'] | ||
|
||
books << book | ||
end | ||
|
||
return books | ||
|
||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
require 'book_repository' | ||
require 'database_connection' | ||
|
||
def reset_books_table | ||
seed_sql = File.read('spec/seeds.sql') | ||
connection = PG.connect({ host: '127.0.0.1', dbname: 'book_store_test' }) | ||
connection.exec(seed_sql) | ||
end | ||
|
||
describe BookRepository do | ||
before(:each) do | ||
reset_books_table | ||
end | ||
|
||
it "returns two books" do | ||
repo = BookRepository.new | ||
|
||
books = repo.all | ||
|
||
expect(books.length).to eq 2 # => 2 | ||
|
||
expect(books.first.id).to eq '1' # => 1 | ||
expect(books.first.title).to eq 'Piranesi' # => 'Piranesi' | ||
expect(books.first.author_name).to eq 'Suzanne Clarke' # => 'Suzanne Clarke' | ||
|
||
expect(books[1].id).to eq "2" | ||
expect(books[1].title).to eq 'Born a Crime' | ||
expect(books[1].author_name).to eq 'Trevor Noah' | ||
end | ||
end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
|
||
TRUNCATE TABLE books 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 books (title, author_name) VALUES ('Piranesi', 'Suzanne Clarke'); | ||
INSERT INTO books (title, author_name) VALUES ('Born a Crime', 'Trevor Noah'); |
Oops, something went wrong.