From adc572fd6b9a7f890290abc2904f627425ae6866 Mon Sep 17 00:00:00 2001 From: Cristian-Adrian Frasineanu Date: Sat, 3 Dec 2016 12:06:44 +0200 Subject: [PATCH] Show question view and edited the save method to flush the buffer immediately after writing a record (would cause a bug when creating multiple questions as the nonactive status won't be persisted). Reset action to set any active question to false when returning to a certain view (dashboard in this case). --- app/Console.cpp | 1 - app/Controller.cpp | 18 +++++++-- app/Controller.h | 1 + app/Model.cpp | 16 ++++++-- app/Model.h | 1 + app/ModelInterface.h | 1 + app/QuestionModel.cpp | 59 +++++++++++++++++++++++++----- app/QuestionModel.h | 8 +++- app/QuestionRepository.cpp | 42 +++++++++++++++++++-- app/QuestionRepository.h | 3 +- app/RepositoryInterface.cpp | 4 ++ app/RepositoryInterface.h | 1 + app/UserModel.cpp | 8 ++-- app/UserRepository.cpp | 5 ++- app/View.cpp | 7 +++- views/questions/show-question.view | 19 ++++++++++ views/user/dashboard.view | 1 + 17 files changed, 164 insertions(+), 31 deletions(-) create mode 100644 views/questions/show-question.view diff --git a/app/Console.cpp b/app/Console.cpp index eb3ffaf..abb90d1 100644 --- a/app/Console.cpp +++ b/app/Console.cpp @@ -211,7 +211,6 @@ void Console::renderNextView(string &viewName) //this->previousViews.erase(this->previousViews.end() - 1, this->previousViews.end()); this->currentView = View(viewName, nextOptions); - this->lastInput = '\0'; clearScreen(); this->handleView(); diff --git a/app/Controller.cpp b/app/Controller.cpp index 6d7b159..368a5d6 100644 --- a/app/Controller.cpp +++ b/app/Controller.cpp @@ -24,14 +24,21 @@ void Controller::prepareView() if (this->hasInput(copyChunk)) { this->controllerAttributions.push_back("input"); - - // Pass the corresposing action to the repository. - this->prepareAction(copyChunk); } if (this->hasOutput(copyChunk)) { this->controllerAttributions.push_back("output"); } + if (this->hasAction(copyChunk)) + { + // Pass the corresposing action to the repository. + this->prepareAction(copyChunk); + + if (!this->hasInput(copyChunk)) + { + this->model.signalAction(this->userInputs["action"]); + } + } while (this->hasInput(copyChunk) || this->hasOutput(copyChunk)) { @@ -225,6 +232,11 @@ bool Controller::hasOutput(const string &raw) return raw.find(Controller::userOutputString) != string::npos; } +bool Controller::hasAction(const string &raw) +{ + return raw.find(Controller::actionString) != string::npos; +} + Controller::~Controller() { delete[] this->controllerName; diff --git a/app/Controller.h b/app/Controller.h index c01d8a7..588aa32 100644 --- a/app/Controller.h +++ b/app/Controller.h @@ -37,6 +37,7 @@ class Controller { bool hasInput(const string &); bool hasOutput(const string &); + bool hasAction(const string &); vector &getControllerAttributions(); char *getControllerName(); diff --git a/app/Model.cpp b/app/Model.cpp index 04cfa02..9252dad 100644 --- a/app/Model.cpp +++ b/app/Model.cpp @@ -63,15 +63,23 @@ void Model::render(const string &outputAlias) string entityName = this->parseEntityName(outputAlias), truncatedAlias = outputAlias; - if (this->repository == NULL) - { - this->attachEntity(entityName); - } + this->attachEntity(entityName); truncatedAlias.erase(0, entityName.size() + 1); this->repository->echo(truncatedAlias); } +void Model::signalAction(const string &actionAlias) +{ + string entityName = this->parseEntityName(actionAlias), + truncatedAlias = actionAlias; + + this->attachEntity(entityName); + + truncatedAlias.erase(0, entityName.size() + 1); + this->repository->apply(truncatedAlias); +} + Model::~Model() { delete this->repository; diff --git a/app/Model.h b/app/Model.h index dc1548a..9b7345a 100644 --- a/app/Model.h +++ b/app/Model.h @@ -21,6 +21,7 @@ class Model { void confirmInput(const map &); void render(const string &); + void signalAction(const string &); ~Model(); }; \ No newline at end of file diff --git a/app/ModelInterface.h b/app/ModelInterface.h index 3f80dea..5a9b378 100644 --- a/app/ModelInterface.h +++ b/app/ModelInterface.h @@ -15,6 +15,7 @@ class ModelInterface { protected: map attributes; vector protectedAttributes; + fstream io; public: virtual void save() = 0; diff --git a/app/QuestionModel.cpp b/app/QuestionModel.cpp index 075aa03..433f2a8 100644 --- a/app/QuestionModel.cpp +++ b/app/QuestionModel.cpp @@ -3,7 +3,6 @@ Question QuestionModel::setAfterUserId(int userId) { this->io.seekg(0, this->io.beg); - while (this->io.read(reinterpret_cast(&this->question), sizeof(question))) { if (this->question.user_id == userId) @@ -23,12 +22,30 @@ Question QuestionModel::setAfterId(int id) return this->question; } +bool QuestionModel::setActiveIfAny() +{ + if (this->question.active == true) + { + return true; + } + + this->io.seekg(0, this->io.beg); + while (this->io.read(reinterpret_cast(&this->question), sizeof(Question))) + { + if (this->question.active == true) + { + return true; + } + } + + return false; +} + vector QuestionModel::retrieveAll() { vector questions; this->io.seekg(0, this->io.beg); - while (this->io.read(reinterpret_cast(&this->question), sizeof(Question))) { if (string(this->question.deleted_at) == "") @@ -40,12 +57,31 @@ vector QuestionModel::retrieveAll() return questions; } +int QuestionModel::getId() +{ + return this->question.id; +} + +char *QuestionModel::getTitle() +{ + return this->question.title; +} + +char *QuestionModel::getBody() +{ + return this->question.body; +} + +char *QuestionModel::getCategory() +{ + return "category"; +} + bool QuestionModel::questionTitleExists(string &title) { Question question; this->io.seekg(0, this->io.beg); - while (this->io.read(reinterpret_cast(&question), sizeof(Question))) { if (question.title == title) @@ -68,24 +104,28 @@ void QuestionModel::markAnswered(int id) void QuestionModel::markAs(const string &status, int id) { - this->setAfterId(id); + if (id != NULL) + { + this->setAfterId(id); + } this->question.active = (status == "active") ? true : false; this->save(); } -// Serialize the object. +// Serialize the question. void QuestionModel::save() { this->io.seekp((this->question.id - 1) * sizeof(Question), this->io.beg); - this->io.clear(); - if (!this->io.write(reinterpret_cast(&this->question), sizeof(Question))) { throw system_error(error_code(3, generic_category()), "Failed persisting data to file!"); } + + // Flush the buffer and update the destination. + this->io.flush(); } void QuestionModel::setAttributes(map &cleanInputs) @@ -112,7 +152,7 @@ void QuestionModel::setAttributes(map &cleanInputs) { this->question.category_id = 1; - // TODO: get the id from the static map defined in the category model. + // TODO: get the id from the category model. } } @@ -121,7 +161,7 @@ void QuestionModel::setAttributes(map &cleanInputs) { time_t t = time(nullptr); strftime(this->question.created_at, sizeof(this->question.created_at), "%c", localtime(&t)); - this->question.active = false; + this->question.active = true; } this->question.id = ++this->lastId; @@ -137,7 +177,6 @@ void QuestionModel::dumpFile() if (db.is_open() && dump.is_open()) { db.seekg(0, db.beg); - while (db.read(reinterpret_cast(&question), sizeof(Question))) { dump << "Id: " << question.id << endl diff --git a/app/QuestionModel.h b/app/QuestionModel.h index 5685e3f..b544423 100644 --- a/app/QuestionModel.h +++ b/app/QuestionModel.h @@ -40,13 +40,19 @@ class QuestionModel : public ModelInterface { static void dumpFile(); QuestionModel(); + bool setActiveIfAny(); Question setAfterUserId(int); Question setAfterId(int); vector retrieveAll(); + int getId(); + char *getTitle(); + char *getBody(); + char *getCategory(); + bool questionTitleExists(string &); void markAnswered(int); - void markAs(const string &, int); + void markAs(const string &, int = NULL); void save(); void setAttributes(map &); diff --git a/app/QuestionRepository.cpp b/app/QuestionRepository.cpp index 6060676..18ceb17 100644 --- a/app/QuestionRepository.cpp +++ b/app/QuestionRepository.cpp @@ -28,7 +28,8 @@ void QuestionRepository::defineValidation() { "title", "..." }, { "body", "..." }, { "category", "..." }, - { "keyword", "..." } + { "keyword", "..." }, + { "action", "Please provide a valid action!" } }; Controller::pushError(string("")); @@ -82,6 +83,8 @@ void QuestionRepository::receiveCleanInput(map &cleanInput) toast(string("Your question was created!"), string("notification")); printString("\n"); + + Controller::hasRedirectTo = "show-question.view"; } else { @@ -92,8 +95,36 @@ void QuestionRepository::receiveCleanInput(map &cleanInput) // Determine what to output via the model. void QuestionRepository::echo(const string &alias) { - if (alias == "") + this->model.setActiveIfAny(); + + if (alias == "id") + { + printString(to_string(this->model.getId()).c_str()); + } + else if (alias == "title") + { + printString(this->model.getTitle()); + } + else if (alias == "body") + { + printString(this->model.getBody()); + } + else if (alias == "category") + { + printString(this->model.getCategory()); + } +} + +void QuestionRepository::apply(const string &action) +{ + // Forget the active question when the action is triggered. + // TODO: The changes aren't persisted, why? (the question remains active forever) + if (action == "reset") { + if (this->model.setActiveIfAny()) + { + this->model.markAs("nonactive"); + } } } @@ -113,6 +144,7 @@ void QuestionRepository::validateItems(map &truncatedInput) toast("\n\n" + string(e.what()) + "\n", string("error")); } + // TODO: implement a category model. if (it->first == "category" && !isInVector(vector({ "something", "tada", "c++" }), it->second)) { Controller::pushError(string("Please provide a valid category.")); @@ -123,7 +155,11 @@ void QuestionRepository::validateItems(map &truncatedInput) { if (truncatedInput.find("action")->second == "create") { - truncatedInput["userId"] = to_string(this->users.setActive().id); + this->users = new UserModel; + truncatedInput["userId"] = to_string(this->users->setActive().id); + + // Prevent multiple IO streams opened at the same time. + delete this->users; } this->receiveCleanInput(truncatedInput); } diff --git a/app/QuestionRepository.h b/app/QuestionRepository.h index 04bd294..5b5dcf7 100644 --- a/app/QuestionRepository.h +++ b/app/QuestionRepository.h @@ -13,7 +13,7 @@ class QuestionRepository : public RepositoryInterface { static string alias; QuestionModel model; - UserModel users; + UserModel *users; void defineValidation(); void receiveCleanInput(map &); @@ -24,6 +24,7 @@ class QuestionRepository : public RepositoryInterface { void validateItems(map &); void echo(const string &); + void apply(const string &); ~QuestionRepository(); }; \ No newline at end of file diff --git a/app/RepositoryInterface.cpp b/app/RepositoryInterface.cpp index e8f9c77..6f7742e 100644 --- a/app/RepositoryInterface.cpp +++ b/app/RepositoryInterface.cpp @@ -1,5 +1,9 @@ #include "RepositoryInterface.h" +void RepositoryInterface::apply(const string &) +{ +} + RepositoryInterface::~RepositoryInterface() { } diff --git a/app/RepositoryInterface.h b/app/RepositoryInterface.h index 588b945..17ccc54 100644 --- a/app/RepositoryInterface.h +++ b/app/RepositoryInterface.h @@ -18,6 +18,7 @@ class RepositoryInterface { public: virtual void validateItems(map &) = 0; virtual void echo(const string &) = 0; + virtual void apply(const string &); virtual ~RepositoryInterface(); }; \ No newline at end of file diff --git a/app/UserModel.cpp b/app/UserModel.cpp index 7a59eaa..37ac2e1 100644 --- a/app/UserModel.cpp +++ b/app/UserModel.cpp @@ -69,18 +69,18 @@ void UserModel::markAs(const string &status, int id) this->save(); } -// Serialize the object. +// Serialize the user. void UserModel::save() { this->io.seekp((this->user.id - 1) * sizeof(User), this->io.beg); - - // Otherwise if we reach the end of the file, the eof flag will be set and write will fail. this->io.clear(); - if (!this->io.write(reinterpret_cast(&this->user), sizeof(User))) { throw system_error(error_code(3, generic_category()), "Failed persisting data to file!"); } + + // Flush the intermediate buffer and update the destination. + this->io.flush(); } void UserModel::setAttributes(map &cleanInputs) diff --git a/app/UserRepository.cpp b/app/UserRepository.cpp index f76739c..ea9f454 100644 --- a/app/UserRepository.cpp +++ b/app/UserRepository.cpp @@ -29,6 +29,7 @@ void UserRepository::defineValidation() { "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." }, + { "action", "Please provide a valid action!" } }; Controller::pushError(string("")); @@ -103,9 +104,10 @@ void UserRepository::receiveCleanInput(map &cleanInput) // Determine what to output via the model. void UserRepository::echo(const string &alias) { + this->model.setActive(); + if (alias == "fullname") { - this->model.setActive(); printString(this->model.getFullName()); } } @@ -118,7 +120,6 @@ void UserRepository::logOutUser() { User activeUser = users.model.setActive(); users.model.markAs(string("logged_out"), activeUser.id); - users.model.save(); } catch (const exception &e) { diff --git a/app/View.cpp b/app/View.cpp index 993debd..7cc6223 100644 --- a/app/View.cpp +++ b/app/View.cpp @@ -56,7 +56,8 @@ void View::loadViewsOptions() logoutView = "logout.view", findOrAskView = "find-or-ask.view", createView = "create.view", - searchResultsView = "search-results.view"; + searchResultsView = "search-results.view", + showQuestionView = "show-question.view"; // Create non-linkable views, e.g. search results @@ -82,7 +83,9 @@ void View::loadViewsOptions() { searchResultsView, { { '1', createView }, { '3', dashboardView } } }, - { createView, { { '1', dashboardView } } } + { createView, { } }, + + { showQuestionView, { { '1', dashboardView } } } }; } diff --git a/views/questions/show-question.view b/views/questions/show-question.view new file mode 100644 index 0000000..e1b276a --- /dev/null +++ b/views/questions/show-question.view @@ -0,0 +1,19 @@ + __ _ _ ___ _ ___ _ +/ _\ |_ __ _ ___| | __ / _ \ |_ _ ___ / _ \ |_ _ ___ +\ \| __/ _` |/ __| |/ // /_)/ | | | / __| / /_)/ | | | / __| +_\ \ || (_| | (__|