2022-02-20 15:15:38 +01:00
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "utf-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< meta http-equiv = "X-UA-Compatible" content = "ie=edge" >
2022-07-28 05:28:26 +02:00
< link rel = "stylesheet" href = "../../static/styles.css?h=ae0cc228" >
2022-02-20 15:15:38 +01:00
< link rel = "stylesheet" href = "../../static/pygments.css" >
< link rel = "shortcut icon" href = "../../static/favicon.png?h=fa09bedd" >
< title > | Lektor Static Content Management System< / title >
< / head >
< body class = "default" >
< nav class = "navbar navbar-inverse navbar-static-top" >
< div class = "container" >
< div class = "navbar-header" >
< button type = "button" class = "navbar-toggle collapsed"
data-toggle="collapse" data-target="#navbar"
aria-expanded="false" aria-controls="navbar">
< span class = "sr-only" > Toggle navigation< / span >
< span class = "icon-bar" > < / span >
< span class = "icon-bar" > < / span >
< span class = "icon-bar" > < / span >
< / button >
< a class = "navbar-brand" href = "../../" > Lektor< / a >
< / div >
< div id = "navbar" class = "collapse navbar-collapse" >
< ul class = "nav navbar-nav" >
< li > < a href = "../../downloads/" > Download< / a > < / li >
< li > < a href = "../../docs/" > Documentation< / a > < / li >
< li > < a href = "../../showcase/" > Showcase< / a > < / li >
< li class = "active" > < a href = "../" > Plugins< / a > < / li >
< li > < a href = "../../community/" > Community< / a > < / li >
< li > < a href = "../../blog/" > Blog< / a > < / li >
< / ul >
< / div >
< / div >
< / nav >
< div class = "body-wrapper" >
< div class = "page-banner page-banner-300" style = "background-image: url(../header.jpg)" > < / div >
< div class = "container" >
<!-- Place this tag in your head or just before your close body tag. -->
< div class = "plugin" >
< div class = "row" >
< div class = "col-sm-12" >
< h1 > Plugin – lektor-limit-dependencies 0.1< / h1 >
< / div >
< / div >
< div class = "row" >
< div class = "col-sm-1" > < / div >
< div class = "col-sm-11" >
< p > Lektor plugin to limit dependencies created by queries< p >
< / div >
< / div >
< div class = "row" >
< div class = "col-sm-3 plugin-margin" >
< h4 > Project links< / h4 >
< ul class = "tree-nav" >
< li > < a href = "https://github.com/dairiki/lektor-limit-dependencies" class = "ext" > Homepage< / a > < / li >
< / ul >
< div class = "separator" >
< h4 > GitHub Statistics< / h4 >
< / div >
< ul class = "button-nav" >
< li > < p > < a class = "github-button" href = "https://github.com/dairiki/lektor-limit-dependencies" data-icon = "octicon-star" data-size = "large" data-show-count = "true" aria-label = "Star lektor-limit-dependencies on GitHub" target = "_blank" > Star< / a > < / p > < / li >
< li > < p > < a class = "github-button" href = "https://github.com/dairiki/lektor-limit-dependencies/fork" data-icon = "octicon-repo-forked" data-show-count = "true" data-size = "large" data-show = "true" aria-label = "Open Issues" target = "_blank" > Fork< / a > < / p > < / li >
< li > < p > < a class = "github-button" href = "https://github.com/dairiki/lektor-limit-dependencies/issues" data-icon = "octicon-issue-opened" data-show-count = "true" data-size = "large" data-show = "true" aria-label = "Open Issues" target = "_blank" > Open Issues< / a > < / p > < / li >
< / ul >
< div class = "separator" >
< h4 > Meta< / h4 >
< / div >
< p > < strong > Version:< / strong > 0.1< / p >
< p > < strong > Author:< / strong >
< a href = "mailto:dairiki@dairiki.org" > Jeff Dairiki< / a >
< / p >
< div class = "separator" >
< h4 > Tags< / h4 >
< / div >
< a href = "../tag/build-optimization/" > build optimization< / a > ,
< a href = "../tag/Jinja-filters/" > Jinja filters< / a > ,
and
< a href = "../tag/setup-env/" > setup-env< / a >
2022-02-24 02:43:35 +01:00
< p >
View < a href = "../tags/" class = "ref" > all tags< / a > .
< / p >
2022-02-20 15:15:38 +01:00
< / div >
< div class = "col-sm-9 doc-styling" >
< h2 > Project Description< / h2 >
< h1 > Lektor Limit Dependencies< / h1 >
2022-04-08 04:53:47 +02:00
< p > < a href = "https://pypi.org/project/lektor-limit-dependencies/" rel = "nofollow" > < img src = "https://img.shields.io/pypi/v/lektor-limit-dependencies.svg" alt = "PyPI version" > < / a >
< a href = "https://pypi.python.org/pypi/lektor-limit-dependencies/" rel = "nofollow" > < img src = "https://img.shields.io/pypi/pyversions/lektor-limit-dependencies.svg" alt = "PyPI Supported Python Versions" > < / a >
< a href = "https://github.com/dairiki/lektor-limit-dependencies/blob/master/LICENSE" rel = "nofollow" > < img src = "https://img.shields.io/github/license/dairiki/lektor-limit-dependencies" alt = "GitHub license" > < / a >
< a href = "https://github.com/dairiki/lektor-limit-dependencies" rel = "nofollow" > < img src = "https://github.com/dairiki/lektor-limit-dependencies/workflows/Tests/badge.svg" alt = "GitHub Actions (Tests)" > < / a > < / p >
2022-02-20 15:15:38 +01:00
< p > This is an experimental < a href = "https://www.getlektor.com/" rel = "nofollow" > Lektor< / a > plugin which aims to provide tools (or,
at least, a tool) to help keep Lektor’ s dependency tracking under
control.< / p >
< h2 > Introduction< / h2 >
< h3 > Motivating Example< / h3 >
< p > Suppose that you would like to list the three most recent blog posts
in the sidebar of your Lektor-based site. This can be done by adding
something like to your site base template:< / p >
< pre > < span class = "p" > < < / span > < span class = "nt" > h3< / span > < span class = "p" > > < / span > Recent Posts< span class = "p" > < /< / span > < span class = "nt" > h3< / span > < span class = "p" > > < / span >
< span class = "p" > < < / span > < span class = "nt" > ul< / span > < span class = "p" > > < / span >
< span class = "cp" > {%< / span > < span class = "k" > for< / span > < span class = "nv" > post< / span > < span class = "k" > in< / span > < span class = "nv" > site.query< / span > < span class = "o" > (< / span > < span class = "s1" > ' /blog' < / span > < span class = "o" > )< / span > < span class = "nv" > .order_by< / span > < span class = "o" > (< / span > < span class = "s1" > ' -pub_date' < / span > < span class = "o" > )< / span > < span class = "nv" > .limit< / span > < span class = "o" > (< / span > < span class = "m" > 3< / span > < span class = "o" > )< / span > < span class = "cp" > %}< / span >
< span class = "p" > < < / span > < span class = "nt" > li< / span > < span class = "p" > > < < / span > < span class = "nt" > a< / span > < span class = "na" > href< / span > < span class = "o" > =< / span > < span class = "s" > " < / span > < span class = "cp" > {{< / span > < span class = "nv" > post< / span > < span class = "o" > |< / span > < span class = "nf" > url< / span > < span class = "cp" > }}< / span > < span class = "s" > " < / span > < span class = "p" > > < / span > < span class = "cp" > {{< / span > < span class = "nv" > post.title< / span > < span class = "cp" > }}< / span > < span class = "p" > < /< / span > < span class = "nt" > a< / span > < span class = "p" > > < /< / span > < span class = "nt" > li< / span > < span class = "p" > > < / span >
< span class = "cp" > {%< / span > < span class = "k" > endfor< / span > < span class = "cp" > %}< / span >
< span class = "p" > < /< / span > < span class = "nt" > ul< / span > < span class = "p" > > < / span >
< / pre >
< p > This is not without drawbacks, however. To sort the post query by
date, Lektor iterates through < em > < strong > all< / strong > < / em > of the blog’ s posts, then sorts
them. In so doing, it records all of the blog posts as dependencies
< em > of every page on which this most-recent-post query is used< / em > . If this
is in the sidebar of every page on your site, < em > now every page on your
site will be rebuilt whenever any blog post at all< / em > (not just one of
the three most recent posts) < em > is edited< / em > .< / p >
< p > Technically, it is true that all pages now depend on all posts. You
might well edit the < code > pub_date< / code > of one of your older posts, such that
it should now appear in the most-recent listing. However, it is not
true that all pages need to be rebuilt for < em > any< / em > edit of any post.
Unfortunately, Lektor’ s dependency tracking system is not elaborate
enough to be able to express details about < em > how< / em > pages are
dependent on other pages; it only records that they < em > are< / em >
dependent, so Lektor has no option but to rebuild everything.< / p >
< h3 > A Solution?< / h3 >
< p > This plugin introduces a Jinja filter, < code > limit_dependencies< / code > . It
2022-04-08 04:53:47 +02:00
expects, as input, a Lektor < a href = "https://www.getlektor.com/docs/api/db/query/" title = "Lektor documentation on the Query class" rel = "nofollow" > query< / a > instance. It iterates through the
2022-02-20 15:15:38 +01:00
input query, and returns a new query instance which will yield the
same results. While it is doing its iteration, it — essentially —
monkey-patches Lektor’ s dependency tracking machinery to prevent it
from recording any dependencies.< / p >
2022-04-08 04:53:47 +02:00
< p > At the end, < code > limit_dependencies< / code > records one dependency on a < a href = "https://www.getlektor.com/docs/api/db/obj/#virtual-source-objects" title = "Lektor documentation on Virtual Source Objects" rel = "nofollow" > virtual
2022-02-20 15:15:38 +01:00
source object< / a > which depends only on the sequence of the identities
of the records in the query result. (Lektor provides a means by which
virtual source objects can report checksums. The
dependency tracking mechanism records those checksums, and will
trigger a rebuild should the checksum change. < code > Limit_dependencies< / code >
generates a virtual source object whose checksum depends on the
sequence of identities in the query result.)< / p >
< p > In the above example, this is exactly what we want. We only want to
trigger a rebuild if the order or composition of the most-recent three
posts changes. (Or if any of their titles change. Note that this
gets covered, too, since when the resulting query is iterated over in
the < code > {% for %}< / code > loop, dependencies will be recorded on the three
most-recent posts.)< / p >
< p > Thus, the example above, if replaced by:< / p >
< pre > < span class = "p" > < < / span > < span class = "nt" > h3< / span > < span class = "p" > > < / span > Recent Posts< span class = "p" > < /< / span > < span class = "nt" > h3< / span > < span class = "p" > > < / span >
< span class = "p" > < < / span > < span class = "nt" > ul< / span > < span class = "p" > > < / span >
< span class = "cp" > {%< / span > < span class = "k" > for< / span > < span class = "nv" > post< / span > < span class = "k" > in< / span > < span class = "nv" > site.query< / span > < span class = "o" > (< / span > < span class = "s1" > ' /blog' < / span > < span class = "o" > )< / span > < span class = "nv" > .order_by< / span > < span class = "o" > (< / span > < span class = "s1" > ' -pub_date' < / span > < span class = "o" > )< / span > < span class = "nv" > .limit< / span > < span class = "o" > (< / span > < span class = "m" > 3< / span > < span class = "o" > )|< / span > < span class = "nf" > limit_dependencies< / span > < span class = "cp" > %}< / span >
< span class = "p" > < < / span > < span class = "nt" > li< / span > < span class = "p" > > < < / span > < span class = "nt" > a< / span > < span class = "na" > href< / span > < span class = "o" > =< / span > < span class = "s" > " < / span > < span class = "cp" > {{< / span > < span class = "nv" > post< / span > < span class = "o" > |< / span > < span class = "nf" > url< / span > < span class = "cp" > }}< / span > < span class = "s" > " < / span > < span class = "p" > > < / span > < span class = "cp" > {{< / span > < span class = "nv" > post.title< / span > < span class = "cp" > }}< / span > < span class = "p" > < /< / span > < span class = "nt" > a< / span > < span class = "p" > > < /< / span > < span class = "nt" > li< / span > < span class = "p" > > < / span >
< span class = "cp" > {%< / span > < span class = "k" > endfor< / span > < span class = "cp" > %}< / span >
< span class = "p" > < /< / span > < span class = "nt" > ul< / span > < span class = "p" > > < / span >
< / pre >
< p > will work in a much more efficient and sane manner. Pages will be
rebuilt only if there are changes in the order, composition or content
of the three most recent posts.< / p >
< h2 > Installation< / h2 >
< p > Add lektor-limit-dependencies to your project from command line:< / p >
< pre > < code > lektor plugins add lektor-limit-dependencies
< / code > < / pre >
< p > See < a href = "https://www.getlektor.com/docs/plugins/" rel = "nofollow" > the Lektor plugin documentation< / a > for more information.< / p >
< h2 > Author< / h2 >
< p > Jeff Dairiki < a href = "mailto:dairiki@dairiki.org" > dairiki@dairiki.org< / a > < / p >
< h2 > Changelog< / h2 >
< h3 > Release 0.1 (2021-02-05)< / h3 >
< p > No code changes.< / p >
< p > Update development status classifier to " stable" .< / p >
< p > Test under python 3.9. Stop testing under 3.5.< / p >
< h3 > Release 0.1a1 (2020-05-19)< / h3 >
< p > Initial release.< / p >
< div class = "comment-box" >
< h2 > Comments< / h2 >
< div id = "disqus_thread" > < / div >
< script >
var disqus_config = function() { this.page.identifier = "/plugins/lektor-limit-dependencies"; this.page.url = "https://www.getlektor.com/plugins/lektor-limit-dependencies/"; };
(function() {
var d = document, s = d.createElement('script');
s.src = '//lektordocumentation.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
< / script >
< noscript >
Please enable JavaScript to view the < a href = "https://disqus.com/?ref_noscript"
rel="nofollow">comments powered by Disqus.< / a >
< / noscript >
< / div >
< / div >
< / div >
< / div >
< / div >
< / div >
< div class = "bottomsummary" >
< div class = "container" >
< / div >
< / div >
< footer >
< div class = "container" >
< div class = "row" >
< div class = "col-sm-4 icon-bar" >
< a href = "https://github.com/lektor/lektor/" title = "Lektor on GitHub"
>< i class = "fa fa-github" > < / i > < / a >
< a href = "https://github.com/lektor/lektor/issues/" title = "Report Issues for Lektor"
>< i class = "fa fa-bug" > < / i > < / a >
< a href = "https://twitter.com/getlektor" title = "Find Lektor on Twitter"
>< i class = "fa fa-twitter" > < / i > < / a >
< a href = "https://gitter.im/lektor/lektor" title = "Chat on Gitter"
>< i class = "fa fa-comment" > < / i > < / a >
< a href = "https://github.com/lektor/lektor-website/tree/master/content/plugins/lektor-limit-dependencies/contents.lr" title = "View source for this page" > < i class = "fa fa-code" > < / i > < / a >
< / div >
< div class = "col-sm-8" >
< a href = "../../license/" > License & Copyright< / a > •
< a href = "../../contact/" > Contact< / a > •
Made with < i class = "fa fa-fw fa-heart" title = "Heart" > < span hidden > Heart< / span > < / i > in Carinthia
< / div >
< / div >
< / div >
< / footer >
2022-02-23 01:34:48 +01:00
< script type = text/javascript src = "../../static/app.js?h=bb1b933a" charset = "utf-8" > < / script >
2022-02-20 15:15:38 +01:00
< script >
((window.gitter = {}).chat = {}).options = {
room: 'lektor/lektor',
activationElement: null
};
document.write('< button class = "js-gitter-toggle-chat-button" > Toggle Chat< / button > ');
var dnt = navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack;
if (dnt != "1" & & dnt != "yes") {
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', 'UA-70822533-1', 'auto');
ga('set', 'anonymizeIp', true);
ga('send', 'pageview');
} else {
console.debug("Respecting Do-Not-Track, not running analytics.");
}
< / script >
< script async src = 'https://www.google-analytics.com/analytics.js' > < / script >
< script async defer id = "github-bjs" src = "https://buttons.github.io/buttons.js" > < / script >
< script > ! function ( d , s , id ) { var js , fjs = d . getElementsByTagName ( s ) [ 0 ] , p = /^http:/ . test ( d . location ) ? 'http' : 'https' ; if ( ! d . getElementById ( id ) ) { js = d . createElement ( s ) ; js . id = id ; js . src = p + '://platform.twitter.com/widgets.js' ; fjs . parentNode . insertBefore ( js , fjs ) ; } } ( document , 'script' , 'twitter-wjs' ) ; < / script >
< script src = "https://sidecar.gitter.im/dist/sidecar.v1.js" async defer > < / script >
< / body >
< / html >