FabianDaniel00

FabianDaniel00

Hi everyone!

I trying to create a Phoenix LiveView dependency to authorize the lifecycle stages with attach_hook/4 via on_mount/4 callback.
I need to attach hook to :mount stage and it possible with the on_mount/4 callback as described here. I don’t know why I getting the following error:

# ArgumentError at GET /some/route

Exception:

    ** (ArgumentError) invalid lifecycle event provided to attach_hook.
    
    Expected one of: :handle_event | :handle_info | :handle_params
    
    Got: :mount
    
        (phoenix_live_view 0.20.1) lib/phoenix_live_view/lifecycle.ex:61: Phoenix.LiveView.Lifecycle.attach_hook/4
        (elixir 1.15.7) lib/enum.ex:2510: Enum."-reduce/3-lists^foldl/2-0-"/3
        (dronteh 0.1.0) lib/my_dependency_web/on_mount.ex:6: MyDependency.OnMount.on_mount/4
        (phoenix_live_view 0.20.1) lib/phoenix_live_view/lifecycle.ex:149: anonymous fn/4 in Phoenix.LiveView.Lifecycle.mount/3
        (phoenix_live_view 0.20.1) lib/phoenix_live_view/lifecycle.ex:212: Phoenix.LiveView.Lifecycle.reduce_socket/3
        (phoenix_live_view 0.20.1) lib/phoenix_live_view/utils.ex:392: anonymous fn/6 in Phoenix.LiveView.Utils.maybe_call_live_view_mount!/5
        (telemetry 1.2.1) /app/deps/telemetry/src/telemetry.erl:321: :telemetry.span/3
        (phoenix_live_view 0.20.1) lib/phoenix_live_view/static.ex:277: Phoenix.LiveView.Static.call_mount_and_handle_params!/5
        (phoenix_live_view 0.20.1) lib/phoenix_live_view/static.ex:118: Phoenix.LiveView.Static.render/3
        (phoenix_live_view 0.20.1) lib/phoenix_live_view/controller.ex:39: Phoenix.LiveView.Controller.live_render/3
        (phoenix 1.7.9) lib/phoenix/router.ex:432: Phoenix.Router.__call__/5
        (dronteh 0.1.0) lib/dronteh_web/endpoint.ex:1: DrontehWeb.Endpoint.plug_builder_call/2
        (dronteh 0.1.0) deps/plug/lib/plug/debugger.ex:136: DrontehWeb.Endpoint."call (overridable 3)"/2
        (dronteh 0.1.0) lib/dronteh_web/endpoint.ex:1: DrontehWeb.Endpoint.call/2
        (phoenix 1.7.9) lib/phoenix/endpoint/sync_code_reload_plug.ex:22: Phoenix.Endpoint.SyncCodeReloadPlug.do_call/4
        (plug_cowboy 2.6.1) lib/plug/cowboy/handler.ex:11: Plug.Cowboy.Handler.init/2
        (cowboy 2.10.0) /app/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
        (cowboy 2.10.0) /app/deps/cowboy/src/cowboy_stream_h.erl:306: :cowboy_stream_h.execute/3
        (cowboy 2.10.0) /app/deps/cowboy/src/cowboy_stream_h.erl:295: :cowboy_stream_h.request_process/3
        (stdlib 5.1.1) proc_lib.erl:241: :proc_lib.init_p_do_apply/3
    

Code:

`lib/phoenix_live_view/lifecycle.ex`

    56         hooks ++ [hook]
    57       end)
    58     end
    59   
    60     def attach_hook(%Socket{}, _id, stage, _fun) do
    61>      raise ArgumentError, """
    62       invalid lifecycle event provided to attach_hook.
    63   
    64       Expected one of: :handle_event | :handle_info | :handle_params
    65   
    66       Got: #{inspect(stage)}
    
`lib/enum.ex`

    2505     operation cannot be expressed by any of the functions in the `Enum`
    2506     module, developers will most likely resort to `reduce/3`.
    2507     """
    2508     @spec reduce(t, acc, (element, acc -> acc)) :: acc
    2509     def reduce(enumerable, acc, fun) when is_list(enumerable) do
    2510>      :lists.foldl(fun, acc, enumerable)
    2511     end
    2512   
    2513     def reduce(first..last//step, acc, fun) do
    2514       reduce_range(first, last, step, acc, fun)
    2515     end
    
`lib/my_dependency_web/on_mount.ex`

    1     defmodule MyDependency do
    2       import Phoenix.LiveView
    3
    4       def on_mount(:default, _params, _session, socket) do
    5         # some code...
    6         socket = attach_hook(socket, "protect_mount", :mount, fn _params, _session, socket) ->
    7           # some code...
    8
    9           {:cont, socket} or {:halt socket} # with redirect
    10        end)
    11
    
`lib/phoenix_live_view/lifecycle.ex`

    144     end
    145   
    146     @doc false
    147     def mount(params, session, %Socket{private: %{@lifecycle => lifecycle}} = socket) do
    148       reduce_socket(lifecycle.mount, socket, fn %{id: {_mod, arg}} = hook, acc ->
    149>        case hook.function.(arg, params, session, acc) do
    150           {:halt, %Socket{redirected: nil}} ->
    151             raise_halt_without_redirect!(hook)
    152   
    153           {:cont, %Socket{redirected: to}} when not is_nil(to) ->
    154             raise_continue_with_redirect!(hook)
    
`lib/phoenix_live_view/lifecycle.ex`

    207   
    208       new_socket
    209     end
    210   
    211     defp reduce_socket([hook | hooks], acc, function) do
    212>      case function.(hook, acc) do
    213         {:cont, %Socket{} = socket} -> reduce_socket(hooks, socket, function)
    214         {:halt, %Socket{} = socket} -> {:halt, socket}
    215         other -> bad_lifecycle_response!(other, hook)
    216       end
    217     end
    
`lib/phoenix_live_view/utils.ex`

    387         :telemetry.span(
    388           [:phoenix, :live_view, :mount],
    389           %{socket: socket, params: params, session: session, uri: uri},
    390           fn ->
    391             socket =
    392>              case Lifecycle.mount(params, session, socket) do
    393                 {:cont, %Socket{} = socket} when exported? ->
    394                   view.mount(params, session, socket)
    395   
    396                 {_, %Socket{} = socket} ->
    397                   {:ok, socket}
    
`/app/deps/telemetry/src/telemetry.erl`

    316           EventPrefix ++ [start],
    317           #{monotonic_time => StartTime, system_time => erlang:system_time()},
    318           merge_ctx(StartMetadata, DefaultCtx)
    319       ),
    320   
    321>      try {_, #{}} = SpanFunction() of
    322         {Result, StopMetadata} ->
    323             StopTime = erlang:monotonic_time(),
    324             execute(
    325                 EventPrefix ++ [stop],
    326                 #{duration => StopTime - StartTime, monotonic_time => StopTime},
    
`lib/phoenix_live_view/static.ex`

    272   
    273     defp call_mount_and_handle_params!(socket, view, session, params, uri) do
    274       mount_params = if socket.router, do: params, else: :not_mounted_at_router
    275   
    276       socket
    277>      |> Utils.maybe_call_live_view_mount!(view, mount_params, session, uri)
    278       |> mount_handle_params(view, params, uri)
    279       |> case do
    280         {:noreply, %Socket{redirected: {:live, _, _}} = socket} ->
    281           {:stop, socket}
    282   
    
`lib/phoenix_live_view/static.ex`

    113           action,
    114           flash,
    115           host_uri
    116         )
    117   
    118>      case call_mount_and_handle_params!(socket, view, mount_session, conn.params, request_url) do
    119         {:ok, socket} ->
    120           data_attrs = [
    121             phx_session: sign_root_session(socket, router, view, to_sign_session, live_session),
    122             phx_static: sign_static_token(socket)
    123           ]
    
`lib/phoenix_live_view/controller.ex`

    34           end
    35         end
    36   
    37     """
    38     def live_render(%Plug.Conn{} = conn, view, opts \\ []) do
    39>      case LiveView.Static.render(conn, view, opts) do
    40         {:ok, content, socket_assigns} ->
    41           conn
    42           |> Phoenix.Controller.put_view(LiveView.Static)
    43           |> Phoenix.Controller.render(
    44             "template.html",
    
`lib/phoenix/router.ex`

    427           :telemetry.execute([:phoenix, :router_dispatch, :stop], measurements, metadata)
    428           halted_conn
    429   
    430         %Plug.Conn{} = piped_conn ->
    431           try do
    432>            plug.call(piped_conn, plug.init(opts))
    433           else
    434             conn ->
    435               measurements = %{duration: System.monotonic_time() - start}
    436               metadata = %{metadata | conn: conn}
    437               :telemetry.execute([:phoenix, :router_dispatch, :stop], measurements, metadata)
    
`lib/dronteh_web/endpoint.ex`

    1>  defmodule DrontehWeb.Endpoint do
    2     use Phoenix.Endpoint, otp_app: :dronteh
    3   
    4     # The session will be stored in the cookie and signed,
    5     # this means its contents can be read but not tampered with.
    6     # Set :encryption_salt if you would also like to encrypt it.
    
`deps/plug/lib/plug/debugger.ex`

    131             case conn do
    132               %Plug.Conn{path_info: ["__plug__", "debugger", "action"], method: "POST"} ->
    133                 Plug.Debugger.run_action(conn)
    134   
    135               %Plug.Conn{} ->
    136>                super(conn, opts)
    137             end
    138           rescue
    139             e in Plug.Conn.WrapperError ->
    140               %{conn: conn, kind: kind, reason: reason, stack: stack} = e
    141               Plug.Debugger.__catch__(conn, kind, reason, stack, @plug_debugger)
    
`lib/dronteh_web/endpoint.ex`

    1>  defmodule DrontehWeb.Endpoint do
    2     use Phoenix.Endpoint, otp_app: :dronteh
    3   
    4     # The session will be stored in the cookie and signed,
    5     # this means its contents can be read but not tampered with.
    6     # Set :encryption_salt if you would also like to encrypt it.
    
`lib/phoenix/endpoint/sync_code_reload_plug.ex`

    17   
    18     def call(conn, {endpoint, opts}), do: do_call(conn, endpoint, opts, true)
    19   
    20     defp do_call(conn, endpoint, opts, retry?) do
    21       try do
    22>        endpoint.call(conn, opts)
    23       rescue
    24         exception in [UndefinedFunctionError] ->
    25           case exception do
    26             %UndefinedFunctionError{module: ^endpoint} when retry? ->
    27               # Sync with the code reloader and retry once
    
`lib/plug/cowboy/handler.ex`

    6     def init(req, {plug, opts}) do
    7       conn = @connection.conn(req)
    8   
    9       try do
    10         conn
    11>        |> plug.call(opts)
    12         |> maybe_send(plug)
    13         |> case do
    14           %Plug.Conn{adapter: {@connection, %{upgrade: {:websocket, websocket_args}} = req}} = conn ->
    15             {handler, state, cowboy_opts} = websocket_args
    16             {__MODULE__, copy_resp_headers(conn, req), {handler, state}, cowboy_opts}
    
`/app/deps/cowboy/src/cowboy_handler.erl`

    32   -optional_callbacks([terminate/3]).
    33   
    34   -spec execute(Req, Env) -> {ok, Req, Env}
    35   	when Req::cowboy_req:req(), Env::cowboy_middleware:env().
    36   execute(Req, Env=#{handler := Handler, handler_opts := HandlerOpts}) ->
    37>  	try Handler:init(Req, HandlerOpts) of
    38   		{ok, Req2, State} ->
    39   			Result = terminate(normal, Req2, State, Handler),
    40   			{ok, Req2, Env#{result => Result}};
    41   		{Mod, Req2, State} ->
    42   			Mod:upgrade(Req2, Env, Handler, State);
    
`/app/deps/cowboy/src/cowboy_stream_h.erl`

    301   	end.
    302   
    303   execute(_, _, []) ->
    304   	ok;
    305   execute(Req, Env, [Middleware|Tail]) ->
    306>  	case Middleware:execute(Req, Env) of
    307   		{ok, Req2, Env2} ->
    308   			execute(Req2, Env2, Tail);
    309   		{suspend, Module, Function, Args} ->
    310   			proc_lib:hibernate(?MODULE, resume, [Env, Tail, Module, Function, Args]);
    311   		{stop, _Req2} ->
    
`/app/deps/cowboy/src/cowboy_stream_h.erl`

    290   %% to simplify the debugging of errors. The proc_lib library
    291   %% already adds the stacktrace to other types of exceptions.
    292   -spec request_process(cowboy_req:req(), cowboy_middleware:env(), [module()]) -> ok.
    293   request_process(Req, Env, Middlewares) ->
    294   	try
    295>  		execute(Req, Env, Middlewares)
    296   	catch
    297   		exit:Reason={shutdown, _}:Stacktrace ->
    298   			erlang:raise(exit, Reason, Stacktrace);
    299   		exit:Reason:Stacktrace when Reason =/= normal, Reason =/= shutdown ->
    300   			erlang:raise(exit, {Reason, Stacktrace}, Stacktrace)
    
`proc_lib.erl`

    No code available.


## Connection details

### Params

    %{}

### Request info

  * URI: http://localhost:4000/some/route
  * Query string: 

### Headers
  
  * accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8
  * accept-encoding: gzip, deflate, br
  * accept-language: en-US,en;q=0.9,hu;q=0.8,sr;q=0.7
  * cache-control: max-age=0
  * connection: keep-alive
  * cookie: _dronteh_key=SFMyNTY.g3QAAAADbQAAAAtfY3NyZl90b2tlbm0AAAAYNEZzX2RLRWpkOXBqbFE1TGJvR0lqcVI2bQAAAA5saXZlX3NvY2tldF9pZG0AAAA7dXNlcnNfc2Vzc2lvbnM6M2E4VW9vN1hBRm51S3hZTWF2S2cyN0RmNHFVZlltQk1obDBZczBxTXBSRT1tAAAACnVzZXJfdG9rZW5tAAAAIN2vFKKO1wBZ7isWDGryoNuw3-KlH2JgTIZdGLNKjKUR.lBUv7RmSNBZqPQ9UZHJ2Lb8_KokcFoukItDpXDw-aGw
  * host: localhost:4000
  * referer: http://localhost:4000/some/route
  * sec-ch-ua: "Brave";v="119", "Chromium";v="119", "Not?A_Brand";v="24"
  * sec-ch-ua-mobile: ?0
  * sec-ch-ua-platform: "Windows"
  * sec-fetch-dest: document
  * sec-fetch-mode: navigate
  * sec-fetch-site: same-origin
  * sec-gpc: 1
  * upgrade-insecure-requests: 1
  * user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36

### Session

    %{"_csrf_token" => "4Fs_dKEjd9pjlQ5LboGIjqR6", "live_socket_id" => "users_sessions:3a8Uoo7XAFnuKxYMavKg27Df4qUfYmBMhl0Ys0qMpRE=", "user_token" => <<221, 175, 20, 162, 142, 215, 0, 89, 238, 43, 22, 12, 106, 242, 160, 219, 176, 223, 226, 165, 31, 98, 96, 76, 134, 93, 24, 179, 74, 140, 165, 17>>}

My code is the following:

defmodule  MyAppWeb.SomeLive do
  use MyAppWeb, :live_view

  on_mount MyDependency

  def mount(_params, _session, socket) do
    # some code...

    {:ok, socket}
  end

  # other code...
end
defmodule MyDependency do
  import Phoenix.LiveView

  def on_mount(:default, _params, _session, socket) do
    # some code...
    socket = attach_hook(socket, "protect_mount", :mount, fn _params, _session, socket) ->
      # some code...

      {:cont, socket} or {:halt socket} # with redirect
    end)

    {:cont, socket}
  end
end

Of course my code has more logic but this is the main idea. I got that error in other projects too.
Can somebody tell me what I doing wrong? :confused:

Versions I use:

  • elixir: ~> 1.15.7
  • phoenix: ~> 1.7.9
  • phoenix_live_view: ~> 0.20.1

Showing Posts 1 to 2

codeanpeace

codeanpeace

Welcome!

No need for this attach_hook since on_mount is already a hook on mount which is why attach_hook doesn’t accept :mount as a valid lifecycle event.

Instead, give something like this a try:

def on_mount(:protect_mount, _params, _session, socket) do
  # some code...
  {:cont, socket}
end
FabianDaniel00

FabianDaniel00 OP

Thanks for the reply! :grin:
Hmm yeah this is makes sense. It seems like I misunderstood the docs.
My goal is the user can write pattern matching scenarios to those methods which can be attached hook to and pattern match such as the :mount stage as I already did with :handle_params and the other lifecycle methods via the attach_hook/4. But I think I can do that what you suggested but with some macro. :thinking:

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews