A dimension a human may select this node by: slice :fiscal_year.
recompute_by declares the unit a CHANGE invalidates. This declares the unit
a PERSON picks — "reprocess just FY25", "re-run last year's documents" — and
they are rarely the same. A node recomputing per :category is still sliced
by :fiscal_year, because that is the question an operator asks.
Without it the library can offer no such control. A cell key is one column or
a "|"-joined identity, so nothing generic can find the year in a row —
fiscal_year on one node and published_on on another are equally invisible
until the node says which it is.
values: makes the control renderable rather than a text box. Only the host
knows which fiscal years exist, and it usually already has the function:
slice :fiscal_year, values: {MyApp.Osc, :available_years, []}Deliberately NOT time-specific. The obvious first guess is a date range, and
it fits almost nothing: the real dimension here is a "FY22" string, and a
version is not temporal at all. Time is one instance of slicing, not its
shape.
Selecting a slice from the SOURCE, not just from stored rows
A slice narrows two different things, and only one of them was reachable.
column filters rows this node already HOLDS — the reprocess case, "re-derive
FY25 from documents I have". But a source whose upstream is addressable by
the same dimension can be asked to fetch only that part: a crawler that takes
fiscal: "FY25/26" walks twelve months instead of the whole corpus.
poll_as: is what the dimension is called when asking the SCANNER for it:
poll MuniWatch.Sources.AgendaCenter, every: "0 12 * * *"
slice :fiscal_year, values: {MuniWatch.Fiscal, :years, []}, poll_as: :fiscalThen Source.refresh(plan, "agenda_center", fiscal: "FY25/26") reaches
poll/1 with the scanner's own vocabulary, while the same slice still
filters fiscal_year for a reprocess.
Two names because they are genuinely two names. A scanner's option belongs to
whatever it wraps — an API query parameter, a CLI flag — and the column
belongs to this node's schema; requiring them to match would make every
scanner rename its arguments after a storage decision. Defaults to column,
which is the common case, so the second name is written only when it differs.
Declared here rather than mapped in a host because a translation table off to
one side drifts from the DSL that needs it, and because the source has to
reach poll/1 and the crontab sweep identically — both read this entity.
Summary
Functions
What to call this dimension when asking the SCANNER for it — poll_as when
given, the column otherwise.
Functions
@spec poll_key(%ReactiveDag.Node.Slice{ __identifier__: term(), __spark_metadata__: term(), column: term(), label: term(), poll_as: term(), values: term() }) :: atom()
What to call this dimension when asking the SCANNER for it — poll_as when
given, the column otherwise.
One place decides, because a default spelled at each call site is a default that disagrees with itself eventually.