mirror of
https://github.com/ysoftdevs/Theatrical-Players-Refactoring-Kata.git
synced 2026-03-22 09:30:19 +01:00
Add C++ implementation
This commit is contained in:
6
cpp/CMakeLists.txt
Normal file
6
cpp/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.4)
|
||||
project(theatrical_players_refactoring_kata)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
add_subdirectory(tests)
|
||||
14
cpp/README.md
Normal file
14
cpp/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
Theatrical Players Refactoring Kata - C++ version
|
||||
========================================================
|
||||
|
||||
See the [top level readme](https://github.com/emilybache/Theatrical-Players-Refactoring-Kata) for general information about this exercise.
|
||||
|
||||
## Dependencies
|
||||
|
||||
The tests use the following header-only projects, which are in the `third_party` sub-directory:
|
||||
|
||||
| Project | Licence |
|
||||
| --- | --- |
|
||||
| [doctest test framework](https://github.com/onqtam/doctest) | [MIT](https://github.com/onqtam/doctest/blob/master/LICENSE.txt) |
|
||||
| [ApprovalTests.cpp](https://github.com/approvals/ApprovalTests.cpp) | [Apache 2.0](https://github.com/approvals/ApprovalTests.cpp/blob/master/LICENSE) |
|
||||
| [JSON for Modern C++](https://github.com/nlohmann/json) | [MIT](https://github.com/nlohmann/json/blob/develop/LICENSE.MIT) |
|
||||
95
cpp/statement.cpp
Normal file
95
cpp/statement.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#include "statement.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
// https://stackoverflow.com/a/7277333/104370
|
||||
class comma_numpunct : public std::numpunct<char>
|
||||
{
|
||||
protected:
|
||||
virtual char do_thousands_sep() const override
|
||||
{
|
||||
return ',';
|
||||
}
|
||||
|
||||
virtual std::string do_grouping() const override
|
||||
{
|
||||
return "\03";
|
||||
}
|
||||
};
|
||||
|
||||
std::string format_as_dollars(float amount)
|
||||
{
|
||||
// this creates a new locale based on the current application default
|
||||
// (which is either the one given on startup, but can be overriden with
|
||||
// std::locale::global) - then extends it with an extra facet that
|
||||
// controls numeric output.
|
||||
const std::locale comma_locale(std::locale(), new comma_numpunct());
|
||||
|
||||
std::ostringstream out;
|
||||
// tell stream to use our new locale.
|
||||
out.imbue(comma_locale);
|
||||
out.precision(2);
|
||||
out << "$" << std::fixed << amount;
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string statement(
|
||||
const nlohmann::json& invoice,
|
||||
const nlohmann::json& plays)
|
||||
{
|
||||
float total_amount = 0;
|
||||
int volume_credits = 0;
|
||||
|
||||
std::stringstream result;
|
||||
result << "Statement for " << invoice["customer"].get<std::string>() << '\n';
|
||||
|
||||
for( const auto& perf : invoice["performances"])
|
||||
{
|
||||
float this_amount = 0;
|
||||
auto play = plays[perf["playID"].get<std::string>()];
|
||||
if (play["type"] == "tragedy")
|
||||
{
|
||||
this_amount = 40000;
|
||||
if (perf["audience"] > 30)
|
||||
{
|
||||
this_amount += 1000 * (perf["audience"].get<int>() - 30);
|
||||
}
|
||||
}
|
||||
|
||||
else if (play["type"] == "comedy")
|
||||
{
|
||||
this_amount = 30000;
|
||||
if (perf["audience"] > 20)
|
||||
{
|
||||
this_amount += 10000 + 500 * (perf["audience"].get<int>() - 20);
|
||||
}
|
||||
|
||||
this_amount += 300 * perf["audience"].get<int>();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
throw std::domain_error("unknown type: " + play["type"].get<std::string>());
|
||||
}
|
||||
|
||||
// add volume credits
|
||||
volume_credits += std::max(perf["audience"].get<int>() - 30, 0);
|
||||
|
||||
// add extra credit for every ten comedy attendees
|
||||
if ("comedy" == play["type"])
|
||||
{
|
||||
volume_credits += perf["audience"].get<int>() / 5;
|
||||
}
|
||||
|
||||
// print line for this order
|
||||
result << " " << play["name"].get<std::string>() << ": " << format_as_dollars(this_amount/100) <<
|
||||
" (" << perf["audience"] << " seats)\n";
|
||||
total_amount += this_amount;
|
||||
}
|
||||
|
||||
result << "Amount owed is " << format_as_dollars(total_amount/100.0f) << "\n";
|
||||
result << "You earned " << volume_credits << " credits";
|
||||
return result.str();
|
||||
}
|
||||
7
cpp/statement.h
Normal file
7
cpp/statement.h
Normal file
@@ -0,0 +1,7 @@
|
||||
// TODO Include Guard
|
||||
|
||||
#include "json.hpp"
|
||||
|
||||
std::string statement(
|
||||
const nlohmann::json& invoice,
|
||||
const nlohmann::json& plays);
|
||||
15
cpp/tests/CMakeLists.txt
Normal file
15
cpp/tests/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
project(tests)
|
||||
|
||||
add_executable(${PROJECT_NAME}
|
||||
../statement.cpp
|
||||
../statement.h
|
||||
test_statement.cpp
|
||||
main.cpp)
|
||||
|
||||
target_include_directories(
|
||||
${PROJECT_NAME}
|
||||
PUBLIC
|
||||
../third_party ..)
|
||||
|
||||
enable_testing()
|
||||
add_test(NAME ${PROJECT_NAME}_test COMMAND ${PROJECT_NAME})
|
||||
2
cpp/tests/main.cpp
Normal file
2
cpp/tests/main.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#define APPROVALS_DOCTEST
|
||||
#include "ApprovalTests.hpp"
|
||||
43
cpp/tests/test_statement.cpp
Normal file
43
cpp/tests/test_statement.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "statement.h"
|
||||
|
||||
#include "json.hpp"
|
||||
#include "doctest.h"
|
||||
#include "ApprovalTests.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string get_adjacent_file(const std::string& filename)
|
||||
{
|
||||
return ApprovalTestNamer().getDirectory() + filename;
|
||||
}
|
||||
|
||||
nlohmann::json read_json_file(const std::string& filename)
|
||||
{
|
||||
std::ifstream invoice_stream(filename);
|
||||
if ( ! invoice_stream.is_open())
|
||||
{
|
||||
std::cout << "Error opening file:" << filename << '\n';
|
||||
std::cout << "Error: " << strerror(errno) << '\n';
|
||||
}
|
||||
nlohmann::json result;
|
||||
invoice_stream >> result;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("test_example_statement")
|
||||
{
|
||||
auto invoice = read_json_file(get_adjacent_file("invoice.json"));
|
||||
auto plays = read_json_file(get_adjacent_file("plays.json"));
|
||||
|
||||
Approvals::verify(statement(invoice, plays));
|
||||
}
|
||||
|
||||
TEST_CASE("test_statement_with_new_play_types")
|
||||
{
|
||||
auto invoice = read_json_file(get_adjacent_file("invoice_new_plays.json"));
|
||||
auto plays = read_json_file(get_adjacent_file("new_plays.json"));
|
||||
CHECK_THROWS_AS(statement(invoice, plays), std::domain_error);
|
||||
}
|
||||
2606
cpp/third_party/ApprovalTests.hpp
vendored
Normal file
2606
cpp/third_party/ApprovalTests.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5939
cpp/third_party/doctest.h
vendored
Normal file
5939
cpp/third_party/doctest.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
22684
cpp/third_party/json.hpp
vendored
Normal file
22684
cpp/third_party/json.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user