Skip to content
Closed
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
86 changes: 86 additions & 0 deletions lib/mix/tasks/gen/ash.gen.resources.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
if Code.ensure_loaded?(Igniter) do
defmodule Mix.Tasks.Ash.Gen.Resources do
@example """
mix ash.gen.resources "Helpdesk.Support.Ticket --attribute subject:string:required --timestamps;Helpdesk.Support.Representative --uuid-primary-key id;Helpdesk.Support.Comment --attribute body:text --relationship belongs_to:ticket:Helpdesk.Support.Ticket"
"""
@moduledoc """
Generate and configure multiple Ash.Resource modules from a semicolon-separated list.

This task takes a semicolon-separated list of resource names with their individual options
and generates each one using the `ash.gen.resource` task.

Each resource entry in the list can have its own specific options. The format is:
`"ResourceName --option1 value1 --option2;AnotherResource --option3 value3"`

## Example

```bash
#{@example}
```

## Resource Options

Each resource supports all options from `mix ash.gen.resource`:

* `--attribute` or `-a` - An attribute or comma separated list of attributes to add, as `name:type`. Modifiers: `primary_key`, `array`, `public`, `sensitive`, and `required`. i.e `-a name:string:required`
* `--relationship` or `-r` - A relationship or comma separated list of relationships to add, as `type:name:dest`. Modifiers: `public`. `belongs_to` only modifiers: `primary_key`, `sensitive`, and `required`. i.e `-r belongs_to:author:MyApp.Accounts.Author:required`
* `--default-actions` - A csv list of default action types to add. The `create` and `update` actions accept the public attributes being added.
* `--uuid-primary-key` or `-u` - Adds a UUIDv4 primary key with that name. i.e `-u id`
* `--uuid-v7-primary-key` - Adds a UUIDv7 primary key with that name.
* `--integer-primary-key` or `-i` - Adds an integer primary key with that name. i.e `-i id`
* `--domain` or `-d` - The domain module to add the resource to. i.e `-d MyApp.MyDomain`. This defaults to the resource's module name, minus the last segment.
* `--extend` or `-e` - A comma separated list of modules or builtins to extend the resource with. i.e `-e postgres,Some.Extension`
* `--base` or `-b` - The base module to use for the resource. i.e `-b Ash.Resource`. Requires that the module is in `config :your_app, :base_resources`
* `--timestamps` or `-t` - If set adds `inserted_at` and `updated_at` timestamps to the resource.
* `--ignore-if-exists` - Does nothing if the resource already exists
* `--conflicts` - How to handle conflicts when the same attribute, relationship, or action already exists. Options: `ignore` (default), `replace`
`ignore` will ignore your addition for that attribute, relationship, or action. `replace` will remove the existing one in favor of yours.
"""

@shortdoc "Generate and configure multiple Ash.Resource modules from a semicolon-separated list."
use Igniter.Mix.Task

@impl Igniter.Mix.Task
def info(_argv, _parent) do
%Igniter.Mix.Task.Info{
positional: [:resources],
example: @example,
schema: [],
aliases: []
}
end

@impl Igniter.Mix.Task
def igniter(igniter) do
igniter.args.positional.resources
|> String.split(";", trim: true)
|> Enum.map(&String.trim/1)
|> Enum.reduce(igniter, fn resource_entry, igniter ->
argv = String.split(resource_entry, ~r/\s+/, trim: true)
Igniter.compose_task(igniter, "ash.gen.resource", argv)
end)
end
end
else
defmodule Mix.Tasks.Ash.Gen.Resources do
@moduledoc """
Generate and configure multiple Ash.Resource modules from a semicolon-separated list.
"""

@shortdoc "Generate and configure multiple Ash.Resource modules from a semicolon-separated list."

use Mix.Task

def run(_argv) do
Mix.shell().error("""
The task 'ash.gen.resources' requires igniter to be run.

Please install igniter and try again.

For more information, see: https://hexdocs.pm/igniter
""")

exit({:shutdown, 1})
end
end
end
120 changes: 120 additions & 0 deletions test/mix/tasks/ash.gen.resources_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
defmodule Mix.Tasks.Ash.Gen.ResourcesTest do
use ExUnit.Case
import Igniter.Test

@moduletag :igniter

test "generates complete blog system with different resource configurations" do
test_project()
|> Igniter.compose_task("ash.gen.resources", [
"MyApp.Blog.Post --uuid-primary-key id --attribute title:string:required:public,body:text:public,status:atom --relationship belongs_to:author:MyApp.Accounts.User:required --default-actions read,create,update --timestamps;MyApp.Blog.Comment --attribute content:text:required:public --relationship belongs_to:post:MyApp.Blog.Post:required,belongs_to:author:MyApp.Accounts.User:required --default-actions read,create;MyApp.Accounts.User --uuid-primary-key id --attribute name:string:required:public,email:string:required:public --default-actions read,create,update"
])
|> assert_creates("lib/my_app/blog/post.ex", """
defmodule MyApp.Blog.Post do
use Ash.Resource,
otp_app: :test,
domain: MyApp.Blog

attributes do
uuid_primary_key(:id)

attribute :title, :string do
allow_nil?(false)
public?(true)
end

attribute :body, :text do
public?(true)
end

attribute(:status, :atom)
timestamps()
end

relationships do
belongs_to :author, MyApp.Accounts.User do
allow_nil?(false)
end
end

actions do
defaults([:read, create: [:title, :body], update: [:title, :body]])
end
end
""")
|> assert_creates("lib/my_app/blog/comment.ex", """
defmodule MyApp.Blog.Comment do
use Ash.Resource,
otp_app: :test,
domain: MyApp.Blog

attributes do
attribute :content, :text do
allow_nil?(false)
public?(true)
end
end

relationships do
belongs_to :post, MyApp.Blog.Post do
allow_nil?(false)
end

belongs_to :author, MyApp.Accounts.User do
allow_nil?(false)
end
end

actions do
defaults([:read, create: [:content]])
end
end
""")
|> assert_creates("lib/my_app/accounts/user.ex", """
defmodule MyApp.Accounts.User do
use Ash.Resource,
otp_app: :test,
domain: MyApp.Accounts

attributes do
uuid_primary_key(:id)

attribute :name, :string do
allow_nil?(false)
public?(true)
end

attribute :email, :string do
allow_nil?(false)
public?(true)
end
end

actions do
defaults([:read, create: [:name, :email], update: [:name, :email]])
end
end
""")
|> assert_creates("lib/my_app/blog.ex", """
defmodule MyApp.Blog do
use Ash.Domain,
otp_app: :test

resources do
resource(MyApp.Blog.Post)
resource(MyApp.Blog.Comment)
end
end
""")
|> assert_creates("lib/my_app/accounts.ex", """
defmodule MyApp.Accounts do
use Ash.Domain,
otp_app: :test

resources do
resource(MyApp.Accounts.User)
end
end
""")
end
end
Loading