Need suggestion to bring programmer code at the end of library module code

Hello, for example in my macro and library I have a validation module, which I need this module to be extended by programmer, please see the comments in this code.

defmodule MishkaDeveloperTools.Helper.Derive.ValidationDerive do
  def call({_field, input}, nil), do: {input, []}

  def call({field, input}, actions) do
    validated = Enum.map(actions, &validate(&1, input, field))

   # etc ...
  end

  def validate(:not_empty, input, field) when is_binary(input) do
    if input == "",
      do: {:error, field, :not_empty, "The #{field} field must not be empty"},
      else: input
  end

  # Other validate functions with different entries
  
  # This place I want to have another validate function from programmer module
  
  def validate(_, _input, field) do
    {:error, field, :type, "Unexpected type error in #{field} field"}
  end
end

for example I have a module that is ProgrammerA

defmodule ProgrammerA do
  def validate(:trim, input, field) when is_binary(input) do
    {:error, field, :trim, "The #{field} field must not be empty"}
  end
  
  def validate(:space, input, field) when is_binary(input) do
    {:error, field, :space, "The #{field} field must not be empty"}
  end
end

Now I want to load all validate functions of ProgrammerA instead of this comment

# This place I want to have another validate function from programmer module in MishkaDeveloperTools.Helper.Derive.ValidationDerive module.

It should be noted, if the pattern is no match with original function and programmer functions

this function should be called

  def validate(_, _input, field) do
    {:error, field, :type, "Unexpected type error in #{field} field"}
  end

To make it clear, when the programmer wants to load my macro, he gives me the address of the module that contains the validation functions

Thank you in advance


More code:

defmodule MishkaDeveloperTools.Helper.Derive.ValidationDerive do

  def validate(:not_empty, input, field) when is_binary(input) do
    if input == "",
      do: {:error, field, :not_empty, "The #{field} field must not be empty"},
      else: input
  end

  # Other validate functions with different entries
  
  def validate(x, y, z) do
    module = Application.get_env(:mishka, :validation_module)
    module.validate(x, y, z)
  end
  
  def validate(_, _input, field) do
    {:error, field, :type, "Unexpected type error in #{field} field"}
  end
end

Unfortunately, it skips the

def validate(_, _input, field) do
    {:error, field, :type, "Unexpected type error in #{field} field"}
end

if I do this, I should force the programmer put this last function inside his module!!

  def validate(x, y, z) do
    module = Application.get_env(:mishka, :validation_module)
    with :skip <- module.validate(x, y, z) do
      {:error, field, :type, "Unexpected type error in #{field} field"}
    end
  end

But it appears that it is XY problem, I think you should describe what you’re trying to achieve in the first place, since your suggested solution is clearly wrong

Hmmm, I do not know how can explain, I thought it was described very well in first post, I just want to add some other validate functions inside my main module, and if the pattern of the other validate are no match, so the function

 def validate(_, _input, field) do
    {:error, field, :type, "Unexpected type error in #{field} field"}
  end

should be called in my module as you can see

For example:

defmodule MishkaDeveloperTools.Helper.Derive.ValidationDerive do
  def call({_field, input}, nil), do: {input, []}

  def call({field, input}, actions) do
    validated = Enum.map(actions, &validate(&1, input, field))

   # etc ...
  end

  def validate(:not_empty, input, field) when is_binary(input) do
    if input == "",
      do: {:error, field, :not_empty, "The #{field} field must not be empty"},
      else: input
  end

  # Other validate functions with different entries
  
  # This place I want to have another validate function from programmer module
  
  def validate(_, _input, field) do
    {:error, field, :type, "Unexpected type error in #{field} field"}
  end
end

this is first face and the face I need after merge 2 module should be this:

defmodule MishkaDeveloperTools.Helper.Derive.ValidationDerive do
  def call({_field, input}, nil), do: {input, []}

  def call({field, input}, actions) do
    validated = Enum.map(actions, &validate(&1, input, field))

   # etc ...
  end

  def validate(:not_empty, input, field) when is_binary(input) do
    if input == "",
      do: {:error, field, :not_empty, "The #{field} field must not be empty"},
      else: input
  end

  def validate(:trim, input, field) when is_binary(input) do
    {:error, field, :trim, "The #{field} field must not be empty"}
  end
  
  def validate(:space, input, field) when is_binary(input) do
    {:error, field, :space, "The #{field} field must not be empty"}
  end
  
  def validate(_, _input, field) do
    {:error, field, :type, "Unexpected type error in #{field} field"}
  end
end

Why do you want this? What are you trying to achieve with this?

This is my macro.

defmodule TestStructBuilder do
  use GuardedStruct

  guardedstruct do
    field(:name, String.t(), enforce: true, derive: "sanitize(trim, upcase) validate(not_empty)")

    field(:title, String.t(), derive: "sanitize(trim, capitalize) validate(not_empty)")
    field(:nickname, String.t(), derive: "validate(not_empty, time)")

    sub_field(:oop, struct()) do
      field(:title, String.t(), enforce: true, derive: "validate(not_empty)")
      field(:fam, String.t(), enforce: true, derive: "validate(not_empty)")

      sub_field(:soos, struct()) do
        field(:fam, String.t(), validator: {AnotherModule, :validator})
        field(:title, String.t(), enforce: true, derive: "validate(not_empty)")
      end

      field(:site, String.t())
    end
  end
end

As you can see I use something like this:

derive: "validate(not_empty)"

it calls my validation function from this file:

Now I want to let you put your custom validation and extend my validation module. then you should be able to use it like this code:

for example

derive: "validate(not_empty, your_new_validation)"

I didn’t understand a word. Can you explain what you’re trying to achieve in plain English?

I want to give you the ability to add your own custom validations.

What validations?

If you really want to spend time and solve my problem, it is better to read the above posts again

In Elixir, you can’t add functions into the module based on functions from the other module. This is just not possible. It is possible to do this in a very hacky way, but you shouldn’t actually do this.

However, this is still an XY problem, your idea of solution is completely wrong. That’s why I am asking about what are you trying to solve with this solution.

Yes, I know, because this I need suggestions like macro __using__, or copy string code and compile it inside my module and etc.

This is the basic stuff

What do you mean?

I mean I know what you said, and the way you suggest is does not work and it is basic stuff

I think you have no idea for this post

Yes, I’ve altered my solution in the next post

  def validate(x, y, z) do
    module = Application.get_env(:mishka, :validation_module)
    with :skip <- module.validate(x, y, z) do
      {:error, field, :type, "Unexpected type error in #{field} field"}
    end
  end

So in your ProgrammerA module you would just write something like

def validate(x, y, z) do
  case ... do
    ...
    _ ->
     :skip
  end
end

This is how callbacks work in whole Elixir and Erlang ecosystem. Even GenServer can return {:noreply, state} and just return control back to the gen_server module.

But this solution is still bad, since extending function with clauses from other module is a wrong idea from the beginning. So, my question still stands, what are you trying to achieve with this?

It was explained here

I don’t want to tell the programmer to use skip, the output and input must be in the same structure that I used in the main module

Please see the post I linked, Please see the sample macro code, Please see the derive! after that you can find what I want.

I just gave an example, maybe you should provide another better way to improve this macro

For example:

defmodule TestStructBuilder do
  use GuardedStruct

  guardedstruct derive: ProgrammerA do
    ...
  end
end

For example the programmer uses a wrong word like derive: "validate(not_empty, bad_word)"

This name does not exist bad_word, so I want to have this:

def validate(_, _input, field) do
    {:error, field, :type, "Unexpected type error in #{field} field"}
end

if there is no type and bad word, I show and error

Yes, I’ve read it. And the question is What are you trying to achieve with what you wrote in the first post? Like, in the first place, bigger picture

I updated this post