Skip to content

Commit 295bcef

Browse files
committed
add docs of 0.1.1
1 parent 60f7b9f commit 295bcef

File tree

4 files changed

+689
-31
lines changed

4 files changed

+689
-31
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Changelog for MishkaDeveloperTools 0.1.2
22

33
- [ ] Add driver for accepting JSON
4+
- [ ] Add Conditional field structure `macro` (**Multiple states of a field**)
45
- [ ] Add driver for making JSON outputs
56
- [ ] Add derives caller inside parent module
67
- [ ] Add Supporting new `Typespecs` for `list(struct())` and previous one `struct()`

guidance/guarded-struct.md

Lines changed: 286 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Before explaining the copyright, I must point out that the primary library, whic
2222

2323
## Copyright
2424

25-
The code in this module is based on the 'typed_struct' library (https://github.com/ejpcmac/typed_struct),
25+
The code in this module is based on the `typed_struct` library (https://github.com/ejpcmac/typed_struct),
2626
which is licensed under the MIT License.
2727

2828
Modifications and additions have been made to enhance its capabilities as part of the current project.
@@ -306,6 +306,7 @@ end
306306
| `"validate(enum=Map[%{status: 1}::%{status: 2}::%{status: 3}])"` | NO | Validate if the data is one of the enum value, which is Map|
307307
| `"validate(enum=Tuple[{:admin, 1}::{:user, 2}::{:banned, 3}])"` | NO | Validate if the data is one of the enum value, which is Tuple|
308308
| `"validate(equal=some_thing)"` | NO | Validate if the data is equal with validation value, which is any type|
309+
| `"validate(either=[string, enum=Integer[1::2::3]])"` | NO | Validate if the data is valid with each derive validation|
309310

310311
```elixir
311312
defmodule MyModule do
@@ -543,3 +544,287 @@ end
543544
name: "Mishka"
544545
}}
545546
```
547+
548+
10. #### Set config to show error inside `defexception`
549+
550+
You may want to display the received errors in Elixir's `defexception`. you just need to enable the `error: true` for `guardedstruct` macro or `sub_field`.
551+
552+
**Note**: When you enable the `error` option. This macro will generate for you a module that is part of the parent module subset, and within that module, it will generate a `defexception` struct.
553+
554+
##### Error `defexception` modules
555+
556+
```elixir
557+
TestCallNestedStructWithError.Error
558+
TestCallNestedStructWithError.Auth.Error
559+
TestCallNestedStructWithError.Auth.Path.Error
560+
```
561+
562+
##### Sample code
563+
564+
```elixir
565+
defmodule TestCallNestedStructWithError do
566+
use GuardedStruct
567+
568+
guardedstruct error: true do
569+
field(:name, String.t(), derive: "validate(string)")
570+
571+
sub_field(:auth, struct(), error: true) do
572+
field(:action, String.t(), derive: "validate(not_empty)")
573+
574+
sub_field(:path, struct(), error: true) do
575+
field(:name, String.t())
576+
end
577+
end
578+
end
579+
end
580+
581+
# And you should call it like this, the second entry should be `true` or `false` to show error `defexception`
582+
TestCallNestedStructWithError.builder(%{name: 1}, true)
583+
```
584+
585+
11. #### `authorized_fields` option to limit user input
586+
587+
If this option is not used, the program will automatically drop fields that are not defined; however, if this option is set, it will return an error to the user if they transmit a field that is not in the list of specified fields. If this option is not used, the program will automatically drop fields that are not defined.
588+
589+
**Please take note** that the `required_fields` and this section are not the same thing, and that the validation of the mandatory fields will take place after this section.
590+
591+
```elixir
592+
defmodule TestAuthorizeKeys do
593+
use GuardedStruct
594+
595+
guardedstruct authorized_fields: true do
596+
field(:name, String.t(), derive: "validate(string)")
597+
598+
sub_field(:auth, struct(), authorized_fields: true) do
599+
field(:action, String.t(), derive: "validate(not_empty)")
600+
601+
sub_field(:path, struct()) do
602+
field(:name, String.t())
603+
end
604+
end
605+
end
606+
end
607+
608+
TestAuthorizeKeys.builder(%{name: "Shahryar", test: "test"})
609+
# Ouput: `{:error, :authorized_fields, [:test]}`
610+
611+
TestAuthorizeKeys.builder(%{name: "Shahryar", auth: %{action: "admin", test: "test"}})
612+
# Ouput: `{:error, :bad_parameters, [%{field: :auth, errors: {:authorized_fields, [:test]}}]}`
613+
```
614+
615+
12. #### `authorized_fields` option to limit user input
616+
617+
This option can be helpful for you if you wish to construct your own modules in various files and then make those modules reusable in the future. Simply implement the macro in another module, and then call that module from the `field` macro. The `struct` and `structs` options are the ones in which the module can be placed. The first one will provide you with an indication that you will be given a map, and the second one will provide you with a list of maps.
618+
619+
620+
```elixir
621+
defmodule TestAuthStruct do
622+
use GuardedStruct
623+
624+
guardedstruct do
625+
field(:action, String.t(), derive: "validate(not_empty)")
626+
end
627+
end
628+
629+
defmodule TestOnValueStruct do
630+
use GuardedStruct
631+
632+
guardedstruct do
633+
field(:name, String.t(), derive: "validate(string)")
634+
field(:auth_path, struct(), struct: TestAuthStruct)
635+
# field(:auth_path, struct(), structs: TestAuthStruct)
636+
end
637+
end
638+
```
639+
640+
13. #### List of structs
641+
642+
As was discussed in the earlier available choices. In the `field` macro that is used to call **another module**, as well as in the `sub_field` macro, you have the ability to retrieve a list of structs rather than a single struct.
643+
644+
```elixir
645+
defmodule TestUserAuthStruct do
646+
use GuardedStruct
647+
648+
guardedstruct do
649+
field(:name, String.t(), derive: "validate(not_empty)")
650+
field(:auth_path, struct(), structs: TestAuthStruct)
651+
652+
sub_field(:profile, list(struct()), structs: true) do
653+
field(:github, String.t(), enforce: true, derive: "validate(url)")
654+
field(:nickname, String.t(), derive: "validate(not_empty)")
655+
end
656+
end
657+
end
658+
659+
TestUserAuthStruct.builder(%{
660+
name: "mishka",
661+
auth_path: [
662+
%{action: "*:admin", path: %{role: "1"}},
663+
%{action: "*:user", path: %{role: "3"}}
664+
]
665+
})
666+
667+
# OR
668+
TestUserAuthStruct.builder(%{
669+
name: "mishka",
670+
auth_path: [
671+
%{action: "*:admin", path: %{role: "1"}},
672+
%{action: "*:user", path: %{role: "3", rel: %{social: "github"}}}
673+
],
674+
profile: [%{github: "https://github.com/mishka-group"}]
675+
})
676+
```
677+
678+
14. #### Struct information function
679+
680+
You will need to include a function known as `__information__()` in each and every module that you develop for your very own `structs`. This function will store a variety of information, such as keys, callers, and so on.
681+
682+
**Note:** There is a possibility that further information will be added to this function; please check its output after each update.
683+
684+
**Note:** If you call another Struct module within the `field` macro, you should not use the `caller` key within this function. This is due to the fact that the constructor information is only available during **compile** time, and not run time.
685+
686+
```elixir
687+
TestStruct.__information__()
688+
```
689+
690+
15. #### Transmitting whole output of builder function to its children
691+
692+
Because new keys have been added, such as `auto`, `on`, and `from` which will be explained in more detail below. The `builder` function is available in the following two different styles.
693+
694+
> If you don't provide the `:root` key, you can just specify the child key, but if you do, you have to send the entire map as an `attar`. This is something to keep in mind.
695+
696+
697+
```elixir
698+
def builder(attrs, error)
699+
700+
def builder({key, attrs} = input, error)
701+
when is_tuple(input) and is_map(attrs) and is_list(key) do
702+
...
703+
end
704+
```
705+
706+
16. #### Auto core key
707+
708+
Even if the user transmits the information and it is already in the input, such as with the ID field, the sequence of fields still has to be formed automatically. You can accomplish what you want to with the help of the `auto` option.
709+
710+
> As you can see in the code below, we have several types of `auto` option calls
711+
712+
---
713+
714+
**TODO**: In the next version, it will not be made automatically if there is data for the key that is desired, but it will be feasible to do so if the data exists.
715+
716+
> When the core keys are called, the entire primary map is sent to each child.
717+
718+
```elixir
719+
defmodule TestAutoValueStruct do
720+
use GuardedStruct
721+
722+
guardedstruct do
723+
field(:username, String.t(), derive: "validate(not_empty)")
724+
field(:user_id, String.t(), auto: {Ecto.UUID, :generate})
725+
field(:parent_id, String.t(), auto: {Ecto.UUID, :generate})
726+
727+
sub_field(:profile, struct()) do
728+
field(:id, String.t(), auto: {Ecto.UUID, :generate})
729+
field(:nickname, String.t(), derive: "validate(not_empty)")
730+
731+
sub_field(:social, struct()) do
732+
field(:id, String.t(), auto: {TestAutoValueStruct, :create_uuid, "test-path"})
733+
field(:skype, String.t(), derive: "validate(string)")
734+
field(:username, String.t(), from: "root::username")
735+
end
736+
end
737+
738+
sub_field(:items, struct(), structs: true) do
739+
field(:id, String.t(), auto: {Ecto.UUID, :generate})
740+
field(:something, String.t(), derive: "validate(string)", from: "root::username")
741+
end
742+
end
743+
744+
def create_uuid(default) do
745+
Ecto.UUID.generate() <> "-#{default}"
746+
end
747+
end
748+
```
749+
750+
17. #### On core key
751+
752+
With the aid of this option, you can make the presence of a field dependent on the presence of another field and, if there is no error, produce an error message.
753+
754+
If you pay attention to the routing method, the routing will start from the sent map itself if `:root` is specified, but if it is not used, the routing will start from the received map in the child if it is not used.
755+
756+
> When the core keys are called, the entire primary map is sent to each child.
757+
758+
```elixir
759+
defmodule TestOnValueStruct do
760+
use GuardedStruct
761+
762+
guardedstruct do
763+
field(:name, String.t(), derive: "validate(string)")
764+
765+
sub_field(:profile, struct()) do
766+
field(:id, String.t(), auto: {Ecto.UUID, :generate})
767+
field(:nickname, String.t(), on: "root::name", derive: "validate(string)")
768+
field(:github, String.t(), derive: "validate(string)")
769+
770+
sub_field(:identity, struct()) do
771+
field(:provider, String.t(), on: "root::profile::github", derive: "validate(string)")
772+
field(:id, String.t(), auto: {Ecto.UUID, :generate})
773+
field(:rel, String.t(), on: "sub_identity::auth_path::action")
774+
775+
sub_field(:sub_identity, struct()) do
776+
field(:id, String.t(), auto: {Ecto.UUID, :generate})
777+
field(:auth_path, struct(), struct: TestAuthStruct)
778+
end
779+
end
780+
end
781+
782+
sub_field(:last_activity, list(struct()), structs: true) do
783+
field(:action, String.t(), enforce: true, derive: "validate(string)", on: "root::name")
784+
end
785+
end
786+
end
787+
```
788+
18. #### From core key
789+
790+
You can select this alternative if you require any data that was delivered in another key to be incorporated into the key that you are looking for. If the key is present, the data associated with it will be copied; however, if the key is not there, the data in and of itself will be retained.
791+
792+
If you pay attention to the routing method, the routing will start from the sent map itself if `:root` is specified, but if it is not used, the routing will start from the received map in the child if it is not used.
793+
794+
---
795+
796+
> When the core keys are called, the entire primary map is sent to each child.
797+
798+
> Note: It is possible that you will need to check that the field you wish to duplicate exists, and in order to do so, you can use either the `on` key or the `enforce` option.
799+
800+
```elixir
801+
defmodule TestAutoValueStruct do
802+
use GuardedStruct
803+
804+
guardedstruct do
805+
field(:username, String.t(), derive: "validate(not_empty)")
806+
field(:user_id, String.t(), auto: {Ecto.UUID, :generate})
807+
field(:parent_id, String.t(), auto: {Ecto.UUID, :generate})
808+
809+
sub_field(:profile, struct()) do
810+
field(:id, String.t(), auto: {Ecto.UUID, :generate})
811+
field(:nickname, String.t(), derive: "validate(not_empty)")
812+
813+
sub_field(:social, struct()) do
814+
field(:id, String.t(), auto: {TestAutoValueStruct, :create_uuid, "test-path"})
815+
field(:skype, String.t(), derive: "validate(string)")
816+
field(:username, String.t(), from: "root::username")
817+
end
818+
end
819+
820+
sub_field(:items, struct(), structs: true) do
821+
field(:id, String.t(), auto: {Ecto.UUID, :generate})
822+
field(:something, String.t(), derive: "validate(string)", from: "root::username")
823+
end
824+
end
825+
826+
def create_uuid(default) do
827+
Ecto.UUID.generate() <> "-#{default}"
828+
end
829+
end
830+
```

0 commit comments

Comments
 (0)