Announcing: AshSqlite! (beta)

Here’s the fix:

defmodule Octafest.Api.Post do
  use Ash.Resource,
    data_layer: AshSqlite.DataLayer

  sqlite do
    table "posts"

    repo Octafest.Repo
  end

  attributes do
    uuid_primary_key :id

    attribute :title, Octafest.Shared.Translated
    attribute :content, Octafest.Shared.Translated
  end

  actions do
    defaults ~w(create read update destroy)a

    read :by_id do
      argument :id, :uuid, allow_nil?: false
      get? true

      filter expr(id == ^arg(:id))
    end

    read :translated do
      argument :locale, :atom, allow_nil?: false

      prepare build(
                load: [
                  {:t_title, locale: arg(:locale)},
                  {:t_content, locale: arg(:locale)}
                ]
              )
    end
  end

  calculations do
    calculate :t_title, :string, {TranslateAttribute, attribute: :title} do
      argument :locale, :atom do
        default :en
      end
    end

    calculate :t_content, :string, {TranslateAttribute, attribute: :content} do
      argument :locale, :atom do
        default :en
      end
    end
  end

  code_interface do
    define_for Octafest.Api
    define :create, action: :create
    define :read_all, action: :read
    define :update, action: :update
    define :destroy, action: :destroy

    define :get_by_id, args: [:id], action: :by_id
    define :translated, args: [:locale], action: :translated
  end
end

defmodule TranslateAttribute do
  use Ash.Calculation

  @impl true
  def select(_, opts, _), do: [opts[:attribute]]

  @impl true
  @spec calculate(any(), any(), %{:locale => atom(), optional(any()) => any()}) :: list()
  def calculate(records, opts, %{locale: locale}) do
    records
    |> Enum.map(fn record ->
      record |> Map.get(opts[:attribute]) |> Map.get(locale)
    end)
  end
end

And this is how you call it:

defmodule OctafestWeb.AdminLive do
  use OctafestWeb, :admin_live_view
  alias Octafest.Api.Post

  def mount(_params, _session, socket) do
    posts = Post.translated!(:en)

    locales = ~w(en lt)a

    create_form = get_post_create_form()

    socket =
      assign(socket,
        posts: posts,
        locales: locales,
        locale: :en
      )

    {:ok, socket}
  end
end

But i’ve decided against using this, as this is quite a bit of code and while i could most likely add an extension it feels somewhat magical.