diff --git a/.gitignore b/.gitignore index d1a1edf06f..b89612b7ea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ /**/.DS_Store /coverage +# Environment variables (including keys and phone number) +.env + # Local cache of Rubocop remote config .rubocop-* diff --git a/Gemfile b/Gemfile index 35de4a7f0e..b76200c7e4 100644 --- a/Gemfile +++ b/Gemfile @@ -10,4 +10,5 @@ end group :development, :test do gem 'rubocop', '1.20' + gem 'twilio-ruby', '5.66.2' end diff --git a/Gemfile.lock b/Gemfile.lock index 66064703c7..6bd19009ae 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -5,9 +5,37 @@ GEM ast (2.4.2) diff-lcs (1.4.4) docile (1.4.0) + faraday (1.10.0) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.0) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + faraday-retry (~> 1.0) + ruby2_keywords (>= 0.0.4) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) + faraday-excon (1.1.0) + faraday-httpclient (1.0.1) + faraday-multipart (1.0.3) + multipart-post (>= 1.2, < 3) + faraday-net_http (1.0.1) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + faraday-retry (1.0.3) + jwt (2.3.0) + multipart-post (2.1.1) + nokogiri (1.13.4-arm64-darwin) + racc (~> 1.4) parallel (1.20.1) parser (3.0.2.0) ast (~> 2.4.1) + racc (1.5.1) rainbow (3.0.0) regexp_parser (2.1.1) rexml (3.2.5) @@ -36,6 +64,7 @@ GEM rubocop-ast (1.11.0) parser (>= 3.0.1.1) ruby-progressbar (1.11.0) + ruby2_keywords (0.0.5) simplecov (0.21.2) docile (~> 1.1) simplecov-html (~> 0.11) @@ -48,6 +77,10 @@ GEM simplecov_json_formatter (0.1.3) terminal-table (3.0.1) unicode-display_width (>= 1.1.1, < 3) + twilio-ruby (5.66.2) + faraday (>= 0.9, < 2.0) + jwt (>= 1.5, <= 2.5) + nokogiri (>= 1.6, < 2.0) unicode-display_width (2.0.0) PLATFORMS @@ -58,6 +91,7 @@ DEPENDENCIES rubocop (= 1.20) simplecov simplecov-console + twilio-ruby (= 5.66.2) RUBY VERSION ruby 3.0.2p107 diff --git a/README.md b/README.md index dbcb154e43..ee09881d82 100644 --- a/README.md +++ b/README.md @@ -17,18 +17,21 @@ Takeaway Challenge Instructions ------- -* Feel free to use google, your notes, books, etc. but work on your own -* If you refer to the solution of another coach or student, please put a link to that in your README -* If you have a partial solution, **still check in a partial solution** -* You must submit a pull request to this repo with your code by 9am Monday morning +* Use IRB to require the order.rb file +* Create a new instance of Order to be able to use the takeaway. +* Create a new instance of Menu to be able to see the menu. -Task ------ +* Use the show_menu method to see what's available at the takeaway. -* Fork this repo -* Run the command 'bundle' in the project directory to ensure you have all the gems -* Write a Takeaway program with the following user stories: +* The pick_item menu is passed an item from the menu and a quantity to add items to your order. +* The order_summary method will show a simplified view of your order, including the total so far. +* When you're ready, call the place_order method to submit your order. + - A text will be sent to your phone number with a summary and expected delivery time. + + +User stories fulfilled +-------- ``` As a customer So that I can check if I want to order something @@ -45,39 +48,4 @@ I would like to check that the total I have been given matches the sum of the va 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 -``` - -* Hints on functionality to implement: - * Ensure you have a list of dishes with prices - * The text should state that the order was placed successfully and that it will be delivered 1 hour from now, e.g. "Thank you! Your order was placed and will be delivered before 18:52". - * The text sending functionality should be implemented using Twilio API. You'll need to register for it. It’s free. - * Use the twilio-ruby gem to access the API - * Use the Gemfile to manage your gems - * Make sure that your Takeaway is thoroughly tested and that you use mocks and/or stubs, as necessary to not to send texts when your tests are run - * However, if your Takeaway is loaded into IRB and the order is placed, the text should actually be sent - * Note that you can only send texts in the same country as you have your account. I.e. if you have a UK account you can only send to UK numbers. - -* Advanced! (have a go if you're feeling adventurous): - * Implement the ability to place orders via text message. - -* A free account on Twilio will only allow you to send texts to "verified" numbers. Use your mobile phone number, don't worry about the customer's mobile phone. - -> :warning: **WARNING:** think twice before you push your **mobile number** or **Twilio API Key** to a public space like GitHub :eyes: -> -> :key: Now is a great time to think about security and how you can keep your private information secret. You might want to explore environment variables. - -* Finally submit a pull request before Monday at 9am with your solution or partial solution. However much or little amount of code you wrote please please please submit a pull request before Monday at 9am - - -In code review we'll be hoping to see: - -* All tests passing -* High [Test coverage](https://github.com/makersacademy/course/blob/main/pills/test_coverage.md) (>95% is good) -* The code is elegant: every class has a clear responsibility, methods are short etc. - -Reviewers will potentially be using this [code review rubric](docs/review.md). Referring to this rubric in advance will make the challenge somewhat easier. You should be the judge of how much challenge you want this at this moment. - -Notes on Test Coverage ------------------- - -You can see your [test coverage](https://github.com/makersacademy/course/blob/main/pills/test_coverage.md) when you run your tests. +``` \ No newline at end of file diff --git a/lib/menu.rb b/lib/menu.rb new file mode 100644 index 0000000000..8ddae42b60 --- /dev/null +++ b/lib/menu.rb @@ -0,0 +1,22 @@ +class Menu + + attr_reader :menu + + def initialize + + @menu = { + "Pepperoni Pizza" => 9, + "Margherita Pizza" => 7, + "Neapolitan Pizza" => 11, + "Fiorentina Pizza" => 10, + } + + end + + def show_menu + + @menu + + end + +end diff --git a/lib/order.rb b/lib/order.rb new file mode 100644 index 0000000000..76c2316f7a --- /dev/null +++ b/lib/order.rb @@ -0,0 +1,103 @@ +require_relative 'menu' +require 'rubygems' +require 'twilio-ruby' +require 'time' +# require 'sinatra' + +# post '/message' do +# number = params['From'] +# body = params['Body'] + +# content_type 'text/xml' +# " +# +# Hello #{number}. You said: #{body} +# +# " +# end + +class Order + + attr_reader :order, :order_total, :simp_order + + def initialize + + @menu = Menu.new + @order = [] + @order_total = 0 + @simp_order = "" + + account_sid = ENV['ACCOUNT_SID'] + auth_token = ENV['AUTH_TOKEN'] + + @client = Twilio::REST::Client.new(account_sid, auth_token) + + end + + def pick_item(item, quantity) + + fail "Please choose from the menu" unless @menu.menu.has_key?(item) + quantity.times { @order << item } + + end + + def order_value + + count = 0 + @order.each do |order_item| + if @menu.menu.has_key?(order_item) + count += @menu.menu[order_item] + end + end + @order_total + count + + end + + def arrival_time + + (Time.now + 1 * 60 * 60).strftime("%k:%M") + + end + + def place_order + + send_order_text + "Thanks! Your order has been received and will be with you by #{arrival_time}" + + end + + def order_summary + + "Your order so far is: #{simplify_order}" + + end + + def simplify_order + + pep = @order.count("Pepperoni Pizza") + marg = @order.count("Margherita Pizza") + neap = @order.count("Neapolitan Pizza") + fior = @order.count("Fiorentina Pizza") + + @simp_order << "#{pep}x Pepperoni Pizza " if @order.include?("Pepperoni Pizza") + @simp_order << "#{marg}x Margherita Pizza " if @order.include?("Margherita Pizza") + @simp_order << "#{neap}x Neapolitan Pizza " if @order.include?("Neapolitan Pizza") + @simp_order << "#{fior}x Fiorentina Pizza " if @order.include?("Fiorentina Pizza") + + @simp_order << "| Totalling: £#{order_value}" + + end + + private + + def send_order_text + + @client.messages.create( + from: '+19805504523', + to: ENV['PHONE_NUM'], + body: "Thanks for your order! #{@simp_order} will be with you by: #{arrival_time}" + ) + + end + +end diff --git a/spec/menu_spec.rb b/spec/menu_spec.rb new file mode 100644 index 0000000000..f11f54a2e8 --- /dev/null +++ b/spec/menu_spec.rb @@ -0,0 +1,26 @@ +require 'menu' + +describe Menu do + + context "the menu can list its items, along with their prices" do + + it "initialises with a hash, which is the menu" do + + expect(subject.menu).to eq({ + "Pepperoni Pizza" => 9, + "Margherita Pizza" => 7, + "Neapolitan Pizza" => 11, + "Fiorentina Pizza" => 10, + }) + + end + + it "displays the menu when called" do + + expect(subject.show_menu).to eq subject.menu + + end + + end + +end diff --git a/spec/order_spec.rb b/spec/order_spec.rb new file mode 100644 index 0000000000..20159ddb24 --- /dev/null +++ b/spec/order_spec.rb @@ -0,0 +1,80 @@ +require 'order' +require 'time' + +describe Order do + + context "A customer can select a number of items from the menu as their order" do + + it "Subject responds to the pick_item method" do + + expect(subject).to respond_to :pick_item + + end + + it "A customer can start their order by adding an item to their order" do + + subject.pick_item("Pepperoni Pizza", 1) + expect(subject.order).to eq(["Pepperoni Pizza"]) + + end + + it "A customer can check their order total is correct by summing up the prices of their order items" do + + subject.pick_item("Pepperoni Pizza", 4) + expect(subject.order_value).to eq 36 + + end + + it "Raises an error when customers try to order something that's not on the menu" do + + expect { subject.pick_item("Hawaiian", 1) }.to raise_error("Please choose from the menu") + + end + + it "A customer can order an item multiple times in one go" do + + subject.pick_item("Pepperoni Pizza", 4) + expect(subject.order).to eq(["Pepperoni Pizza", "Pepperoni Pizza", "Pepperoni Pizza", "Pepperoni Pizza"]) + + end + + end + + context "A customer can place an order" do + + it "the time method can be used to print the current time plus one hour" do + + expect(subject.arrival_time).to eq (Time.now + 1 * 60 * 60).strftime("%k:%M") + + end + + it "a customer can submit their order" do + + subject.pick_item("Pepperoni Pizza", 4) + allow(subject).to receive(:send_order_text).and_return "Text sent!" + + expect(subject.place_order).to eq "Thanks! Your order has been received and will be with you by #{subject.arrival_time}" + + end + + it "a customer gets an current order summary when adding an item to their order" do + + subject.pick_item("Pepperoni Pizza", 4) + + expect(subject.order_summary).to eq "Your order so far is: #{subject.simp_order}" + + end + + it "a simplified order summary gives quantities of items ordered" do + + subject.pick_item("Pepperoni Pizza", 4) + subject.pick_item("Fiorentina Pizza", 4) + subject.order_value + + expect(subject.simplify_order).to eq "4x Pepperoni Pizza 4x Fiorentina Pizza | Totalling: £#{subject.order_value}" + + end + + end + +end