kriwil.com

Aldi on Random Stuff
2004 - 2024
Oct 27, 2023

The Ruff Formatter

TL;DR: The Ruff formatter is an extremely fast Python formatter, written in Rust. It’s over 30x faster than Black and 100x faster than YAPF, formatting large-scale Python projects in milliseconds — all while achieving >99.9% Black compatibility.

Sep 10, 2023

Download Twitter Archive

I haven't used twitter for a while, so I guess while I remember, I should probably download my data there. Here's the steps if you wonder:

From twitter page go to:
MoreSettings and SupportSettings and privacyYour accountDownload an archive of your data

Then just follow the instructions.

Nov 2, 2019

Phoenix Live View, The Beginner Guide

Table of Contents

[TOC]

This guide assumes you have Elixir and Phoenix installed. So go do that if you haven't.

If you don't know what LiveView is, basically a wrapper around websocket to transfer data and update the page seamlessly. I suggest you watch Chris McCord talk about it on ElixirConf EU 2019 to understand more.

Initial Setup

Now, let's dive into it. First you need to create Phoenix project.

```
$ mix phx.new random_cat --no-ecto
```

Answer Y to Fetch and install dependencies? question. We're passing --no-ecto flag because what we're going to create doesn't have anything to do with database.

To make sure it's working, run the server, and then go open http://localhost:4000 on your browser. You should see "Welcome to Phoenix!" message along with some links.

```
$ cd random_cat
$ mix phx.server
```

The page is controlled by index function inside lib/controllers/page_controller.ex. Now, we need to install LiveView. To do that, open mix.exs and add {:phoenix_live_view, "~> 0.3.1"} to deps, and while we're at it, add httposion as well. It's HTTP client library, and we need that for our small project:

```
defp deps do
[
  {:phoenix, "~> 1.4.9"},
  {:phoenix_pubsub, "~> 1.1"},
  {:phoenix_html, "~> 2.11"},
  {:phoenix_live_view, "~> 0.3.1"},
  {:phoenix_live_reload, "~> 1.2", only: :dev},
  {:httpoison, "~> 1.6.1"},
  {:gettext, "~> 0.11"},
  {:jason, "~> 1.0"},
  {:plug_cowboy, "~> 2.0"}
]
end
```

Stop the server and install the added library by running mix deps.get. Then you can re-run the server using mix phx.server.

LiveView

This is where the fun starts. First define the /live endpoint. Open lib/random_cat_web/endpoint.ex and add socket "/live", Phoenix.LiveView.Socket:

```
defmodule RandomCatWeb.Endpoint do
  use Phoenix.Endpoint, otp_app: :random_cat

  socket "/live", Phoenix.LiveView.Socket
  ...
end
```

Then configure the signing salt, open config/config.exs, and add live_view: [signing_salt: ...]:

```
config :random_cat, RandomCatWeb.Endpoint,
  url: [host: "localhost"],
  secret_key_base: "abc",
  render_errors: [view: RandomCatWeb.ErrorView, accepts: ~w(html json)],
  pubsub: [name: RandomCat.PubSub, adapter: Phoenix.PubSub.PG2],
  live_view: [signing_salt: "xyz"]
```

You can generate the salt by running mix phx.gen.secret 32.

Next we need to initialize the socket connection via javascript. First add phoenix_live_view npm dependencies:

```
{
  "dependencies": {
    "phoenix": "file:../../../deps/phoenix",
    "phoenix_html": "file:../../../deps/phoenix_html",
    "phoenix_live_view": "file:../../../deps/phoenix_live_view"
  }
}
```

And run npm install --prefix assets.

Then Open assets/js/app.js and insert these lines:

```
import {Socket} from "phoenix"
import LiveSocket from "phoenix_live_view"

let liveSocket = new LiveSocket("/live", Socket)
liveSocket.connect()
```

For now, that's all javascript we have to write in order to make things work.

Now we create the actual LiveView. Create new file inside lib/random_cat_web/live (you need to create live directory first), called cat_live.ex. Insert this code block:

```
defmodule RandomCatWeb.CatLive do
  use Phoenix.LiveView


  def render(assigns) do
    ~L"""
    <img src="<%= @url %>">
    """
  end

  def mount(%{}, socket) do
    fallback = "http://lorempixel.com/g/500/300/animals/"
    case HTTPoison.get("http://aws.random.cat/meow") do
      {:ok, %HTTPoison.Response{body: body}} ->
        case Jason.decode(body) do
          {:ok, %{"file" => url}} ->
            {:ok, assign(socket, :url, url)}
          {:error, _} ->
            {:ok, assign(socket, :url, fallback)}
        end

      {:error, _} ->
        {:ok, assign(socket, :url, fallback)}
    end
  end
end
```

We'll explore this part by part. Basically LiveView needs to have 2 functions in order for it to works, render and mount. First part is the render.

```
def render(assigns) do
  ~L"""
  <img src="<%= @url %>">
  """
end
```

This part defines how you will render the live view content. This could be a whole page, or just part of a page. We could also use template file for this(we'll convert to template file later). The ~L part is the sigil for live view template.

```
def mount(%{}, socket) do
  fallback = "http://lorempixel.com/g/500/300/animals/"
  case HTTPoison.get("http://aws.random.cat/meow") do
    {:ok, %HTTPoison.Response{body: body}} ->
      case Jason.decode(body) do
        {:ok, %{"file" => url}} ->
          {:ok, assign(socket, :url, url)}
        {:error, _} ->
          {:ok, assign(socket, :url, fallback)}
      end

    {:error, _} ->
      {:ok, assign(socket, :url, fallback)}
  end
end
```

The next one is mount. Mount is accessed when the LiveView is rendered for the first time. Our mount is simple. It's trying to fetch data from http://aws.random.cat/meow, and use fallback it the data is invalid, or the fetch is failed. If we get the data sucessfully, we show the cat image.

The last part is add the LiveView to the router. Open lib/random_cat_web/router.ex, andd add live "/cat", CatLive to scope "/" so:

```
scope "/", RandomCatWeb do
  pipe_through :browser

  get "/", PageController, :index
  live "/cat", CatLive
end
```

Now everything is ready, restart the server just to be sure, then open http://localhost:4000/cat The cat image should show up.

Right now, the only way to update the image it to do the page refresh. Next, we're going to change that and add a button to update the image without refreshing the page.

LiveView Event Handling

The way event being handled is you can call single function from the page. It's done via socket, but you don't need to do anything with that. All we need is use the provided binding on our element and let LiveView do the rest.

First, let's update the template. We add a button that will trigger function on the server, using phx-click binding. Your render function should be similiar to this:

```
def render(assigns) do
  ~L"""
  <div>
    <img src="<%= @url %>">
  </div>
  <button phx-click="moar">moar!</button>
  """
end
```

Next we create the function that will handle that click event. What we want to do is to update the image everytime the button is clicked. To do that, we refactor the mount function, so we could re-use the HTTP call routine. Your mount should be like this:

```
def mount(_session, socket) do
  {:ok, assign(socket, :url, get_cat_url())}
end
```

And for the get_cat_url function, it will always return url.

```
defp get_cat_url() do
  fallback = "http://lorempixel.com/g/500/300/animals/"
  case HTTPoison.get("http://aws.random.cat/meow") do
    {:ok, %HTTPoison.Response{body: body}} ->
      case Jason.decode(body) do
        {:ok, %{"file" => url}} ->
          url
        {:error, _} ->
          fallback
      end

    {:error, _} ->
      fallback
  end
end
```

Last, in function to handle moar event, we assign :url to new url from get_cat_url everytime the function is called.

```
def handle_event("moar", _values, socket) do
  {:noreply, assign(socket, :url, get_cat_url())}
end
```

Your whole cat_live.ex content should be like this:

```
defmodule RandomCatWeb.CatLive do
  use Phoenix.LiveView


  def render(assigns) do
    ~L"""
    <div>
      <img src="<%= @url %>">
    </div>
    <button phx-click="moar">moar!</button>
    """
  end

  def mount(_session, socket) do
    {:ok, assign(socket, :url, get_cat_url())}
  end

  def handle_event("moar", _values, socket) do
    {:noreply, assign(socket, :url, get_cat_url())}
  end

  defp get_cat_url() do
    fallback = "http://lorempixel.com/g/500/300/animals/"
    case HTTPoison.get("http://aws.random.cat/meow") do
      {:ok, %HTTPoison.Response{body: body}} ->
        case Jason.decode(body) do
          {:ok, %{"file" => url}} ->
            url
          {:error, _} ->
            fallback
        end

      {:error, _} ->
        fallback
    end
  end
end
```

Now go restart the server and try clicking the button. Now we could update the image without restarting the page. Remember we only write 4 lines of javascript so far.

Hooks

The last thing that I want to show is how to bind more javascript code. In our case, we want to be able to maybe show loading message after the button is clicked. We could use phx-hook for that.

First, update your app.js to this:

```
let Hooks = {};
Hooks.MoarButton = {
  mounted() {
    this.el.addEventListener('click', e => {
      this.el.innerText = 'Loading ...';
      this.el.disabled = true;
    });
  }
};

let liveSocket = new LiveSocket('/live', Socket, { hooks: Hooks });
```

This hook will set the button to Loading ..., and disable it once the element is clicked.

To use the hook, update the button in the template to use phx-hook:

```
...
<button phx-click="moar" phx-hook="MoarButton">moar!</button>
...
```

So there the very basic way to setup LiveView. If you want to know more, you could check the documentation on hexdocs.pm. I hope that helps, and if you see anything wrong or invalid, please tweet me @kriwil. Thank you for reading!

Oct 23, 2018

Microservice Architecture at Medium

Even though it is much better supported by new technologies, microservice architecture still involves a high level of complexity and complication. For small teams to start, a monolithic app is still often a better option. However, do spend the time to architect the monolithic app in a way that is easier to migrate to a microservice architecture later when the system and the team grow.

Microsevices definitely help you scale. But keep in mind setting up (good) microsevices is hard and takes time.

Oct 21, 2018

DHH on working with family people

From the experience I've had working with family people, I've found an amazing ability to get stuff done when the objectives are reasonably clear, the work appears to have meaning, and if it can be done within the scope of what should constitute a work week. When there are real constraints on your time, like you have to pickup the kids or make them dinner or put them to bed it appears to bring a serenity of focus to the specific hours dedicated to work.

Constrain is indeed helpful. It makes you better at managing your time, and your tasks.

Oct 21, 2018

Gmail Creator and YC Partner Paul Buchheit on Joining Google, How to Become a Great Engineer and Happiness

From then on, video games just seemed like a pointless waste of time compared to programming.

I wished I had that kind of passion.

That was enough to fit a small Linux install, but it wasn't enough room for Emacs which is why I'm a vi person.

\cough\

I wanted to go work on Linux stuff and figured I'd at least meet some smart people there, and maybe they'd later start a company that would actually be successful.

Paul Buchheit when asked why he took Google's offer.

When they sat down with me they said, "we want you to build an email something." That was all the specification I got! So I went off to build something with email, which became Gmail.

Basically he created Gmail without any spec. And now maybe the most popular email provider.

Oct 20, 2018

Joel Spolsky on his first BillG Review

I thought about how strange it was that he had two legs, two arms, one head, etc., almost exactly like a regular human being.

Joel Spolky, seeing Bill Gates for the first time.

I always love this kind of story.

Oct 18, 2018

Bill Gates, Remembering Paul Allen

Paul foresaw that computers would change the world. Even in high school, before any of us knew what a personal computer was, he was predicting that computer chips would get super-powerful and would eventually give rise to a whole new industry. That insight of his was the cornerstone of everything we did together.

Paul deserved more time in life. He would have made the most of it. I will miss him tremendously.

Oct 17, 2018

elementary OS 5 Juno is Here

elementary OS is made up of two main parts: the "desktop" which includes the core user experience, look and feel, and system pieces; and the apps that come with the OS out of the box. elementary OS 5 Juno includes major updates across several of these core apps.

I know what I'm going to do this weekend.

Oct 16, 2018

GitHub annual game jam

Everyone around the world is welcome to participate, from newbies to professional game developers—and your game can be as simple or complex as you want. It's a great excuse to learn a new technology, collaborate on something over the weekends with friends, or create a game for the first time!

I've never created game before, even though that was my reason jumping in to programming. Could be very interesting.

Oct 15, 2018

How I've Attracted The First 500 Paid Users For My SaaS That Costs $5/mo

Again, you are a part of your product. Your blogposts have to be your story, not someone's. People read your articles, then they will be interested in you. Some my customers said they would like to support me rather than my product. Telling your story would increase your product value.

Been following Inkdrop stories. A lot could be learned from him.

Oct 15, 2018

Football Manager 2019

Doesn't seem to have significant improvement. And no Linux version (yet?). The perfect time to stop buying the series.

Oct 15, 2018

Fascinating story from Google+ team

If your team, say on Gmail or Android, was to integrate Google+'s features then your team would be awarded a 1.5-3x multiplier on top of your yearly bonus. Your bonus was already something like 15% of your salary.

That's why it spread like a virus.

And lot more gems in the thread.

Oct 14, 2018

AutoMono

Programming font derived from Roboto Mono (with a hint of Monaco, SF Mono, Gotham & Futura)

Looks good initially.