ReactiveDag.Node.Slice (reactive_dag v0.17.0-rc.40)

Copy Markdown View Source

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: :fiscal

Then 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

poll_key(slice)

@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.