From abdc118ec7d6147cccb0532ddafa1af961b8985a Mon Sep 17 00:00:00 2001 From: Marina Papapetrou Date: Thu, 24 Nov 2022 20:58:59 +0000 Subject: [PATCH] partial completion of challenge --- lib/dish.rb | 17 +++ lib/dish_list.rb | 35 ++++++ lib/test_recipe.md | 261 +++++++++++++++++++++++++++++++++++++++ lib/text_confirmation.rb | 26 ++++ spec/dish_list_spec.rb | 59 +++++++++ spec/dish_spec.rb | 43 +++++++ spec/integration_spec.rb | 80 ++++++++++++ 7 files changed, 521 insertions(+) create mode 100644 lib/dish.rb create mode 100644 lib/dish_list.rb create mode 100644 lib/test_recipe.md create mode 100644 lib/text_confirmation.rb create mode 100644 spec/dish_list_spec.rb create mode 100644 spec/dish_spec.rb create mode 100644 spec/integration_spec.rb diff --git a/lib/dish.rb b/lib/dish.rb new file mode 100644 index 0000000000..03a5f95956 --- /dev/null +++ b/lib/dish.rb @@ -0,0 +1,17 @@ +Dish = Struct.new(:dish, :price) do + def is_selected? + @selected == true + end + + def format + "#{dish}, £#{'%.2f' % price}" + end + + def selected + @selected = true + end + + def deselect + @selected = false + end +end diff --git a/lib/dish_list.rb b/lib/dish_list.rb new file mode 100644 index 0000000000..5fa358c49b --- /dev/null +++ b/lib/dish_list.rb @@ -0,0 +1,35 @@ +class DishList + def initialize + @dish_list = [] + end + + def add(dish_with_price) + @dish_list << dish_with_price + end + + def list + @dish_list + end + + def selection + + @dish_list.select do |dish| + dish.is_selected? + end + + end + + def receipt + selection.map do |item| + item.format + end.push(grand_total) + end + + def grand_total + sum = selection.map do |item| + item.price + end.sum + + "Total cost: £#{'%.2f' % sum}" + end +end diff --git a/lib/test_recipe.md b/lib/test_recipe.md new file mode 100644 index 0000000000..593761ff77 --- /dev/null +++ b/lib/test_recipe.md @@ -0,0 +1,261 @@ +# {{Golden Square Solo Project}} Multi-Class Planned Design Recipe + +## Describe the Problem + +Here is a project to test your golden square skills overall: + +> As a customer +> So that I can check if I want to order something +> I would like to see a list of dishes with prices. +> +> As a customer +> So that I can order the meal I want +> I would like to be able to select some number of several available dishes. +> +> As a customer +> So that I can verify that my order is correct +> I would like to see an itemised receipt with a grand total. + +Use the `twilio-ruby` gem to implement this next one. You will need to use +doubles too. + +> As a customer +> So that I am reassured that my order will be delivered on time +> I would like to receive a text such as "Thank you! Your order was placed and +> will be delivered before 18:52" after I have ordered. + +Fair warning: if you push your Twilio API Key to a public Github repository, +anyone will be able to see and use it. What are the security implications of +that? How will you keep that information out of your repository? + +## Design the Class System + +> Initial mapping out of classes + +``` + ┌─────────────────────┐ + │ DishList │ + ├──────────── │ + │- add(dish) │ + │ │ + │- menu │ + │ │ + │- selection │ + │ │ ┌────────────────┐ + │- receipt(selection) │ │ConfirmOrder │ + │ │ ├────────────── │ + └─────────────────────┘ │ │ + ▲ │ - dependency │ + │ │ injector │ + ┌─────────┴────────┐ │ │ + │ Dish(dish, price)│ │ - API request │ + ├──────────────────┤ │ │ + │- dish │ │ - confirmation │ + │ │ │ message │ + │- price │ │ │ + │ │ └────────────────┘ + │- format_dish │ + │ =>"dish, £price"│ + │ │ + │- select │ + │ │ + │- deselect │ + │ │ + │- is_selected? │ + └──────────────────┘ + +``` + +> Class models + +```ruby + +class DishList + def initialize + end + + def add(dish_with_price) # dish_with_price is an instance of Dish + # Dish gets added to the menu + # Returns nothing + end + + def list + # Returns list of all dishes + end + + def selection + # returns list of selected dishes + end + + def receipt + # takes selection and returns itemised receipt with grand total + end + + def grand_total + # calculates grand total + end +end + +Dish = Struct.new(:dish, :price) do + def is_selected? + # returns true if dish has been selected + end + + def format + # Returns a string in the form "dish, £price" + end + + def selected + # formats dish for selection + end + + def deselect + # reverse formatting, deselects dish + end +end + +class ConfirmOrder + def intialize(requester) + # dependency injector for testing API usage + end + + def confirmation_message + # send confirmation message to customer confirming order with time estimate + end + + def request_to_API + # make request to API to send message to customer + end +end + +``` + +## Integration Tests + +```ruby + +# Gets all dishes + +menu = DishList.new +dish_1 = Dish.new("Pizza", 12.00) +dish_2 = Dish.new("Pasta", 11.00) +menu.add(dish_1) +menu.add(dish_2) +menu.list => [dish_1, dish_2] + +menu = DishList.new +dish_1 = Dish.new("Pizza", 12.00) +dish_2 = Dish.new("Pasta", 11.00) +dish_3 = Dish.new("Cake", 7.00) +menu.add(dish_1) +menu.add(dish_2) +dish_1.select +dish_2.select +menu.selection => [dish_1, dish_2] + +dish_list = DishList.new +dish_1 = Dish.new("pizza", 12.00) +dish_2 = Dish.new("pasta", 12.00) +dish_3 = Dish.new("cake", 12.00) +dish_list.add(dish_1) +dish_list.add(dish_2) +dish_list.add(dish_3) +dish_1.selected +dish_2.selected +dish_2.deselect +dish_3.selected +dish_list.selection => [dish_1, dish_3] + +dish_list = DishList.new +dish_1 = Dish.new("pizza", 12.00) +dish_2 = Dish.new("pasta", 12.00) +dish_3 = Dish.new("cake", 12.00) +dish_list.add(dish_1) +dish_list.add(dish_2) +dish_list.add(dish_3) +dish_1.selected +dish_2.selected +dish_list.receipt => ["pizza, £12.00", "pasta, £12.00"] + +dish_list = DishList.new +dish_1 = Dish.new("pizza", 12.00) +dish_2 = Dish.new("pasta", 12.00) +dish_3 = Dish.new("cake", 12.00) +dish_list.add(dish_1) +dish_list.add(dish_2) +dish_list.add(dish_3) +dish_1.selected +dish_2.selected +dish_list.receipt => ["pizza, £12.00", "pasta, £12.00", "Total cost: £24.00"] +``` + +## Unit Tests + +```ruby + +# Constructs DishList + +dish_list = DishList.new +dish_list.list => [] + +dish_list = DishList.new +dish_list.selection => [] + +dish_list = DishList.new +dish_1 = double :fake_dish +dish_2 = double :fake_dish +dish_list.add(dish_1) +dish_list.add(dish_2) +dish_list.list => [dish_1, dish_2] + +dish_list = DishList.new +dish_1 = double :dish, is_selected?: true +dish_2 = double :dish, is_selected?: true +dish_3 = double :dish, is_selected?: false +dish_list.add(dish_1) +dish_list.add(dish_2) +dish_list.add(dish_3) +dish_list.selection => [dish_1, dish_2] + +dish_list = DishList.new +dish_1 = double :dish, is_selected?: true, format: "pizza, £12.00", price: 12.00 +dish_2 = double :dish, is_selected?: true, format: "pasta, £12.00", price: 12.00 +dish_list.add(dish_1) +dish_list.add(dish_2) +dish_list.receipt => ["pizza, £12.00", "pasta, £12.00", "Total cost: £24.00"] + +dish_list = DishList.new +dish_1 = double :dish, is_selected?: true, price: 12.00 +dish_2 = double :dish, is_selected?: true, price: 12.00 +dish_list.add(dish_1) +dish_list.add(dish_2) +dish_list.grand_totalresult => "Total cost: £24.00" + +``` + +```ruby + +# Constructs Dish + +dish = Dish.new("pizza", 12.00) +dish.dish => "pizza" + +dish = Dish.new("pizza", 12.00) +dish.price => 12.00 + +dish = Dish.new("pizza", 12.00) +dish.format => "pizza, £12.00" + +dish = Dish.new("pizza", 12.00) +dish.selected +dish.is_selected? => true + +dish = Dish.new("pizza", 12.00) +dish.is_selected? => false + +dish = Dish.new("pizza", 12.00) +dish.selected +dish.deselect => false + +``` + diff --git a/lib/text_confirmation.rb b/lib/text_confirmation.rb new file mode 100644 index 0000000000..aadc105122 --- /dev/null +++ b/lib/text_confirmation.rb @@ -0,0 +1,26 @@ +require 'twilio-ruby' +require 'date' + +class Text +def initialize(client_number) + @client_number = client_number + end + + def time_estimate + (Time.now + 3600).strftime("%k:%M") + end + + def message + account_sid = ENV['TWILIO_ACCOUNT_SID'] + auth_token = ENV['TWILIO_AUTH_TOKEN'] + @client = Twilio::REST::Client.new(account_sid, auth_token) + + message = @client.messages.create( + body: "Thank you! Your order was placed and + will be delivered before #{time_estimate}", + from: ENV['BUSINESS_NUMBER'] , + to: "#{@client_number}" ) + + puts message.sid + end +end diff --git a/spec/dish_list_spec.rb b/spec/dish_list_spec.rb new file mode 100644 index 0000000000..3d7604bc43 --- /dev/null +++ b/spec/dish_list_spec.rb @@ -0,0 +1,59 @@ +require 'dish_list' + +RSpec.describe DishList do + + context "initially" do + it "returns empty list" do + dish_list = DishList.new + expect(dish_list.list).to eq [] + end + + it "returns empty selection" do + dish_list = DishList.new + expect(dish_list.selection).to eq [] + end + end + + context "creates a double for dish" do + it "returns fake list" do + dish_list = DishList.new + dish_1 = double :fake_dish + dish_2 = double :fake_dish + dish_list.add(dish_1) + dish_list.add(dish_2) + expect(dish_list.list).to eq [dish_1, dish_2] + end + + it "returns fake selection" do + dish_list = DishList.new + dish_1 = double :dish, is_selected?: true + dish_2 = double :dish, is_selected?: true + dish_3 = double :dish, is_selected?: false + dish_list.add(dish_1) + dish_list.add(dish_2) + dish_list.add(dish_3) + result = dish_list.selection + expect(result).to eq [dish_1, dish_2] + end + + it "returns fake receipt" do + dish_list = DishList.new + dish_1 = double :dish, is_selected?: true, format: "pizza, £12.00", price: 12.00 + dish_2 = double :dish, is_selected?: true, format: "pasta, £12.00", price: 12.00 + dish_list.add(dish_1) + dish_list.add(dish_2) + result = dish_list.receipt + expect(result).to eq ["pizza, £12.00", "pasta, £12.00", "Total cost: £24.00"] + end + + it "returns grand total as string" do + dish_list = DishList.new + dish_1 = double :dish, is_selected?: true, price: 12.00 + dish_2 = double :dish, is_selected?: true, price: 12.00 + dish_list.add(dish_1) + dish_list.add(dish_2) + result = dish_list.grand_total + expect(result).to eq "Total cost: £24.00" + end + end +end diff --git a/spec/dish_spec.rb b/spec/dish_spec.rb new file mode 100644 index 0000000000..1269fc8e44 --- /dev/null +++ b/spec/dish_spec.rb @@ -0,0 +1,43 @@ +require 'dish' + +RSpec.describe Dish do + + context "constructs" do + it "dish" do + dish = Dish.new("pizza", 12.00) + expect(dish.dish).to eq "pizza" + end + + it "price" do + dish = Dish.new("pizza", 12.00) + expect(dish.price).to eq 12.00 + end + end + + context "formats dish" do + it "for a receipt" do + dish = Dish.new("pizza", 12.00) + expect(dish.format).to eq "pizza, £12.00" + end + end + + context "when asking if dish is selected" do + it "returns true if selected" do + dish = Dish.new("pizza", 12.00) + dish.selected + expect(dish.is_selected?).to eq true + end + + it "returns false if not selected" do + dish = Dish.new("pizza", 12.00) + expect(dish.is_selected?).to eq false + end + + it "returns false if dish is deselected" do + dish = Dish.new("pizza", 12.00) + dish.selected + expect(dish.deselect).to eq false + end + end + +end diff --git a/spec/integration_spec.rb b/spec/integration_spec.rb new file mode 100644 index 0000000000..e2d81c86c7 --- /dev/null +++ b/spec/integration_spec.rb @@ -0,0 +1,80 @@ +require 'dish' +require 'dish_list' + +RSpec.describe "integration" do + + context "when dishes are added to the dish list" do + it "returns full list" do + dish_list = DishList.new + dish_1 = Dish.new("pizza", 12.00) + dish_2 = Dish.new("pasta", 12.00) + dish_list.add(dish_1) + dish_list.add(dish_2) + result = dish_list.list + expect(result).to eq [dish_1, dish_2] + end + end + + context "when dishes are selected" do + it "returns list of selected dishes" do + dish_list = DishList.new + dish_1 = Dish.new("pizza", 12.00) + dish_2 = Dish.new("pasta", 12.00) + dish_3 = Dish.new("cake", 12.00) + dish_list.add(dish_1) + dish_list.add(dish_2) + dish_list.add(dish_3) + dish_1.selected + dish_2.selected + result = dish_list.selection + expect(result).to eq [dish_1, dish_2] + end + end + + context "when dishes are selected and then deselected" do + it "returns list of selected dishes" do + dish_list = DishList.new + dish_1 = Dish.new("pizza", 12.00) + dish_2 = Dish.new("pasta", 12.00) + dish_3 = Dish.new("cake", 12.00) + dish_list.add(dish_1) + dish_list.add(dish_2) + dish_list.add(dish_3) + dish_1.selected + dish_2.selected + dish_2.deselect + dish_3.selected + result = dish_list.selection + expect(result).to eq [dish_1, dish_3] + end + end + + context "when dishes are selected" do + it "returns grand total" do + dish_list = DishList.new + dish_1 = Dish.new("pizza", 12.00) + dish_2 = Dish.new("pasta", 12.00) + dish_3 = Dish.new("cake", 12.00) + dish_list.add(dish_1) + dish_list.add(dish_2) + dish_list.add(dish_3) + dish_1.selected + dish_2.selected + expect(dish_list.grand_total).to eq "Total cost: £24.00" + end + + it "returns itemised receipt with grand total" do + dish_list = DishList.new + dish_1 = Dish.new("pizza", 12.00) + dish_2 = Dish.new("pasta", 12.00) + dish_3 = Dish.new("cake", 12.00) + dish_list.add(dish_1) + dish_list.add(dish_2) + dish_list.add(dish_3) + dish_1.selected + dish_2.selected + expect(dish_list.receipt).to eq ["pizza, £12.00", "pasta, £12.00", "Total cost: £24.00"] + end + end + +end