Skip to main content
memo.forof
  • index
  • tags
  • rss
  • GenServers and Phoenix.PubSubs and Task.Supervisors

    October 26 2024

    I've been looking for an #elixir redux-alike (primarly the event-bus stuff), and have found that Phoenix.PubSub paired with a GenServer and a Task supervisor seems to get the job done.

    I did chase a few different constructions, like using Task.start with callbacks, but found that GenServer doesn't have a predicable state when callbacks are executed.

    The following is scratched out, as there's opportunity to make it a bit more generic, like sending messages back to the original caller, etc.

    objective: Schedule async work that can crash.

    defmodule MyApp.Dispatcher do
      use GenServer
    
      @topic "MY_TOPIC"
    
      def start_link(_) do
        GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
      end
    
      def init(state \\ %{}) do
        # subscribe to events emitted elsewhere
        Phoenix.PubSub.subscribe(MyApp.PubSub, @topic)
    
        # Start a Task.Supervisor
        Task.Supervisor.start_link(name: MyApp.Dispatcher.TaskSupervisor)
    
        {:ok, state}
      end
    
      # handle messages sent from dispatched tasks
      def handle_info({:DOWN, _ref, :process, pid, _reason}, state) do
        next = Map.delete(state, pid)
    
        # broadcast that a task is complete
        PubSub.broadcast(MyApp.PubSub, @topic, {:tasks?, next})
    
        {:noreply, next}
      end
    
      def handle_info({_event, %{id: _id}} = msg, state) do
        task =
          Task.Supervisor.async_nolink(MyApp.Dispatcher.TaskSupervisor, fn ->
            # doing something async. It may or may not crash
            # For instance, maybe this does a database write
          end)
    
        {:noreply, Map.put(state, task.pid, task)}
      end
    
      def handle_info(_, state) do
        {:noreply, state}
      end
    end
  • Reader modes are insane

    October 25 2024

    I've been fiddling with content extraction using @mozilla/readability. As a data-source, what better candidate than this very-here website, so I made some revisions.

    The basic question is "what components must my webpage have in order to trigger reader mode". Surely, this is standardized.

    Well... extremely no, it seems. Having done some reading, the best I've come up with is that adding schema attributes won't hurt anything, but the ways in which browser reader modes parse content is highly eccentric. For instance, the following qualifies for reader mode using Firefox, but @mozilla/readability fails to extract a publishedTime.

    <article itemscope itemtype="https://schema.org/Article">
      <h1 class="post-title" itemprop="headline">Reader modes are insane</h1>
    
        <time datetime="2024-10-25" itemprop="datePublished">
          2024-10-25
        </time>
    
      <section class="post-content" itemprop="articleBody">
        <p>...</p>
      </section>
    </article>

    Links: #

    • https://www.ctrl.blog/entry/browser-reading-mode-parsers.html
    • https://schema.org/Article
  • component libraries

    October 23 2024

    A non-comprehensive list of #component libraries:

    • Chakra
    • DaisyUI
    • Flowbite
    • Kuma UI
    • MUI
    • Mantine
    • NextUI
    • Semantic UI
    • Shadcn/ui
    • Shoelace
  • elixir-ls bozo-mode

    October 22 2024

    #neovim #elixir formatter got you down? As in, not working no-sir?

    Unable to find formatter for /path/to/file.exs:
    ** (Mix.Error) Formatter plugin Phoenix.LiveView.HTMLFormatter cannot be found
    

    Fix:

    $ rm .elixir-ls/build

    Links #

    • https://elixirforum.com/t/elixirls-stopped-working-unable-to-find-formatter/59412/23
    • https://github.com/elixir-lsp/elixir-ls/issues/1110
  • neovim messages

    October 21 2024

    #neovim messages are often hard to grab. If you see an error near the status-line, print it out with :messages

  • docker, nginx, and root

    October 18 2024

    Need #nginx to run as non-root? Just use the unprivileged #docker image.

    FROM nginxinc/nginx-unprivileged:1.27.2-alpine
    WORKDIR /
    
    COPY ./my-html    /usr/share/nginx/html/
    COPY ./nginx.conf /etc/nginx/nginx.conf

    Since non-root can't use :80, the above image defaults to :8080, so any nginx configuration will need to reflect that, as well as any other infrastructure, like istio or kubernetes.

    # nginx.conf
    
    worker_processes  1;
    pid               /tmp/nginx.pid;
    
    events {
      worker_connections 1024;
    }
    
    http {
      server {
        listen 8080;
      }
    }

    Links #

    • https://hub.docker.com/r/nginxinc/nginx-unprivileged
    • https://medium.com/@Kfir-G/securing-docker-non-root-user-best-practices-5784ac25e755
    • https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html
  • GenServer and concurrency

    October 17 2024

    I've built a GenServer pub/sub to handle async effects from my #phoenix application, but have been wondering how it handles multiple broadcasts. Given that a GenServer processes messages from a queue, how can I concurrently handle those messages.

    Answer: by dispatching to Task

    defmodule Subscriber do
      use GenServer
    
      alias Handler
    
      def start_link(_opts) do
        GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
      end
    
      @impl true
      def init(_) do
        Phoenix.PubSub.subscribe(MyPubSub, "my-topic")
    
        {:ok, nil}
      end
    
      @impl true
      def handle_info(msg, _) do
        # Task.start for fire-and-forget
        Task.start(Handler, :handle, [msg])
    
        {:noreply, nil}
      end
    end
    
    defmodule Handler do
      def handle({:foo, %{url: url }}) do
        # Maybe this is an API request to another service
      end
    
      def handle({:bar, %{url: url }}) do
        # ...
      end
    end

    Links #

    • https://elixirforum.com/t/whats-the-best-pattern-for-a-pubsub-event-listener-genserver-seems-like-the-wrong-choice/35703
  • robots.txt and AI crawlers

    October 16 2024

    staaaaahp #

    Trying a variety of incantations to block the #ai crawlers, though it'll take constant revision. May build a lil' generator for it.

    User-agent: GPTBot
    Disallow: /
    User-agent: ChatGPT-User
    Disallow: /
    User-agent: Google-Extended
    Disallow: /
    User-agent: PerplexityBot
    Disallow: /
    User-agent: Amazonbot
    Disallow: /
    User-agent: ClaudeBot
    Disallow: /
    User-agent: Omgilibot
    Disallow: /
    User-Agent: FacebookBot
    Disallow: /
    User-Agent: Applebot
    Disallow: /
    User-agent: anthropic-ai
    Disallow: /
    User-agent: Bytespider
    Disallow: /
    User-agent: Claude-Web
    Disallow: /
    User-agent: Diffbot
    Disallow: /
    User-agent: ImagesiftBot
    Disallow: /
    User-agent: Omgilibot
    Disallow: /
    User-agent: Omgili
    Disallow: /
    User-agent: YouBot
    Disallow: /
    

    #deno install #

    Seems deno continues to converge towards v2 node - there's an install command in v2. Will revise some deno.json to see how the lockfiles work in CI.

    Links #

    • https://www.cyberciti.biz/web-developer/block-openai-bard-bing-ai-crawler-bots-using-robots-txt-file/
    • https://github.com/ai-robots-txt/ai.robots.txt
    • https://www.404media.co/websites-are-blocking-the-wrong-ai-scrapers-because-ai-companies-keep-making-new-ones/
    • https://docs.deno.com/runtime/fundamentals/modules/#integrity-checking-and-lock-files
  • On parsing, and also private networks

    October 15 2024

    #fly.io and flycast #

    I've been working on some html -> markdown conversions with #elixir. It's been quite tricky to build a general-purpose utility for this, and so I've despaired by deciding to just do it with #deno. This means deploying a small deno app that accepts API requests from an elixir app. I don't want it on the public internet, and fly.io has a pretty simple way to support non-public facing apps that still benefit from fly proxy features, like auto-starting machines.

    fly ips allocate-v6 --private
    

    Now, from my other fly machines, I can simply make requests to http://my-fly-app.flycast.

    #finch vs ipv6 #

    Blurgh, because these requests are on an #ipv6 private network, I had to swap out #finch for #req, as there's no obvious way (to me) by which I can set finch's connection options. I also had to revise the deno app to use :: as a host.

    Links #

    • https://fly.io/docs/blueprints/private-applications-flycast
    • https://community.fly.io/t/trouble-connecting-to-private-app/10698/3
    • https://danschultzer.com/posts/ipv6-only-network-in-elixir
  • An Ollama cheatsheet

    October 14 2024

    An #ollama cheatsheet...

    Also, I saw a hero trailer on netflix that had obviously been AI dubbed... sounded totally demented.

    links #

    • https://secretdatascientist.com/ollama-cheatsheet/
3/4
  • Previous
  • Next