From 1f843372d30fcb3bd21ed545c5a16b8dfe5b6c0d Mon Sep 17 00:00:00 2001 From: silverqx Date: Sun, 10 Jul 2022 10:40:48 +0200 Subject: [PATCH] tom added force option to make commands Allow to overwrite already existing migration, model, seeder. --- cmake/Modules/TinySources.cmake | 2 + tom/include/include.pri | 1 + tom/include/tom/commands/make/makecommand.hpp | 56 ++++++++++ .../tom/commands/make/migrationcommand.hpp | 4 +- .../tom/commands/make/modelcommand.hpp | 15 ++- .../tom/commands/make/seedercommand.hpp | 8 +- .../make/support/migrationcreator.hpp | 7 -- .../commands/make/support/modelcreator.hpp | 9 -- .../commands/make/support/seedercreator.hpp | 9 -- tom/src/src.pri | 1 + tom/src/tom/commands/make/makecommand.cpp | 100 ++++++++++++++++++ .../tom/commands/make/migrationcommand.cpp | 30 ++++-- tom/src/tom/commands/make/modelcommand.cpp | 56 +++++----- tom/src/tom/commands/make/seedercommand.cpp | 41 +++---- .../make/support/migrationcreator.cpp | 43 -------- .../commands/make/support/modelcreator.cpp | 42 -------- .../commands/make/support/seedercreator.cpp | 43 -------- tools/completions/tom.zsh | 9 +- 18 files changed, 251 insertions(+), 225 deletions(-) create mode 100644 tom/include/tom/commands/make/makecommand.hpp create mode 100644 tom/src/tom/commands/make/makecommand.cpp diff --git a/cmake/Modules/TinySources.cmake b/cmake/Modules/TinySources.cmake index 80a0eeb76..1f4d0dba9 100644 --- a/cmake/Modules/TinySources.cmake +++ b/cmake/Modules/TinySources.cmake @@ -266,6 +266,7 @@ function(tinytom_sources out_headers out_sources) commands/integratecommand.hpp commands/listcommand.hpp commands/make/concerns/prepareoptionvalues.hpp + commands/make/makecommand.hpp commands/make/migrationcommand.hpp commands/make/modelcommand.hpp commands/make/modelcommandconcepts.hpp @@ -334,6 +335,7 @@ function(tinytom_sources out_headers out_sources) commands/integratecommand.cpp commands/listcommand.cpp commands/make/concerns/prepareoptionvalues.cpp + commands/make/makecommand.cpp commands/make/migrationcommand.cpp commands/make/modelcommand.cpp # commands/make/projectcommand.cpp diff --git a/tom/include/include.pri b/tom/include/include.pri index 713502053..a98f6a956 100644 --- a/tom/include/include.pri +++ b/tom/include/include.pri @@ -19,6 +19,7 @@ headersList += \ $$PWD/tom/commands/integratecommand.hpp \ $$PWD/tom/commands/listcommand.hpp \ $$PWD/tom/commands/make/concerns/prepareoptionvalues.hpp \ + $$PWD/tom/commands/make/makecommand.hpp \ $$PWD/tom/commands/make/migrationcommand.hpp \ $$PWD/tom/commands/make/modelcommand.hpp \ $$PWD/tom/commands/make/modelcommandconcepts.hpp \ diff --git a/tom/include/tom/commands/make/makecommand.hpp b/tom/include/tom/commands/make/makecommand.hpp new file mode 100644 index 000000000..781c06e86 --- /dev/null +++ b/tom/include/tom/commands/make/makecommand.hpp @@ -0,0 +1,56 @@ +#pragma once +#ifndef TOM_COMMANDS_MAKE_MAKECOMMAND_HPP +#define TOM_COMMANDS_MAKE_MAKECOMMAND_HPP + +#include +TINY_SYSTEM_HEADER + +#include + +#include "tom/commands/command.hpp" + +TINYORM_BEGIN_COMMON_NAMESPACE + +namespace Tom::Commands::Make +{ + + /*! Base class for the make-related commands. */ + class MakeCommand : public Command + { + Q_DISABLE_COPY(MakeCommand) + + /*! Alias for the filesystem path. */ + using fspath = std::filesystem::path; + + public: + /*! Constructor. */ + MakeCommand(Application &application, QCommandLineParser &parser); + /*! Virtual destructor. */ + inline ~MakeCommand() override = default; + + protected: + /*! Check whether the created file already exists and create folder if needed. */ + void prepareFileSystem( + const QString &type, const fspath &folder, const QString &basename, + const QString &className = {}) const; + + /*! Throw if the given classname constains a namespace or path. */ + static void throwIfContainsNamespaceOrPath( + const QString &type, const QString &className, + const QString &source); + + private: + /*! Ensure that a file in the given folder doesn't already exist. */ + void throwIfModelAlreadyExists( + const QString &type, const fspath &folder, const QString &basename, + const QString &className) const; + + /*! Ensure a directory exists. */ + static void ensureDirectoryExists(const fspath &path); + }; + +} // namespace Tom::Commands::Make + +TINYORM_END_COMMON_NAMESPACE + +#endif // TOM_COMMANDS_MAKE_MAKECOMMAND_HPP diff --git a/tom/include/tom/commands/make/migrationcommand.hpp b/tom/include/tom/commands/make/migrationcommand.hpp index 09b3a4ce9..058967a88 100644 --- a/tom/include/tom/commands/make/migrationcommand.hpp +++ b/tom/include/tom/commands/make/migrationcommand.hpp @@ -5,7 +5,7 @@ #include TINY_SYSTEM_HEADER -#include "tom/commands/command.hpp" +#include "tom/commands/make/makecommand.hpp" #include "tom/commands/make/support/migrationcreator.hpp" #include "tom/tomconstants.hpp" @@ -15,7 +15,7 @@ namespace Tom::Commands::Make { /*! Create a new migration file. */ - class MigrationCommand : public Command + class MigrationCommand : public MakeCommand { Q_DISABLE_COPY(MigrationCommand) diff --git a/tom/include/tom/commands/make/modelcommand.hpp b/tom/include/tom/commands/make/modelcommand.hpp index 799c0df75..1d3dc6501 100644 --- a/tom/include/tom/commands/make/modelcommand.hpp +++ b/tom/include/tom/commands/make/modelcommand.hpp @@ -7,8 +7,8 @@ TINY_SYSTEM_HEADER #include -#include "tom/commands/command.hpp" #include "tom/commands/make/concerns/prepareoptionvalues.hpp" +#include "tom/commands/make/makecommand.hpp" #include "tom/commands/make/modelcommandconcepts.hpp" #include "tom/commands/make/support/modelcreator.hpp" #include "tom/tomconstants.hpp" @@ -28,7 +28,7 @@ namespace Support } // namespace Support /*! Create a new model class. */ - class ModelCommand : public Command, + class ModelCommand : public MakeCommand, protected Concerns::PrepareOptionValues { Q_DISABLE_COPY(ModelCommand) @@ -121,13 +121,12 @@ namespace Support private: /*! Throw if the model name constains a namespace or path. */ static void throwIfContainsNamespaceOrPath( - const std::vector &classNames, const QString &source); + const std::vector &classNames, const QString &source, + const QString &commandType = QStringLiteral("model")); /*! Throw if the model name constains a namespace or path. */ - static void throwIfContainsNamespaceOrPath(const QStringList &classNames, - const QString &source); - /*! Throw if the model name constains a namespace or path. */ - static void throwIfContainsNamespaceOrPath(const QString &className, - const QString &source); + static void throwIfContainsNamespaceOrPath( + const QStringList &classNames, const QString &source, + const QString &commandType = QStringLiteral("model")); }; /* public */ diff --git a/tom/include/tom/commands/make/seedercommand.hpp b/tom/include/tom/commands/make/seedercommand.hpp index 5195a9b69..548c4ee94 100644 --- a/tom/include/tom/commands/make/seedercommand.hpp +++ b/tom/include/tom/commands/make/seedercommand.hpp @@ -5,7 +5,7 @@ #include TINY_SYSTEM_HEADER -#include "tom/commands/command.hpp" +#include "tom/commands/make/makecommand.hpp" #include "tom/commands/make/support/seedercreator.hpp" #include "tom/tomconstants.hpp" @@ -15,7 +15,7 @@ namespace Tom::Commands::Make { /*! Create a new seeder class. */ - class SeederCommand : public Command + class SeederCommand : public MakeCommand { Q_DISABLE_COPY(SeederCommand) @@ -54,10 +54,6 @@ namespace Tom::Commands::Make /*! The seeder creator instance. */ Support::SeederCreator m_creator {}; - - private: - /*! Throw if the seeder name constains a namespace or path. */ - static void throwIfContainsNamespaceOrPath(const QString &className); }; /* public */ diff --git a/tom/include/tom/commands/make/support/migrationcreator.hpp b/tom/include/tom/commands/make/support/migrationcreator.hpp index d741eebf4..8f0fd8cd7 100644 --- a/tom/include/tom/commands/make/support/migrationcreator.hpp +++ b/tom/include/tom/commands/make/support/migrationcreator.hpp @@ -50,13 +50,6 @@ namespace Tom::Commands::Make::Support populateStub(const QString &name, QString &&stub, const QString &table); /*! Get the class name of a migration name. */ static QString getClassName(const QString &name); - /*! Ensure a directory exists. */ - static void ensureDirectoryExists(const fspath &path); - - private: - /*! Ensure that a migration with the given name doesn't already exist. */ - static void throwIfMigrationAlreadyExists(const QString &name, - const fspath &migrationsPath); }; } // namespace Tom::Commands::Make::Support diff --git a/tom/include/tom/commands/make/support/modelcreator.hpp b/tom/include/tom/commands/make/support/modelcreator.hpp index 7e6cf8705..f51eaaccb 100644 --- a/tom/include/tom/commands/make/support/modelcreator.hpp +++ b/tom/include/tom/commands/make/support/modelcreator.hpp @@ -41,9 +41,6 @@ namespace Tom::Commands::Make::Support /*! Get the full path to the model. */ static fspath getPath(const QString &basename, const fspath &path); - /*! Ensure a directory exists. */ - static void ensureDirectoryExists(const fspath &path); - /*! Populate the place-holders in the model stub. */ std::string populateStub(const QString &className, const CmdOptions &cmdOptions, bool isSetPreserveOrder); @@ -192,12 +189,6 @@ namespace Tom::Commands::Make::Support /*! Cached relations list size to avoid recomputation. */ std::size_t m_relationsListSize = 0; - - private: - /*! Ensure that a model with the given name doesn't already exist. */ - static void throwIfModelAlreadyExists( - const QString &className, const QString &basename, - const fspath &modelsPath); }; } // namespace Tom::Commands::Make::Support diff --git a/tom/include/tom/commands/make/support/seedercreator.hpp b/tom/include/tom/commands/make/support/seedercreator.hpp index aa82bfd1d..22e10b690 100644 --- a/tom/include/tom/commands/make/support/seedercreator.hpp +++ b/tom/include/tom/commands/make/support/seedercreator.hpp @@ -37,21 +37,12 @@ namespace Tom::Commands::Make::Support /*! Get the full path to the seeder. */ static fspath getPath(const QString &basename, const fspath &path); - /*! Ensure a directory exists. */ - static void ensureDirectoryExists(const fspath &path); - /*! Populate the place-holders in the seeder stub. */ static std::string populateStub(const QString &className, QString &&table); /*! Get the table name from the seeder class name. */ static QString getTableName(QString className); - - private: - /*! Ensure that a seeder with the given name doesn't already exist. */ - void throwIfSeederAlreadyExists( - const QString &className, const QString &basename, - const fspath &seedersPath) const; }; } // namespace Tom::Commands::Make::Support diff --git a/tom/src/src.pri b/tom/src/src.pri index 7e67dc8a4..994791e2a 100644 --- a/tom/src/src.pri +++ b/tom/src/src.pri @@ -14,6 +14,7 @@ sourcesList += \ $$PWD/tom/commands/integratecommand.cpp \ $$PWD/tom/commands/listcommand.cpp \ $$PWD/tom/commands/make/concerns/prepareoptionvalues.cpp \ + $$PWD/tom/commands/make/makecommand.cpp \ $$PWD/tom/commands/make/migrationcommand.cpp \ $$PWD/tom/commands/make/modelcommand.cpp \ # $$PWD/tom/commands/make/projectcommand.cpp \ diff --git a/tom/src/tom/commands/make/makecommand.cpp b/tom/src/tom/commands/make/makecommand.cpp new file mode 100644 index 000000000..265b630ad --- /dev/null +++ b/tom/src/tom/commands/make/makecommand.cpp @@ -0,0 +1,100 @@ +#include "tom/commands/make/makecommand.hpp" + +#include "tom/exceptions/invalidargumenterror.hpp" +#include "tom/tomconstants.hpp" + +namespace fs = std::filesystem; + +using fspath = std::filesystem::path; + +using Tom::Constants::DateTimePrefix; +using Tom::Constants::force; + +TINYORM_BEGIN_COMMON_NAMESPACE + +namespace Tom::Commands::Make +{ + +/* public */ + +MakeCommand::MakeCommand(Application &application, QCommandLineParser &parser) + : Command(application, parser) +{} + +/* protected */ + +void MakeCommand::prepareFileSystem( + const QString &type, const fspath &folder, const QString &basename, + const QString &className) const +{ + throwIfModelAlreadyExists(type, folder, basename, className); + + ensureDirectoryExists(folder); +} + +void MakeCommand::throwIfContainsNamespaceOrPath( + const QString &type, const QString &className, const QString &source) +{ + if (!className.contains(QStringLiteral("::")) && !className.contains(QChar('/')) && + !className.contains(QChar('\\')) + ) + return; + + throw Exceptions::InvalidArgumentError( + QStringLiteral("Namespace or path is not allowed in the %1 name (%2).") + .arg(type, source)); +} + +/* private */ + +void MakeCommand::throwIfModelAlreadyExists( + const QString &type, const fspath &folder, const QString &basename, + const QString &className) const +{ + // Nothing to check + if (!fs::exists(folder)) + return; + + auto itemName = className.isEmpty() ? basename : className; + + using options = fs::directory_options; + + for (const auto &entry : + fs::directory_iterator(folder, options::skip_permission_denied) + ) { + // Check only files + if (!entry.is_regular_file()) + continue; + + // Extract base filename without the extension + auto entryName = QString::fromStdString(entry.path().stem().string()); + + // If checking a migration then also remove the datetime prefix + if (type == QStringLiteral("migration")) + entryName = entryName.mid(DateTimePrefix.size() + 1); + + if (entryName == basename) { + // Allow overwriting a file using the --force option + if (!isSet(force)) + throw Exceptions::InvalidArgumentError( + QStringLiteral("A '%1' %2 already exists.") + .arg(std::move(itemName), type)); + + comment(QStringLiteral("Overwriting '%1' already existing %2.") + .arg(std::move(itemName), type)); + break; + } + } +} + +void MakeCommand::ensureDirectoryExists(const fspath &path) +{ + if (fs::exists(path) && fs::is_directory(path)) + return; + + fs::create_directories(path); +} + +} // namespace Tom::Commands::Make + +TINYORM_END_COMMON_NAMESPACE diff --git a/tom/src/tom/commands/make/migrationcommand.cpp b/tom/src/tom/commands/make/migrationcommand.cpp index b0cef99d2..da1a3dc73 100644 --- a/tom/src/tom/commands/make/migrationcommand.cpp +++ b/tom/src/tom/commands/make/migrationcommand.cpp @@ -23,6 +23,7 @@ using StringUtils = Orm::Tiny::Utils::String; using Tom::Constants::create_; using Tom::Constants::create_up; +using Tom::Constants::force; using Tom::Constants::fullpath; using Tom::Constants::path_; using Tom::Constants::path_up; @@ -41,7 +42,7 @@ namespace Tom::Commands::Make /* public */ MigrationCommand::MigrationCommand(Application &application, QCommandLineParser &parser) - : Command(application, parser) + : MakeCommand(application, parser) {} const std::vector &MigrationCommand::positionalArguments() const @@ -56,13 +57,16 @@ const std::vector &MigrationCommand::positionalArguments() c QList MigrationCommand::optionsSignature() const { return { - {create_, QStringLiteral("The table name to be created"), create_up}, // Value - {table_, QStringLiteral("The table name to migrate (update)"), table_up}, // Value - {path_, QStringLiteral("The location where the migration file should be " - "created"), path_up}, // Value - {realpath_, QStringLiteral("Indicate that any provided migration file paths are " - "pre-resolved absolute paths")}, - {fullpath, QStringLiteral("Output the full path of the created migration")}, + {create_, QStringLiteral("The table name to be created"), create_up}, // Value + {table_, QStringLiteral("The table name to migrate (update)"), table_up}, // Value + {path_, QStringLiteral("The location where the migration file should be " + "created"), path_up}, // Value + {realpath_, QStringLiteral("Indicate that any provided migration file paths " + "are pre-resolved absolute paths")}, + {fullpath, QStringLiteral("Output the full path of the created migration")}, + + {{QChar('f'), + force}, QStringLiteral("Overwrite the migration file if already exists")}, }; } @@ -76,6 +80,9 @@ int MigrationCommand::run() auto [datetimePrefix, migrationName, extension] = prepareMigrationNameClassName(argument(NAME).trimmed()); + // Check whether a migration file already exists and create parent folder if needed + prepareFileSystem(QStringLiteral("migration"), getMigrationPath(), migrationName); + auto table = value(table_); auto createArg = value(create_); @@ -206,6 +213,11 @@ void MigrationCommand::writeMigration( fspath MigrationCommand::getMigrationPath() const { + static fspath cached; + + if (!cached.empty()) + return cached; + // Default location if (!isSet(path_)) return application().getMigrationsPath(); @@ -225,7 +237,7 @@ fspath MigrationCommand::getMigrationPath() const QStringLiteral("Migrations path '%1' exists and it's not a directory.") .arg(migrationsPath.c_str())); - return migrationsPath; + return cached = migrationsPath; } } // namespace Tom::Commands::Make diff --git a/tom/src/tom/commands/make/modelcommand.cpp b/tom/src/tom/commands/make/modelcommand.cpp index c94c313b9..232d143fc 100644 --- a/tom/src/tom/commands/make/modelcommand.cpp +++ b/tom/src/tom/commands/make/modelcommand.cpp @@ -41,6 +41,7 @@ using Tom::Constants::disable_incrementing; using Tom::Constants::disable_timestamps; using Tom::Constants::fillable; using Tom::Constants::fillable_up; +using Tom::Constants::force; using Tom::Constants::foreign_key; using Tom::Constants::foreign_key_up; using Tom::Constants::fullpath; @@ -84,7 +85,7 @@ namespace Tom::Commands::Make /* public */ ModelCommand::ModelCommand(Application &application, QCommandLineParser &parser) - : Command(application, parser) + : MakeCommand(application, parser) {} const std::vector &ModelCommand::positionalArguments() const @@ -186,6 +187,9 @@ QList ModelCommand::optionsSignature() const "paths are pre-resolved absolute paths")}, {fullpath, QStringLiteral("Output the full path of the created " "model")}, + {{QChar('f'), + force}, QStringLiteral("Overwrite the model class if already " + "exists")}, }; } @@ -227,6 +231,7 @@ int ModelCommand::run() const auto [className, cmdOptions] = prepareModelClassNames(argument(NAME), createCmdOptions()); + // Unused warnings showUnusedOptionsWarnings(cmdOptions); if (!m_unusedBtmOptions.empty() || m_shownUnusedForeignKey || @@ -234,6 +239,10 @@ int ModelCommand::run() ) newLine(); + // Check whether a model file already exists and create parent folder if needed + prepareFileSystem(QStringLiteral("model"), getModelPath(), className.toLower(), + className); + // Ready to write the model to the disk 🧨✨ writeModel(className, cmdOptions); @@ -275,7 +284,8 @@ ModelCommand::prepareModelClassNames(QString &&className, CmdOptions &&cmdOption ] = cmdOptions; // Validate the model class names - throwIfContainsNamespaceOrPath(className, QStringLiteral("argument 'name'")); + MakeCommand::throwIfContainsNamespaceOrPath(QStringLiteral("model"), className, + QStringLiteral("argument 'name'")); throwIfContainsNamespaceOrPath(oneToOneList, QStringLiteral("option --one-to-one")); throwIfContainsNamespaceOrPath(oneToManyList, @@ -285,9 +295,11 @@ ModelCommand::prepareModelClassNames(QString &&className, CmdOptions &&cmdOption throwIfContainsNamespaceOrPath(belongsToManyList, QStringLiteral("option --belongs-to-many")); throwIfContainsNamespaceOrPath(pivotClasses, - QStringLiteral("option --pivot")); + QStringLiteral("option --pivot"), + QStringLiteral("pivot model")); throwIfContainsNamespaceOrPath(pivotInverseClasses, - QStringLiteral("option --pivot-inverse")); + QStringLiteral("option --pivot-inverse"), + QStringLiteral("pivot model")); oneToOneList = StringUtils::studly(std::move(oneToOneList)); oneToManyList = StringUtils::studly(std::move(oneToManyList)); @@ -484,6 +496,11 @@ RelationsOrder ModelCommand::relationsOrder() fspath ModelCommand::getModelPath() const { + static fspath cached; + + if (!cached.empty()) + return cached; + // Default location if (!isSet(path_)) return application().getModelsPath(); @@ -503,7 +520,7 @@ fspath ModelCommand::getModelPath() const QStringLiteral("Models path '%1' exists and it's not a directory.") .arg(modelsPath.c_str())); - return modelsPath; + return cached = modelsPath; } const std::unordered_set &ModelCommand::relationNames() @@ -525,6 +542,7 @@ void ModelCommand::createMigration(const QString &className) const table.append(QChar('s')); call(MakeMigration, {longOption(create_, table), + boolCmd(force), QStringLiteral("create_%1_table").arg(std::move(table))}); } @@ -534,37 +552,25 @@ void ModelCommand::createSeeder(const QString &className) const // FUTURE tom, add hidden options support to the tom's parser, add --table option for the make:seeder command and pass singular table name for pivot models because currently the make:seeder command generates eg. taggeds table name (even if this table name is in the commented code), command to reproduce: tom make:model Tagged --pivot-model --seeder silverqx - call(MakeSeeder, {std::move(seederClassName)}); + call(MakeSeeder, {boolCmd(force), std::move(seederClassName)}); } /* private */ void ModelCommand::throwIfContainsNamespaceOrPath( - const std::vector &classNames, const QString &source) + const std::vector &classNames, const QString &source, + const QString &commandType) { for (const auto &classNameList : classNames) - throwIfContainsNamespaceOrPath(classNameList, source); + throwIfContainsNamespaceOrPath(classNameList, source, commandType); } -void ModelCommand::throwIfContainsNamespaceOrPath(const QStringList &classNames, - const QString &source) +void ModelCommand::throwIfContainsNamespaceOrPath( + const QStringList &classNames, const QString &source, + const QString &commandType) { for (const auto &className : classNames) - throwIfContainsNamespaceOrPath(className, source); -} - -void ModelCommand::throwIfContainsNamespaceOrPath(const QString &className, - const QString &source) -{ - if (!className.contains(QStringLiteral("::")) && !className.contains(QChar('/')) && - !className.contains(QChar('\\')) - ) - return; - - throw Exceptions::InvalidArgumentError( - QStringLiteral("Namespace or path is not allowed in the model " - "names (%1).") - .arg(source)); + MakeCommand::throwIfContainsNamespaceOrPath(commandType, className, source); } } // namespace Tom::Commands::Make diff --git a/tom/src/tom/commands/make/seedercommand.cpp b/tom/src/tom/commands/make/seedercommand.cpp index acb3ff60f..13e9f89cb 100644 --- a/tom/src/tom/commands/make/seedercommand.cpp +++ b/tom/src/tom/commands/make/seedercommand.cpp @@ -15,10 +15,12 @@ using Orm::Constants::NAME; using StringUtils = Orm::Tiny::Utils::String; +using Tom::Constants::force; using Tom::Constants::fullpath; using Tom::Constants::path_; using Tom::Constants::path_up; using Tom::Constants::realpath_; +using Tom::Constants::seeder; TINYORM_BEGIN_COMMON_NAMESPACE @@ -28,7 +30,7 @@ namespace Tom::Commands::Make /* public */ SeederCommand::SeederCommand(Application &application, QCommandLineParser &parser) - : Command(application, parser) + : MakeCommand(application, parser) {} const std::vector &SeederCommand::positionalArguments() const @@ -43,11 +45,14 @@ const std::vector &SeederCommand::positionalArguments() cons QList SeederCommand::optionsSignature() const { return { - {path_, QStringLiteral("The location where the seeder file should be " - "created"), path_up}, // Value - {realpath_, QStringLiteral("Indicate that any provided seeder file paths are " - "pre-resolved absolute paths")}, - {fullpath, QStringLiteral("Output the full path of the created seeder")}, + {path_, QStringLiteral("The location where the seeder file should be " + "created"), path_up}, // Value + {realpath_, QStringLiteral("Indicate that any provided seeder file paths are " + "pre-resolved absolute paths")}, + {fullpath, QStringLiteral("Output the full path of the created seeder")}, + + {{QChar('f'), + force}, QStringLiteral("Overwrite the seeder class if already exists")}, }; } @@ -57,6 +62,9 @@ int SeederCommand::run() const auto className = prepareSeederClassName(argument(NAME)); + // Check whether a seeder file already exists and create parent folder if needed + prepareFileSystem(seeder, getSeederPath(), className.toLower(), className); + // Ready to write the seeder to the disk 🧨✨ writeSeeder(className); @@ -68,7 +76,8 @@ int SeederCommand::run() QString SeederCommand::prepareSeederClassName(QString &&className) { // Validate the seeder name - throwIfContainsNamespaceOrPath(className); + throwIfContainsNamespaceOrPath(QStringLiteral("seeder"), className, + QStringLiteral("argument 'name'")); static const auto Seeder = QStringLiteral("Seeder"); static const auto Seeder_lc = QStringLiteral("seeder"); @@ -106,6 +115,11 @@ void SeederCommand::writeSeeder(const QString &className) const fspath SeederCommand::getSeederPath() const { + static fspath cached; + + if (!cached.empty()) + return cached; + // Default location if (!isSet(path_)) return application().getSeedersPath(); @@ -125,18 +139,7 @@ fspath SeederCommand::getSeederPath() const QStringLiteral("Seeders path '%1' exists and it's not a directory.") .arg(seedersPath.c_str())); - return seedersPath; -} - -void SeederCommand::throwIfContainsNamespaceOrPath(const QString &className) -{ - if (!className.contains(QStringLiteral("::")) && !className.contains(QChar('/')) && - !className.contains(QChar('\\')) - ) - return; - - throw Exceptions::InvalidArgumentError( - "Namespace or path is not allowed in the seeder name."); + return cached = seedersPath; } } // namespace Tom::Commands::Make diff --git a/tom/src/tom/commands/make/support/migrationcreator.cpp b/tom/src/tom/commands/make/support/migrationcreator.cpp index ae0bb115c..769c1b900 100644 --- a/tom/src/tom/commands/make/support/migrationcreator.cpp +++ b/tom/src/tom/commands/make/support/migrationcreator.cpp @@ -7,11 +7,8 @@ #include #include "tom/commands/make/stubs/migrationstubs.hpp" -#include "tom/exceptions/invalidargumenterror.hpp" #include "tom/tomconstants.hpp" -namespace fs = std::filesystem; - using fspath = std::filesystem::path; using StringUtils = Orm::Tiny::Utils::String; @@ -35,15 +32,11 @@ fspath MigrationCreator::create( auto migrationPath = getPath(std::move(datetimePrefix), name, std::move(extension), migrationsPath); - throwIfMigrationAlreadyExists(name, migrationsPath); - /* First we will get the stub file for the migration, which serves as a type of template for the migration. Once we have those we will populate the various place-holders, and save the file. */ auto stub = getStub(table, create); - ensureDirectoryExists(migrationsPath); - // Output it as binary stream to force line endings to LF std::ofstream(migrationPath, std::ios::out | std::ios::binary) << populateStub(name, std::move(stub), table); @@ -112,42 +105,6 @@ QString MigrationCreator::getClassName(const QString &name) return StringUtils::studly(name); } -void MigrationCreator::ensureDirectoryExists(const fspath &path) -{ - if (fs::exists(path) && fs::is_directory(path)) - return; - - fs::create_directories(path); -} - -/* private */ - -void MigrationCreator::throwIfMigrationAlreadyExists(const QString &name, - const fspath &migrationsPath) -{ - // Nothing to check - if (!fs::exists(migrationsPath)) - return; - - using options = fs::directory_options; - - for (const auto &entry : - fs::directory_iterator(migrationsPath, options::skip_permission_denied) - ) { - // Check only files - if (!entry.is_regular_file()) - continue; - - // Extract migration name without datetime prefix and extension - auto entryName = QString::fromStdString(entry.path().stem().string()) - .mid(DateTimePrefix.size() + 1); - - if (entryName == name) - throw Exceptions::InvalidArgumentError( - QStringLiteral("A '%1' migration already exists.").arg(name)); - } -} - } // namespace Tom::Commands::Make::Support TINYORM_END_COMMON_NAMESPACE diff --git a/tom/src/tom/commands/make/support/modelcreator.cpp b/tom/src/tom/commands/make/support/modelcreator.cpp index 83806e846..36c475fbb 100644 --- a/tom/src/tom/commands/make/support/modelcreator.cpp +++ b/tom/src/tom/commands/make/support/modelcreator.cpp @@ -17,9 +17,6 @@ #include "tom/commands/make/modelcommandconcepts.hpp" #include "tom/commands/make/stubs/modelstubs.hpp" -#include "tom/exceptions/invalidargumenterror.hpp" - -namespace fs = std::filesystem; using fspath = std::filesystem::path; @@ -74,10 +71,6 @@ fspath ModelCreator::create(const QString &className, const CmdOptions &cmdOptio auto modelPath = getPath(basename, modelsPath); - throwIfModelAlreadyExists(className, basename, modelsPath); - - ensureDirectoryExists(modelsPath); - // Output it as binary stream to force line endings to LF std::ofstream(modelPath, std::ios::out | std::ios::binary) << populateStub(className, cmdOptions, isSetPreserveOrder); @@ -92,14 +85,6 @@ fspath ModelCreator::getPath(const QString &basename, const fspath &path) return path / (basename.toStdString() + ".hpp"); } -void ModelCreator::ensureDirectoryExists(const fspath &path) -{ - if (fs::exists(path) && fs::is_directory(path)) - return; - - fs::create_directories(path); -} - std::string ModelCreator::populateStub( const QString &className, const CmdOptions &cmdOptions, const bool isSetPreserveOrder) @@ -1062,33 +1047,6 @@ QString ModelCreator::joinRelationsList(RelationsWithOrder &&relationsList) return relationsQList.join(NEWLINE); } -/* private */ - -void ModelCreator::throwIfModelAlreadyExists( - const QString &className, const QString &basename, const fspath &modelsPath) -{ - // Nothing to check - if (!fs::exists(modelsPath)) - return; - - using options = fs::directory_options; - - for (const auto &entry : - fs::directory_iterator(modelsPath, options::skip_permission_denied) - ) { - // Check only files - if (!entry.is_regular_file()) - continue; - - // Extract base filename without the extension - auto entryName = QString::fromStdString(entry.path().stem().string()); - - if (entryName == basename) - throw Exceptions::InvalidArgumentError( - QStringLiteral("A '%1' model already exists.").arg(className)); - } -} - } // namespace Tom::Commands::Make::Support TINYORM_END_COMMON_NAMESPACE diff --git a/tom/src/tom/commands/make/support/seedercreator.cpp b/tom/src/tom/commands/make/support/seedercreator.cpp index b75c26bc5..3b6ef7ee0 100644 --- a/tom/src/tom/commands/make/support/seedercreator.cpp +++ b/tom/src/tom/commands/make/support/seedercreator.cpp @@ -5,9 +5,6 @@ #include #include "tom/commands/make/stubs/seederstubs.hpp" -#include "tom/exceptions/invalidargumenterror.hpp" - -namespace fs = std::filesystem; using fspath = std::filesystem::path; @@ -28,10 +25,6 @@ fspath SeederCreator::create(const QString &className, fspath &&seedersPath) con auto seederPath = getPath(basename, seedersPath); - throwIfSeederAlreadyExists(className, basename, seedersPath); - - ensureDirectoryExists(seedersPath); - /* Populate the various place-holders, and save the file. Output it as binary stream to force line endings to LF. */ std::ofstream(seederPath, std::ios::out | std::ios::binary) @@ -47,14 +40,6 @@ fspath SeederCreator::getPath(const QString &basename, const fspath &path) return path / (basename.toStdString() + ".hpp"); } -void SeederCreator::ensureDirectoryExists(const fspath &path) -{ - if (fs::exists(path) && fs::is_directory(path)) - return; - - fs::create_directories(path); -} - std::string SeederCreator::populateStub(const QString &className, QString &&table) { @@ -87,34 +72,6 @@ QString SeederCreator::getTableName(QString className) return StringUtils::snake(className); } -/* private */ - -void SeederCreator::throwIfSeederAlreadyExists( - const QString &className, const QString &basename, - const fspath &seedersPath) const -{ - // Nothing to check - if (!fs::exists(seedersPath)) - return; - - using options = fs::directory_options; - - for (const auto &entry : - fs::directory_iterator(seedersPath, options::skip_permission_denied) - ) { - // Check only files - if (!entry.is_regular_file()) - continue; - - // Extract base filename without the extension - auto entryName = QString::fromStdString(entry.path().stem().string()); - - if (entryName == basename) - throw Exceptions::InvalidArgumentError( - QStringLiteral("A '%1' seeder already exists.").arg(className)); - } -} - } // namespace Tom::Commands::Make::Support TINYORM_END_COMMON_NAMESPACE diff --git a/tools/completions/tom.zsh b/tools/completions/tom.zsh index 10941a153..553de5d1a 100644 --- a/tools/completions/tom.zsh +++ b/tools/completions/tom.zsh @@ -187,7 +187,8 @@ _tom() { '--table=[The table to migrate]:table name' \ '--path=[The location where the migration file should be created]:folder path:_files -/' \ '--realpath[Indicate any provided migration file paths are pre-resolved absolute paths]' \ - '--fullpath[Output the full path of the migration]' + '--fullpath[Output the full path of the migration]' \ + '(-f --force)'{-f,--force}'[Overwrite the model class if already exists]' ;; make:model) @@ -220,7 +221,8 @@ _tom() { '(-o --preserve-order)'{-o,--preserve-order}'[Preserve relations order defined on the command-line]' \ '--path=[The location where the migration file should be created]:folder path:_files -/' \ '--realpath[Indicate any provided migration file paths are pre-resolved absolute paths]' \ - '--fullpath[Output the full path of the migration]' + '--fullpath[Output the full path of the migration]' \ + '(-f --force)'{-f,--force}'[Overwrite the migration file if already exists]' ;; make:seeder) @@ -229,7 +231,8 @@ _tom() { '1::class name:()' \ '--path=[The location where the migration file should be created]:folder path:_files -/' \ '--realpath[Indicate any provided migration file paths are pre-resolved absolute paths]' \ - '--fullpath[Output the full path of the migration]' + '--fullpath[Output the full path of the migration]' \ + '(-f --force)'{-f,--force}'[Overwrite the seeder class if already exists]' ;; migrate:fresh)