Skip to content

Commit

Permalink
add docs of conditional fields
Browse files Browse the repository at this point in the history
  • Loading branch information
shahryarjb committed Nov 7, 2023
1 parent eea2641 commit 693fd52
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 3 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

We tried to deliver a series of our client's [**CMS**](https://github.com/mishka-group/mishka-cms) built on [**Elixir**](https://elixir-lang.org/) at the start of the [**Mishka Group**](https://github.com/mishka-group) project, but we recently archived this open-source project and have yet to make plans to rework and expand it. This system was created using [**Phoenix**](https://www.phoenixframework.org/) and [**Phoenix LiveView**](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html). After a long period, a series of macros and functional modules emerged from this project and our other projects, which we are gradually publishing in this library.

> **NOTICE**: Do not use the master branch; this library is under heavy development. Expect version `0.1.1`, and for using the new features, please wait until a new release is out.
> **NOTICE**: Do not use the master branch; this library is under heavy development. Expect version `0.1.2`, and for using the new features, please wait until a new release is out.
---

Expand All @@ -24,7 +24,7 @@ The package can be installed by adding `mishka_developer_tools` to your list of
```elixir
def deps do
[
{:mishka_developer_tools, "~> 0.1.1"}
{:mishka_developer_tools, "~> 0.1.2"}
]
end
```
Expand Down
92 changes: 92 additions & 0 deletions guidance/guarded-struct.md
Original file line number Diff line number Diff line change
Expand Up @@ -948,3 +948,95 @@ end
```

**Note**: You can see when you use it inside a derive, the GuardedStruct calculates the you module `alias`.

21. #### Conditional fields

One of the unique capabilities of this macro is the ability to define conditions and differentiate between the various kinds of `fields`. Assume that you want the `social` field to be able to take both a value `string` and a `map` where `address` and `provider` are included in the `map`.
It is important to notice that the `conditional_field` contained within this macro have the capability of supporting `sub_field`. You can look at some illustrations down below.

```elixir
defmodule ConditionalFieldComplexTest do
use GuardedStruct
alias ConditionalFieldValidatorTestValidators, as: VAL

guardedstruct do
field(:provider, String.t())

sub_field(:profile, struct()) do
field(:name, String.t(), enforce: true)
field(:family, String.t(), enforce: true)

conditional_field(:address, any()) do
field(:address, String.t(), hint: "address1", validator: {VAL, :is_string_data})

sub_field(:address, struct(), hint: "address2", validator: {VAL, :is_map_data}) do
field(:location, String.t(), enforce: true)
field(:text_location, String.t(), enforce: true)
end

sub_field(:address, struct(), hint: "address3", validator: {VAL, :is_map_data}) do
field(:location, String.t(), enforce: true, derive: "validate(string, location)")
field(:text_location, String.t(), enforce: true)
field(:email, String.t(), enforce: true)
end
end
end

conditional_field(:product, any()) do
field(:product, String.t(), hint: "product1", validator: {VAL, :is_string_data})

sub_field(:product, struct(), hint: "product2", validator: {VAL, :is_map_data}) do
field(:name, String.t(), enforce: true)
field(:price, integer(), enforce: true)

sub_field(:information, struct()) do
field(:creator, String.t(), enforce: true)
field(:company, String.t(), enforce: true)

conditional_field(:inventory, integer() | struct(), enforce: true) do
field(:inventory, integer(),
hint: "inventory1",
validator: {VAL, :is_int_data},
derive: "validate(integer, max_len=33)"
)

sub_field(:inventory, struct(), hint: "inventory2", validator: {VAL, :is_map_data}) do
field(:count, integer(), enforce: true)
field(:expiration, integer(), enforce: true)
end
end
end
end
end
end
end
```

Call the builder

```elixir
ConditionalFieldComplexTest.builder(%{
provider: "Mishka",
profile: %{
name: "Shahryar",
family: "Tavakkoli",
address: %{
location: "geo:48.198634,-16.371648,3.4;crs=wgs84;u=40.0",
text_location: "Nowhere",
email: "[email protected]"
}
},
product: %{
name: "MishkaDeveloperTools",
price: 0,
information: %{
creator: "Shahryar Tavakkoli",
company: "mishka group",
inventory: %{
count: 3_000_000,
expiration: 33
}
}
}
})
```
96 changes: 96 additions & 0 deletions lib/macros/guarded_struct.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1065,6 +1065,102 @@ defmodule GuardedStruct do
```
**Note**: You can see when you use it inside a derive, the GuardedStruct calculates the you module `alias`.
21. #### Conditional fields
One of the unique capabilities of this macro is the ability to define conditions
and differentiate between the various kinds of `fields`. Assume that you want the `social`
field to be able to take both a value `string` and a `map` where `address` and `provider`
are included in the `map`.
It is important to notice that the `conditional_field` contained within this macro have
the capability of supporting `sub_field`. You can look at some illustrations down below.
```elixir
defmodule ConditionalFieldComplexTest do
use GuardedStruct
alias ConditionalFieldValidatorTestValidators, as: VAL
guardedstruct do
field(:provider, String.t())
sub_field(:profile, struct()) do
field(:name, String.t(), enforce: true)
field(:family, String.t(), enforce: true)
conditional_field(:address, any()) do
field(:address, String.t(), hint: "address1", validator: {VAL, :is_string_data})
sub_field(:address, struct(), hint: "address2", validator: {VAL, :is_map_data}) do
field(:location, String.t(), enforce: true)
field(:text_location, String.t(), enforce: true)
end
sub_field(:address, struct(), hint: "address3", validator: {VAL, :is_map_data}) do
field(:location, String.t(), enforce: true, derive: "validate(string, location)")
field(:text_location, String.t(), enforce: true)
field(:email, String.t(), enforce: true)
end
end
end
conditional_field(:product, any()) do
field(:product, String.t(), hint: "product1", validator: {VAL, :is_string_data})
sub_field(:product, struct(), hint: "product2", validator: {VAL, :is_map_data}) do
field(:name, String.t(), enforce: true)
field(:price, integer(), enforce: true)
sub_field(:information, struct()) do
field(:creator, String.t(), enforce: true)
field(:company, String.t(), enforce: true)
conditional_field(:inventory, integer() | struct(), enforce: true) do
field(:inventory, integer(),
hint: "inventory1",
validator: {VAL, :is_int_data},
derive: "validate(integer, max_len=33)"
)
sub_field(:inventory, struct(), hint: "inventory2", validator: {VAL, :is_map_data}) do
field(:count, integer(), enforce: true)
field(:expiration, integer(), enforce: true)
end
end
end
end
end
end
end
```
Call the builder
```elixir
ConditionalFieldComplexTest.builder(%{
provider: "Mishka",
profile: %{
name: "Shahryar",
family: "Tavakkoli",
address: %{
location: "geo:48.198634,-16.371648,3.4;crs=wgs84;u=40.0",
text_location: "Nowhere",
email: "[email protected]"
}
},
product: %{
name: "MishkaDeveloperTools",
price: 0,
information: %{
creator: "Shahryar Tavakkoli",
company: "mishka group",
inventory: %{
count: 3_000_000,
expiration: 33
}
}
}
})
```
"""
defmacro guardedstruct(opts \\ [], do: block) do
ast = register_struct(block, opts, :root, __CALLER__.module)
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
defmodule MishkaDeveloperTools.MixProject do
use Mix.Project
@version "0.1.1"
@version "0.1.2"

def project do
[
Expand Down

0 comments on commit 693fd52

Please sign in to comment.