Compare commits
5 commits
60e8303d29
...
8ae140555a
Author | SHA1 | Date | |
---|---|---|---|
8ae140555a | |||
5197fa9ae9 | |||
1d113da45a | |||
9c5320a31c | |||
ca2e0e88a6 |
|
@ -5,6 +5,14 @@ All notable changes to wuttaweb will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## v0.2.0 (2024-07-14)
|
||||||
|
|
||||||
|
### Feat
|
||||||
|
|
||||||
|
- add basic support for menu handler
|
||||||
|
|
||||||
|
- add "web handler" feature; it must get the menu handler
|
||||||
|
|
||||||
## v0.1.0 (2024-07-12)
|
## v0.1.0 (2024-07-12)
|
||||||
|
|
||||||
### Feat
|
### Feat
|
||||||
|
|
6
docs/api/wuttaweb/handler.rst
Normal file
6
docs/api/wuttaweb/handler.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
``wuttaweb.handler``
|
||||||
|
====================
|
||||||
|
|
||||||
|
.. automodule:: wuttaweb.handler
|
||||||
|
:members:
|
|
@ -8,6 +8,7 @@
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
app
|
app
|
||||||
|
handler
|
||||||
helpers
|
helpers
|
||||||
menus
|
menus
|
||||||
static
|
static
|
||||||
|
|
|
@ -6,6 +6,11 @@ This package provides a "web layer" for custom apps.
|
||||||
|
|
||||||
It uses traditional server-side rendering with VueJS on the front-end.
|
It uses traditional server-side rendering with VueJS on the front-end.
|
||||||
|
|
||||||
|
Good documentation and 100% `test coverage`_ are priorities for this
|
||||||
|
project.
|
||||||
|
|
||||||
|
.. _test coverage: https://buildbot.rattailproject.org/coverage/wuttaweb/
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 3
|
:maxdepth: 3
|
||||||
:caption: Contents:
|
:caption: Contents:
|
||||||
|
|
|
@ -6,7 +6,7 @@ build-backend = "hatchling.build"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "WuttaWeb"
|
name = "WuttaWeb"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
description = "Web App for Wutta Framework"
|
description = "Web App for Wutta Framework"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]
|
authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]
|
||||||
|
@ -35,7 +35,7 @@ dependencies = [
|
||||||
"pyramid_mako",
|
"pyramid_mako",
|
||||||
"waitress",
|
"waitress",
|
||||||
"WebHelpers2",
|
"WebHelpers2",
|
||||||
"WuttJamaican[db]>=0.6.1",
|
"WuttJamaican[db]>=0.7.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,14 +48,14 @@ tests = ["pytest-cov", "tox"]
|
||||||
main = "wuttaweb.app:main"
|
main = "wuttaweb.app:main"
|
||||||
|
|
||||||
|
|
||||||
[project.entry-points."wutta.providers"]
|
[project.entry-points."wutta.app.providers"]
|
||||||
wuttaweb = "wuttaweb.app:WebAppProvider"
|
wuttaweb = "wuttaweb.app:WebAppProvider"
|
||||||
|
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
Homepage = "https://rattailproject.org/"
|
Homepage = "https://wuttaproject.org/"
|
||||||
Repository = "https://kallithea.rattailproject.org/rattail-project/wuttaweb"
|
Repository = "https://forgejo.wuttaproject.org/wutta/wuttaweb"
|
||||||
Changelog = "https://kallithea.rattailproject.org/rattail-project/wuttaweb/files/master/CHANGELOG.md"
|
Changelog = "https://forgejo.wuttaproject.org/wutta/wuttaweb/src/branch/master/CHANGELOG.md"
|
||||||
|
|
||||||
|
|
||||||
[tool.commitizen]
|
[tool.commitizen]
|
||||||
|
|
|
@ -34,28 +34,28 @@ from pyramid.config import Configurator
|
||||||
|
|
||||||
class WebAppProvider(AppProvider):
|
class WebAppProvider(AppProvider):
|
||||||
"""
|
"""
|
||||||
The :term:`app provider<app provider>` for WuttaWeb. This adds
|
The :term:`app provider` for WuttaWeb. This adds some methods
|
||||||
some methods to get web-specific :term:`handlers<handler>`.
|
specific to web apps.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_web_menu_handler(self, **kwargs):
|
def get_web_handler(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Get the configured "menu" handler for the web app.
|
Get the configured "web" handler for the app.
|
||||||
|
|
||||||
Specify a custom handler in your config file like this:
|
Specify a custom handler in your config file like this:
|
||||||
|
|
||||||
.. code-block:: ini
|
.. code-block:: ini
|
||||||
|
|
||||||
[wuttaweb]
|
[wutta]
|
||||||
menus.handler_spec = poser.web.menus:PoserMenuHandler
|
web.handler_spec = poser.web.handler:PoserWebHandler
|
||||||
|
|
||||||
:returns: Instance of :class:`~wuttaweb.menus.MenuHandler`.
|
:returns: Instance of :class:`~wuttaweb.handler.WebHandler`.
|
||||||
"""
|
"""
|
||||||
if 'web_menu_handler' not in self.__dict__:
|
if 'web_handler' not in self.__dict__:
|
||||||
spec = self.config.get('wuttaweb.menus.handler_spec',
|
spec = self.config.get(f'{self.appname}.web.handler_spec',
|
||||||
default='wuttaweb.menus:MenuHandler')
|
default='wuttaweb.handler:WebHandler')
|
||||||
self.web_menu_handler = self.app.load_object(spec)(self.config)
|
self.web_handler = self.app.load_object(spec)(self.config)
|
||||||
return self.web_menu_handler
|
return self.web_handler
|
||||||
|
|
||||||
|
|
||||||
def make_wutta_config(settings):
|
def make_wutta_config(settings):
|
||||||
|
|
57
src/wuttaweb/handler.py
Normal file
57
src/wuttaweb/handler.py
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
# -*- coding: utf-8; -*-
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# wuttaweb -- Web App for Wutta Framework
|
||||||
|
# Copyright © 2024 Lance Edgar
|
||||||
|
#
|
||||||
|
# This file is part of Wutta Framework.
|
||||||
|
#
|
||||||
|
# Wutta Framework is free software: you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU General Public License as published by the Free
|
||||||
|
# Software Foundation, either version 3 of the License, or (at your option) any
|
||||||
|
# later version.
|
||||||
|
#
|
||||||
|
# Wutta Framework is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
# more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along with
|
||||||
|
# Wutta Framework. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
"""
|
||||||
|
Web Handler
|
||||||
|
|
||||||
|
This defines the :term:`handler` for the web layer.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from wuttjamaican.app import GenericHandler
|
||||||
|
|
||||||
|
|
||||||
|
class WebHandler(GenericHandler):
|
||||||
|
"""
|
||||||
|
Base class and default implementation for the "web" :term:`handler`.
|
||||||
|
|
||||||
|
This is responsible for determining the "menu handler" and
|
||||||
|
(eventually) possibly other things.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_menu_handler(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Get the configured "menu" handler for the web app.
|
||||||
|
|
||||||
|
Specify a custom handler in your config file like this:
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
[wutta.web]
|
||||||
|
menus.handler_spec = poser.web.menus:PoserMenuHandler
|
||||||
|
|
||||||
|
:returns: Instance of :class:`~wuttaweb.menus.MenuHandler`.
|
||||||
|
"""
|
||||||
|
if not hasattr(self, 'menu_handler'):
|
||||||
|
spec = self.config.get(f'{self.appname}.web.menus.handler_spec',
|
||||||
|
default='wuttaweb.menus:MenuHandler')
|
||||||
|
self.menu_handler = self.app.load_object(spec)(self.config)
|
||||||
|
return self.menu_handler
|
|
@ -138,7 +138,7 @@ class MenuHandler(GenericHandler):
|
||||||
The web app calls this method but you normally should not need
|
The web app calls this method but you normally should not need
|
||||||
to override it; you can override :meth:`make_menus()` instead.
|
to override it; you can override :meth:`make_menus()` instead.
|
||||||
"""
|
"""
|
||||||
raw_menus = self.make_menus(request, **kwargs)
|
raw_menus = self._make_raw_menus(request, **kwargs)
|
||||||
|
|
||||||
# now we have "simple" (raw) menus definition, but must refine
|
# now we have "simple" (raw) menus definition, but must refine
|
||||||
# that somewhat to produce our final menus
|
# that somewhat to produce our final menus
|
||||||
|
@ -209,6 +209,16 @@ class MenuHandler(GenericHandler):
|
||||||
|
|
||||||
return final_menus
|
return final_menus
|
||||||
|
|
||||||
|
def _make_raw_menus(self, request, **kwargs):
|
||||||
|
"""
|
||||||
|
Construct the initial full set of "raw" menus.
|
||||||
|
|
||||||
|
For now this just calls :meth:`make_menus()` which generally
|
||||||
|
means a "hard-coded" menu set. Eventually it may allow for
|
||||||
|
loading dynamic menus from config instead.
|
||||||
|
"""
|
||||||
|
return self.make_menus(request, **kwargs)
|
||||||
|
|
||||||
def _is_allowed(self, request, item):
|
def _is_allowed(self, request, item):
|
||||||
"""
|
"""
|
||||||
Logic to determine if a given menu item is "allowed" for
|
Logic to determine if a given menu item is "allowed" for
|
||||||
|
|
|
@ -36,12 +36,16 @@ hooks contained here, depending on the circumstance.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
from pyramid import threadlocal
|
from pyramid import threadlocal
|
||||||
|
|
||||||
from wuttaweb import helpers
|
from wuttaweb import helpers
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def new_request(event):
|
def new_request(event):
|
||||||
"""
|
"""
|
||||||
Event hook called when processing a new request.
|
Event hook called when processing a new request.
|
||||||
|
@ -129,6 +133,7 @@ def before_render(event):
|
||||||
request = event.get('request') or threadlocal.get_current_request()
|
request = event.get('request') or threadlocal.get_current_request()
|
||||||
config = request.wutta_config
|
config = request.wutta_config
|
||||||
app = config.get_app()
|
app = config.get_app()
|
||||||
|
web = app.get_web_handler()
|
||||||
|
|
||||||
context = event
|
context = event
|
||||||
context['app'] = app
|
context['app'] = app
|
||||||
|
@ -137,7 +142,7 @@ def before_render(event):
|
||||||
context['url'] = request.route_url
|
context['url'] = request.route_url
|
||||||
context['json'] = json
|
context['json'] = json
|
||||||
|
|
||||||
menus = app.get_web_menu_handler()
|
menus = web.get_menu_handler()
|
||||||
context['menus'] = menus.do_make_menus(request)
|
context['menus'] = menus.do_make_menus(request)
|
||||||
|
|
||||||
|
|
||||||
|
|
2
tasks.py
2
tasks.py
|
@ -15,7 +15,7 @@ def release(c, skip_tests=False):
|
||||||
Release a new version of WuttJamaican
|
Release a new version of WuttJamaican
|
||||||
"""
|
"""
|
||||||
if not skip_tests:
|
if not skip_tests:
|
||||||
c.run('tox')
|
c.run('pytest')
|
||||||
|
|
||||||
# rebuild pkg
|
# rebuild pkg
|
||||||
if os.path.exists('dist'):
|
if os.path.exists('dist'):
|
||||||
|
|
20
tests/test_handler.py
Normal file
20
tests/test_handler.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# -*- coding: utf-8; -*-
|
||||||
|
|
||||||
|
from unittest import TestCase
|
||||||
|
|
||||||
|
from wuttjamaican.conf import WuttaConfig
|
||||||
|
|
||||||
|
from wuttaweb import handler as mod
|
||||||
|
from wuttaweb.menus import MenuHandler
|
||||||
|
|
||||||
|
|
||||||
|
class TestWebHandler(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.config = WuttaConfig()
|
||||||
|
self.app = self.config.get_app()
|
||||||
|
self.handler = mod.WebHandler(self.config)
|
||||||
|
|
||||||
|
def test_menu_handler_default(self):
|
||||||
|
menus = self.handler.get_menu_handler()
|
||||||
|
self.assertIsInstance(menus, MenuHandler)
|
|
@ -159,6 +159,12 @@ class TestMenuHandler(TestCase):
|
||||||
self.assertTrue(entry['is_sep'])
|
self.assertTrue(entry['is_sep'])
|
||||||
self.assertFalse(entry['is_menu'])
|
self.assertFalse(entry['is_menu'])
|
||||||
|
|
||||||
|
def test_make_raw_menus(self):
|
||||||
|
# minimal test to ensure it calls the other method
|
||||||
|
with patch.object(self.handler, 'make_menus') as make_menus:
|
||||||
|
self.handler._make_raw_menus(self.request, foo='bar')
|
||||||
|
make_menus.assert_called_once_with(self.request, foo='bar')
|
||||||
|
|
||||||
def test_do_make_menus_prune_unallowed_item(self):
|
def test_do_make_menus_prune_unallowed_item(self):
|
||||||
test_menus = [
|
test_menus = [
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue