User:Admin
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}
- 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