| |
@@ -1,3 +1,42 @@
|
| |
+ # -*- coding: utf-8 -*-
|
| |
+ # This file is part of Fedora Hubs.
|
| |
+ #
|
| |
+ # Copyright (C) 2015 Dhriti Shikhar
|
| |
+ # Copyright (C) 2015 Ralph Bean
|
| |
+ # Copyright (C) 2015 Remy DeCausemaker
|
| |
+ # Copyright (C) 2016 Pierre-Yves Chibon
|
| |
+ # Copyright (C) 2017 Jeremy Cline
|
| |
+ #
|
| |
+ # This program is free software: you can redistribute it and/or modify
|
| |
+ # it under the terms of the GNU Affero General Public License as published by
|
| |
+ # the Free Software Foundation, either version 3 of the License, or
|
| |
+ # (at your option) any later version.
|
| |
+ #
|
| |
+ # This program 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 Affero General Public License for more details.
|
| |
+ #
|
| |
+ # You should have received a copy of the GNU Affero General Public License
|
| |
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
|
| |
+ """
|
| |
+ Widgets are the reusable building blocks of Fedora Hubs.
|
| |
+
|
| |
+ To implement a new widget, add a new module in :mod:`hubs.widgets` package that
|
| |
+ implements the interface defined in :class:`hubs.widgets.Widget`. Then, add
|
| |
+ that module to the :data:`hubs.widgets.registry` dictionary.
|
| |
+
|
| |
+ If you would like it to show up in a user hub by default, add it to
|
| |
+ :mod:`hubs.defaults`.
|
| |
+
|
| |
+ If you wish to add it to a group page in the development environment, add your
|
| |
+ new widget to the ``populate.py`` script in the repository root. Next destroy
|
| |
+ your database, rebuild it, and re-run the app. Your widget should show up.
|
| |
+
|
| |
+ Attributes:
|
| |
+ registry (dict): This dictionary is a registry of available widgets in
|
| |
+ Fedora Hubs.
|
| |
+ """
|
| |
from __future__ import unicode_literals
|
| |
|
| |
from hubs.widgets import dummy
|
| |
@@ -120,7 +159,14 @@
|
| |
def render(module, session, widget, *args, **kwargs):
|
| |
""" Main API entry point.
|
| |
|
| |
- Call this to render a widget into HTML
|
| |
+ Call this to render a widget into HTML.
|
| |
+
|
| |
+ Args:
|
| |
+ module: An object that implements the Widget interface
|
| |
+ session (sqlalchemy.orm.scoping.scoped_session): An SQLAlchemy session
|
| |
+ to use when querying the database.
|
| |
+ widget (hubs.models.Widget): The Widget instance to render
|
| |
+
|
| |
"""
|
| |
# The API returns exactly this data. Shared cache
|
| |
data = module.data(session, widget, *args, **kwargs)
|
| |
@@ -130,3 +176,76 @@
|
| |
|
| |
# Use the API data to fill out a template, and potentially decorate it.
|
| |
return module.render(**data)
|
| |
+
|
| |
+
|
| |
+ class Widget(object):
|
| |
+ """
|
| |
+ This is the widget interface all widgets must implement.
|
| |
+
|
| |
+ In order to implement a new widget in Fedora Hubs, you should subclass this
|
| |
+ class, implement its methods, and set the required class attributes. You
|
| |
+ must then register the :class:`Widget` in the Widget registry. The widget
|
| |
+ registry is a simple dictionary found at :func:`hubs.widgets.registry`.
|
| |
+
|
| |
+ Attributes:
|
| |
+ template (jinja2.Template): The `Jinja2 template`_ for this widget.
|
| |
+ position (str): The position of the widget in the rendered page. It
|
| |
+ should be one of the following values: 'left', 'right', or 'both'.
|
| |
+ render (callable): This attribute is set to ``template.render``
|
| |
+ automatically and should be a callable that returns some HTML for
|
| |
+ this widget.
|
| |
+
|
| |
+ .. _Jinja2 template:
|
| |
+ http://jinja.pocoo.org/docs/latest/api/#jinja2.Template
|
| |
+ """
|
| |
+
|
| |
+ template = None
|
| |
+ position = None
|
| |
+ render = None if template is None else template.render
|
| |
+
|
| |
+ @staticmethod
|
| |
+ def data(session, widget, *args, **kwargs):
|
| |
+ """
|
| |
+ This generates the data used to render a widget.
|
| |
+
|
| |
+ The result of this function is cached and this function will not be
|
| |
+ called again until the :meth:`Widget.should_invalidate` returns
|
| |
+ `True`.
|
| |
+
|
| |
+ Args:
|
| |
+ module: An object that implements the Widget interface
|
| |
+ session (sqlalchemy.orm.scoping.scoped_session): An SQLAlchemy
|
| |
+ session to use when querying the database.
|
| |
+ widget (hubs.models.Widget): The Widget instance to render
|
| |
+
|
| |
+ Returns:
|
| |
+ dict: A JSON-serializable dictionary of data.
|
| |
+
|
| |
+ This data will be cached.
|
| |
+ """
|
| |
+ raise NotImplementedError()
|
| |
+
|
| |
+ @staticmethod
|
| |
+ def should_invalidate(message, session, widget):
|
| |
+ """
|
| |
+ A function that is used to determine when to invalidate the widget's
|
| |
+ cache.
|
| |
+
|
| |
+ This function will be called when a fedmsg arrives. The widget should
|
| |
+ use this to decide whether it needs to regenerate its data.
|
| |
+
|
| |
+ Args:
|
| |
+ message (dict): The fedmsg that triggered this function call.
|
| |
+ session (sqlalchemy.orm.scoping.scoped_session): An SQLAlchemy
|
| |
+ session to use when querying the database.
|
| |
+ widget (hubs.models.Widget): The Widget instance to render
|
| |
+
|
| |
+ Returns:
|
| |
+ bool: True if the cache needs to be rebuilt, False otherwise
|
| |
+
|
| |
+ A widget should return `False` if the message received would not
|
| |
+ change the result of a call it the :meth:`Widget.data` function.
|
| |
+ When it returns `True`, this widget's :meth:`Widget.data` function
|
| |
+ will be called to refresh the cache.
|
| |
+ """
|
| |
+ raise NotImplementedError()
|
| |
Widgets in Hubs have an interface they are expected to implement. This
formally defines and documents this interface as Sphinx docblocks in
code. The docblocks are then rendered to HTML using the Sphinx autodoc
plugin as part of a new API section of the documentation project. This
replaces the separate API documentation for widgets originally found in
the dev guide. This has the advantage of creating a single source of
truth for API documentation.
This commit does introduce a new
Widget
class to define theinterface. It should function in the same way as the widgets currently
defined at the module level and truthfully only serves to bundle the
interface into one neat object within the module.
I'm interested to hear what people think about implementing widgets as modules
or as classes. They're pretty much equivalent and both approaches should work,
but we should pick one way or the other and document it. If we want to use the
module approach, I can probably move the docblocks around and fiddle with sphinx
a bit to make the documentation still work and make sense.
To get a good idea of what this looks like, just build the docs with
make html
and thennavigate to the
api.html
page.Signed-off-by: Jeremy Cline jeremy@jcline.org