MishkaCmsCore.Runtime.Compilers.UserClosure (Mishka CMS Core v0.0.1)

Copy Markdown View Source

Whether anything a page or a layout draws can read the signed-in user — the question that decides, once at compile time, whether serving that page has to load and verify one at all.

MishkaCmsWeb.RuntimeLive used to be handed a finished @current_user on every mount, because the live session it is routed in loads one for every LiveView under it. For a signed-in visitor that is a token-row read and a user read on every page and every live navigation, and the great majority of database pages are brochure pages that never mention a visitor at all.

So the compiler answers it instead, and the answer is baked into the record's own module: MishkaCmsCore.Runtime.Compilers.LiveViewCompiler.build_assigns/1 writes it into __page_information__/0 and MishkaCmsCore.Runtime.Compilers.LayoutCompiler.build_layout_information/1 into __layout_information__/0, both under private.reads_user. A page whose own answer and whose layout's answer are both false is served without anything being looked up; every other page is served exactly as it was, by the same verification. A page that names permissions is judged by a user whatever this says — see MishkaCmsWeb.RuntimeLive's admit/3, which asks for one before it asks this.

One record at a time, which is what keeps the answer fresh

A page and its layout are asked SEPARATELY and their answers are put together at request time, because a module is only ever rebuilt for its own record: editing a layout rebuilds the layout module and no page under it. An answer about somebody else's source, baked here, would go stale the moment that source changed — the layout would start reading the visitor and every page already compiled would go on saying nobody is signed in.

Nothing else has to be asked, because a runtime component is handed ONLY the assigns its call site writes: MishkaCmsCore.Runtime.LiveViewHelpers.component/1 builds that map from the tag's own attributes, so a component's assigns holds component_name, site and whatever else the tag named, and nothing of the page's. A component can therefore reach the visitor in exactly two ways, and this module reads both off the record's own source:

  • the tag hands it something naming current_user, which is text in this record; or
  • the tag hands it the caller's WHOLE bag — assigns, or @socket, whose own assigns is the Phoenix.LiveView.Socket.AssignsNotInSocket struct holding that same map in __assigns__, and whose private holds the visitor's session besides.

So the question never leaves the record being compiled, and the answer cannot be made wrong by editing anything else.

Asked of the text, on purpose

MishkaCmsCore.Runtime.Compilers.LiveViewCompiler.shows_messages?/1 asks its question of the PARSED template, because a wrong yes there is a visitor who is told nothing. Here the polarity is the other way round: a wrong yes costs two queries on one page, and a wrong no is a signed-in visitor shown a page that believes nobody is signed in. So this one looks for the name in every piece of source the record can reach and over-approximates deliberately — a page whose prose happens to contain current_user pays for a user it does not need, and nothing breaks.

That also makes it indifferent to spelling, which matters here: @current_user, assigns[:current_user] — the form MishkaCmsCore.Runtime.PageBuilder.Conditions writes — socket.assigns.current_user in a handler and @current_user inside a helper's ~H sigil are four shapes in three grammars, and the name is the only thing all of them share.

What it reads

Every field of the record that is markup or Elixir — template, body, helpers, events, infos, and the prelude, clauses and module attributes a component keeps in extra. The name is looked for in all of them, and all of them are scanned for <.component …/> tags, because a tag written inside a helper's ~H sigil hands over exactly what one in the template does.

Everything it cannot decide is a yes

A tag that spreads a map of attributes, a template that cannot be tokenised, a record that is not there: each answers true. The one thing this must never do is answer false for a page that turns out to want a user.

Summary

Functions

Whether anything record — a page or a layout — draws can read the signed-in user.

Functions

reads_user?(record)

@spec reads_user?(map() | nil) :: boolean()

Whether anything record — a page or a layout — draws can read the signed-in user.

true for anything it cannot work out, a nil record included.