Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
b5f9a2a
present error when customer not found
gabriel-lima May 25, 2018
a7bb62a
present error when product not found
gabriel-lima May 25, 2018
3cf0b2f
test save order
gabriel-lima May 29, 2018
75b2449
save order with two products
gabriel-lima May 29, 2018
d27c57e
present success with order_id and total_price
gabriel-lima May 29, 2018
1a23cf0
refactor to improve performance
gabriel-lima May 29, 2018
0dd4b73
extracts methods
gabriel-lima May 29, 2018
6411856
refactor tests
gabriel-lima May 29, 2018
c1d7f66
extract files
gabriel-lima May 29, 2018
b420c52
generate rspec files
gabriel-lima May 29, 2018
8ff4bf8
gateway tests
gabriel-lima May 29, 2018
06c2780
extract customer gateway
gabriel-lima May 29, 2018
85b7827
product gateway tests
gabriel-lima May 29, 2018
abdb718
extract product gateway
gabriel-lima May 29, 2018
1440791
Merge branch 'master' into solution
gabriel-lima May 30, 2018
1b31769
Merge branch 'master' into solution
gabriel-lima May 30, 2018
85e14aa
order gateway tests
gabriel-lima May 30, 2018
f21db8b
extract order gateway
gabriel-lima May 30, 2018
8d87e53
Merge branch 'master' into solution
hudolfhess Jun 13, 2018
b43cedd
Merge branch 'master' into solution
hudolfhess Jun 13, 2018
a2ba218
Created CreateOrderPresenterJson
hudolfhess Jun 13, 2018
01bc1eb
extracted presenter
hudolfhess Jun 13, 2018
87ef45b
added line
hudolfhess Jun 13, 2018
182bf2f
Controller implemented
hudolfhess Jun 13, 2018
9567234
Controller test
hudolfhess Jun 13, 2018
7f566b6
implement json presenter
gabriel-lima Jun 13, 2018
6e23e67
Merge branch 'solution' of github.com:jurassic-park-hackers/workshop-…
gabriel-lima Jun 13, 2018
f73c233
fix spacing and variable in camel case
gabriel-lima Jul 4, 2018
3fe31db
Merge pull request #1 from jurassic-park-hackers/meetup-ruby-joinville
gabriel-lima Jul 4, 2018
938288d
Merge branch 'master' into solution
hudolfhess Jul 4, 2018
aa24de1
show orders by customer use case
gabriel-lima Jul 4, 2018
67fae19
extracted common rule to order entity
gabriel-lima Jul 4, 2018
aad3a72
extrated use case
gabriel-lima Jul 4, 2018
e7d721e
adds how to run tests
gabriel-lima Jul 4, 2018
b69e81d
implemented get_orders_by_customer_id
gabriel-lima Jul 4, 2018
ae325ce
fix save order test
gabriel-lima Jul 4, 2018
594de56
using module name in presenter
hudolfhess Feb 19, 2019
db144d1
refactor
hudolfhess Feb 19, 2019
6c4e6f8
Merge pull request #2 from jurassic-park-hackers/refactor-presenter-u…
hudolfhess Feb 19, 2019
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
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,16 @@ docker-compose run web rake db:create
docker-compose run web rake db:migrate
```

# To run tests

## Unit tests

```sh
docker-compose run web rspec spec/lib --order rand -fd
```

## Integrated tests

```sh
docker-compose run web rspec spec/gateways spec/presenters spec/controllers --order rand -fd
```
21 changes: 21 additions & 0 deletions app/controllers/orders_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
require './lib/orders/create_order_usecase'

class OrdersController < ActionController::Base
def create
customer_gateway = CustomerGatewayDatabase.new
product_gateway = ProductGatewayDatabase.new
order_gateway = OrderGatewayDatabase.new
presenter = Orders::CreateOrderPresenterJson.new

CreateOrderUsecase.new(customer_gateway, product_gateway, order_gateway, presenter)
.execute(params[:customer_id], get_products_id_with_quantity)

render presenter.response
end

private

def get_products_id_with_quantity
params[:products_id_with_quantity].map{ |p| {product_id: p[:product_id].to_i, quantity: p[:quantity].to_f} }
end
end
7 changes: 7 additions & 0 deletions app/gateways/customer_gateway_database.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require './lib/customers/gateways'

class CustomerGatewayDatabase < CustomerGateway
def customer_exists?(customer_id)
Customer.exists?(customer_id)
end
end
22 changes: 22 additions & 0 deletions app/gateways/order_gateway_database.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
require './lib/orders/gateways'

class OrderGatewayDatabase < OrderGateway
def save_order(customer_id, order_products)
order = Order.create(customer_id: customer_id)

for order_product in order_products
OrderProduct.create(
product_id: order_product.product_id,
quantity: order_product.quantity,
price: order_product.price,
order_id: order.id
)
end

return order.id
end

def get_orders_by_customer_id(customer_id)
return Order.where(customer_id: customer_id)
end
end
14 changes: 14 additions & 0 deletions app/gateways/product_gateway_database.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require './lib/products/gateways'
require './lib/products/exceptions'

class ProductGatewayDatabase < ProductGateway
def find_products_by_ids(products_ids)
products = Product.where(id: products_ids)

products_ids_found = products.map(&:id)
products_ids_not_found = products_ids.select{ |product_id| products_ids_found.exclude?(product_id) }
raise ProductsNotFoundException.new(products_ids_not_found) if products_ids_not_found.length > 0

return products
end
end
1 change: 1 addition & 0 deletions app/models/order.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
class Order < ApplicationRecord
has_many :order_products
end
3 changes: 3 additions & 0 deletions app/models/product.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
class Product < ApplicationRecord
def product_id
id
end
end
50 changes: 50 additions & 0 deletions app/presenters/orders/create_order_presenter_json.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
require './lib/orders/presenters'

module Orders
class CreateOrderPresenterJson < CreateOrderPresenter
def initialize
@status = :bad_request
@errors = []
@order = {:order_id => nil, :total_price => nil}
end

def show_error_customer_not_found
@status = :not_found
@errors += ['Customer not found.']
end

def show_error_product_not_found(product_id)
@status = :not_found
@errors += ['Product not found: ' + product_id.to_s]
end

def show_order(order_id, total_price)
@status = :ok
@order[:order_id] = order_id
@order[:total_price] = total_price
end

def response
{
'status': @status,
'json': create_json_response
}
end

private

def create_json_response
if @errors.empty?
if @order[:order_id].nil?
json = {}
else
json = @order
end
else
json = {:errors => @errors}
end

json
end
end
end
2 changes: 1 addition & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
post '/orders/', to: 'orders#create'
end
6 changes: 4 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
version: '2'
services:
db:
image: postgres
image: postgres:9.6.2
ports:
- '5432'
volumes:
- ./tmp/db:/var/lib/postgresql/data
- /var/lib/postgresql/data
web:
build: .
command: bundle exec rails s -p 3000 -b '0.0.0.0'
Expand Down
3 changes: 3 additions & 0 deletions lib/customers/gateways.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class CustomerGateway
def customer_exists?(customer_id); end
end
60 changes: 60 additions & 0 deletions lib/orders/create_order_usecase.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
require './lib/orders/structs'
require './lib/products/exceptions'
require './lib/orders/order_entity'

class CreateOrderUsecase
def initialize(customer_gateway, product_gateway, order_gateway, create_order_presenter)
@customer_gateway = customer_gateway
@product_gateway = product_gateway
@order_gateway = order_gateway
@presenter = create_order_presenter
end

def execute(customer_id, products_id_with_quantity)
return if not customer_exists?(customer_id)

products, has_error = find_products(products_id_with_quantity)
return if has_error

order_products = get_order_products(products, products_id_with_quantity)
total_price = OrderEntity.get_total_price(order_products)

order_id = @order_gateway.save_order(customer_id, order_products)
@presenter.show_order(order_id, total_price.round(2))
end

private

def customer_exists?(customer_id)
if not @customer_gateway.customer_exists?(customer_id)
@presenter.show_error_customer_not_found
return false
end

return true
end

def find_products(products_id_with_quantity)
begin
products_ids = products_id_with_quantity.map { |p| p[:product_id] }
return @product_gateway.find_products_by_ids(products_ids), false
rescue ProductsNotFoundException => ex
for product_id in ex.products_ids
@presenter.show_error_product_not_found(product_id)
end

return [], true
end
end

def get_order_products(products, products_id_with_quantity)
order_products = []

for product in products
quantity = products_id_with_quantity.select { |p| p[:product_id] == product.product_id }[0][:quantity]
order_products += [OrderProductStruct.new(product.product_id, quantity, product.price)]
end

return order_products
end
end
4 changes: 4 additions & 0 deletions lib/orders/gateways.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class OrderGateway
def save_order(customer_id, order_products); end
def get_orders_by_customer_id(customer_id); end
end
13 changes: 13 additions & 0 deletions lib/orders/order_entity.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class OrderEntity
class << self
def get_total_price(order_products)
total_price = 0

for order_product in order_products
total_price += order_product.price * order_product.quantity
end

return total_price
end
end
end
10 changes: 10 additions & 0 deletions lib/orders/presenters.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class CreateOrderPresenter
def show_error_customer_not_found; end
def show_error_product_not_found(product_id); end
def show_order(order_id, total_price); end
end

class ShowOrdersByCustomerPresenter
def show_orders(orders); end
def error_customer_not_found(customer_id); end
end
28 changes: 28 additions & 0 deletions lib/orders/show_orders_by_customer_usecase.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
require './lib/orders/structs'
require './lib/orders/order_entity'

class ShowOrdersByCustomerUsecase
def initialize(customer_gateway, order_gateway, show_orders_by_customer_presenter)
@customer_gateway = customer_gateway
@order_gateway = order_gateway
@presenter = show_orders_by_customer_presenter
end

def execute(customer_id)
if not @customer_gateway.customer_exists?(customer_id)
@presenter.error_customer_not_found(customer_id)
return
end

orders = @order_gateway.get_orders_by_customer_id(customer_id)
response = ShowOrdersByCustomerResponse.new([])
for order in orders
total_price = OrderEntity.get_total_price(order.order_products)
response.orders_response += [
OrderResponse.new(order.order_id, order.customer_id, total_price, order.order_products)
]
end

@presenter.show_orders(response)
end
end
8 changes: 8 additions & 0 deletions lib/orders/structs.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
ProductStruct = Struct.new(:product_id, :name, :price)

OrderProductStruct = Struct.new(:product_id, :quantity, :price)

OrderGatewayStruct = Struct.new(:order_id, :customer_id, :order_products)

ShowOrdersByCustomerResponse = Struct.new(:orders_response)
OrderResponse = Struct.new(:order_id, :customer_id, :total_price, :order_products)
8 changes: 8 additions & 0 deletions lib/products/exceptions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class ProductsNotFoundException < StandardError
attr_reader :products_ids

def initialize(products_ids)
@products_ids = products_ids
super
end
end
3 changes: 3 additions & 0 deletions lib/products/gateways.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class ProductGateway
def find_products_by_ids(products_ids); end
end
18 changes: 18 additions & 0 deletions spec/controllers/orders_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
require 'rails_helper'

RSpec.describe OrdersController do
describe 'create order' do
it 'create order with products and customer' do
product = Product.create(price: 100.0)
customer = Customer.create()
products_id_with_quantity = [{product_id: product.id, quantity: 20}]

post :create, as: :json, customer_id: customer.id, products_id_with_quantity: products_id_with_quantity, format: :json

response_body = ActiveSupport::JSON.decode(response.body)
expect(response.status).to eq(200)
expect(response_body["order_id"]).to_not eq(nil)
expect(response_body["total_price"]).to eq("2000.0")
end
end
end
25 changes: 25 additions & 0 deletions spec/gateways/customer_gateway_database_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
require 'rails_helper'

RSpec.describe CustomerGatewayDatabase do
let(:gateway) { described_class.new() }

describe '#customer_exists?' do
context 'when customer exists' do
it 'returns true' do
customer = Customer.create()

result = gateway.customer_exists?(customer.id)

expect(result).to be true
end
end

context 'when customer does not exists' do
it 'returns false' do
result = gateway.customer_exists?(123)

expect(result).to be false
end
end
end
end
Loading