From 7f5f47042a6542eb35559f13a1d30fcd859b837f Mon Sep 17 00:00:00 2001 From: Braeden Sowinski Date: Tue, 29 Sep 2026 11:55:46 -0700 Subject: [PATCH] lecture 04 --- lectures/04/counter/.formatter.exs | 4 ++ lectures/04/counter/.gitignore | 26 +++++++++++ lectures/04/counter/README.md | 21 +++++++++ lectures/04/counter/counter.db | 1 + lectures/04/counter/lib/counter.ex | 18 ++++++++ .../04/counter/lib/counter/application.ex | 21 +++++++++ lectures/04/counter/lib/counter/store.ex | 38 ++++++++++++++++ lectures/04/counter/lib/counter/worker.ex | 45 +++++++++++++++++++ lectures/04/counter/mix.exs | 29 ++++++++++++ lectures/04/counter/test/counter_test.exs | 8 ++++ lectures/04/counter/test/test_helper.exs | 1 + lectures/04/counter2/.formatter.exs | 4 ++ lectures/04/counter2/.gitignore | 26 +++++++++++ lectures/04/counter2/README.md | 21 +++++++++ .../04/counter2/lib/counter/application.ex | 21 +++++++++ lectures/04/counter2/lib/counter/worker.ex | 38 ++++++++++++++++ lectures/04/counter2/mix.exs | 29 ++++++++++++ lectures/04/counter2/test/counter_test.exs | 8 ++++ lectures/04/counter2/test/test_helper.exs | 1 + lectures/04/lec4.ex | 36 +++++++++++++++ lectures/04/notes.ex | 31 +++++++++++++ note.md | 1 + 22 files changed, 428 insertions(+) create mode 100644 lectures/04/counter/.formatter.exs create mode 100644 lectures/04/counter/.gitignore create mode 100644 lectures/04/counter/README.md create mode 100644 lectures/04/counter/counter.db create mode 100644 lectures/04/counter/lib/counter.ex create mode 100644 lectures/04/counter/lib/counter/application.ex create mode 100644 lectures/04/counter/lib/counter/store.ex create mode 100644 lectures/04/counter/lib/counter/worker.ex create mode 100644 lectures/04/counter/mix.exs create mode 100644 lectures/04/counter/test/counter_test.exs create mode 100644 lectures/04/counter/test/test_helper.exs create mode 100644 lectures/04/counter2/.formatter.exs create mode 100644 lectures/04/counter2/.gitignore create mode 100644 lectures/04/counter2/README.md create mode 100644 lectures/04/counter2/lib/counter/application.ex create mode 100644 lectures/04/counter2/lib/counter/worker.ex create mode 100644 lectures/04/counter2/mix.exs create mode 100644 lectures/04/counter2/test/counter_test.exs create mode 100644 lectures/04/counter2/test/test_helper.exs create mode 100644 lectures/04/lec4.ex create mode 100644 lectures/04/notes.ex diff --git a/lectures/04/counter/.formatter.exs b/lectures/04/counter/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/lectures/04/counter/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/lectures/04/counter/.gitignore b/lectures/04/counter/.gitignore new file mode 100644 index 0000000..ccae38f --- /dev/null +++ b/lectures/04/counter/.gitignore @@ -0,0 +1,26 @@ +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where third-party dependencies like ExDoc output generated docs. +/doc/ + +# Ignore .fetch files in case you like to edit your project deps locally. +/.fetch + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). +*.ez + +# Ignore package tarball (built via "mix hex.build"). +counter-*.tar + +# Temporary files, for example, from tests. +/tmp/ diff --git a/lectures/04/counter/README.md b/lectures/04/counter/README.md new file mode 100644 index 0000000..f15a4ad --- /dev/null +++ b/lectures/04/counter/README.md @@ -0,0 +1,21 @@ +# Counter + +**TODO: Add description** + +## Installation + +If [available in Hex](https://hex.pm/docs/publish), the package can be installed +by adding `counter` to your list of dependencies in `mix.exs`: + +```elixir +def deps do + [ + {:counter, "~> 0.1.0"} + ] +end +``` + +Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) +and published on [HexDocs](https://hexdocs.pm). Once published, the docs can +be found at . + diff --git a/lectures/04/counter/counter.db b/lectures/04/counter/counter.db new file mode 100644 index 0000000..ddf901f --- /dev/null +++ b/lectures/04/counter/counter.db @@ -0,0 +1 @@ +ƒa \ No newline at end of file diff --git a/lectures/04/counter/lib/counter.ex b/lectures/04/counter/lib/counter.ex new file mode 100644 index 0000000..2e364fe --- /dev/null +++ b/lectures/04/counter/lib/counter.ex @@ -0,0 +1,18 @@ +defmodule Counter do + @moduledoc """ + Documentation for `Counter`. + """ + + @doc """ + Hello world. + + ## Examples + + iex> Counter.hello() + :world + + """ + def hello do + :world + end +end diff --git a/lectures/04/counter/lib/counter/application.ex b/lectures/04/counter/lib/counter/application.ex new file mode 100644 index 0000000..a32efdd --- /dev/null +++ b/lectures/04/counter/lib/counter/application.ex @@ -0,0 +1,21 @@ +defmodule Counter.Application do + # See https://hexdocs.pm/elixir/Application.html + # for more information on OTP Applications + @moduledoc false + + use Application + + @impl true + def start(_type, _args) do + children = [ + # Starts a worker by calling: Counter.Worker.start_link(arg) + {Counter.Store, "counter.db"}, # we must start the store before the worker + {Counter.Worker, 0} + ] + + # See https://hexdocs.pm/elixir/Supervisor.html + # for other strategies and supported options + opts = [strategy: :one_for_one, name: Counter.Supervisor] + Supervisor.start_link(children, opts) + end +end diff --git a/lectures/04/counter/lib/counter/store.ex b/lectures/04/counter/lib/counter/store.ex new file mode 100644 index 0000000..ba43dea --- /dev/null +++ b/lectures/04/counter/lib/counter/store.ex @@ -0,0 +1,38 @@ +defmodule Counter.Store do + use GenServer + + # client + def start_link(file) do + GenServer.start_link(__MODULE__, file, name: __MODULE__) + end + + def put(value) do + GenServer.cast(__MODULE__, {:put, value}) + end + + def get() do + GenServer.call(__MODULE__, :get) + end + + # server + @impl true + def init(file) do + {:ok, file} + end + + @impl true + def handle_call(:get, _from, file) do + reply = + case File.read(file) do + {:ok, content} -> :erlang.binary_to_term(content) + _ -> nil + end + {:reply, reply, file} + end + + @impl true + def handle_cast({:put, value}, file) do + File.write(file, :erlang.term_to_binary(value)) + {:noreply, file} + end +end diff --git a/lectures/04/counter/lib/counter/worker.ex b/lectures/04/counter/lib/counter/worker.ex new file mode 100644 index 0000000..e3f2201 --- /dev/null +++ b/lectures/04/counter/lib/counter/worker.ex @@ -0,0 +1,45 @@ +defmodule Counter.Worker do + use GenServer + + @store Counter.Store + + # client + def start_link(n \\ 0) do + GenServer.start_link(__MODULE__, n, name: __MODULE__) + end + + def inc(amt \\ 1) do + GenServer.cast(__MODULE__, {:inc, amt}) + end + + def value() do + GenServer.call(__MODULE__, :value) + end + + # server + @impl true + def init(arg) do + {:ok, @store.get() || arg} + end + + @impl true + def handle_call(:value, _from, state) do + {:reply, state, state} + end + + @impl true + def handle_cast({:inc, amt}, state) do + {:noreply, state + amt} + end + + # for demonstration only + @impl true + def handle_info(:reset, _state) do + {:noreply, 0} + end + + @impl true + def terminate(_reason, state) do + @store.put(state) + end +end diff --git a/lectures/04/counter/mix.exs b/lectures/04/counter/mix.exs new file mode 100644 index 0000000..a48c727 --- /dev/null +++ b/lectures/04/counter/mix.exs @@ -0,0 +1,29 @@ +defmodule Counter.MixProject do + use Mix.Project + + def project do + [ + app: :counter, + version: "0.1.0", + elixir: "~> 1.17", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger], + mod: {Counter.Application, []} + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + # {:dep_from_hexpm, "~> 0.3.0"}, + # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} + ] + end +end diff --git a/lectures/04/counter/test/counter_test.exs b/lectures/04/counter/test/counter_test.exs new file mode 100644 index 0000000..99884d6 --- /dev/null +++ b/lectures/04/counter/test/counter_test.exs @@ -0,0 +1,8 @@ +defmodule CounterTest do + use ExUnit.Case + doctest Counter + + test "greets the world" do + assert Counter.hello() == :world + end +end diff --git a/lectures/04/counter/test/test_helper.exs b/lectures/04/counter/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/lectures/04/counter/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/lectures/04/counter2/.formatter.exs b/lectures/04/counter2/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/lectures/04/counter2/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/lectures/04/counter2/.gitignore b/lectures/04/counter2/.gitignore new file mode 100644 index 0000000..ccae38f --- /dev/null +++ b/lectures/04/counter2/.gitignore @@ -0,0 +1,26 @@ +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where third-party dependencies like ExDoc output generated docs. +/doc/ + +# Ignore .fetch files in case you like to edit your project deps locally. +/.fetch + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). +*.ez + +# Ignore package tarball (built via "mix hex.build"). +counter-*.tar + +# Temporary files, for example, from tests. +/tmp/ diff --git a/lectures/04/counter2/README.md b/lectures/04/counter2/README.md new file mode 100644 index 0000000..f15a4ad --- /dev/null +++ b/lectures/04/counter2/README.md @@ -0,0 +1,21 @@ +# Counter + +**TODO: Add description** + +## Installation + +If [available in Hex](https://hex.pm/docs/publish), the package can be installed +by adding `counter` to your list of dependencies in `mix.exs`: + +```elixir +def deps do + [ + {:counter, "~> 0.1.0"} + ] +end +``` + +Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) +and published on [HexDocs](https://hexdocs.pm). Once published, the docs can +be found at . + diff --git a/lectures/04/counter2/lib/counter/application.ex b/lectures/04/counter2/lib/counter/application.ex new file mode 100644 index 0000000..da1b98f --- /dev/null +++ b/lectures/04/counter2/lib/counter/application.ex @@ -0,0 +1,21 @@ +defmodule Counter.Application do + # See https://hexdocs.pm/elixir/Application.html + # for more information on OTP Applications + @moduledoc false + + use Application + + @impl true + def start(_type, _args) do + children = [ + # Starts a worker by calling: Counter.Worker.start_link(arg) + Supervisor.child_spec({Counter.Worker, :c1}, id: :counter1), + Supervisor.child_spec({Counter.Worker, :c2}, id: :counter2) + ] + + # See https://hexdocs.pm/elixir/Supervisor.html + # for other strategies and supported options + opts = [strategy: :one_for_one, name: Counter.Supervisor] + Supervisor.start_link(children, opts) + end +end diff --git a/lectures/04/counter2/lib/counter/worker.ex b/lectures/04/counter2/lib/counter/worker.ex new file mode 100644 index 0000000..5bd0920 --- /dev/null +++ b/lectures/04/counter2/lib/counter/worker.ex @@ -0,0 +1,38 @@ +defmodule Counter.Worker do + use GenServer + + # client + def start_link(name) do + GenServer.start_link(__MODULE__, nil, name: name) + end + + def inc(name, amt \\ 1) do + GenServer.cast(name, {:inc, amt}) + end + + def value(name) do + GenServer.call(name, :value) + end + + # server impl + @impl true + def init(_arg) do + {:ok, 0} + end + + @impl true + def handle_call(:value, _from, state) do + {:reply, state, state} + end + + @impl true + def handle_cast({:inc, amt}, state) do + {:noreply, state + amt} + end + + # for demonstration only + @impl true + def handle_info(:reset, _state) do + {:noreply, 0} + end +end diff --git a/lectures/04/counter2/mix.exs b/lectures/04/counter2/mix.exs new file mode 100644 index 0000000..a48c727 --- /dev/null +++ b/lectures/04/counter2/mix.exs @@ -0,0 +1,29 @@ +defmodule Counter.MixProject do + use Mix.Project + + def project do + [ + app: :counter, + version: "0.1.0", + elixir: "~> 1.17", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger], + mod: {Counter.Application, []} + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + # {:dep_from_hexpm, "~> 0.3.0"}, + # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} + ] + end +end diff --git a/lectures/04/counter2/test/counter_test.exs b/lectures/04/counter2/test/counter_test.exs new file mode 100644 index 0000000..99884d6 --- /dev/null +++ b/lectures/04/counter2/test/counter_test.exs @@ -0,0 +1,8 @@ +defmodule CounterTest do + use ExUnit.Case + doctest Counter + + test "greets the world" do + assert Counter.hello() == :world + end +end diff --git a/lectures/04/counter2/test/test_helper.exs b/lectures/04/counter2/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/lectures/04/counter2/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/lectures/04/lec4.ex b/lectures/04/lec4.ex new file mode 100644 index 0000000..8fe5c2f --- /dev/null +++ b/lectures/04/lec4.ex @@ -0,0 +1,36 @@ +defmodule Lec4 do + def create(n \\ 4) do + Enum.map(1..n, fn _ -> parent = self(); spawn(fn -> loop(parent) end) end) + end + + def info(pid) when is_pid(pid) do + case Process.info(pid, [:trap_exit, :links]) do + nil -> {pid, :dead} + x -> {pid, x} + end + end + + def info(pids) when is_list(pids) do + Enum.map(pids, &info/1) + end + + def trap_exit(pid) do + send(pid, :trap_exit) + end + + def link(pid, other) do + send(pid, {:link, other}) + end + + defp loop(parent) do + receive do + :trap_exit -> + Process.flag(:trap_exit, true) + {:link, other} -> + Process.link(other) + x -> + send(parent, x) + end + loop(parent) + end +end diff --git a/lectures/04/notes.ex b/lectures/04/notes.ex new file mode 100644 index 0000000..25b2d4b --- /dev/null +++ b/lectures/04/notes.ex @@ -0,0 +1,31 @@ +# raise("hell") # runtime error +# exit(:die) # typtically exit with an atom +# throw(:what) +# throw(1) # can throw anything + +try do + 1/0 +catch type, value -> + IO.inspect({type, value}) +end + +# 3 types of runime error +try do + raise("hell") +catch type, value -> + IO.inspect({type, value}) +end + +try do + throw :up +catch type, value -> + IO.inspect({type, value}) +end + +try do + exit(1) +catch type, value -> + IO.inspect({type, value}) +after + IO.puts("done") +end diff --git a/note.md b/note.md index 7fb0cac..55f8488 100644 --- a/note.md +++ b/note.md @@ -10,3 +10,4 @@ # projects `mix new ` creates a prject dir with name `iex -S mix` runs mix script +`mix new --sup`