API Documentation¶
Element creation¶
In order to instruct the render engine how to render various components, we
create elements and tie various resources to these elements via includes. The
central component to this process is the Element
class.
-
class
render_engine.elemtools.
Element
(layout, config, parent=None, uuid=None)¶ The main base class that all render engine elements inherit from. A typical element may be defined like so:
from ...elemtools import Element, js, css class Accordion(Element): includes = [ css('jquery-ui'), js.inline('accordion') ]
Some elements require fetching additional information from either the the database or some other source. The staff listing element would be a good example of this. In such scenarios, the
Element.before_render
method can be used to augment theElement.data
dictionary like so:from ...elemtools import Element from models import Staff class StaffListing(Element): def before_render(self): # Fetch the staff id's from the element's config ids = self.config.get('staffIds', []) # Load the staff records from the database and place them in a place # that the element's template file can read them from self.data['staff_members'] = Staff.query.filter( Staff.id.in_(ids) )
To complete our staff listing example, the template may look something like:
<div {{ element|get_selectors }}> <div id="staff-listing-{{ element.uuid }}"> {% for i in staff_members %} <div class="staff-listing-member"> <div class="staff-listing-member-name"> {{ i.first_name }} {{ i.last_name }} </div> </div> {% endfor %} </div> </div>
-
config
¶ The element’s config dictionary (this is built by the layoutbuilder).
-
parent
¶ If the element is a child of another element, the reference to the parent is stored here.
-
uuid
¶ A
uuid.uuid4
object used to uniquely identify one element from another at render time.
-
data
¶ A dictionary to be passed off to the template engine (Jinja2) at render time.
-
includes
¶ A list of non-standard included assets that the element needs to render correctly. This list should always include objects generated by functions
js
,css
, and friends. For example:includes = [ js('angular'), css('jquery-ui') ]
-
dynamic
¶ A simple boolean value that tells the render engine whether this element may contain dynamic content or not.
-
children
¶ The element’s child elements, if any.
-
before_render
()¶ A hook that can be implemented for modifying the element’s data dictionary attribute, which may be then used by the template, etc. This is the primary method by which an element can request additional information from the database.
-
iter_all_children
(bypass_disabled=False)¶ Returns an iterator that iterates over all of the element’s children. This is a recursive function: it will return grand children, great grandchildren, etc.
-
module_name
¶ The module name that the element is defined in.
-
path
¶ The path to the element.
-
render
()¶ Renders the element’s template. Keyword arguments will be passed through to the template engine.
-
render_children
()¶ A helper method that renders the element’s immediate children.
-
template_file
¶ The path to the element’s template file.
-
Including resources¶
Each Element
has an includes
list which is used to instruct the
render engine how to fetch additional resources needed for rendering (e.g.
CSS files and JavaScript files that aren’t part of the global site
resources).
-
render_engine.elemtools.
js
(name, footer=False)¶ Specifies a JavaScript file as a dependency (include) for an element. See
Element
for examples on usage.Parameters: -
inline
(name, footer=False)¶ This is a lot like
js
, except it pulls the resource into the body of the rendered template, wrapped in <script></script>. For convenience, this function is aliased tojs.inline
. E.g.:from ...elemtools import Element, js class SomeThing(Element): includes = [ js.inline('durp') ]
It should be noted that this function looks for a resource of extension
.js
(make sure you don’t add that to the name parameter). It looks for this file in the parent element’s package directory. So, for example, let’s say the above example lives atsrc/render_engine/elements/something/__init__.py
, thejs.inline('durp')
call will expect to find the JavaScript file located atsrc/render_engine/elements/something/durp.js
.Parameters:
-
Registry components¶
These are classes that are instantiated by the render engine (once each). You
can find these instantiated at render_engine.elemtools.element_registry
and render_engine.elemtools.include_registry
.
-
class
render_engine.elemtools.
Registry
¶ -
register
(key, value)¶ Add an item to the registry
Parameters: - key – The key to set the object to.
- value – The object to save in the registry.
-
-
class
render_engine.elemtools.
ElementRegistry
¶ -
create_element_from_config
(layout, config, parent=None)¶ Instantiates an
Element
object based on the config passed to it.Parameters: - config (dict) – The configuration dictionary to be passed to the element.
- parent (render_engine.elemtools.Element) – The parent element (if any) to pass.
Returns: A new element.
Return type:
-
-
render_engine.elemtools.
element_registry
¶ An instance of
ElementRegistry
. This is where you should register all of your element definitions.
Bringing everything together¶
All of the necessary components for a layout are brought together into one
place via Layout
. Generally, these objects are instantiated for you via
the models.Menu.layout
property.
-
class
render_engine.elemtools.
Layout
(element_registry, include_registry, config=None, elements=None, pre_main_elements=None, post_main_elements=None, template_variables=None)¶ The central object that stores all of the information about a given layout.
-
get_all_includes
()¶ Gets all of the includes arrays from all elements in the layout, combines them together into a single array, removes duplicates, and finally sorts them.
-
get_dynamic_elements
()¶ Returns all elements which have been flagged as having dynamic content.
-
iter_all_elements
(bypass_disabled=False)¶ Returns an iterator of all elements in the layout. By “all elements”, I mean literally all of them: this is a recursive function that pulls in all of the element’s children, etc. as well.
-
render
()¶ Renders and returns the main content area of the layout.
-
render_post_main
()¶ Renders and returns the “post main” content area of the layout.
-
render_pre_main
()¶ Renders and returns the “pre main” content area of the layout.
-
Utilities¶
There are a number of utility functions, decorators, etc. that I have created to help with this whole thing. They’re listed below. :)
-
util.
authenticate
(fn)¶ A simple decorator that checks whether or not the user is logged in or not. If the user isn’t logged in, a 401 is sent. Note that this is intended to be used with the API and not the render engine portion of the app.
For example:
class SomeResource(Resource): @authenticate def get(self): return jsonify({ 'stuff': 'cool' })
-
class
util.
creds
(*allowed_creds)¶ Checks that the user is logged in and has the credentials specified. This, like the
authenticate
decorator, is intended to be used with the API portion of the app. (A JSON 401 will be sent if the request fails.)For example:
class SomeOtherResource(Resource): @creds('admin', 'dev') def get(self): return jsonify({ 'stuff': 'whatever' })
-
render_engine.elemtools.
augment_query
(query, config)¶ Creates a new query based, augmented with configuration data as provided by the output of the resource-list directive. Basically, it takes a query and applies order by, limit, etc. to that query, as specified by the user-configured resource-list element.
For example, consider the following JSON configuration:
{ "query": { "dynamic": true, "filters": { "date": false, "ids": [], "ministryIds": [3, 4] }, "limit": 10, "orderBy": "id" } }
It’s a rather tedious endeavor to apply all of the necessary filtering, ordering, limiting, etc. to the above JSON, so this function does most of it for you. We could, for instance, create a query on the staff table, then pass that query and the “query” property of the JSON configuration above to
augment_query
like so:from models import Staff # We only want staff that are enabled original_query = Staff.query.filter_by(enabled='yes') # Now we can create an augmented version of the query like so: augmented_query = augment_query(original_query, config['query']) # Fetch all results results = augmented_query.all()
Note that
augment_query
does not do any date filtering. If such filtering is required, it needs to be applied to the query that is passed toaugment_query
.Also,
augment_query
does NOT modify the query passed to it, but rather creates a new query based on that original query. (This is typical of all SQL Alchemy query building.)
Template engine filters¶
I’ve created a number of (I think) useful template filters so that we can reduce some of the more obnoxious templating tasks, such as resolving links to menu records, HTML element id and class auto population stuff, etc.
-
render_engine.filters.
get_selectors
(element, *additional_classes)¶ Based upon the element’s config parameters (as specified by the layout builder), outputs id and class attributes for use in HTML tag definitions.
For example, consider the following element config JSON:
{ "type": "text-block", "text": "This is so cool!", "dom": { "identifier": "some-text-block", "classes": "custom-textblock my-textblock" } }
And now consider the following template for the “text-block” type:
<div {{ element|get_selectors }}> ... </div>
This will output the following DOM:
<div id="some-text-block" class="text-block custom-textblock my-textblock"> ... </div>
You’ll notice that the filter also adds the “type” of the element as a class.
-
render_engine.filters.
resolve_link
(link_config)¶ Accepts link configuration generated by the layoutbuilder’s dynamic-link directive and yields a URL.
-
render_engine.filters.
classify
(obj)¶ A simple helper for applying classes conditionally to a DOM element.
For example:
<div class="{{ {'things': True, 'stuff': False, 'whatever': True}|classify }} "> ... </div>
... will return:
<div class="things whatever"> ... </div>
-
render_engine.filters.
get_link
(menu)¶ Given a
Category
model object, outputs a corresponding relative URL.
ORM Models¶
Some of the models have some additional, non-standard behavior. My attempt is to document that behavior here. All of the normal stuff (e.g. column definitions) will be ommitted. Furthermore, ORM classes that don’t have any oddities will be also be ommitted.
-
class
models.
Menu
(**kwargs)¶ -
layout
¶ Returns a
render_engine.elemtools.Layout
object based on the menu’slayout_config
data (JSON).
-