Dstar.Actions (dstar v0.3.0)

Copy Markdown View Source

Helpers for generating Datastar action expressions and encoding module names.

Examples

# In a Phoenix template:
<button data-on:click={Dstar.post(MyApp.CounterHandler, "increment")}>+</button>

# Other HTTP verbs:
<button data-on:click={Dstar.delete(MyApp.ItemHandler, "remove")}>×</button>

# Dynamic module (reads from signal):
<button data-on:click={Dstar.post("increment")}>+</button>

Literal event and module values are percent-encoded as one URL path segment. Dynamic module signals are encoded in the browser. Exact empty and dot segments are rejected because browser URL parsing would remove or normalize them.

A :prefix, when supplied to a module form, must be a local absolute application path: it starts with one / and contains no dot segment, backslash, query, fragment, or control character. "" means no prefix.

Summary

Functions

Decodes a URL-safe module name back to an Elixir module.

Generates a @delete(...) action expression for Datastar attributes.

Generates a @delete(...) expression with a local absolute URL prefix.

Encodes a module name for URL use.

Generates a @get(...) action expression for Datastar attributes.

Generates a @get(...) expression with a local absolute URL prefix.

Runs action whenever Dstar.Signals.nudge/3 bumps key.

Generates a @patch(...) action expression for Datastar attributes.

Generates a @patch(...) expression with a local absolute URL prefix.

Generates a @post(...) action expression for Datastar attributes.

Generates a @post(...) expression with a local absolute URL prefix.

Generates a @put(...) action expression for Datastar attributes.

Generates a @put(...) expression with a local absolute URL prefix.

Functions

decode_module(encoded)

@spec decode_module(String.t()) :: {:ok, module()} | :error

Decodes a URL-safe module name back to an Elixir module.

Returns {:ok, module} if the module exists, :error otherwise.

Examples

iex> Dstar.Actions.decode_module("my_app-counter_view")
{:ok, MyApp.CounterView}

delete(module_or_name, name_or_opts \\ [])

@spec delete(module(), String.t()) :: String.t()
@spec delete(String.t(), keyword()) :: String.t()

Generates a @delete(...) action expression for Datastar attributes.

CSRF is handled automatically — Datastar sends all signals (including csrf) as body params on POST/PUT/PATCH and in the datastar query parameter on GET/DELETE; RenameCsrfParam maps it to _csrf_token.

With a known module (compile-time):

iex> Dstar.Actions.delete(MyApp.CounterHandler, "increment")
~s|@delete("/ds/my_app-counter_handler/increment")|

With a dynamic module signal (runtime on client):

iex> expression = Dstar.Actions.delete("increment")
iex> String.contains?(expression, "encodeURIComponent")
true

With a URL prefix:

iex> Dstar.Actions.delete(MyApp.Handler, "save", prefix: "/ws")
~s|@delete("/ws/ds/my_app-handler/save")|

Options

  • :prefix — local absolute application path (for example, "/my-workspace"). Only for the module form; relative, protocol-relative, cross-origin, dot-segment, query, fragment, backslash, and control-containing values are rejected; percent escapes must decode as valid UTF-8. "" is the same as omitting it.
  • :module — literal module override. Only for the dynamic form. Without it, the $_dstar_module signal is read and encoded at runtime.

Event and module values are encoded as one literal route segment. Empty, ".", and ".." values raise ArgumentError because browsers normalize those segments.

delete(module, event_name, opts)

@spec delete(module(), String.t(), keyword()) :: String.t()

Generates a @delete(...) expression with a local absolute URL prefix.

The prefix must start with one / and cannot contain a dot segment, backslash, query, fragment, or control character. Percent escapes must be valid UTF-8.

Example

iex> Dstar.Actions.delete(MyApp.Handler, "save", prefix: "/my-workspace")
~s|@delete("/my-workspace/ds/my_app-handler/save")|

encode_module(module)

@spec encode_module(module()) :: String.t()

Encodes a module name for URL use.

Examples

iex> Dstar.Actions.encode_module(MyApp.CounterView)
"my_app-counter_view"

iex> Dstar.Actions.encode_module(MyApp.Web.ChatView)
"my_app-web-chat_view"

event(module_or_name, name_or_opts \\ [])

This function is deprecated. Use Dstar.Actions.post/2 (or get/put/patch/delete) instead.

event(module, event_name, opts)

This function is deprecated. Use Dstar.Actions.post/3 (or get/put/patch/delete) instead.

get(module_or_name, name_or_opts \\ [])

@spec get(module(), String.t()) :: String.t()
@spec get(String.t(), keyword()) :: String.t()

Generates a @get(...) action expression for Datastar attributes.

CSRF is handled automatically — Datastar sends all signals (including csrf) as body params on POST/PUT/PATCH and in the datastar query parameter on GET/DELETE; RenameCsrfParam maps it to _csrf_token.

With a known module (compile-time):

iex> Dstar.Actions.get(MyApp.CounterHandler, "increment")
~s|@get("/ds/my_app-counter_handler/increment")|

With a dynamic module signal (runtime on client):

iex> expression = Dstar.Actions.get("increment")
iex> String.contains?(expression, "encodeURIComponent")
true

With a URL prefix:

iex> Dstar.Actions.get(MyApp.Handler, "save", prefix: "/ws")
~s|@get("/ws/ds/my_app-handler/save")|

Options

  • :prefix — local absolute application path (for example, "/my-workspace"). Only for the module form; relative, protocol-relative, cross-origin, dot-segment, query, fragment, backslash, and control-containing values are rejected; percent escapes must decode as valid UTF-8. "" is the same as omitting it.
  • :module — literal module override. Only for the dynamic form. Without it, the $_dstar_module signal is read and encoded at runtime.

Event and module values are encoded as one literal route segment. Empty, ".", and ".." values raise ArgumentError because browsers normalize those segments.

get(module, event_name, opts)

@spec get(module(), String.t(), keyword()) :: String.t()

Generates a @get(...) expression with a local absolute URL prefix.

The prefix must start with one / and cannot contain a dot segment, backslash, query, fragment, or control character. Percent escapes must be valid UTF-8.

Example

iex> Dstar.Actions.get(MyApp.Handler, "save", prefix: "/my-workspace")
~s|@get("/my-workspace/ds/my_app-handler/save")|

on_nudge(key, action)

@spec on_nudge(String.t() | atom(), String.t()) :: %{
  required(String.t()) => String.t()
}

Runs action whenever Dstar.Signals.nudge/3 bumps key.

Returns an attribute map for HEEx spread:

<div id="posts" {on_nudge("posts", event("reload"))}>

Unlike data-effect, this does not fire on page init — only when that one nudge changes. See the Live collections guide.

Examples

iex> Dstar.Actions.on_nudge("posts", "@post('/reload')")
%{
  "data-on-signal-patch" => "@post('/reload')",
  "data-on-signal-patch-filter" => ~S({"include":"^nudges\\.posts$"})
}

patch(module_or_name, name_or_opts \\ [])

@spec patch(module(), String.t()) :: String.t()
@spec patch(String.t(), keyword()) :: String.t()

Generates a @patch(...) action expression for Datastar attributes.

CSRF is handled automatically — Datastar sends all signals (including csrf) as body params on POST/PUT/PATCH and in the datastar query parameter on GET/DELETE; RenameCsrfParam maps it to _csrf_token.

With a known module (compile-time):

iex> Dstar.Actions.patch(MyApp.CounterHandler, "increment")
~s|@patch("/ds/my_app-counter_handler/increment")|

With a dynamic module signal (runtime on client):

iex> expression = Dstar.Actions.patch("increment")
iex> String.contains?(expression, "encodeURIComponent")
true

With a URL prefix:

iex> Dstar.Actions.patch(MyApp.Handler, "save", prefix: "/ws")
~s|@patch("/ws/ds/my_app-handler/save")|

Options

  • :prefix — local absolute application path (for example, "/my-workspace"). Only for the module form; relative, protocol-relative, cross-origin, dot-segment, query, fragment, backslash, and control-containing values are rejected; percent escapes must decode as valid UTF-8. "" is the same as omitting it.
  • :module — literal module override. Only for the dynamic form. Without it, the $_dstar_module signal is read and encoded at runtime.

Event and module values are encoded as one literal route segment. Empty, ".", and ".." values raise ArgumentError because browsers normalize those segments.

patch(module, event_name, opts)

@spec patch(module(), String.t(), keyword()) :: String.t()

Generates a @patch(...) expression with a local absolute URL prefix.

The prefix must start with one / and cannot contain a dot segment, backslash, query, fragment, or control character. Percent escapes must be valid UTF-8.

Example

iex> Dstar.Actions.patch(MyApp.Handler, "save", prefix: "/my-workspace")
~s|@patch("/my-workspace/ds/my_app-handler/save")|

post(module_or_name, name_or_opts \\ [])

@spec post(module(), String.t()) :: String.t()
@spec post(String.t(), keyword()) :: String.t()

Generates a @post(...) action expression for Datastar attributes.

CSRF is handled automatically — Datastar sends all signals (including csrf) as body params on POST/PUT/PATCH and in the datastar query parameter on GET/DELETE; RenameCsrfParam maps it to _csrf_token.

With a known module (compile-time):

iex> Dstar.Actions.post(MyApp.CounterHandler, "increment")
~s|@post("/ds/my_app-counter_handler/increment")|

With a dynamic module signal (runtime on client):

iex> expression = Dstar.Actions.post("increment")
iex> String.contains?(expression, "encodeURIComponent")
true

With a URL prefix:

iex> Dstar.Actions.post(MyApp.Handler, "save", prefix: "/ws")
~s|@post("/ws/ds/my_app-handler/save")|

Options

  • :prefix — local absolute application path (for example, "/my-workspace"). Only for the module form; relative, protocol-relative, cross-origin, dot-segment, query, fragment, backslash, and control-containing values are rejected; percent escapes must decode as valid UTF-8. "" is the same as omitting it.
  • :module — literal module override. Only for the dynamic form. Without it, the $_dstar_module signal is read and encoded at runtime.

Event and module values are encoded as one literal route segment. Empty, ".", and ".." values raise ArgumentError because browsers normalize those segments.

post(module, event_name, opts)

@spec post(module(), String.t(), keyword()) :: String.t()

Generates a @post(...) expression with a local absolute URL prefix.

The prefix must start with one / and cannot contain a dot segment, backslash, query, fragment, or control character. Percent escapes must be valid UTF-8.

Example

iex> Dstar.Actions.post(MyApp.Handler, "save", prefix: "/my-workspace")
~s|@post("/my-workspace/ds/my_app-handler/save")|

put(module_or_name, name_or_opts \\ [])

@spec put(module(), String.t()) :: String.t()
@spec put(String.t(), keyword()) :: String.t()

Generates a @put(...) action expression for Datastar attributes.

CSRF is handled automatically — Datastar sends all signals (including csrf) as body params on POST/PUT/PATCH and in the datastar query parameter on GET/DELETE; RenameCsrfParam maps it to _csrf_token.

With a known module (compile-time):

iex> Dstar.Actions.put(MyApp.CounterHandler, "increment")
~s|@put("/ds/my_app-counter_handler/increment")|

With a dynamic module signal (runtime on client):

iex> expression = Dstar.Actions.put("increment")
iex> String.contains?(expression, "encodeURIComponent")
true

With a URL prefix:

iex> Dstar.Actions.put(MyApp.Handler, "save", prefix: "/ws")
~s|@put("/ws/ds/my_app-handler/save")|

Options

  • :prefix — local absolute application path (for example, "/my-workspace"). Only for the module form; relative, protocol-relative, cross-origin, dot-segment, query, fragment, backslash, and control-containing values are rejected; percent escapes must decode as valid UTF-8. "" is the same as omitting it.
  • :module — literal module override. Only for the dynamic form. Without it, the $_dstar_module signal is read and encoded at runtime.

Event and module values are encoded as one literal route segment. Empty, ".", and ".." values raise ArgumentError because browsers normalize those segments.

put(module, event_name, opts)

@spec put(module(), String.t(), keyword()) :: String.t()

Generates a @put(...) expression with a local absolute URL prefix.

The prefix must start with one / and cannot contain a dot segment, backslash, query, fragment, or control character. Percent escapes must be valid UTF-8.

Example

iex> Dstar.Actions.put(MyApp.Handler, "save", prefix: "/my-workspace")
~s|@put("/my-workspace/ds/my_app-handler/save")|