Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
af01c04
Add Java solution.
christianhujer Aug 21, 2021
969a24b
Add Kotlin solution.
christianhujer Aug 21, 2021
54b012d
Add Rust solution.
christianhujer Aug 21, 2021
4dc9adc
Add solution rationale.
christianhujer Sep 4, 2021
514edaa
Update Java solution to include a test with Cucumber.
christianhujer Mar 19, 2022
8d2e42f
Update Kotlin solution to include a test with Cucumber.
christianhujer Mar 19, 2022
30b18c0
Add solution in Golang.
christianhujer Mar 19, 2022
087c810
Add Go to the list of solutions.
christianhujer Mar 19, 2022
f198e7f
Fix spelling mistake and remove solutions branch link.
christianhujer Mar 19, 2022
c2f890c
Add solution in C++.
christianhujer May 22, 2022
c927673
Add solution in C.
christianhujer May 22, 2022
2622ad5
Add solution in Fortran.
christianhujer May 28, 2022
b765493
Add solution in C#. Not with SpecFlow yet, but that's planned.
christianhujer Jun 2, 2022
4030eb4
Add ExpenseReport solution in SQL.
christianhujer Jun 4, 2022
8874c48
Add solution in m68k Assembler.
christianhujer Jun 5, 2022
d267679
Add solution in Pascal.
christianhujer Jun 5, 2022
a7cd1a3
Add solution in Rexx.
christianhujer Jun 5, 2022
813b750
Add solution in Ada.
christianhujer Jun 5, 2022
fb9a443
Add solution in XSLT.
christianhujer Jun 5, 2022
0a92fab
Add solution in Groovy.
christianhujer Jun 5, 2022
daba78b
Add solution in Amiga BASIC.
christianhujer Jun 5, 2022
051b3f6
Add solution in PostScript.
christianhujer Jun 11, 2022
0731bc8
Fix bug in faketime usage.
christianhujer Jun 16, 2022
cd64f52
Make test pass in all timezones.
christianhujer Jun 16, 2022
794e4c2
Improve Pascal solution.
christianhujer Jun 22, 2022
a463cab
Add solution in Haskell.
christianhujer Jun 22, 2022
156bab1
Warn about force-push as well.
christianhujer Jun 23, 2022
8dc914a
Add solution for AArch64 Assembler.
christianhujer Jun 23, 2022
c0f3ab7
Add solution in Python.
christianhujer Jun 27, 2022
a0cd340
Add Black.
christianhujer Jun 27, 2022
07d3e2b
Change static method to class method, as it reflects on the class.
christianhujer Jun 27, 2022
9f2c00e
Use a few more functions.
christianhujer Jul 4, 2022
cd5add5
Add Perl solution.
christianhujer Jul 4, 2022
f173b46
Add mutation testing to the solution in Golang.
christianhujer Aug 28, 2022
daae2aa
Significantly improve Prolog solution.
christianhujer Oct 3, 2022
13c6ab0
Add solution rationale.
christianhujer Sep 4, 2021
a79ba63
Add solution in C.
christianhujer May 22, 2022
b1e5b2e
Fix typo
sidnik007 Feb 25, 2024
7298834
Add enum and interface.
sidnik007 Feb 25, 2024
e19cf59
Updated package-lock.json.
sidnik007 Feb 25, 2024
005983e
Print date and time.
sidnik007 Feb 25, 2024
4a33588
Failing characterization test due to time mismatch.
sidnik007 Feb 25, 2024
b11c023
Pass the failing test.
sidnik007 Feb 25, 2024
9179527
Rearrange function
sidnik007 Feb 25, 2024
32b16a4
Extract isMeal, getName and isOverLimit.
sidnik007 Feb 25, 2024
ca3d8d4
Move isMeal to Expense class
sidnik007 Feb 25, 2024
24539b3
Move getName to Expense class
sidnik007 Feb 25, 2024
0102d72
Move isOverLimit to Expense class
sidnik007 Feb 25, 2024
b629122
Optimize getName.
sidnik007 Feb 25, 2024
db72df2
Optimize isOverLimit.
sidnik007 Feb 25, 2024
2fb47ca
Optimize isMeal.
sidnik007 Feb 25, 2024
cadc1bc
Extract sumMeal, printDetails and sumTotal.
sidnik007 Feb 25, 2024
cfe0e0f
Inline variables in printDetails.
sidnik007 Feb 25, 2024
1b80438
Extract printHeader and printSummary.
sidnik007 Feb 25, 2024
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
39 changes: 37 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,44 @@ Find a more complete list on [Wikipedia](https://en.wikipedia.org/wiki/List_of_p
- Provide a level 2 challenge for creating an HTML report besides the Plain Text report.

## Solutions
To see solutions, switch to the [solutions branch](https://github.com/christianhujer/expensereport/tree/solutions#solutions).
**Warning** The solutions branch will be rebased and force-pushed!

**Warning** The solutions branch will be rebased!
Currently, solutions exist in the following languages:
- [Ada](expensereport-ada/)
- [Assembler](expensereport-asm-68k-masm/) (Motorola 68020, Amiga OS, MaxonASM)
- [Assembler](expensereport-asm-aarch64-linux-gasm/) (AArch64, Linux, GNU Assembler)
- [BASIC](expensereport-basic-amiga/) (Amiga BASIC, Commodore Amiga) ⇐ Quite amazing! First BASIC without line numbers!
- [C](expensereport-c/)
- [C#](expensereport-csharp/)
- [C++](expensereport-cxx/)
- [Fortran](expensereport-fortran/)
- [Go](expensereport-go/) (with BDD)
- [Groovy](expensereport-groovy-script/)
- [Haskell](expensereport-haskell/)
- [Java](expensereport-java/) (with BDD)
- [Kotlin](expensereport-kotlin/) (with BDD)
- [Pascal](expensereport-pascal/)
- [PostScript](expensereport-postscript/)
- [Python](expensereport-python/) (with BDD)
- [Rexx](expensereport-rexx/) (tested with Regina Rexx and ARexx)
- [Rust](expensereport-rust/)
- [SQL](expensereport-sql/)
- [XML/XSLT](expensereport-xslt/)

### Solution Rationale
- Make adding the new requirement (lunch with an expense limit 2000) as easy and simple and error-free as possible. That means solving the OCP-violations of `printReport()`.
- Small ("atomic") functions. Extract until you can no longer reasonably extract further methods.
- A balance between a language independent design expressed idiomatic in the target language.

Currently, soltuions exist in the following languages:
- [Java](expensereport-java/)
- [Kotlin](expensereport-kotlin/)
- [Rust](expensereport-rust/)

### Solution Rationale
- Make adding the new requirement (lunch with an expense limit 2000) as easy and simple and error-free as possible. That means solving the OCP-violations of `printReport()`.
- Small ("atomic") functions. Extract until you can no longer reasonably extract further methods.
- A balance between a language independent design expressed idiomatic in the target language.

## Credits and License
I first encountered the ExpenseReport example during a bootcamp at Equal Experts.
Expand Down
2 changes: 1 addition & 1 deletion expensereport-ada/Makefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
SHELL:=/bin/bash

CPPFLAGS:=-W -Wall -pedantic -Werror

.PHONY: all
all: expensereport
diff expected.txt <(./expensereport)

.PHONY: clean
clean::
Expand Down
10 changes: 10 additions & 0 deletions expensereport-ada/expected.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Expenses:
Dinner 5000
Dinner 5001 X
Breakfast 1000
Breakfast 1001 X
Car Rental 4
Lunch 2000
Lunch 2001 X
Meal expenses: 16003
Total expenses: 16007
112 changes: 82 additions & 30 deletions expensereport-ada/expensereport.adb
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,108 @@ with Text_IO, Text_IO.Unbounded_IO, Ada.Command_Line, Ada.Strings.Unbounded, Ada
use Text_IO, Text_IO.Unbounded_IO, Ada.Command_Line, Ada.Strings.Unbounded;

procedure expensereport is
type ExpenseType is (Breakfast, Dinner, CarRental);
type ExpenseType is tagged record
name: Unbounded_String;
limit: Integer;
isMeal: Boolean;
end record;

type Expense is tagged record
eType: ExpenseType;
amount: Integer;
end record;

Dinner: constant ExpenseType := (To_Unbounded_String("Dinner"), 5000, true);
Breakfast: constant ExpenseType := (To_Unbounded_String("Breakfast"), 1000, true);
CarRental: constant ExpenseType := (To_Unbounded_String("Car Rental"), 2147483647, false);
Lunch: constant ExpenseType := (To_Unbounded_String("Lunch"), 2000, true);

type ExpenseList is array(Positive range <>) of Expense;

procedure printReport(expenses: in ExpenseList) is
total : Integer := 0;
mealExpenses : Integer := 0;
expenseName : Unbounded_String;
mealOverExpensesMarker : Character := ' ';
function isMeal(exp: Expense) return Boolean is
begin
Put_Line("Expenses:");
return exp.eType.isMeal;
end;

function getName(exp: Expense) return Unbounded_String is
begin
return exp.eType.name;
end;

function isOverLimit(exp: Expense) return Boolean is
begin
return exp.amount > exp.eType.limit;
end;

function sumMeals(expenses: in ExpenseList) return Integer is
meals : Integer := 0;
begin
meals := 0;
for i in expenses'Range loop
if (expenses(i).eType = Breakfast or expenses(i).eType = Dinner) then
mealExpenses := mealExpenses + expenses(i).amount;
if (isMeal(expenses(i))) then
meals := meals + expenses(i).amount;
end if;
end loop;
return meals;
end;

expenseName := To_Unbounded_String("Foo" & "Foo");
case expenses(i).eType is
when Breakfast =>
expenseName := To_Unbounded_String("Breakfast");
when Dinner =>
expenseName := To_Unbounded_String("Dinner");
when CarRental =>
expenseName := To_Unbounded_String("Car Rental");
end case;

if ((expenses(i).eType = Breakfast and expenses(i).amount > 1000) or (expenses(i).eType = Dinner and expenses(i).amount > 5000)) then
mealOverExpensesMarker := 'X';
else
mealOverExpensesMarker := ' ';
end if;
function sumTotal(expenses: in ExpenseList) return Integer is
total : Integer := 0;
begin
total := 0;
for i in expenses'Range loop
total := total + expenses(i).amount;
end loop;
return total;
end;

procedure printHeader is
begin
Put_Line("Expenses:");
end printHeader;

Put_Line(expenseName & Ada.Characters.Latin_1.HT & Ada.Strings.Fixed.Trim(Integer'Image(expenses(i).amount), Ada.Strings.Left) & Ada.Characters.Latin_1.HT & mealOverExpensesMarker);
function overLimitMarker(exp: Expense) return Character is
begin
if (isOverLimit(exp)) then
return 'X';
else
return ' ';
end if;
end;

total := total + expenses(i).amount;
procedure printDetail(exp: Expense) is
begin
Put_Line(getName(exp) & Ada.Characters.Latin_1.HT & Ada.Strings.Fixed.Trim(Integer'Image(exp.amount), Ada.Strings.Left) & Ada.Characters.Latin_1.HT & overLimitMarker(exp));
end printDetail;

procedure printDetails(expenses: in ExpenseList) is
begin
for i in expenses'Range loop
printDetail(expenses(i));
end loop;
end printDetails;

Put_Line("Meal expenses:" & Integer'Image(mealExpenses));
Put_Line("Total expenses:" & Integer'Image(total));
procedure printSummary(expenses: in ExpenseList) is
begin
Put_Line("Meal expenses:" & Integer'Image(sumMeals(expenses)));
Put_Line("Total expenses:" & Integer'Image(sumTotal(expenses)));
end printSummary;

procedure printReport(expenses: in ExpenseList) is
begin
printHeader;
printDetails(expenses);
printSummary(expenses);
end printReport;

expenses : ExpenseList := (
(Breakfast, 1),
(Breakfast, 1)
(Dinner, 5000),
(Dinner, 5001),
(Breakfast, 1000),
(Breakfast, 1001),
(CarRental, 4),
(Lunch, 2000),
(Lunch, 2001)
);
begin
printReport(expenses);
Expand Down
Loading