Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Metaprogramming examples

metaprogramming.livemd

Metaprogramming examples

Description

The idea is to documentate examples of good usage of metaprogramming with Elixir for future reference and learning.

Run local DB with Docker.

docker run --rm -it -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 postgres:13.2

General Setup

Installing Dependencies and configuring the App

Installing dependencies and configuring the DB running on the container to work with our app and Kino.

Mix.install([
  {:ecto, "~> 3.10"},
  {:ecto_sql, "~> 3.10"},
  {:postgrex, "~> 0.17.3"},
  {:kino, "~> 0.11.0"},
  {:kino_db, "~> 0.2.4"},
  {:timex, "~> 3.0"}
])
defmodule Repo do
  use Ecto.Repo, otp_app: :my_app, adapter: Ecto.Adapters.Postgres
end
Kino.start_child({Repo, url: "postgres://postgres:postgres@localhost/postgres"})

Creating tables

defmodule Migrations.AddWeatherTable do
  use Ecto.Migration

  def up do
    # Drop the table if it already exists
    drop_if_exists(table("weather"))

    # Create the table
    create table("weather") do
      add(:city, :string, size: 40)
      add(:temp_lo, :integer)
      add(:temp_hi, :integer)
      add(:prcp, :float)
      add(:last_rain_at, :utc_datetime_usec)

      timestamps()
    end
  end

  def down do
    drop(table("weather"))
  end
end
Ecto.Migrator.down(Repo, 1, Migrations.AddWeatherTable)
Ecto.Migrator.up(Repo, 1, Migrations.AddWeatherTable)

Populating Tables

defmodule Weather do
  use Ecto.Schema

  schema "weather" do
    field(:city, :string)
    field(:temp_lo, :integer)
    field(:temp_hi, :integer)
    field(:prcp, :float, default: 0.0)
    field(:last_rain_at, :utc_datetime_usec)

    timestamps()
  end
end
use Timex

today = Timex.now()
two_days_ago = today |> Timex.shift(days: -2)

Repo.delete_all(Weather)

%Weather{temp_lo: 0, temp_hi: 23} |> Repo.insert!()
%Weather{temp_lo: 0, temp_hi: 23, last_rain_at: today} |> Repo.insert!()
%Weather{temp_lo: 0, temp_hi: 23, last_rain_at: two_days_ago} |> Repo.insert!()

Showing current DB state for reference

Weather |> Repo.all() |> Kino.DataTable.new()

Macros

defmodule Transactions do
  import Ecto.Query

  defmacro coalesce_now(binding) do
    quote do
      coalesce(unquote(binding), fragment("TIMEZONE('utc', NOW())"))
    end
  end

  # def coalesce_now(binding) do
  # coalesce(binding, fragment("TIMEZONE('utc', NOW())"))
  # end

  def filter_by_last_rain(query, date) do
    where(query, [w], coalesce_now(w.last_rain_at) >= ^date)
  end
end
import Ecto.Query
use Timex

yesterday = Timex.now() |> Timex.shift(days: -1)

q = from(w in Weather) |> Transactions.filter_by_last_rain(yesterday)
q |> Repo.all() |> Kino.DataTable.new()