Outputs a sequence of values from the specified enumerator, applying the specified filtering/ordering/limits.
|X|hax.iterator:myTag: ..
directive in a
*.haxproj file,
prefix
, suffix
, joinVia
, content
{X|myTag: .. |}
Hence, formatting and structure of the output is mostly defined in the X-tag's one-off declaration / configuration, while the per-tag invocation / parameters define what values to enumerate, which ones to show, in what order etc.
This topic is in a compact format for users already familiar with the essentials of declaring X-tags and invoking them. If found challenging, try the more-elaborate-and-introductory topics first to form a better grasp on the basics and overall mechanisms.
Early or Page — this X-renderer determines dynamically (documented further below) whether or not it requires a page context for rendering, so depending on each hax.iterator-invoking X-tag's configuration and/or parameters:
To demonstrate valid *.haxproj directives declaring hax.iterator X-tags:
|X|hax.iterator:myenum |X|hax.iterator:myenumlist: prefix = "<ul class=\"my-blogs\"><li>", suffix = "</li></ul>", joinVia = "</li><li>", content => <a id="b_link_{:i:}" href="/{:v:}/index.html"> {B|title: {:v:} |} </a>
To elaborate, for example the last one of the above, as all |X| directives declaring X-tags do:
|X|
followed by the X-renderer identifier (here hax.iterator
) :
colon and the desired X-tag name to be used to invoke it (here myenumlist
), :
colon and now hax.iterator-specific configuration:prefix
— (empty by default) the output to produce once immediately prior to enumerationsuffix
— (empty by default) the output to produce once immediately following enumerationjoinVia
— (,
by default) the output to produce in-between all enumerated itemscontent
— the principal content output for each enumerated item {:i:}
outputs an iteration index: 0 for the first enumerated item and afterwards
increments by 1 for each further item, regardless of its value or which ordering/filtering was active (ie. no gaps ever
between successive such increments). {:v:}
outputs the current item in plain-text form as returned by the chosen
enumerator. {:n:}
outputs the equivalent to {:i:}
+ 1. {:l:}
outputs the total number ("length of list") of all items being
currently enumerated.content
is empty (the default), this is equivalent to it being {:v:}
.content
does not contain any of these 2 placeholders, it will obviously be output repeatedly, identically,
once per item.For example, given the above example |X| declaration directive:
{X|myenumlist: BlokNames |}
to output (displayed here with added line-wraps for readability)
<ul class="my-blogs">
<li><a id="b_link_0" href="/basics/index.html"> Basics </a></li>
<li><a id="b_link_1" href="/tags/index.html"> haXtags </a></li>
<li><a id="b_link_2" href="/xtypes/index.html"> X-renderers </a></li>
</ul>
BlokNames
is one of a handful of built-in enumerators — full list below. But for now,
continuing this first exploration, to apply a sort order to the very same enumerator, prepend a so-called modifier:
{X|myenumlist: But(Ordered Descending) BlokNames |}
, this will instead output:
<ul class="my-blogs">
<li><a id="b_link_0" href="/xtypes/index.html"> X-renderers </a></li>
<li><a id="b_link_1" href="/tags/index.html"> haXtags </a></li>
<li><a id="b_link_2" href="/basics/index.html"> Basics </a></li>
</ul>
Here, the But
clause allows expressing the Ordered
modifier, one of a handful of
built-in such modifiers. Another is LimitTo
for applying a limit
{X|myenumlist: But(LimitTo 2) BlokNames |}
to output:
<ul class="my-blogs">
<li><a id="b_link_0" href="/basics/index.html"> Basics </a></li>
<li><a id="b_link_1" href="/tags/index.html"> haXtags </a></li>
</ul>
Or applying that limit after sorting
{X|myenumlist: But(LimitTo 2) (But(Ordered Descending) BlokNames )|}
to output:
<ul class="my-blogs">
<li><a id="b_link_0" href="/xtypes/index.html"> X-renderers </a></li>
<li><a id="b_link_1" href="/tags/index.html"> haXtags </a></li>
</ul>
By now, this is starting to become unwieldy: multiple such nested But
s can be somewhat verbose and keeping track of
correct parenthesis placement easily error-prone, plus their right-to-left flow (ie. above: first-sort-then-limit logic written as
first the limit part, then the sort part) possibly counterintuitive for non-programmers. The (functionally
equivalent But
alternative, the) With
clause avoids such nesting-via-parens and flips that right-to-left flow over.
So the same output as above can be achieved with a much simpler notation:
{X|myenumlist: With BlokNames [Ordered Descending , LimitTo 2] |}
— ie. "with the
enumerated values, but ordered this way: give us the first 2":
<ul class="my-blogs">
<li><a id="b_link_0" href="/xtypes/index.html"> X-renderers </a></li>
<li><a id="b_link_1" href="/tags/index.html"> haXtags </a></li>
</ul>
Range startnum endnum
— a range of numbers,{X|myenum: Range 2 7 |}
gives: 2, 3, 4, 5, 6, 7Values [..]
— a given list of text values,{X|myenum: Values ["Hudak", "Wadler", "Peyton-Jones", "Bird", "Okasaki"] |}
gives: Hudak, Wadler, Peyton-Jones, Bird, OkasakiBlokNames
— names of all Bloks defined in the project,{X|myenum: BlokNames |}
gives: basics, tags, xtypesFeedNames blokstoo?
— names of all "feeds" known in the project, including or excluding the above BlokNames
{X|myenum: FeedNames True |}
gives: xdesc, basics, tags, xtypes{X|myenum: FeedNames False |}
gives: xdescFeedPosts
and FeedValues
— a bit more involved:In addition to the simpler enumerators outlined above, FeedValues
and FeedPosts
enumerate items derived on the fly
from the project's Blok pages (if any) and/or the project's "feed-posts" (if any). These 2
enumerators have quite a few commonalities:
FeedPosts <filter> []
andFeedValues <filter> "<fieldname>"
All
(no filtering, anything goes), or the functionally exactly
equivalent Some{ feeds=[], cats=[], dates=AnyDate }
(also no filtering), or the same syntax-sensitive form but
with some or all of these 3 properties set: []
means "all of them")cat
egories []
means "all of them")AnyDate
to indicate "from any date" or
Between "from" "up-until"
with both text values of course encoding dates in the well-known,
machine-friendly format "YYYY-MM-DD"
In the current version, a known issue: in a project with Blok pages, using in
Blok pages either All
or any Some
that effectively selects any Bloks as feeds is "not currently supported"
(ie. expect incorrect, inconsistent or missing results). All other pages are OK for such uses, however, as are all
auto-generated Blok 'index' pages (if any). To be rectified in a future release — still, the functionality as-is
is valuable enough for the already-working-today use cases.
In this site, {X|myenum: FeedValues All "cat" |}
produces some peculiar outputs:
demoSimplest, demoCfgArgs, hax.miniTag, hax.htmlImage, hax.htmlLink, hax.htmlLinks, hax.htmlAnchors, hax.xmlEscape, hax.dtFormat, hax.unMarkup, hax.noOp, hax.snippet, hax.iterator, hax.feedView, —that final comma baffles, for one— here's how and why:
All
uses as input feeds essentially what the described-earlier FeedNames True
enumerator
outputs: xdesc, basics, tags, xtypes
— in this site there are 3 Bloks defined and 1
"feed" (named xdesc
, in the bottom ~80-90 lines of
default.haxproj).xdesc
all have (perhaps somewhat-unusually-so) each a unique
cat
egoryhax.*
but that's simply due to the content semantics of this site:
documenting HaXtatic) — Blok pages however don't have such cat
egoriesFeedValues
in particular doesn't output duplicate values (only 1
faux-cat
was output from a few dozen Blok "posts") — making FeedValues
the appropriate choice for grouping
for example actual posts together in individual (to be dynamically assembled from such unique-value outputs) sub-ordinate
hax.iterator
s.So much for All
, now to demonstrate Some
more examples:
{X|myenum: FeedValues Some{ feeds=[],cats=[],dates=AnyDate } "dt:year"|}
— as mentioned, such a Some
is identical to All
, but here instead of a post's cat
, its date's year
→ 1234, 2016{X|myenum: FeedValues Some{ feeds=["xdesc"],cats=[],dates=AnyDate } "dt:year"|}
→ 1234{X|myenum: FeedValues Some{ feeds=["xdesc"],cats=[],dates=AnyDate } "cat" |}
— as we saw, xdesc
has 11 "posts" but each has a uniquely distinct cat
:
→ demoSimplest, demoCfgArgs, hax.miniTag, hax.htmlImage, hax.htmlLink, hax.htmlLinks, hax.htmlAnchors, hax.xmlEscape, hax.dtFormat, hax.unMarkup, hax.noOp, hax.snippet, hax.iterator, hax.feedView{X|myenum: FeedValues Some{ feeds=["xdesc"],cats=[],dates=AnyDate } "link"|}
— however, most of them share the same (empty, hence the below extra comma) link
:
→ http://github.com/metaleap/haxtatic/blob/master/src/X/DemoSimplest.hs, http://github.com/metaleap/haxtatic/blob/master/src/X/DemoCfgArgs.hs,{X|myenum: FeedValues Some{ feeds=["xdesc"],cats=[],dates=Between "1234-11-" "1234-12-31" } "dt"|}
— filtering by date range:
→ 1234-12-15, 1234-11-15{X|myenum: FeedValues Some{ feeds=["xdesc"],cats=[],dates=Between "1234-11-" "1234-12-31"} "cfgmore"|}
— but both these results from that date-range filter share the same cfgmore
(custom more
field) value:
→ (no other settings)So the above outlines exhaustively how FeedValues
operates, hoes does FeedPosts
differ?
FeedValues
enumerated unique single-field values for all "posts" selected,FeedPosts
the entire (if passing the filter) post (all its field values) is output.Ordered
modifier overrides this order.For this, clearly FeedPosts
shouldn't just dictate some particular output formatting or other for such data records! Instead,
all that it outputs for each item is a syntactic notation of vars
to then be fed directly into an outer hax.snippet. So
just a moderate grasp of snippets and how to populate their vars
when invoking them via X-tags, plus
the above joinVia
/content
directive properties (plus possibly the below WrapEachIn
modifier) clears the path to infinitely versatile micro-content rendering.
To give an impression of this in practice, here's just the output produced when selecting:
{X|myenum: FeedPosts Some{ feeds=["xdesc"], cats=["hax.snippet"], dates=AnyDate} ["cfgmore"] |}
— ie. selecting from this site's xdesc
"feed" just the "post" describing hax.snippet with not-only all standard
fields (more below) but-also the custom more
field cfgmore:
("cfgmore","<code>vars</code>, <code>content</code>"),("feed","xdesc"),("dt","1234-06-05"),("dt:year","1234"),("cat","hax.snippet"),("title",""),("link",""),("content","Renders the named \"snippet\" (aka. \"controls\" / \"components\" / \"sub-templates\") substituting the specified\r\n\tnamed-parameter values.")
A simple list of name-value-pair tuples — just without the enclosing []
square brackets that are
not to be forgotten when ensuring the wrapping of these per-item outputs inside what will typically (effectively have to) end up amounting
to both {X|mysnippet: vars=[
and ], content=> |}
.
"That sounds more complicated than necessary, wouldn't it be nicer if one could specify just-the-name of any
snippet and FeedPosts
then invoked it directly, instead of outputting syntax to then wrap other syntax
around?" Certainly true, but this would lose the flexibility to include further additional vars
(or even content=>
, or
future parameters) with the snippet's X-tag. Maybe such a shortcut will appear in a future release.
In addition to the specified haxproj custom more
fields, the following standard fields are always returned (even
if empty) as de-facto vars
by FeedPosts
, and also all understood by FeedValues
:
feed
— the post's "feed" name or Blok namedt
— post date in the standard format YYYY-MM-DD
dt:year
— post date's year only (eg. for grouping with FeedValues
)cat
— for Blok "posts": empty — otherwise: as set for the posttitle
— for Blok "posts": detected from <h1>
like {P|title|}
—
otherwise: as set for the postlink
— for Blok "posts": relative URI path to the page —
otherwise: as set for the postcontent
— for Blok "posts": content of the first <p>
if any —
otherwise: as set for the postSo enumerators are built-in routines that know how to iterate certain ranges, collections, lists, etc. Can their result sets be "tweaked"/mangled/sliced/diced/etc prior to the final output? This is achieved by adding to the 1 enumerator possible per hax.iterator X-tag any number of modifiers.
As explained above, any such modifiers can be expressed either one-per-But
(which in turn can be nested),
or stated as an ordered sequence of multiple such modifiers per With
clause.
(Technically, one could also specify a complete But
clause (in parens) as the
enumerator in a With
clause, or one could also specify a complete With
clause (in parens) as the
enumerator in a But
clause, but practically there's no good reason one should want to.)
Syntax: using With (enumerator) [modifier 1st , modifier 2nd , .. , modifier last]
, always wrap the
enumerator in parentheses and all modifiers together in 1 set of square brackets. Using But (only modifier)
(enumerator-or-another-But)
, always wrap the 1 modifier in parentheses and then subsequently the enumerator
too.
Limits the number of items:
{X|myenum: But (LimitTo 2) (FeedNames True) |}
produces xdesc, basics
instead of xdesc, basics, tags, xtypesSkips a number of items:
{X|myenum: With (Range 12 3) [Skip 4] |}
produces 8, 7, 6, 5, 4, 3
instead of 12, 11, 10, 9, 8, 7, 6, 5, 4, 3Encloses every item within a given prefix and suffix:
{X|myenum: But ( WrapEachIn ("/" , "/index.html") ) (BlokNames) |}
produces /basics/index.html, /tags/index.html, /xtypes/index.html
instead of basics, tags, xtypesRe-orders the items either Ascending
or Descending
or Shuffle
d
{X|myenum: With (FeedNames True) [Ordered Descending] |}
produces xtypes, xdesc, tags, basics
instead of xdesc, basics, tags, xtypes{X|myenum: But (Ordered Ascending) (Values ["zeta","phi","gamma","beta","alpha"]) |}
produces alpha, beta, gamma, phi, zeta
instead of zeta, phi, gamma, beta, alpha{X|myenum: With (Range 1 23) [Ordered (Shuffle False) , LimitTo 6, WrapEachIn ("#",". ")] |}
produces #23. , #22. , #21. , #20. , #18. , #17.
instead of 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23
(Shuffle True)
or (Shuffle False)
are valid. When used in a central project file such as a
*.haxproj, a template or a snippet (rather than directly inside a
content source file), Shuffle True
shuffles differently for each output file while Shuffle False
shuffles
identically for all output files during this processing run (but still varying with each processing run).{X|myenum: With (Values ["foo","bar"]) [Ordered None] |}
"produces" foo, bar
"instead of" foo, bar — ie. None
does not touch the items at all: useless
perhaps, except for example when quickly and temporarily wanting to disable reordering somewhere inside some But
nesting.As noted earlier, the FeedPosts
enumerator already orders items returned (not alphabetically
but by post date), and always does so: defaulting to descending absent any Ordered
placed to the contrary. So
Ordered Descending
, Ordered None
or no Ordered
modifier whatsoever all result in identical outputs, while
Ordered Ascending
shows "oldest first" and of course a Shuffle
just randomizes the sort order as described above.
An X-tag of type hax.iterator
defaults to Early but is delayed to Page stage
whenever:
Ordered (Shuffle True)
modifier (see above)FeedValues
or FeedPosts
enumerator (see
above) selecting either All
or Some
that will effectively
include any such Bloks via its specified feeds
property.