changed
README.md
|
@@ -115,6 +115,52 @@ This validation can be skipped for `nil` or blank values by including
|
115
115
|
See the documentation on `Vex.Validators.Length` for details on
|
116
116
|
available options.
|
117
117
|
|
118
|
+ ### Number
|
119
|
+
|
120
|
+ Ensure a value is a number greater than a given number:
|
121
|
+
|
122
|
+ ```elixir
|
123
|
+ Vex.valid? value, number: [greater_than: 2]
|
124
|
+ ```
|
125
|
+
|
126
|
+ Ensure a value is a number less than or equal to a given number:
|
127
|
+
|
128
|
+ ```elixir
|
129
|
+ Vex.valid? value, number: [less_than_or_equal_to: 10]
|
130
|
+ ```
|
131
|
+
|
132
|
+ Ensure a value is a number is within a given range:
|
133
|
+
|
134
|
+ ```elixir
|
135
|
+ Vex.valid? value, number: [greater_than_or_equal_to: 0, less_than: 10]
|
136
|
+ ```
|
137
|
+
|
138
|
+ This validation can be skipped for `nil` or blank values by including
|
139
|
+ `allow_nil: true` or `allow_blank: true` respectively in the options.
|
140
|
+
|
141
|
+ See the documentation on `Vex.Validators.Number` for details
|
142
|
+ on available options.
|
143
|
+
|
144
|
+ ### UUID
|
145
|
+
|
146
|
+ Ensure a value is a valid UUID string:
|
147
|
+
|
148
|
+ ```elixir
|
149
|
+ Vex.valid? value, uuid: true
|
150
|
+ ```
|
151
|
+
|
152
|
+ Ensure a value is a valid UUID string in a given format:
|
153
|
+
|
154
|
+ ```elixir
|
155
|
+ Vex.valid? value, uuid: [format: :hex]
|
156
|
+ ```
|
157
|
+
|
158
|
+ This validation can be skipped for `nil` or blank values by including
|
159
|
+ `allow_nil: true` or `allow_blank: true` respectively in the options.
|
160
|
+
|
161
|
+ See the documentation on `Vex.Validators.Uuid` for details
|
162
|
+ on available options.
|
163
|
+
|
118
164
|
### Acceptance
|
119
165
|
|
120
166
|
Ensure an attribute is set to a positive (or custom) value. For use
|
changed
hex_metadata.config
|
@@ -16,11 +16,12 @@
|
16
16
|
<<"lib/vex/validators/by.ex">>,<<"lib/vex/validators/confirmation.ex">>,
|
17
17
|
<<"lib/vex/validators/exclusion.ex">>,<<"lib/vex/validators/format.ex">>,
|
18
18
|
<<"lib/vex/validators/inclusion.ex">>,<<"lib/vex/validators/length.ex">>,
|
19
|
- <<"lib/vex/validators/presence.ex">>,<<"mix.exs">>,<<"README.md">>,
|
19
|
+ <<"lib/vex/validators/number.ex">>,<<"lib/vex/validators/presence.ex">>,
|
20
|
+ <<"lib/vex/validators/uuid.ex">>,<<"mix.exs">>,<<"README.md">>,
|
20
21
|
<<"LICENSE">>]}.
|
21
22
|
{<<"licenses">>,[<<"MIT License">>]}.
|
22
23
|
{<<"links">>,[{<<"github">>,<<"https://github.com/CargoSense/vex">>}]}.
|
23
24
|
{<<"maintainers">>,[<<"Bruce Williams">>,<<"Ben Wilson">>,<<"John Hyland">>]}.
|
24
25
|
{<<"name">>,<<"vex">>}.
|
25
26
|
{<<"requirements">>,[]}.
|
26
|
- {<<"version">>,<<"0.7.0">>}.
|
27
|
+ {<<"version">>,<<"0.8.0">>}.
|
added
lib/vex/validators/number.ex
|
@@ -0,0 +1,205 @@
|
1
|
+ defmodule Vex.Validators.Number do
|
2
|
+ @moduledoc """
|
3
|
+ Ensure a value is a number.
|
4
|
+
|
5
|
+ ## Options
|
6
|
+
|
7
|
+ At least one of the following must be provided:
|
8
|
+
|
9
|
+ * `:is`: The value is a number (integer or float) or not.
|
10
|
+ * `:equal_to`: The value is a number equal to this number.
|
11
|
+ * `:greater_than` : The value is a number greater than this number.
|
12
|
+ * `:greater_than_or_equal_to`: The value is a number greater than or equal to this number.
|
13
|
+ * `:less_than` : The value is a number less than this number.
|
14
|
+ * `:less_than_or_equal_to`: The value is a number less than or equal to this number.
|
15
|
+
|
16
|
+ Optional:
|
17
|
+
|
18
|
+ * `:message`: A custom error message. May be in EEx format and use the fields described
|
19
|
+ in [Custom Error Messages](#module-custom-error-messages).
|
20
|
+ * `:allow_nil`: A boolean whether to skip this validation for `nil` values.
|
21
|
+ * `:allow_blank`: A boolean whether to skip this validaton for blank values.
|
22
|
+
|
23
|
+ The `:is` option can be provided in place of the keyword list if no other options are set.
|
24
|
+ When multiple options are than the validator will do an `and` logic between them.
|
25
|
+
|
26
|
+ ## Examples
|
27
|
+
|
28
|
+ Examples when using the `:is` option:
|
29
|
+
|
30
|
+ iex> Vex.Validators.Number.validate("not_a_number", is: true)
|
31
|
+ {:error, "must be a number"}
|
32
|
+ iex> Vex.Validators.Number.validate(3.14, is: true)
|
33
|
+ :ok
|
34
|
+
|
35
|
+ iex> Vex.Validators.Number.validate("not_a_number", is: false)
|
36
|
+ :ok
|
37
|
+ iex> Vex.Validators.Number.validate(3.14, is: false)
|
38
|
+ {:error, "must not be a number"}
|
39
|
+
|
40
|
+ Examples when using the boolean value in options for the `:is` option:
|
41
|
+
|
42
|
+ iex> Vex.Validators.Number.validate("not_a_number", true)
|
43
|
+ {:error, "must be a number"}
|
44
|
+ iex> Vex.Validators.Number.validate(3.14, true)
|
45
|
+ :ok
|
46
|
+
|
47
|
+ iex> Vex.Validators.Number.validate("not_a_number", false)
|
48
|
+ :ok
|
49
|
+ iex> Vex.Validators.Number.validate(3.14, false)
|
50
|
+ {:error, "must not be a number"}
|
51
|
+
|
52
|
+ Examples when using the `:equal_to` option:
|
53
|
+
|
54
|
+ iex> Vex.Validators.Number.validate(3.14, equal_to: 1.41)
|
55
|
+ {:error, "must be a number equal to 1.41"}
|
56
|
+ iex> Vex.Validators.Number.validate(3.14, equal_to: 3.14)
|
57
|
+ :ok
|
58
|
+ iex> Vex.Validators.Number.validate(3.14, equal_to: 6.28)
|
59
|
+ {:error, "must be a number equal to 6.28"}
|
60
|
+
|
61
|
+ Examples when using the `:greater_than` option:
|
62
|
+
|
63
|
+ iex> Vex.Validators.Number.validate(3.14, greater_than: 1.41)
|
64
|
+ :ok
|
65
|
+ iex> Vex.Validators.Number.validate(3.14, greater_than: 3.14)
|
66
|
+ {:error, "must be a number greater than 3.14"}
|
67
|
+ iex> Vex.Validators.Number.validate(3.14, greater_than: 6.28)
|
68
|
+ {:error, "must be a number greater than 6.28"}
|
69
|
+
|
70
|
+ Examples when using the `:greater_than_or_equal_to` option:
|
71
|
+
|
72
|
+ iex> Vex.Validators.Number.validate(3.14, greater_than_or_equal_to: 1.41)
|
73
|
+ :ok
|
74
|
+ iex> Vex.Validators.Number.validate(3.14, greater_than_or_equal_to: 3.14)
|
75
|
+ :ok
|
76
|
+ iex> Vex.Validators.Number.validate(3.14, greater_than_or_equal_to: 6.28)
|
77
|
+ {:error, "must be a number greater than or equal to 6.28"}
|
78
|
+
|
79
|
+ Examples when using the `:less_than` option:
|
80
|
+
|
81
|
+ iex> Vex.Validators.Number.validate(3.14, less_than: 1.41)
|
82
|
+ {:error, "must be a number less than 1.41"}
|
83
|
+ iex> Vex.Validators.Number.validate(3.14, less_than: 3.14)
|
84
|
+ {:error, "must be a number less than 3.14"}
|
85
|
+ iex> Vex.Validators.Number.validate(3.14, less_than: 6.28)
|
86
|
+ :ok
|
87
|
+
|
88
|
+ Examples when using the `:less_than_or_equal_to` option:
|
89
|
+
|
90
|
+ iex> Vex.Validators.Number.validate(3.14, less_than_or_equal_to: 1.41)
|
91
|
+ {:error, "must be a number less than or equal to 1.41"}
|
92
|
+ iex> Vex.Validators.Number.validate(3.14, less_than_or_equal_to: 3.14)
|
93
|
+ :ok
|
94
|
+ iex> Vex.Validators.Number.validate(3.14, less_than_or_equal_to: 6.28)
|
95
|
+ :ok
|
96
|
+
|
97
|
+ Examples when using the combinations of the above options:
|
98
|
+
|
99
|
+ iex> Vex.Validators.Number.validate("not_a_number", is: true, greater_than: 0, less_than_or_equal_to: 3.14)
|
100
|
+ {:error, "must be a number"}
|
101
|
+ iex> Vex.Validators.Number.validate(0, is: true, greater_than: 0, less_than_or_equal_to: 3.14)
|
102
|
+ {:error, "must be a number greater than 0"}
|
103
|
+ iex> Vex.Validators.Number.validate(1.41, is: true, greater_than: 0, less_than_or_equal_to: 3.14)
|
104
|
+ :ok
|
105
|
+ iex> Vex.Validators.Number.validate(3.14, is: true, greater_than: 0, less_than_or_equal_to: 3.14)
|
106
|
+ :ok
|
107
|
+ iex> Vex.Validators.Number.validate(6.28, is: true, greater_than: 0, less_than_or_equal_to: 3.14)
|
108
|
+ {:error, "must be a number less than or equal to 3.14"}
|
109
|
+
|
110
|
+ ## Custom Error Messages
|
111
|
+
|
112
|
+ Custom error messages (in EEx format), provided as :message, can use the following values:
|
113
|
+
|
114
|
+ iex> Vex.Validators.Number.__validator__(:message_fields)
|
115
|
+ [
|
116
|
+ value: "Bad value",
|
117
|
+ is: "Is number",
|
118
|
+ equal_to: "Equal to number",
|
119
|
+ greater_than: "Greater than number",
|
120
|
+ greater_than_or_equal_to: "Greater than or equal to number",
|
121
|
+ less_than: "Less than number",
|
122
|
+ less_than_or_equal_to: "Less than or equal to number"
|
123
|
+ ]
|
124
|
+
|
125
|
+ An example:
|
126
|
+
|
127
|
+ iex> Vex.Validators.Number.validate(3.14, less_than: 1.41,
|
128
|
+ ...> message: "<%= inspect value %> should be less than <%= less_than %>")
|
129
|
+ {:error, "3.14 should be less than 1.41"}
|
130
|
+ """
|
131
|
+
|
132
|
+ use Vex.Validator
|
133
|
+
|
134
|
+ @option_keys [
|
135
|
+ :is,
|
136
|
+ :equal_to,
|
137
|
+ :greater_than,
|
138
|
+ :greater_than_or_equal_to,
|
139
|
+ :less_than,
|
140
|
+ :less_than_or_equal_to
|
141
|
+ ]
|
142
|
+
|
143
|
+ @message_fields [
|
144
|
+ value: "Bad value",
|
145
|
+ is: "Is number",
|
146
|
+ equal_to: "Equal to number",
|
147
|
+ greater_than: "Greater than number",
|
148
|
+ greater_than_or_equal_to: "Greater than or equal to number",
|
149
|
+ less_than: "Less than number",
|
150
|
+ less_than_or_equal_to: "Less than or equal to number"
|
151
|
+ ]
|
152
|
+ def validate(value, options) when is_boolean(options) do
|
153
|
+ validate(value, is: options)
|
154
|
+ end
|
155
|
+
|
156
|
+ def validate(value, options) when is_list(options) do
|
157
|
+ unless_skipping value, options do
|
158
|
+ Enum.reduce_while(options, :ok, fn
|
159
|
+ {k, o}, _ when k in @option_keys ->
|
160
|
+ case do_validate(value, k, o) do
|
161
|
+ :ok ->
|
162
|
+ {:cont, :ok}
|
163
|
+
|
164
|
+ {:error, default_message} ->
|
165
|
+ fields =
|
166
|
+ options
|
167
|
+ |> Keyword.take(@option_keys)
|
168
|
+ |> Keyword.put(:value, value)
|
169
|
+ |> Keyword.put(:less_than, options[:less_than])
|
170
|
+
|
171
|
+ error = {:error, message(options, default_message, fields)}
|
172
|
+
|
173
|
+ {:halt, error}
|
174
|
+ end
|
175
|
+
|
176
|
+ _, _ ->
|
177
|
+ {:cont, :ok}
|
178
|
+ end)
|
179
|
+ end
|
180
|
+ end
|
181
|
+
|
182
|
+ defp do_validate(_, _, nil), do: :ok
|
183
|
+ defp do_validate(v, :is, o) when is_number(v) === o, do: :ok
|
184
|
+ defp do_validate(_, :is, true), do: {:error, "must be a number"}
|
185
|
+ defp do_validate(_, :is, false), do: {:error, "must not be a number"}
|
186
|
+
|
187
|
+ defp do_validate(_, k, o) when not is_number(o),
|
188
|
+ do: raise("Invalid value #{inspect(o)} for option #{k}")
|
189
|
+
|
190
|
+ defp do_validate(v, :equal_to, o) when is_number(v) and v == o, do: :ok
|
191
|
+ defp do_validate(_, :equal_to, o), do: {:error, "must be a number equal to #{o}"}
|
192
|
+ defp do_validate(v, :greater_than, o) when is_number(v) and v > o, do: :ok
|
193
|
+ defp do_validate(_, :greater_than, o), do: {:error, "must be a number greater than #{o}"}
|
194
|
+ defp do_validate(v, :greater_than_or_equal_to, o) when is_number(v) and v >= o, do: :ok
|
195
|
+
|
196
|
+ defp do_validate(_, :greater_than_or_equal_to, o),
|
197
|
+ do: {:error, "must be a number greater than or equal to #{o}"}
|
198
|
+
|
199
|
+ defp do_validate(v, :less_than, o) when is_number(v) and v < o, do: :ok
|
200
|
+ defp do_validate(_, :less_than, o), do: {:error, "must be a number less than #{o}"}
|
201
|
+ defp do_validate(v, :less_than_or_equal_to, o) when is_number(v) and v <= o, do: :ok
|
202
|
+
|
203
|
+ defp do_validate(_, :less_than_or_equal_to, o),
|
204
|
+ do: {:error, "must be a number less than or equal to #{o}"}
|
205
|
+ end
|
added
lib/vex/validators/uuid.ex
|
@@ -0,0 +1,154 @@
|
1
|
+ defmodule Vex.Validators.Uuid do
|
2
|
+ @moduledoc """
|
3
|
+ Ensure a value is a valid UUID string.
|
4
|
+
|
5
|
+ ## Options
|
6
|
+
|
7
|
+ At least one of the following must be provided:
|
8
|
+
|
9
|
+ * `:format`: Required. An atom that defines the UUID format of the value:
|
10
|
+
|
11
|
+ * `:default`: The value must be a string with format `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx`.
|
12
|
+ * `:hex`: The value must be a string with the format `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`.
|
13
|
+ * `:urn`: The value must be a string with the format `urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx`.
|
14
|
+ * `:any`: The value must be a string of any of the supported formats (`:default`, `:hex` or `:urn`).
|
15
|
+ * `:not_any`: The value must not be a valid UUID string.
|
16
|
+
|
17
|
+ *Note: `x` is a hex number.*
|
18
|
+
|
19
|
+ Optional:
|
20
|
+
|
21
|
+ * `:message`: A custom error message. May be in EEx format
|
22
|
+ and use the fields described in [Custom Error Messages](#module-custom-error-messages).
|
23
|
+ * `:allow_nil`: A boolean whether to skip this validation for `nil` values.
|
24
|
+ * `:allow_blank`: A boolean whether to skip this validaton for blank values.
|
25
|
+
|
26
|
+ The value for `:format` can be provided instead of the options keyword list.
|
27
|
+ Additionally, if the options is a boolean value, then:
|
28
|
+
|
29
|
+ * `true`: Is the same as the `[format: :any]` options.
|
30
|
+ * `false`: Is the same as the `[format: :not_any]` options.
|
31
|
+
|
32
|
+ ## Examples
|
33
|
+
|
34
|
+ Examples when using the `:any` or `true` options:
|
35
|
+
|
36
|
+ iex> Vex.Validators.Uuid.validate("02aa7f48-3ccd-11e4-b63e-14109ff1a304", format: :any)
|
37
|
+ :ok
|
38
|
+ iex> Vex.Validators.Uuid.validate("02aa7f48-3ccd-11e4-b63e-14109ff1a30", format: :any)
|
39
|
+ {:error, "must be a valid UUID string"}
|
40
|
+
|
41
|
+ iex> Vex.Validators.Uuid.validate("02aa7f48-3ccd-11e4-b63e-14109ff1a304", true)
|
42
|
+ :ok
|
43
|
+ iex> Vex.Validators.Uuid.validate("02aa7f48-3ccd-11e4-b63e-14109ff1a30", true)
|
44
|
+ {:error, "must be a valid UUID string"}
|
45
|
+
|
46
|
+ Examples when using the `:not_any` or `false` options:
|
47
|
+
|
48
|
+ iex> Vex.Validators.Uuid.validate("not_a_uuid", format: :not_any)
|
49
|
+ :ok
|
50
|
+ iex> Vex.Validators.Uuid.validate("02aa7f48-3ccd-11e4-b63e-14109ff1a304", format: :not_any)
|
51
|
+ {:error, "must not be a valid UUID string"}
|
52
|
+
|
53
|
+ iex> Vex.Validators.Uuid.validate("not_a_uuid", false)
|
54
|
+ :ok
|
55
|
+ iex> Vex.Validators.Uuid.validate("02aa7f48-3ccd-11e4-b63e-14109ff1a304", false)
|
56
|
+ {:error, "must not be a valid UUID string"}
|
57
|
+
|
58
|
+ Examples when using the `:default` option:
|
59
|
+
|
60
|
+ iex> Vex.Validators.Uuid.validate("02aa7f48-3ccd-11e4-b63e-14109ff1a304", format: :default)
|
61
|
+ :ok
|
62
|
+ iex> Vex.Validators.Uuid.validate("02aa7f483ccd11e4b63e14109ff1a304", format: :default)
|
63
|
+ {:error, "must be a valid UUID string in default format"}
|
64
|
+
|
65
|
+ Examples when using the `:hex` option:
|
66
|
+
|
67
|
+ iex> Vex.Validators.Uuid.validate("02aa7f483ccd11e4b63e14109ff1a304", format: :hex)
|
68
|
+ :ok
|
69
|
+ iex> Vex.Validators.Uuid.validate("urn:uuid:02aa7f48-3ccd-11e4-b63e-14109ff1a304", format: :hex)
|
70
|
+ {:error, "must be a valid UUID string in hex format"}
|
71
|
+
|
72
|
+ Examples when using the `:urn` option:
|
73
|
+
|
74
|
+ iex> Vex.Validators.Uuid.validate("urn:uuid:02aa7f48-3ccd-11e4-b63e-14109ff1a304", format: :urn)
|
75
|
+ :ok
|
76
|
+ iex> Vex.Validators.Uuid.validate("02aa7f48-3ccd-11e4-b63e-14109ff1a304", format: :urn)
|
77
|
+ {:error, "must be a valid UUID string in urn format"}
|
78
|
+
|
79
|
+ ## Custom Error Messages
|
80
|
+
|
81
|
+ Custom error messages (in EEx format), provided as `:message`, can use the following values:
|
82
|
+
|
83
|
+ iex> Vex.Validators.Uuid.__validator__(:message_fields)
|
84
|
+ [value: "Bad value", format: "The UUID format"]
|
85
|
+
|
86
|
+ An example:
|
87
|
+
|
88
|
+ iex> Vex.Validators.Uuid.validate("not_a_uuid", format: :any,
|
89
|
+ ...> message: "<%= value %> should be <%= format %> UUID")
|
90
|
+ {:error, "not_a_uuid should be any UUID"}
|
91
|
+ """
|
92
|
+
|
93
|
+ use Vex.Validator
|
94
|
+
|
95
|
+ @uuid_formats [:default, :hex, :urn]
|
96
|
+
|
97
|
+ @formats [:any, :not_any] ++ @uuid_formats
|
98
|
+
|
99
|
+ @urn_prefix "urn:uuid:"
|
100
|
+
|
101
|
+ @message_fields [value: "Bad value", format: "The UUID format"]
|
102
|
+ def validate(value, true), do: validate(value, format: :any)
|
103
|
+ def validate(value, false), do: validate(value, format: :not_any)
|
104
|
+ def validate(value, options) when options in @formats, do: validate(value, format: options)
|
105
|
+
|
106
|
+ def validate(value, options) when is_list(options) do
|
107
|
+ unless_skipping value, options do
|
108
|
+ format = options[:format]
|
109
|
+
|
110
|
+ case do_validate(value, format) do
|
111
|
+ :ok -> :ok
|
112
|
+ {:error, reason} -> {:error, message(options, reason, value: value, format: format)}
|
113
|
+ end
|
114
|
+ end
|
115
|
+ end
|
116
|
+
|
117
|
+ defp do_validate(<<_::64, ?-, _::32, ?-, _::32, ?-, _::32, ?-, _::96>>, :default) do
|
118
|
+ :ok
|
119
|
+ end
|
120
|
+
|
121
|
+ defp do_validate(<<_::256>>, :hex) do
|
122
|
+ :ok
|
123
|
+ end
|
124
|
+
|
125
|
+ defp do_validate(<<@urn_prefix, _::64, ?-, _::32, ?-, _::32, ?-, _::32, ?-, _::96>>, :urn) do
|
126
|
+ :ok
|
127
|
+ end
|
128
|
+
|
129
|
+ defp do_validate(_, format) when format in @uuid_formats do
|
130
|
+ {:error, "must be a valid UUID string in #{format} format"}
|
131
|
+ end
|
132
|
+
|
133
|
+ defp do_validate(value, :any) do
|
134
|
+ error = {:error, "must be a valid UUID string"}
|
135
|
+
|
136
|
+ Enum.reduce_while(@uuid_formats, error, fn format, _ ->
|
137
|
+ case do_validate(value, format) do
|
138
|
+ :ok -> {:halt, :ok}
|
139
|
+ _ -> {:cont, error}
|
140
|
+ end
|
141
|
+ end)
|
142
|
+ end
|
143
|
+
|
144
|
+ defp do_validate(value, :not_any) do
|
145
|
+ case do_validate(value, :any) do
|
146
|
+ :ok -> {:error, "must not be a valid UUID string"}
|
147
|
+ _ -> :ok
|
148
|
+ end
|
149
|
+ end
|
150
|
+
|
151
|
+ defp do_validate(_, format) do
|
152
|
+ raise "Invalid value #{inspect(format)} for option :format"
|
153
|
+ end
|
154
|
+ end
|
changed
mix.exs
|
@@ -4,7 +4,7 @@ defmodule Vex.Mixfile do
|
4
4
|
def project do
|
5
5
|
[
|
6
6
|
app: :vex,
|
7
|
- version: "0.7.0",
|
7
|
+ version: "0.8.0",
|
8
8
|
elixir: "~> 1.2",
|
9
9
|
deps: deps(),
|
10
10
|
consolidate_protocols: Mix.env() != :test,
|