score.webassets¶
The aim of this module is to provide an infrastructure for handling assets in a web project. An asset is a supplementary resource required by the web application, like a javascript file, or css definitions.
Quickstart¶
Configure a folder, where this module should store all created bundles and the provide a list of modules, to provide as web assets:
[score.init]
modules =
score.ctx
score.http
score.css
score.js
score.webassets
[webassets]
rootdir = ${here}/_bundles
modules =
css
js
You can now include your css and javascript assets in your html templates. The next fragment is in jinja2 format:
<html>
<head>
<style>
{# Will render the content of the given css file #}
{{ webassets_content('css', 'above-the-fold.css') }}
</style>
{# Will load *all* css files with a <link> tag #}
{{ webassets_link('css') }}
{# Will load a bundle consiting of two javascript files #}
{{ webassets_link('js', 'file1.js', 'file2.js') }}
</head>
...
Configuration¶
-
score.webassets.
init
(confdict, http=None, tpl=None)[source]¶ Initializes this module acoording to our module initialization guidelines with the following configuration keys:
- rootdir None
- The folder where this module will store the bundled assets.
- modules []
- A list of configured score modules to retrieve proxy objects from. The
configured modules listed here must all expose a
score_webassets_proxy()
method, that returns aWebassetsProxy
object. - freeze False
Option for speeding up asset hash calculations.
See Asset Freezing for valid values.
- tpl.autobundle False
- Whether the webassets_* functions registered with
score.tpl
should provide bundles instead of separate files. This should be set to True on deployment systems to speed up web page rendering.
Details¶
Asset Versioning¶
Most of a web applications assets rarely change. At least they stay exactly the same for long periods of time. A common technique for helping browsers cache these assets more efficiently is the exposure of versioning information of assets within their URLs.
Let us assume we have a css asset called colors.css
, which has the
following content at its first deployment:
h1.article-heading {
background-color: #E3D9C6;
}
The URL of this resource might be http://example.com/css/colors.css. If we would use just this URL, the browser would need to check back every once in a while to see if this resource has changed. It would thus request the resource much more often than necessary.
If we instead add a “version string” to the URL, we can tell the browser to cache this resource forever. When the resource changes, we change the URL and point to the new URL in our HTML.
The initial “version” of the resource might now have the URL http://example.com/css/colors.css?version=1. When a browser requests this URL, we send all required HTTP headers that tell the browser that the resource found in this URL will never change.
If we add a definition to our css asset at a later point …
h1.article-heading {
background-color: #E3D9C6;
}
h2.article-heading {
background-color: #DDC49A;
}
… we immediately change the URL of that resource to http://example.com/css/colors.css?version=2. The browser sees a new URL and assumes that it must be a different asset (which it technically is) and requests its contents. We, again, tell it to keep this file forever and to never ask the web server again for this exact URL.
This feature is automatically enabled, although it does not use incremental values as “version strings”, as in the examples above. Instead, it operates using hashes of the asset contents. That’s why they are referred to as asset hashes throughout the documentation.
Asset Freezing¶
Normally, the webassets module will determine the asset hash based on the asset’s content. Unfortunately, this method is very slow: whenever we need the hash of an asset, we will need to render the content of the asset.
To avoid such expensive operations during deployment, the module has two
different modes for freezing the asset hashes. You can configure
score.webassets to remember the hash of each asset by passing True
in the
module’s freeze
configuration. This ensures that hashes are calculated at
most once.
[webassets]
freeze = True
If you have a deployment script, it is even better to pre-calculate the hash and to provide that value in the module configuration:
$ score webassets freeze
b18ed2b601ab3850
[webassets]
freeze = b18ed2b601ab3850
Proxy¶
Note
The intended audience of this section is module developers. This is the reason the next few suggestions may seem a bit too abstract for day to day usage.
Every module that wants to provide web assets through this module must
return a sub-class of WebassetsProxy
from the configured module’s
score_webassets_proxy()
function.
As an example, we will assume that we want to build a module called “myobjects”, that can grant access to javascript objects stored in JSON files. We will be accessing the objects one by one, but may occasionally need to retrieve multiple objects at once.
To provide these objects as web assets, we need to create a proxy object. We
will use the simpler TemplateWebassetsProxy
to keep the example code
short:
from score.webassets import TemplateWebassetsProxy
class MyobjectsWebassets(TemplateWebassetsProxy):
def __init__(self, tpl):
super().__init__(tpl, 'application/json')
def render_url(self, url):
return '''
<script>
(function() {
var url = JSON.decode(%s);
fetch(url).then(function(response) {
return response.json();
}).then(function(result) {
myGlobalObjectStorage.add(result);
});
})();
</script>
''' % (json.dumps(url),)
def create_bundle(self, paths):
return ''.join(map(self.render, paths))
Your configured module must now return an instance of this class in its
score_webassets_proxy()
method:
from score.init import ConfiguredModule
class ConfiguredMyobjectsModule(ConfiguredModule)
def __init__(self, tpl):
self.tpl = tpl # a score.tpl dependency
super().__init__('myobjects')
def score_webassets_proxy(self):
return MyobjectsWebassets(self.tpl)
After configuring score.webassets to include this module, you can make use of your new module inside html templates. The next fragment is in jinja2 format:
<html>
<head>
<script>
// some code initializing myGlobalObjectStorage
</script>
{# lazy-loading all assets #}
{{ webassets_link('myobjects') }}
{# embedding asset content directly #}
<script>
(function() {
myGlobalObjectStorage.add(JSON.decode(
{{ webassets_content('myobjects') }}
));
})();
</script>
</head>
...
API¶
Configuration¶
-
score.webassets.
init
(confdict, http=None, tpl=None)[source] Initializes this module acoording to our module initialization guidelines with the following configuration keys:
- rootdir None
- The folder where this module will store the bundled assets.
- modules []
- A list of configured score modules to retrieve proxy objects from. The
configured modules listed here must all expose a
score_webassets_proxy()
method, that returns aWebassetsProxy
object. - freeze False
Option for speeding up asset hash calculations.
See Asset Freezing for valid values.
- tpl.autobundle False
- Whether the webassets_* functions registered with
score.tpl
should provide bundles instead of separate files. This should be set to True on deployment systems to speed up web page rendering.
-
class
score.webassets.
ConfiguredWebassetsModule
[source]¶ This module’s
configuration class
.-
get_asset_content
(module, path)[source]¶ Returns the content of the asset identified my its module and path.
-
get_asset_mimetype
(module, path)[source]¶ Returns the mime type of the asset identified my its module and path.
-
get_asset_hash
(module, path)[source]¶ Provides the hash of the asset identified my its module and path.
-
get_asset_url
(module, path)[source]¶ Returns the relative URL to the asset identified by its module and path, that this module can resolve via
get_request_response()
.You won’t need this function, if you’re using
score.http
. But if your means of deployment is different, you will want to create URLs to your assets using this function. It will look something like this:/css/reset.css?_v=0b2931cc6255c72e
This should be rewritten to something you can detect in your application:
/_score_webassets/css/reset.css?_v=0b2931cc6255c72e
Whenever a URL starting with your custom prefix is requested, you can pass the modified
Request
with the original URL toget_request_response()
:response = webassets.get_request_response(Request( '/css/reset.css', {'_v': '0b2931cc6255c72e'}, {'Accept-Encoding': 'gzip,deflate', 'Referer': ... } ))
-
get_bundle_name
(module, paths=None)[source]¶ Provides a unique name for a bundle consisting of assets found in given module and given paths. Will use the module’s
default paths
, if the latter is omitted.This feature is used internally for storing different bundles inside the same folder, for example.
-
get_bundle_hash
(module, paths=None)[source]¶ Provides the hash of a bundle consisting of assets found in given module and given paths. Will use the module’s
default paths
, if the latter is omitted.
-
get_bundle_content
(module, paths=None)[source]¶ Returns the content of requested bundle. The module name is required and will create a bundle with module’s
default paths
. It is also possible to create a bundle with a specific list of asset paths.
-
get_bundle_url
(module, paths=None)[source]¶ Returns the relative URL to given bundle, that this module can resolve via
get_request_response()
. The module name is required and will create a bundle with module’sdefault paths
. It is also possible to create a bundle with a specific list of asset paths.See
get_asset_url()
for example usage.
-
get_request_response
(request)[source]¶ Provides the most efficient response to an HTTP
Request
to obtain an asset. The return value is either a single int, denoting an HTTP status code (like 404 or 304), or a 2-tuple(headers, body)
. The headers list in the latter case is a dict mapping header names to their values, whereas the body is just a string. Note that none of the return values are formatted in any way. They will need to be properly encoded (which should happen automatically in most frameworks).
-
Helpers¶
-
class
score.webassets.
Request
¶ A
collections.namedtuple()
describing the parts of HTTP request, that are required forConfiguredWebassetsModule.get_request_response()
to work. It consists of these 3 values:- path: The path section of the requested URL.
- GET: Parsed dict of the query part.
- headers: Another dict containing all headers, the client provided.
-
class
score.webassets.
WebassetsProxy
[source]¶ A proxy object defining the behaviour of a type of web asset.
-
iter_default_paths
()[source]¶ Provide a generator iterating over the paths, that should be used if no explicit path list was given.
-
hash
(path)[source]¶ Returns a hash for path, that will change whenever the rendered content of the asset changes.
-
render_url
(url)[source]¶ Returns the string to embed in an HTML document to load given url. This might be a <link> tag for css assets, or a <script> tag for javascript assets.
-
-
class
score.webassets.
TemplateWebassetsProxy
(tpl, mimetype)[source]¶ A type of
WebassetsProxy
that treats templates like assets. It accepts a configuredscore.tpl
module and a mime type string and will provide almost all templates, that the tpl module knows of, as assets. If the tpl module knows of css files, for example, this class can be used to provide these css files as assets.The default path list–as returned by
iter_default_paths
–will omit all files starting with underscore and all files inside folders that start with an underscore. Assuming the tpl module lists the following template paths …banana.css _orange.css fresh/ banana.css _apple.css _old/ pear.css passion-fruit.css
… only
banana.css
andfresh/banana.css
will be returned byiter_default_paths
.