-
Notifications
You must be signed in to change notification settings - Fork 60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add type validator. #24
base: master
Are you sure you want to change the base?
Conversation
cd0cd49
to
636318b
Compare
|> Enum.take(Enum.count(acceptable_types) - 1) | ||
|> Enum.map(&inspect/1) | ||
|> Enum.join(", ") | ||
"#{but_last} or #{last_type}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you elaborate a bit about what this function is doing? I get what the result is but the method you need to achieve it is confusing to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given something like [:string, :atom, [list: :string]]
it should return ":string, :atom or [list: :string]"
.
So given types t1, t2, ..., tn
it should join t1...t(n-1)
with a comma and join t1...t(n-1)
and tn
with or
.
As types do not all implement String.Chars
, I am using inspect
to get a string.
I am not particularly happy with this implementation so if you have improvements idea I am all ears.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have some suggestions:
{last_type, but_last_types} =
acceptable_types
|> Enum.map(&inspect/1)
|> List.pop_at(-1)
but_last = Enum.join(but_last_types, ", ")
"#{but_last} or #{last_type}"
or alternatively:
defp acceptable_type_str([type]) do
inspect(type)
end
defp acceptable_type_str([type, last_type]) do
"#{inspect(type)} or #{inspect(last_type)}"
end
defp acceptable_type_str([type | tail]) do
"#{inspect(type)}, " <> acceptable_type_string(tail)
end
defp acceptable_type_str(type) do
inspect(type)
end
or:
defp acceptable_type_str([type | tail]) do
"#{inspect(type)}" <>
case tail do
[] -> ""
[last] -> " or #{inspect(last)}"
more -> ", " <> acceptable_type_str(more)
end
end
defp acceptable_type_str(type) do
inspect(type)
end
I might have a went a bit overboard here... 😄
Hey there! First I want to say that this is an impressive endeavor to embark upon. You're basically retro-fitting static type analysis into the language, and that can be a real challenge. You've done a very good job so far. I'll tell you that I am at the moment a little skeptical that we can make this work in a sufficiently generic way. We know a thing or two about type systems in elixir from our work on doing GraphQL (http://absinthe-graphql.org/), and it can be a rather hairy process. For now, the first roadblock is From there the next thing people are going to want is the ability to specify particular values. You can do it in elixir typespec with atoms e.g Then we're in trouble because the moment we allow values into the system we gotta worry about mandatory vs optional values for our container types, and so on. You see why I'm a bit concerned about trying to support this. What are your thoughts @bruce ? |
Hi @benwilson512, Thank you very much for the detailed feedback!
You are perfectly right. I was using
The current approach will clearly not fit this case. For top level values, the
Wouldn't a proper I'll update the PR with proper |
636318b
to
868947a
Compare
I just updated the PR with proper Let me know what you think. |
868947a
to
b1654ff
Compare
Not if we allow values. Allowing values would let us say something like "not, only must you supply a map with atom keys, the only valid keys I do agree that trying to deal with values is probably beyond us here. |
@benwilson512 Thanks for the feedback! I get your point about allowing values, but I really think it would make things too complex By the way, to give a real use case, I am currently planning to use the type validator |
|
||
defp do_validate(value, :regex) do | ||
Regex.regex?(value) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm inclined to think btw that this should be handled as just a regular struct. Regexes, Ranges, and so on are merely structs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought Regex.regex?
was doing something but after checking the source code it did not 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Heh yeah just go with pattern matching on %Regex{}.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, consider removing :regex
altogether. They're just structs afterall, so people could just say Regex
and it would work as is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I removed :regex
completely, the struct matcher works well enough!
As a total side note, if you used GraphQL not only would your JSON output be automatically type checked, but your JSON input from clients would be too ;) |
😆 that's a shameless Absinthe plug right there, @benwilson512. |
@benwilson512 Thank you for the review! |
b1654ff
to
d1b98ad
Compare
@benwilson512 I just updated the PR! |
@benwilson512 ping 😃 |
Any progress on this? I would really loves this functionality. |
Is there still interest in merging this? I could review and merge this. |
I have been using this as a custom validator in one of my libraries [1] for quite a while now, so if you could review and merge it, it would be great! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some documentation suggestions. Also I would ask you, if you could update the README with some simple overview over the new validator.
* `map: {key_type, value_type}`: checks keys and value in the map with the provided types. | ||
* `list: type`: checks every element in the list for the given types. | ||
* `tuple: {type_a, type_b}`: check each element of the tuple with the provided types, | ||
the types tuple should be the same size as the tuple itself. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should
implies that it's merely recommended to be the same size but the validator enforces them to be the same. I would suggest must
.
defp do_validate(tuple, tuple: types)
when is_tuple(tuple) and is_tuple(types) and tuple_size(tuple) == tuple_size(types) do
@spec validate(any, Keyword.t) :: :ok | {:error, String.t} | ||
def validate(value, options) when is_list(options) do | ||
acceptable_types = Keyword.get(options, :is, []) | ||
if do_validate(value, acceptable_types) do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Convention would suggest to name this with a trailing ?
(e.g. is_valid_type?
). Just a thought though.
defp do_validate(value, :reference) when is_reference(value), do: true | ||
defp do_validate(value, :port) when is_port(value), do: true | ||
defp do_validate(value, :pid) when is_pid(value), do: true | ||
defp do_validate(%{__struct__: module}, module), do: true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is missing from the @moduledoc
. If I got that right you can also do this?
Vex.Validators.Type.validate(%MyStruct{}, is: MyStruct)
Any life signs here? |
Won't have much time in the next few weeks, sorry. |
Hi,
I would like to validate the types of the values in a struct, but I could not find any easy
way to do it, so I added a validator to check for types.
It works with all builtin types and can be used to check complex types
%{:atom => binary}
, or things like this.Take a look at the docs examples and test cases for more info.
Thank you!