Skip to content

Commit

Permalink
fix docs formatting and clarify db_before_import signal usage
Browse files Browse the repository at this point in the history
  • Loading branch information
guruofgentoo committed Oct 25, 2022
1 parent e1d1a6e commit 4337461
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 66 deletions.
28 changes: 14 additions & 14 deletions docs/source/readme/components.rst
Expand Up @@ -5,43 +5,43 @@ Keg components follow the paradigm of flask extensions, and provide some default
purpose of setting up model/view structure. Using components, a project may be broken down into
logical blocks, each having their own entities, blueprints, templates, tests, etc.

* Components need to be registered in config at `KEG_REGISTERED_COMPONENTS`
* Components need to be registered in config at ``KEG_REGISTERED_COMPONENTS``

* The path given here should be a full dotted path to the top level of the component

* e.g. `my_app.components.blog`
* e.g. ``my_app.components.blog``

* At the top level of the component, `__component__` must be defined as an instance of KegComponent
* At the top level of the component, ``__component__`` must be defined as an instance of KegComponent

* Depending on the needs of the component, model and view discovery may be driven by the subclasses
of KegComponent that have path defaults
* Examples:

* `__component__ = KegModelComponent('blog')`
* `__component__ = KegViewComponent('blog')`
* `__component__ = KegModelViewComponent('blog')`
* ``__component__ = KegModelComponent('blog')``
* ``__component__ = KegViewComponent('blog')``
* ``__component__ = KegModelViewComponent('blog')``

* Component discovery

* A component will attempt to load model and blueprints on app init
* The default paths relative to the component may be modified or extended on the component's definition
* Default model path in "model" components: `.model.entities`
* Default model path in "model" components: ``.model.entities``

* Override via the component's `db_visit_modules` list of relative import paths
* Override via the component's ``db_visit_modules`` list of relative import paths

* Default blueprint path for "view" components: `.views.component_bp`
* Default blueprint path for "view" components: ``.views.component_bp``

* Use the `create_named_blueprint` or `create_blueprint` helpers on the component's `__component__`
* Use the ``create_named_blueprint`` or ``create_blueprint`` helpers on the component's ``__component__``
to create blueprints with configured template folders
* Override via the component's `load_blueprints` list
* Override via the component's ``load_blueprints`` list

* List elements are a tuple of the relative import path and the name of the blueprint attribute

* Components have their own template stores, in a `templates` folder
* Components have their own template stores, in a ``templates`` folder

* Override the component's template path via the `template_folder` attribute
* Override the component's template path via the ``template_folder`` attribute

* Paths may also be supplied to the constructor

* e.g. `__component__ = KegComponent('blog', db_visit_modules=('.somewhere.else', ))`
* e.g. ``__component__ = KegComponent('blog', db_visit_modules=('.somewhere.else', ))``

20 changes: 10 additions & 10 deletions docs/source/readme/configuration.rst
Expand Up @@ -25,21 +25,21 @@ Configuration Variables
CLI Command
-----------

The command `<myapp> develop config` will give detailed information about the files and objects
The command ``<myapp> develop config`` will give detailed information about the files and objects
being used to configure an application.

Profile Priority
----------------

All configuration classes with the name `DefaultProfile` will be applied to the app's config
All configuration classes with the name ``DefaultProfile`` will be applied to the app's config
first.

Then, the configuration classes that match the "selected" profile will be applied on top of the
app's existing configuration. This makes the settings from the "selected" profile override any
settings from the `DefaultProfile.`
settings from the ``DefaultProfile``.

Practically speaking, any configuration that applies to the entire app regardless of what context
it is being used in will generally go in `myapp.config` in the `DefaultProfile` class.
it is being used in will generally go in ``myapp.config`` in the ``DefaultProfile`` class.

Selecting a Configuration Profile
---------------------------------
Expand All @@ -49,17 +49,17 @@ look for. It should be a string.

A Keg app considers the "selected" profile as follows:

* If `config_profile` was passed into `myapp.init()` as an argument, use it as the
selected profile. The `--profile` cli option uses this method to set the selected profile and
* If ``config_profile`` was passed into ``myapp.init()`` as an argument, use it as the
selected profile. The ``--profile`` cli option uses this method to set the selected profile and
therefore has the highest priority.
* Look in the app's environment namespace for "CONFIG_PROFILE". If found, use it.
* If running tests, use "TestProfile". Whether or not the app is operating in this mode is
controlled by the use of:

- `myapp.init(use_test_profile=True)` which is used by `MyApp.testing_prep()`
- ``myapp.init(use_test_profile=True)`` which is used by ``MyApp.testing_prep()``
- looking in the app's environment namespace for "USE_TEST_PROFILE" which is used by
`keg.testing.invoke_command()`
``keg.testing.invoke_command()``

* Look in the app's main config file (`app.config`) and all it's other
config files for the variable `DEFAULT_PROFILE`. If found, use the value from the file with
* Look in the app's main config file (``app.config``) and all it's other
config files for the variable ``DEFAULT_PROFILE``. If found, use the value from the file with
highest priority.
66 changes: 33 additions & 33 deletions docs/source/readme/views.rst
Expand Up @@ -3,16 +3,16 @@ Views

While generic Flask views will certainly work in this framework, Keg provides a BaseView that
applies a certain amount of magic around route and blueprint setup. BaseView is based on Flask's
MethodView. Best practice is to set up a blueprint and attach the views to it via the `blueprint`
MethodView. Best practice is to set up a blueprint and attach the views to it via the ``blueprint``
attribute. Be aware, BaseView will set up some route, endpoint, and template location defaults,
but these can be configured if needed.

Blueprint Setup
---------------

Adding views to a blueprint is accomplished via the `blueprint` attribute on the view. Note,
Adding views to a blueprint is accomplished via the ``blueprint`` attribute on the view. Note,
BaseView magic kicks in when the class is created, so assigning the blueprint later on will not
currently have the desired effect.
currently have the desired effect::

import flask
from keg.web import BaseView
Expand All @@ -26,7 +26,7 @@ currently have the desired effect.
def get(self):
return 'method get'

Once the blueprint is created, you must attach it to the app via the `use_blueprints` app attribute:
Once the blueprint is created, you must attach it to the app via the ``use_blueprints`` app attribute::

from keg.app import Keg
from my_app.views import blueprint
Expand All @@ -37,7 +37,7 @@ Once the blueprint is created, you must attach it to the app via the `use_bluepr
use_blueprints = (blueprint, )

Blueprints take some parameters for URL prefix and template path. BaseView will respect these when
generating URLs and finding templates:
generating URLs and finding templates::

blueprint = flask.Blueprint(
'custom',
Expand All @@ -60,13 +60,13 @@ Template Discovery
To avoid requiring the developer to configure all the things, BaseView will attempt to discover the
correct template for a view, based on the view class name. Generally, this is a camel-case to
underscore-notation conversion. Blueprint name is included in the path, unless the blueprint has
its own `template_path` defined.
its own ``template_path`` defined.

* `class MyBestView` in blueprint named "public" -> `<app>/templates/public/my_best_view.html`
* `class View2` in blueprint named "other" with template path "foo" -> `<app>/foo/view2.html`
* ``class MyBestView`` in blueprint named "public" -> ``<app>/templates/public/my_best_view.html``
* ``class View2`` in blueprint named "other" with template path "foo" -> ``<app>/foo/view2.html``

A view may be given a `template_name` attribute to override the default filename, although the same
path is used for discovery:
A view may be given a ``template_name`` attribute to override the default filename, although the same
path is used for discovery::

class TemplateOverride(BaseView):
blueprint = blueprint
Expand All @@ -78,7 +78,7 @@ path is used for discovery:
URL and Endpoint Calculation
----------------------------

BaseView has `calc_url` and `calc_endpoint` class methods which will allow the developer to avoid
BaseView has ``calc_url`` and ``calc_endpoint`` class methods which will allow the developer to avoid
hard-coding those types of values throughout the code. These methods will both produce the full
URL/endpoint, including the blueprint prefix (if any).

Expand All @@ -88,13 +88,13 @@ Route Generation
BaseView will, by default, create rules for views on their respective blueprints. Generally, this
is based on the view class name as a camel-case to dash-notation conversion:

* `class MyBestView` in blueprint named "public": `/my-best-view` -> `public.my-best-view`
* `class View2` in blueprint named "other" with URL prefix "foo": `/foo/view2` -> `other.view2`
* ``class MyBestView`` in blueprint named "public": ``/my-best-view`` -> ``public.my-best-view``
* ``class View2`` in blueprint named "other" with URL prefix "foo": ``/foo/view2`` -> ``other.view2``

Note that BaseView is a MethodView implementation, so methods named `get`, `post`, etc. will be
Note that BaseView is a MethodView implementation, so methods named ``get``, ``post``, etc. will be
respected as the appropriate targets in the request/response cycle.

A view may be given a `url` attribute to override the default:
A view may be given a ``url`` attribute to override the default::

class RouteOverride(BaseView):
blueprint = blueprint
Expand All @@ -103,82 +103,82 @@ A view may be given a `url` attribute to override the default:
def get(self):
return self.render()

See `keg_apps/web/views/routing.py` for other routing possibilities that BaseView supports.
See ``keg_apps/web/views/routing.py`` for other routing possibilities that BaseView supports.

Class View Lifecycle
--------------
--------------------

Keg views use Flask's `dispatch_request` to call several methods walking a view through its
Keg views use Flask's ``dispatch_request`` to call several methods walking a view through its
response cycle. As the methods progress, assumptions may be built for access, availability,
etc. Many of these methods will not normally be present on a view.

The view lifecycle is as follows:

* `process_calling_args`
* ``process_calling_args``

* Gather arguments from the route definition and the query string
* If `expected_qs_args` is set on the view, look for these arguments in the query string
* If ``expected_qs_args`` is set on the view, look for these arguments in the query string
* URL arguments from the route definition have precedence over GET args in the query string
* Arguments are processed once, then stored on the view

* `pre_auth`
* ``pre_auth``

* Meant for actions that should take place before a user/session has been verified
* Assumptions: calling args

* `check_auth`
* ``check_auth``

* Meant to verify the user/session has access to this resource
* Failure at this point should take appropriate action in the method itself (403, 401, etc.)
* Extensions such as keg-auth leverage this method to insert permission-based authorization into the view cycle
* Assumptions: calling args

* `pre_loaders`
* ``pre_loaders``

* Authentication/authorization has passed, but we haven't loaded any related view dependencies
* Assumptions: calling args, auth

* Loader methods

* Any method on the view ending with `_loader` is called with args
* Any method on the view ending with ``_loader`` is called with args
* Return value of the method is stored with the calling args, keyed by the method name

* e.g. a method named `record_loader` will set a value in calling args for `record`
* e.g. a method named ``record_loader`` will set a value in calling args for ``record``

* Methods folliwng this in the lifecycle can use the newly-set arg
* If no value is returned, Keg assumes a required dependency could not be loaded and returns a 404 response
* Order of execution of a view's loaders may not be assumed
* Assumptions: calling args, auth

* `pre_method`
* ``pre_method``

* Ideal method for running code shared by all response methods (e.g. `get`, `post`, etc.)
* Ideal method for running code shared by all response methods (e.g. ``get``, ``post``, etc.)
* Assumptions: calling args, auth, loader args

* Responding method

* The method used here is generally the lowercase of the request method (e.g. `get`, `post`, etc.)
* If the request method is HEAD, but there is no `head` method, Keg looks for `get` instead
* The method used here is generally the lowercase of the request method (e.g. ``get``, ``post``, etc.)
* If the request method is HEAD, but there is no ``head`` method, Keg looks for ``get`` instead
* This method may return the view's response
* Assumptions: calling args, auth, loader args

* If responding method does not return a reponse:

* I.e. the responding method returned something falsy that isn't an empty string
* `pre_render`
* ``pre_render``

* Assumptions: calling args, auth, loader args

* `render`
* ``render``

* Returns a response object
* By default, renders the template with args assigned on the view
* See Template Discovery above

* `pre_response`
* ``pre_response``

* A response has been generated, but has not been sent yet
* The response is included as the `_response` arg for this method
* The response is included as the ``_response`` arg for this method
* The response should not be assumed to be mutable
* If a different response should be sent, return that response from this method
* Assumptions: calling args, auth, loader args, response (from responding method or render)
25 changes: 17 additions & 8 deletions docs/source/signals.rst
Expand Up @@ -19,35 +19,44 @@ below for when the signals are fired, and what can be counted upon to be availab
Keg Events
----------

* `init_complete`
* ``init_complete``

- All of the app's init tasks have run
- App's `on_init_complete` method has run
- App's ``on_init_complete`` method has run
- At this point in the process, it should be safe to assume all app-related objects are present

* `config_complete`
* ``config_complete``

- App config has been loaded
- Config is the first property of the app to be initialized. `app.config` will be available,
but do not count on anything else.

* `db_before_import`
* ``db_before_import``

- Database options have been configured, and the app is about to visit modules containing
entities
- Config, logging, and error handling have been loaded, but no other extensions
- Config, logging, and error handling have been loaded, but no other extensions, and
the app's ``visit_modules`` has not yet been processed
- Some SQLAlchemy metadata attributes, such as naming convention, need to be set prior to
entities loading. Attaching a method on this signal is an ideal way to set these properties.
- A common practice with signals is to attach handlers in a separate module, and then list that
module in the app's visit_modules. This works with many signals, however, the database layer
gets set up very early in app init to make it available in other steps of the init process.

- As a result, ``db_before_import`` happens long before visit_modules is processed.
- Instead, use ``db_before_import`` somewhere that gets loaded at import time for the app (e.g.
in the module containing the app itself, or something it imports).

- If customization of the db object, metadata, engine options, etc. is needed, ensure that
no modules containing entities are imported before the connected callback runs.

* `testing_run_start`
* ``testing_run_start``

- `app.testing_prep` has set up necessary context and is about to return the test app
- ``app.testing_prep`` has set up necessary context and is about to return the test app
- Not run during normal operation
- Provides a hook to inject necessary test objects

* `db_clear_pre`, `db_clear_post`, `db_init_pre`, `db_init_post`
* ``db_clear_pre``, ``db_clear_post``, ``db_init_pre``, ``db_init_post``

- Called during the database initialization process, which occurs in test setup and from CLI
commands
1 change: 0 additions & 1 deletion docs/source/utils.rst
Expand Up @@ -10,5 +10,4 @@ Utils
.. autofunction:: keg.utils.app_environ_get
.. autofunction:: keg.utils.ensure_dirs
.. autofunction:: keg.utils.pymodule_fpaths_to_objects
.. autofunction:: keg.utils.reraise_lastexc
.. autofunction:: keg.utils.visit_modules

0 comments on commit 4337461

Please sign in to comment.