hola_shopping_cart 0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +111 -0
- data/bin/hola +5 -0
- data/lib/hola/app.rb +40 -0
- data/lib/hola/cart/item/processor.rb +50 -0
- data/lib/hola/cart/item.rb +28 -0
- data/lib/hola/cart/renderer.rb +35 -0
- data/lib/hola/cart.rb +31 -0
- data/lib/hola/errors.rb +5 -0
- data/lib/hola/offer/get_one_free.rb +29 -0
- data/lib/hola/offer/no_offer.rb +10 -0
- data/lib/hola/offer/strawberry_bulk_discount.rb +29 -0
- data/lib/hola/offer/two_thirds_bulk_discount.rb +33 -0
- data/lib/hola/offer.rb +33 -0
- data/lib/hola/product/inventory.rb +30 -0
- data/lib/hola/product/selector.rb +45 -0
- data/lib/hola/product.rb +52 -0
- data/lib/hola/utils/money.rb +38 -0
- data/lib/hola.rb +13 -0
- metadata +137 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 519019c411f966ece64787aca2326ccc50fedea00d6fb581035d1ea50e3b95ad
|
4
|
+
data.tar.gz: daedaf24f05f04c2d3c2ab2eb5f315a7d7d1c33971fe7d4e9fe9e0b580ec0c4b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e0d042982394c72a4b702a261d3616c8af16bbd266f09ec3cc4b7896bea2bda1d164c4a23eab41a5e65583a2c4f7fe741cc49ea1670343bb7c37d9e35c151a4e
|
7
|
+
data.tar.gz: 2447418bbba5340df270c318ba10146ef6345393d08928f529ee2902482d12972ef46738f7ef3577067e1a48970134666b2564d1ed11166bf81c9a38ee8e1b28
|
data/README.md
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
# Hola Shopping Cart 🛍️
|
2
|
+
|
3
|
+
## Challenge
|
4
|
+
> *Build an app that supports adding products to a shopping cart and displaying a total price.*
|
5
|
+
> | *Product code* | *Name* | *Price* |
|
6
|
+
> | -------------- | -------------- | --------- |
|
7
|
+
> | *GR1* | *Green Tea* | *3.11 €* |
|
8
|
+
> | *SR1* | *Strawberries* | *5.00 €* |
|
9
|
+
> | *CF1* | *Coffee* | *11.23 €* |
|
10
|
+
> ### *Special conditions*
|
11
|
+
> * *Green Tea has buy-one-get-one-free offer*
|
12
|
+
>
|
13
|
+
> * *Strawberries price changes to 4.50€ if you buy 3 or more*
|
14
|
+
>
|
15
|
+
> * *Coffee price drops to 2/3 of the original price if you buy 3 or more*
|
16
|
+
>
|
17
|
+
> *Shopping cart can accept items in any order and be flexible regarding pricing rules.*
|
18
|
+
> ### *Test data*
|
19
|
+
>| *Basket* | *Total price expected* |
|
20
|
+
>| ------------------------- | ---------------------- |
|
21
|
+
>| *GR1, GR1* | *3.11€* |
|
22
|
+
>| *SR1, SR1, GR1, SR1* | *16.61€* |
|
23
|
+
>| *GR1, CF1, SR1, CF1, CF1* | *30.57€* |
|
24
|
+
|
25
|
+
|
26
|
+
## Solution
|
27
|
+
|
28
|
+
### Hola Shopping Cart 🛍️
|
29
|
+
|
30
|
+
*Are you tired of shopping on over-designed webshops? Hola Shopping Cart is a simple, eyes-friendly CLI application to browse products and fill the shopping cart. Don't leave your favorite terminal to go shopping.*
|
31
|
+
|
32
|
+

|
33
|
+
|
34
|
+
|
35
|
+
## Installation
|
36
|
+
|
37
|
+
*Requirements: Ruby >= 3.0.0 installed*
|
38
|
+
|
39
|
+
Available as a gem through [RubyGems](https://rubygems.org/gems/hola_shopping_cart), install it:
|
40
|
+
|
41
|
+
```
|
42
|
+
$ gem install hola_shopping_cart
|
43
|
+
```
|
44
|
+
|
45
|
+
## Usage
|
46
|
+
In order to start shopping, run the command:
|
47
|
+
|
48
|
+
```
|
49
|
+
$ hola
|
50
|
+
```
|
51
|
+
Use your keyboard's Up/Down arrow keys to navigate the product list. Press Enter to select a product. Select a quantity by inserting a number. When you want to finish shopping, press `n` to see the shopping cart.
|
52
|
+
|
53
|
+
## Arhitecture
|
54
|
+
Three primary actions happen in the life cycle of the app:
|
55
|
+
* User input capture
|
56
|
+
* Processing items in a cart
|
57
|
+
* Rendering a cart to display it
|
58
|
+
|
59
|
+

|
60
|
+
|
61
|
+
### User input
|
62
|
+
The underlying library [TTY::Prompt](https://github.com/piotrmurach/tty-prompt) is used to ease up gathering user input from the command line. The library has a number of handy prompt types; one of them is `select`, which is used to show a list of available products and make a selection. The command line immediately displays a user selection for a great user experience. After that, it's up to the processing action to take over.
|
63
|
+
|
64
|
+
### Processing
|
65
|
+
Processing action is responsible for fetching necessary data, processing the product offers, and answering with `Cart::Item`. The item is then stored in the `Cart`, and processing is complete. The processing is done intentionally before adding an item to the cart to avoid big performance spikes at the end when the cart is rendered. That brings additional benefits. For example, if the product price or offer changes in the middle of the user's shopping, the total price will respect the price shown when the product is added to the cart.
|
66
|
+
|
67
|
+
Sequence diagram of processing an item:
|
68
|
+

|
69
|
+
|
70
|
+
### Cart render
|
71
|
+
The [TTY::Table](https://github.com/piotrmurach/tty-table) table formatting component is used to perform rendering. The table is rendered with an ASCII-type border.
|
72
|
+
```
|
73
|
+
+------------+------+--------+--------+------------------------+
|
74
|
+
|Item |Price |Quantity|Subtotal|Offer |
|
75
|
+
+------------+------+--------+--------+------------------------+
|
76
|
+
|Green Tea |3.11€ |1 |3.11€ | |
|
77
|
+
|Strawberries|5.00€ |1 |5.00€ | |
|
78
|
+
|Coffee |11.23€|3 |22.46€ |Two-Thirds Bulk Discount|
|
79
|
+
+------------+------+--------+--------+------------------------+
|
80
|
+
|Total | | |30.57€ | |
|
81
|
+
+------------+------+--------+--------+------------------------+
|
82
|
+
```
|
83
|
+
|
84
|
+
### Flexibility
|
85
|
+
The architecture is designed to be flexible as much as possible when comes to adding new special offers or modifying existing ones.
|
86
|
+
|
87
|
+
To add a new special offer, you must create a new file inside `hola/offers`. The example below shows adding a price reduction of 20%.
|
88
|
+
```ruby
|
89
|
+
module Hola
|
90
|
+
class Offer
|
91
|
+
class NewSpecialDiscount < Offer
|
92
|
+
def price
|
93
|
+
super * 0.8
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
```
|
99
|
+
The last step is to add a newly created special offer to a product.
|
100
|
+
```ruby
|
101
|
+
product.apply_offer("NewSpecialDiscount")
|
102
|
+
```
|
103
|
+
|
104
|
+
## Tests
|
105
|
+
```
|
106
|
+
rspec ./spec
|
107
|
+
.............................................................................................
|
108
|
+
|
109
|
+
Finished in 0.0316 seconds (files took 0.15474 seconds to load)
|
110
|
+
93 examples, 0 failures
|
111
|
+
```
|
data/bin/hola
ADDED
data/lib/hola/app.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tty-prompt"
|
4
|
+
require "hola/product/selector"
|
5
|
+
require "hola/cart/renderer"
|
6
|
+
require "hola/cart"
|
7
|
+
|
8
|
+
# This class facilitates user flow experience.
|
9
|
+
# It allows user to select products and prints the cart.
|
10
|
+
module Hola
|
11
|
+
class App
|
12
|
+
def initialize(prompt = TTY::Prompt.new)
|
13
|
+
@prompt = prompt
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
prompt.say("Hola. It's shopping time 🛍️")
|
18
|
+
|
19
|
+
loop do
|
20
|
+
selection = Product::Selector.new(prompt).perform
|
21
|
+
cart.add(
|
22
|
+
product_id: selection[:product_id],
|
23
|
+
quantity: selection[:quantity]
|
24
|
+
)
|
25
|
+
|
26
|
+
break unless prompt.yes?("Would you like to add more items?")
|
27
|
+
end
|
28
|
+
|
29
|
+
prompt.say(Cart::Renderer.new(cart).perform)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :prompt
|
35
|
+
|
36
|
+
def cart
|
37
|
+
@cart ||= Cart.new
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "hola/cart/item"
|
4
|
+
require "hola/offer"
|
5
|
+
Dir.glob(File.expand_path("../../offer/*.rb", __dir__), &method(:require))
|
6
|
+
|
7
|
+
# The item processor is responsible for processing the item before
|
8
|
+
# it's added to a cart. It applies the product's offer
|
9
|
+
# and responds with a cart item value object.
|
10
|
+
module Hola
|
11
|
+
class Cart
|
12
|
+
class Item
|
13
|
+
class Processor
|
14
|
+
attr_reader :product_id, :quantity
|
15
|
+
|
16
|
+
def initialize(product_id:, quantity:)
|
17
|
+
@product_id = product_id
|
18
|
+
@quantity = quantity
|
19
|
+
end
|
20
|
+
|
21
|
+
def perform
|
22
|
+
Cart::Item.new(
|
23
|
+
product: product,
|
24
|
+
quantity: quantity,
|
25
|
+
subtotal: offer.subtotal,
|
26
|
+
offer: (offer.name if offer.applied?)
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def product
|
33
|
+
@product ||= Product.find(product_id)
|
34
|
+
end
|
35
|
+
|
36
|
+
def offer
|
37
|
+
@offer ||= begin
|
38
|
+
klass = product.offer? ? product.offer : "NoOffer"
|
39
|
+
Object.const_get("Hola::Offer::#{klass}").new(
|
40
|
+
product: product,
|
41
|
+
quantity: quantity
|
42
|
+
)
|
43
|
+
end
|
44
|
+
rescue NameError
|
45
|
+
raise ArgumentError.new("Offer not found")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Value object for Item in a Cart.
|
4
|
+
# It only knows how to display the values.
|
5
|
+
module Hola
|
6
|
+
class Cart
|
7
|
+
class Item
|
8
|
+
attr_reader :product, :quantity, :subtotal, :offer
|
9
|
+
|
10
|
+
def initialize(product: nil, quantity: 0, subtotal: 0, offer: "")
|
11
|
+
@product = product
|
12
|
+
@quantity = quantity
|
13
|
+
@subtotal = subtotal
|
14
|
+
@offer = offer
|
15
|
+
end
|
16
|
+
|
17
|
+
def output
|
18
|
+
[
|
19
|
+
product.name,
|
20
|
+
Utils::Money.to_currency(product.price),
|
21
|
+
quantity,
|
22
|
+
Utils::Money.to_currency(subtotal),
|
23
|
+
offer
|
24
|
+
]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tty-table"
|
4
|
+
require "hola/product"
|
5
|
+
|
6
|
+
# Renders cart in table format
|
7
|
+
module Hola
|
8
|
+
class Cart
|
9
|
+
class Renderer
|
10
|
+
def initialize(cart)
|
11
|
+
@cart = cart
|
12
|
+
end
|
13
|
+
|
14
|
+
def perform
|
15
|
+
table.render(:ascii)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :cart
|
21
|
+
|
22
|
+
def table
|
23
|
+
@table ||= TTY::Table.new(
|
24
|
+
header: %w[Item Price Quantity Subtotal Offer],
|
25
|
+
rows: [
|
26
|
+
:separator,
|
27
|
+
*cart.output,
|
28
|
+
:separator,
|
29
|
+
["Total", "", "", Utils::Money.to_currency(cart.total), ""]
|
30
|
+
]
|
31
|
+
)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/hola/cart.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "hola/cart/item/processor"
|
4
|
+
require "hola/cart/item"
|
5
|
+
require "hola/product"
|
6
|
+
|
7
|
+
# The cart is responsible for holding list of items
|
8
|
+
module Hola
|
9
|
+
class Cart
|
10
|
+
attr_reader :items
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@items = Hash.new { Cart::Item.new }
|
14
|
+
end
|
15
|
+
|
16
|
+
def add(product_id:, quantity:)
|
17
|
+
items[product_id] = Cart::Item::Processor.new(
|
18
|
+
product_id: product_id,
|
19
|
+
quantity: items[product_id].quantity + quantity
|
20
|
+
).perform
|
21
|
+
end
|
22
|
+
|
23
|
+
def total
|
24
|
+
items.sum { |_k, item| item.subtotal }
|
25
|
+
end
|
26
|
+
|
27
|
+
def output
|
28
|
+
items.map { |_k, item| item.output }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/hola/errors.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hola
|
4
|
+
class Offer
|
5
|
+
class GetOneFree < Offer
|
6
|
+
def name
|
7
|
+
"Get One Free"
|
8
|
+
end
|
9
|
+
|
10
|
+
def quantity
|
11
|
+
@_quantity ||= super - quantity_reduction
|
12
|
+
end
|
13
|
+
|
14
|
+
def applied?
|
15
|
+
eligible?
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def quantity_reduction
|
21
|
+
eligible? ? 1 : 0
|
22
|
+
end
|
23
|
+
|
24
|
+
def eligible?
|
25
|
+
@quantity > 1
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bigdecimal"
|
4
|
+
|
5
|
+
module Hola
|
6
|
+
class Offer
|
7
|
+
class StrawberryBulkDiscount < Offer
|
8
|
+
DISCOUNTED_PRICE = BigDecimal("4.5")
|
9
|
+
|
10
|
+
def name
|
11
|
+
"Strawberry Bulk Discount"
|
12
|
+
end
|
13
|
+
|
14
|
+
def price
|
15
|
+
eligible? ? DISCOUNTED_PRICE : super
|
16
|
+
end
|
17
|
+
|
18
|
+
def applied?
|
19
|
+
eligible?
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def eligible?
|
25
|
+
quantity >= 3
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bigdecimal"
|
4
|
+
|
5
|
+
module Hola
|
6
|
+
class Offer
|
7
|
+
class TwoThirdsBulkDiscount < Offer
|
8
|
+
def name
|
9
|
+
"Two-Thirds Bulk Discount"
|
10
|
+
end
|
11
|
+
|
12
|
+
def price
|
13
|
+
super * discount
|
14
|
+
end
|
15
|
+
|
16
|
+
def applied?
|
17
|
+
eligible?
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def discount
|
23
|
+
return BigDecimal(1) unless eligible?
|
24
|
+
|
25
|
+
BigDecimal(2) / BigDecimal(3)
|
26
|
+
end
|
27
|
+
|
28
|
+
def eligible?
|
29
|
+
quantity >= 3
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/hola/offer.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# A parent class that all offers should inherit from
|
4
|
+
module Hola
|
5
|
+
class Offer
|
6
|
+
attr_reader :quantity
|
7
|
+
|
8
|
+
def initialize(product:, quantity:)
|
9
|
+
@product = product
|
10
|
+
@quantity = quantity
|
11
|
+
end
|
12
|
+
|
13
|
+
def name
|
14
|
+
""
|
15
|
+
end
|
16
|
+
|
17
|
+
def price
|
18
|
+
@price ||= product.price
|
19
|
+
end
|
20
|
+
|
21
|
+
def applied?
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
def subtotal
|
26
|
+
price * quantity
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_reader :product
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "singleton"
|
4
|
+
require "hola/product"
|
5
|
+
|
6
|
+
# The inventory stores products.
|
7
|
+
# The Hola shop has only 3 products in stock.
|
8
|
+
# This class can be extended to support bulk import of products from files
|
9
|
+
# provided via the command line or
|
10
|
+
# get completely replaced by the database in the future.
|
11
|
+
module Hola
|
12
|
+
class Product
|
13
|
+
class Inventory
|
14
|
+
include Singleton
|
15
|
+
attr_reader :products
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@products = [
|
19
|
+
Product.new(name: "Green Tea", price: 3.11, offer: "GetOneFree"),
|
20
|
+
Product.new(name: "Strawberries", price: 5.0, offer: "StrawberryBulkDiscount"),
|
21
|
+
Product.new(name: "Coffee", price: 11.23, offer: "TwoThirdsBulkDiscount")
|
22
|
+
]
|
23
|
+
end
|
24
|
+
|
25
|
+
def find_product(id)
|
26
|
+
products.find { |p| p.id == id }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tty-prompt"
|
4
|
+
require "hola/product"
|
5
|
+
|
6
|
+
# This class provides an interface for a user to select a product
|
7
|
+
module Hola
|
8
|
+
class Product
|
9
|
+
class Selector
|
10
|
+
def initialize(prompt)
|
11
|
+
@prompt = prompt
|
12
|
+
end
|
13
|
+
|
14
|
+
def perform
|
15
|
+
{
|
16
|
+
product_id: select_product,
|
17
|
+
quantity: select_quantity
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :prompt
|
24
|
+
|
25
|
+
def product_options
|
26
|
+
@product_options ||= Product.list.map(&:to_option)
|
27
|
+
end
|
28
|
+
|
29
|
+
def select_product
|
30
|
+
prompt.select("Choose a product", product_options)
|
31
|
+
end
|
32
|
+
|
33
|
+
def select_quantity
|
34
|
+
prompt.ask(
|
35
|
+
"Select quantity (stock: 100)? ",
|
36
|
+
convert: :int
|
37
|
+
) do |q|
|
38
|
+
q.in("1-100")
|
39
|
+
q.messages[:range?] = "out of expected range"
|
40
|
+
q.messages[:convert?] = "not a number"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/hola/product.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "hola/product/inventory"
|
4
|
+
require "hola/utils/money"
|
5
|
+
require "securerandom"
|
6
|
+
|
7
|
+
module Hola
|
8
|
+
class Product
|
9
|
+
class << self
|
10
|
+
def list
|
11
|
+
Inventory.instance.products
|
12
|
+
end
|
13
|
+
|
14
|
+
def find(id)
|
15
|
+
Inventory.instance.find_product(id)
|
16
|
+
end
|
17
|
+
|
18
|
+
def currency
|
19
|
+
"€"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :id, :name, :price, :offer
|
24
|
+
|
25
|
+
def initialize(name:, price:, offer: "")
|
26
|
+
@id = SecureRandom.uuid
|
27
|
+
@name = name
|
28
|
+
@price = Utils::Money.parse(price)
|
29
|
+
@offer = offer
|
30
|
+
end
|
31
|
+
|
32
|
+
def offer?
|
33
|
+
!offer.empty?
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_option
|
37
|
+
{name: title, value: id}
|
38
|
+
end
|
39
|
+
|
40
|
+
def apply_offer(name)
|
41
|
+
self.offer = name
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
attr_writer :offer
|
47
|
+
|
48
|
+
def title
|
49
|
+
"#{name} (#{Utils::Money.to_currency(price)})"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bigdecimal"
|
4
|
+
require "hola/errors"
|
5
|
+
|
6
|
+
module Hola
|
7
|
+
module Utils
|
8
|
+
class Money
|
9
|
+
class << self
|
10
|
+
def parse(...)
|
11
|
+
new(...).parse
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_currency(...)
|
15
|
+
new(...).to_currency
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(value)
|
20
|
+
@value = value
|
21
|
+
end
|
22
|
+
|
23
|
+
def parse
|
24
|
+
BigDecimal((value || 0).to_s.strip)
|
25
|
+
rescue ArgumentError, TypeError => err
|
26
|
+
raise Error.new(err.message)
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_currency
|
30
|
+
"#{format("%.2f", value)}#{Product.currency}"
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
attr_reader :value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/hola.rb
ADDED
metadata
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hola_shopping_cart
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.4'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Luka Domitrovic
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-02-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: tty-prompt
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.23.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.23.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: tty-table
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.12.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.12.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.12'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.12'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec-uuid
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.5.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.5.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.60.2
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.60.2
|
83
|
+
description: " Are you tired of shopping on over-designed webshops? \n Hola Shopping
|
84
|
+
Cart is a simple, eyes-friendly CLI application to browse \n products and fill
|
85
|
+
the shopping cart. \n Don't leave your favorite terminal to go shopping. \n It's
|
86
|
+
an example app for a technical assignment ;)\n"
|
87
|
+
email:
|
88
|
+
- luka@domitrovic.si
|
89
|
+
executables:
|
90
|
+
- hola
|
91
|
+
extensions: []
|
92
|
+
extra_rdoc_files: []
|
93
|
+
files:
|
94
|
+
- README.md
|
95
|
+
- bin/hola
|
96
|
+
- lib/hola.rb
|
97
|
+
- lib/hola/app.rb
|
98
|
+
- lib/hola/cart.rb
|
99
|
+
- lib/hola/cart/item.rb
|
100
|
+
- lib/hola/cart/item/processor.rb
|
101
|
+
- lib/hola/cart/renderer.rb
|
102
|
+
- lib/hola/errors.rb
|
103
|
+
- lib/hola/offer.rb
|
104
|
+
- lib/hola/offer/get_one_free.rb
|
105
|
+
- lib/hola/offer/no_offer.rb
|
106
|
+
- lib/hola/offer/strawberry_bulk_discount.rb
|
107
|
+
- lib/hola/offer/two_thirds_bulk_discount.rb
|
108
|
+
- lib/hola/product.rb
|
109
|
+
- lib/hola/product/inventory.rb
|
110
|
+
- lib/hola/product/selector.rb
|
111
|
+
- lib/hola/utils/money.rb
|
112
|
+
homepage: https://rubygems.org/gems/hola_shopping_cart
|
113
|
+
licenses:
|
114
|
+
- MIT
|
115
|
+
metadata:
|
116
|
+
homepage_uri: https://rubygems.org/gems/hola_shopping_cart
|
117
|
+
source_code_uri: https://github.com/luxxi/hola_shopping_cart
|
118
|
+
post_install_message:
|
119
|
+
rdoc_options: []
|
120
|
+
require_paths:
|
121
|
+
- lib
|
122
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - "~>"
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '3.0'
|
127
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
requirements: []
|
133
|
+
rubygems_version: 3.5.3
|
134
|
+
signing_key:
|
135
|
+
specification_version: 4
|
136
|
+
summary: Hola Shopping Cart CLI app
|
137
|
+
test_files: []
|