shahryarjb
GuardedStruct - A macro allows to build Structs that provide you with a number of important options Validation, Sanitizing, Constructor
GuardedStruct macro allows to build Structs that provide you with a number of important options Validation, Sanitizing, Constructor
The creation of this macro will allow you to build Structs that provide you with a number of important options, including the following:
- Validation
- Sanitizing
- Constructor
- It provides the capacity to operate in a nested style simultaneously.
Suppose you are going to collect a number of pieces of information from the user, and before doing anything else, you are going to sanitize them. After that, you are going to validate each piece of data, and if there are no issues, you will either display it in a proper output or save it somewhere else. All of the characteristics that are associated with this macro revolve around cleaning and validating the data.
The features that we list below are individually based on a particular strategy and requirement, but thankfully, they may be combined and mixed in any way that you see fit.
My Blog post about GuardedStruct:
Nested Example
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
Installing the library:
def deps do
[
{:guarded_struct, "~> 0.0.1"}
]
end
Links
Github: GitHub - mishka-group/guarded_struct: GuardedStruct macro allows to build Structs that provide you with a number of important options Validation, Sanitizing, Constructor · GitHub
Hex: guarded_struct | Hex
LiveBook Document: guarded_struct/guidance/guarded-struct.livemd at master · mishka-group/guarded_struct · GitHub
Most Liked
dimitarvp
This looks great, I’ll try it.
Some of the naming is IMO prone to changes, f.ex. :is_map_data should be just fine written as :is_map. Same for :is_string_data. Also not sure about enforce, why not christen it mandatory? ![]()
Also the derive thing might need another format but I believe I can work with it as-is.
Thanks for making this.
dimitarvp
Ah, thanks. I’d definitely go for something shorter and self-documenting.
I am aware but IMO Elixir blundered the name here. enforce is ambiguous and mandatory is much clearer.
You got me.
I commented before reviewing it, it was just a surface observation. Thanks for elaborating. ![]()
Definitely. I believe Elixir’s community really has to unite around one godlike parsing and validation / type+rule enforcement library. Yours looks like a solid candidate.
shahryarjb
Hello,
I truly appreciate you sharing your feedback—it’s incredibly valuable to me.
Since the initial idea was inspired by another Elixir library and some of the points raised involve certain trade-offs, I’ll do my best to address them where possible. However, unfortunately, I can’t make structural changes at this stage. This library has been in use for about a year now, and rewriting macros would require significant time. Additionally, I am currently actively working on another open-source project.
In my opinion, the name of a macro should be completely transparent about its purpose and usage. This ensures that developers can immediately understand what the macro is and what it does. Additionally, the consumer might be a project manager with limited technical knowledge of Elixir, so clarity is crucial.
I don’t believe having three macros in the entire program is excessive. I agree that there is a learning curve involved, but it’s relatively short. To address this, I’ve made an effort to provide extensive LiveBook examples and detailed documentation to help users understand and utilize the macros effectively.
Thank you for this
The validator feature was the very first functionality I added to the macro. If nothing is explicitly provided, it automatically looks for and reads from the module itself, assuming relevant information is available. If you wish to use an external module, it should be provided as a tuple.
That said, I’d be delighted to support this feature alongside the current ones. I’d be even happier if someone with the time could submit a pull request for it—I think it would turn out to be a very clean and elegant addition!
The macro already supports this functionality. You can pass any type to it as needed.
Including the specific case you mentioned.
Currently, the t() type is generated directly within the module. As for documentation, I was aiming for a custom approach to write concise documentation for individual fields. However, this is something I plan to address more thoroughly in the future.
Regarding the callback, I plan to include it in the next version. I already have a task assigned for it.
I’m sorry, but I’m not a big fan of splitting files excessively. I believe the main macro module is already sufficiently divided into other modules. Further splitting would hinder rapid development, as developers would spend too much time searching for modules and functions.
I prefer structuring modules based on their responsibilities rather than merely reducing the number of lines.
I completely agree with many of the points you’ve mentioned. I’d love to add more features and create diverse implementations. However, managing the balance between compile-time and runtime has been quite time-consuming for me.
I’d really appreciate it if the community could contribute by submitting pull requests for the features they need or at least opening an issue before working on a pull request.
Personally, I’m heavily focused on the Mishka Chelekom project at the moment, so I might not be able to implement the features you’ve mentioned in the short term.
I hope my responses don’t come across as unwillingness to listen to and address feedback. On the contrary, I truly enjoy improving things based on constructive criticism. After all, projects only get better with input from their users.
I simply tried to explain the thoughts and considerations that have been on my mind.
Thank you in advance
Popular in Announcing
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









