Skip to content

Commit 206972d

Browse files
committed
implement extract for structs and other fixes
1 parent bc0b20e commit 206972d

17 files changed

+67
-40
lines changed

.travis.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ notifications:
33
recipients:
44
55
otp_release:
6-
- R16B
6+
- 17.1
77
before_install:
88
- git clone https://github.com/elixir-lang/elixir
99
- cd elixir && make && cd ..
1010
before_script: "export PATH=`pwd`/elixir/bin:$PATH"
11-
script: "MIX_ENV=test mix do deps.get, test"
11+
script: "MIX_ENV=test mix do deps.get, test"

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1616
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1717
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1818
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19-
THE SOFTWARE.
19+
THE SOFTWARE.

lib/vex.ex

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ defmodule Vex do
8282
end
8383

8484
defp sources do
85-
Mix.project |> Keyword.get(:vex, []) |> Keyword.get(:sources, [Vex.Validators])
85+
Mix.Project.get.project |> Keyword.get(:vex, []) |> Keyword.get(:sources, [Vex.Validators])
8686
end
8787

8888
defp extract(data, attribute, :confirmation) do

lib/vex/blank.ex

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
defprotocol Vex.Blank do
2-
@only [Atom, Tuple, List, BitString, Any]
2+
@only [Atom, Tuple, List, BitString, Map, Any]
33
@doc "Whether an item is blank"
44
def blank?(value)
55
end
@@ -29,6 +29,10 @@ defimpl Vex.Blank, for: Atom do
2929
def blank?(_), do: false
3030
end
3131

32+
defimpl Vex.Blank, for: Map do
33+
def blank?(map), do: map_size(map) == 0
34+
end
35+
3236
defimpl Vex.Blank, for: Any do
3337
def blank?(_), do: false
34-
end
38+
end

lib/vex/extract.ex

+20
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,26 @@ defimpl Vex.Extract, for: List do
1717
end
1818
end
1919

20+
defmodule Vex.Extract.Struct do
21+
defmacro for_struct do
22+
quote do
23+
defimpl Vex.Blank, for: __MODULE__ do
24+
def blank?(struct), do: (struct |> Map.from_struct |> map_size) == 0
25+
end
26+
27+
defimpl Vex.Extract, for: __MODULE__ do
28+
def settings(%{__struct__: module}) do
29+
module.__vex_validations__
30+
end
31+
32+
def attribute(map, name) do
33+
Map.get(map, name)
34+
end
35+
end
36+
end
37+
end
38+
end
39+
2040
defimpl Vex.Extract, for: Tuple do
2141
def settings(record) do
2242
[name | _tail] = Tuple.to_list(record)

lib/vex/struct.ex

+4-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ defmodule Vex.Struct do
1111

1212
defmacro __before_compile__(_) do
1313
quote do
14-
def __record__(:vex_validations), do: @vex_validations
14+
def __vex_validations__(), do: @vex_validations
15+
16+
require Vex.Extract.Struct
17+
Vex.Extract.Struct.for_struct
1518
end
1619
end
1720

lib/vex/validator.ex

+3-3
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ defmodule Vex.Validator do
4343
iex> Vex.Validator.validate?([name: "foo"], if: &(&1[:name] != "foo"))
4444
false
4545
iex> Vex.Validator.validate?([name: "foo"], unless: &(&1[:name] != "foo"))
46-
true
46+
true
4747
"""
4848
def validate?(data, options) when is_list(options) do
4949
cond do
@@ -62,11 +62,11 @@ defmodule Vex.Validator do
6262
end
6363
defp validate_if(data, condition) when is_function(condition) do
6464
!!condition.(data)
65-
end
65+
end
6666

6767
defp do_validate_if_condition(data, {name, value}) when is_atom(name) do
6868
Vex.Extract.attribute(data, name) == value
69-
end
69+
end
7070
defp do_validate_if_condition(data, condition) when is_atom(condition) do
7171
!Vex.Blank.blank?(Vex.Extract.attribute(data, condition))
7272
end

lib/vex/validator/source.ex

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ defimpl Vex.Validator.Source, for: Atom do
2020
end
2121

2222
defp validator_by_structure(source, name) do
23-
check Module.concat(source, camelize(String.to_atom(name)))
23+
check Module.concat(source, camelize(Atom.to_string(name)))
2424
end
2525

2626
defp check(validator) do

lib/vex/validators/acceptance.ex

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ defmodule Vex.Validators.Acceptance do
2121
iex> Vex.Validators.Acceptance.validate(nil, message: "must be accepted!")
2222
{:error, "must be accepted!"}
2323
iex> Vex.Validators.Acceptance.validate(1, [as: "yes"])
24-
{:error, %s(must be accepted with `"yes"`)}
24+
{:error, ~S(must be accepted with `"yes"`)}
2525
iex> Vex.Validators.Acceptance.validate("verily", [as: "verily"])
2626
:ok
2727

lib/vex/validators/by.ex

+4-4
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ defmodule Vex.Validators.By do
2929
iex> Vex.Validators.By.validate({}, [function: &is_list/1, allow_blank: true])
3030
:ok
3131
iex> Vex.Validators.By.validate([1], [function: &is_list/1, message: "must be a list"])
32-
:ok
32+
:ok
3333
iex> Vex.Validators.By.validate("a", [function: &is_list/1, message: "must be a list"])
3434
{:error, "must be a list"}
3535
@@ -43,10 +43,10 @@ defmodule Vex.Validators.By do
4343
An example:
4444
4545
iex> Vex.Validators.By.validate("blah", [function: &is_list/1, message: "<%= inspect value %> isn't a list"])
46-
{:error, %s("blah" isn't a list)}
46+
{:error, ~S("blah" isn't a list)}
4747
"""
4848
use Vex.Validator
49-
49+
5050
@message_fields [value: "The bad value"]
5151
def validate(value, func) when is_function(func), do: validate(value, function: func)
5252
def validate(value, options) when is_list(options) do
@@ -59,4 +59,4 @@ defmodule Vex.Validators.By do
5959
end
6060
end
6161
end
62-
end
62+
end

lib/vex/validators/confirmation.ex

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ defmodule Vex.Validators.Confirmation do
4040
An example:
4141
4242
iex> Vex.Validators.Confirmation.validate(["foo", nil], message: "<%= inspect confirmation %> doesn't match <%= inspect value %>")
43-
{:error, %s(nil doesn't match "foo")}
43+
{:error, ~S(nil doesn't match "foo")}
4444
4545
"""
4646
use Vex.Validator

lib/vex/validators/exclusion.ex

+4-4
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ defmodule Vex.Validators.Exclusion do
1717
iex> Vex.Validators.Exclusion.validate(1, [in: [1, 2, 3]])
1818
{:error, "must not be one of [1, 2, 3]"}
1919
iex> Vex.Validators.Exclusion.validate(1, [in: [1, 2, 3], message: "<%= value %> shouldn't be in <%= inspect list %>"])
20-
{:error, "1 shouldn't be in [1, 2, 3]"}
20+
{:error, "1 shouldn't be in [1, 2, 3]"}
2121
iex> Vex.Validators.Exclusion.validate(4, [1, 2, 3])
2222
:ok
2323
iex> Vex.Validators.Exclusion.validate("a", %w(a b c))
24-
{:error, %s(must not be one of ["a", "b", "c"])}
24+
{:error, ~S(must not be one of ["a", "b", "c"])}
2525
iex> Vex.Validators.Exclusion.validate("a", in: %w(a b c), message: "must not be abc, talkin' 'bout 123")
2626
{:error, "must not be abc, talkin' 'bout 123"}
2727
@@ -35,7 +35,7 @@ defmodule Vex.Validators.Exclusion do
3535
An example:
3636
3737
iex> Vex.Validators.Exclusion.validate("a", in: %w(a b c), message: "<%= inspect value %> is a disallowed value")
38-
{:error, %s("a" is a disallowed value)}
38+
{:error, ~S("a" is a disallowed value)}
3939
"""
4040

4141
use Vex.Validator
@@ -56,4 +56,4 @@ defmodule Vex.Validators.Exclusion do
5656
defp result(true, _), do: :ok
5757
defp result(false, message), do: {:error, message}
5858

59-
end
59+
end

lib/vex/validators/format.ex

+6-6
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,17 @@ defmodule Vex.Validators.Format do
1313
1414
## Examples
1515
16-
iex> Vex.Validators.Format.validate("foo", ~r/"^f"/)
16+
iex> Vex.Validators.Format.validate("foo", ~r/^f/)
1717
:ok
18-
iex> Vex.Validators.Format.validate("foo", ~r/"o{3,}"/)
18+
iex> Vex.Validators.Format.validate("foo", ~r/o{3,}/)
1919
{:error, "must have the correct format"}
20-
iex> Vex.Validators.Format.validate("foo", [with: ~r/"^f"/])
20+
iex> Vex.Validators.Format.validate("foo", [with: ~r/^f/])
2121
:ok
22-
iex> Vex.Validators.Format.validate("bar", [with: ~r/"^f"/, message: "must start with an f"])
22+
iex> Vex.Validators.Format.validate("bar", [with: ~r/^f/, message: "must start with an f"])
2323
{:error, "must start with an f"}
24-
iex> Vex.Validators.Format.validate("", [with: ~r/"^f"/, allow_blank: true])
24+
iex> Vex.Validators.Format.validate("", [with: ~r/^f/, allow_blank: true])
2525
:ok
26-
iex> Vex.Validators.Format.validate(nil, [with: ~r/"^f"/, allow_nil: true])
26+
iex> Vex.Validators.Format.validate(nil, [with: ~r/^f/, allow_nil: true])
2727
:ok
2828
2929
## Custom Error Messages

lib/vex/validators/inclusion.ex

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ defmodule Vex.Validators.Inclusion do
2121
iex> Vex.Validators.Inclusion.validate("a", %w(a b c))
2222
:ok
2323
iex> Vex.Validators.Inclusion.validate(nil, %w(a b c))
24-
{:error, %s(must be one of ["a", "b", "c"])}
24+
{:error, ~S(must be one of ["a", "b", "c"])}
2525
iex> Vex.Validators.Inclusion.validate(nil, [in: %w(a b c), allow_nil: true])
2626
:ok
2727
iex> Vex.Validators.Inclusion.validate("", [in: %w(a b c), allow_blank: true])
@@ -37,7 +37,7 @@ defmodule Vex.Validators.Inclusion do
3737
An example:
3838
3939
iex> Vex.Validators.Inclusion.validate("a", in: [1, 2, 3], message: "<%= inspect value %> is not an allowed value")
40-
{:error, %s("a" is not an allowed value)}
40+
{:error, ~S("a" is not an allowed value)}
4141
4242
"""
4343
use Vex.Validator

test/validations/absence_test.exs

+5-5
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ defmodule AbsenceTest do
2323
end
2424

2525
test "record, included absence validation" do
26-
assert !Vex.valid?(AbsenceTestRecord.new name: "I have a name")
27-
assert Vex.valid?(AbsenceTestRecord.new name: nil)
28-
assert Vex.valid?(AbsenceTestRecord.new name: [])
29-
assert Vex.valid?(AbsenceTestRecord.new name: "")
30-
assert Vex.valid?(AbsenceTestRecord.new name: {})
26+
assert !Vex.valid?(%AbsenceTestRecord{name: "I have a name"})
27+
assert Vex.valid?(%AbsenceTestRecord{name: nil})
28+
assert Vex.valid?(%AbsenceTestRecord{name: []})
29+
assert Vex.valid?(%AbsenceTestRecord{name: ""})
30+
assert Vex.valid?(%AbsenceTestRecord{name: {}})
3131
end
3232

3333
end

test/validations/acceptance_test.exs

+5-5
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,14 @@ defmodule AcceptanceTest do
4040
end
4141

4242
test "record, included basic presence validation" do
43-
assert Vex.valid?(AcceptanceTestRecord.new accepts_terms: "yes")
44-
assert Vex.valid?(AcceptanceTestRecord.new accepts_terms: true)
43+
assert Vex.valid?(%AcceptanceTestRecord{accepts_terms: "yes"})
44+
assert Vex.valid?(%AcceptanceTestRecord{accepts_terms: true})
4545
end
4646

4747
test "record, included custom presence validation" do
48-
assert Vex.valid?(CustomAcceptanceTestRecord.new accepts_terms: "yes")
49-
assert !Vex.valid?(CustomAcceptanceTestRecord.new accepts_terms: true)
50-
assert !Vex.valid?(CustomAcceptanceTestRecord.new accepts_terms: false)
48+
assert Vex.valid?(%CustomAcceptanceTestRecord{accepts_terms: "yes"})
49+
assert !Vex.valid?(%CustomAcceptanceTestRecord{accepts_terms: true})
50+
assert !Vex.valid?(%CustomAcceptanceTestRecord{accepts_terms: false})
5151
end
5252

5353
end

test/vex_test.exs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ defmodule VexTest do
1515
end
1616

1717
test "record, included complex validation" do
18-
user = UserTest.new username: "actualuser", password: "abcdefghi", password_confirmation: "abcdefghi"
18+
user = %UserTest{username: "actualuser", password: "abcdefghi", password_confirmation: "abcdefghi"}
1919
assert Vex.valid?(user)
2020
assert length(Vex.results(user)) > 0
2121
assert length(Vex.errors(user)) == 0

0 commit comments

Comments
 (0)