quesurifn
So, I’m building an SMTP client that I’d like to be able to be called from different processes and have different state for each process. The SMTP client needs to track for example if the HELO command has been used or not. How do I handle state?
My current code is this:
defmodule Client do
@moduledoc """
Documentation for `Smtpclient`.
"""
use Agent
def start_link(_opts) do
Agent.start_link(fn -> %{} end, name: __MODULE__)
end
def connect(pid, tld) do
set_value(pid, "tld", tld)
{:ok, records} = DNS.resolve(tld, :mx)
Enum.sort_by(records, fn(r) -> elem(r, 0) end)
sock = do_connect(records, 0)
set_socket(pid, sock)
set_value(pid, "hostname", tld)
{:ok, sock}
end
@spec helo(any) :: any
def helo(pid) do
already_helloed = get_value(pid, "already_helloed")
if already_helloed do # if already helloed return early
{:error, "already helloed"}
end
sock = get_socket(pid)
tld = get_value(pid, "tld")
sock |> Socket.Stream.send!("HELO #{tld}")
set_value(pid, "already_helloed", true)
sock |> Socket.Stream.recv!
end
def noop(pid) do
try do
sock = get_socket(pid)
sock |> Socket.Stream.send!("NOOP")
sock |> Socket.Stream.recv!
rescue
Socker.Error -> {:error, :no_connection}
end
end
def quit(sock) do
try do
sock |> Socket.Stream.send!("QUIT")
sock |> Socket.Stream.recv!
sock |> Socket.close!()
rescue
Socket.Error -> {:error, "something is wrong"}
end
end
def rctp_to(pid, email) do
try do
sock = get_socket(pid)
sock |> Socket.Stream.send!("RCPT_TO #{email}")
sock |> Socket.Stream.recv!
rescue
Socker.Error -> {:error, "something is wrong"}
end
end
defp set_socket(pid, sock) do
Agent.update(pid, &Map.put(&1, "socket", sock))
end
defp get_socket(pid) do
Agent.get(pid, &Map.get(&1, "socket"))
end
defp set_value(pid, key, value) do
Agent.update(pid, &Map.put(&1, key, value))
end
defp get_value(pid, key) do
Agent.get(pid, &Map.get(&1, key))
end
defp do_connect(hostnames, i) do
try do
host = elem(Enum.at(hostnames, i), 1)
Socket.TCP.connect!(to_string(host), 25, packet: :line)
rescue
Socket.Error ->
if Enum.at(hostnames, i) == nil do
{:error, "all mx servers down"}
end
do_connect(hostnames, i + 1)
end
end
end
Trending in Chat/Questions
Hey folks, I’ve been using Elixir for over a couple of years (phx, ecto, broadway, oban).
For this kind of work you do not tend, in my e...
New
Hi, I’m Dheeraj Sudan from the UK. I’m a software developer and also run a business with my wife Meenu Hinduja. I’m interested in getting...
New
Other Trending Topics
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve.
They are GUI (Emerge) and State management (S...
New
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #blog-post
- #phoenix_html
- #iex
- #graphql
- #ai
- #genstage
- #elixirconf-us
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex










First 5 of 5 Posts
hauleth
You probably should use
:gen_statemif you want state machine. It will make a lot of stuff much easier. Additionally if you want SMTP client/server then there is already implementation of that in Erlanghttps://github.com/gen-smtp/gen_smtp
You can use that either as an implementation or as inspiration.
Sebb
I find
:gen_statemvery hard to understand. I prefer functional fsm with the state in map/struct and pattern matching in multiclauses as handlers. Also because fsm can become hard to test if they have side effects (like timeouts) I think its worth to think about how to make them as pure as possible.IloSophiep
I think there are two “default” ways to handle state (and a lot more specific ones):
Because Elixir is really good with processes people sometimes expect everything they code to do “process-y” stuff. But if you can, i think, it is recommended to start simple and do not introduce processes if you don’t need to. Your first sentence makes me think, that might work for you.
As an example: You have a module
TodoListwith a functionadd/1that adds one item to the list. You might think “How / Where do i keep that list, so callingadd/1three times keeps all the old entries still there?” The easiest answer is "Just return the data to the caller and let them keep the state around! So instead ofyou use
so the caller of your client keeps the state around and hands it to you for you to work on it.
quesurifn
I might be wrong but the SMTP client requires a relay. I want a client that can delivery without a relay.
hauleth
SMTP Relay for client is just an regular SMTP Server. So if you can deliver via relay then you can deliver directly (as soon as you can do so in a way that will not cause your email to be rejected/marked as spam automatically).