Mykolas
Mykolas
AEAsh Elixir
Created by Deleted User on 2/7/2023 in #support
I18n within resources
And this is how a basic form for creating looks like:
56 replies
AEAsh Elixir
Created by Deleted User on 2/7/2023 in #support
I18n within resources
This is the usage:
defmodule Octafest.Blog.Post do
use Ash.Resource,
data_layer: AshPostgres.DataLayer

postgres do
table "posts"

repo Octafest.Repo
end

code_interface do
define_for Octafest.Blog
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
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
end

attributes do
uuid_primary_key :id

attribute :title, Octafest.Shared.Translated
attribute :content, Octafest.Shared.Translated
end
end
defmodule Octafest.Blog.Post do
use Ash.Resource,
data_layer: AshPostgres.DataLayer

postgres do
table "posts"

repo Octafest.Repo
end

code_interface do
define_for Octafest.Blog
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
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
end

attributes do
uuid_primary_key :id

attribute :title, Octafest.Shared.Translated
attribute :content, Octafest.Shared.Translated
end
end
56 replies
AEAsh Elixir
Created by Deleted User on 2/7/2023 in #support
I18n within resources
Ok so created a shared embed like so:
defmodule Octafest.Shared.Translated do
@behaviour Access

use Ash.Resource,
data_layer: :embedded

attributes do
for locale <- ~w(en lt)a do
attribute locale, :string
end
end

@impl Access
def fetch(term, key), do: Map.fetch(term, key)

@impl Access
def get_and_update(data, key, func) do
Map.get_and_update(data, key, func)
end

@impl Access
def pop(data, key), do: Map.pop(data, key)

@impl Access
def get(map, key, default), do: Map.get(map, key, default)

end
defmodule Octafest.Shared.Translated do
@behaviour Access

use Ash.Resource,
data_layer: :embedded

attributes do
for locale <- ~w(en lt)a do
attribute locale, :string
end
end

@impl Access
def fetch(term, key), do: Map.fetch(term, key)

@impl Access
def get_and_update(data, key, func) do
Map.get_and_update(data, key, func)
end

@impl Access
def pop(data, key), do: Map.pop(data, key)

@impl Access
def get(map, key, default), do: Map.get(map, key, default)

end
But the problem is still having the hardcoded locales. I mean as a workaround i can have that as a list of all available locales which for now is limited and then only show the relevant ones for the tenant?
56 replies
AEAsh Elixir
Created by Deleted User on 2/7/2023 in #support
I18n within resources
Hmm ok back at this, i have an interesting case and trying to figure out how to best do this: Octafest is a multi-tenant thing. And translations are really tenant bound and dynamic. For e.g. Tenant A has two languages English and Lithuanian. Default is Lithuanian. Tenant B has three languages English, Italian and German. Default is english. Since json columns don't really need a defined structure the way i've done it in php land is just have json columns for the translated stuff and then show the ui according to the tenants settings. Tenant settings define what languages they support and the default language. That in turn determines what get's populated in the translated field column form. Also i've switched out the relationship, so it's not:
model
-> translations
-> [
en -> [ name: 'English name', description: 'English description ],
lt -> [ name: 'Lithuanian name', description: 'Lithuanian description' ]
]
model
-> translations
-> [
en -> [ name: 'English name', description: 'English description ],
lt -> [ name: 'Lithuanian name', description: 'Lithuanian description' ]
]
But:
model -> name -> [en: 'English name', lt: 'Lithuanian name']
-> description -> [en: 'English description', lt: 'Lithuanian description']
model -> name -> [en: 'English name', lt: 'Lithuanian name']
-> description -> [en: 'English description', lt: 'Lithuanian description']
Any help is greatly appreciated
56 replies
AEAsh Elixir
Created by Deleted User on 2/7/2023 in #support
I18n within resources
It should have an empty value for each locale as i have a tabs thing where you can switch between languages. I'll have a look at the add_form thing 🙂 thank you! Ash is amazing 🙂
56 replies
AEAsh Elixir
Created by Deleted User on 2/7/2023 in #support
I18n within resources
Any help is greatly appreciated.
56 replies
AEAsh Elixir
Created by Deleted User on 2/7/2023 in #support
I18n within resources
I can't get this to render the form though i've tried adding forms: [auto?: true] but nothing happens:
<.simple_form for={@create_form} phx-submit="create_post">
<.input field={@create_form[:title]} type="text" placeholder="Title..." />
<.input field={@create_form[:content]} type="textarea" placeholder="Content..." />

<.inputs_for :let={translations} field={@create_form[:translations]}>
<.inputs_for :let={translation} :for={locale <- @locales} field={translations[locale]}>
<div class="grid gap-4">
<p class="text-sm uppercase text-gray-600"><%= locale %></p>
<.input field={translation[:title]} type="text" />
<.input field={translation[:content]} type="text" />
</div>
</.inputs_for>
</.inputs_for>

<.button>
create
</.button>
</.simple_form>
<.simple_form for={@create_form} phx-submit="create_post">
<.input field={@create_form[:title]} type="text" placeholder="Title..." />
<.input field={@create_form[:content]} type="textarea" placeholder="Content..." />

<.inputs_for :let={translations} field={@create_form[:translations]}>
<.inputs_for :let={translation} :for={locale <- @locales} field={translations[locale]}>
<div class="grid gap-4">
<p class="text-sm uppercase text-gray-600"><%= locale %></p>
<.input field={translation[:title]} type="text" />
<.input field={translation[:content]} type="text" />
</div>
</.inputs_for>
</.inputs_for>

<.button>
create
</.button>
</.simple_form>
56 replies
AEAsh Elixir
Created by Deleted User on 2/7/2023 in #support
I18n within resources
Yeah the whole point is to have client facing editable content for pages etc.
56 replies
AEAsh Elixir
Created by Deleted User on 2/7/2023 in #support
I18n within resources
because this is just for adding the translated fields. I then need to modify the create changeset to include the embeds with validations add read actions which accept locale and move the fields into top level access
56 replies
AEAsh Elixir
Created by Deleted User on 2/7/2023 in #support
I18n within resources
I'm thinking if it's worth the effort it's not that hard to do what you've shown and this adds quite a bit of magic that you can't see.
56 replies
AEAsh Elixir
Created by Deleted User on 2/7/2023 in #support
I18n within resources
Or is there a better/cleaner way to insert embeded schemas?
56 replies
AEAsh Elixir
Created by Deleted User on 2/7/2023 in #support
I18n within resources
Does this look like i'm moving in the right direction?
56 replies
AEAsh Elixir
Created by Deleted User on 2/7/2023 in #support
I18n within resources
now on to figuring out how to embed this, any pointers?
56 replies
AEAsh Elixir
Created by Deleted User on 2/7/2023 in #support
I18n within resources
ok got this to build 😄
i18n_attributes do
i18n_attribute(:slug, :string, constraints: [required: true, unique: true])
i18n_attribute(:title, :string)
end
i18n_attributes do
i18n_attribute(:slug, :string, constraints: [required: true, unique: true])
i18n_attribute(:title, :string)
end
56 replies
AEAsh Elixir
Created by Deleted User on 2/7/2023 in #support
I18n within resources
but this fails with
❯ mix phx.server
Compiling 4 files (.ex)

00:50:06.367 [error] Task #PID<0.303.0> started from #PID<0.287.0> terminating
** (FunctionClauseError) no function clause matching in Keyword.merge/2
(elixir 1.14.5) lib/keyword.ex:979: Keyword.merge([], true)
(spark 1.1.11) lib/spark/dsl/extension.ex:1078: anonymous fn/9 in Spark.Dsl.Extension.do_build_section/6
(elixir 1.14.5) lib/enum.ex:2468: Enum."-reduce/3-lists^foldl/2-0-"/3
(spark 1.1.11) lib/spark/dsl/extension.ex:1075: Spark.Dsl.Extension.do_build_section/6
lib/ash_i18n/resource.ex:48: anonymous fn/3 in :elixir_compiler_3.__MODULE__/1
(elixir 1.14.5) lib/task/supervised.ex:89: Task.Supervised.invoke_mfa/2
(elixir 1.14.5) lib/task/supervised.ex:34: Task.Supervised.reply/4
(stdlib 4.3.1) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
Function: #Function<0.104469677/0 in Kernel.ParallelCompiler.async/1>
Args: []

== Compilation error in file lib/ash_i18n/resource.ex ==
** (exit) an exception was raised:
** (FunctionClauseError) no function clause matching in Keyword.merge/2
(elixir 1.14.5) lib/keyword.ex:979: Keyword.merge([], true)
(spark 1.1.11) lib/spark/dsl/extension.ex:1078: anonymous fn/9 in Spark.Dsl.Extension.do_build_section/6
(elixir 1.14.5) lib/enum.ex:2468: Enum."-reduce/3-lists^foldl/2-0-"/3
(spark 1.1.11) lib/spark/dsl/extension.ex:1075: Spark.Dsl.Extension.do_build_section/6
lib/ash_i18n/resource.ex:48: anonymous fn/3 in :elixir_compiler_3.__MODULE__/1
(elixir 1.14.5) lib/task/supervised.ex:89: Task.Supervised.invoke_mfa/2
(elixir 1.14.5) lib/task/supervised.ex:34: Task.Supervised.reply/4
(stdlib 4.3.1) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
❯ mix phx.server
Compiling 4 files (.ex)

00:50:06.367 [error] Task #PID<0.303.0> started from #PID<0.287.0> terminating
** (FunctionClauseError) no function clause matching in Keyword.merge/2
(elixir 1.14.5) lib/keyword.ex:979: Keyword.merge([], true)
(spark 1.1.11) lib/spark/dsl/extension.ex:1078: anonymous fn/9 in Spark.Dsl.Extension.do_build_section/6
(elixir 1.14.5) lib/enum.ex:2468: Enum."-reduce/3-lists^foldl/2-0-"/3
(spark 1.1.11) lib/spark/dsl/extension.ex:1075: Spark.Dsl.Extension.do_build_section/6
lib/ash_i18n/resource.ex:48: anonymous fn/3 in :elixir_compiler_3.__MODULE__/1
(elixir 1.14.5) lib/task/supervised.ex:89: Task.Supervised.invoke_mfa/2
(elixir 1.14.5) lib/task/supervised.ex:34: Task.Supervised.reply/4
(stdlib 4.3.1) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
Function: #Function<0.104469677/0 in Kernel.ParallelCompiler.async/1>
Args: []

== Compilation error in file lib/ash_i18n/resource.ex ==
** (exit) an exception was raised:
** (FunctionClauseError) no function clause matching in Keyword.merge/2
(elixir 1.14.5) lib/keyword.ex:979: Keyword.merge([], true)
(spark 1.1.11) lib/spark/dsl/extension.ex:1078: anonymous fn/9 in Spark.Dsl.Extension.do_build_section/6
(elixir 1.14.5) lib/enum.ex:2468: Enum."-reduce/3-lists^foldl/2-0-"/3
(spark 1.1.11) lib/spark/dsl/extension.ex:1075: Spark.Dsl.Extension.do_build_section/6
lib/ash_i18n/resource.ex:48: anonymous fn/3 in :elixir_compiler_3.__MODULE__/1
(elixir 1.14.5) lib/task/supervised.ex:89: Task.Supervised.invoke_mfa/2
(elixir 1.14.5) lib/task/supervised.ex:34: Task.Supervised.reply/4
(stdlib 4.3.1) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
56 replies
AEAsh Elixir
Created by Deleted User on 2/7/2023 in #support
I18n within resources
here's my field:
defmodule AshI18n.I18nField do
defstruct name: "", validations: []

def i18n_attributes(resource) do
Spark.Dsl.Extension.get_entities(resource, [:i18n_attributes])
end
end
defmodule AshI18n.I18nField do
defstruct name: "", validations: []

def i18n_attributes(resource) do
Spark.Dsl.Extension.get_entities(resource, [:i18n_attributes])
end
end
56 replies
AEAsh Elixir
Created by Deleted User on 2/7/2023 in #support
I18n within resources
as i want to do this:
defmodule AshI18n.Resource do
alias AshI18n.I18nField

@i18n_attribute_schema [
name: [
type: :atom,
required: true,
doc: "The name of the field"
],
validations: [
type: :keyword_list,
doc: "The basic validations for the field",
default: [],
keys: [required: [type: :atom], unique: [type: :atom]]
]
]

@i18n_attribute %Spark.Dsl.Entity{
name: :i18n_attribute,
describe: "Adds a translated field",
examples: [
"field :slug, :required, :unique",
"field :title"
],
target: I18nField,
args: [:name, :validations],
auto_set_fields: true,
schema: @i18n_attribute_schema
}

@i18n_attributes %Spark.Dsl.Section{
name: :i18n_attributes,
describe: "A section for configuring translations for a resource.",
examples: [
"""
i18n_attributes do
field :slug, [:required, :unique]
field :title, []
end
"""
],
entities: [
@i18n_attribute
]
}

use Spark.Dsl.Extension,
sections: [@i18n_attributes],
transformers: [AshI18n.Resource.Transformers.SetupI18n]
end
defmodule AshI18n.Resource do
alias AshI18n.I18nField

@i18n_attribute_schema [
name: [
type: :atom,
required: true,
doc: "The name of the field"
],
validations: [
type: :keyword_list,
doc: "The basic validations for the field",
default: [],
keys: [required: [type: :atom], unique: [type: :atom]]
]
]

@i18n_attribute %Spark.Dsl.Entity{
name: :i18n_attribute,
describe: "Adds a translated field",
examples: [
"field :slug, :required, :unique",
"field :title"
],
target: I18nField,
args: [:name, :validations],
auto_set_fields: true,
schema: @i18n_attribute_schema
}

@i18n_attributes %Spark.Dsl.Section{
name: :i18n_attributes,
describe: "A section for configuring translations for a resource.",
examples: [
"""
i18n_attributes do
field :slug, [:required, :unique]
field :title, []
end
"""
],
entities: [
@i18n_attribute
]
}

use Spark.Dsl.Extension,
sections: [@i18n_attributes],
transformers: [AshI18n.Resource.Transformers.SetupI18n]
end
56 replies
AEAsh Elixir
Created by Deleted User on 2/7/2023 in #support
I18n within resources
where can i find a simple example implementing DSL.Entity
56 replies
AEAsh Elixir
Created by Deleted User on 2/7/2023 in #support
I18n within resources
ok on it
56 replies
AEAsh Elixir
Created by Deleted User on 2/7/2023 in #support
I18n within resources
where would i start?
56 replies