Created new settings table, schema, and context
This commit is contained in:
parent
4ff2cc445f
commit
3a6930457b
5 changed files with 253 additions and 80 deletions
28
lib/pinchflat/settings/setting.ex
Normal file
28
lib/pinchflat/settings/setting.ex
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
defmodule Pinchflat.Settings.Setting do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
@allowed_fields [
|
||||
:onboarding,
|
||||
:pro_enabled,
|
||||
:yt_dlp_version
|
||||
]
|
||||
|
||||
@required_fields ~w(
|
||||
onboarding
|
||||
pro_enabled
|
||||
)a
|
||||
|
||||
schema "settings" do
|
||||
field :onboarding, :boolean, default: false
|
||||
field :pro_enabled, :boolean, default: false
|
||||
field :yt_dlp_version, :string
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(setting, attrs) do
|
||||
setting
|
||||
|> cast(attrs, @allowed_fields)
|
||||
|> validate_required(@required_fields)
|
||||
end
|
||||
end
|
||||
64
lib/pinchflat/settings/settings.ex
Normal file
64
lib/pinchflat/settings/settings.ex
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
defmodule Pinchflat.Settings do
|
||||
@moduledoc """
|
||||
The Settings context.
|
||||
"""
|
||||
import Ecto.Query, warn: false
|
||||
|
||||
alias Pinchflat.Repo
|
||||
alias Pinchflat.Settings.Setting
|
||||
|
||||
@doc """
|
||||
Returns the only setting record. It _should_ be impossible
|
||||
to create or delete this record, so it's assertive about
|
||||
assuming it's the only one.
|
||||
|
||||
Returns %Setting{}
|
||||
"""
|
||||
def record do
|
||||
Setting
|
||||
|> limit(1)
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Updates a setting, returning the new value.
|
||||
Is setup to take a keyword list argument so you
|
||||
can call it like `Settings.set(onboarding: true)`
|
||||
|
||||
Returns {:ok, value} | {:error, :invalid_key} | {:error, %Ecto.Changeset{}}
|
||||
"""
|
||||
def set([{attr, value}]) do
|
||||
record()
|
||||
|> Setting.changeset(%{attr => value})
|
||||
|> Repo.update()
|
||||
|> case do
|
||||
{:ok, %{^attr => _}} -> {:ok, value}
|
||||
{:ok, _} -> {:error, :invalid_key}
|
||||
{:error, changeset} -> {:error, changeset}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the value of a setting.
|
||||
|
||||
Returns {:ok, value} | {:error, :invalid_key}
|
||||
"""
|
||||
def get(name) do
|
||||
case Map.fetch(record(), name) do
|
||||
{:ok, value} -> {:ok, value}
|
||||
:error -> {:error, :invalid_key}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the value of a setting, raising if it doesn't exist.
|
||||
|
||||
Returns value
|
||||
"""
|
||||
def get!(name) do
|
||||
case get(name) do
|
||||
{:ok, value} -> value
|
||||
{:error, _} -> raise "Setting `#{name}` not found"
|
||||
end
|
||||
end
|
||||
end
|
||||
18
priv/repo/migrations/20240404174144_create_settings.exs
Normal file
18
priv/repo/migrations/20240404174144_create_settings.exs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
defmodule Pinchflat.Repo.Migrations.CreateSettings do
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
create table(:settings) do
|
||||
add :onboarding, :boolean, default: false, null: false
|
||||
add :pro_enabled, :boolean, default: false, null: false
|
||||
add :yt_dlp_version, :string
|
||||
end
|
||||
|
||||
# Make an initial record because this will be the only one ever inserted
|
||||
execute "INSERT INTO settings (onboarding, pro_enabled, yt_dlp_version) VALUES (false, false, NULL)"
|
||||
end
|
||||
|
||||
def down do
|
||||
drop table(:settings)
|
||||
end
|
||||
end
|
||||
108
test/pinchflat/settings_backup_test.exs
Normal file
108
test/pinchflat/settings_backup_test.exs
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
defmodule Pinchflat.SettingsBackupTest do
|
||||
use Pinchflat.DataCase
|
||||
|
||||
alias Pinchflat.SettingsBackup
|
||||
alias Pinchflat.SettingsBackup.SettingBackup
|
||||
|
||||
# NOTE: We're treating some of these tests differently
|
||||
# than in other modules because certain settings
|
||||
# are always created on app boot (including in the test env),
|
||||
# so we can't treat these like a clean slate.
|
||||
|
||||
describe "list_settings/0" do
|
||||
test "returns all settings" do
|
||||
SettingsBackup.set!("foo", "bar")
|
||||
results = SettingsBackup.list_settings()
|
||||
|
||||
assert Enum.all?(results, fn setting -> match?(%SettingBackup{}, setting) end)
|
||||
end
|
||||
end
|
||||
|
||||
describe "set/2" do
|
||||
test "creates a new setting if one does not exist" do
|
||||
original = Repo.aggregate(SettingBackup, :count, :id)
|
||||
SettingsBackup.set!("foo", "bar")
|
||||
assert Repo.aggregate(SettingBackup, :count, :id) == original + 1
|
||||
end
|
||||
|
||||
test "updates an existing setting if one exists" do
|
||||
SettingsBackup.set!("foo", "bar")
|
||||
original = Repo.aggregate(SettingBackup, :count, :id)
|
||||
SettingsBackup.set!("foo", "baz")
|
||||
assert Repo.aggregate(SettingBackup, :count, :id) == original
|
||||
assert SettingsBackup.get!("foo") == "baz"
|
||||
end
|
||||
|
||||
test "returns the parsed value" do
|
||||
assert SettingsBackup.set!("foo", true) == true
|
||||
assert SettingsBackup.set!("foo", false) == false
|
||||
assert SettingsBackup.set!("foo", 123) == 123
|
||||
assert SettingsBackup.set!("foo", 12.34) == 12.34
|
||||
assert SettingsBackup.set!("foo", "bar") == "bar"
|
||||
end
|
||||
|
||||
test "allows for atom keys" do
|
||||
assert SettingsBackup.set!(:foo, "bar") == "bar"
|
||||
end
|
||||
|
||||
test "blows up when an unsupported datatype is used" do
|
||||
assert_raise FunctionClauseError, fn ->
|
||||
SettingsBackup.set!("foo", nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "set/3" do
|
||||
test "allows manual specification of datatype" do
|
||||
assert SettingsBackup.set!("foo", "true", :boolean) == true
|
||||
assert SettingsBackup.set!("foo", "false", :boolean) == false
|
||||
assert SettingsBackup.set!("foo", "123", :integer) == 123
|
||||
assert SettingsBackup.set!("foo", "12.34", :float) == 12.34
|
||||
end
|
||||
end
|
||||
|
||||
describe "get/1" do
|
||||
test "returns the value of the setting" do
|
||||
SettingsBackup.set!("str", "bar")
|
||||
SettingsBackup.set!("bool", true)
|
||||
SettingsBackup.set!("int", 123)
|
||||
SettingsBackup.set!("float", 12.34)
|
||||
|
||||
assert SettingsBackup.get!("str") == "bar"
|
||||
assert SettingsBackup.get!("bool") == true
|
||||
assert SettingsBackup.get!("int") == 123
|
||||
assert SettingsBackup.get!("float") == 12.34
|
||||
end
|
||||
|
||||
test "allows for atom keys" do
|
||||
SettingsBackup.set!("str", "bar")
|
||||
assert SettingsBackup.get!(:str) == "bar"
|
||||
end
|
||||
|
||||
test "blows up when the setting does not exist" do
|
||||
assert_raise Ecto.NoResultsError, fn ->
|
||||
SettingsBackup.get!("foo")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "fetch/2" do
|
||||
test "creates a setting if one doesn't exist" do
|
||||
original = Repo.aggregate(SettingBackup, :count, :id)
|
||||
assert SettingsBackup.fetch!("foo", "bar") == "bar"
|
||||
assert Repo.aggregate(SettingBackup, :count, :id) == original + 1
|
||||
end
|
||||
|
||||
test "returns an existing setting if one does exist" do
|
||||
SettingsBackup.set!("foo", "bar")
|
||||
|
||||
assert SettingsBackup.fetch!("foo", "baz") == "bar"
|
||||
end
|
||||
end
|
||||
|
||||
describe "fetch/3" do
|
||||
test "allows manual specification of datatype" do
|
||||
assert SettingsBackup.fetch!("foo", "true", :boolean) == true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,108 +1,63 @@
|
|||
defmodule Pinchflat.SettingsBackupTest do
|
||||
defmodule Pinchflat.SettingsTest do
|
||||
use Pinchflat.DataCase
|
||||
|
||||
alias Pinchflat.SettingsBackup
|
||||
alias Pinchflat.SettingsBackup.SettingBackup
|
||||
alias Pinchflat.Settings
|
||||
alias Pinchflat.Settings.Setting
|
||||
|
||||
# NOTE: We're treating some of these tests differently
|
||||
# than in other modules because certain settings
|
||||
# are always created on app boot (including in the test env),
|
||||
# so we can't treat these like a clean slate.
|
||||
|
||||
describe "list_settings/0" do
|
||||
test "returns all settings" do
|
||||
SettingsBackup.set!("foo", "bar")
|
||||
results = SettingsBackup.list_settings()
|
||||
setup do
|
||||
# Ensure we have a clean slate
|
||||
Settings.set(onboarding: false)
|
||||
Settings.set(pro_enabled: false)
|
||||
Settings.set(yt_dlp_version: nil)
|
||||
|
||||
assert Enum.all?(results, fn setting -> match?(%SettingBackup{}, setting) end)
|
||||
:ok
|
||||
end
|
||||
|
||||
describe "record/0" do
|
||||
test "returns the only setting" do
|
||||
assert %Setting{} = Settings.record()
|
||||
end
|
||||
end
|
||||
|
||||
describe "set/2" do
|
||||
test "creates a new setting if one does not exist" do
|
||||
original = Repo.aggregate(SettingBackup, :count, :id)
|
||||
SettingsBackup.set!("foo", "bar")
|
||||
assert Repo.aggregate(SettingBackup, :count, :id) == original + 1
|
||||
describe "set/1" do
|
||||
test "updates the setting" do
|
||||
assert {:ok, true} = Settings.set(onboarding: true)
|
||||
assert {:ok, true} = Settings.get(:onboarding)
|
||||
end
|
||||
|
||||
test "updates an existing setting if one exists" do
|
||||
SettingsBackup.set!("foo", "bar")
|
||||
original = Repo.aggregate(SettingBackup, :count, :id)
|
||||
SettingsBackup.set!("foo", "baz")
|
||||
assert Repo.aggregate(SettingBackup, :count, :id) == original
|
||||
assert SettingsBackup.get!("foo") == "baz"
|
||||
test "returns an error if the setting key doesn't exist" do
|
||||
assert {:error, :invalid_key} = Settings.set(foo: "bar")
|
||||
end
|
||||
|
||||
test "returns the parsed value" do
|
||||
assert SettingsBackup.set!("foo", true) == true
|
||||
assert SettingsBackup.set!("foo", false) == false
|
||||
assert SettingsBackup.set!("foo", 123) == 123
|
||||
assert SettingsBackup.set!("foo", 12.34) == 12.34
|
||||
assert SettingsBackup.set!("foo", "bar") == "bar"
|
||||
end
|
||||
|
||||
test "allows for atom keys" do
|
||||
assert SettingsBackup.set!(:foo, "bar") == "bar"
|
||||
end
|
||||
|
||||
test "blows up when an unsupported datatype is used" do
|
||||
assert_raise FunctionClauseError, fn ->
|
||||
SettingsBackup.set!("foo", nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "set/3" do
|
||||
test "allows manual specification of datatype" do
|
||||
assert SettingsBackup.set!("foo", "true", :boolean) == true
|
||||
assert SettingsBackup.set!("foo", "false", :boolean) == false
|
||||
assert SettingsBackup.set!("foo", "123", :integer) == 123
|
||||
assert SettingsBackup.set!("foo", "12.34", :float) == 12.34
|
||||
test "returns an error if the setting value is invalid" do
|
||||
assert {:error, %Ecto.Changeset{}} = Settings.set(onboarding: "bar")
|
||||
end
|
||||
end
|
||||
|
||||
describe "get/1" do
|
||||
test "returns the value of the setting" do
|
||||
SettingsBackup.set!("str", "bar")
|
||||
SettingsBackup.set!("bool", true)
|
||||
SettingsBackup.set!("int", 123)
|
||||
SettingsBackup.set!("float", 12.34)
|
||||
|
||||
assert SettingsBackup.get!("str") == "bar"
|
||||
assert SettingsBackup.get!("bool") == true
|
||||
assert SettingsBackup.get!("int") == 123
|
||||
assert SettingsBackup.get!("float") == 12.34
|
||||
test "returns the setting value" do
|
||||
assert {:ok, false} = Settings.get(:onboarding)
|
||||
end
|
||||
|
||||
test "allows for atom keys" do
|
||||
SettingsBackup.set!("str", "bar")
|
||||
assert SettingsBackup.get!(:str) == "bar"
|
||||
test "returns an error if the setting key doesn't exist" do
|
||||
assert {:error, :invalid_key} = Settings.get(:foo)
|
||||
end
|
||||
end
|
||||
|
||||
describe "get!/1" do
|
||||
test "returns the setting value" do
|
||||
assert Settings.get!(:onboarding) == false
|
||||
end
|
||||
|
||||
test "blows up when the setting does not exist" do
|
||||
assert_raise Ecto.NoResultsError, fn ->
|
||||
SettingsBackup.get!("foo")
|
||||
test "raises an error if the setting key doesn't exist" do
|
||||
assert_raise RuntimeError, "Setting `foo` not found", fn ->
|
||||
Settings.get!(:foo)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "fetch/2" do
|
||||
test "creates a setting if one doesn't exist" do
|
||||
original = Repo.aggregate(SettingBackup, :count, :id)
|
||||
assert SettingsBackup.fetch!("foo", "bar") == "bar"
|
||||
assert Repo.aggregate(SettingBackup, :count, :id) == original + 1
|
||||
end
|
||||
|
||||
test "returns an existing setting if one does exist" do
|
||||
SettingsBackup.set!("foo", "bar")
|
||||
|
||||
assert SettingsBackup.fetch!("foo", "baz") == "bar"
|
||||
end
|
||||
end
|
||||
|
||||
describe "fetch/3" do
|
||||
test "allows manual specification of datatype" do
|
||||
assert SettingsBackup.fetch!("foo", "true", :boolean) == true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue