User: Admin: Difference between revisions

From ElixirBlocks
Jump to: navigation, search
No edit summary
No edit summary
 
(19 intermediate revisions by the same user not shown)
Line 1: Line 1:
from(a in Alert, where: a.timed_alert == true)
|> Repo.update_all(set: [enabled: false])
<source>
<source>
defmodule App do
defmodule AppWeb.Sandbox do
  use AppWeb, :live_view


   def get_titles() do
   def mount(_params, _session, socket) do
     data = [
     {:ok, socket}
      "https://cointelegraph.com/rss",
  end
      "https://chaski.huffpost.com/us/auto/vertical/us-news"
      # Add more RSS feed URLs here
    ]


    data
  def handle_event("start_task", _params, socket) do
    |> Enum.map(fn url ->
    Task.async(fn -> :hello_from_task end)
      case ElixirRss.fetch_and_parse(url) do
    {:noreply, socket}
        {:ok, feed} -> feed.entries |> Enum.map(fn entry -> entry end)
        {:error, reason} -> {:error, reason}
      end
    end)
    |> Enum.map(fn entry_list ->
      entry_list
      |> Enum.map(fn entry -> entry.title end)
    end)
   end
   end


   def hello do
   def handle_info({ref, result}, socket) do
     rss_feeds = [
     IO.inspect"________________________________________________________________________________"
      "https://cointelegraph.com/rss",
    IO.inspect({:task_result_received, ref, result})
      "https://chaski.huffpost.com/us/auto/vertical/us-news"
    {:noreply, socket}
      # Add more RSS feed URLs here
  end
     ]
 
  def handle_info(msg, socket) do
    # Optionally log or inspect the unhandled message if you're curious
    # IO.inspect({:unhandled_info, msg})
     {:noreply, socket}
  end


     rss_feeds
  def render(assigns) do
     |> Enum.map(fn url ->
     ~H"""
       case ElixirRss.fetch_and_parse(url) do
     <div>
        {:ok, feed} -> feed.entries |> Enum.map(fn entry -> entry end)
      <button phx-click="start_task">Start Task</button>
        {:error, reason} -> {:error, reason}
       <p>Barebones</p>
      end
    </div>
     end)
     """
   end
   end
end
end
</source>
</source>




<source>
<source>
defmodule AppWeb.PageLive do
defmodule AppWeb.Sandbox do
   use AppWeb, :live_view
   use AppWeb, :live_view
   def mount(_params, _session, socket) do
   def mount(_params, _session, socket) do
    if connected?(socket) do
     {:ok, socket}
      Phoenix.PubSub.subscribe(App.PubSub, "message")
    end
     {:ok, assign(socket, message_item: "different")}
   end
   end
   def handle_info({:pubsub_transmission, text}, socket) do
 
     IO.inspect "pub sub working"
   def handle_event("start_task", _params, socket) do
     {:noreply, assign(socket, message_item: text)}
     Task.async(fn -> :hello_from_task end)
     {:noreply, socket}
   end
   end


   def loop(data) do
   def handle_info({ref, result}, socket) do
    IO.inspect data
    IO.inspect"________________________________________________________________________________"
    if data > 0 do
    IO.inspect({:task_result_received, ref, result})
      IO.inspect "notify"
    {:noreply, socket}
      # run code to compare files, loop. If different run pubsub code below
  end
      :timer.sleep(2000)
      loop(data-1)


    end
  def handle_info({:DOWN, ref, :process, pid, reason}, socket) do
    if data <= 0 do
    IO.inspect({:task_completed, ref, pid, reason})
    {:noreply, socket}
  end


      Phoenix.PubSub.broadcast(App.PubSub, "message", {:pubsub_transmission, "data"})
  def render(assigns) do
       "done"
    ~H"""
    end
    <div>
      <button phx-click="start_task">Start Task</button>
       <p>Barebones</p>
    </div>
    """
   end
   end
end
</source>




Async claude ai example


   def handle_event("change-text", _params, socket) do
 
     spawn(fn -> loop(3) end)
Explain to me the relationship between handle_info and making an async call
     {:noreply, assign(socket, message_item: "START" )}
 
The relationship between handle_info and making async calls in Phoenix LiveView is fundamental to understanding how to handle non-blocking operations in your application.
 
When you make an asynchronous call in Elixir (like using Task.async), you're creating a separate process that runs independently from your LiveView process. This allows your LiveView to remain responsive while potentially time-consuming operations (like API calls) are happening.
 
Here's how they work together:
 
Async Operation Initiation: When you start an async operation using Task.async, it returns a reference to the task.
Message Passing: When the async task completes, it automatically sends a message back to the process that created it. This message contains the task reference and the result: {ref, result}.
handle_info Reception: The handle_info/2 callback in LiveView is what receives these messages. It's like a mailbox for your LiveView process that handles any messages not related to LiveView events.
Process Cleanup: After receiving the result, you typically call Process.demonitor(ref, [:flush]) to clean up process monitoring and prevent receiving a :DOWN message when the task process exits.
This pattern creates a complete async lifecycle:
 
Start async task →
Task runs independently →
Task completes and sends message →
handle_info receives message →
Update LiveView state
In the example I provided, there's a clear separation of concerns:
 
Task.async(fn -> api() end) starts the async operation
handle_info({ref, result}, socket) receives and processes the result
The LiveView remains responsive throughout the entire operation
This pattern is what makes LiveView capable of handling concurrent operations while maintaining a responsive UI for users.
________________________
 
<source>
 
defmodule AppWeb.Sandbox do
  use AppWeb, :live_view
 
   def mount(_params, _session, socket) do
     # Schedule the API call to happen after mount
    if connected?(socket), do: Process.send_after(self(), :fetch_data, 0)
     {:ok, assign(socket, data: nil)}
   end
   end
  def render(assigns) do


     # AppWeb.PageLive.loop("xxx")
  def handle_info(:fetch_data, socket) do
     ~H"""
     # Launch API call in separate process
    SANDBOX
    Task.async(fn -> api() end)
     {:noreply, socket}
  end


     <.button phx-click="change-text" value={@message_item}>CLICK ME</.button>
  def handle_info({ref, result}, socket) when is_reference(ref) do
     # Clean up the DOWN message
    Process.demonitor(ref, [:flush])
    {:noreply, assign(socket, data: result)}
  end


     <div>
  def api do
     Req.request(url: "https://api.github.com/repos/wojtekmach/req")
  end


      <%= @message_item %>
  def render(assigns) do
    </div>
    ~H"""
    SANDBOX
    <%= if @data, do: inspect(@data) %>
     """
     """
   end
   end
Line 97: Line 144:
</source>
</source>


___________________


elixir :  1.16.2


Erl:  20




# PostGres
Created Saturday 18 February 2023






Launch [PostGres](#PostGres) from Terminal
------------------------------------------
sudo -u postgres psql


<source>


Set User Permission
-------------------
<https://commandprompt.com/education/how-to-create-a-superuser-in-postgresql/>


ALTER USER user_name WITH PASSWORD 'new_password';
defmodule Heading do
  use Phoenix.Component
  def head(assigns) do
  ~H"""


    <p>Hello, <%= @headings %>!</p>


Run Post Gres Commands
  """
----------------------
  end
end






sudo -i -u postgres
defmodule AppWeb.PageLive do
psql
  use AppWeb, :live_view 
  import Heading
  def mount(_params, _session, socket)  do
{:ok, socket} 
  end


  def headings do
    ["one","two","three"]
  end


Create and Delete Database
  def render(assigns) do
  ~H"""


CREATE DATABASE name;
    <Heading.head  headings={headings()} />
DROP DATABASE name;
## View user list


\du


View all databases
  """
  end
end


postgres=# \l


Change databases
</source>


postgres=# \c    name-of-database


View Tables
https://app.studyraid.com/en/read/11967/381872/component-communication


After you change databases and are selected on one of them do this:
https://thepugautomatic.com/2020/08/communicating-between-liveviews-on-the-same-page/


\dt
<source>
from(a in Article,
  order_by: [desc: a.inserted_at],  # Assuming 'inserted_at' is the timestamp for when the article was created or updated
  limit: 8
)
|> Repo.all()
</source>


SELECT * FROM mytablename;


EMPTY TABLE
<source>
DELETE FROM name-of-table


CREATE TABLE
defmodule AppWeb.Landing do
  use AppWeb, :live_view
  alias App.Testbeds
  alias App.Testbeds.Testbed
  alias App.Repo  # Ensure to alias Repo
  import Ecto.Query  # Import Ecto.Query to work with queries


CREATE TABLE table_name(
  def mount(_params, _session, socket) do
  column1 datatype,
    # Default to ordering by name
  column2 datatype,
    testbeds = Repo.all(from(t in Testbed, order_by: t.name))
  column3 datatype,
  .....
  columnN datatype,
  PRIMARY KEY( one or more columns )
);


INSERT
    # Assign the testbeds to the socket for rendering
    {:ok, assign(socket, testbeds: testbeds, sort: :name)}
  end


INSERT INTO ITEMS(id, name) VALUES(1,'htvjbjbgni');     Strings are single quote
  def render(assigns) do
    ~H"""
     <h1>Hello World! YAY</h1>


    <div>
      <!-- Buttons to toggle sorting -->
      <button phx-click="sort_by_name">Sort by Name</button>
      <button phx-click="sort_by_updated_at">Sort by Updated By</button>


    </div>


    <ul>
      <%= for testbed <- @testbeds do %>
        <li><%= testbed.name %></li>
      <% end %>
    </ul>
    """
  end


  def handle_event("sort_by_name", _params, socket) do
    testbeds = Repo.all(from(t in Testbed, order_by: t.name))
    {:noreply, assign(socket, testbeds: testbeds, sort: :name)}
  end
  def handle_event("sort_by_updated_at", _params, socket) do
    testbeds = Repo.all(from(t in Testbed, order_by: t.inserted_at))
    {:noreply, assign(socket, testbeds: testbeds, sort: :inserted_at)}
  end


==Roadmap==


<source>
end
defmodule AppWeb.ItemsLive do
    use AppWeb, :live_view 
    def mount(_params, _session, socket)  do
      {:ok, assign(socket, switches: ["grindy","sonic","ogre"])} 
    end
   
    def handle_event("run",_params,socket) do 
       
        IO.inspect "DOWNLOAD VIA SSH"


        {:noreply, redirect(socket, to: "/download")}
</source>


    end
_______________________________________________________
 


    def render(assigns) do
* Create a form with a post request.
       
* Create the route needed for that request
        ~H"""
* Create the controller needed for that route


        <ul>
        <%= for switch <- @switches do %>
        <a phx-submit="click" href="/download">  <li> <%= switch %>  </li> </a>


        <%end%>
==Form==
        </ul>
<pre>
        """  
<.form action={~p"/todos"} method="post" class="mt-10 space-y-8">
     end
  <div>
    <label for="contact_name" class="block text-sm font-semibold leading-6 text-zinc-800">
      Name
    </label>
    <input
      id="todo"
      type="text"
      name="todo"
      class="mt-2 block w-full rounded-lg text-zinc-900 focus:ring-0 sm:text-sm sm:leading-6 border-zinc-300 focus:border-zinc-400"
    />
  </div>
 
  <button
    type="submit"
    class="rounded-lg bg-zinc-900 hover:bg-zinc-700 py-2 px-3 text-sm font-semibold leading-6 text-white active:text-white/80"
  >
     Save Contact
  </button>
 
</.form>
</pre>




end
==Controller==
</source>
<pre>
# In your TodoController
defmodule AppWeb.TodoController do
  use AppWeb, :controller


  # Action to handle the form submission (POST request)
  def create(conn, %{"todo" => todo_name}) do
    # Here you can handle the form data (e.g., save it to the database)
    IO.puts("Received Todo: #{todo_name}")


==Notes==
    # For now, let's just redirect to the home page
List of Switches and the option to select the one needed.
    conn
Button to download JSON file.
    |> put_flash(:info, "Todo saved!")
Drag and Drop Palette to upload JSON file.
    |> redirect(to: "/")
  end
end
</pre>


After JSON file is uploaded a button titled "Make Active" is available.
________________________________________________________


# CRUD Forms


To understand CRUD forms you will need to understand the following concepts.




* Ecto Schema
* Ecto Context
* Form Basics
* form/1
* simple_form/1
* changesets


We are not going to go in order.






The Context provides CRUD functions for the chosen data schema. Phoenix contexts define the interface through which other parts of the codebase can interact with the app layer.




_______________________
* Create routes
* create page with form (submit get request)


(ecto schema, context, changesets)




Line 242: Line 350:




https://medium.com/@fraiha26/pushing-events-to-livecomponents-from-js-hooks-w-phoenix-elixir-d5544d4c3dfa


Changeset notes and forms


NOTE: Use case statements for pattern matching
_______________________________________________________
____________________________________________
In that case, you really should switch it to prod mode. Instead of doing all that above with the "config/secret.exs" file, create the ".env" in the project root, then add DotenvParser.load_file(".env") to "config/runtime.exs" as the first line inside the if config_env() == :prod do. In the .env file, set the DATABASE_URL and SECRET_KEY_BASE. Then run it as MIX_ENV=prod mix phx.server.


Create a basic <.form></form> without error handling
<source>
<source>
<.form :let={f} for={@changeset}  action={~p"/items"} >


  <.input field={f[:title]} type="text" label="Title" />
 
  <.button>Save Item</.button>
 
</.form>
</source>


With error handling looks like this:
  def get_titles do
    urls = [
      "https://cointelegraph.com/rss",
      "https://chaski.huffpost.com/us/auto/vertical/us-news"
      # Add more RSS feed URLs here
    ]


    urls
    |> Enum.map(fn url ->
      with {:ok, feed} <- ElixirRss.fetch_and_parse(url) do
        Enum.map(feed.entries, & &1.title)
      else
        {:error, reason} -> {:error, reason}
      end
    end)
  end


<source>


</source>


<.form :let={f} for={@changeset}  action={~p"/items"} >


  <.error :if={@changeset.action}>
<source>
    Oops, something went wrong! Please check the errors below.
defmodule App do
  </.error>


  def get_titles() do
    data = [
      "https://cointelegraph.com/rss",
      "https://chaski.huffpost.com/us/auto/vertical/us-news"
      # Add more RSS feed URLs here
    ]


  <.input field={f[:title]} type="text" label="Title" />
    data
 
    |> Enum.map(fn url ->
  <.button>Save Item</.button>
      case ElixirRss.fetch_and_parse(url) do
 
        {:ok, feed} -> feed.entries |> Enum.map(fn entry -> entry end)
</.form>
        {:error, reason} -> {:error, reason}
 
      end
</source>
    end)
    |> Enum.map(fn entry_list ->
      entry_list
      |> Enum.map(fn entry -> entry.title end)
    end)
  end


Create the routes for render and post to endpoint.
  def hello do
    rss_feeds = [
      "https://cointelegraph.com/rss",
      "https://chaski.huffpost.com/us/auto/vertical/us-news"
      # Add more RSS feed URLs here
    ]


 
    rss_feeds
 
    |> Enum.map(fn url ->
The controller for the page render needs to pass in the changeset via ```Items.change_item(%Item{})```
      case ElixirRss.fetch_and_parse(url) do
 
        {:ok, feed} -> feed.entries |> Enum.map(fn entry -> entry end)
 
        {:error, reason} -> {:error, reason}
<source>
      end
  def new(conn, _params) do
    end)
    changeset = Items.change_item(%Item{})
    render(conn, :new, changeset: changeset)
   end
   end
end


</source>
</source>




Post route without error handling
<source>
<source>
defmodule AppWeb.PageLive do
  use AppWeb, :live_view
  def mount(_params, _session, socket) do
    if connected?(socket) do
      Phoenix.PubSub.subscribe(App.PubSub, "message")
    end
    {:ok, assign(socket, message_item: "different")}
  end
  def handle_info({:pubsub_transmission, text}, socket) do
    IO.inspect "pub sub working"
    {:noreply, assign(socket, message_item: text)}
  end


   def create(conn, %{"item" => item_params}) do
   def loop(data) do
    case Items.create_item(item_params) do
    IO.inspect data
       {:ok, item} ->
    if data > 0 do
        conn
      IO.inspect "notify"
        |> put_flash(:info, "Item created successfully.")
      # run code to compare files, loop. If different run pubsub code below
        |> redirect(to: ~p"/items/#{item}")
      :timer.sleep(2000)
    end
       loop(data-1)
 
    end
    if data <= 0 do
 
      Phoenix.PubSub.broadcast(App.PubSub, "message", {:pubsub_transmission, "data"})
      "done"
    end
   end
   end


</source>


With error handling


<source>
   def handle_event("change-text", _params, socket) do
   def create(conn, %{"item" => item_params}) do
     spawn(fn -> loop(3) end)
     case Items.create_item(item_params) do
    {:noreply, assign(socket, message_item: "START" )}
      {:ok, item} ->
  end
        conn
  def render(assigns) do
        |> put_flash(:info, "Item created successfully.")
 
        |> redirect(to: ~p"/items/#{item}")
    # AppWeb.PageLive.loop("xxx")
    ~H"""
    SANDBOX
 
    <.button phx-click="change-text" value={@message_item}>CLICK ME</.button>
 
    <div>


       {:error, %Ecto.Changeset{} = changeset} ->
       <%= @message_item %>
        render(conn, :new, changeset: changeset)
    </div>
     end
     """
   end
   end
end


</source>
</source>




_____________________________________________
elixir :  1.16.2
https://medium.com/@vincentlin/phoenix-very-simple-form-c4b121697fcb
 
Erl:   20




'''home.html.heex'''
# PostGres
<source>
Created Saturday 18 February 2023
<form action="/submit" method="post">
 
  <label for="name">Name:</label><br>
  <input type="text" id="name" name="name"><br>
  <input type="hidden" name="_csrf_token" value={@csrf_token} />
  <input type="submit" value="Submit">
</form>


</source>


Launch [PostGres](#PostGres) from Terminal
------------------------------------------
sudo -u postgres psql


page_controller.ex


<source>
Set User Permission
-------------------
<https://commandprompt.com/education/how-to-create-a-superuser-in-postgresql/>


ALTER USER user_name WITH PASSWORD 'new_password';


defmodule AppWeb.PageController do
  use AppWeb, :controller


  def home(conn, _params) do
Run Post Gres Commands
    # The home page is often custom made,
----------------------


    csrf_token = Plug.CSRFProtection.get_csrf_token()
    render(conn, :home, csrf_token: csrf_token)
  end




  def submit(conn, _params) do
sudo -i -u postgres
    # The home page is often custom made,
psql
    IO.inspect(_params)
    IO.inspect "___________________________"
    csrf_token = Plug.CSRFProtection.get_csrf_token()
    render(conn, :home, csrf_token: csrf_token)
  end


end


Create and Delete Database


</source>
CREATE DATABASE name;
DROP DATABASE name;
## View user list


\du


View all databases


router.ex
postgres=# \l


<source>
Change databases
  scope "/", AppWeb do
    pipe_through :browser
    post "/submit", PageController, :submit
    get "/", PageController, :home


  end
postgres=# \c    name-of-database
</source>


View Tables


After you change databases and are selected on one of them do this:


\dt


SELECT * FROM mytablename;


EMPTY TABLE
DELETE FROM name-of-table


CREATE TABLE


CREATE TABLE table_name(
  column1 datatype,
  column2 datatype,
  column3 datatype,
  .....
  columnN datatype,
  PRIMARY KEY( one or more columns )
);


INSERT


INSERT INTO ITEMS(id, name) VALUES(1,'htvjbjbgni');    Strings are single quote




Line 400: Line 555:




==Roadmap==


<source>
defmodule AppWeb.ItemsLive do
    use AppWeb, :live_view 
    def mount(_params, _session, socket)  do
      {:ok, assign(socket, switches: ["grindy","sonic","ogre"])} 
    end
   
    def handle_event("run",_params,socket) do 
       
        IO.inspect "DOWNLOAD VIA SSH"


Ecto.Adapters.SQL.query(Repo, "select * from artists where id=1")
        {:noreply, redirect(socket, to: "/download")}


    end
 


    def render(assigns) do
       
        ~H"""


Ecto cheatsheet:  https://hexdocs.pm/ecto/crud.html
        <ul>
__________________________________________________________________
        <%= for switch <- @switches do %>
Most programming languages give you the ability to set global state by creating globally scoped variables. Elixir does not do this. Instead, Elixir provides a tool called a "processes" to persist  state. Elixir applications are composed of these "processes".
        <a phx-submit="click" href="/download">  <li> <%= switch %>  </li> </a>


Processes by themselves are complicated to use, thus Elixir provides abstractions to make working with processes easier. One of these abstractions is called a GenServer.
        <%end%>
        </ul>
        """
    end


GenServers are not only used to store state, they are also used to invoke work independent of the main application process.


==Creating a GenServer to Store State==
end
</source>


To Create a GenServer, start by creating a basic Module that ''uses'' the GenServer module (image below)


<source>
==Notes==
List of Switches and the option to select the one needed.
Button to download JSON file.
Drag and Drop Palette to upload JSON file.


defmodule App do 
After JSON file is uploaded a button titled "Make Active" is available.
  use GenServer




end


</source>


We now need to describe the actions we want to perform. Being that this is used to store state, we will use basic CRUD actions: Create, Read, Update, Delete. To begins, empty functions describing each action is created.


<source>


defmodule App do
  def create do


  end
  def read do


  end


  def update do 


  end


  def delete do


  end


end




</source>


==What now?==


To fill in the functions with the appropriate code we must first have a basic understanding of how GenServers are structured.


GenServers are composed of the GenServer module invoking callbacks. The callbacks have built in names that you, as a developer have to use.


These callbacks are named:


* init
* handle_cast
* handle_call




To demonstrate, here is an example :


<source>


{:ok, pid} = GenServer.start(ModuleNameOfGenServer, %{})
</source>


The above function named "start" calls the function named init in the Module below.
https://medium.com/@fraiha26/pushing-events-to-livecomponents-from-js-hooks-w-phoenix-elixir-d5544d4c3dfa
In GenServer syntax the name of the GenServer functions always invoke callbacks that are determined by the language.  


Changeset notes and forms
NOTE: Use case statements for pattern matching
____________________________________________
Create a basic <.form></form> without error handling
<source>
<source>
<.form :let={f} for={@changeset}  action={~p"/items"} >


defmodule ModuleNameOfGenServer do
  <.input field={f[:title]} type="text" label="Title" />
   # 1
    
   def init(state \\ %{}) do
   <.button>Save Item</.button>
    {:ok, state}
 
  end
</.form>
</source>
</source>
With error handling looks like this:




This is a list of the GenServer functions and their corresponding callbacks.
<source>


* GenServer.start/1  -->      init/1
* GenServer.call/3  -->      def handle_call/3
* GenServer.cast/2  -->      handle_cast/2


<.form :let={f} for={@changeset}  action={~p"/items"} >


== Start and Call ==
  <.error :if={@changeset.action}>
    Oops, something went wrong! Please check the errors below.
  </.error>


Call and Cast can be used to perform the same operations. The


__________________________________________________________
  <.input field={f[:title]} type="text" label="Title" />
Genserver timed events
 
  <.button>Save Item</.button>
 
</.form>


<source>
</source>
defmodule Go do
  use GenServer


  def go() do
Create the routes for render and post to endpoint.
    GenServer.start_link(__MODULE__, [])
  end


  def init(list) do
    :timer.apply_interval(1000, __MODULE__, :add, [self(), "weeeee"])
    {:ok, list}
  end


  def view(pid) do
    GenServer.call(pid, :view)
  end


  def handle_call(:view, _from, list) do
The controller for the page render needs to pass in the changeset via ```Items.change_item(%Item{})```
    {:reply, list, list}
  end


  def add(pid, new_item) do
    GenServer.call(pid, {:item, new_item})
  end


   def handle_call({:item, new_item}, _from, state) do
<source>
     result = [new_item] ++ state
   def new(conn, _params) do
     {:reply, result, result}
     changeset = Items.change_item(%Item{})
     render(conn, :new, changeset: changeset)
   end
   end
end
 
</source>
</source>




Post route without error handling
<source>
<source>
defmodule ShoppingList do
  use GenServer


   def start_link() do
   def create(conn, %{"item" => item_params}) do
     GenServer.start_link(ShoppingList, [])
     case Items.create_item(item_params) do
      {:ok, item} ->
        conn
        |> put_flash(:info, "Item created successfully.")
        |> redirect(to: ~p"/items/#{item}")
    end
   end
   end


  def init(list) do
</source>
    {:ok, list}
  end


  def view(pid) do
With error handling
    GenServer.call(pid, :view)
  end


   def handle_call(:view, _from, list) do
<source>
     {:reply, list, list}
   def create(conn, %{"item" => item_params}) do
  end
     case Items.create_item(item_params) do
      {:ok, item} ->
        conn
        |> put_flash(:info, "Item created successfully.")
        |> redirect(to: ~p"/items/#{item}")


  def add(pid, new_item) do
      {:error, %Ecto.Changeset{} = changeset} ->
    GenServer.call(pid, {:item, new_item})
        render(conn, :new, changeset: changeset)
    end
   end
   end


  def handle_call({:item, new_item}, _from, state) do
    result = [new_item] ++ state
    {:reply, result, result}
  end
end
</source>
</source>




https://blog.appsignal.com/2018/06/12/elixir-alchemy-deconstructing-genservers.html
_____________________________________________
https://medium.com/@vincentlin/phoenix-very-simple-form-c4b121697fcb
 
 
'''home.html.heex'''
<source>
<source>
 
<form action="/submit" method="post">
defmodule KeyValue do
   <label for="name">Name:</label><br>
   # 1
   <input type="text" id="name" name="name"><br>
  def init(state \\ %{}) do
   <input type="hidden" name="_csrf_token" value={@csrf_token} />
    {:ok, state}
   <input type="submit" value="Submit">
   end
</form>
   # 2
  def handle_cast({:put, key, value}, state) do
    {:noreply, Map.put(state, key, value)}
   end
  # 3
  def handle_call({:get, key}, _from, state) do
    {:reply, Map.fetch!(state, key), state}
  end
end
 
# {:ok, pid} = GenServer.start(KeyValue, %{})
# GenServer.cast(pid, {:put, :foo, "bar"})
# GenServer.call(pid, {:get, :foo})


</source>
</source>




page_controller.ex


<source>




defmodule AppWeb.PageController do
  use AppWeb, :controller
  def home(conn, _params) do
    # The home page is often custom made,
    csrf_token = Plug.CSRFProtection.get_csrf_token()
    render(conn, :home, csrf_token: csrf_token)
  end




Send messages via these functions wrapped in module functions .You can look at these are functions used to fire "GenServer Events".
  def submit(conn, _params) do
    # The home page is often custom made,
    IO.inspect(_params)
    IO.inspect "___________________________"
    csrf_token = Plug.CSRFProtection.get_csrf_token()
    render(conn, :home, csrf_token: csrf_token)
  end


* start_link
end
* GenServer.call
* GenServer.cast


The corresponding server functions that reply are:


* init
</source>
* handle__call
* handle_cast
*


These can be looked at as "listeners" - in JavaScript parlance.
__________________________________________


If you have experience with programming languages other than Elixir the idea of GenServers may be very foreign and require a strong context shift to learn. This document is intended to simplify the concept.


* What: GenServers are simply a way to hold state and/or invoke actions based on incoming events.
router.ex
* when
 
* where
<source>
* why
  scope "/", AppWeb do
* how
    pipe_through :browser
    post "/submit", PageController, :submit
    get "/", PageController, :home
 
  end
</source>




Line 629: Line 775:




___________________________________________
GenServer timer example


<source>
defmodule Timer do
  use GenServer


  def init(_) do
    :timer.send_interval(1000, :xyz)
    {:ok, 0}
  end


  def handle_info(:xyz, state) do
    IO.inspect(state)
    {:noreply, state + 1}
  end
end


</source>




<source>
[
  {
    data:{
        creationTime,
        publishDate,
    }


    featured:{
Ecto.Adapters.SQL.query(Repo, "select * from artists where id=1")
        setAmount,
        items:[]
    },


    children:{
        setAmount,
        items:[]
    },
    links:{
        setAmount,
        items:[]
    }


  }


]
Ecto cheatsheet:  https://hexdocs.pm/ecto/crud.html
__________________________________________________________________
Most programming languages give you the ability to set global state by creating globally scoped variables. Elixir does not do this. Instead, Elixir provides a tool called a "processes" to persist  state. Elixir applications are composed of these "processes".


</source>
Processes by themselves are complicated to use, thus Elixir provides abstractions to make working with processes easier. One of these abstractions is called a GenServer.


jason.encode / jason.decode
GenServers are not only used to store state, they are also used to invoke work independent of the main application process.  


<source>
==Creating a GenServer to Store State==


  def run(num) do
To Create a GenServer, start by creating a basic Module that ''uses'' the GenServer module (image below)


    Enum.each(0..99999, fn(_x) ->
<source>
      task = Task.async(fn -> num + _x end)
    IO.inspect Task.await(task)
  end)
</source>


Enum.map(feed.entries, fn x -> %{url: x.url, title: x.title} end)
defmodule App do 
  use GenServer


App is split:


App that captures RSS in UI and lets admin select stories. THere are settings to determine the number of FEATURED,CHILD and LINK stories.
end
News categories are horizontal and scrollable.
This exports a json doc with all RSS data for each story.
The


</source>


We now need to describe the actions we want to perform. Being that this is used to store state, we will use basic CRUD actions: Create, Read, Update, Delete. To begins, empty functions describing each action is created.


<source>


App that reads the json data. This app creates tables and is the front end.
defmodule App do
  def create do


  end
  def read do


  end


  def update do 


https://stephenbussey.com/tags/elixir.html
  end
 
  def delete do


<pre>
  end
defmodule AppWeb.GroupLive do
  use AppWeb, :live_view
  alias App.TestBeds
  alias App.Groups


  def mount(_params, _session, socket) do
end
    {:ok, assign(socket, testbeds: TestBeds.list_testbeds(), groups: Groups.list_groups())}
  end


  def handle_event("select-group", params, socket)  do 
    group = App.Groups.get_group!(params["group-id"])
    testbed = App.TestBeds.get_test_bed!(params["testbed_id"])
    App.TestBeds.update_test_bed(testbed, %{group_id: group.id})
    {:noreply, socket}
  end


  def updated_groups(testbed_id)  do 
</source>
    testbed = App.TestBeds.get_test_bed!(testbed_id)
    groups = App.Groups.list_groups
    tail = Enum.filter(groups, fn(item)-> item.id !== testbed.group_id end)
    head = Enum.filter(groups, fn(item)-> item.id == testbed.group_id end)
            |>Enum.at(0)
    [head | tail]
  end


  def render(assigns) do
==What now?==
    ~H"""
        <%= for testbed <- Enum.sort_by(@testbeds , &("#{&1.name}"), :asc)do %>
        <div class="grid grid-cols-4 gap-4 p-8 rounded shadow bg-white">
          <h2> Name</h2>
          <div class="name"><%= testbed.name %> </div>
            <h2> Group</h2>
          <div>
            <form phx-change="select-group" name="testbed_id"  >
              <input type="hidden" name="testbed_id" value={testbed.id}/>
              <select id="group-selection"  name="group-id" class="">
                <%= for group <- updated_groups(testbed.id) do %>
                  <%= if group !== nil do %>
                      <option value= {"#{group.id}"}><%=group.name %></option>
                      <%else%>
                      <option value= {"Unassigned"}>NONE</option>
                  <% end %>


                <% end %>
To fill in the functions with the appropriate code we must first have a basic understanding of how GenServers are structured.
              </select>
            </form>
          </div>
        </div>
      <% end %>
    """
  end
end
</pre>


GenServers are composed of the GenServer module invoking callbacks. The callbacks have built in names that you, as a developer have to use.


== Developer Notes ==
These callbacks are named:
===Groups Feature ===
To complete this feature do these things.
* Run through the steps in [http://elixirblocks.com/index.php?title=Create_Foreign_Key_Relationship_Between_Ecto_Tables  | this document] to create Group table.
* Groups have these fields  '''name: string''', '''description: string''' and '''color: string''' 
* Add unique constraint in migration file:  


<pre>
* init
  def change do
* handle_cast
    create unique_index(:groups, [:name])
* handle_call
  end
</pre>


* Port over Group LiveView code (temporarily placed at discussion page).
* On completion '''possibly''', create a Group named '''No Group''' and set all Testbeds to it.


To demonstrate, here is an example :


<source>


{:ok, pid} = GenServer.start(ModuleNameOfGenServer, %{})
</source>


________
The above function named "start" calls the function named init in the Module below.
https://blog.appsignal.com/2022/10/11/phoenix-liveview-018-new-special-html-attributes.html
In GenServer syntax the name of the GenServer functions always invoke callbacks that are determined by the language.  
https://blixtdev.com/whats-new-phoenix-1-7/
https://github.com/devato/inertia_phoenix
https://phoenixonrails.com/blog/you-can-stop-using-form-for-in-phoenix


<source>


https://samuelmullen.com/articles/phoenix_templates_rendering_and_layouts
defmodule ModuleNameOfGenServer do
  # 1
  def init(state \\ %{}) do
    {:ok, state}
  end
</source>


WorkFlow of Todo App in Both LiveView and DeadView.


Note:
This is a list of the GenServer functions and their corresponding callbacks.


Create foreign key data.
* GenServer.start/1  -->      init/1
Create live view. List all testbeds and form to assign each a group by group-name.
* GenServer.call/3  -->      def handle_call/3
* GenServer.cast/2  -->      handle_cast/2


Repeat in dead view


== Start and Call ==


Mental model and data flows
Call and Cast can be used to perform the same operations. The


__________________________________________________________
Genserver timed events


home.html.heex
<source>
<a href="/data" name="we are the world" >  click me</a>
defmodule Go do
  use GenServer


<%= @message %>
  def go() do
    GenServer.start_link(__MODULE__, [])
  end


  def init(list) do
    :timer.apply_interval(1000, __MODULE__, :add, [self(), "weeeee"])
    {:ok, list}
  end


page controller
  def view(pid) do
    GenServer.call(pid, :view)
  end


<pre>
  def handle_call(:view, _from, list) do
defmodule AppWeb.PageController do
    {:reply, list, list}
   use AppWeb, :controller
   end


   def home(conn, _params) do
   def add(pid, new_item) do
     IO.inspect _params
     GenServer.call(pid, {:item, new_item})
    render(conn, :home, message: "Hello")
   end
   end


  def handle_call({:item, new_item}, _from, state) do
    result = [new_item] ++ state
    {:reply, result, result}
  end
end
end
</pre>
</source>


get "/:yay", PageController, :index


get "/", PageController, :index
<source>
defmodule ShoppingList do
  use GenServer


* Explain control flow of "Dead Views" and the Generator HTML Control flow.
  def start_link() do
* Proper Way to use Modal
    GenServer.start_link(ShoppingList, [])
* How to give Focus to Fields in Modal (or other forms fields)
  end
*
*
* How to Create Database Seed Data.
* How to structure code so controller code is in one file and HTML Heex is in another
* How to Use Generators in a Real World Project
* How to Work with Database Data via Basic Commands and Custom Change sets.
* How to Render Database Data to LiveView (via Context Commands).
* How to Create a Chat using PubSub.
* How to Create Event Handlers in LiveView
* How to Create a Dynamic Route from Scratch.
* How to Use Elixirs Template language
* How to Work with Modal
* How to Use CSS From Scratch (Without bundler)
* How to use JS hooks


  def init(list) do
    {:ok, list}
  end


  def view(pid) do
    GenServer.call(pid, :view)
  end


______________________
  def handle_call(:view, _from, list) do
https://studioindie.co/blog/heex-guide/
    {:reply, list, list}
  end


<source>
  def add(pid, new_item) do
get "/", PageController, :some-atom    #This is the route. some-atom is the controller name
    GenServer.call(pid, {:item, new_item})
</source>
  end


 
   def handle_call({:item, new_item}, _from, state) do
 
     result = [new_item] ++ state
<source>
     {:reply, result, result}
defmodule AppWeb.PageController do
  use AppWeb, :controller
 
   def some-atom(conn, _params) do                 # controller name
    # The home page is often custom made,
     # so skip the default app layout.
     render(conn, :some-atom, layout: false)      # :some-atom is template name
   end
   end
end
end
</source>
</source>


Controllers page_html.ex contains


https://blog.appsignal.com/2018/06/12/elixir-alchemy-deconstructing-genservers.html
<source>
<source>
defmodule AppWeb.PageHTML do
  use AppWeb, :html


   embed_templates "page_html/*"
defmodule KeyValue do
   # 1
  def init(state \\ %{}) do
    {:ok, state}
  end
  # 2
  def handle_cast({:put, key, value}, state) do
    {:noreply, Map.put(state, key, value)}
  end
  # 3
  def handle_call({:get, key}, _from, state) do
    {:reply, Map.fetch!(state, key), state}
  end
end
end
# {:ok, pid} = GenServer.start(KeyValue, %{})
# GenServer.cast(pid, {:put, :foo, "bar"})
# GenServer.call(pid, {:get, :foo})
</source>
</source>


'''page_html''' is the name of the folder in '''Controllers/page_html'''






Controllers/page_html/some-atom.html.heex  (this is the template)






Send messages via these functions wrapped in module functions .You can look at these are functions used to fire "GenServer Events".


__________________________________________
* start_link
Set all association with preload:
* GenServer.call
* GenServer.cast


<source>
The corresponding server functions that reply are:
  def list_groups do
 
    Repo.all(Group)
* init
    |> Repo.preload([:testbeds])
* handle__call
  end
* handle_cast
</source>
*


=== Insert Child into Parent ===
These can be looked at as "listeners" - in JavaScript parlance.
Example:
__________________________________________


<source>
If you have experience with programming languages other than Elixir the idea of GenServers may be very foreign and require a strong context shift to learn. This document is intended to simplify the concept.
App.get_group!(2) # arg is id
|> Ecto.build_assoc(:testbeds)
|> Repo.insert()


Or
* What: GenServers are simply a way to hold state and/or invoke actions based on incoming events.
* when
* where
* why
* how


group = App.get_group!(2) # arg is id


testbeds = Ecto.build_assoc(group, :testbeds)


App.Group.create_group(testbeds)






</source>


<source>
group = App.Groups.get_group!(1)
thing = Ecto.build_assoc(group, :testbeds, name: "DUMB")
alias App.{Repo}
Repo.insert(thing)




</source>
___________________________________________
GenServer timer example


<source>
defmodule Timer do
  use GenServer


  def init(_) do
    :timer.send_interval(1000, :xyz)
    {:ok, 0}
  end


==Example==
  def handle_info(:xyz, state) do
 
    IO.inspect(state)
<source>
    {:noreply, state + 1}
avatar = %Avatar{nick_name: "Elixir", pic_url: "http://elixir-lang.org/images/logo.png"}
  end
user = %User{name: "John Doe", email: "john.doe@example.com", avatar: avatar}
end


</source>
</source>


https://www.reddit.com/r/elixir/comments/oh318k/is_there_a_way_to_avoid_ectoassociationnotloaded/h4mmcfi/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button
# Create Group Table


<source>
<source>
mix phx.gen.html Groups Group groups name:string 
[
</source>
  {
    data:{
        creationTime,
        publishDate,
    }
 
    featured:{
        setAmount,
        items:[]
    },


Set the resources /
    children:{
        setAmount,
        items:[]
    },
    links:{
        setAmount,
        items:[]
    }


<source>
  }
mix ecto.migrate
</source>


]


For testbeds create a migration file and add this field:  group_id:references:groups
</source>
 
jason.encode / jason.decode


<source>
<source>
def change do
 
     alter table(:testbeds) do    # customize to your code
  def run(num) do
       add :group_id, references(:groups)
 
     end
     Enum.each(0..99999, fn(_x) ->
   end
       task = Task.async(fn -> num + _x end)
     IO.inspect Task.await(task)
   end)
</source>
</source>


Enum.map(feed.entries, fn x -> %{url: x.url, title: x.title} end)


Update Groups Schema (Don't forget alias / has_many)
App is split:


<source>
App that captures RSS in UI and lets admin select stories. THere are settings to determine the number of FEATURED,CHILD and LINK stories.
News categories are horizontal and scrollable.
This exports a json doc with all RSS data for each story.
The


defmodule App.Groups.Group do
use Ecto.Schema
import Ecto.Changeset
alias App.Testbeds.Testbed  # Alias!


schema "posts" do
field :body, :string
field :title, :string
has_many :testbeds, Testbed    # Has many


timestamps()
end


@doc false
App that reads the json data. This app creates tables and is the front end.
def changeset(post, attrs) do
 
post
|> cast(attrs, [:title, :body])
|> validate_required([:title, :body])
end
end


</source>


TESTBEDS Schema


<source>
https://stephenbussey.com/tags/elixir.html
defmodule App.Testbeds.Testbed do
  use Ecto.Schema
  import Ecto.Changeset
  alias App.Groups.Group      # Alias
  schema "testbeds" do
    field :name, :string
    belongs_to :group, Group    # Belongs to
    timestamps()
  end


   @doc false
<pre>
   def changeset(testbed, attrs) do
defmodule AppWeb.GroupLive do
     testbed
   use AppWeb, :live_view
    |> cast(attrs, [:name])
  alias App.TestBeds
    |> validate_required([:name])
  alias App.Groups
 
   def mount(_params, _session, socket) do
     {:ok, assign(socket, testbeds: TestBeds.list_testbeds(), groups: Groups.list_groups())}
   end
   end
end


  def handle_event("select-group", params, socket)  do 
    group = App.Groups.get_group!(params["group-id"])
    testbed = App.TestBeds.get_test_bed!(params["testbed_id"])
    App.TestBeds.update_test_bed(testbed, %{group_id: group.id})
    {:noreply, socket}
  end


</source>
  def updated_groups(testbed_id)  do 
_____________________________________
    testbed = App.TestBeds.get_test_bed!(testbed_id)
Side notes.
    groups = App.Groups.list_groups
    tail = Enum.filter(groups, fn(item)-> item.id !== testbed.group_id end)
    head = Enum.filter(groups, fn(item)-> item.id == testbed.group_id end)
            |>Enum.at(0)
    [head | tail]
  end


defmodule App.Repo.Migrations.CreateTestbeds do
  def render(assigns) do
  use Ecto.Migration
    ~H"""
        <%= for testbed <- Enum.sort_by(@testbeds , &("#{&1.name}"), :asc)do %>
        <div class="grid grid-cols-4 gap-4 p-8 rounded shadow bg-white">
          <h2> Name</h2>
          <div class="name"><%= testbed.name %> </div>
            <h2> Group</h2>
          <div>
            <form phx-change="select-group" name="testbed_id"  >
              <input type="hidden" name="testbed_id" value={testbed.id}/>
              <select id="group-selection"  name="group-id" class="">
                <%= for group <- updated_groups(testbed.id) do %>
                  <%= if group !== nil do %>
                      <option value= {"#{group.id}"}><%=group.name %></option>
                      <%else%>
                      <option value= {"Unassigned"}>NONE</option>
                  <% end %>


  def change do
                <% end %>
    create table(:testbeds) do
              </select>
      add :name, :string
            </form>
      add :group_id, references(:groups, on_delete: :nothing)
          </div>
 
        </div>
      timestamps()
      <% end %>
    end
     """
 
     create index(:testbeds, [:group_id])
   end
   end
end
end
</pre>




________________________________
== Developer Notes ==
===Groups Feature ===
To complete this feature do these things.
* Run through the steps in [http://elixirblocks.com/index.php?title=Create_Foreign_Key_Relationship_Between_Ecto_Tables  | this document] to create Group table.
* Groups have these fields  '''name: string''', '''description: string''' and '''color: string''' 
* Add unique constraint in migration file:


mix phx.gen.context Comments Comment comments name:string content:text post_id:references:posts
<pre>
  def change do
    create unique_index(:groups, [:name])
  end
</pre>


* Port over Group LiveView code (temporarily placed at discussion page).
* On completion '''possibly''', create a Group named '''No Group''' and set all Testbeds to it.




mix phx.gen.html Testbeds Testbed testbeds name:string  group_id:references:groups


https://graffino.com/web-development/first-steps-in-elixir-and-phoenix-create-a-blog-prototype


________
https://blog.appsignal.com/2022/10/11/phoenix-liveview-018-new-special-html-attributes.html
https://blixtdev.com/whats-new-phoenix-1-7/
https://github.com/devato/inertia_phoenix
https://phoenixonrails.com/blog/you-can-stop-using-form-for-in-phoenix


https://blog.logrocket.com/getting-started-ecto-phoenix/


https://serokell.io/blog/ecto-guide-for-beginners
https://samuelmullen.com/articles/phoenix_templates_rendering_and_layouts


WorkFlow of Todo App in Both LiveView and DeadView.


mix phx.gen.html Testbeds Testbed testbeds name:string
Note:


mix phx.gen.html Groups Group groups name:string
Create foreign key data.
Create live view. List all testbeds and form to assign each a group by group-name.


Create migration for
Repeat in dead view




Mental model and data flows


mix ecto.gen.migration add_testbed_group_reference


home.html.heex
<a href="/data" name="we are the world" >  click me</a>


Migration:
<%= @message %>


<source>
  def change do
    alter table(:testbeds) do    # customize to your code
      add :group_id, references(:groups)
    end
  end


</source>
page controller


<pre>
defmodule AppWeb.PageController do
  use AppWeb, :controller


Manually Change:
  def home(conn, _params) do
 
    IO.inspect _params
<source>
    render(conn, :home, message: "Hello")
 
   end
defmodule App.Group do
   use Ecto.Schema


  schema "groups" do
    field :title, :string
    field :tagline, :string
    has_many :testbeds, App.Testbed
  end
end
end
</pre>


</source>
get "/:yay", PageController, :index
 
get "/", PageController, :index
 
* Explain control flow of "Dead Views" and the Generator HTML Control flow.
* Proper Way to use Modal
* How to give Focus to Fields in Modal (or other forms fields)
*
*
* How to Create Database Seed Data.
* How to structure code so controller code is in one file and HTML Heex is in another
* How to Use Generators in a Real World Project
* How to Work with Database Data via Basic Commands and Custom Change sets.
* How to Render Database Data to LiveView (via Context Commands).
* How to Create a Chat using PubSub.
* How to Create Event Handlers in LiveView
* How to Create a Dynamic Route from Scratch.
* How to Use Elixirs Template language
* How to Work with Modal
* How to Use CSS From Scratch (Without bundler)
* How to use JS hooks


testbeds


<source>
defmodule App.Repo.Migrations.AddTextbedGroups do
  use Ecto.Migration
  def change do
    create table(:testbeds) do
      add :name, :string
      add :group_id, references(:groups)
    end
  end
end
</source>


______________________
https://studioindie.co/blog/heex-guide/


<source>
<source>
# lib/app/testbeds.ex
get "/", PageController, :some-atom    #This is the route. some-atom is the controller name
</source>
 


defmodule App.Testbed do
  use Ecto.Schema


   schema "testbedss" do
<source>
     field :name, :string
defmodule AppWeb.PageController do
     belongs_to :group, App.Group
   use AppWeb, :controller
 
  def some-atom(conn, _params) do                 # controller name
     # The home page is often custom made,
     # so skip the default app layout.
    render(conn, :some-atom, layout: false)      # :some-atom is template name
   end
   end
end
end
</source>


Controllers page_html.ex contains


<source>
defmodule AppWeb.PageHTML do
  use AppWeb, :html
  embed_templates "page_html/*"
end
</source>
</source>


'''page_html''' is the name of the folder in '''Controllers/page_html'''






Controllers/page_html/some-atom.html.heex  (this is the template)








__________________________________________
Set all association with preload:


<source>
  def list_groups do
    Repo.all(Group)
    |> Repo.preload([:testbeds])
  end
</source>


_____________________
=== Insert Child into Parent ===
 
Example:
%App.TestBeds.TestBed{
  __meta__: #Ecto.Schema.Metadata<:loaded, "testbeds">,
  id: 1,
  developer: "None",
  name: "sdfsd",
  owner: "sdfsddf",
  note: "sdsfsddf",
  status: "Available",
  url: "sdf",
  version: "ddf",
  manager: "sf",
  inserted_at: ~N[2023-08-21 13:45:39],
  updated_at: ~N[2023-08-21 13:45:44]
}
________________________________________________________________---


<source>
<source>
defmodule AppWeb.SendLive do
App.get_group!(2) # arg is id
  use AppWeb, :live_view
|> Ecto.build_assoc(:testbeds)
 
|> Repo.insert()
  def mount(_params, _session, socket) do
    {:ok, socket}
  end
 
 
  def handle_event("send", %{"text" => text}, socket) do


    AppWeb.Endpoint.broadcast(topic, "message", text)
Or
    {:noreply, socket}
  end
 


  defp topic do
group = App.get_group!(2) # arg is id
    "chat"
  end


testbeds = Ecto.build_assoc(group, :testbeds)


  def render(assigns)do 
App.Group.create_group(testbeds)
  ~H"""
<div>
  <h1>Send Message</h1>


  <form phx-submit="send">
    <input type="text" name="text" />
    <button type="submit">Send</button>
  </form>
</div>


"""
  end
 
end


</source>
</source>
______________________________________________________________


<source>
<source>
group = App.Groups.get_group!(1)
thing = Ecto.build_assoc(group, :testbeds, name: "DUMB")
alias App.{Repo}
Repo.insert(thing)


defmodule AppWeb.ReceiveLive do
  use AppWeb, :live_view
 
  def mount(_params, _session, socket) do
    if connected?(socket) do
      AppWeb.Endpoint.subscribe(topic)
    end
    {:ok, assign(socket, messages: "")}
  end
 
  def handle_info(%{event: "message", payload: message}, socket) do
    IO.inspect message
    {:noreply, assign(socket, messages: message)}
  end
 


  defp topic do
</source>
    "chat"
 
  end


  def render(assigns)do 
  ~H"""
<div>
  <h1>ChatLive</h1>
  <%= @messages %>


==Example==


</div>
<source>
avatar = %Avatar{nick_name: "Elixir", pic_url: "http://elixir-lang.org/images/logo.png"}
user = %User{name: "John Doe", email: "john.doe@example.com", avatar: avatar}


"""
</source>
  end
 
 
https://www.reddit.com/r/elixir/comments/oh318k/is_there_a_way_to_avoid_ectoassociationnotloaded/h4mmcfi/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button
end
 
# Create Group Table


<source>
mix phx.gen.html Groups Group groups name:string 
</source>
</source>
______________________________________________________________
 
When you hover over a list of items this hook finds items with the same name and sets the opacity of all others to 0. You see all the items similar to the one you are hovering over.
Set the resources /


<source>
<source>
mix ecto.migrate
</source>


TestbedVersionHover: {
For testbeds create a migration file and add this field:   group_id:references:groups
// https://jsfiddle.net/IrvinDominin/7K2Z3/
            mounted() {


                let rows = document.getElementsByClassName("testbed-version")
<source>
                // this.ele = element
def change do
// rows = this.ele;
    alter table(:testbeds) do    # customize to your code
               
      add :group_id, references(:groups)
for (var i = 0; i < rows.length; i++) {
    end
rows[i].onmouseenter = function (event) {
  end
for (var j = 0; j < rows.length; j++) {
</source>
let preversion = this.textContent;
let version = preversion.slice(0,5)
let compareVersion = rows[j].textContent.slice(0,5)
if (compareVersion === version) continue
rows[j].className += " other";
}
};
rows[i].onmouseleave = function (event) {
var hovers = document.getElementsByClassName('other');
var len = hovers.length;
for (var j = 0; j < len; j++) {
hovers[0].className = hovers[0].className.replace(/\sother(\s|$)/, '');
}
};
}


                // this.ele.addEventListener("click", (e) => {
                //    console.log(e.target)
                   
                // });


            }
Update Groups Schema (Don't forget alias / has_many)
        }
</source>


<source>


* How to Create Database Seed Data.
defmodule App.Groups.Group do
* How to structure code so controller code is in one file and HTML Heex is in another
use Ecto.Schema
* How to Use Generators in a Real World Project
import Ecto.Changeset
* How to Work with Database Data via Basic Commands and Custom Change sets.
alias App.Testbeds.Testbed  # Alias!
* How to Render Database Data to LiveView (via Context Commands).
* How to Create a Chat using PubSub.
* How to Create Event Handlers in LiveView
* How to Create a Dynamic Route from Scratch.
* How to Use Elixirs Template language
* How to Work with Modal
* How to Use CSS From Scratch (Without bundler)
* How to use JS hooks
* Dissect Table Component and Update it to Order List in Reverse.


Selenium
schema "posts" do
https://www.builder.io/blog/debug-nodejs
field :body, :string
field :title, :string
has_many :testbeds, Testbed    # Has many


https://marketplace.visualstudio.com/items?itemName=ms-vscode.live-server#running-the-extension
timestamps()
end


This was added recently (see microsoft/vscode#109276).
@doc false
    1. Open the palette (Ctrl + Shift + P)
def changeset(post, attrs) do
    2. Select "Simple Browser: Preview"
post
    3. Enter web address
|> cast(attrs, [:title, :body])
 
|> validate_required([:title, :body])
_______________________
end
end


</source>


TESTBEDS Schema


<source>
<source>
const {By, Key, Builder} = require("selenium-webdriver");
defmodule App.Testbeds.Testbed do
const assert = require("assert")
  use Ecto.Schema
  import Ecto.Changeset
  alias App.Groups.Group      # Alias
  schema "testbeds" do
    field :name, :string
    belongs_to :group, Group    # Belongs to
    timestamps()
  end
 
  @doc false
  def changeset(testbed, attrs) do
    testbed
    |> cast(attrs, [:name])
    |> validate_required([:name])
  end
end


require("chromedriver");


async function test_case(){
</source>
  let driver = await new Builder().forBrowser("chrome").build();
_____________________________________
  await driver.get("sie");
Side notes.


defmodule App.Repo.Migrations.CreateTestbeds do
  use Ecto.Migration


   await driver.findElement(By.name('password')).clear();
   def change do
  await driver.findElement(By.name('identification')).clear();
    create table(:testbeds) do
  await driver.findElement(By.name('identification')).sendKeys("bill");
      add :name, :string
  await driver.findElement(By.name('password')).sendKeys("password", Key.RETURN);
      add :group_id, references(:groups, on_delete: :nothing)


      timestamps()
    end


    create index(:testbeds, [:group_id])
  end
end




setTimeout(()=>{
________________________________
  driver.findElement(By.className("req_lan_license")).click();
 
},5000)
mix phx.gen.context Comments Comment comments name:string content:text post_id:references:posts




</source>


mix phx.gen.html Testbeds Testbed testbeds name:string  group_id:references:groups


https://graffino.com/web-development/first-steps-in-elixir-and-phoenix-create-a-blog-prototype




<source>
https://blog.logrocket.com/getting-started-ecto-phoenix/
defmodule AppWeb.PageLive do


  use AppWeb, :live_view 
https://serokell.io/blog/ecto-guide-for-beginners
  alias App.Testbeds
  def mount(_params, _session, socket)do 
    {:ok, assign(socket, testbeds: Testbeds.list_testbeds())} 
  end


    def button_event("invoke_editing") do
    %JS{}
    |> JS.remove_class("active", to: "#editing_button")
    |> JS.add_class("hidden", to: "#editing_button")
    |> JS.remove_class("hidden", to: "#preview_button")
    |> JS.add_class("active", to: "#preview_button")
    |> JS.remove_class("active", to: "#preview")
    |> JS.add_class("hidden", to: "#preview")
    |> JS.remove_class("hidden", to: "#editing")
    |> JS.add_class("active", to: "#editing")
  end


  def button_event("invoke_preview") do
mix phx.gen.html Testbeds Testbed testbeds name:string
    %JS{}
 
    |> JS.remove_class("active", to: "#preview_button")
mix phx.gen.html Groups Group groups name:string
    |> JS.add_class("hidden", to: "#preview_button")
 
    |> JS.remove_class("hidden", to: "#editing_button")
Create migration for
    |> JS.add_class("active", to: "#editing_button")
 
    |> JS.remove_class("active", to: "#editing")
    |> JS.add_class("hidden", to: "#editing")
    |> JS.remove_class("hidden", to: "#preview")
    |> JS.add_class("active", to: "#preview")


  end


  def handle_event("send", params, socket) do
mix ecto.gen.migration add_testbed_group_reference
    hide_modal("notes-modal-0")
    {:noreply, socket}
  end


 


  def render(assigns) do 
Migration:
    ~H"""


    <%= for testbed <- @testbeds do %>
<source>
<.modal id={"notes-modal-#{testbed.id}"}>
   def change do
    <.button phx-click={button_event("invoke_editing")} id="editing_button" class="hidden">
    alter table(:testbeds) do   # customize to your code
      Preview
       add :group_id, references(:groups)
    </.button>
     end
    <.button phx-click={button_event("invoke_preview")} id="preview_button">
      Edit Me
    </.button>
    <div id="editing"> PREVIEWING THE WORK</div>
    <form phx-submit = "send"   id="preview" class="hidden">
      <textarea value = {testbed.note}></textarea>
      <.button type="submit" phx-click={hide_modal("notes-modal-#{testbed.id}")}>SAVE WORK</.button>
    </form>
</.modal>
 
    <%= if testbed.note do %>
       <div phx-click={show_modal("notes-modal-#{testbed.id}")}>READ ME  ...<%= testbed.note %></div>
     <% end %>
 
    <% end %>
     
      """
   end
   end


end
</source>




Manually Change:


<source>


defmodule App.Group do
  use Ecto.Schema
  schema "groups" do
    field :title, :string
    field :tagline, :string
    has_many :testbeds, App.Testbed
  end
end


</source>
</source>


testbeds


<source>
defmodule App.Repo.Migrations.AddTextbedGroups do
  use Ecto.Migration
  def change do
    create table(:testbeds) do
      add :name, :string
      add :group_id, references(:groups)
    end
  end
end
</source>




<source>
<source>
import {HfInference} from "@huggingface/inference";
# lib/app/testbeds.ex
import dotenv from "dotenv";
dotenv.config()


const HF_ACCESS_TOKEN = process.env.HF_ACCESS_TOKEN
defmodule App.Testbed do
 
  use Ecto.Schema
const hf = new HfInference(HF_ACCESS_TOKEN);


  schema "testbedss" do
    field :name, :string
    belongs_to :group, App.Group
  end
end


/*______Uncomment for this example______________________


const model = "nlpconnect/vit-gpt2-image-captioning";
</source>
const imageURL = "https://i.imgur.com/lTvb7Et.png";


const response = await fetch(imageURL);
const imageBlob = await response.blob();


const result  = await hf.imageToText({
data: imageBlob,
model: model,
});
_______________________________________________________*/






/*________Another example ___________________*/


const result = await hf.summarization({
model: 'facebook/bart-large-cnn',
inputs: "The role of a dumb man is to get smarter oogy bookie boo",
parameters:{
max_length: 100
}
});








_____________________


console.log(result);
%App.TestBeds.TestBed{
</source>
  __meta__: #Ecto.Schema.Metadata<:loaded, "testbeds">,
  id: 1,
  developer: "None",
  name: "sdfsd",
  owner: "sdfsddf",
  note: "sdsfsddf",
  status: "Available",
  url: "sdf",
  version: "ddf",
  manager: "sf",
  inserted_at: ~N[2023-08-21 13:45:39],
  updated_at: ~N[2023-08-21 13:45:44]
}
________________________________________________________________---


<source>
defmodule AppWeb.SendLive do
  use AppWeb, :live_view
 
  def mount(_params, _session, socket) do
    {:ok, socket}
  end
 
 
  def handle_event("send", %{"text" => text}, socket) do


    AppWeb.Endpoint.broadcast(topic, "message", text)
    {:noreply, socket}
  end
 


  defp topic do
    "chat"
  end




  def render(assigns)do 
  ~H"""
<div>
  <h1>Send Message</h1>


  <form phx-submit="send">
    <input type="text" name="text" />
    <button type="submit">Send</button>
  </form>
</div>


"""
  end
 
end


</source>
______________________________________________________________


<source>


________________________________________
defmodule AppWeb.ReceiveLive do
 
  use AppWeb, :live_view
 
 
 
  def mount(_params, _session, socket) do
https://fly.io/phoenix-files/sdeb-toggling-element/
    if connected?(socket) do
https://www.youtube.com/watch?v=vBgZvQapqhs
      AppWeb.Endpoint.subscribe(topic)
    end
    {:ok, assign(socket, messages: "")}
  end
 
  def handle_info(%{event: "message", payload: message}, socket) do
    IO.inspect message
    {:noreply, assign(socket, messages: message)}
  end
 


https://blog.testdouble.com/posts/2022-11-28-how-to-use-javascript-with-phoenix-liveview/
  defp topic do
    "chat"
  end


https://hexdocs.pm/phoenix_live_view/0.19.3/Phoenix.Component.html
  def render(assigns)do 
_______________________________________
  ~H"""
https://www.smashingmagazine.com/2011/10/quick-look-math-animations-javascript/
<div>
* Depth first traversal
  <h1>ChatLive</h1>
* Breadth first traversal
  <%= @messages %>


https://dev.to/codesphere/10-algorithms-every-developer-should-learn-3lnm


https://www.youtube.com/watch?v=fPz40W9mfCg
</div>


__________________________________
"""
  end
 
end


</source>
______________________________________________________________
When you hover over a list of items this hook finds items with the same name and sets the opacity of all others to 0. You see all the items similar to the one you are hovering over.


<source>
<source>


defmodule AppWeb.GroupLive do
TestbedVersionHover: {
  use AppWeb, :live_view
// https://jsfiddle.net/IrvinDominin/7K2Z3/
  alias App.TestBeds
            mounted() {
  alias App.Groups


                let rows = document.getElementsByClassName("testbed-version")
                // this.ele = element
// rows = this.ele;
               
for (var i = 0; i < rows.length; i++) {
rows[i].onmouseenter = function (event) {
for (var j = 0; j < rows.length; j++) {
let preversion = this.textContent;
let version = preversion.slice(0,5)
let compareVersion = rows[j].textContent.slice(0,5)
if (compareVersion === version) continue
rows[j].className += " other";
}
};
rows[i].onmouseleave = function (event) {
var hovers = document.getElementsByClassName('other');
var len = hovers.length;
for (var j = 0; j < len; j++) {
hovers[0].className = hovers[0].className.replace(/\sother(\s|$)/, '');
}
};
}


  def mount(_params, _session, socket) do
                // this.ele.addEventListener("click", (e) => {
     {:ok, assign(socket, testbeds: TestBeds.list_testbeds(), groups: Groups.list_groups())}
                //     console.log(e.target)
  end
                   
 
                // });
 
  def handle_event("select-group", params, socket) do 
       
      IO.inspect params
      {:noreply, socket}
  end
 


  def render(assigns) do
            }
    ~H"""
        }
</source>


 


  <div class="grid grid-columns-2 grid-flow-col gap-4">
* How to Create Database Seed Data.
      <%= for testbed <- @testbeds do %>
* How to structure code so controller code is in one file and HTML Heex is in another
        <div class="column-1">
* How to Use Generators in a Real World Project
          <div class="name"><%= testbed.name %></div>
* How to Work with Database Data via Basic Commands and Custom Change sets.
        </div>
* How to Render Database Data to LiveView (via Context Commands).
        <div class="column-2">   
* How to Create a Chat using PubSub.
            <form phx-change="select-group"  >
* How to Create Event Handlers in LiveView
              <input type="hidden" name="testbed_id" value={testbed.id}/>
* How to Create a Dynamic Route from Scratch.
              <select id="group-selection"  name="group-id" >
* How to Use Elixirs Template language
                <%= for group <- @groups do %>
* How to Work with Modal
                  <option value= {"#{group.id}"}  ><%=group.name %></option>
* How to Use CSS From Scratch (Without bundler)
                <% end %>
* How to use JS hooks
              </select>
* Dissect Table Component and Update it to Order List in Reverse.
            </form>
        </div>
      <% end %>
  </div>


    """
Selenium
  end
https://www.builder.io/blog/debug-nodejs
end
</source>


https://marketplace.visualstudio.com/items?itemName=ms-vscode.live-server#running-the-extension


____________________________
This was added recently (see microsoft/vscode#109276).
<source>
    1. Open the palette (Ctrl + Shift + P)
defmodule AppWeb.GroupLive do
    2. Select "Simple Browser: Preview"
  use AppWeb, :live_view
    3. Enter web address
  alias App.TestBeds
  alias App.Groups


_______________________


  def mount(_params, _session, socket) do


    {:ok, assign(socket, testbeds: TestBeds.list_testbeds(), groups: Groups.list_groups(), messages: [])}
  end


<source>
const {By, Key, Builder} = require("selenium-webdriver");
const assert = require("assert")


  def handle_event("select-group", params, socket) do 
require("chromedriver");


async function test_case(){
  let driver = await new Builder().forBrowser("chrome").build();
  await driver.get("sie");
  await driver.findElement(By.name('password')).clear();
  await driver.findElement(By.name('identification')).clear();
  await driver.findElement(By.name('identification')).sendKeys("bill");
  await driver.findElement(By.name('password')).sendKeys("password", Key.RETURN);


      group = App.Groups.get_group!(params["group-id"])
      # IO.inspect group
      testbed = App.TestBeds.get_test_bed!(params["testbed_id"])
   
      App.TestBeds.update_test_bed(testbed, %{group_id: group.id})
       
   
      {:noreply, socket}
  end




  def updated_groups(testbed_id)  do 
      groups = App.Groups.list_groups
      tail = Enum.filter(groups, fn(item)-> item.id !== testbed_id end)
      head = Enum.filter(groups, fn(item)-> item.id == testbed_id end)
              |>Enum.at(0)
    [head | tail]
       
  end


  # def handle_event("groups", params, socket)do 
  setTimeout(()=>{
#      selected_testbed = %{name: "my testbed name", id: 3}
  driver.findElement(By.className("req_lan_license")).click();
#      groups = App.Groups.list_groups
  },5000)
#      tail = Enum.filter(groups, fn(item)-> item.id !== selected_testbed.id end)
#      head = Enum.filter(groups, fn(item)-> item.id == selected_testbed.id end)
  #              |>Enum.at(0)
       
   
   
#      # result = Enum.filter([1, 2, 3], fn x -> x.id !== 1 end)
#      IO.inspect [head | tail]
       
#      #
#      {:noreply, socket, assigns.message:}
#  end




</source>




  def render(assigns) do
   
    ~H"""
      <%!-- <div phx-click = "groups">CLICK ME</div> --%>
 




      <%= for testbed <- @testbeds do %>
<source>
        <div class="grid grid-cols-4 gap-4 p-8 rounded shadow bg-white">
defmodule AppWeb.PageLive do
          <h2> Name</h2>
          <div class="name"><%= testbed.name %> </div>
            <h2> Group</h2>
          <div>


            <form phx-change="select-group" name="testbed_id" >
  use AppWeb, :live_view  
              <input type="hidden" name="testbed_id" value={testbed.id}/>
  alias App.Testbeds
              <select id="group-selection"  name="group-id" class="">
  def mount(_params, _session, socket)do  
       
    {:ok, assign(socket, testbeds: Testbeds.list_testbeds())}  
                <%= for group <- updated_groups(testbed.id) do %>
                  <option value= {"#{group.id}"} ><%=group.name %></option>
                  <!-- Write code to reorder with testbed.group_id matching group_id -->
                <% end %>
              </select>
            </form>
          </div>
        </div>
      <% end %>
 
    """
   end
   end
end
</source>


    def button_event("invoke_editing") do
    %JS{}
    |> JS.remove_class("active", to: "#editing_button")
    |> JS.add_class("hidden", to: "#editing_button")
    |> JS.remove_class("hidden", to: "#preview_button")
    |> JS.add_class("active", to: "#preview_button")
    |> JS.remove_class("active", to: "#preview")
    |> JS.add_class("hidden", to: "#preview")
    |> JS.remove_class("hidden", to: "#editing")
    |> JS.add_class("active", to: "#editing")
  end


  def button_event("invoke_preview") do
    %JS{}
    |> JS.remove_class("active", to: "#preview_button")
    |> JS.add_class("hidden", to: "#preview_button")
    |> JS.remove_class("hidden", to: "#editing_button")
    |> JS.add_class("active", to: "#editing_button")
    |> JS.remove_class("active", to: "#editing")
    |> JS.add_class("hidden", to: "#editing")
    |> JS.remove_class("hidden", to: "#preview")
    |> JS.add_class("active", to: "#preview")


  end


_____________________________________
  def handle_event("send", params, socket) do
    hide_modal("notes-modal-0")
    {:noreply, socket}
  end


 


<source>
   def render(assigns) do  
defmodule AppWeb.PageLive do
    ~H"""
  use AppWeb, :live_view 
    alias App.Testbeds
   alias App.Groups
  def mount(_params, _session, socketdo
    {:ok, assign(socket, testbeds: Testbeds.list_testbeds(), groups: Groups.list_groups())}
  end


  def handle_event("select-group", params, socket) do 
    <%= for testbed <- @testbeds do %>
      group = App.Groups.get_group!(params["group-id"])
<.modal id={"notes-modal-#{testbed.id}"}>
      testbed = App.Testbeds.get_testbed!(params["testbed_id"])
    <.button phx-click={button_event("invoke_editing")} id="editing_button" class="hidden">
      result = App.Testbeds.update_testbed(testbed, %{group_id: group.id})
      Preview
       IO.inspect result
    </.button>
        
    <.button phx-click={button_event("invoke_preview")} id="preview_button">
      {:noreply, socket}
      Edit Me
    </.button>
    <div id="editing"> PREVIEWING THE WORK</div>
    <form phx-submit = "send"  id="preview" class="hidden">
      <textarea value = {testbed.note}></textarea>
      <.button type="submit" phx-click={hide_modal("notes-modal-#{testbed.id}")}>SAVE WORK</.button>
    </form>
</.modal>
 
    <%= if testbed.note do %>
      <div phx-click={show_modal("notes-modal-#{testbed.id}")}>READ ME  ...<%= testbed.note %></div>
    <% end %>
 
    <% end %>
        
       """
   end
   end


    def updated_groups(testbed_id)  do 
end
      testbed = App.Testbeds.get_testbed!(testbed_id)
 
      groups = App.Groups.list_groups
 
      tail = Enum.filter(groups, fn(item)-> item.id !== testbed.group_id end)
 
      head = Enum.filter(groups, fn(item)-> item.id == testbed.group_id end)
 
              |>Enum.at(0)
 
</source>
      [head | tail]
 
    end
 
 
 
<source>
import {HfInference} from "@huggingface/inference";
import dotenv from "dotenv";
dotenv.config()


const HF_ACCESS_TOKEN = process.env.HF_ACCESS_TOKEN


  def render(assigns) do
const hf = new HfInference(HF_ACCESS_TOKEN);
   
    ~H"""


       <%= for testbed <- @testbeds do %>
 
         <div class="grid grid-cols-4 gap-4 p-8 rounded shadow bg-white">
/*______Uncomment for this example______________________
           <h2> Name</h2>
 
           <div class="name"><%= testbed.name %> </div>
const model = "nlpconnect/vit-gpt2-image-captioning";
             <h2> Group</h2>
const imageURL = "https://i.imgur.com/lTvb7Et.png";
 
const response = await fetch(imageURL);
const imageBlob = await response.blob();
 
const result  = await hf.imageToText({
data: imageBlob,
model: model,
});
 
_______________________________________________________*/
 
 
 
/*________Another example ___________________*/
 
const result = await hf.summarization({
model: 'facebook/bart-large-cnn',
inputs: "The role of a dumb man is to get smarter oogy bookie boo",
parameters:{
max_length: 100
}
});
 
 
 
 
 
console.log(result);
</source>
 
 
 
 
 
 
 
 
 
 
________________________________________
 
 
 
https://fly.io/phoenix-files/sdeb-toggling-element/
https://www.youtube.com/watch?v=vBgZvQapqhs
 
https://blog.testdouble.com/posts/2022-11-28-how-to-use-javascript-with-phoenix-liveview/
 
https://hexdocs.pm/phoenix_live_view/0.19.3/Phoenix.Component.html
_______________________________________
https://www.smashingmagazine.com/2011/10/quick-look-math-animations-javascript/
* Depth first traversal
* Breadth first traversal
 
https://dev.to/codesphere/10-algorithms-every-developer-should-learn-3lnm
 
https://www.youtube.com/watch?v=fPz40W9mfCg
 
__________________________________
 
 
<source>
 
defmodule AppWeb.GroupLive do
  use AppWeb, :live_view
  alias App.TestBeds
  alias App.Groups
 
 
  def mount(_params, _session, socket) do
    {:ok, assign(socket, testbeds: TestBeds.list_testbeds(), groups: Groups.list_groups())}
  end
 
 
  def handle_event("select-group", params, socket)  do 
       
      IO.inspect params
      {:noreply, socket}
  end
 
 
  def render(assigns) do
    ~H"""
 
 
 
  <div class="grid grid-columns-2 grid-flow-col gap-4">
      <%= for testbed <- @testbeds do %>
        <div class="column-1">
          <div class="name"><%= testbed.name %></div>
        </div>
        <div class="column-2">   
            <form phx-change="select-group"  >
              <input type="hidden" name="testbed_id" value={testbed.id}/>
              <select id="group-selection"  name="group-id" >
                <%= for group <- @groups do %>
                  <option value= {"#{group.id}"}  ><%=group.name %></option>
                <% end %>
              </select>
            </form>
        </div>
      <% end %>
  </div>
 
    """
  end
end
</source>
 
 
____________________________
<source>
defmodule AppWeb.GroupLive do
  use AppWeb, :live_view
  alias App.TestBeds
  alias App.Groups
 
 
  def mount(_params, _session, socket) do
 
    {:ok, assign(socket, testbeds: TestBeds.list_testbeds(), groups: Groups.list_groups(), messages: [])}
  end
 
 
  def handle_event("select-group", params, socket)  do 
 
 
      group = App.Groups.get_group!(params["group-id"])
      # IO.inspect group
      testbed = App.TestBeds.get_test_bed!(params["testbed_id"])
   
      App.TestBeds.update_test_bed(testbed, %{group_id: group.id})
       
   
      {:noreply, socket}
  end
 
 
  def updated_groups(testbed_id)  do 
      groups = App.Groups.list_groups
      tail = Enum.filter(groups, fn(item)-> item.id !== testbed_id end)
      head = Enum.filter(groups, fn(item)-> item.id == testbed_id end)
              |>Enum.at(0)
    [head | tail]
       
  end
 
# def handle_event("groups", params, socket)do 
#      selected_testbed = %{name: "my testbed name", id: 3}
#      groups = App.Groups.list_groups
#      tail = Enum.filter(groups, fn(item)-> item.id !== selected_testbed.id end)
#      head = Enum.filter(groups, fn(item)-> item.id == selected_testbed.id end)
#              |>Enum.at(0)
       
#      # result = Enum.filter([1, 2, 3], fn x -> x.id !== 1 end)
#      IO.inspect [head | tail]
       
#      #
#      {:noreply, socket, assigns.message:}
#  end
 
 
 
 
  def render(assigns) do
   
    ~H"""
      <%!-- <div phx-click = "groups">CLICK ME</div> --%>
 
 
 
       <%= for testbed <- @testbeds do %>
         <div class="grid grid-cols-4 gap-4 p-8 rounded shadow bg-white">
           <h2> Name</h2>
           <div class="name"><%= testbed.name %> </div>
             <h2> Group</h2>
           <div>
           <div>


             <form phx-change="select-group" name="testbed_id"  >
             <form phx-change="select-group" name="testbed_id"  >
               <input type="hidden" name="testbed_id" value={testbed.id}/>
               <input type="hidden" name="testbed_id" value={testbed.id}/>
               <select id="group-selection"  name="group-id" class="">
               <select id="group-selection"  name="group-id" class="">
          
          
                 <%= for group <- updated_groups(testbed.id) do %>
                 <%= for group <- updated_groups(testbed.id) do %>
                  <option value= {"#{group.id}"}  ><%=group.name %></option>
                  <!-- Write code to reorder with testbed.group_id matching group_id -->
                <% end %>
              </select>
            </form>
          </div>
        </div>
      <% end %>
 
    """
  end
end
 
</source>
 
 
 
 
_____________________________________
 
 
<source>
defmodule AppWeb.PageLive do
  use AppWeb, :live_view 
    alias App.Testbeds
  alias App.Groups
  def mount(_params, _session, socket)  do
    {:ok, assign(socket, testbeds: Testbeds.list_testbeds(), groups: Groups.list_groups())}
  end
 
  def handle_event("select-group", params, socket)  do 
      group = App.Groups.get_group!(params["group-id"])
      testbed = App.Testbeds.get_testbed!(params["testbed_id"])
      result = App.Testbeds.update_testbed(testbed, %{group_id: group.id})
      IO.inspect result
     
      {:noreply, socket}
  end
 
    def updated_groups(testbed_id)  do 
      testbed = App.Testbeds.get_testbed!(testbed_id)
      groups = App.Groups.list_groups
      tail = Enum.filter(groups, fn(item)-> item.id !== testbed.group_id end)
      head = Enum.filter(groups, fn(item)-> item.id == testbed.group_id end)
              |>Enum.at(0)
      [head | tail]
    end
 
 
  def render(assigns) do
   
    ~H"""
 
      <%= for testbed <- @testbeds do %>
        <div class="grid grid-cols-4 gap-4 p-8 rounded shadow bg-white">
          <h2> Name</h2>
          <div class="name"><%= testbed.name %> </div>
            <h2> Group</h2>
          <div>
 
            <form phx-change="select-group" name="testbed_id"  >
              <input type="hidden" name="testbed_id" value={testbed.id}/>
              <select id="group-selection"  name="group-id" class="">
       
                <%= for group <- updated_groups(testbed.id) do %>
 
                  <%!-- <option value= {"#{group.id}"}><%=group.name %></option> --%>
                  <!-- Write code to reorder with testbed.group_id matching group_id -->
 
                  <%= if group !== nil do %>
                      <option value= {"#{group.id}"}><%=group.name %></option>
     
                  <% end %>
 
 
                <% end %>
              </select>
            </form>
          </div>
        </div>
      <% end %>
 
    """
  end
end
</source>
 
<source>
  has_many :testbeds, TestBed, on_delete: :restrict  # Has many
</source>
 
https://elixirforum.com/t/ecto-has-many-on-delete-protect/49550
 
https://hexdocs.pm/ecto/Ecto.Changeset.html#unique_constraint/3
 
____________________________________________________________________
 
 
== How to Create a Mix Project and Use It in Another Mix Project ==
 
To create and use a **Mix project** inside another **Mix project** in Elixir, follow the steps below:
 
=== 1. Create a New Mix Project ===
 
First, you need to create a new Mix project. Let's call this project `library_project`. Run the following command:
 
<pre>
mix new library_project --module LibraryProject
</pre>
 
This will create a new Elixir Mix project named `library_project`. The `--module` flag specifies the name of the module that will be created (in this case `LibraryProject`).
 
The directory structure of `library_project` will look like this:
 
<pre>
library_project/
  |_ lib/
      |_ library_project.ex
  |_ mix.exs
</pre>
 
You can then add some basic functionality inside the `library_project/lib/library_project.ex` file:
 
<pre>
defmodule LibraryProject do
  def hello do
    IO.puts "Hello from LibraryProject!"
  end
end
</pre>
 
Now, `library_project` is ready to be used by another Mix project.
 
=== 2. Create a Second Mix Project ===
 
Next, create the second Mix project where you want to use `library_project`. For example:
 
<pre>
mix new app_project --module AppProject
</pre>
 
This will create another Elixir project called `app_project`.
 
The directory structure for this project will look like:
 
<pre>
app_project/
  |_ lib/
      |_ app_project.ex
  |_ mix.exs
</pre>
 
=== 3. Add `library_project` as a Dependency in `app_project` ===
 
Now, you want to make `library_project` available in `app_project`. There are two main ways to do this:
 
==== Option 1: Local Dependency (via `path`) ====
 
If `library_project` is a local project (i.e., it's stored in a different directory), you can add it to the `mix.exs` of `app_project` as a local dependency by specifying the path to `library_project`.
 
Edit the `mix.exs` of `app_project`:
 
<pre>
defmodule AppProject.MixProject do
  use Mix.Project
 
  def project do
    [
      app: :app_project,
      version: "0.1.0",
      elixir: "~> 1.14",
      start_permanent: Mix.env() == :prod,
      deps: deps()
    ]
  end
 
  defp deps do
    [
      {:library_project, path: "../library_project"}  # Add the local dependency
    ]
  end
end
</pre>
 
Here, `{:library_project, path: "../library_project"}` tells Mix to use the local `library_project` located in the `../library_project` directory (relative to `app_project`).
 
After updating the `mix.exs`, run the following command:
 
<pre>
cd app_project
mix deps.get
</pre>
 
This will fetch the dependency and compile `library_project`.
 
==== Option 2: Publish and Fetch as a Hex Package ====
 
If you want to use `library_project` as a package in another project (for example, by publishing it to **Hex.pm**, Elixir’s package manager), you need to publish it to Hex and then add it as a dependency.
 
1. Publish `library_project` to Hex (if it's public or for your private use).
2. Add `library_project` to your `app_project`’s `mix.exs` dependencies:
 
<pre>
defp deps do
  [
    {:library_project, "~> 0.1.0"}  # Specify the published version from Hex
  ]
end
</pre>
 
Run `mix deps.get` to fetch the package from Hex.
 
=== 4. Use `library_project` in `app_project` ===
 
Now, you can use the functionality from `library_project` inside your `app_project`. For example, in `app_project/lib/app_project.ex`, you can call the function from `library_project`:
 
<pre>
defmodule AppProject do
  def start do
    IO.puts "Starting app_project!"
    LibraryProject.hello()  # Call the function from the library_project
  end
end
</pre>
 
=== 5. Run the App ===
 
Finally, you can run your `app_project`:
 
<pre>
cd app_project
mix run -e "AppProject.start()"
</pre>
 
This will print:
 
<pre>
Starting app_project!
Hello from LibraryProject!
</pre>
 
== Recap of the Process ==
1. **Create the `library_project`**: Use `mix new` to generate a library project.
2. **Create the `app_project`**: Create the second project where you will use the library.
3. **Add the `library_project` as a dependency**:
  - Use the `path` option to point to the local project or publish it to Hex for public access.
4. **Use the `library_project` in the `app_project`**: Call functions or use modules from the library inside the second project.
5. **Run the app**: Use `mix run` to execute the project.


                  <%!-- <option value= {"#{group.id}"}><%=group.name %></option> --%>
This way, you can reuse code from one Mix project in another by adding it as a dependency.
                  <!-- Write code to reorder with testbed.group_id matching group_id -->


                  <%= if group !== nil do %>
                      <option value= {"#{group.id}"}><%=group.name %></option>
     
                  <% end %>




                <% end %>
              </select>
            </form>
          </div>
        </div>
      <% end %>
 
    """
  end
end
</source>
<source>
  has_many :testbeds, TestBed, on_delete: :restrict  # Has many
</source>


https://elixirforum.com/t/ecto-has-many-on-delete-protect/49550
==z==
 
Overloaded functions are extremely common in Elixir applications. They are
https://hexdocs.pm/ecto/Ecto.Changeset.html#unique_constraint/3
regularly used for recursive functions and for changing behavior based on
configuration.

Latest revision as of 19:27, 25 June 2025

from(a in Alert, where: a.timed_alert == true) |> Repo.update_all(set: [enabled: false])


defmodule AppWeb.Sandbox do
  use AppWeb, :live_view

  def mount(_params, _session, socket) do
    {:ok, socket}
  end

  def handle_event("start_task", _params, socket) do
    Task.async(fn -> :hello_from_task end)
    {:noreply, socket}
  end

  def handle_info({ref, result}, socket) do
    IO.inspect"________________________________________________________________________________"
    IO.inspect({:task_result_received, ref, result})
    {:noreply, socket}
  end

  def handle_info(msg, socket) do
    # Optionally log or inspect the unhandled message if you're curious
    # IO.inspect({:unhandled_info, msg})
    {:noreply, socket}
  end

  def render(assigns) do
    ~H"""
    <div>
      <button phx-click="start_task">Start Task</button>
      <p>Barebones</p>
    </div>
    """
  end
end


defmodule AppWeb.Sandbox do
  use AppWeb, :live_view

  def mount(_params, _session, socket) do
    {:ok, socket}
  end

  def handle_event("start_task", _params, socket) do
    Task.async(fn -> :hello_from_task end)
    {:noreply, socket}
  end

  def handle_info({ref, result}, socket) do
    IO.inspect"________________________________________________________________________________"
    IO.inspect({:task_result_received, ref, result})
    {:noreply, socket}
  end

  def handle_info({:DOWN, ref, :process, pid, reason}, socket) do
    IO.inspect({:task_completed, ref, pid, reason})
    {:noreply, socket}
  end

  def render(assigns) do
    ~H"""
    <div>
      <button phx-click="start_task">Start Task</button>
      <p>Barebones</p>
    </div>
    """
  end
end


Async claude ai example


Explain to me the relationship between handle_info and making an async call

The relationship between handle_info and making async calls in Phoenix LiveView is fundamental to understanding how to handle non-blocking operations in your application.

When you make an asynchronous call in Elixir (like using Task.async), you're creating a separate process that runs independently from your LiveView process. This allows your LiveView to remain responsive while potentially time-consuming operations (like API calls) are happening.

Here's how they work together:

Async Operation Initiation: When you start an async operation using Task.async, it returns a reference to the task. Message Passing: When the async task completes, it automatically sends a message back to the process that created it. This message contains the task reference and the result: {ref, result}. handle_info Reception: The handle_info/2 callback in LiveView is what receives these messages. It's like a mailbox for your LiveView process that handles any messages not related to LiveView events. Process Cleanup: After receiving the result, you typically call Process.demonitor(ref, [:flush]) to clean up process monitoring and prevent receiving a :DOWN message when the task process exits. This pattern creates a complete async lifecycle:

Start async task → Task runs independently → Task completes and sends message → handle_info receives message → Update LiveView state In the example I provided, there's a clear separation of concerns:

Task.async(fn -> api() end) starts the async operation handle_info({ref, result}, socket) receives and processes the result The LiveView remains responsive throughout the entire operation This pattern is what makes LiveView capable of handling concurrent operations while maintaining a responsive UI for users. ________________________


defmodule AppWeb.Sandbox do
  use AppWeb, :live_view

  def mount(_params, _session, socket) do
    # Schedule the API call to happen after mount
    if connected?(socket), do: Process.send_after(self(), :fetch_data, 0)
    {:ok, assign(socket, data: nil)}
  end

  def handle_info(:fetch_data, socket) do
    # Launch API call in separate process
    Task.async(fn -> api() end)
    {:noreply, socket}
  end

  def handle_info({ref, result}, socket) when is_reference(ref) do
    # Clean up the DOWN message
    Process.demonitor(ref, [:flush])
    {:noreply, assign(socket, data: result)}
  end

  def api do
    Req.request(url: "https://api.github.com/repos/wojtekmach/req")
  end

  def render(assigns) do
    ~H"""
    SANDBOX
    <%= if @data, do: inspect(@data) %>
    """
  end
end

___________________







 defmodule Heading do
   use Phoenix.Component
   def head(assigns) do
	   ~H"""

	     <p>Hello, <%= @headings %>!</p>

	   """ 
   end
end



defmodule AppWeb.PageLive do
   use AppWeb, :live_view  
   import Heading
   def mount(_params, _session, socket)  do
	 {:ok, socket}  
   end

   def headings do 
    ["one","two","three"]
   end

   def render(assigns) do
	   ~H"""

	     <Heading.head  headings={headings()} />


	   """ 
   end
end


https://app.studyraid.com/en/read/11967/381872/component-communication

https://thepugautomatic.com/2020/08/communicating-between-liveviews-on-the-same-page/

from(a in Article,
  order_by: [desc: a.inserted_at],  # Assuming 'inserted_at' is the timestamp for when the article was created or updated
  limit: 8
)
|> Repo.all()



defmodule AppWeb.Landing do
  use AppWeb, :live_view
  alias App.Testbeds
  alias App.Testbeds.Testbed
  alias App.Repo  # Ensure to alias Repo
  import Ecto.Query  # Import Ecto.Query to work with queries

  def mount(_params, _session, socket) do
    # Default to ordering by name
    testbeds = Repo.all(from(t in Testbed, order_by: t.name))

    # Assign the testbeds to the socket for rendering
    {:ok, assign(socket, testbeds: testbeds, sort: :name)}
  end

  def render(assigns) do
    ~H"""
    <h1>Hello World! YAY</h1>

    <div>
      <!-- Buttons to toggle sorting -->
      <button phx-click="sort_by_name">Sort by Name</button>
      <button phx-click="sort_by_updated_at">Sort by Updated By</button>

    </div>

    <ul>
      <%= for testbed <- @testbeds do %>
        <li><%= testbed.name %></li>
      <% end %>
    </ul>
    """
  end

  def handle_event("sort_by_name", _params, socket) do
    testbeds = Repo.all(from(t in Testbed, order_by: t.name))
    {:noreply, assign(socket, testbeds: testbeds, sort: :name)}
  end

  def handle_event("sort_by_updated_at", _params, socket) do
    testbeds = Repo.all(from(t in Testbed, order_by: t.inserted_at))
    {:noreply, assign(socket, testbeds: testbeds, sort: :inserted_at)}
  end


end

_______________________________________________________

  • Create a form with a post request.
  • Create the route needed for that request
  • Create the controller needed for that route


Form

<.form action={~p"/todos"} method="post" class="mt-10 space-y-8">
  <div>
    <label for="contact_name" class="block text-sm font-semibold leading-6 text-zinc-800">
      Name
    </label>
    <input
      id="todo"
      type="text"
      name="todo"
      class="mt-2 block w-full rounded-lg text-zinc-900 focus:ring-0 sm:text-sm sm:leading-6 border-zinc-300 focus:border-zinc-400"
    />
  </div>

  <button
    type="submit"
    class="rounded-lg bg-zinc-900 hover:bg-zinc-700 py-2 px-3 text-sm font-semibold leading-6 text-white active:text-white/80"
  >
    Save Contact
  </button>

</.form>


Controller

# In your TodoController
defmodule AppWeb.TodoController do
  use AppWeb, :controller

  # Action to handle the form submission (POST request)
  def create(conn, %{"todo" => todo_name}) do
    # Here you can handle the form data (e.g., save it to the database)
    IO.puts("Received Todo: #{todo_name}")

    # For now, let's just redirect to the home page
    conn
    |> put_flash(:info, "Todo saved!")
    |> redirect(to: "/")
  end
end

________________________________________________________

  1. CRUD Forms

To understand CRUD forms you will need to understand the following concepts.


  • Ecto Schema
  • Ecto Context
  • Form Basics
  • form/1
  • simple_form/1
  • changesets

We are not going to go in order.


The Context provides CRUD functions for the chosen data schema. Phoenix contexts define the interface through which other parts of the codebase can interact with the app layer.


_______________________

  • Create routes
  • create page with form (submit get request)

(ecto schema, context, changesets)









_______________________________________________________ In that case, you really should switch it to prod mode. Instead of doing all that above with the "config/secret.exs" file, create the ".env" in the project root, then add DotenvParser.load_file(".env") to "config/runtime.exs" as the first line inside the if config_env() == :prod do. In the .env file, set the DATABASE_URL and SECRET_KEY_BASE. Then run it as MIX_ENV=prod mix phx.server.



  def get_titles do
    urls = [
      "https://cointelegraph.com/rss",
      "https://chaski.huffpost.com/us/auto/vertical/us-news"
      # Add more RSS feed URLs here
    ]

    urls
    |> Enum.map(fn url ->
      with {:ok, feed} <- ElixirRss.fetch_and_parse(url) do
        Enum.map(feed.entries, & &1.title)
      else
        {:error, reason} -> {:error, reason}
      end
    end)
  end


defmodule App do

  def get_titles() do
    data = [
      "https://cointelegraph.com/rss",
      "https://chaski.huffpost.com/us/auto/vertical/us-news"
      # Add more RSS feed URLs here
    ]

    data
    |> Enum.map(fn url ->
      case ElixirRss.fetch_and_parse(url) do
        {:ok, feed} -> feed.entries |> Enum.map(fn entry -> entry end)
        {:error, reason} -> {:error, reason}
      end
    end)
    |> Enum.map(fn entry_list ->
      entry_list
      |> Enum.map(fn entry -> entry.title end)
    end)
  end

  def hello do
    rss_feeds = [
      "https://cointelegraph.com/rss",
      "https://chaski.huffpost.com/us/auto/vertical/us-news"
      # Add more RSS feed URLs here
    ]

    rss_feeds
    |> Enum.map(fn url ->
      case ElixirRss.fetch_and_parse(url) do
        {:ok, feed} -> feed.entries |> Enum.map(fn entry -> entry end)
        {:error, reason} -> {:error, reason}
      end
    end)
  end
end


defmodule AppWeb.PageLive do
  use AppWeb, :live_view
  def mount(_params, _session, socket) do
    if connected?(socket) do
      Phoenix.PubSub.subscribe(App.PubSub, "message")
    end
    {:ok, assign(socket, message_item: "different")}
  end
  def handle_info({:pubsub_transmission, text}, socket) do
    IO.inspect "pub sub working"
    {:noreply, assign(socket, message_item: text)}
  end

  def loop(data) do
     IO.inspect data
     if data > 0 do
      IO.inspect "notify"
      # run code to compare files, loop. If different run pubsub code below
      :timer.sleep(2000)
      loop(data-1)

     end
     if data <= 0 do

      Phoenix.PubSub.broadcast(App.PubSub, "message", {:pubsub_transmission, "data"})
      "done"
     end
  end



  def handle_event("change-text", _params, socket) do
    spawn(fn -> loop(3) end)
    {:noreply, assign(socket, message_item: "START" )}
  end
  def render(assigns) do

    # AppWeb.PageLive.loop("xxx")
    ~H"""
    SANDBOX

    <.button phx-click="change-text" value={@message_item}>CLICK ME</.button>

    <div>

      <%= @message_item %>
    </div>
    """
  end
end


elixir : 1.16.2

Erl: 20


  1. PostGres

Created Saturday 18 February 2023


Launch [PostGres](#PostGres) from Terminal


sudo -u postgres psql


Set User Permission


<https://commandprompt.com/education/how-to-create-a-superuser-in-postgresql/>

ALTER USER user_name WITH PASSWORD 'new_password';


Run Post Gres Commands



sudo -i -u postgres psql


Create and Delete Database

CREATE DATABASE name; DROP DATABASE name;

    1. View user list

\du

View all databases

postgres=# \l

Change databases

postgres=# \c name-of-database

View Tables

After you change databases and are selected on one of them do this:

\dt

SELECT * FROM mytablename;

EMPTY TABLE DELETE FROM name-of-table

CREATE TABLE

CREATE TABLE table_name(

  column1 datatype,
  column2 datatype,
  column3 datatype,
  .....
  columnN datatype,
  PRIMARY KEY( one or more columns )

);

INSERT

INSERT INTO ITEMS(id, name) VALUES(1,'htvjbjbgni'); Strings are single quote



Roadmap

defmodule AppWeb.ItemsLive do
    use AppWeb, :live_view  
    def mount(_params, _session, socket)  do
      {:ok, assign(socket, switches: ["grindy","sonic","ogre"])}  
    end
    
    def handle_event("run",_params,socket) do  
        
        IO.inspect "DOWNLOAD VIA SSH"

        {:noreply, redirect(socket, to: "/download")}

    end
   

    def render(assigns) do
        
        ~H"""

        <ul>
        <%= for switch <- @switches do %>
         <a phx-submit="click" href="/download">  <li> <%= switch %>  </li> </a>

        <%end%>
        </ul>
        """ 
    end


 end


Notes

List of Switches and the option to select the one needed. Button to download JSON file. Drag and Drop Palette to upload JSON file.

After JSON file is uploaded a button titled "Make Active" is available.













https://medium.com/@fraiha26/pushing-events-to-livecomponents-from-js-hooks-w-phoenix-elixir-d5544d4c3dfa

Changeset notes and forms

NOTE: Use case statements for pattern matching ____________________________________________

Create a basic <.form></form> without error handling

<.form :let={f} for={@changeset}  action={~p"/items"} >

  <.input field={f[:title]} type="text" label="Title" />
  
  <.button>Save Item</.button>
   
</.form>

With error handling looks like this:




<.form :let={f} for={@changeset}  action={~p"/items"} >

  <.error :if={@changeset.action}>
    Oops, something went wrong! Please check the errors below.
  </.error>


  <.input field={f[:title]} type="text" label="Title" />
  
  <.button>Save Item</.button>
   
</.form>

Create the routes for render and post to endpoint.


The controller for the page render needs to pass in the changeset via ```Items.change_item(%Item{})```


  def new(conn, _params) do
    changeset = Items.change_item(%Item{})
    render(conn, :new, changeset: changeset)
  end


Post route without error handling


  def create(conn, %{"item" => item_params}) do
    case Items.create_item(item_params) do
      {:ok, item} ->
        conn
        |> put_flash(:info, "Item created successfully.")
        |> redirect(to: ~p"/items/#{item}")
    end
  end

With error handling

  def create(conn, %{"item" => item_params}) do
    case Items.create_item(item_params) do
      {:ok, item} ->
        conn
        |> put_flash(:info, "Item created successfully.")
        |> redirect(to: ~p"/items/#{item}")

      {:error, %Ecto.Changeset{} = changeset} ->
        render(conn, :new, changeset: changeset)
    end
  end


_____________________________________________ https://medium.com/@vincentlin/phoenix-very-simple-form-c4b121697fcb


home.html.heex

<form action="/submit" method="post">
  <label for="name">Name:</label><br>
  <input type="text" id="name" name="name"><br>
  <input type="hidden" name="_csrf_token" value={@csrf_token} />
  <input type="submit" value="Submit">
</form>


page_controller.ex



defmodule AppWeb.PageController do
  use AppWeb, :controller

  def home(conn, _params) do
    # The home page is often custom made,

    csrf_token = Plug.CSRFProtection.get_csrf_token()
    render(conn, :home, csrf_token: csrf_token)
  end


  def submit(conn, _params) do
    # The home page is often custom made,
     IO.inspect(_params)
     IO.inspect "___________________________"
    csrf_token = Plug.CSRFProtection.get_csrf_token()
    render(conn, :home, csrf_token: csrf_token)
  end

end


router.ex

  scope "/", AppWeb do
    pipe_through :browser
    post "/submit", PageController, :submit
    get "/", PageController, :home

  end









Ecto.Adapters.SQL.query(Repo, "select * from artists where id=1")


Ecto cheatsheet: https://hexdocs.pm/ecto/crud.html __________________________________________________________________ Most programming languages give you the ability to set global state by creating globally scoped variables. Elixir does not do this. Instead, Elixir provides a tool called a "processes" to persist state. Elixir applications are composed of these "processes".

Processes by themselves are complicated to use, thus Elixir provides abstractions to make working with processes easier. One of these abstractions is called a GenServer.

GenServers are not only used to store state, they are also used to invoke work independent of the main application process.

Creating a GenServer to Store State

To Create a GenServer, start by creating a basic Module that uses the GenServer module (image below)


defmodule App do  
  use GenServer


end

We now need to describe the actions we want to perform. Being that this is used to store state, we will use basic CRUD actions: Create, Read, Update, Delete. To begins, empty functions describing each action is created.


defmodule App do
   def create do 

   end
   def read do 

   end

   def update do  

   end 

   def delete do 

   end

end

What now?

To fill in the functions with the appropriate code we must first have a basic understanding of how GenServers are structured.

GenServers are composed of the GenServer module invoking callbacks. The callbacks have built in names that you, as a developer have to use.

These callbacks are named:

  • init
  • handle_cast
  • handle_call


To demonstrate, here is an example :


{:ok, pid} = GenServer.start(ModuleNameOfGenServer, %{})

The above function named "start" calls the function named init in the Module below. In GenServer syntax the name of the GenServer functions always invoke callbacks that are determined by the language.


defmodule ModuleNameOfGenServer do
  # 1
  def init(state \\ %{}) do
    {:ok, state}
  end


This is a list of the GenServer functions and their corresponding callbacks.

  • GenServer.start/1 --> init/1
  • GenServer.call/3 --> def handle_call/3
  • GenServer.cast/2 --> handle_cast/2


Start and Call

Call and Cast can be used to perform the same operations. The

__________________________________________________________ Genserver timed events

defmodule Go do
  use GenServer

  def go() do
    GenServer.start_link(__MODULE__, [])
  end

  def init(list) do
    :timer.apply_interval(1000, __MODULE__, :add, [self(), "weeeee"])
    {:ok, list}
  end

  def view(pid) do
    GenServer.call(pid, :view)
  end

  def handle_call(:view, _from, list) do
    {:reply, list, list}
  end

  def add(pid, new_item) do
    GenServer.call(pid, {:item, new_item})
  end

  def handle_call({:item, new_item}, _from, state) do
    result = [new_item] ++ state
    {:reply, result, result}
  end
end


defmodule ShoppingList do
  use GenServer

  def start_link() do
    GenServer.start_link(ShoppingList, [])
  end

  def init(list) do
    {:ok, list}
  end

  def view(pid) do
    GenServer.call(pid, :view)
  end

  def handle_call(:view, _from, list) do
    {:reply, list, list}
  end

  def add(pid, new_item) do
    GenServer.call(pid, {:item, new_item})
  end

  def handle_call({:item, new_item}, _from, state) do
    result = [new_item] ++ state
    {:reply, result, result}
  end
end


https://blog.appsignal.com/2018/06/12/elixir-alchemy-deconstructing-genservers.html


defmodule KeyValue do
  # 1
  def init(state \\ %{}) do
    {:ok, state}
  end
 
  # 2
  def handle_cast({:put, key, value}, state) do
    {:noreply, Map.put(state, key, value)}
  end
 
  # 3
  def handle_call({:get, key}, _from, state) do
    {:reply, Map.fetch!(state, key), state}
  end
end

# {:ok, pid} = GenServer.start(KeyValue, %{})
# GenServer.cast(pid, {:put, :foo, "bar"})
# GenServer.call(pid, {:get, :foo})




Send messages via these functions wrapped in module functions .You can look at these are functions used to fire "GenServer Events".

  • start_link
  • GenServer.call
  • GenServer.cast

The corresponding server functions that reply are:

  • init
  • handle__call
  • handle_cast

These can be looked at as "listeners" - in JavaScript parlance. __________________________________________

If you have experience with programming languages other than Elixir the idea of GenServers may be very foreign and require a strong context shift to learn. This document is intended to simplify the concept.

  • What: GenServers are simply a way to hold state and/or invoke actions based on incoming events.
  • when
  • where
  • why
  • how





___________________________________________ GenServer timer example

defmodule Timer do
  use GenServer

  def init(_) do
    :timer.send_interval(1000, :xyz)
    {:ok, 0}
  end

  def handle_info(:xyz, state) do
    IO.inspect(state)
    {:noreply, state + 1}
  end
end


[
   {
     data:{
        creationTime,
        publishDate,
     }

     featured:{
        setAmount,
        items:[]
     },

     children:{
        setAmount,
        items:[]
     },
     links:{
        setAmount,
        items:[]
     }

   }

]

jason.encode / jason.decode


  def run(num) do

    Enum.each(0..99999, fn(_x) ->
      task = Task.async(fn -> num + _x end)
    IO.inspect Task.await(task)
  end)

Enum.map(feed.entries, fn x -> %{url: x.url, title: x.title} end)

App is split:

App that captures RSS in UI and lets admin select stories. THere are settings to determine the number of FEATURED,CHILD and LINK stories. News categories are horizontal and scrollable. This exports a json doc with all RSS data for each story. The



App that reads the json data. This app creates tables and is the front end.



https://stephenbussey.com/tags/elixir.html

defmodule AppWeb.GroupLive do
  use AppWeb, :live_view
  alias App.TestBeds
  alias App.Groups

  def mount(_params, _session, socket) do
    {:ok, assign(socket, testbeds: TestBeds.list_testbeds(), groups: Groups.list_groups())}
  end

  def handle_event("select-group", params, socket)  do  
     group = App.Groups.get_group!(params["group-id"])
     testbed = App.TestBeds.get_test_bed!(params["testbed_id"])
     App.TestBeds.update_test_bed(testbed, %{group_id: group.id})
     {:noreply, socket}
  end

  def updated_groups(testbed_id)  do  
     testbed = App.TestBeds.get_test_bed!(testbed_id)
     groups = App.Groups.list_groups
     tail = Enum.filter(groups, fn(item)-> item.id !== testbed.group_id end)
     head = Enum.filter(groups, fn(item)-> item.id == testbed.group_id end)
            |>Enum.at(0)
    [head | tail]
  end

  def render(assigns) do
    ~H"""
        <%= for testbed <- Enum.sort_by(@testbeds , &("#{&1.name}"), :asc)do %>
        <div class="grid grid-cols-4 gap-4 p-8 rounded shadow bg-white">
          <h2> Name</h2>
          <div class="name"><%= testbed.name %> </div>
            <h2> Group</h2>
          <div>
            <form phx-change="select-group" name="testbed_id"  >
               <input type="hidden" name="testbed_id" value={testbed.id}/>
               <select id="group-selection"  name="group-id" class="">
                <%= for group <- updated_groups(testbed.id) do %>
                  <%= if group !== nil do %>
                      <option value= {"#{group.id}"}><%=group.name %></option>
                      <%else%>
                      <option value= {"Unassigned"}>NONE</option>
                  <% end %>

                <% end %>
               </select>
            </form>
          </div>
        </div>
       <% end %>
    """
  end
end


Developer Notes

Groups Feature

To complete this feature do these things.

  • Run through the steps in | this document to create Group table.
  • Groups have these fields name: string, description: string and color: string
  • Add unique constraint in migration file:
  def change do
     create unique_index(:groups, [:name])
  end
  • Port over Group LiveView code (temporarily placed at discussion page).
  • On completion possibly, create a Group named No Group and set all Testbeds to it.



________ https://blog.appsignal.com/2022/10/11/phoenix-liveview-018-new-special-html-attributes.html https://blixtdev.com/whats-new-phoenix-1-7/ https://github.com/devato/inertia_phoenix https://phoenixonrails.com/blog/you-can-stop-using-form-for-in-phoenix


https://samuelmullen.com/articles/phoenix_templates_rendering_and_layouts

WorkFlow of Todo App in Both LiveView and DeadView.

Note:

Create foreign key data. Create live view. List all testbeds and form to assign each a group by group-name.

Repeat in dead view


Mental model and data flows


home.html.heex <a href="/data" name="we are the world" > click me</a>

<%= @message %>


page controller

defmodule AppWeb.PageController do
  use AppWeb, :controller

  def home(conn, _params) do
    IO.inspect _params
    render(conn, :home, message: "Hello")
  end

end

get "/:yay", PageController, :index

get "/", PageController, :index

  • Explain control flow of "Dead Views" and the Generator HTML Control flow.
  • Proper Way to use Modal
  • How to give Focus to Fields in Modal (or other forms fields)
  • How to Create Database Seed Data.
  • How to structure code so controller code is in one file and HTML Heex is in another
  • How to Use Generators in a Real World Project
  • How to Work with Database Data via Basic Commands and Custom Change sets.
  • How to Render Database Data to LiveView (via Context Commands).
  • How to Create a Chat using PubSub.
  • How to Create Event Handlers in LiveView
  • How to Create a Dynamic Route from Scratch.
  • How to Use Elixirs Template language
  • How to Work with Modal
  • How to Use CSS From Scratch (Without bundler)
  • How to use JS hooks


______________________ https://studioindie.co/blog/heex-guide/

get "/", PageController, :some-atom    #This is the route. some-atom is the controller name


defmodule AppWeb.PageController do
  use AppWeb, :controller

  def some-atom(conn, _params) do                 # controller name
    # The home page is often custom made,
    # so skip the default app layout.
    render(conn, :some-atom, layout: false)      # :some-atom is template name
  end
end

Controllers page_html.ex contains

defmodule AppWeb.PageHTML do
  use AppWeb, :html

  embed_templates "page_html/*"
end

page_html is the name of the folder in Controllers/page_html


Controllers/page_html/some-atom.html.heex (this is the template)



__________________________________________ Set all association with preload:

  def list_groups do
    Repo.all(Group)
     |> Repo.preload([:testbeds])
  end

Insert Child into Parent

Example:

App.get_group!(2) # arg is id
|> Ecto.build_assoc(:testbeds)
|> Repo.insert()

Or

group = App.get_group!(2) # arg is id

testbeds = Ecto.build_assoc(group, :testbeds)

App.Group.create_group(testbeds)
group = App.Groups.get_group!(1)
thing = Ecto.build_assoc(group, :testbeds, name: "DUMB")
alias App.{Repo}
Repo.insert(thing)


Example

avatar = %Avatar{nick_name: "Elixir", pic_url: "http://elixir-lang.org/images/logo.png"}
user = %User{name: "John Doe", email: "john.doe@example.com", avatar: avatar}

https://www.reddit.com/r/elixir/comments/oh318k/is_there_a_way_to_avoid_ectoassociationnotloaded/h4mmcfi/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button

  1. Create Group Table
mix phx.gen.html Groups Group groups name:string

Set the resources /

mix ecto.migrate


For testbeds create a migration file and add this field: group_id:references:groups

def change do
    alter table(:testbeds) do    # customize to your code
      add :group_id, references(:groups)
    end
  end


Update Groups Schema (Don't forget alias / has_many)


defmodule App.Groups.Group do
	use Ecto.Schema
	import Ecto.Changeset
	alias App.Testbeds.Testbed   # Alias!

	schema "posts" do
		field :body, :string
		field :title, :string
		has_many :testbeds, Testbed    # Has many

		timestamps()
	end

	@doc false
	def changeset(post, attrs) do
		post
		|> cast(attrs, [:title, :body])
		|> validate_required([:title, :body])
	end
end

TESTBEDS Schema

defmodule App.Testbeds.Testbed do
  use Ecto.Schema
  import Ecto.Changeset
  alias App.Groups.Group      # Alias
  schema "testbeds" do
    field :name, :string
    belongs_to :group, Group    # Belongs to
    timestamps()
  end

  @doc false
  def changeset(testbed, attrs) do
    testbed
    |> cast(attrs, [:name])
    |> validate_required([:name])
  end
end

_____________________________________ Side notes.

defmodule App.Repo.Migrations.CreateTestbeds do

 use Ecto.Migration
 def change do
   create table(:testbeds) do
     add :name, :string
     add :group_id, references(:groups, on_delete: :nothing)
     timestamps()
   end
   create index(:testbeds, [:group_id])
 end

end


________________________________

mix phx.gen.context Comments Comment comments name:string content:text post_id:references:posts


mix phx.gen.html Testbeds Testbed testbeds name:string group_id:references:groups

https://graffino.com/web-development/first-steps-in-elixir-and-phoenix-create-a-blog-prototype


https://blog.logrocket.com/getting-started-ecto-phoenix/

https://serokell.io/blog/ecto-guide-for-beginners


mix phx.gen.html Testbeds Testbed testbeds name:string

mix phx.gen.html Groups Group groups name:string

Create migration for


mix ecto.gen.migration add_testbed_group_reference


Migration:

  def change do
    alter table(:testbeds) do    # customize to your code
      add :group_id, references(:groups)
    end
  end


Manually Change:


defmodule App.Group do
  use Ecto.Schema

  schema "groups" do
    field :title, :string
    field :tagline, :string
    has_many :testbeds, App.Testbed
  end
end

testbeds

defmodule App.Repo.Migrations.AddTextbedGroups do
  use Ecto.Migration

  def change do
    create table(:testbeds) do
      add :name, :string
      add :group_id, references(:groups)
    end
  end
end


# lib/app/testbeds.ex

defmodule App.Testbed do
  use Ecto.Schema

  schema "testbedss" do
    field :name, :string
    belongs_to :group, App.Group
  end
end






_____________________

%App.TestBeds.TestBed{

 __meta__: #Ecto.Schema.Metadata<:loaded, "testbeds">,
 id: 1,
 developer: "None",
 name: "sdfsd",
 owner: "sdfsddf",
 note: "sdsfsddf",
 status: "Available",
 url: "sdf",
 version: "ddf",
 manager: "sf",
 inserted_at: ~N[2023-08-21 13:45:39],
 updated_at: ~N[2023-08-21 13:45:44]

} ________________________________________________________________---

defmodule AppWeb.SendLive do
  use AppWeb, :live_view
  
  def mount(_params, _session, socket) do
    {:ok, socket}
  end
  
  
  def handle_event("send", %{"text" => text}, socket) do

    AppWeb.Endpoint.broadcast(topic, "message", text)
    {:noreply, socket}
  end
  

  defp topic do
    "chat"
  end


  def render(assigns)do   
   ~H"""
<div>
  <h1>Send Message</h1>

  <form phx-submit="send">
    <input type="text" name="text" />
    <button type="submit">Send</button>
  </form>
</div>

"""
  end
  
end

______________________________________________________________


defmodule AppWeb.ReceiveLive do
  use AppWeb, :live_view
  
  def mount(_params, _session, socket) do
    if connected?(socket) do
      AppWeb.Endpoint.subscribe(topic)
    end
    {:ok, assign(socket, messages: "")}
  end
  
  def handle_info(%{event: "message", payload: message}, socket) do
    IO.inspect message
    {:noreply, assign(socket, messages: message)}
  end
  

  defp topic do
    "chat"
  end

  def render(assigns)do   
   ~H"""
<div>
  <h1>ChatLive</h1>
  <%= @messages %>


</div>

"""
  end
  
end

______________________________________________________________ When you hover over a list of items this hook finds items with the same name and sets the opacity of all others to 0. You see all the items similar to the one you are hovering over.


TestbedVersionHover: {
			// https://jsfiddle.net/IrvinDominin/7K2Z3/
            mounted() {

                let rows = document.getElementsByClassName("testbed-version")
                // this.ele = element
				// rows = this.ele;
                
				for (var i = 0; i < rows.length; i++) {
					rows[i].onmouseenter = function (event) {
						for (var j = 0; j < rows.length; j++) {
							let preversion = this.textContent;
							let version = preversion.slice(0,5)
							let compareVersion = rows[j].textContent.slice(0,5)
							if (compareVersion === version) continue
							rows[j].className += " other";
						}
					};
				
					rows[i].onmouseleave = function (event) {
						var hovers = document.getElementsByClassName('other');
						var len = hovers.length;
						for (var j = 0; j < len; j++) {
							hovers[0].className = hovers[0].className.replace(/\sother(\s|$)/, '');
						}
					};
				}

                // this.ele.addEventListener("click", (e) => {
                //     console.log(e.target)
                    
                // });

            }
        }


  • How to Create Database Seed Data.
  • How to structure code so controller code is in one file and HTML Heex is in another
  • How to Use Generators in a Real World Project
  • How to Work with Database Data via Basic Commands and Custom Change sets.
  • How to Render Database Data to LiveView (via Context Commands).
  • How to Create a Chat using PubSub.
  • How to Create Event Handlers in LiveView
  • How to Create a Dynamic Route from Scratch.
  • How to Use Elixirs Template language
  • How to Work with Modal
  • How to Use CSS From Scratch (Without bundler)
  • How to use JS hooks
  • Dissect Table Component and Update it to Order List in Reverse.

Selenium https://www.builder.io/blog/debug-nodejs

https://marketplace.visualstudio.com/items?itemName=ms-vscode.live-server#running-the-extension

This was added recently (see microsoft/vscode#109276).

   1. Open the palette (Ctrl + Shift + P)
   2. Select "Simple Browser: Preview"
   3. Enter web address

_______________________


const {By, Key, Builder} = require("selenium-webdriver");
const assert = require("assert")

require("chromedriver");

async function test_case(){
  let driver = await new Builder().forBrowser("chrome").build();
  await driver.get("sie");


  await driver.findElement(By.name('password')).clear();
  await driver.findElement(By.name('identification')).clear();
  await driver.findElement(By.name('identification')).sendKeys("bill");
  await driver.findElement(By.name('password')).sendKeys("password", Key.RETURN);




 setTimeout(()=>{
   driver.findElement(By.className("req_lan_license")).click();
 },5000)



defmodule AppWeb.PageLive do 

  use AppWeb, :live_view  
  alias App.Testbeds 
  def mount(_params, _session, socket)do   
     {:ok, assign(socket, testbeds: Testbeds.list_testbeds())}   
  end

    def button_event("invoke_editing") do
    %JS{}
    |> JS.remove_class("active", to: "#editing_button")
    |> JS.add_class("hidden", to: "#editing_button")
    |> JS.remove_class("hidden", to: "#preview_button")
    |> JS.add_class("active", to: "#preview_button")
    |> JS.remove_class("active", to: "#preview")
    |> JS.add_class("hidden", to: "#preview")
    |> JS.remove_class("hidden", to: "#editing")
    |> JS.add_class("active", to: "#editing")
  end

  def button_event("invoke_preview") do
    %JS{}
    |> JS.remove_class("active", to: "#preview_button")
    |> JS.add_class("hidden", to: "#preview_button")
    |> JS.remove_class("hidden", to: "#editing_button")
    |> JS.add_class("active", to: "#editing_button")
    |> JS.remove_class("active", to: "#editing")
    |> JS.add_class("hidden", to: "#editing")
    |> JS.remove_class("hidden", to: "#preview")
    |> JS.add_class("active", to: "#preview")

  end

  def handle_event("send", params, socket) do
    hide_modal("notes-modal-0")
    {:noreply, socket}
  end

  

  def render(assigns) do  
     ~H"""

    <%= for testbed <- @testbeds do %>
		<.modal id={"notes-modal-#{testbed.id}"}>
		    <.button phx-click={button_event("invoke_editing")} id="editing_button" class="hidden">
		      Preview
		    </.button>
		    <.button phx-click={button_event("invoke_preview")} id="preview_button">
		      Edit Me
		    </.button>
		    <div id="editing"> PREVIEWING THE WORK</div>
		    <form phx-submit = "send"   id="preview" class="hidden">
		      <textarea value = {testbed.note}></textarea>
		      <.button type="submit" phx-click={hide_modal("notes-modal-#{testbed.id}")}>SAVE WORK</.button>
		    </form>
		</.modal>

    <%= if testbed.note do %>
      <div phx-click={show_modal("notes-modal-#{testbed.id}")}>READ ME  ...<%= testbed.note %></div>
    <% end %>
	   
    <% end %>
       
      """
  end

end



import {HfInference} from "@huggingface/inference";
import dotenv from "dotenv";
dotenv.config()

const HF_ACCESS_TOKEN = process.env.HF_ACCESS_TOKEN

const hf = new HfInference(HF_ACCESS_TOKEN);


/*______Uncomment for this example______________________

const model = "nlpconnect/vit-gpt2-image-captioning";
const imageURL = "https://i.imgur.com/lTvb7Et.png";

const response = await fetch(imageURL);
const imageBlob = await response.blob();

const result  = await hf.imageToText({
	data: imageBlob,
	model: model,
});

_______________________________________________________*/



/*________Another example ___________________*/

const result = await hf.summarization({
	model: 'facebook/bart-large-cnn',
	inputs: "The role of a dumb man is to get smarter oogy bookie boo",
	parameters:{
		max_length: 100
	}
});





console.log(result);






________________________________________


https://fly.io/phoenix-files/sdeb-toggling-element/ https://www.youtube.com/watch?v=vBgZvQapqhs

https://blog.testdouble.com/posts/2022-11-28-how-to-use-javascript-with-phoenix-liveview/

https://hexdocs.pm/phoenix_live_view/0.19.3/Phoenix.Component.html _______________________________________ https://www.smashingmagazine.com/2011/10/quick-look-math-animations-javascript/

  • Depth first traversal
  • Breadth first traversal

https://dev.to/codesphere/10-algorithms-every-developer-should-learn-3lnm

https://www.youtube.com/watch?v=fPz40W9mfCg

__________________________________



defmodule AppWeb.GroupLive do
  use AppWeb, :live_view
  alias App.TestBeds
  alias App.Groups


  def mount(_params, _session, socket) do
    {:ok, assign(socket, testbeds: TestBeds.list_testbeds(), groups: Groups.list_groups())}
  end


  def handle_event("select-group", params, socket)  do  
         
       IO.inspect params
       {:noreply, socket}
  end


  def render(assigns) do
    ~H"""

  

  <div class="grid grid-columns-2 grid-flow-col gap-4">
      <%= for testbed <- @testbeds do %>
        <div class="column-1">
          <div class="name"><%= testbed.name %></div>
        </div>
        <div class="column-2">    
            <form phx-change="select-group"  >
               <input type="hidden" name="testbed_id" value={testbed.id}/>
               <select id="group-selection"  name="group-id" >
                <%= for group <- @groups do %>
                  <option value= {"#{group.id}"}  ><%=group.name %></option>
                <% end %>
               </select>
            </form>
         </div>
       <% end %>
   </div>

    """
  end
end


____________________________

defmodule AppWeb.GroupLive do
  use AppWeb, :live_view
  alias App.TestBeds
  alias App.Groups


  def mount(_params, _session, socket) do

    {:ok, assign(socket, testbeds: TestBeds.list_testbeds(), groups: Groups.list_groups(), messages: [])}
  end


  def handle_event("select-group", params, socket)  do  


      group = App.Groups.get_group!(params["group-id"])
       # IO.inspect group
       testbed = App.TestBeds.get_test_bed!(params["testbed_id"])
     
       App.TestBeds.update_test_bed(testbed, %{group_id: group.id})
         
    
       {:noreply, socket}
  end


  def updated_groups(testbed_id)  do  
       groups = App.Groups.list_groups
       tail = Enum.filter(groups, fn(item)-> item.id !== testbed_id end)
       head = Enum.filter(groups, fn(item)-> item.id == testbed_id end)
              |>Enum.at(0)
     [head | tail]
         
  end

 # def handle_event("groups", params, socket)do  
 #       selected_testbed = %{name: "my testbed name", id: 3}
 #       groups = App.Groups.list_groups
 #       tail = Enum.filter(groups, fn(item)-> item.id !== selected_testbed.id end)
 #       head = Enum.filter(groups, fn(item)-> item.id == selected_testbed.id end)
 #              |>Enum.at(0)
         
 
 #       # result = Enum.filter([1, 2, 3], fn x -> x.id !== 1 end)
 #      IO.inspect [head | tail]
         
 #      #
 #      {:noreply, socket, assigns.message:}
 #  end




  def render(assigns) do
    
    ~H"""
       <%!-- <div phx-click = "groups">CLICK ME</div> --%>
  


      <%= for testbed <- @testbeds do %>
        <div class="grid grid-cols-4 gap-4 p-8 rounded shadow bg-white">
          <h2> Name</h2>
          <div class="name"><%= testbed.name %> </div>
            <h2> Group</h2>
          <div>

            <form phx-change="select-group" name="testbed_id"  >
               <input type="hidden" name="testbed_id" value={testbed.id}/>
               <select id="group-selection"  name="group-id" class="">
        
                <%= for group <- updated_groups(testbed.id) do %>
                  <option value= {"#{group.id}"}  ><%=group.name %></option>
                  <!-- Write code to reorder with testbed.group_id matching group_id -->
                <% end %>
               </select>
            </form>
          </div>
        </div>
       <% end %>
   
    """
  end
end



_____________________________________


defmodule AppWeb.PageLive do
   use AppWeb, :live_view  
     alias App.Testbeds
  alias App.Groups
   def mount(_params, _session, socket)  do
	     {:ok, assign(socket, testbeds: Testbeds.list_testbeds(), groups: Groups.list_groups())}
  end

   def handle_event("select-group", params, socket)  do  
       group = App.Groups.get_group!(params["group-id"])
       testbed = App.Testbeds.get_testbed!(params["testbed_id"])
       result = App.Testbeds.update_testbed(testbed, %{group_id: group.id})
       IO.inspect result
      
       {:noreply, socket}
  end

    def updated_groups(testbed_id)  do  
       testbed = App.Testbeds.get_testbed!(testbed_id)
       groups = App.Groups.list_groups
       tail = Enum.filter(groups, fn(item)-> item.id !== testbed.group_id end)
       head = Enum.filter(groups, fn(item)-> item.id == testbed.group_id end)
              |>Enum.at(0)
 
       [head | tail]
    end


  def render(assigns) do
    
    ~H"""

      <%= for testbed <- @testbeds do %>
        <div class="grid grid-cols-4 gap-4 p-8 rounded shadow bg-white">
          <h2> Name</h2>
          <div class="name"><%= testbed.name %> </div>
            <h2> Group</h2>
          <div>

            <form phx-change="select-group" name="testbed_id"  >
               <input type="hidden" name="testbed_id" value={testbed.id}/>
               <select id="group-selection"  name="group-id" class="">
        
                <%= for group <- updated_groups(testbed.id) do %>

                  <%!-- <option value= {"#{group.id}"}><%=group.name %></option> --%>
                  <!-- Write code to reorder with testbed.group_id matching group_id -->

                  <%= if group !== nil do %>
                      <option value= {"#{group.id}"}><%=group.name %></option>
       
                  <% end %>


                <% end %>
               </select>
            </form>
          </div>
        </div>
       <% end %>
   
    """
  end
end
  has_many :testbeds, TestBed, on_delete: :restrict   # Has many

https://elixirforum.com/t/ecto-has-many-on-delete-protect/49550

https://hexdocs.pm/ecto/Ecto.Changeset.html#unique_constraint/3

____________________________________________________________________


How to Create a Mix Project and Use It in Another Mix Project

To create and use a **Mix project** inside another **Mix project** in Elixir, follow the steps below:

1. Create a New Mix Project

First, you need to create a new Mix project. Let's call this project `library_project`. Run the following command:

mix new library_project --module LibraryProject

This will create a new Elixir Mix project named `library_project`. The `--module` flag specifies the name of the module that will be created (in this case `LibraryProject`).

The directory structure of `library_project` will look like this:

library_project/
  |_ lib/
      |_ library_project.ex
  |_ mix.exs

You can then add some basic functionality inside the `library_project/lib/library_project.ex` file:

defmodule LibraryProject do
  def hello do
    IO.puts "Hello from LibraryProject!"
  end
end

Now, `library_project` is ready to be used by another Mix project.

2. Create a Second Mix Project

Next, create the second Mix project where you want to use `library_project`. For example:

mix new app_project --module AppProject

This will create another Elixir project called `app_project`.

The directory structure for this project will look like:

app_project/
  |_ lib/
      |_ app_project.ex
  |_ mix.exs

3. Add `library_project` as a Dependency in `app_project`

Now, you want to make `library_project` available in `app_project`. There are two main ways to do this:

Option 1: Local Dependency (via `path`)

If `library_project` is a local project (i.e., it's stored in a different directory), you can add it to the `mix.exs` of `app_project` as a local dependency by specifying the path to `library_project`.

Edit the `mix.exs` of `app_project`:

defmodule AppProject.MixProject do
  use Mix.Project

  def project do
    [
      app: :app_project,
      version: "0.1.0",
      elixir: "~> 1.14",
      start_permanent: Mix.env() == :prod,
      deps: deps()
    ]
  end

  defp deps do
    [
      {:library_project, path: "../library_project"}  # Add the local dependency
    ]
  end
end

Here, `{:library_project, path: "../library_project"}` tells Mix to use the local `library_project` located in the `../library_project` directory (relative to `app_project`).

After updating the `mix.exs`, run the following command:

cd app_project
mix deps.get

This will fetch the dependency and compile `library_project`.

Option 2: Publish and Fetch as a Hex Package

If you want to use `library_project` as a package in another project (for example, by publishing it to **Hex.pm**, Elixir’s package manager), you need to publish it to Hex and then add it as a dependency.

1. Publish `library_project` to Hex (if it's public or for your private use). 2. Add `library_project` to your `app_project`’s `mix.exs` dependencies:

defp deps do
  [
    {:library_project, "~> 0.1.0"}  # Specify the published version from Hex
  ]
end

Run `mix deps.get` to fetch the package from Hex.

4. Use `library_project` in `app_project`

Now, you can use the functionality from `library_project` inside your `app_project`. For example, in `app_project/lib/app_project.ex`, you can call the function from `library_project`:

defmodule AppProject do
  def start do
    IO.puts "Starting app_project!"
    LibraryProject.hello()  # Call the function from the library_project
  end
end

5. Run the App

Finally, you can run your `app_project`:

cd app_project
mix run -e "AppProject.start()"

This will print:

Starting app_project!
Hello from LibraryProject!

Recap of the Process

1. **Create the `library_project`**: Use `mix new` to generate a library project. 2. **Create the `app_project`**: Create the second project where you will use the library. 3. **Add the `library_project` as a dependency**:

  - Use the `path` option to point to the local project or publish it to Hex for public access.

4. **Use the `library_project` in the `app_project`**: Call functions or use modules from the library inside the second project. 5. **Run the app**: Use `mix run` to execute the project.

This way, you can reuse code from one Mix project in another by adding it as a dependency.



z

Overloaded functions are extremely common in Elixir applications. They are regularly used for recursive functions and for changing behavior based on configuration.