shahryarjb

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:

  1. Validation
  2. Sanitizing
  3. Constructor
  4. It provides the capacity to operate in a nested style simultaneously.

Run in Livebook

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

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? :thinking:

Also the derive thing might need another format but I believe I can work with it as-is.

Thanks for making this.

dimitarvp

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. :slight_smile: I commented before reviewing it, it was just a surface observation. Thanks for elaborating. :+1:

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

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

Where Next?

Popular in Announcing Top

wmnnd
Hi there, for my project DBLSQD, I needed a file storage solution that is a bit more flexible than Arc. Because I thought others might f...
New
tmbb
I’ve published the first version of my Makeup library. It’s a syntax highlighter for Elixir in the spirit of Pygments, Currently it highl...
New
seancribbs
Today I released a new dialyzer Mix task as the dialyzex package! At the time we started writing this task, the existing dialyzer integra...
New
mischov
import Meeseeks.CSS html = HTTPoison.get!("https://news.ycombinator.com/").body for story <- Meeseeks.all(html, css("tr.athing")) do...
New
dominicletz
Hi, I thought I had posted my library before but seems I hadn’t. The project is still in early stages but it’s growing and so I think it...
New
Crowdhailer
I have been updating a library that allows you to pipe between functions that use the erlang result tuple convention. Assuming you have ...
New
Flo0807
Hello everyone! I am excited to share our heart project Backpex with you. After building several Phoenix applications, we realized that...
New
fuelen
Hey folks! Want to present a toolkit for writing command-line user interfaces. It provides a convenient interface for colorizing text...
New
Qqwy
TypeCheck: Fast and flexible runtime type-checking for your Elixir projects. Core ideas Type- and function specifications are const...
336 14482 100
New
pkrawat1
Hey guyz We at @aviabird are working on a payment library in elixir/phoenix. We are targeting March 2018 to add 56 Gateways to it. Have...
New

Other popular topics Top

sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
greenz1
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 54120 245
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement