salestax 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a0fd7c247bcbb8a37acc5defcc9a57b095a9f0b4
4
+ data.tar.gz: c2741cf4cbaa3dffbe9f1f2367aa5ce1fe45fe40
5
+ SHA512:
6
+ metadata.gz: 6bb6e03211201492c8f448b7e9094e5c50e1863f5dacfb30c2e06294bc961d22a4364c9e6d7f432d6ecb22e0317a0e7171ec8a44208580b84ab11b81bc3b00b6
7
+ data.tar.gz: 11346abbfd2b1a2127ea88e70cb928802ee7aeabaa1ce64c4b50826cdef649efdd54447cc8f89a7f017d690efd442e22960ba78863fb040099a9ef094cb23b40
File without changes
@@ -0,0 +1,67 @@
1
+ Sales Tax
2
+ ==========
3
+
4
+ A basic sales tax calculator.
5
+
6
+ ## Description
7
+
8
+ Basic sales tax is applicable at a rate of 10% on all goods, except books, food, and medical products that are exempt. Import duty is an additional sales tax applicable on all imported goods at a rate of 5%, with no exemptions.
9
+
10
+ When I purchase items I receive a receipt that lists the name of all the items and their price (including tax), finishing with the total cost of the items, and the total amounts of sales taxes paid. The rounding rules for sales tax are that for a tax rate of n%, a shelf price of p contains (np/100 rounded up to the nearest 0.05) amount of sales tax.
11
+
12
+ Write an application that prints out the receipt details for these shopping carts; this application should be written in Ruby [1] and use Rspec [2] to test inputs and the expected outputs. The output should be to standard out or CSV.
13
+
14
+ Proper object orientated design is important. Each row in the input represents a line item of the receipt.
15
+
16
+ ### Examples
17
+
18
+ #### Example A
19
+
20
+ Input:
21
+
22
+ 1, book, 12.49
23
+ 1, music CD, 14.99
24
+ 1, chocolate bar, 0.85
25
+
26
+ Output:
27
+
28
+ 1, book, 12.49
29
+ 1, music CD, 16.49
30
+ 1, chocolate bar, 0.85
31
+
32
+ Sales Taxes: 1.50
33
+ Total: 29.83
34
+
35
+ #### Example B
36
+
37
+ Input:
38
+
39
+ 1, imported box of chocolates, 10.00
40
+ 1, imported bottle of perfume, 47.50
41
+
42
+ Output:
43
+
44
+ 1, imported box of chocolates, 10.50
45
+ 1, imported bottle of perfume, 54.65
46
+
47
+ Sales Taxes: 7.65
48
+ Total: 65.15
49
+
50
+ #### Example C
51
+
52
+ Input:
53
+
54
+ 1, imported bottle of perfume, 27.99
55
+ 1, bottle of perfume, 18.99
56
+ 1, packet of headache pills, 9.75
57
+ 1, box of imported chocolates, 11.25
58
+
59
+ Output:
60
+
61
+ 1, imported bottle of perfume, 32.19
62
+ 1, bottle of perfume, 20.89
63
+ 1, packet of headache pills, 9.75
64
+ 1, box of imported chocolates, 11.85
65
+
66
+ Sales Taxes: 6.70
67
+ Total: 74.68
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/sales_tax'
4
+
5
+ SalesTax::Application.new.run
@@ -0,0 +1,3 @@
1
+ 1, book, 12.49
2
+ 1, music CD, 14.99
3
+ 1, chocolate bar, 0.85
@@ -0,0 +1,2 @@
1
+ 1, imported box of chocolates, 10.00
2
+ 1, imported bottle of perfume, 47.50
@@ -0,0 +1,4 @@
1
+ 1, imported bottle of perfume, 27.99
2
+ 1, bottle of perfume, 18.99
3
+ 1, packet of headache pills, 9.75
4
+ 1, box of imported chocolates, 11.25
@@ -0,0 +1 @@
1
+ require_relative 'sales_tax/application'
@@ -0,0 +1,57 @@
1
+ require 'bigdecimal'
2
+
3
+ module SalesTax
4
+ module Accountable
5
+ def to_hash
6
+ accountable_hash
7
+ end
8
+
9
+ protected
10
+
11
+ def accountable_hash
12
+ {
13
+ unit_sales_tax: unit_sales_tax.to_s('F'),
14
+ total_unit_price: (unit_price + unit_sales_tax).to_s('F')
15
+ }
16
+ end
17
+
18
+ private
19
+
20
+ def unit_sales_tax
21
+ round_tax(unit_price * sales_tax_rate)
22
+ end
23
+
24
+ def unit_price
25
+ BigDecimal(unit_price_str)
26
+ end
27
+
28
+ def round_tax(tax)
29
+ round_factor = BigDecimal('1')/BigDecimal('0.05')
30
+ (tax * round_factor).ceil/round_factor
31
+ end
32
+
33
+ def sales_tax_rate
34
+ basic_sales_tax_rate + import_duty_sales_tax_rate
35
+ end
36
+
37
+ def import_duty_sales_tax_rate
38
+ imported? ? reference_import_duty_sales_tax_rate : BigDecimal('0')
39
+ end
40
+
41
+ def basic_sales_tax_rate
42
+ basic_sales_tax_exempt? ? BigDecimal('0') : reference_basic_sales_tax_rate
43
+ end
44
+
45
+ def reference_basic_sales_tax_rate
46
+ BigDecimal('0.1')
47
+ end
48
+
49
+ def reference_import_duty_sales_tax_rate
50
+ BigDecimal('0.05')
51
+ end
52
+
53
+ def basic_sales_tax_exempt?
54
+ [:food, :medicine, :book].include?(category)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,27 @@
1
+ require_relative 'line_item/parser'
2
+ require_relative 'receipt'
3
+
4
+ module SalesTax
5
+ class Application
6
+ def initialize(args = {})
7
+ @input = args[:input] || $stdin
8
+ @parser = LineItem::Parser
9
+ @receipt = Receipt.new
10
+ end
11
+
12
+ def run
13
+ loop do
14
+ raw_input = input.gets
15
+ break unless raw_input
16
+ raw_input.chomp!
17
+ line_item = parser.parse(raw_input)
18
+ receipt.add_item(line_item) if line_item
19
+ end
20
+ puts receipt.print
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :input, :parser, :receipt
26
+ end
27
+ end
@@ -0,0 +1,45 @@
1
+ require_relative '../accountable'
2
+
3
+ module SalesTax
4
+ module LineItem
5
+ class Base
6
+ include SalesTax::Accountable
7
+
8
+ def self.augment(attributes)
9
+ new(attributes).to_hash if attributes[:description] =~ description_matcher
10
+ end
11
+
12
+ def initialize(args = {})
13
+ @quantity_str = args[:quantity] || ''
14
+ @description = args[:description]
15
+ @unit_price_str = args[:unit_price] || ''
16
+ end
17
+
18
+ def to_hash
19
+ accountable_hash.merge(
20
+ {
21
+ quantity: quantity_str,
22
+ description: description,
23
+ unit_price: unit_price_str
24
+ }
25
+ )
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :quantity_str, :description, :unit_price_str
31
+
32
+ def self.description_matcher
33
+ /\w+/
34
+ end
35
+
36
+ def category
37
+ :other
38
+ end
39
+
40
+ def imported?
41
+ /imported/.match(description) ? true : false
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,15 @@
1
+ module SalesTax
2
+ module LineItem
3
+ class Book < Base
4
+ def category
5
+ :book
6
+ end
7
+
8
+ private
9
+
10
+ def self.description_matcher
11
+ /book/
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module SalesTax
2
+ module LineItem
3
+ class Food < Base
4
+ def category
5
+ :food
6
+ end
7
+
8
+ private
9
+
10
+ def self.description_matcher
11
+ /chocolate/
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module SalesTax
2
+ module LineItem
3
+ class Medicine < Base
4
+ def category
5
+ :medicine
6
+ end
7
+
8
+ private
9
+
10
+ def self.description_matcher
11
+ /headache/
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,42 @@
1
+ require_relative 'base'
2
+ require_relative 'book'
3
+ require_relative 'food'
4
+ require_relative 'medicine'
5
+
6
+ module SalesTax
7
+ module LineItem
8
+ module Parser
9
+ module_function
10
+
11
+ FormatError = Class.new(StandardError)
12
+
13
+ @input_validator = lambda do |_in|
14
+ raise FormatError unless _in =~ /\A\d+,[^,]*\w+, \d+(.\d)?\d*\z/
15
+ end
16
+
17
+ @pre_processor = lambda do |_in|
18
+ quantity_str, description, unit_price_str = _in.split(',')
19
+
20
+ {
21
+ quantity: quantity_str,
22
+ description: description.strip,
23
+ unit_price: unit_price_str.strip
24
+ }
25
+ end
26
+
27
+ @augmenter = lambda do |_in|
28
+ augmented = nil
29
+ [LineItem::Book, LineItem::Medicine, LineItem::Food].each do |augmenter|
30
+ augmented = augmenter.augment(_in)
31
+ break if augmented
32
+ end
33
+ augmented || LineItem::Base.augment(_in)
34
+ end
35
+
36
+ def parse(_in)
37
+ @input_validator.call(_in)
38
+ @augmenter.call(@pre_processor.call(_in))
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,66 @@
1
+ module SalesTax
2
+ class Receipt
3
+ def initialize(args = {})
4
+ @items = args[:items] || []
5
+ end
6
+
7
+ def add_item(item)
8
+ items << item
9
+ end
10
+
11
+ def print
12
+ _print = ''
13
+ _print << print_items
14
+ _print << "\n"
15
+ _print << print_totals
16
+ _print
17
+ end
18
+
19
+ private
20
+
21
+ attr_accessor :items
22
+
23
+ def print_items
24
+ _items = ''
25
+ items.each { |i| _items << print_item(i) }
26
+ _items
27
+ end
28
+
29
+ def print_item(item)
30
+ taxed_price_str = format_to_money_string(item[:total_unit_price])
31
+ "#{item[:quantity]}, #{item[:description]}, #{taxed_price_str}\n"
32
+ end
33
+
34
+ def print_totals
35
+ total, sales_taxes_total = calculate_totals
36
+ _totals = ''
37
+ _totals << print_sales_taxes_total(sales_taxes_total)
38
+ _totals << print_total(total)
39
+ _totals
40
+ end
41
+
42
+ def calculate_totals
43
+ total = BigDecimal('0')
44
+ sales_taxes_total = BigDecimal('0')
45
+ items.each do |item|
46
+ total += BigDecimal(item[:total_unit_price])
47
+ sales_taxes_total += BigDecimal(item[:unit_sales_tax])
48
+ end
49
+ [total, sales_taxes_total]
50
+ end
51
+
52
+ def print_sales_taxes_total(bd)
53
+ st_total = format_to_money_string(bd.to_s('F'))
54
+ "Sales Taxes: #{st_total}\n"
55
+ end
56
+
57
+ def print_total(bd)
58
+ total = format_to_money_string(bd.to_s('F'))
59
+ "Total: #{total}\n"
60
+ end
61
+
62
+ def format_to_money_string(str)
63
+ sprintf("%.2f", str)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,64 @@
1
+ require_relative '../lib/sales_tax/accountable'
2
+ require_relative 'shared_examples_for_accountable'
3
+ require_relative 'shared_examples_for_taxable'
4
+
5
+ RSpec.describe SalesTax::Accountable do
6
+ AccountableDouble = Struct.new(:unit_price_str, :imported?, :category) do
7
+ include SalesTax::Accountable
8
+ end
9
+
10
+ let(:accountable) {
11
+ AccountableDouble.new('11.25', true, :other)
12
+ }
13
+
14
+ describe 'AccountableDouble' do
15
+ let(:object) { accountable }
16
+ it_behaves_like 'a accountable'
17
+ it_behaves_like 'a taxable'
18
+ end
19
+
20
+ describe '#to_hash' do
21
+ describe '[:total_unit_price]' do
22
+ subject { accountable.to_hash[:unit_sales_tax] }
23
+ it { is_expected.to eq '1.7'}
24
+ end
25
+
26
+ describe '[:unit_sales_tax]'do
27
+ subject {
28
+ AccountableDouble.new('11.25', imported?, category).to_hash[:unit_sales_tax]
29
+ }
30
+
31
+ context 'when basic exempt' do
32
+ ['food', 'medicine', 'book'].each do |cat|
33
+ context "when #{cat}" do
34
+ let(:category) { cat.to_sym }
35
+
36
+ context 'when local' do
37
+ let(:imported?) { false }
38
+ it { is_expected.to eq '0.0' }
39
+ end
40
+
41
+ context 'when imported' do
42
+ let(:imported?) { true }
43
+ it { is_expected.to eq '0.6' }
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ context 'when basic applicable' do
50
+ let(:category) { :other }
51
+
52
+ context 'when local' do
53
+ let(:imported?) { false }
54
+ it { is_expected.to eq '1.15' }
55
+ end
56
+
57
+ context 'when imported' do
58
+ let(:imported?) { true }
59
+ it { is_expected.to eq '1.7' }
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,60 @@
1
+ require_relative '../../lib/sales_tax/application'
2
+
3
+ RSpec.describe SalesTax::Application do
4
+
5
+ let(:app) { lambda { |f| SalesTax::Application.new(input: f).run } }
6
+
7
+ context 'when cart is provided' do
8
+
9
+ shared_examples 'a receipt printer' do |cart, receipt|
10
+ it 'prints the correct receipt' do
11
+ expect { app.call(cart) }.to output(receipt).to_stdout
12
+ end
13
+ end
14
+
15
+ context 'from data/example_input_a.txt' do
16
+ cart = File.open('data/example_input_a.txt', 'r')
17
+
18
+ correct_receipt = <<-END.gsub(/^\s+\|/, '')
19
+ |1, book, 12.49
20
+ |1, music CD, 16.49
21
+ |1, chocolate bar, 0.85
22
+ |
23
+ |Sales Taxes: 1.50
24
+ |Total: 29.83
25
+ END
26
+
27
+ it_behaves_like 'a receipt printer', cart, correct_receipt
28
+ end
29
+
30
+ context 'from data/example_input_b.txt' do
31
+ cart = File.open('data/example_input_b.txt', 'r')
32
+
33
+ correct_receipt = <<-END.gsub(/^\s+\|/, '')
34
+ |1, imported box of chocolates, 10.50
35
+ |1, imported bottle of perfume, 54.65
36
+ |
37
+ |Sales Taxes: 7.65
38
+ |Total: 65.15
39
+ END
40
+
41
+ it_behaves_like 'a receipt printer', cart, correct_receipt
42
+ end
43
+
44
+ context 'from data/example_input_c.txt' do
45
+ cart = File.open('data/example_input_c.txt', 'r')
46
+
47
+ correct_receipt = <<-END.gsub(/^\s+\|/, '')
48
+ |1, imported bottle of perfume, 32.19
49
+ |1, bottle of perfume, 20.89
50
+ |1, packet of headache pills, 9.75
51
+ |1, box of imported chocolates, 11.85
52
+ |
53
+ |Sales Taxes: 6.70
54
+ |Total: 74.68
55
+ END
56
+
57
+ it_behaves_like 'a receipt printer', cart, correct_receipt
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,14 @@
1
+ require_relative '../lib/sales_tax/line_item/base'
2
+ require_relative 'shared_examples_for_line_item_base'
3
+
4
+ RSpec.describe SalesTax::LineItem::Base do
5
+ matching_h = {
6
+ description: 'anything'
7
+ }
8
+
9
+ non_matching_h = {
10
+ description: ''
11
+ }
12
+
13
+ it_behaves_like 'a line item', :other, matching_h, non_matching_h
14
+ end
@@ -0,0 +1,14 @@
1
+ require_relative '../lib/sales_tax/line_item/book'
2
+ require_relative 'shared_examples_for_line_item_base'
3
+
4
+ RSpec.describe SalesTax::LineItem::Book do
5
+ matching_h = {
6
+ description: 'some books'
7
+ }
8
+
9
+ non_matching_h = {
10
+ description: 'this is not readable'
11
+ }
12
+
13
+ it_behaves_like 'a line item', :book, matching_h, non_matching_h
14
+ end
@@ -0,0 +1,14 @@
1
+ require_relative '../lib/sales_tax/line_item/food'
2
+ require_relative 'shared_examples_for_line_item_base'
3
+
4
+ RSpec.describe SalesTax::LineItem::Food do
5
+ matching_h = {
6
+ description: 'some chocolate fun'
7
+ }
8
+
9
+ non_matching_h = {
10
+ description: 'non food product'
11
+ }
12
+
13
+ it_behaves_like 'a line item', :food, matching_h, non_matching_h
14
+ end
@@ -0,0 +1,14 @@
1
+ require_relative '../lib/sales_tax/line_item/medicine'
2
+ require_relative 'shared_examples_for_line_item_base'
3
+
4
+ RSpec.describe SalesTax::LineItem::Medicine do
5
+ matching_h = {
6
+ description: 'epic headache relief'
7
+ }
8
+
9
+ non_matching_h = {
10
+ description: 'not medical stuff'
11
+ }
12
+
13
+ it_behaves_like 'a line item', :medicine, matching_h, non_matching_h
14
+ end
@@ -0,0 +1,71 @@
1
+ require_relative '../lib/sales_tax/line_item/parser'
2
+
3
+ RSpec.describe SalesTax::LineItem::Parser do
4
+ let(:parser) { SalesTax::LineItem::Parser }
5
+
6
+ shared_examples 'a correct parser' do |_in, _out|
7
+ it 'does the job' do
8
+ expect(parser.parse(_in)).to include _out
9
+ end
10
+ end
11
+
12
+ shared_examples 'a complaining parser' do |_in|
13
+ it 'screams' do
14
+ expect{ parser.parse(_in) }.to raise_exception SalesTax::LineItem::Parser::FormatError
15
+ end
16
+ end
17
+
18
+ it "asks for augmenting of it's parsing"
19
+
20
+ context 'when invalid input' do
21
+ context 'with missing arguments' do
22
+ it_behaves_like 'a complaining parser',
23
+ '1, description'
24
+ end
25
+
26
+ context 'with too many arguments' do
27
+ it_behaves_like 'a complaining parser',
28
+ '1, description, 12.49, 1'
29
+ end
30
+
31
+ context 'with arguments in wrong order' do
32
+ it_behaves_like 'a complaining parser',
33
+ '12.49, description, 1'
34
+ end
35
+
36
+ context 'with no description' do
37
+ it_behaves_like 'a complaining parser',
38
+ '1, , 12.49'
39
+ end
40
+ end
41
+
42
+ context 'when valid input' do
43
+ context 'control' do
44
+ it_behaves_like 'a correct parser',
45
+ '1, description, 12.49',
46
+ {
47
+ quantity: '1',
48
+ description: 'description',
49
+ unit_price: '12.49'
50
+ }
51
+ end
52
+
53
+ context 'with quantity' do
54
+ it_behaves_like 'a correct parser',
55
+ '5, description, 0.0',
56
+ {quantity: '5'}
57
+ end
58
+
59
+ context 'with description' do
60
+ it_behaves_like 'a correct parser',
61
+ '0, a imported description, 0.0',
62
+ {description: 'a imported description'}
63
+ end
64
+
65
+ context 'with unit price' do
66
+ it_behaves_like 'a correct parser',
67
+ '0, description, 12.49',
68
+ {unit_price: '12.49'}
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,101 @@
1
+ require_relative '../lib/sales_tax/receipt'
2
+
3
+ RSpec.describe SalesTax::Receipt do
4
+ let(:receipt) { SalesTax::Receipt.new }
5
+
6
+ describe 'the public interface' do
7
+ subject{ receipt }
8
+ it { is_expected.to respond_to :add_item, :print }
9
+ end
10
+
11
+ describe '#add_item' do
12
+ it 'adds item to items list' do
13
+ item = Object.new
14
+ receipt.add_item(item)
15
+ expect(receipt.instance_variable_get(:@items)).to include(item)
16
+ end
17
+ end
18
+
19
+ describe '#print' do
20
+ subject { receipt.print }
21
+ it { is_expected.to be_instance_of String }
22
+
23
+ context 'in general' do
24
+ let(:receipt) {
25
+ items = [
26
+ {
27
+ quantity: '1',
28
+ description: 'a item',
29
+ unit_price: '11.1',
30
+ unit_sales_tax: '0',
31
+ total_unit_price: '11.1'
32
+ },
33
+ ]
34
+
35
+ SalesTax::Receipt.new(items: items)
36
+ }
37
+
38
+ it 'formats numbers to two decimal places' do
39
+ expect(subject).to match /11\.10\n\n.+0\.00/
40
+ end
41
+
42
+ it 'matches exactly' do
43
+ expect(subject).to match <<-END.gsub(/^\s+\|/, '')
44
+ |1, a item, 11.10
45
+ |
46
+ |Sales Taxes: 0.00
47
+ |Total: 11.10
48
+ END
49
+ end
50
+ end
51
+
52
+ context 'with two items' do
53
+ let(:receipt) {
54
+ items = [
55
+ {
56
+ quantity: '1',
57
+ description: 'a description',
58
+ unit_price: '12.49',
59
+ unit_sales_tax: '0.65',
60
+ total_unit_price: '13.14'
61
+ },
62
+ {
63
+ quantity: '1',
64
+ description: 'another description',
65
+ unit_price: '18.99',
66
+ unit_sales_tax: '1.9',
67
+ total_unit_price: '20.89'
68
+ }
69
+ ]
70
+
71
+ SalesTax::Receipt.new(items: items)
72
+ }
73
+
74
+ it 'prints every item' do
75
+ expect(subject).to match /a description.+\n.+another description/
76
+ end
77
+
78
+ it 'prints taxed prices' do
79
+ expect(subject).to match /13\.14\n.+20\.89/
80
+ end
81
+
82
+ it 'prints a sales taxes total' do
83
+ expect(subject).to match /2\.55/
84
+ end
85
+
86
+ it 'prints a total' do
87
+ expect(subject).to match /34\.03/
88
+ end
89
+
90
+ it 'matches exactly' do
91
+ expect(subject).to match <<-END.gsub(/^\s+\|/, '')
92
+ |1, a description, 13.14
93
+ |1, another description, 20.89
94
+ |
95
+ |Sales Taxes: 2.55
96
+ |Total: 34.03
97
+ END
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,9 @@
1
+ RSpec.shared_examples 'a accountable' do
2
+ describe '#to_hash' do
3
+ subject { object.to_hash }
4
+
5
+ it { is_expected.to be_instance_of Hash }
6
+
7
+ it { is_expected.to include :unit_sales_tax, :total_unit_price }
8
+ end
9
+ end
@@ -0,0 +1,72 @@
1
+ RSpec.shared_examples 'a line item' do |category, matching_h, non_matching_h|
2
+ let(:object) {
3
+ described_class.new(
4
+ quantity: '1',
5
+ description: description,
6
+ unit_price: '1'
7
+ )
8
+ }
9
+
10
+ describe 'the public interface' do
11
+ it { expect(described_class).to respond_to :augment }
12
+ let(:description) { '' }
13
+ it { expect(object).to respond_to :to_hash }
14
+ end
15
+
16
+ describe '.augment' do
17
+ context 'with a matching hash' do
18
+ it 'returns a augmented hash' do
19
+ expect(described_class.augment(matching_h)).to be_instance_of Hash
20
+ end
21
+ end
22
+ context 'with non-matching hash' do
23
+ it 'returns nil' do
24
+ expect(described_class.augment(non_matching_h)).to be_nil
25
+ end
26
+ end
27
+ end
28
+
29
+ context 'with a standard description' do
30
+ let(:description) { 'a description' }
31
+
32
+ it_behaves_like 'a accountable'
33
+ it_behaves_like 'a taxable'
34
+
35
+ describe '#to_hash' do
36
+ subject { object.to_hash }
37
+
38
+ let(:expected_hash) {
39
+ {
40
+ quantity: '1',
41
+ description: 'a description'
42
+ }
43
+ }
44
+
45
+ it { is_expected.to be_instance_of Hash }
46
+ it { is_expected.to include expected_hash }
47
+ end
48
+
49
+ describe '#category' do
50
+ it 'returns the correct symbol' do
51
+ expect(object.send(:category)).to eq category
52
+ end
53
+ end
54
+ end
55
+
56
+ describe '#imported?' do
57
+ subject { object.send(:imported?) }
58
+
59
+ context 'when imported' do
60
+ let(:description) {'a imported description'}
61
+ it { is_expected.to be }
62
+
63
+ let(:description) {'imported description'}
64
+ it { is_expected.to be }
65
+ end
66
+
67
+ context 'when local' do
68
+ let(:description) {'a local description'}
69
+ it { is_expected.to be_falsey }
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,16 @@
1
+ RSpec.shared_examples 'a taxable' do
2
+ describe '#unit_price_str' do
3
+ subject { object.send(:unit_price_str) }
4
+ it { is_expected.to be_instance_of String }
5
+ end
6
+
7
+ describe '#imported?' do
8
+ subject { object.send(:imported?) }
9
+ it { is_expected.to satisfy { |v| v.instance_of?(TrueClass) || v.instance_of?(FalseClass) } }
10
+ end
11
+
12
+ describe '#category' do
13
+ subject { object.send(:category) }
14
+ it { is_expected.to be_instance_of Symbol }
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: salestax
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Matias Anaya
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.1'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.1'
27
+ description: A basic sales tax calculator utility, that accepts a list of items and
28
+ prints a receipt with taxes.
29
+ email:
30
+ - matiasanaya@gmail.com
31
+ executables:
32
+ - salestax
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - ".gitignore"
37
+ - README.md
38
+ - bin/salestax
39
+ - data/example_input_a.txt
40
+ - data/example_input_b.txt
41
+ - data/example_input_c.txt
42
+ - lib/sales_tax.rb
43
+ - lib/sales_tax/accountable.rb
44
+ - lib/sales_tax/application.rb
45
+ - lib/sales_tax/line_item/base.rb
46
+ - lib/sales_tax/line_item/book.rb
47
+ - lib/sales_tax/line_item/food.rb
48
+ - lib/sales_tax/line_item/medicine.rb
49
+ - lib/sales_tax/line_item/parser.rb
50
+ - lib/sales_tax/receipt.rb
51
+ - spec/accountable_spec.rb
52
+ - spec/features/sales_tax_spec.rb
53
+ - spec/line_item_base_spec.rb
54
+ - spec/line_item_book_spec.rb
55
+ - spec/line_item_food_spec.rb
56
+ - spec/line_item_medicine_spec.rb
57
+ - spec/line_item_parser_spec.rb
58
+ - spec/receipt_spec.rb
59
+ - spec/shared_examples_for_accountable.rb
60
+ - spec/shared_examples_for_line_item_base.rb
61
+ - spec/shared_examples_for_taxable.rb
62
+ homepage: https://github.com/matiasanaya/sales-tax
63
+ licenses:
64
+ - UNLICENSE
65
+ metadata: {}
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '2.1'
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubyforge_project:
82
+ rubygems_version: 2.2.2
83
+ signing_key:
84
+ specification_version: 4
85
+ summary: A basic sales tax calculator
86
+ test_files:
87
+ - spec/accountable_spec.rb
88
+ - spec/features/sales_tax_spec.rb
89
+ - spec/line_item_base_spec.rb
90
+ - spec/line_item_book_spec.rb
91
+ - spec/line_item_food_spec.rb
92
+ - spec/line_item_medicine_spec.rb
93
+ - spec/line_item_parser_spec.rb
94
+ - spec/receipt_spec.rb
95
+ - spec/shared_examples_for_accountable.rb
96
+ - spec/shared_examples_for_line_item_base.rb
97
+ - spec/shared_examples_for_taxable.rb