···11import Config
2233# Configure your database
44-config :sower, Sower.Repo, database: "./_build/dev.db"
44+config :sower, Sower.Repo,
55+ username: "postgres",
66+ password: "postgres",
77+ hostname: "localhost",
88+ database: "sower_dev",
99+ port: 5432,
1010+ show_sensitive_data_on_connection_error: true,
1111+ pool_size: 10
512613# For development, we disable any cache and enable
714# debugging and code reloading.
+5-6
config/runtime.exs
···2525 working_dir: System.get_env("SOWER_WORKDIR", "#{File.cwd() |> elem(1)}/tmp")
26262727if config_env() == :prod do
2828- database_path =
2929- System.get_env("SOWER_DATABASE_PATH") ||
2828+ database_url =
2929+ System.get_env("SOWER_DATABASE_URL") ||
3030 raise """
3131- environment variable SOWER_DATABASE_PATH is missing.
3232- For example: /var/lib/sower/sower.db
3131+ environment variable SOWER_DATABASE_URL is missing.
3232+ For example: ecto://postgres:postgres@localhost/ecto_simple
3333 """
34343535- maybe_ipv6 = if System.get_env("ECTO_IPV6") in ~w(true 1), do: [:inet6], else: []
3636- config :sower, Sower.Repo, database: database_path, socket_options: maybe_ipv6
3535+ config :sower, Sower.Repo, url: database_url, socket_options: [:inet6]
37363837 # The secret key base is used to sign/encrypt cookies and other secrets.
3938 # A default value is used in config/dev.exs and config/test.exs but you
···11defmodule Sower do
22- @moduledoc """
33- Sower keeps the contexts that define your domain
44- and business logic.
22+ use Ash.Domain
5366- Contexts are also responsible for managing your data, regardless
77- if it comes from the database, an external API or others.
88- """
44+ resources do
55+ resource Sower.Seed
66+ end
97end
+6-3
lib/sower/repo.ex
···11defmodule Sower.Repo do
22- use Ecto.Repo,
33- otp_app: :sower,
44- adapter: Ecto.Adapters.SQLite3
22+ use AshPostgres.Repo, otp_app: :sower
33+44+ # Installs Postgres extensions that ash commonly uses
55+ def installed_extensions do
66+ ["uuid-ossp", "citext"]
77+ end
58end
+48-30
lib/sower/seed.ex
···11defmodule Sower.Seed do
22- import Ecto.Query
33- alias Sower.Repo
22+ use Ash.Resource,
33+ data_layer: AshPostgres.DataLayer,
44+ domain: Sower
4555- def list_seeds do
66- Repo.all(Sower.Seed.Instance)
77- end
66+ actions do
77+ defaults([:read, :create, :destroy])
8899- def create_or_insert_seed(attrs \\ %{}) do
1010- %Sower.Seed.Instance{}
1111- |> Sower.Seed.Instance.changeset(attrs)
1212- |> Repo.insert(
1313- on_conflict: {:replace, [:updated_at]},
1414- conflict_target: [:name, :type, :out_path]
1515- )
99+ create :new do
1010+ accept([:name, :type])
1111+ end
1612 end
17131818- def find_latest_seed(name, type) do
1919- Sower.Seed.Instance
2020- |> where([s], s.name == ^name)
2121- |> where([s], s.type == ^type)
2222- |> order_by([s], desc: s.updated_at)
2323- |> first()
2424- |> Repo.all()
2525- |> List.first()
2626- end
1414+ attributes do
1515+ uuid_primary_key(:id)
27162828- def get_seed!(id), do: Repo.get!(Sower.Seed.Instance, id)
1717+ attribute :name, :string do
1818+ allow_nil?(false)
1919+ public?(true)
2020+ end
29213030- def update_seed(id, attrs \\ %{}) do
3131- seed = get_seed!(id)
2222+ attribute :type, :atom do
2323+ allow_nil?(false)
2424+ public?(true)
2525+ constraints(one_of: [:nixos, :home_manager, :nix_darwin])
2626+ end
2727+ end
32283333- seed
3434- |> Sower.Seed.Instance.changeset(attrs)
3535- |> Repo.update()
2929+ code_interface do
3030+ define(:new, args: [:name, :type])
3631 end
37323838- def delete_seed(id) do
3939- get_seed!(id)
4040- |> Repo.delete()
3333+ postgres do
3434+ table("seeds")
3535+ repo(Sower.Repo)
4136 end
3737+3838+ # relationships do
3939+ # belongs_to :tree, Sower.Tree
4040+ # end
4241end
4242+4343+# def create_or_insert_seed(attrs \\ %{}) do
4444+# %Sower.Seed.Instance{}
4545+# |> Sower.Seed.Instance.changeset(attrs)
4646+# |> Repo.insert(
4747+# on_conflict: {:replace, [:updated_at]},
4848+# conflict_target: [:name, :type, :out_path]
4949+# )
5050+# end
5151+#
5252+# def find_latest_seed(name, type) do
5353+# Sower.Seed.Instance
5454+# |> where([s], s.name == ^name)
5555+# |> where([s], s.type == ^type)
5656+# |> order_by([s], desc: s.updated_at)
5757+# |> first()
5858+# |> Repo.all()
5959+# |> List.first()
6060+# end
···11+defmodule Sower.Repo.Migrations.Install2Extensions20240409161420 do
22+ @moduledoc """
33+ Installs any extensions that are mentioned in the repo's `installed_extensions/0` callback
44+55+ This file was autogenerated with `mix ash_postgres.generate_migrations`
66+ """
77+88+ use Ecto.Migration
99+1010+ def up do
1111+ execute("CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"")
1212+ execute("CREATE EXTENSION IF NOT EXISTS \"citext\"")
1313+ end
1414+1515+ def down do
1616+ # Uncomment this if you actually want to uninstall the extensions
1717+ # when this migration is rolled back:
1818+ # execute("DROP EXTENSION IF EXISTS \"uuid-ossp\"")
1919+ # execute("DROP EXTENSION IF EXISTS \"citext\"")
2020+ end
2121+end
+21
priv/repo/migrations/20240409161423_add_seeds.exs
···11+defmodule Sower.Repo.Migrations.AddSeeds do
22+ @moduledoc """
33+ Updates resources based on their most recent snapshots.
44+55+ This file was autogenerated with `mix ash_postgres.generate_migrations`
66+ """
77+88+ use Ecto.Migration
99+1010+ def up do
1111+ create table(:seeds, primary_key: false) do
1212+ add :id, :uuid, null: false, default: fragment("gen_random_uuid()"), primary_key: true
1313+ add :name, :text, null: false
1414+ add :type, :text, null: false
1515+ end
1616+ end
1717+1818+ def down do
1919+ drop table(:seeds)
2020+ end
2121+end