diff --git a/config.py b/config.py index 3d54914a..397ca74f 100644 --- a/config.py +++ b/config.py @@ -11,9 +11,6 @@ class Config: DEBUG = True - # Parse domain directory and autoload domain modules - AUTOLOAD_DOMAIN = True - # A secret key for this particular Protean installation. Used in secret-key # hashing algorithms. SECRET_KEY = os.environ.get( diff --git a/docs/user/composing-a-domain.rst b/docs/user/composing-a-domain.rst index fe5177d3..fc98ca23 100644 --- a/docs/user/composing-a-domain.rst +++ b/docs/user/composing-a-domain.rst @@ -11,7 +11,7 @@ The Domain is the one-stop gateway to: - Retrieve dynamically-constructed artifacts like repositories and models - Access injected technology components at runtime -Initializing a Domain +Define a Domain --------------------- Constructing the object graph is a two-step procedure. First, you initialize a domain object at a reasonable starting @@ -30,24 +30,28 @@ in application configuration. .. code-block:: python - from sample_app import domain - @domain.aggregate class User: name = String() email = String(required=True) -Configuring a Domain --------------------- +Initializing the Domain +----------------------- -You can pass a config file to the domain, like so: +Finally, the domain is initialized by calling the ``init`` method. This method will construct the object graph and +inject dependencies into the domain elements. .. code-block:: python - domain.config.from_pyfile(config_path) + domain.init() + +By default, a protean domain is configured to use an in-memory repository. This is useful for testing and prototyping. -Refer to :doc:`config` to understand the different ways to configure the domain. +If you do not want Protean to traverse the directory structure to discover domain elements, you can pass the +``traverse`` flag as ``False`` to the ``init`` method. + +You can optionally pass a config file to the domain before initializing it. Refer to :doc:`config` to understand the different ways to configure the domain. Activating a Domain ------------------- @@ -102,6 +106,8 @@ the ``app`` object. @app.before_request def set_context(): + domain.init() + # Push up a Domain Context # This should be done within Flask App context = domain.domain_context() diff --git a/docs/user/quickstart.rst b/docs/user/quickstart.rst index 35f3846d..fb914ad4 100644 --- a/docs/user/quickstart.rst +++ b/docs/user/quickstart.rst @@ -68,6 +68,17 @@ Note that Protean uses SQLAlchemy to access the SQLITE database internally. A database file ``quickstart.db`` will be created in the location you will be running your application from. +Initialize the domain +--------------------- + +Before you can use the domain, you need to initialize it. Initialize the domain by calling :meth:`protean.Domain.init` on the domain instance. + +.. code-block:: python + + domain.init(traverse=False) + +Since all our code is in the same module, we can use ``traverse=False``. If you have your code spread across multiple modules, you can set ``traverse=True`` to traverse the entire module tree and load all the elements. + Configure Flask --------------- @@ -81,20 +92,24 @@ We also register a function to run before Flask processes the very first request app = Flask(__name__) - @app.before_first_request + @app.before_request def set_context(): + context = domain.domain_context() + context.push() + +If you want to create the database tables automatically from the structure defined in the domain, you can: + +.. code-block:: python + + @app.before_first_request + def setup_db(): with domain.domain_context(): for provider in domain.providers_list(): for _, aggregate in domain.registry.aggregates.items(): - domain.get_dao(aggregate.cls) + domain.repository_for(aggregate.cls)._dao provider._metadata.create_all() - @app.before_request - def set_context(): - context = domain.domain_context() - context.push() - .. |flask| raw:: html Flask diff --git a/pyproject.toml b/pyproject.toml index fb41713b..e6952002 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,6 +38,7 @@ click = ">=7.0" cookiecutter = ">=1.7.0" copier = ">=6.1.0" inflection = ">=0.5.1" +marshmallow = ">=3.15.0" # FIXME Remove core dependency python-dateutil = ">=2.8.2" werkzeug = ">=2.0.0" @@ -51,7 +52,6 @@ redis = {version = "~3.5.2", optional = true} sqlalchemy = {version = "~1.4.50", optional = true} psycopg2 = {version = ">=2.9.9", optional = true} celery = { version = "~5.2.7", extras = ["redis"], optional = true} -marshmallow = {version = ">=3.15.0", optional = true} flask = {version = ">=1.1.1", optional = true} sendgrid = {version = ">=6.1.3", optional = true} message-db-py = {version = ">=0.1.2", optional = true} @@ -63,7 +63,6 @@ postgresql = ["sqlalchemy", "psycopg2"] sqlite = ["sqlalchemy"] celery = ["celery"] message-db = ["message-db-py"] -marshmallow = ["marshmallow"] flask = ["flask"] sendgrid = ["sendgrid"] diff --git a/src/protean/domain/__init__.py b/src/protean/domain/__init__.py index 423c1207..e8d998da 100644 --- a/src/protean/domain/__init__.py +++ b/src/protean/domain/__init__.py @@ -96,7 +96,6 @@ class Domain(_PackageBoundObject): "ENV": None, "DEBUG": None, "SECRET_KEY": None, - "AUTOLOAD_DOMAIN": True, "IDENTITY_STRATEGY": IdentityStrategy.UUID.value, "IDENTITY_TYPE": IdentityType.STRING.value, "DATABASES": { @@ -171,15 +170,14 @@ def __init__( # FIXME Should all protean elements be subclassed from a base element? self._pending_class_resolutions: dict[str, Any] = defaultdict(list) - def init(self): # noqa: C901 + def init(self, traverse=True): # noqa: C901 """Parse the domain folder, and attach elements dynamically to the domain. Protean parses all files in the domain file's folder, as well as under it, to load elements. So, all domain files are to be nested under the file contain the domain definition. - One can use the `AUTOLOAD_DOMAIN` flag in Protean config, `True` by default, - to control this functionality. + One can use the `traverse` flag to control this functionality, `True` by default. When enabled, Protean is responsible for loading domain elements and ensuring all functionality is activated. @@ -194,7 +192,7 @@ def init(self): # noqa: C901 This method bubbles up circular import issues, if present, in the domain code. """ - if self.config["AUTOLOAD_DOMAIN"] is True: + if traverse is True: # Standard Library Imports import importlib.util import inspect