From bc5a113c4c3ab2855ebb04b4c2ec1034bf10e33c Mon Sep 17 00:00:00 2001 From: Cristian-Adrian Frasineanu Date: Fri, 25 Nov 2016 00:24:30 +0200 Subject: [PATCH] Errors bag (avoid memory leak from controller). Renamed methods for passing user input. --- app/Bootstrap.cpp | 5 +--- app/Console.cpp | 51 +++++++++++++++------------------------ app/Console.h | 17 +++++-------- app/Controller.cpp | 18 +++++++++++--- app/Controller.h | 6 +++++ app/Helpers.cpp | 2 +- app/Helpers.h | 16 ++++++++++++ app/Model.cpp | 24 ++++++++---------- app/Model.h | 8 +++--- app/ModelInterface.h | 1 + app/RepositoryInterface.h | 3 +++ app/UserModel.cpp | 5 ++-- app/UserModel.h | 3 ++- app/UserRepository.cpp | 43 +++++++++++++++++++++------------ app/UserRepository.h | 7 ++++++ app/View.h | 2 ++ 16 files changed, 124 insertions(+), 87 deletions(-) diff --git a/app/Bootstrap.cpp b/app/Bootstrap.cpp index 67d88f5..cc7a9d0 100644 --- a/app/Bootstrap.cpp +++ b/app/Bootstrap.cpp @@ -14,10 +14,7 @@ void main() { console.showPrompt(); console.setLastInput(_getch()); - if (console.takeActionIfAny() == false) - { - console.renderNextView(); - } + console.takeActionOrNext(); } catch (const invalid_argument &e) { diff --git a/app/Console.cpp b/app/Console.cpp index 7607d59..1ff4570 100644 --- a/app/Console.cpp +++ b/app/Console.cpp @@ -17,12 +17,13 @@ Console::Console() map availableOptions = View::getViewsOptions().find(Console::initialView)->second; this->previousViews = {}; - this->currentView = View(Console::initialView, availableOptions); this->delay = 2000; this->loadActions(); this->loadViews(Console::viewsFolder); - if (find(this->loadedViews.begin(), this->loadedViews.end(), this->currentView.getViewName()) != this->loadedViews.end()) + this->currentView = View(Console::initialView, availableOptions); + + if (isInVector(this->loadedViews, Console::initialView)) { this->handleView(); } @@ -44,12 +45,13 @@ Console::Console(char *mode) map availableOptions = View::getViewsOptions().find(Console::initialView)->second; this->previousViews = {}; - this->currentView = View(viewName, availableOptions); this->delay = 2000; this->loadActions(); this->loadViews(Console::viewsFolder); - if (find(this->loadedViews.begin(), this->loadedViews.end(), this->currentView.getViewName()) != this->loadedViews.end()) + this->currentView = View(viewName, availableOptions); + + if (isInVector(this->loadedViews, viewName)) { this->handleView(); } @@ -72,11 +74,6 @@ void Console::setLastInput(char input) } } -vector &Console::getActions() -{ - return this->actions; -} - void Console::showPrompt() { cout << endl @@ -88,11 +85,6 @@ char Console::getLastInput() return this->lastInput; } -string & Console::getViewsFolder() -{ - return Console::viewsFolder; -} - void Console::loadViews(const fs::path &viewsFolder) { if (!fs::exists(viewsFolder) @@ -118,8 +110,6 @@ void Console::loadViews(const fs::path &viewsFolder) void Console::loadActions() { // TODO: load actions via config file - // The confirm is basically linked to the next view. - // For instance, the login view will have then next view the dashboard. vector actions = { 'q', 'b', 'n', 'c' }; this->actions = actions; @@ -132,24 +122,21 @@ void Console::handleView() path.append("\\").append(this->currentView.getViewName()); stringstream buffer; - ifstream viewFile(path, ios::out); + ifstream viewFile(path, ios::in); if (viewFile.is_open()) { buffer << viewFile.rdbuf(); this->currentView.setRawFormat(buffer.str()); + vector emptyErrorBag = {}; + Controller::setErrorsBag(emptyErrorBag); + // If there's any validation error catch it and reload the view. - // TODO: Fix the memory leak when catching an error. - try - { - this->theController = Controller(this->currentView.getViewName(), buffer.str(), View::getViewExtension()); - } - catch (const invalid_argument &e) - { - cout << endl - << e.what() - << endl; + this->theController = Controller(this->currentView.getViewName(), buffer.str(), View::getViewExtension()); + if (!Controller::getErrorsBag().empty()) { + string message = "\nPlease correct the following: "; + printVector(Controller::getErrorsBag(), message); sleepAndClearBuffer(3 * this->delay); this->reloadView(); @@ -195,9 +182,9 @@ void Console::reloadView() this->handleView(); } -bool Console::takeActionIfAny() +void Console::takeActionOrNext() { - if (isInVector(this->getActions(), this->lastInput)) + if (isInVector(this->actions, this->lastInput)) { switch (this->lastInput) { @@ -216,9 +203,11 @@ bool Console::takeActionIfAny() default: break; } - return true; } - return false; + else + { + this->renderNextView(); + } } bool Console::shouldExit() diff --git a/app/Console.h b/app/Console.h index 6b24c41..179ffb2 100644 --- a/app/Console.h +++ b/app/Console.h @@ -1,9 +1,8 @@ -#include -#include +#pragma once + #include #include #include -#include #include "Helpers.h" #include "View.h" @@ -30,25 +29,21 @@ class Console { void loadViews(const fs::path &); void loadActions(); + void renderNextView(); + void handleView(); + void renderPreviousView(); public: Console(); Console(char *); - vector &getActions(); char getLastInput(); - string &getViewsFolder(); - void setLastInput(char); - bool takeActionIfAny(); + void takeActionOrNext(); bool shouldExit(); void breakTheLoop(); void showPrompt(); unsigned getDelay(); - - void handleView(); - void renderNextView(); - void renderPreviousView(); void reloadView(); ~Console(); diff --git a/app/Controller.cpp b/app/Controller.cpp index df7fb26..b6262f6 100644 --- a/app/Controller.cpp +++ b/app/Controller.cpp @@ -5,6 +5,8 @@ string Controller::viewInputFormat = "@input-"; string Controller::viewOutputFormat = "@output-"; +vector Controller::errorsBag = {}; + void Controller::justShow() { cout << this->viewChunk @@ -49,14 +51,14 @@ void Controller::prepareView() } } - // Show the rest + // Show the rest. if (copyChunk.size()) { cout << copyChunk << endl; } - // Send the payload to be validated and handled by the main model + // Send the payload to the accessor model to be passed to the according repository. if (isInVector(this->controllerAttributions, "input")) { this->model.confirmInput(this->userInputs); @@ -75,6 +77,16 @@ void Controller::prepareViewInput(const string &subChunk, const string &inputAli this->userInputs[inputAlias] = userInput; } +void Controller::setErrorsBag(vector &errorsBag) +{ + Controller::errorsBag = errorsBag; +} + +vector Controller::getErrorsBag() +{ + return Controller::errorsBag; +} + Controller::Controller() { this->controllerName = new char[strlen("NO_CONTROLLER") + 1]; @@ -119,7 +131,7 @@ void Controller::chopChunkAndGetAlias(string &chunk) chunk.erase(0, chunk.find(inputAlias) + inputAlias.size() + 2); } -// Don't assign multiple Controllers, stupid. +// Don't assign it multiple times, stupid. void Controller::operator=(const Controller &controller) { delete[] this->controllerName; diff --git a/app/Controller.h b/app/Controller.h index a8093b4..57840fb 100644 --- a/app/Controller.h +++ b/app/Controller.h @@ -1,3 +1,5 @@ +#pragma once + #include #include "Helpers.h" @@ -9,6 +11,7 @@ class Controller { private: static string viewInputFormat; static string viewOutputFormat; + static vector errorsBag; char *controllerName; vector controllerAttributions; @@ -21,6 +24,9 @@ class Controller { void prepareViewInput(const string &, const string &); // TODO: take the chunk one by one for output and request output from model for each output variable public: + static void setErrorsBag(vector &); + static vector getErrorsBag(); + Controller(); Controller(char *, string &, string &); diff --git a/app/Helpers.cpp b/app/Helpers.cpp index e535d1c..2a08fb6 100644 --- a/app/Helpers.cpp +++ b/app/Helpers.cpp @@ -11,7 +11,7 @@ void sleepAndClearBuffer(unsigned delay) Sleep(delay); // Clear the keyboard buffer during the sleep cycle (if there are any keys pressed), - // so whatever keys are hit are discarded + // so whatever keys are hit are discarded. while (_kbhit()) { _getch(); diff --git a/app/Helpers.h b/app/Helpers.h index 1854963..d3a074e 100644 --- a/app/Helpers.h +++ b/app/Helpers.h @@ -56,6 +56,22 @@ inline bool isInMap(map &haystack, FirstT needle) return haystack.find(needle) != haystack.end(); } +template +inline void printVector(vector &v, string &message) +{ + if (message != "") + { + cout << message + << endl; + } + + for (vector::iterator it = v.begin(); it != v.end(); it++) + { + cout << (*it) + << endl; + } +} + void toLowerCase(string &); void sleepAndClearBuffer(unsigned delay); void clearScreen(); \ No newline at end of file diff --git a/app/Model.cpp b/app/Model.cpp index 14e9454..f6b8617 100644 --- a/app/Model.cpp +++ b/app/Model.cpp @@ -8,29 +8,31 @@ string Model::parseEntityName(string inputAlias) void Model::attachEntity(string &model) { toLowerCase(model); - if (model == "user") + if (model == UserRepository::getAlias()) { this->repository = new UserRepository(); } - else if (model == "question") + /*else if (model == "question") { } else if (model == "category") { - } + }*/ } Model::Model() { this->repository = NULL; this->rawInput = {}; - this->serializedInput = {}; + this->truncatedInput = {}; } -void Model::sendSerializedInput() +void Model::confirmInput(const map &payLoad) { + this->rawInput = payLoad; + // There's interaction with only one model on the view. string entityName = this->parseEntityName(this->rawInput.begin()->first), inputAlias; @@ -45,17 +47,11 @@ void Model::sendSerializedInput() inputAlias = it->first; inputAlias.erase(0, entityName.size() + 1); - this->serializedInput[inputAlias] = it->second; + this->truncatedInput[inputAlias] = it->second; } - // Sanitize the input, if there are any errors, display them and reload the view. - this->repository->validateItems(this->serializedInput); -} - -void Model::confirmInput(const map &payLoad) -{ - this->rawInput = payLoad; - this->sendSerializedInput(); + // Validate the input, if there are any errors, display them and reload the view. + this->repository->validateItems(this->truncatedInput); } Model::~Model() diff --git a/app/Model.h b/app/Model.h index 64a5c39..7167356 100644 --- a/app/Model.h +++ b/app/Model.h @@ -1,3 +1,5 @@ +#pragma once + #include "Helpers.h" #include "RepositoryInterface.h" #include "UserRepository.h" @@ -6,17 +8,13 @@ using namespace std; class Model { private: - static string userModelAlias; - static string questionModelAlias; - map rawInput; - map serializedInput; + map truncatedInput; RepositoryInterface *repository; string parseEntityName(string); void attachEntity(string &); - void sendSerializedInput(); public: Model(); diff --git a/app/ModelInterface.h b/app/ModelInterface.h index a8b7ee6..3d6644d 100644 --- a/app/ModelInterface.h +++ b/app/ModelInterface.h @@ -1,6 +1,7 @@ #pragma once #include + #include "Helpers.h" using namespace std; diff --git a/app/RepositoryInterface.h b/app/RepositoryInterface.h index 47d91d2..b5b7f1b 100644 --- a/app/RepositoryInterface.h +++ b/app/RepositoryInterface.h @@ -1,6 +1,7 @@ #pragma once #include "Helpers.h" + #include "ModelInterface.h" using namespace std; @@ -20,6 +21,8 @@ class RepositoryInterface { map ValidationErrors; ModelInterface *model; + + vector errorsBag; public: virtual void validateItems(map &) = 0; virtual void retrieveItemForActive() = 0; diff --git a/app/UserModel.cpp b/app/UserModel.cpp index 33a11a9..24910ea 100644 --- a/app/UserModel.cpp +++ b/app/UserModel.cpp @@ -1,5 +1,6 @@ #include "UserModel.h" +// Serialize the object when writing a new record. void UserModel::save() { //this->io.write(reinterpret_cast(&this->User), sizeof(this->User)); @@ -50,11 +51,11 @@ UserModel::~UserModel() string UserModel::pathToFile = "..\\database\\users.store"; void UserModel::openIOStream() { - this->io.open(UserModel::pathToFile, ios::in | ios::out | ios::app | ios::binary); + this->io.open(UserModel::pathToFile, ios::in | ios::out | ios::binary | ios::app); if (!this->io.is_open()) { - cout << "Couldn't open " + UserModel::pathToFile; + cout << "Couldn't open the file stream to " + UserModel::pathToFile; } } diff --git a/app/UserModel.h b/app/UserModel.h index 86d986b..7dc22a8 100644 --- a/app/UserModel.h +++ b/app/UserModel.h @@ -1,4 +1,5 @@ -#include +#pragma once + #include #include #include diff --git a/app/UserRepository.cpp b/app/UserRepository.cpp index 45a2ba8..d3736a6 100644 --- a/app/UserRepository.cpp +++ b/app/UserRepository.cpp @@ -1,4 +1,7 @@ #include "UserRepository.h" +#include "Controller.h" + +string UserRepository::alias = "user"; void UserRepository::defineValidation() { @@ -11,11 +14,13 @@ void UserRepository::defineValidation() }; this->ValidationErrors = { - { "fullname", "Please enter a valid firstname and lastname." }, - { "email", "Please enter a valid email address." }, - { "username", "Your username should have 8-20 characters, should start only with a letter, doesn't repeat special characters and finishes with a letter or a number." }, - { "password", "Your password should habe 5-16 characters, and contain an uppercase letter, a number and a special character." }, + { "fullname", "Invalid full name." }, + { "email", "Invalid email address." }, + { "username", "Username should be 8-20 characters long, should start only with a letter and should not repeat special characters." }, + { "password", "Password should be 5-16 characters long, and contain at least an uppercase letter, a number and a special character." }, }; + + this->errorsBag = {}; } UserRepository::UserRepository() @@ -26,21 +31,24 @@ UserRepository::UserRepository() void UserRepository::receiveCleanInput(map &cleanInput) { - log(cleanInput, "cleanInput", "after validation"); - this->model->setAttributes(cleanInput); this->model->save(); } -void UserRepository::validateItems(map &serializedInput) +string &UserRepository::getAlias() +{ + return UserRepository::alias; +} + +void UserRepository::validateItems(map &truncatedInput) { - for (map::iterator it = serializedInput.begin(); it != serializedInput.end(); it++) + for (map::iterator it = truncatedInput.begin(); it != truncatedInput.end(); it++) { try { if (!regex_match(it->second.c_str(), regex(this->ValidationRules[it->first.c_str()]))) { - throw invalid_argument(this->ValidationErrors[it->first.c_str()]); + this->errorsBag.push_back(this->ValidationErrors[it->first.c_str()]); } } catch (const regex_error &e) @@ -53,22 +61,27 @@ void UserRepository::validateItems(map &serializedInput) } } - this->receiveCleanInput(serializedInput); + // If there are no errors, send it along. + if (this->errorsBag.empty()) + { + this->receiveCleanInput(truncatedInput); + } + + // Let the console know about the errors. + Controller::setErrorsBag(this->errorsBag); } void UserRepository::retrieveItemForActive() { - // Search for the record having active set to true + // Search for the record having active set to true. } void UserRepository::retrieveAll() { - // Print everything + // Print everything. } UserRepository::~UserRepository() { delete this->model; -} - - +} \ No newline at end of file diff --git a/app/UserRepository.h b/app/UserRepository.h index 4455f46..677be8e 100644 --- a/app/UserRepository.h +++ b/app/UserRepository.h @@ -1,13 +1,20 @@ +#pragma once + #include #include "RepositoryInterface.h" #include "UserModel.h" + class UserRepository : public RepositoryInterface { private: + static string alias; + void defineValidation(); void receiveCleanInput(map &); public: + static string &getAlias(); + UserRepository(); void validateItems(map &); diff --git a/app/View.h b/app/View.h index 814f5b4..ee868d6 100644 --- a/app/View.h +++ b/app/View.h @@ -1,3 +1,5 @@ +#pragma once + #include using namespace std;