Ecto Changeset add functionality for warnings
Asked Answered
T

1

7

I created a fork of ecto repository to extend Ecto.Changeset module with the ability to add warnings to the changeset. I wanted to have an add_warnings/4 function which adds a warning to the changeset as a simple list of warnings with this structure warnings: [{atom, {String.t, Keyword.t}}], similar to errors. The difference between the behavior of warnings and errors is that when an error occurs the data are not persisted, but when a warning occurs the data are persisted.

Ecto.Changeset struct extended with keys warnings and warningless?:

defstruct valid?: false, warningless?: false, data: nil, params: nil, changes: %{}, repo: nil,
        errors: [], warnings: [], validations: [], required: [], prepare: [],
        constraints: [], filters: %{}, action: nil, types: nil,
        empty_values: @empty_values

Ecto functions for casting, changing, processing params, etc. adjusted. Function add_warnings/4 added:

@spec add_warning(t, atom, String.t, Keyword.t) :: t
def add_warning(%{warnings: warnings} = changeset, key, message, keys \\ []) when is_binary(message) do
  %{changeset | warnings: [{key, {message, keys}}|warnings], warningless?: false}
end 

The result is that I receive changeset with expected keys:

#Ecto.Changeset<action: nil, changes: %{}, data: #Company.Booking<>, errors: [],
valid?: true, warnings: [], warningless?: true>

When I make a change with error and warning I receive:

#Ecto.Changeset<action: nil,
changes: %{pickup_address: #Ecto.Changeset<action: :update,
changes: %{street_name: nil}, data: #Company.Address<>,
errors: [street_name: {"can't be blank", [validation: :required]}],
valid?: false,
warnings: [phone_number: {"This phone number is not common in Netherlands",
  []}], warningless?: false>}, data: #Company.Booking<>, errors: [],
valid?: false, warnings: [], warningless?: true>

So, everything is as expected, as far as warnings are concerned. Then, when I make a change with a warning but without an error, I receive:

#Ecto.Changeset<action: nil,
changes: %{pickup_address: #Ecto.Changeset<action: :update,
changes: %{street_name: "sss"}, data: #Company.Address<>, errors: [],
valid?: true,
warnings: [phone_number: {"This phone number is not common in Netherlands",
  []}], warningless?: false>}, data: #Company.Booking<>, errors: [],
valid?: true, warnings: [], warningless?: true>

Everything is as expected. When I don't make any changes to the form I still should receive a warning for phone number, but I receive:

#Ecto.Changeset<action: nil, changes: %{}, data: #Company.Booking<>,    errors: [],
valid?: true, warnings: [], warningless?: true>

I got a changeset without any warnings as there is no changes key in changeset because the data didn't change.

The question is as follows, how to implement warnings functionality to always have warnings in the changeset, even if no change was made?

Textbook answered 23/6, 2017 at 9:32 Comment(1)
You are not showing any changeset function. Presumably what you will want to do is use Ecto.Changeset.get_field/3 instead of Ecto.Changeset.get_change/3 when adding a warning. Though, what is the purpose of this? If you will want to persist the data on warning, you will lose the changeset and not be able to show the user a message.Komsa
W
1

You should consider to prefill the warnings at the very beginning of the each changeset function you would create - since you can't use plug there you can come up to write a macro that will handle this logic for you, __using__ is advised, so it would be quite easy to distinguish your logic from Ecto's default logic.

Your validation shouldn't add warnings to warnings list, but you have to implement it another way around - if the field is fine, you would remove already existing warnings from this list. That way you would be sure that your changeset is fine when it's warningless, because it removed all the warnings from this list and it would works perfectly for empty changes in changeset.

Wimble answered 26/10, 2017 at 7:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.