Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calc examples #1655

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions documentation/how-to/localized-datetime.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Localized DateTime

## Introduction
Most datetimes will be recorded at UTC. This is useful, but we are often in a specific timezone that's not UTC and we'd like to see the date/time in our current locale. This is a perfect usecase for an `Ash.Calculation`.

```elixir
defmodule App.Calculations.LocalDateTime do
use Ash.Resource.Calculation

@impl true
def init(opts), do: {:ok, opts}

@impl true
def load(_query, _opt, _context), do: []

@impl true
def calculate(records, opts, _context) do
Enum.map(records, fn record ->
record
|> Map.get(opts[:field])
|> Timex.to_datetime("Asia/Hong_Kong")
end)
end

@impl true
def expression(opts, _context) do
expr(fragment("((? AT TIME ZONE 'UTC') AT TIME ZONE 'Asia/Hong_Kong')", ^ref(opts[:field])))
end
end
```

The above code can be adjusted to a passed in value or even read the value from the context, but this is only an example to show how you can implement this calculation in both the data query and Elixir code.
98 changes: 98 additions & 0 deletions documentation/how-to/translated-fields.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Translated Fields

## Introduction

Some websites may need to have database fields in multiple languages and only show certain fields pending a locale. We can create a calculation to handle this quite easily.

```elixir
defmodule App.Calculations.LocalizeName do
use Ash.Resource.Calculation

@impl true
def init(opts), do: {:ok, opts}

@impl true
def load(_query, _opts, _context), do: []

@impl true
def expression(opts, context) do
field = to_string(opts[:field])
en = (field <> "_en") |> String.to_existing_atom()
cn = (field <> "_zh_CN") |> String.to_existing_atom()
hk = (field <> "_zh_HK") |> String.to_existing_atom()
actor = Map.get(context, :actor)

locale =
case context do
%{source_context: %{locale: locale}} when is_atom(locale) or is_binary(locale) ->
locale

_ ->
nil
end

cond do
not is_nil(actor) ->
expr(
cond do
type(^actor(:locale), :string) == :zh_CN && not is_nil(^ref(cn)) ->
^ref(cn)

type(^actor(:locale), :string) == :zh_HK && not is_nil(^ref(hk)) ->
^ref(hk)

true ->
^ref(en)
end
)

not is_nil(locale) ->
expr(
cond do
type(^locale, :string) == :zh_CN && not is_nil(^ref(cn)) ->
^ref(cn)

type(^locale, :string) == :zh_HK && not is_nil(^ref(hk)) ->
^ref(hk)

true ->
^ref(en)
end
)

true ->
expr(^ref(en))
end
end
end

defmodule App.Tickets.Ticket do
use Ash.Resource,
data_layer: AshPostgres.DataLayer,
domain: App.Tickets.Domain

postgres do
repo App.Repo
table "tickets"
end

attributes do
uuid_primary_key :id
attribute :name_en, :string, allow_nil?: false, public?: true
attribute :name_zh_CN, :string, allow_nil?: true, public?: true
attribute :name_zh_HK, :string, allow_nil?: true, public?: true
timestamps()
end

calculations do
calculate :name, :string, {App.Calculations.LocalizeName, [field: :name]}
end

actions do
defaults [:create, :read, :update, :destroy]
default_accept :*
end
end
```

The above code will automatically choose the right name field based on the current locale passed in as an argument, or the current actor's locale that's specified. If none are passed in, then it will show the English name specified by the `name_en`, which is required.
Loading