fix: add basic support for wutta-continuum data versioning/history
not much "support" per se in here, mostly some stubs to allow for smooth operation if/when it is installed
This commit is contained in:
parent
7002986cb7
commit
2fa82bee8c
|
@ -35,6 +35,7 @@ intersphinx_mapping = {
|
||||||
'rattail': ('https://rattailproject.org/docs/rattail/', None),
|
'rattail': ('https://rattailproject.org/docs/rattail/', None),
|
||||||
'rattail-manual': ('https://rattailproject.org/docs/rattail-manual/', None),
|
'rattail-manual': ('https://rattailproject.org/docs/rattail-manual/', None),
|
||||||
'sqlalchemy': ('http://docs.sqlalchemy.org/en/latest/', None),
|
'sqlalchemy': ('http://docs.sqlalchemy.org/en/latest/', None),
|
||||||
|
'wutta-continuum': ('https://rattailproject.org/docs/wutta-continuum/', None),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,18 +2,12 @@
|
||||||
WuttJamaican
|
WuttJamaican
|
||||||
============
|
============
|
||||||
|
|
||||||
This package provides a "base layer" for custom apps, regardless of
|
This package aims to provide a "base layer" for apps regardless of
|
||||||
environment/platform:
|
platform or environment (console, web, GUI).
|
||||||
|
|
||||||
* console
|
It comes from patterns developed within the `Rattail Project`_, and
|
||||||
* web
|
roughly corresponds with the "base and data layers" as described in
|
||||||
* GUI
|
:doc:`rattail-manual:index`.
|
||||||
|
|
||||||
It mostly is a distillation of certain patterns developed within the
|
|
||||||
`Rattail Project`_, which are deemed generally useful. (At least,
|
|
||||||
according to the author.) It roughly corresponds to the "base layer"
|
|
||||||
as described in the Rattail Manual (see
|
|
||||||
:doc:`rattail-manual:base/index`).
|
|
||||||
|
|
||||||
.. _Rattail Project: https://rattailproject.org/
|
.. _Rattail Project: https://rattailproject.org/
|
||||||
|
|
||||||
|
@ -22,9 +16,6 @@ project.
|
||||||
|
|
||||||
.. _test coverage: https://buildbot.rattailproject.org/coverage/wuttjamaican/
|
.. _test coverage: https://buildbot.rattailproject.org/coverage/wuttjamaican/
|
||||||
|
|
||||||
Rattail is still the main use case so far, and will be refactored
|
|
||||||
along the way to incorporate what this package has to offer.
|
|
||||||
|
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
|
@ -35,6 +26,11 @@ Features
|
||||||
|
|
||||||
.. _SQLAlchemy: https://www.sqlalchemy.org
|
.. _SQLAlchemy: https://www.sqlalchemy.org
|
||||||
|
|
||||||
|
See also these projects which build on WuttJamaican:
|
||||||
|
|
||||||
|
* :doc:`wutta-continuum:index`
|
||||||
|
* `WuttaWeb <https://rattailproject.org/docs/wuttaweb/>`_
|
||||||
|
|
||||||
|
|
||||||
Contents
|
Contents
|
||||||
--------
|
--------
|
||||||
|
|
|
@ -590,6 +590,21 @@ class AppHandler:
|
||||||
if setting:
|
if setting:
|
||||||
session.delete(setting)
|
session.delete(setting)
|
||||||
|
|
||||||
|
def continuum_is_enabled(self):
|
||||||
|
"""
|
||||||
|
Returns boolean indicating if Wutta-Continuum is installed and
|
||||||
|
enabled.
|
||||||
|
|
||||||
|
Default will be ``False`` as enabling it requires additional
|
||||||
|
installation and setup. For instructions see
|
||||||
|
:doc:`wutta-continuum:narr/install`.
|
||||||
|
"""
|
||||||
|
for provider in self.providers.values():
|
||||||
|
if hasattr(provider, 'continuum_is_enabled'):
|
||||||
|
return provider.continuum_is_enabled()
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
##############################
|
##############################
|
||||||
# getters for other handlers
|
# getters for other handlers
|
||||||
##############################
|
##############################
|
||||||
|
|
|
@ -625,7 +625,7 @@ class WuttaConfig:
|
||||||
|
|
||||||
class WuttaConfigExtension:
|
class WuttaConfigExtension:
|
||||||
"""
|
"""
|
||||||
Base class for all config extensions.
|
Base class for all :term:`config extensions <config extension>`.
|
||||||
"""
|
"""
|
||||||
key = None
|
key = None
|
||||||
|
|
||||||
|
@ -638,6 +638,17 @@ class WuttaConfigExtension:
|
||||||
object in any way necessary.
|
object in any way necessary.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def startup(self, config):
|
||||||
|
"""
|
||||||
|
This method is called after the config object is fully created
|
||||||
|
and all extensions have been applied, i.e. after
|
||||||
|
:meth:`configure()` has been called for each extension.
|
||||||
|
|
||||||
|
At this point the config *settings* for the running app should
|
||||||
|
be settled, and each extension is then allowed to act on those
|
||||||
|
initial settings if needed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def generic_default_files(appname):
|
def generic_default_files(appname):
|
||||||
"""
|
"""
|
||||||
|
@ -962,8 +973,13 @@ def make_config(
|
||||||
# apply all registered extensions
|
# apply all registered extensions
|
||||||
# TODO: maybe let config disable some extensions?
|
# TODO: maybe let config disable some extensions?
|
||||||
extensions = load_entry_points(extension_entry_points)
|
extensions = load_entry_points(extension_entry_points)
|
||||||
for extension in extensions.values():
|
extensions = [ext() for ext in extensions.values()]
|
||||||
|
for extension in extensions:
|
||||||
log.debug("applying config extension: %s", extension.key)
|
log.debug("applying config extension: %s", extension.key)
|
||||||
extension().configure(config)
|
extension.configure(config)
|
||||||
|
|
||||||
|
# let extensions run startup hooks if needed
|
||||||
|
for extension in extensions:
|
||||||
|
extension.startup(config)
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
|
@ -65,6 +65,7 @@ class Role(Base):
|
||||||
See also :attr:`user_refs`.
|
See also :attr:`user_refs`.
|
||||||
"""
|
"""
|
||||||
__tablename__ = 'role'
|
__tablename__ = 'role'
|
||||||
|
__versioned__ = {}
|
||||||
|
|
||||||
uuid = uuid_column()
|
uuid = uuid_column()
|
||||||
|
|
||||||
|
@ -122,6 +123,7 @@ class Permission(Base):
|
||||||
Represents a permission granted to a role.
|
Represents a permission granted to a role.
|
||||||
"""
|
"""
|
||||||
__tablename__ = 'permission'
|
__tablename__ = 'permission'
|
||||||
|
__versioned__ = {}
|
||||||
|
|
||||||
role_uuid = uuid_fk_column('role.uuid', primary_key=True, nullable=False)
|
role_uuid = uuid_fk_column('role.uuid', primary_key=True, nullable=False)
|
||||||
role = orm.relationship(
|
role = orm.relationship(
|
||||||
|
@ -155,6 +157,7 @@ class User(Base):
|
||||||
See also :attr:`role_refs`.
|
See also :attr:`role_refs`.
|
||||||
"""
|
"""
|
||||||
__tablename__ = 'user'
|
__tablename__ = 'user'
|
||||||
|
__versioned__ = {}
|
||||||
|
|
||||||
uuid = uuid_column()
|
uuid = uuid_column()
|
||||||
|
|
||||||
|
@ -217,6 +220,7 @@ class UserRole(Base):
|
||||||
user "belongs" or "is assigned" to the role.
|
user "belongs" or "is assigned" to the role.
|
||||||
"""
|
"""
|
||||||
__tablename__ = 'user_x_role'
|
__tablename__ = 'user_x_role'
|
||||||
|
__versioned__ = {}
|
||||||
|
|
||||||
uuid = uuid_column()
|
uuid = uuid_column()
|
||||||
|
|
||||||
|
|
|
@ -118,6 +118,7 @@ class Person(Base):
|
||||||
Employee relationship etc.
|
Employee relationship etc.
|
||||||
"""
|
"""
|
||||||
__tablename__ = 'person'
|
__tablename__ = 'person'
|
||||||
|
__versioned__ = {}
|
||||||
|
|
||||||
uuid = uuid_column()
|
uuid = uuid_column()
|
||||||
|
|
||||||
|
|
|
@ -200,6 +200,19 @@ class TestAppHandler(FileTestCase):
|
||||||
value = self.app.get_setting(session, 'foo')
|
value = self.app.get_setting(session, 'foo')
|
||||||
self.assertIsNone(value)
|
self.assertIsNone(value)
|
||||||
|
|
||||||
|
def test_continuum_is_enabled(self):
|
||||||
|
|
||||||
|
# false by default
|
||||||
|
with patch.object(self.app, 'providers', new={}):
|
||||||
|
self.assertFalse(self.app.continuum_is_enabled())
|
||||||
|
|
||||||
|
# but "any" provider technically could enable it...
|
||||||
|
class MockProvider:
|
||||||
|
def continuum_is_enabled(self):
|
||||||
|
return True
|
||||||
|
with patch.object(self.app, 'providers', new={'mock': MockProvider()}):
|
||||||
|
self.assertTrue(self.app.continuum_is_enabled())
|
||||||
|
|
||||||
def test_model(self):
|
def test_model(self):
|
||||||
try:
|
try:
|
||||||
from wuttjamaican.db import model
|
from wuttjamaican.db import model
|
||||||
|
|
|
@ -7,6 +7,8 @@ from unittest.mock import patch, MagicMock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from wuttjamaican import conf as mod
|
||||||
|
# TODO: get rid of this eventually
|
||||||
from wuttjamaican import conf
|
from wuttjamaican import conf
|
||||||
from wuttjamaican.exc import ConfigurationError
|
from wuttjamaican.exc import ConfigurationError
|
||||||
from wuttjamaican.app import AppHandler
|
from wuttjamaican.app import AppHandler
|
||||||
|
@ -673,11 +675,11 @@ class TestMakeConfig(FileTestCase):
|
||||||
generic = self.write_file('generic.conf', '')
|
generic = self.write_file('generic.conf', '')
|
||||||
myfile = self.write_file('my.conf', '')
|
myfile = self.write_file('my.conf', '')
|
||||||
|
|
||||||
with patch('wuttjamaican.conf.WuttaConfig') as WuttaConfig:
|
with patch.object(mod, 'WuttaConfig') as WuttaConfig:
|
||||||
with patch('wuttjamaican.conf.load_entry_points') as load_entry_points:
|
with patch.object(mod, 'load_entry_points') as load_entry_points:
|
||||||
|
|
||||||
# no entry points loaded if extend=False
|
# no entry points loaded if extend=False
|
||||||
config = conf.make_config(appname='wuttatest', extend=False)
|
config = mod.make_config(appname='wuttatest', extend=False)
|
||||||
WuttaConfig.assert_called_once_with([], appname='wuttatest',
|
WuttaConfig.assert_called_once_with([], appname='wuttatest',
|
||||||
usedb=None, preferdb=None)
|
usedb=None, preferdb=None)
|
||||||
load_entry_points.assert_not_called()
|
load_entry_points.assert_not_called()
|
||||||
|
@ -685,7 +687,7 @@ class TestMakeConfig(FileTestCase):
|
||||||
# confirm entry points for default appname
|
# confirm entry points for default appname
|
||||||
load_entry_points.reset_mock()
|
load_entry_points.reset_mock()
|
||||||
WuttaConfig.reset_mock()
|
WuttaConfig.reset_mock()
|
||||||
config = conf.make_config([], appname='wutta')
|
config = mod.make_config([], appname='wutta')
|
||||||
WuttaConfig.assert_called_once_with([], appname='wutta',
|
WuttaConfig.assert_called_once_with([], appname='wutta',
|
||||||
usedb=None, preferdb=None)
|
usedb=None, preferdb=None)
|
||||||
load_entry_points.assert_called_once_with('wutta.config.extensions')
|
load_entry_points.assert_called_once_with('wutta.config.extensions')
|
||||||
|
@ -693,7 +695,7 @@ class TestMakeConfig(FileTestCase):
|
||||||
# confirm entry points for custom appname
|
# confirm entry points for custom appname
|
||||||
load_entry_points.reset_mock()
|
load_entry_points.reset_mock()
|
||||||
WuttaConfig.reset_mock()
|
WuttaConfig.reset_mock()
|
||||||
config = conf.make_config(appname='wuttatest')
|
config = mod.make_config(appname='wuttatest')
|
||||||
WuttaConfig.assert_called_once_with([], appname='wuttatest',
|
WuttaConfig.assert_called_once_with([], appname='wuttatest',
|
||||||
usedb=None, preferdb=None)
|
usedb=None, preferdb=None)
|
||||||
load_entry_points.assert_called_once_with('wuttatest.config.extensions')
|
load_entry_points.assert_called_once_with('wuttatest.config.extensions')
|
||||||
|
@ -706,9 +708,10 @@ class TestMakeConfig(FileTestCase):
|
||||||
WuttaConfig.reset_mock()
|
WuttaConfig.reset_mock()
|
||||||
testconfig = MagicMock()
|
testconfig = MagicMock()
|
||||||
WuttaConfig.return_value = testconfig
|
WuttaConfig.return_value = testconfig
|
||||||
config = conf.make_config(appname='wuttatest')
|
config = mod.make_config(appname='wuttatest')
|
||||||
WuttaConfig.assert_called_once_with([], appname='wuttatest',
|
WuttaConfig.assert_called_once_with([], appname='wuttatest',
|
||||||
usedb=None, preferdb=None)
|
usedb=None, preferdb=None)
|
||||||
load_entry_points.assert_called_once_with('wuttatest.config.extensions')
|
load_entry_points.assert_called_once_with('wuttatest.config.extensions')
|
||||||
foo_cls.assert_called_once_with()
|
foo_cls.assert_called_once_with()
|
||||||
foo_obj.configure.assert_called_once_with(testconfig)
|
foo_obj.configure.assert_called_once_with(testconfig)
|
||||||
|
foo_obj.startup.assert_called_once_with(testconfig)
|
||||||
|
|
Loading…
Reference in a new issue