diff --git a/README.md b/README.md index e3c1f6e..402b03b 100644 --- a/README.md +++ b/README.md @@ -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. --- @@ -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 ``` diff --git a/guidance/guarded-struct.md b/guidance/guarded-struct.md index 46d3a05..aa6933a 100644 --- a/guidance/guarded-struct.md +++ b/guidance/guarded-struct.md @@ -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: "shahryar@mishka.group" + } + }, + product: %{ + name: "MishkaDeveloperTools", + price: 0, + information: %{ + creator: "Shahryar Tavakkoli", + company: "mishka group", + inventory: %{ + count: 3_000_000, + expiration: 33 + } + } + } +}) +``` diff --git a/lib/macros/guarded_struct.ex b/lib/macros/guarded_struct.ex index 96f54be..0ab3621 100644 --- a/lib/macros/guarded_struct.ex +++ b/lib/macros/guarded_struct.ex @@ -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: "shahryar@mishka.group" + } + }, + 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) diff --git a/mix.exs b/mix.exs index 0456e39..5d46f85 100644 --- a/mix.exs +++ b/mix.exs @@ -1,6 +1,6 @@ defmodule MishkaDeveloperTools.MixProject do use Mix.Project - @version "0.1.1" + @version "0.1.2" def project do [